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.
- package/LICENSE +21 -0
- package/README.ja.md +377 -0
- package/README.md +377 -0
- package/dist/bin/magi-benchmark.d.ts +14 -0
- package/dist/bin/magi-benchmark.js +93 -0
- package/dist/bin/magi-mcp.d.ts +8 -0
- package/dist/bin/magi-mcp.js +28 -0
- package/dist/bin/magi.d.ts +2 -0
- package/dist/bin/magi.js +634 -0
- package/dist/src/adapters/base.d.ts +34 -0
- package/dist/src/adapters/base.js +149 -0
- package/dist/src/adapters/claude.d.ts +29 -0
- package/dist/src/adapters/claude.js +65 -0
- package/dist/src/adapters/codex.d.ts +21 -0
- package/dist/src/adapters/codex.js +41 -0
- package/dist/src/adapters/gemini.d.ts +18 -0
- package/dist/src/adapters/gemini.js +31 -0
- package/dist/src/adapters/registry.d.ts +19 -0
- package/dist/src/adapters/registry.js +59 -0
- package/dist/src/audit/hash-chain.d.ts +21 -0
- package/dist/src/audit/hash-chain.js +70 -0
- package/dist/src/audit/types.d.ts +25 -0
- package/dist/src/audit/types.js +1 -0
- package/dist/src/audit/writer.d.ts +18 -0
- package/dist/src/audit/writer.js +100 -0
- package/dist/src/benchmark/golden-tasks.d.ts +9 -0
- package/dist/src/benchmark/golden-tasks.js +476 -0
- package/dist/src/benchmark/reporter.d.ts +5 -0
- package/dist/src/benchmark/reporter.js +107 -0
- package/dist/src/benchmark/runner.d.ts +30 -0
- package/dist/src/benchmark/runner.js +224 -0
- package/dist/src/benchmark/scorer.d.ts +12 -0
- package/dist/src/benchmark/scorer.js +124 -0
- package/dist/src/benchmark/types.d.ts +54 -0
- package/dist/src/benchmark/types.js +1 -0
- package/dist/src/cache/deliberation-cache.d.ts +49 -0
- package/dist/src/cache/deliberation-cache.js +127 -0
- package/dist/src/cli/commands/config-cmd.d.ts +11 -0
- package/dist/src/cli/commands/config-cmd.js +190 -0
- package/dist/src/cli/commands/demo.d.ts +12 -0
- package/dist/src/cli/commands/demo.js +66 -0
- package/dist/src/cli/commands/setup.d.ts +7 -0
- package/dist/src/cli/commands/setup.js +182 -0
- package/dist/src/cli/i18n.d.ts +89 -0
- package/dist/src/cli/i18n.js +176 -0
- package/dist/src/cli/interactive-select.d.ts +27 -0
- package/dist/src/cli/interactive-select.js +130 -0
- package/dist/src/cli/tui-setup.d.ts +24 -0
- package/dist/src/cli/tui-setup.js +42 -0
- package/dist/src/config/cli-detector.d.ts +37 -0
- package/dist/src/config/cli-detector.js +99 -0
- package/dist/src/config/user-config.d.ts +81 -0
- package/dist/src/config/user-config.js +134 -0
- package/dist/src/context/auto-collector.d.ts +43 -0
- package/dist/src/context/auto-collector.js +337 -0
- package/dist/src/context/manager.d.ts +35 -0
- package/dist/src/context/manager.js +162 -0
- package/dist/src/context/serializer.d.ts +20 -0
- package/dist/src/context/serializer.js +52 -0
- package/dist/src/demo/recorded-deliberation.d.ts +13 -0
- package/dist/src/demo/recorded-deliberation.js +277 -0
- package/dist/src/engine/angel-detector.d.ts +83 -0
- package/dist/src/engine/angel-detector.js +334 -0
- package/dist/src/engine/at-field.d.ts +40 -0
- package/dist/src/engine/at-field.js +195 -0
- package/dist/src/engine/berserk-orchestrator.d.ts +66 -0
- package/dist/src/engine/berserk-orchestrator.js +378 -0
- package/dist/src/engine/change-metrics.d.ts +56 -0
- package/dist/src/engine/change-metrics.js +214 -0
- package/dist/src/engine/consensus.d.ts +20 -0
- package/dist/src/engine/consensus.js +146 -0
- package/dist/src/engine/dead-sea-scrolls.d.ts +132 -0
- package/dist/src/engine/dead-sea-scrolls.js +610 -0
- package/dist/src/engine/drift-detector.d.ts +39 -0
- package/dist/src/engine/drift-detector.js +225 -0
- package/dist/src/engine/dummy-plug.d.ts +44 -0
- package/dist/src/engine/dummy-plug.js +190 -0
- package/dist/src/engine/engram-manager.d.ts +55 -0
- package/dist/src/engine/engram-manager.js +306 -0
- package/dist/src/engine/events.d.ts +130 -0
- package/dist/src/engine/events.js +44 -0
- package/dist/src/engine/gospel.d.ts +30 -0
- package/dist/src/engine/gospel.js +129 -0
- package/dist/src/engine/hallucination-detector.d.ts +33 -0
- package/dist/src/engine/hallucination-detector.js +215 -0
- package/dist/src/engine/human-resolver.d.ts +19 -0
- package/dist/src/engine/human-resolver.js +89 -0
- package/dist/src/engine/instrumentality.d.ts +64 -0
- package/dist/src/engine/instrumentality.js +297 -0
- package/dist/src/engine/iruel-battle.d.ts +79 -0
- package/dist/src/engine/iruel-battle.js +319 -0
- package/dist/src/engine/kernel/deliberation-kernel.d.ts +12 -0
- package/dist/src/engine/kernel/deliberation-kernel.js +303 -0
- package/dist/src/engine/kernel/index.d.ts +8 -0
- package/dist/src/engine/kernel/index.js +7 -0
- package/dist/src/engine/kernel/phase-runner.d.ts +10 -0
- package/dist/src/engine/kernel/phase-runner.js +155 -0
- package/dist/src/engine/kernel/post-processor.d.ts +17 -0
- package/dist/src/engine/kernel/post-processor.js +131 -0
- package/dist/src/engine/kernel/types.d.ts +107 -0
- package/dist/src/engine/kernel/types.js +1 -0
- package/dist/src/engine/kernel/unit-executor.d.ts +6 -0
- package/dist/src/engine/kernel/unit-executor.js +132 -0
- package/dist/src/engine/lcl-manager.d.ts +44 -0
- package/dist/src/engine/lcl-manager.js +143 -0
- package/dist/src/engine/middleware/cache.d.ts +7 -0
- package/dist/src/engine/middleware/cache.js +29 -0
- package/dist/src/engine/middleware/chain.d.ts +18 -0
- package/dist/src/engine/middleware/chain.js +45 -0
- package/dist/src/engine/middleware/firewall.d.ts +8 -0
- package/dist/src/engine/middleware/firewall.js +24 -0
- package/dist/src/engine/middleware/index.d.ts +4 -0
- package/dist/src/engine/middleware/index.js +3 -0
- package/dist/src/engine/middleware/types.d.ts +43 -0
- package/dist/src/engine/middleware/types.js +1 -0
- package/dist/src/engine/nebuchadnezzar-key.d.ts +61 -0
- package/dist/src/engine/nebuchadnezzar-key.js +203 -0
- package/dist/src/engine/neon-genesis.d.ts +52 -0
- package/dist/src/engine/neon-genesis.js +203 -0
- package/dist/src/engine/objective-judge.d.ts +53 -0
- package/dist/src/engine/objective-judge.js +214 -0
- package/dist/src/engine/offline-mode.d.ts +18 -0
- package/dist/src/engine/offline-mode.js +46 -0
- package/dist/src/engine/orchestrator.d.ts +79 -0
- package/dist/src/engine/orchestrator.js +58 -0
- package/dist/src/engine/secret-cipher.d.ts +26 -0
- package/dist/src/engine/secret-cipher.js +114 -0
- package/dist/src/engine/seele-council.d.ts +90 -0
- package/dist/src/engine/seele-council.js +482 -0
- package/dist/src/engine/self-destruct.d.ts +61 -0
- package/dist/src/engine/self-destruct.js +231 -0
- package/dist/src/engine/self-evolution.d.ts +64 -0
- package/dist/src/engine/self-evolution.js +368 -0
- package/dist/src/engine/sync-rate.d.ts +45 -0
- package/dist/src/engine/sync-rate.js +151 -0
- package/dist/src/engine/type666-firewall.d.ts +76 -0
- package/dist/src/engine/type666-firewall.js +343 -0
- package/dist/src/engine/umbilical-cable.d.ts +41 -0
- package/dist/src/engine/umbilical-cable.js +192 -0
- package/dist/src/index.d.ts +106 -0
- package/dist/src/index.js +426 -0
- package/dist/src/mcp/server.d.ts +38 -0
- package/dist/src/mcp/server.js +196 -0
- package/dist/src/metrics/token-tracker.d.ts +38 -0
- package/dist/src/metrics/token-tracker.js +112 -0
- package/dist/src/parsers/json-extractor.d.ts +9 -0
- package/dist/src/parsers/json-extractor.js +239 -0
- package/dist/src/parsers/opinion-schema.d.ts +81 -0
- package/dist/src/parsers/opinion-schema.js +147 -0
- package/dist/src/parsers/unstructured-parser.d.ts +20 -0
- package/dist/src/parsers/unstructured-parser.js +122 -0
- package/dist/src/pipelines/architecture.d.ts +10 -0
- package/dist/src/pipelines/architecture.js +9 -0
- package/dist/src/pipelines/bug-analysis.d.ts +9 -0
- package/dist/src/pipelines/bug-analysis.js +8 -0
- package/dist/src/pipelines/code-review.d.ts +10 -0
- package/dist/src/pipelines/code-review.js +30 -0
- package/dist/src/pipelines/custom.d.ts +14 -0
- package/dist/src/pipelines/custom.js +29 -0
- package/dist/src/pipelines/registry.d.ts +9 -0
- package/dist/src/pipelines/registry.js +20 -0
- package/dist/src/prompts/personas.d.ts +6 -0
- package/dist/src/prompts/personas.js +44 -0
- package/dist/src/prompts/schemas.d.ts +4 -0
- package/dist/src/prompts/schemas.js +24 -0
- package/dist/src/prompts/templates.d.ts +6 -0
- package/dist/src/prompts/templates.js +91 -0
- package/dist/src/repl/accessibility.d.ts +23 -0
- package/dist/src/repl/accessibility.js +46 -0
- package/dist/src/repl/banner.d.ts +4 -0
- package/dist/src/repl/banner.js +28 -0
- package/dist/src/repl/boot-animation.d.ts +13 -0
- package/dist/src/repl/boot-animation.js +143 -0
- package/dist/src/repl/completer.d.ts +21 -0
- package/dist/src/repl/completer.js +168 -0
- package/dist/src/repl/context.d.ts +24 -0
- package/dist/src/repl/context.js +42 -0
- package/dist/src/repl/display-utils.d.ts +13 -0
- package/dist/src/repl/display-utils.js +65 -0
- package/dist/src/repl/event-listener.d.ts +18 -0
- package/dist/src/repl/event-listener.js +112 -0
- package/dist/src/repl/export-formatter.d.ts +8 -0
- package/dist/src/repl/export-formatter.js +73 -0
- package/dist/src/repl/ghost-text.d.ts +31 -0
- package/dist/src/repl/ghost-text.js +119 -0
- package/dist/src/repl/handoff-animation.d.ts +15 -0
- package/dist/src/repl/handoff-animation.js +65 -0
- package/dist/src/repl/history.d.ts +16 -0
- package/dist/src/repl/history.js +130 -0
- package/dist/src/repl/job-registry.d.ts +26 -0
- package/dist/src/repl/job-registry.js +80 -0
- package/dist/src/repl/magi-repl.d.ts +72 -0
- package/dist/src/repl/magi-repl.js +1008 -0
- package/dist/src/repl/multiline-input.d.ts +45 -0
- package/dist/src/repl/multiline-input.js +78 -0
- package/dist/src/repl/prompt-builder.d.ts +19 -0
- package/dist/src/repl/prompt-builder.js +36 -0
- package/dist/src/repl/repl-state.d.ts +5 -0
- package/dist/src/repl/repl-state.js +19 -0
- package/dist/src/repl/result-display.d.ts +8 -0
- package/dist/src/repl/result-display.js +195 -0
- package/dist/src/repl/session-stats.d.ts +26 -0
- package/dist/src/repl/session-stats.js +119 -0
- package/dist/src/repl/slash-commands.d.ts +60 -0
- package/dist/src/repl/slash-commands.js +725 -0
- package/dist/src/repl/terminal-sanitize.d.ts +14 -0
- package/dist/src/repl/terminal-sanitize.js +19 -0
- package/dist/src/reporters/console.d.ts +7 -0
- package/dist/src/reporters/console.js +78 -0
- package/dist/src/reporters/json.d.ts +2 -0
- package/dist/src/reporters/json.js +3 -0
- package/dist/src/reporters/markdown.d.ts +2 -0
- package/dist/src/reporters/markdown.js +65 -0
- package/dist/src/reporters/streaming.d.ts +20 -0
- package/dist/src/reporters/streaming.js +178 -0
- package/dist/src/tui/activity-log.d.ts +23 -0
- package/dist/src/tui/activity-log.js +67 -0
- package/dist/src/tui/animations.d.ts +39 -0
- package/dist/src/tui/animations.js +167 -0
- package/dist/src/tui/ansi.d.ts +28 -0
- package/dist/src/tui/ansi.js +51 -0
- package/dist/src/tui/boot-sequence.d.ts +11 -0
- package/dist/src/tui/boot-sequence.js +98 -0
- package/dist/src/tui/colors.d.ts +101 -0
- package/dist/src/tui/colors.js +71 -0
- package/dist/src/tui/header.d.ts +24 -0
- package/dist/src/tui/header.js +122 -0
- package/dist/src/tui/index.d.ts +3 -0
- package/dist/src/tui/index.js +3 -0
- package/dist/src/tui/keypress.d.ts +25 -0
- package/dist/src/tui/keypress.js +95 -0
- package/dist/src/tui/layout.d.ts +74 -0
- package/dist/src/tui/layout.js +171 -0
- package/dist/src/tui/magi-tui.d.ts +101 -0
- package/dist/src/tui/magi-tui.js +754 -0
- package/dist/src/tui/panel.d.ts +45 -0
- package/dist/src/tui/panel.js +292 -0
- package/dist/src/tui/screen-buffer.d.ts +54 -0
- package/dist/src/tui/screen-buffer.js +262 -0
- package/dist/src/tui/status-bar.d.ts +25 -0
- package/dist/src/tui/status-bar.js +124 -0
- package/dist/src/tui/terminal-detect.d.ts +26 -0
- package/dist/src/tui/terminal-detect.js +44 -0
- package/dist/src/tui/tui-helpers.d.ts +12 -0
- package/dist/src/tui/tui-helpers.js +37 -0
- package/dist/src/types/adapter.d.ts +75 -0
- package/dist/src/types/adapter.js +36 -0
- package/dist/src/types/config.d.ts +108 -0
- package/dist/src/types/config.js +85 -0
- package/dist/src/types/consensus.d.ts +55 -0
- package/dist/src/types/consensus.js +17 -0
- package/dist/src/types/core.d.ts +178 -0
- package/dist/src/types/core.js +85 -0
- package/dist/src/types/magi-api.d.ts +62 -0
- package/dist/src/types/magi-api.js +7 -0
- package/dist/src/types/phase-h.d.ts +142 -0
- package/dist/src/types/phase-h.js +7 -0
- package/dist/src/types/phase-i.d.ts +186 -0
- package/dist/src/types/phase-i.js +6 -0
- package/dist/src/types/phase-k.d.ts +259 -0
- package/dist/src/types/phase-k.js +6 -0
- package/dist/src/types/phase-l.d.ts +199 -0
- package/dist/src/types/phase-l.js +6 -0
- package/dist/src/types/pipeline.d.ts +37 -0
- package/dist/src/types/pipeline.js +2 -0
- package/dist/src/utils/abstain-factory.d.ts +2 -0
- package/dist/src/utils/abstain-factory.js +18 -0
- package/dist/src/utils/errors.d.ts +34 -0
- package/dist/src/utils/errors.js +59 -0
- package/dist/src/utils/file-validator.d.ts +50 -0
- package/dist/src/utils/file-validator.js +124 -0
- package/dist/src/utils/fire-and-forget.d.ts +5 -0
- package/dist/src/utils/fire-and-forget.js +10 -0
- package/dist/src/utils/flag-validator.d.ts +21 -0
- package/dist/src/utils/flag-validator.js +79 -0
- package/dist/src/utils/freeze.d.ts +8 -0
- package/dist/src/utils/freeze.js +16 -0
- package/dist/src/utils/language-detector.d.ts +16 -0
- package/dist/src/utils/language-detector.js +159 -0
- package/dist/src/utils/latency-tracker.d.ts +45 -0
- package/dist/src/utils/latency-tracker.js +100 -0
- package/dist/src/utils/logger.d.ts +33 -0
- package/dist/src/utils/logger.js +112 -0
- package/dist/src/utils/process.d.ts +40 -0
- package/dist/src/utils/process.js +253 -0
- package/dist/src/utils/retry.d.ts +12 -0
- package/dist/src/utils/retry.js +30 -0
- package/dist/src/utils/safe-fs.d.ts +38 -0
- package/dist/src/utils/safe-fs.js +56 -0
- package/dist/src/utils/safe-json-parse.d.ts +15 -0
- package/dist/src/utils/safe-json-parse.js +49 -0
- package/dist/src/utils/sanitize.d.ts +14 -0
- package/dist/src/utils/sanitize.js +186 -0
- package/dist/src/utils/semaphore.d.ts +22 -0
- package/dist/src/utils/semaphore.js +57 -0
- package/dist/src/utils/shutdown.d.ts +6 -0
- package/dist/src/utils/shutdown.js +51 -0
- package/dist/src/utils/tty.d.ts +5 -0
- package/dist/src/utils/tty.js +7 -0
- package/package.json +82 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B-06 Nebuchadnezzar Key (Creator Override)
|
|
3
|
+
*
|
|
4
|
+
* Ed25519 key pair for creator override of MAGI consensus decisions.
|
|
5
|
+
* The private key is returned once at generation and NEVER stored.
|
|
6
|
+
* Only the public key is persisted in `.magi/nebuchadnezzar.json`.
|
|
7
|
+
*/
|
|
8
|
+
import type { OverrideRequest, OverrideResult, NebuchadnezzarState } from '../types/phase-k.js';
|
|
9
|
+
import type { MagiEventBus } from './events.js';
|
|
10
|
+
export declare class NebuchadnezzarKey {
|
|
11
|
+
private state;
|
|
12
|
+
private readonly statePath;
|
|
13
|
+
private readonly eventBus?;
|
|
14
|
+
private usedNonces;
|
|
15
|
+
constructor(workspaceDir: string, eventBus?: MagiEventBus);
|
|
16
|
+
/**
|
|
17
|
+
* Generate a new Ed25519 key pair.
|
|
18
|
+
* Public key is stored; private key is returned once and NOT persisted.
|
|
19
|
+
*/
|
|
20
|
+
generateKeyPair(): Promise<{
|
|
21
|
+
publicKey: string;
|
|
22
|
+
privateKey: string;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Override a consensus decision with a signed request.
|
|
26
|
+
*/
|
|
27
|
+
override(request: OverrideRequest, previousDecision: string): Promise<OverrideResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Reset consecutive override counter (called when deliberation completes without override).
|
|
30
|
+
*/
|
|
31
|
+
resetConsecutiveOverrides(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Load state from disk.
|
|
34
|
+
*/
|
|
35
|
+
load(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Persist state to disk with 0o600 permissions.
|
|
38
|
+
*/
|
|
39
|
+
persist(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Check if public key has been generated/loaded.
|
|
42
|
+
*/
|
|
43
|
+
isInitialized(): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Get current state (or null if not initialized).
|
|
46
|
+
*/
|
|
47
|
+
getState(): NebuchadnezzarState | null;
|
|
48
|
+
/**
|
|
49
|
+
* Confidence multiplier based on consecutive override count.
|
|
50
|
+
* 1 override = 1.0, 2 consecutive = 0.9, 3+ consecutive = 0.7
|
|
51
|
+
*/
|
|
52
|
+
getConfidenceMultiplier(): number;
|
|
53
|
+
/**
|
|
54
|
+
* Static helper: sign a message for an override request.
|
|
55
|
+
* Returns hex-encoded Ed25519 signature.
|
|
56
|
+
*/
|
|
57
|
+
/** Generate a fresh nonce for an override request. */
|
|
58
|
+
static generateNonce(): string;
|
|
59
|
+
private evictExpiredNonces;
|
|
60
|
+
static sign(privateKeyHex: string, deliberationId: string, newDecision: string, reason: string, timestamp: string, nonce: string): string;
|
|
61
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B-06 Nebuchadnezzar Key (Creator Override)
|
|
3
|
+
*
|
|
4
|
+
* Ed25519 key pair for creator override of MAGI consensus decisions.
|
|
5
|
+
* The private key is returned once at generation and NEVER stored.
|
|
6
|
+
* Only the public key is persisted in `.magi/nebuchadnezzar.json`.
|
|
7
|
+
*/
|
|
8
|
+
import { generateKeyPairSync, sign as cryptoSign, verify as cryptoVerify, randomUUID } from 'node:crypto';
|
|
9
|
+
import { readFile } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { logger } from '../utils/logger.js';
|
|
12
|
+
import { safePersist } from '../utils/safe-fs.js';
|
|
13
|
+
import { safeJsonParse } from '../utils/safe-json-parse.js';
|
|
14
|
+
const STATE_FILENAME = 'nebuchadnezzar.json';
|
|
15
|
+
const REPLAY_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
|
|
16
|
+
const MAX_NONCE_CACHE = 1000;
|
|
17
|
+
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
18
|
+
export class NebuchadnezzarKey {
|
|
19
|
+
state = null;
|
|
20
|
+
statePath;
|
|
21
|
+
eventBus;
|
|
22
|
+
usedNonces = new Map(); // nonce → expiresAt
|
|
23
|
+
constructor(workspaceDir, eventBus) {
|
|
24
|
+
this.statePath = join(workspaceDir, STATE_FILENAME);
|
|
25
|
+
this.eventBus = eventBus;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate a new Ed25519 key pair.
|
|
29
|
+
* Public key is stored; private key is returned once and NOT persisted.
|
|
30
|
+
*/
|
|
31
|
+
async generateKeyPair() {
|
|
32
|
+
if (this.state !== null) {
|
|
33
|
+
throw new Error('NebuchadnezzarKey: already initialized. Cannot generate a second key pair.');
|
|
34
|
+
}
|
|
35
|
+
const { publicKey, privateKey } = generateKeyPairSync('ed25519', {
|
|
36
|
+
publicKeyEncoding: { type: 'spki', format: 'der' },
|
|
37
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'der' },
|
|
38
|
+
});
|
|
39
|
+
const publicKeyHex = publicKey.toString('hex');
|
|
40
|
+
const privateKeyHex = privateKey.toString('hex');
|
|
41
|
+
this.state = {
|
|
42
|
+
publicKey: publicKeyHex,
|
|
43
|
+
overrideCount: 0,
|
|
44
|
+
consecutiveOverrides: 0,
|
|
45
|
+
createdAt: new Date().toISOString(),
|
|
46
|
+
};
|
|
47
|
+
await this.persist();
|
|
48
|
+
return { publicKey: publicKeyHex, privateKey: privateKeyHex };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Override a consensus decision with a signed request.
|
|
52
|
+
*/
|
|
53
|
+
async override(request, previousDecision) {
|
|
54
|
+
if (this.state === null) {
|
|
55
|
+
throw new Error('NebuchadnezzarKey: not initialized. Call generateKeyPair() or load() first.');
|
|
56
|
+
}
|
|
57
|
+
// Replay prevention: timestamp freshness
|
|
58
|
+
const requestTime = new Date(request.timestamp).getTime();
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
if (Number.isNaN(requestTime) || Math.abs(now - requestTime) > REPLAY_WINDOW_MS) {
|
|
61
|
+
throw new Error(`NebuchadnezzarKey: timestamp too stale or invalid. Window: ${REPLAY_WINDOW_MS}ms`);
|
|
62
|
+
}
|
|
63
|
+
// Replay prevention: nonce uniqueness
|
|
64
|
+
if (!request.nonce || !UUID_V4_RE.test(request.nonce)) {
|
|
65
|
+
throw new Error('NebuchadnezzarKey: nonce must be a valid UUID v4.');
|
|
66
|
+
}
|
|
67
|
+
this.evictExpiredNonces();
|
|
68
|
+
if (this.usedNonces.has(request.nonce)) {
|
|
69
|
+
throw new Error('NebuchadnezzarKey: nonce already used. Replay attack rejected.');
|
|
70
|
+
}
|
|
71
|
+
this.usedNonces.set(request.nonce, now + REPLAY_WINDOW_MS);
|
|
72
|
+
// Verify Ed25519 signature (nonce included in signed message)
|
|
73
|
+
const message = `${request.deliberationId}:${request.newDecision}:${request.reason}:${request.timestamp}:${request.nonce}`;
|
|
74
|
+
const publicKeyDer = Buffer.from(this.state.publicKey, 'hex');
|
|
75
|
+
const signatureBuffer = Buffer.from(request.signature, 'hex');
|
|
76
|
+
const valid = cryptoVerify(null, // Ed25519 does not use a separate hash algorithm
|
|
77
|
+
Buffer.from(message, 'utf-8'), { key: publicKeyDer, format: 'der', type: 'spki' }, signatureBuffer);
|
|
78
|
+
if (!valid) {
|
|
79
|
+
throw new Error('NebuchadnezzarKey: invalid signature. Override rejected.');
|
|
80
|
+
}
|
|
81
|
+
// Update state
|
|
82
|
+
this.state.overrideCount += 1;
|
|
83
|
+
this.state.consecutiveOverrides += 1;
|
|
84
|
+
this.state.lastOverrideAt = new Date().toISOString();
|
|
85
|
+
const confidenceMultiplier = this.getConfidenceMultiplier();
|
|
86
|
+
const appliedAt = new Date().toISOString();
|
|
87
|
+
const result = {
|
|
88
|
+
success: true,
|
|
89
|
+
deliberationId: request.deliberationId,
|
|
90
|
+
previousDecision,
|
|
91
|
+
newDecision: request.newDecision,
|
|
92
|
+
confidenceMultiplier,
|
|
93
|
+
consecutiveOverrides: this.state.consecutiveOverrides,
|
|
94
|
+
overrideCount: this.state.overrideCount,
|
|
95
|
+
appliedAt,
|
|
96
|
+
};
|
|
97
|
+
// Audit log
|
|
98
|
+
logger.audit('nebuchadnezzar.override', request.deliberationId, {
|
|
99
|
+
previousDecision,
|
|
100
|
+
newDecision: request.newDecision,
|
|
101
|
+
reason: request.reason,
|
|
102
|
+
overrideCount: this.state.overrideCount,
|
|
103
|
+
consecutiveOverrides: this.state.consecutiveOverrides,
|
|
104
|
+
confidenceMultiplier,
|
|
105
|
+
});
|
|
106
|
+
// Persist updated state
|
|
107
|
+
await this.persist();
|
|
108
|
+
// Emit event
|
|
109
|
+
if (this.eventBus) {
|
|
110
|
+
this.eventBus.emit('nebuchadnezzar:override', {
|
|
111
|
+
deliberationId: request.deliberationId,
|
|
112
|
+
previousDecision,
|
|
113
|
+
newDecision: request.newDecision,
|
|
114
|
+
consecutiveOverrides: this.state.consecutiveOverrides,
|
|
115
|
+
emittedAt: new Date(),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Reset consecutive override counter (called when deliberation completes without override).
|
|
122
|
+
*/
|
|
123
|
+
resetConsecutiveOverrides() {
|
|
124
|
+
if (this.state !== null) {
|
|
125
|
+
this.state.consecutiveOverrides = 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Load state from disk.
|
|
130
|
+
*/
|
|
131
|
+
async load() {
|
|
132
|
+
const raw = await readFile(this.statePath, 'utf-8');
|
|
133
|
+
const parsed = safeJsonParse(raw);
|
|
134
|
+
this.state = parsed;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Persist state to disk with 0o600 permissions.
|
|
138
|
+
*/
|
|
139
|
+
async persist() {
|
|
140
|
+
if (this.state === null) {
|
|
141
|
+
throw new Error('NebuchadnezzarKey: no state to persist.');
|
|
142
|
+
}
|
|
143
|
+
await safePersist(this.statePath, JSON.stringify(this.state, null, 2));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Check if public key has been generated/loaded.
|
|
147
|
+
*/
|
|
148
|
+
isInitialized() {
|
|
149
|
+
return this.state !== null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get current state (or null if not initialized).
|
|
153
|
+
*/
|
|
154
|
+
getState() {
|
|
155
|
+
if (this.state === null)
|
|
156
|
+
return null;
|
|
157
|
+
return { ...this.state };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Confidence multiplier based on consecutive override count.
|
|
161
|
+
* 1 override = 1.0, 2 consecutive = 0.9, 3+ consecutive = 0.7
|
|
162
|
+
*/
|
|
163
|
+
getConfidenceMultiplier() {
|
|
164
|
+
if (this.state === null)
|
|
165
|
+
return 1.0;
|
|
166
|
+
const c = this.state.consecutiveOverrides;
|
|
167
|
+
if (c <= 1)
|
|
168
|
+
return 1.0;
|
|
169
|
+
if (c === 2)
|
|
170
|
+
return 0.9;
|
|
171
|
+
return 0.7;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Static helper: sign a message for an override request.
|
|
175
|
+
* Returns hex-encoded Ed25519 signature.
|
|
176
|
+
*/
|
|
177
|
+
/** Generate a fresh nonce for an override request. */
|
|
178
|
+
static generateNonce() {
|
|
179
|
+
return randomUUID();
|
|
180
|
+
}
|
|
181
|
+
evictExpiredNonces() {
|
|
182
|
+
const now = Date.now();
|
|
183
|
+
for (const [nonce, expiresAt] of this.usedNonces) {
|
|
184
|
+
if (expiresAt <= now)
|
|
185
|
+
this.usedNonces.delete(nonce);
|
|
186
|
+
}
|
|
187
|
+
if (this.usedNonces.size > MAX_NONCE_CACHE) {
|
|
188
|
+
this.usedNonces.clear();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
static sign(privateKeyHex, deliberationId, newDecision, reason, timestamp, nonce) {
|
|
192
|
+
const message = `${deliberationId}:${newDecision}:${reason}:${timestamp}:${nonce}`;
|
|
193
|
+
const privateKeyDer = Buffer.from(privateKeyHex, 'hex');
|
|
194
|
+
try {
|
|
195
|
+
const signature = cryptoSign(null, // Ed25519 does not use a separate hash algorithm
|
|
196
|
+
Buffer.from(message, 'utf-8'), { key: privateKeyDer, format: 'der', type: 'pkcs8' });
|
|
197
|
+
return signature.toString('hex');
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
privateKeyDer.fill(0);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B-10 Neon Genesis (世界のやり直し)
|
|
3
|
+
*
|
|
4
|
+
* MAGIシステムの完全リセット。全審議記録を消去し、
|
|
5
|
+
* 選ばれた記憶のみを次世代に引き継ぐ。
|
|
6
|
+
*
|
|
7
|
+
* 処理フロー:
|
|
8
|
+
* 1. genesis:started イベント発火
|
|
9
|
+
* 2. preResetDeliberation — MAGI自身が消滅を議論 (optional)
|
|
10
|
+
* 3. collectSnapshot — 審議統計収集
|
|
11
|
+
* 4. selectSurvivalMemories — 10% Fisher-Yatesシャッフル選択
|
|
12
|
+
* 5. .cipher ファイル退避
|
|
13
|
+
* 6. purge — .magi/ 配下削除 (genesis-log.json + .cipher 除外)
|
|
14
|
+
* 7. .cipher 復元
|
|
15
|
+
* 8. writeGenesisLog — genesis-log.json に痕跡記録
|
|
16
|
+
* 9. genesis:complete イベント発火
|
|
17
|
+
*/
|
|
18
|
+
import type { GenesisLog } from '../types/phase-l.js';
|
|
19
|
+
import type { MagiEventBus } from './events.js';
|
|
20
|
+
export declare class NeonGenesis {
|
|
21
|
+
private readonly workspaceDir;
|
|
22
|
+
private readonly eventBus;
|
|
23
|
+
constructor(workspaceDir: string, eventBus?: MagiEventBus);
|
|
24
|
+
/**
|
|
25
|
+
* Execute the Neon Genesis — full system reset.
|
|
26
|
+
*/
|
|
27
|
+
execute(reason: string, initiator: string, force?: boolean): Promise<GenesisLog>;
|
|
28
|
+
/**
|
|
29
|
+
* Collect snapshot of current MAGI state from chronicle and rounds.
|
|
30
|
+
*/
|
|
31
|
+
private collectSnapshot;
|
|
32
|
+
/**
|
|
33
|
+
* Load semantic patterns from engram store.
|
|
34
|
+
*/
|
|
35
|
+
private loadSemanticPatterns;
|
|
36
|
+
/**
|
|
37
|
+
* Select survival memories using Fisher-Yates shuffle at given rate.
|
|
38
|
+
*/
|
|
39
|
+
selectSurvivalMemories(patterns: string[], rate?: number): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Purge the .magi/ workspace directory, preserving specified files.
|
|
42
|
+
*/
|
|
43
|
+
private purge;
|
|
44
|
+
/**
|
|
45
|
+
* Write genesis log to workspace.
|
|
46
|
+
*/
|
|
47
|
+
private writeGenesisLog;
|
|
48
|
+
/**
|
|
49
|
+
* Load existing genesis log, if any.
|
|
50
|
+
*/
|
|
51
|
+
loadGenesisLog(): Promise<GenesisLog | null>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B-10 Neon Genesis (世界のやり直し)
|
|
3
|
+
*
|
|
4
|
+
* MAGIシステムの完全リセット。全審議記録を消去し、
|
|
5
|
+
* 選ばれた記憶のみを次世代に引き継ぐ。
|
|
6
|
+
*
|
|
7
|
+
* 処理フロー:
|
|
8
|
+
* 1. genesis:started イベント発火
|
|
9
|
+
* 2. preResetDeliberation — MAGI自身が消滅を議論 (optional)
|
|
10
|
+
* 3. collectSnapshot — 審議統計収集
|
|
11
|
+
* 4. selectSurvivalMemories — 10% Fisher-Yatesシャッフル選択
|
|
12
|
+
* 5. .cipher ファイル退避
|
|
13
|
+
* 6. purge — .magi/ 配下削除 (genesis-log.json + .cipher 除外)
|
|
14
|
+
* 7. .cipher 復元
|
|
15
|
+
* 8. writeGenesisLog — genesis-log.json に痕跡記録
|
|
16
|
+
* 9. genesis:complete イベント発火
|
|
17
|
+
*/
|
|
18
|
+
import { randomUUID, randomInt } from 'node:crypto';
|
|
19
|
+
import { readFile, readdir, rm, copyFile } from 'node:fs/promises';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { logger } from '../utils/logger.js';
|
|
22
|
+
import { safeMkdir, safeWriteFile } from '../utils/safe-fs.js';
|
|
23
|
+
import { safeJsonParse } from '../utils/safe-json-parse.js';
|
|
24
|
+
const GENESIS_LOG_FILE = 'genesis-log.json';
|
|
25
|
+
const CIPHER_FILE = '.cipher';
|
|
26
|
+
const SURVIVAL_RATE = 0.10; // 10% of semantic patterns survive
|
|
27
|
+
export class NeonGenesis {
|
|
28
|
+
workspaceDir;
|
|
29
|
+
eventBus;
|
|
30
|
+
constructor(workspaceDir, eventBus) {
|
|
31
|
+
this.workspaceDir = workspaceDir;
|
|
32
|
+
this.eventBus = eventBus;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Execute the Neon Genesis — full system reset.
|
|
36
|
+
*/
|
|
37
|
+
async execute(reason, initiator, force = false) {
|
|
38
|
+
if (!force) {
|
|
39
|
+
throw new Error('Neon Genesis aborted: --force flag required. This permanently deletes all deliberation data.');
|
|
40
|
+
}
|
|
41
|
+
const resetId = randomUUID();
|
|
42
|
+
logger.warn('Neon Genesis initiated', { resetId, reason, initiator });
|
|
43
|
+
this.eventBus?.emit('genesis:started', {
|
|
44
|
+
resetId,
|
|
45
|
+
reason,
|
|
46
|
+
emittedAt: new Date(),
|
|
47
|
+
});
|
|
48
|
+
// Collect snapshot before purge
|
|
49
|
+
const snapshot = await this.collectSnapshot();
|
|
50
|
+
// Select survival memories from engram patterns
|
|
51
|
+
const patterns = await this.loadSemanticPatterns();
|
|
52
|
+
const survivalMemories = this.selectSurvivalMemories(patterns, SURVIVAL_RATE);
|
|
53
|
+
// Check for cipher file
|
|
54
|
+
const cipherPath = join(this.workspaceDir, CIPHER_FILE);
|
|
55
|
+
const cipherBackupPath = join(this.workspaceDir, `.cipher-backup-${resetId}`);
|
|
56
|
+
let cipherPreserved = false;
|
|
57
|
+
// Backup cipher if it exists
|
|
58
|
+
try {
|
|
59
|
+
await copyFile(cipherPath, cipherBackupPath);
|
|
60
|
+
cipherPreserved = true;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
logger.debug('Neon Genesis: no cipher file to backup', { error: String(err) });
|
|
64
|
+
}
|
|
65
|
+
// Purge workspace
|
|
66
|
+
await this.purge([GENESIS_LOG_FILE, `.cipher-backup-${resetId}`]);
|
|
67
|
+
// Restore cipher
|
|
68
|
+
if (cipherPreserved) {
|
|
69
|
+
try {
|
|
70
|
+
await safeMkdir(this.workspaceDir);
|
|
71
|
+
await copyFile(cipherBackupPath, cipherPath);
|
|
72
|
+
await rm(cipherBackupPath, { force: true });
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
logger.warn('Failed to restore cipher file after genesis');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Write genesis log
|
|
79
|
+
const log = {
|
|
80
|
+
resetId,
|
|
81
|
+
previousGeneration: snapshot.totalDeliberations,
|
|
82
|
+
totalDeliberations: snapshot.totalDeliberations,
|
|
83
|
+
survivalMemories,
|
|
84
|
+
cipherMessagePreserved: cipherPreserved,
|
|
85
|
+
lastDeliberation: snapshot.lastDeliberation,
|
|
86
|
+
resetAt: new Date().toISOString(),
|
|
87
|
+
initiator,
|
|
88
|
+
};
|
|
89
|
+
await this.writeGenesisLog(log);
|
|
90
|
+
logger.warn('Neon Genesis complete', {
|
|
91
|
+
resetId,
|
|
92
|
+
survived: survivalMemories.length,
|
|
93
|
+
cipherPreserved,
|
|
94
|
+
});
|
|
95
|
+
this.eventBus?.emit('genesis:complete', {
|
|
96
|
+
resetId,
|
|
97
|
+
survivalMemories: survivalMemories.length,
|
|
98
|
+
cipherPreserved,
|
|
99
|
+
emittedAt: new Date(),
|
|
100
|
+
});
|
|
101
|
+
return log;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Collect snapshot of current MAGI state from chronicle and rounds.
|
|
105
|
+
*/
|
|
106
|
+
async collectSnapshot() {
|
|
107
|
+
let totalDeliberations = 0;
|
|
108
|
+
let lastDeliberation = null;
|
|
109
|
+
// Read chronicle.jsonl
|
|
110
|
+
const chroniclePath = join(this.workspaceDir, 'gospel', 'chronicle.jsonl');
|
|
111
|
+
try {
|
|
112
|
+
const raw = await readFile(chroniclePath, 'utf-8');
|
|
113
|
+
const lines = raw.trim().split('\n').filter(Boolean);
|
|
114
|
+
totalDeliberations = lines.length;
|
|
115
|
+
if (lines.length > 0) {
|
|
116
|
+
try {
|
|
117
|
+
const lastEntry = safeJsonParse(lines[lines.length - 1]);
|
|
118
|
+
lastDeliberation = lastEntry.timestamp ?? lastEntry.recordedAt ?? null;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
logger.debug('Neon Genesis: corrupt chronicle last line', { error: String(err) });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
logger.debug('Neon Genesis: no chronicle found, fresh system', { error: String(err) });
|
|
127
|
+
}
|
|
128
|
+
return { totalDeliberations, lastDeliberation };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Load semantic patterns from engram store.
|
|
132
|
+
*/
|
|
133
|
+
async loadSemanticPatterns() {
|
|
134
|
+
const engramPath = join(this.workspaceDir, 'engram', 'semantic.json');
|
|
135
|
+
try {
|
|
136
|
+
const raw = await readFile(engramPath, 'utf-8');
|
|
137
|
+
const patterns = safeJsonParse(raw);
|
|
138
|
+
if (Array.isArray(patterns)) {
|
|
139
|
+
return patterns.map((p) => p.pattern ?? p.description ?? JSON.stringify(p));
|
|
140
|
+
}
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
logger.debug('Neon Genesis: failed to load semantic patterns', { error: String(err) });
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Select survival memories using Fisher-Yates shuffle at given rate.
|
|
150
|
+
*/
|
|
151
|
+
selectSurvivalMemories(patterns, rate = SURVIVAL_RATE) {
|
|
152
|
+
if (patterns.length === 0)
|
|
153
|
+
return [];
|
|
154
|
+
const count = Math.max(1, Math.ceil(patterns.length * rate));
|
|
155
|
+
const shuffled = [...patterns];
|
|
156
|
+
// Fisher-Yates shuffle (partial — only need first `count` elements)
|
|
157
|
+
for (let i = shuffled.length - 1; i > 0 && (shuffled.length - 1 - i) < count; i--) {
|
|
158
|
+
const j = randomInt(i + 1);
|
|
159
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
160
|
+
}
|
|
161
|
+
return shuffled.slice(-count);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Purge the .magi/ workspace directory, preserving specified files.
|
|
165
|
+
*/
|
|
166
|
+
async purge(preserveFiles) {
|
|
167
|
+
try {
|
|
168
|
+
const entries = await readdir(this.workspaceDir, { withFileTypes: true });
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
const name = entry.name;
|
|
171
|
+
if (preserveFiles.includes(name) || name === CIPHER_FILE)
|
|
172
|
+
continue;
|
|
173
|
+
const fullPath = join(this.workspaceDir, name);
|
|
174
|
+
await rm(fullPath, { recursive: true, force: true });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
logger.debug('Neon Genesis: workspace not found, nothing to purge', { error: String(err) });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Write genesis log to workspace.
|
|
183
|
+
*/
|
|
184
|
+
async writeGenesisLog(log) {
|
|
185
|
+
await safeMkdir(this.workspaceDir);
|
|
186
|
+
const logPath = join(this.workspaceDir, GENESIS_LOG_FILE);
|
|
187
|
+
await safeWriteFile(logPath, JSON.stringify(log, null, 2));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Load existing genesis log, if any.
|
|
191
|
+
*/
|
|
192
|
+
async loadGenesisLog() {
|
|
193
|
+
try {
|
|
194
|
+
const logPath = join(this.workspaceDir, GENESIS_LOG_FILE);
|
|
195
|
+
const raw = await readFile(logPath, 'utf-8');
|
|
196
|
+
return safeJsonParse(raw);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
logger.debug('Neon Genesis: genesis log not found', { error: String(err) });
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Result from running a single tool */
|
|
2
|
+
export interface ToolResult {
|
|
3
|
+
tool: 'tsc' | 'eslint' | 'vitest';
|
|
4
|
+
available: boolean;
|
|
5
|
+
passed: boolean;
|
|
6
|
+
issues: ToolIssue[];
|
|
7
|
+
summary: string;
|
|
8
|
+
rawOutput?: string;
|
|
9
|
+
}
|
|
10
|
+
/** A single issue found by a tool */
|
|
11
|
+
export interface ToolIssue {
|
|
12
|
+
file?: string;
|
|
13
|
+
line?: number;
|
|
14
|
+
severity: 'error' | 'warning' | 'info';
|
|
15
|
+
message: string;
|
|
16
|
+
rule?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Combined result from all tools */
|
|
19
|
+
export interface ObjectiveResult {
|
|
20
|
+
score: number;
|
|
21
|
+
tools: ToolResult[];
|
|
22
|
+
summary: string;
|
|
23
|
+
evaluatedAt: Date;
|
|
24
|
+
}
|
|
25
|
+
/** Command runner function signature */
|
|
26
|
+
type RunCommandFn = (cmd: string, args: string[], cwd: string) => Promise<{
|
|
27
|
+
stdout: string;
|
|
28
|
+
stderr: string;
|
|
29
|
+
exitCode: number;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* ObjectiveJudge: runs static analysis and tests to produce an objective
|
|
33
|
+
* quality score that supplements the subjective AI opinions.
|
|
34
|
+
*
|
|
35
|
+
* Graceful degradation: if a tool is not available, it's skipped and
|
|
36
|
+
* the score is calculated from available tools only.
|
|
37
|
+
*/
|
|
38
|
+
export declare class ObjectiveJudge {
|
|
39
|
+
private readonly cwd;
|
|
40
|
+
private readonly runCommand;
|
|
41
|
+
constructor(cwd: string, runCommand?: RunCommandFn);
|
|
42
|
+
private static defaultRunCommand;
|
|
43
|
+
evaluate(): Promise<ObjectiveResult>;
|
|
44
|
+
calculateScore(tools: ToolResult[]): number;
|
|
45
|
+
private buildSummary;
|
|
46
|
+
private runTsc;
|
|
47
|
+
private runEslint;
|
|
48
|
+
private runVitest;
|
|
49
|
+
parseTscOutput(output: string): ToolIssue[];
|
|
50
|
+
parseEslintOutput(output: string): ToolIssue[];
|
|
51
|
+
parseVitestOutput(output: string): ToolIssue[];
|
|
52
|
+
}
|
|
53
|
+
export {};
|