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,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiline input support for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
* 1. `/paste` command: opens a collector that reads lines until "." on its own line.
|
|
6
|
+
* 2. Trailing backslash (`\`): lightweight continuation for 2-3 line inputs.
|
|
7
|
+
*/
|
|
8
|
+
/** Maximum number of lines in a multiline buffer (DoS prevention). */
|
|
9
|
+
export declare const MAX_MULTILINE_LINES = 10000;
|
|
10
|
+
/** Maximum number of continuation lines (trailing backslash). */
|
|
11
|
+
export declare const MAX_CONTINUATION_LINES = 100;
|
|
12
|
+
export type MultilineResult = {
|
|
13
|
+
status: 'collecting';
|
|
14
|
+
} | {
|
|
15
|
+
status: 'complete';
|
|
16
|
+
text: string;
|
|
17
|
+
} | {
|
|
18
|
+
status: 'aborted';
|
|
19
|
+
};
|
|
20
|
+
export interface MultilineCollector {
|
|
21
|
+
/** Feed a line to the collector. Returns the current state. */
|
|
22
|
+
feed(line: string): MultilineResult;
|
|
23
|
+
/** Get the current buffer (read-only). */
|
|
24
|
+
getBuffer(): readonly string[];
|
|
25
|
+
/** Reset the collector for reuse. */
|
|
26
|
+
reset(): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create a multiline collector for `/paste` mode.
|
|
30
|
+
*
|
|
31
|
+
* - Single "." line → complete (join all previous lines)
|
|
32
|
+
* - "/abort" → aborted
|
|
33
|
+
* - Anything else → collecting
|
|
34
|
+
*/
|
|
35
|
+
export declare function createMultilineCollector(): MultilineCollector;
|
|
36
|
+
/**
|
|
37
|
+
* Whether a line ends with a single backslash (continuation marker).
|
|
38
|
+
* Double backslash `\\` at end is an escaped backslash, not continuation.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isContinuationLine(line: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Join continuation lines: strip trailing backslash from each line
|
|
43
|
+
* except the last, then join with newline.
|
|
44
|
+
*/
|
|
45
|
+
export declare function joinContinuationLines(lines: string[]): string;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multiline input support for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
* 1. `/paste` command: opens a collector that reads lines until "." on its own line.
|
|
6
|
+
* 2. Trailing backslash (`\`): lightweight continuation for 2-3 line inputs.
|
|
7
|
+
*/
|
|
8
|
+
/** Maximum number of lines in a multiline buffer (DoS prevention). */
|
|
9
|
+
export const MAX_MULTILINE_LINES = 10_000;
|
|
10
|
+
/** Maximum number of continuation lines (trailing backslash). */
|
|
11
|
+
export const MAX_CONTINUATION_LINES = 100;
|
|
12
|
+
/**
|
|
13
|
+
* Create a multiline collector for `/paste` mode.
|
|
14
|
+
*
|
|
15
|
+
* - Single "." line → complete (join all previous lines)
|
|
16
|
+
* - "/abort" → aborted
|
|
17
|
+
* - Anything else → collecting
|
|
18
|
+
*/
|
|
19
|
+
export function createMultilineCollector() {
|
|
20
|
+
let buffer = [];
|
|
21
|
+
return {
|
|
22
|
+
feed(line) {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (trimmed === '/abort') {
|
|
25
|
+
buffer = [];
|
|
26
|
+
return { status: 'aborted' };
|
|
27
|
+
}
|
|
28
|
+
if (trimmed === '.') {
|
|
29
|
+
const text = buffer.join('\n');
|
|
30
|
+
buffer = [];
|
|
31
|
+
return { status: 'complete', text };
|
|
32
|
+
}
|
|
33
|
+
if (buffer.length >= MAX_MULTILINE_LINES) {
|
|
34
|
+
buffer = [];
|
|
35
|
+
return { status: 'aborted' };
|
|
36
|
+
}
|
|
37
|
+
buffer.push(line);
|
|
38
|
+
return { status: 'collecting' };
|
|
39
|
+
},
|
|
40
|
+
getBuffer() {
|
|
41
|
+
return buffer;
|
|
42
|
+
},
|
|
43
|
+
reset() {
|
|
44
|
+
buffer = [];
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Whether a line ends with a single backslash (continuation marker).
|
|
50
|
+
* Double backslash `\\` at end is an escaped backslash, not continuation.
|
|
51
|
+
*/
|
|
52
|
+
export function isContinuationLine(line) {
|
|
53
|
+
const trimmed = line.trimEnd();
|
|
54
|
+
if (!trimmed.endsWith('\\'))
|
|
55
|
+
return false;
|
|
56
|
+
// Count trailing backslashes
|
|
57
|
+
let count = 0;
|
|
58
|
+
for (let i = trimmed.length - 1; i >= 0 && trimmed[i] === '\\'; i--) {
|
|
59
|
+
count++;
|
|
60
|
+
}
|
|
61
|
+
// Odd number of trailing backslashes → continuation
|
|
62
|
+
return count % 2 === 1;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Join continuation lines: strip trailing backslash from each line
|
|
66
|
+
* except the last, then join with newline.
|
|
67
|
+
*/
|
|
68
|
+
export function joinContinuationLines(lines) {
|
|
69
|
+
return lines
|
|
70
|
+
.map((line, i) => {
|
|
71
|
+
if (i < lines.length - 1) {
|
|
72
|
+
// Remove the trailing continuation backslash
|
|
73
|
+
return line.trimEnd().replace(/\\$/, '');
|
|
74
|
+
}
|
|
75
|
+
return line;
|
|
76
|
+
})
|
|
77
|
+
.join('\n');
|
|
78
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic prompt builder for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Renders session state into the prompt string:
|
|
5
|
+
* MAGI[01|NORM|3/3|CTX:2]>
|
|
6
|
+
* MAGI[02|BER|3/3|CTX:3]> (BERSERK — deep red)
|
|
7
|
+
* MAGI[03|NORM|2/3|CTX:1|!]> (error indicator)
|
|
8
|
+
*/
|
|
9
|
+
export interface SessionState {
|
|
10
|
+
deliberationCount: number;
|
|
11
|
+
berserkActive: boolean;
|
|
12
|
+
onlineUnits: number;
|
|
13
|
+
totalUnits: number;
|
|
14
|
+
contextEntryCount: number;
|
|
15
|
+
hasError: boolean;
|
|
16
|
+
backgroundJobCount: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const DEFAULT_SESSION_STATE: SessionState;
|
|
19
|
+
export declare function buildPrompt(state: SessionState): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic prompt builder for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Renders session state into the prompt string:
|
|
5
|
+
* MAGI[01|NORM|3/3|CTX:2]>
|
|
6
|
+
* MAGI[02|BER|3/3|CTX:3]> (BERSERK — deep red)
|
|
7
|
+
* MAGI[03|NORM|2/3|CTX:1|!]> (error indicator)
|
|
8
|
+
*/
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { EVA_PALETTE } from '../tui/colors.js';
|
|
11
|
+
const { frame, abstain, melchior } = EVA_PALETTE;
|
|
12
|
+
const FRAME_CHALK = chalk.rgb(frame.r, frame.g, frame.b);
|
|
13
|
+
const DIM_CHALK = chalk.rgb(abstain.r, abstain.g, abstain.b);
|
|
14
|
+
const BERSERK_CHALK = chalk.rgb(melchior.r, melchior.g, melchior.b);
|
|
15
|
+
export const DEFAULT_SESSION_STATE = {
|
|
16
|
+
deliberationCount: 0,
|
|
17
|
+
berserkActive: false,
|
|
18
|
+
onlineUnits: 3,
|
|
19
|
+
totalUnits: 3,
|
|
20
|
+
contextEntryCount: 0,
|
|
21
|
+
hasError: false,
|
|
22
|
+
backgroundJobCount: 0,
|
|
23
|
+
};
|
|
24
|
+
export function buildPrompt(state) {
|
|
25
|
+
const count = String(state.deliberationCount).padStart(2, '0');
|
|
26
|
+
const mode = state.berserkActive ? 'BER' : 'NORM';
|
|
27
|
+
const units = `${state.onlineUnits}/${state.totalUnits}`;
|
|
28
|
+
const ctx = `CTX:${state.contextEntryCount}`;
|
|
29
|
+
const jobFlag = state.backgroundJobCount > 0 ? `|J:${state.backgroundJobCount}` : '';
|
|
30
|
+
const errorFlag = state.hasError ? '|!' : '';
|
|
31
|
+
const info = `${count}|${mode}|${units}|${ctx}${jobFlag}${errorFlag}`;
|
|
32
|
+
if (state.berserkActive) {
|
|
33
|
+
return BERSERK_CHALK.bold('MAGI') + BERSERK_CHALK(`[${info}]`) + BERSERK_CHALK('> ');
|
|
34
|
+
}
|
|
35
|
+
return FRAME_CHALK.bold('MAGI') + DIM_CHALK(`[${info}]`) + DIM_CHALK('> ');
|
|
36
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL state machine — enforces valid state transitions.
|
|
3
|
+
*/
|
|
4
|
+
export type ReplState = 'INIT' | 'READY' | 'READING_INPUT' | 'MULTILINE_COLLECTING' | 'DISPATCHING' | 'CONFIRMING' | 'DELIBERATING' | 'SHOWING_RESULT' | 'DISPOSING';
|
|
5
|
+
export declare function assertTransition(from: ReplState, to: ReplState): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REPL state machine — enforces valid state transitions.
|
|
3
|
+
*/
|
|
4
|
+
const VALID_TRANSITIONS = {
|
|
5
|
+
INIT: ['READY', 'DISPOSING'],
|
|
6
|
+
READY: ['READING_INPUT', 'DELIBERATING', 'DISPOSING'],
|
|
7
|
+
READING_INPUT: ['DISPATCHING', 'MULTILINE_COLLECTING', 'READY', 'DISPOSING'],
|
|
8
|
+
MULTILINE_COLLECTING: ['DISPATCHING', 'READING_INPUT', 'DISPOSING'],
|
|
9
|
+
DISPATCHING: ['DELIBERATING', 'CONFIRMING', 'MULTILINE_COLLECTING', 'READING_INPUT', 'READY', 'DISPOSING'],
|
|
10
|
+
CONFIRMING: ['DISPATCHING', 'READING_INPUT', 'READY', 'DISPOSING'],
|
|
11
|
+
DELIBERATING: ['SHOWING_RESULT', 'READY', 'DISPOSING'],
|
|
12
|
+
SHOWING_RESULT: ['READY', 'DISPOSING'],
|
|
13
|
+
DISPOSING: [],
|
|
14
|
+
};
|
|
15
|
+
export function assertTransition(from, to) {
|
|
16
|
+
if (!VALID_TRANSITIONS[from].includes(to)) {
|
|
17
|
+
throw new Error(`Invalid REPL state transition: ${from} → ${to}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich result display for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Renders a box-drawn CONSENSUS summary with per-unit vote bars,
|
|
5
|
+
* key points, dissent, and EVA_PALETTE colors.
|
|
6
|
+
*/
|
|
7
|
+
import type { MagiDeliberation } from '../types/core.js';
|
|
8
|
+
export declare function formatResultDisplay(result: MagiDeliberation): string;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich result display for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Renders a box-drawn CONSENSUS summary with per-unit vote bars,
|
|
5
|
+
* key points, dissent, and EVA_PALETTE colors.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { EVA_PALETTE, unitChalk } from '../tui/colors.js';
|
|
9
|
+
import { stringDisplayWidth } from '../tui/screen-buffer.js';
|
|
10
|
+
import { isUnanimous, getMajorityVote } from '../types/consensus.js';
|
|
11
|
+
import { sanitizeForTerminal } from './terminal-sanitize.js';
|
|
12
|
+
import { voteSymbol } from './accessibility.js';
|
|
13
|
+
import { getResponsiveBoxWidth, padVisibleRight, truncateVisible, visibleWidth, wrapDisplayText, } from './display-utils.js';
|
|
14
|
+
const { frame, approve, reject, abstain } = EVA_PALETTE;
|
|
15
|
+
const FRAME = chalk.rgb(frame.r, frame.g, frame.b);
|
|
16
|
+
const APPROVE_C = chalk.rgb(approve.r, approve.g, approve.b);
|
|
17
|
+
const REJECT_C = chalk.rgb(reject.r, reject.g, reject.b);
|
|
18
|
+
const ABSTAIN_C = chalk.rgb(abstain.r, abstain.g, abstain.b);
|
|
19
|
+
const DIM = chalk.rgb(abstain.r, abstain.g, abstain.b);
|
|
20
|
+
const BAR_WIDTH = 10;
|
|
21
|
+
const FILLED = '█';
|
|
22
|
+
const EMPTY = '░';
|
|
23
|
+
// ── Helpers ──────────────────────────────────────────────────
|
|
24
|
+
function confidenceBar(confidence, vote) {
|
|
25
|
+
const filled = Math.round(confidence * BAR_WIDTH);
|
|
26
|
+
const empty = BAR_WIDTH - filled;
|
|
27
|
+
const bar = FILLED.repeat(filled) + EMPTY.repeat(empty);
|
|
28
|
+
const pct = `${Math.round(confidence * 100)}%`;
|
|
29
|
+
const color = vote === 'APPROVE' ? APPROVE_C
|
|
30
|
+
: vote === 'REJECT' ? REJECT_C
|
|
31
|
+
: ABSTAIN_C;
|
|
32
|
+
return color(bar) + DIM(` ${pct}`);
|
|
33
|
+
}
|
|
34
|
+
function confidenceBarWidth(confidence) {
|
|
35
|
+
const pct = `${Math.round(confidence * 100)}%`;
|
|
36
|
+
return BAR_WIDTH + 1 + stringDisplayWidth(pct);
|
|
37
|
+
}
|
|
38
|
+
function verdictLabel(decision) {
|
|
39
|
+
if (decision.startsWith('UNANIMOUS_APPROVE') || decision === 'MAJORITY_APPROVE') {
|
|
40
|
+
return '可 決';
|
|
41
|
+
}
|
|
42
|
+
if (decision.startsWith('UNANIMOUS_REJECT') || decision === 'MAJORITY_REJECT') {
|
|
43
|
+
return '否 決';
|
|
44
|
+
}
|
|
45
|
+
return '保 留';
|
|
46
|
+
}
|
|
47
|
+
function padRight(text, width) {
|
|
48
|
+
const displayWidth = stringDisplayWidth(text);
|
|
49
|
+
const padding = Math.max(0, width - displayWidth);
|
|
50
|
+
return text + ' '.repeat(padding);
|
|
51
|
+
}
|
|
52
|
+
function frameLine(content, innerWidth) {
|
|
53
|
+
return FRAME(' ║ ') + padVisibleRight(content, innerWidth) + FRAME(' ║');
|
|
54
|
+
}
|
|
55
|
+
function renderWrappedBlock(prefix, text, innerWidth, color = DIM) {
|
|
56
|
+
const prefixWidth = stringDisplayWidth(prefix);
|
|
57
|
+
const wrapped = wrapDisplayText(text, Math.max(1, innerWidth - prefixWidth));
|
|
58
|
+
return wrapped.map((line, index) => frameLine(color(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`), innerWidth));
|
|
59
|
+
}
|
|
60
|
+
function renderOpinionLine(unit, vote, conf, innerWidth) {
|
|
61
|
+
const sym = voteSymbol(vote);
|
|
62
|
+
if (innerWidth >= 38) {
|
|
63
|
+
const unitLabel = unitChalk(unit)(padRight(unit, 12));
|
|
64
|
+
const voteLabel = vote === 'APPROVE' ? APPROVE_C(padRight(`${sym} APPROVE`, 11))
|
|
65
|
+
: vote === 'REJECT' ? REJECT_C(padRight(`${sym} REJECT`, 11))
|
|
66
|
+
: ABSTAIN_C(padRight(`${sym} ABSTAIN`, 11));
|
|
67
|
+
const bar = confidenceBar(conf, vote);
|
|
68
|
+
const spare = Math.max(1, innerWidth - 12 - 11 - confidenceBarWidth(conf));
|
|
69
|
+
return [frameLine(unitLabel + voteLabel + bar + ' '.repeat(spare), innerWidth)];
|
|
70
|
+
}
|
|
71
|
+
const compactVote = vote === 'APPROVE' ? APPROVE_C(`${sym} APPROVE`)
|
|
72
|
+
: vote === 'REJECT' ? REJECT_C(`${sym} REJECT`)
|
|
73
|
+
: ABSTAIN_C(`${sym} ABSTAIN`);
|
|
74
|
+
return [
|
|
75
|
+
frameLine(unitChalk(unit)(truncateVisible(unit, Math.max(1, innerWidth - visibleWidth(compactVote) - 1))) + ' ' + compactVote, innerWidth),
|
|
76
|
+
frameLine(confidenceBar(conf, vote), innerWidth),
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
function renderHeader(decision, confidence, innerWidth) {
|
|
80
|
+
const confPct = `CONF ${Math.round(confidence * 100)}%`;
|
|
81
|
+
const verdict = verdictLabel(decision);
|
|
82
|
+
if (innerWidth >= 32) {
|
|
83
|
+
const headerRight = isUnanimous(decision) ? `${verdict} ${confPct}` : confPct;
|
|
84
|
+
const decisionLabel = truncateVisible(`MAGI DECISION: ${decision}`, Math.max(1, innerWidth - stringDisplayWidth(headerRight) - 1));
|
|
85
|
+
const headerPadding = Math.max(1, innerWidth - stringDisplayWidth(decisionLabel) - stringDisplayWidth(headerRight));
|
|
86
|
+
return [frameLine(chalk.bold(decisionLabel) + ' '.repeat(headerPadding) + DIM(headerRight), innerWidth)];
|
|
87
|
+
}
|
|
88
|
+
return [
|
|
89
|
+
frameLine(chalk.bold(truncateVisible(`MAGI ${decision}`, innerWidth)), innerWidth),
|
|
90
|
+
frameLine(DIM(isUnanimous(decision) ? `${verdict} ${confPct}` : confPct), innerWidth),
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
// ── Main export ──────────────────────────────────────────────
|
|
94
|
+
export function formatResultDisplay(result) {
|
|
95
|
+
const totalWidth = getResponsiveBoxWidth(80, 24);
|
|
96
|
+
const innerWidth = totalWidth - 8; // " ║ " + content + " ║"
|
|
97
|
+
const { consensus, rounds, totalDurationMs } = result;
|
|
98
|
+
const { decision, confidence, summary } = consensus;
|
|
99
|
+
const lines = [];
|
|
100
|
+
// ── Top border
|
|
101
|
+
lines.push(FRAME(` ╔${'═'.repeat(totalWidth - 4)}╗`));
|
|
102
|
+
// ── Decision header
|
|
103
|
+
lines.push(...renderHeader(decision, confidence, innerWidth));
|
|
104
|
+
// ── Separator
|
|
105
|
+
lines.push(FRAME(` ╠${'═'.repeat(totalWidth - 4)}╣`));
|
|
106
|
+
// ── Duration + rounds
|
|
107
|
+
const durSec = (totalDurationMs / 1000).toFixed(1);
|
|
108
|
+
const durLine = `Duration: ${durSec}s Rounds: ${rounds.length}`;
|
|
109
|
+
for (const line of wrapDisplayText(durLine, innerWidth)) {
|
|
110
|
+
lines.push(frameLine(DIM(line), innerWidth));
|
|
111
|
+
}
|
|
112
|
+
// ── Thin separator
|
|
113
|
+
lines.push(FRAME(` ╠${'─'.repeat(totalWidth - 4)}╣`));
|
|
114
|
+
// ── Per-unit votes (from last round)
|
|
115
|
+
const lastRound = rounds[rounds.length - 1];
|
|
116
|
+
if (lastRound?.opinions) {
|
|
117
|
+
for (const op of lastRound.opinions) {
|
|
118
|
+
lines.push(...renderOpinionLine(op.unit, op.vote, op.confidence ?? 0, innerWidth));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ── Thin separator
|
|
122
|
+
lines.push(FRAME(` ╠${'─'.repeat(totalWidth - 4)}╣`));
|
|
123
|
+
// ── Summary
|
|
124
|
+
if (summary) {
|
|
125
|
+
lines.push(...renderWrappedBlock('Summary: ', sanitizeForTerminal(summary), innerWidth));
|
|
126
|
+
}
|
|
127
|
+
// ── Key points (deduplicated, max 3)
|
|
128
|
+
const keyPoints = extractKeyPoints(result);
|
|
129
|
+
if (keyPoints.length > 0) {
|
|
130
|
+
lines.push(frameLine(DIM(padRight('Key Points:', innerWidth)), innerWidth));
|
|
131
|
+
for (const kp of keyPoints) {
|
|
132
|
+
lines.push(...renderWrappedBlock('⬢ ', sanitizeForTerminal(kp), innerWidth));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ── Dissent
|
|
136
|
+
const dissent = extractDissent(result);
|
|
137
|
+
if (dissent) {
|
|
138
|
+
lines.push(FRAME(` ╠${'─'.repeat(totalWidth - 4)}╣`));
|
|
139
|
+
lines.push(frameLine(DIM(padRight(`Dissent (${dissent.unit}):`, innerWidth)), innerWidth));
|
|
140
|
+
lines.push(...renderWrappedBlock('', sanitizeForTerminal(dissent.text), innerWidth));
|
|
141
|
+
}
|
|
142
|
+
// ── DEADLOCK resolution
|
|
143
|
+
if (decision === 'DEADLOCK' && consensus.method) {
|
|
144
|
+
lines.push(FRAME(` ╠${'─'.repeat(totalWidth - 4)}╣`));
|
|
145
|
+
lines.push(...renderWrappedBlock('Resolution: ', String(consensus.method), innerWidth));
|
|
146
|
+
}
|
|
147
|
+
// ── Bottom border with verdict
|
|
148
|
+
if (isUnanimous(decision)) {
|
|
149
|
+
const vl = verdictLabel(decision);
|
|
150
|
+
const bottomInner = `── ${vl} ──`;
|
|
151
|
+
const bottomPad = Math.max(0, totalWidth - 4 - stringDisplayWidth(bottomInner));
|
|
152
|
+
const leftPad = Math.floor(bottomPad / 2);
|
|
153
|
+
const rightPad = bottomPad - leftPad;
|
|
154
|
+
lines.push(FRAME(` ╚${'═'.repeat(leftPad)}`) + FRAME.bold(bottomInner) + FRAME(`${'═'.repeat(rightPad)}╝`));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
lines.push(FRAME(` ╚${'═'.repeat(totalWidth - 4)}╝`));
|
|
158
|
+
}
|
|
159
|
+
return '\n' + lines.join('\n') + '\n';
|
|
160
|
+
}
|
|
161
|
+
// ── Key point extraction ─────────────────────────────────────
|
|
162
|
+
function extractKeyPoints(result) {
|
|
163
|
+
const lastRound = result.rounds[result.rounds.length - 1];
|
|
164
|
+
if (!lastRound?.opinions)
|
|
165
|
+
return [];
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
const points = [];
|
|
168
|
+
for (const op of lastRound.opinions) {
|
|
169
|
+
if (!op.keyPoints)
|
|
170
|
+
continue;
|
|
171
|
+
for (const kp of op.keyPoints) {
|
|
172
|
+
const normalized = kp.trim().toLowerCase();
|
|
173
|
+
if (normalized && !seen.has(normalized)) {
|
|
174
|
+
seen.add(normalized);
|
|
175
|
+
points.push(kp.trim());
|
|
176
|
+
if (points.length >= 3)
|
|
177
|
+
return points;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return points;
|
|
182
|
+
}
|
|
183
|
+
// ── Dissent extraction ───────────────────────────────────────
|
|
184
|
+
function extractDissent(result) {
|
|
185
|
+
const majorityVote = getMajorityVote(result.consensus.decision);
|
|
186
|
+
const lastRound = result.rounds[result.rounds.length - 1];
|
|
187
|
+
if (!lastRound?.opinions)
|
|
188
|
+
return undefined;
|
|
189
|
+
for (const op of lastRound.opinions) {
|
|
190
|
+
if (op.vote !== majorityVote && op.reasoning) {
|
|
191
|
+
return { unit: op.unit, text: op.reasoning.split('\n')[0] ?? '' };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session statistics tracking for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Tracks inputs, commands, deliberations, and decisions across a session.
|
|
5
|
+
* Displays a box-drawn summary on /quit if any deliberation was run.
|
|
6
|
+
*/
|
|
7
|
+
import type { ConsensusDecision } from '../types/consensus.js';
|
|
8
|
+
export interface SessionStats {
|
|
9
|
+
sessionStartedAt: number;
|
|
10
|
+
totalInputCount: number;
|
|
11
|
+
slashCommandCount: number;
|
|
12
|
+
deliberationCount: number;
|
|
13
|
+
berserkCount: number;
|
|
14
|
+
approveCount: number;
|
|
15
|
+
rejectCount: number;
|
|
16
|
+
deadlockCount: number;
|
|
17
|
+
totalDurationMs: number;
|
|
18
|
+
longestDurationMs: number;
|
|
19
|
+
estimatedTokens: number;
|
|
20
|
+
contextEntriesUsed: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function createSessionStats(): SessionStats;
|
|
23
|
+
export declare function recordInput(stats: SessionStats): void;
|
|
24
|
+
export declare function recordSlashCommand(stats: SessionStats): void;
|
|
25
|
+
export declare function recordDeliberation(stats: SessionStats, decision: ConsensusDecision, durationMs: number, tokens: number, berserk: boolean): void;
|
|
26
|
+
export declare function formatSessionSummary(stats: SessionStats): string;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session statistics tracking for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Tracks inputs, commands, deliberations, and decisions across a session.
|
|
5
|
+
* Displays a box-drawn summary on /quit if any deliberation was run.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { EVA_PALETTE } from '../tui/colors.js';
|
|
9
|
+
import { isApproval, isRejection } from '../types/consensus.js';
|
|
10
|
+
import { centerVisible, getResponsiveBoxWidth, padVisibleRight, visibleWidth, wrapDisplayText } from './display-utils.js';
|
|
11
|
+
const { frame, abstain, textPrimary } = EVA_PALETTE;
|
|
12
|
+
const FRAME_C = chalk.rgb(frame.r, frame.g, frame.b);
|
|
13
|
+
const DIM_C = chalk.rgb(abstain.r, abstain.g, abstain.b);
|
|
14
|
+
const TEXT_C = chalk.rgb(textPrimary.r, textPrimary.g, textPrimary.b);
|
|
15
|
+
export function createSessionStats() {
|
|
16
|
+
return {
|
|
17
|
+
sessionStartedAt: Date.now(),
|
|
18
|
+
totalInputCount: 0,
|
|
19
|
+
slashCommandCount: 0,
|
|
20
|
+
deliberationCount: 0,
|
|
21
|
+
berserkCount: 0,
|
|
22
|
+
approveCount: 0,
|
|
23
|
+
rejectCount: 0,
|
|
24
|
+
deadlockCount: 0,
|
|
25
|
+
totalDurationMs: 0,
|
|
26
|
+
longestDurationMs: 0,
|
|
27
|
+
estimatedTokens: 0,
|
|
28
|
+
contextEntriesUsed: 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function recordInput(stats) {
|
|
32
|
+
stats.totalInputCount++;
|
|
33
|
+
}
|
|
34
|
+
export function recordSlashCommand(stats) {
|
|
35
|
+
stats.slashCommandCount++;
|
|
36
|
+
}
|
|
37
|
+
export function recordDeliberation(stats, decision, durationMs, tokens, berserk) {
|
|
38
|
+
stats.deliberationCount++;
|
|
39
|
+
stats.totalDurationMs += durationMs;
|
|
40
|
+
stats.longestDurationMs = Math.max(stats.longestDurationMs, durationMs);
|
|
41
|
+
stats.estimatedTokens += tokens;
|
|
42
|
+
if (berserk)
|
|
43
|
+
stats.berserkCount++;
|
|
44
|
+
if (isApproval(decision)) {
|
|
45
|
+
stats.approveCount++;
|
|
46
|
+
}
|
|
47
|
+
else if (isRejection(decision)) {
|
|
48
|
+
stats.rejectCount++;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
stats.deadlockCount++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function formatDuration(ms) {
|
|
55
|
+
const totalSec = Math.floor(ms / 1000);
|
|
56
|
+
const min = Math.floor(totalSec / 60);
|
|
57
|
+
const sec = totalSec % 60;
|
|
58
|
+
return `${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
|
59
|
+
}
|
|
60
|
+
function formatSeconds(ms) {
|
|
61
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
62
|
+
}
|
|
63
|
+
function formatMetricLines(label, value, width) {
|
|
64
|
+
const labelWidth = visibleWidth(label);
|
|
65
|
+
const valueWidth = visibleWidth(value);
|
|
66
|
+
if (width >= 24 && labelWidth + valueWidth + 1 <= width) {
|
|
67
|
+
return [padVisibleRight(label, width - valueWidth) + value];
|
|
68
|
+
}
|
|
69
|
+
return wrapDisplayText(`${label}: ${value}`, width);
|
|
70
|
+
}
|
|
71
|
+
export function formatSessionSummary(stats) {
|
|
72
|
+
if (stats.deliberationCount === 0)
|
|
73
|
+
return '';
|
|
74
|
+
const elapsed = Date.now() - stats.sessionStartedAt;
|
|
75
|
+
const avgDur = stats.deliberationCount > 0
|
|
76
|
+
? formatSeconds(stats.totalDurationMs / stats.deliberationCount)
|
|
77
|
+
: '0.0s';
|
|
78
|
+
const berserkSuffix = stats.berserkCount > 0 ? ` (${stats.berserkCount} BERSERK)` : '';
|
|
79
|
+
const inputSuffix = stats.slashCommandCount > 0 ? ` (${stats.slashCommandCount} commands)` : '';
|
|
80
|
+
// Decision breakdown: ✓2A / ✗1R / ⚠1D
|
|
81
|
+
const parts = [];
|
|
82
|
+
if (stats.approveCount > 0)
|
|
83
|
+
parts.push(`✓${stats.approveCount}A`);
|
|
84
|
+
if (stats.rejectCount > 0)
|
|
85
|
+
parts.push(`✗${stats.rejectCount}R`);
|
|
86
|
+
if (stats.deadlockCount > 0)
|
|
87
|
+
parts.push(`⚠${stats.deadlockCount}D`);
|
|
88
|
+
const decisionLine = parts.join(' / ') || '―';
|
|
89
|
+
const width = getResponsiveBoxWidth(43, 24);
|
|
90
|
+
const innerWidth = Math.max(1, width - 4);
|
|
91
|
+
const bodyLines = [
|
|
92
|
+
...formatMetricLines('Duration', formatDuration(elapsed), innerWidth),
|
|
93
|
+
...formatMetricLines('Inputs', `${stats.totalInputCount}${inputSuffix}`, innerWidth),
|
|
94
|
+
'',
|
|
95
|
+
...formatMetricLines('Deliberations', `${stats.deliberationCount}${berserkSuffix}`, innerWidth),
|
|
96
|
+
...formatMetricLines('Decisions', decisionLine, innerWidth),
|
|
97
|
+
...formatMetricLines('Avg Duration', avgDur, innerWidth),
|
|
98
|
+
...formatMetricLines('Longest', formatSeconds(stats.longestDurationMs), innerWidth),
|
|
99
|
+
'',
|
|
100
|
+
...formatMetricLines('Est. Tokens', `~${stats.estimatedTokens.toLocaleString('en-US')}`, innerWidth),
|
|
101
|
+
...formatMetricLines('Context', `${stats.contextEntriesUsed} entries`, innerWidth),
|
|
102
|
+
];
|
|
103
|
+
const lines = [
|
|
104
|
+
'',
|
|
105
|
+
FRAME_C(` ╔${'═'.repeat(innerWidth)}╗`),
|
|
106
|
+
FRAME_C(' ║') + TEXT_C(centerVisible('SESSION SUMMARY', innerWidth)) + FRAME_C('║'),
|
|
107
|
+
FRAME_C(` ╠${'═'.repeat(innerWidth)}╣`),
|
|
108
|
+
...bodyLines.map((line, index) => {
|
|
109
|
+
if (line === '') {
|
|
110
|
+
return FRAME_C(` ╠${'─'.repeat(innerWidth)}╣`);
|
|
111
|
+
}
|
|
112
|
+
const tone = index <= 1 ? DIM_C : DIM_C;
|
|
113
|
+
return FRAME_C(' ║') + tone(padVisibleRight(line, innerWidth)) + FRAME_C('║');
|
|
114
|
+
}),
|
|
115
|
+
FRAME_C(` ╚${'═'.repeat(innerWidth)}╝`),
|
|
116
|
+
'',
|
|
117
|
+
];
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash command registry for MAGI REPL.
|
|
3
|
+
*
|
|
4
|
+
* Each command returns a SlashCommandResult that tells the REPL
|
|
5
|
+
* whether to run a deliberation (with TUI) or just print inline output.
|
|
6
|
+
*/
|
|
7
|
+
import type { Magi } from '../index.js';
|
|
8
|
+
import type { ReplContext } from './context.js';
|
|
9
|
+
import type { TaskType, TaskArtifact } from '../types/core.js';
|
|
10
|
+
export interface DeliberationRequest {
|
|
11
|
+
task: {
|
|
12
|
+
type: TaskType;
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
artifacts?: TaskArtifact[];
|
|
16
|
+
context?: string;
|
|
17
|
+
};
|
|
18
|
+
berserk?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ConfirmationRequest {
|
|
21
|
+
description: string;
|
|
22
|
+
execute: () => Promise<SlashCommandResult>;
|
|
23
|
+
requiredInput?: string;
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface BackgroundJobRequest {
|
|
27
|
+
name: string;
|
|
28
|
+
run: (signal: AbortSignal) => Promise<void>;
|
|
29
|
+
onEvent?: (message: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface WatchCommandRequest {
|
|
32
|
+
command: 'start' | 'stop' | 'status';
|
|
33
|
+
pollIntervalMs?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface SlashCommandResult {
|
|
36
|
+
deliberation?: DeliberationRequest;
|
|
37
|
+
output?: string;
|
|
38
|
+
action?: 'quit' | 'clear' | 'reset' | 'toggle-sound' | 'paste' | 'export' | 'jobs';
|
|
39
|
+
confirm?: ConfirmationRequest;
|
|
40
|
+
background?: BackgroundJobRequest;
|
|
41
|
+
watch?: WatchCommandRequest;
|
|
42
|
+
/** Callback that needs exclusive stdin access (e.g. raw mode menus).
|
|
43
|
+
* REPL will pause readline, run the callback, then resume. */
|
|
44
|
+
interactive?: () => Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
export type ArgKind = 'none' | 'file' | 'freeform';
|
|
47
|
+
export interface SlashCommand {
|
|
48
|
+
description: string;
|
|
49
|
+
usage: string;
|
|
50
|
+
argKind: ArgKind;
|
|
51
|
+
aliases?: string[];
|
|
52
|
+
execute(args: string, magi: Magi, context: ReplContext): Promise<SlashCommandResult>;
|
|
53
|
+
}
|
|
54
|
+
export declare function parseInput(input: string): {
|
|
55
|
+
isSlash: boolean;
|
|
56
|
+
command: string;
|
|
57
|
+
args: string;
|
|
58
|
+
};
|
|
59
|
+
export declare function suggestCommand(input: string, commands: Iterable<string>): string | undefined;
|
|
60
|
+
export declare function createSlashCommands(): Map<string, SlashCommand>;
|