mini-coder 0.5.14 → 0.6.1
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/README.md +26 -109
- package/bin/mc.ts +8 -11
- package/bun.lock +79 -269
- package/nono-mini-coder.json +42 -0
- package/package.json +17 -22
- package/src/agent.ts +243 -1403
- package/src/args.ts +289 -0
- package/src/headless.ts +41 -359
- package/src/index.ts +29 -1016
- package/src/oauth.ts +117 -0
- package/src/prompt.ts +219 -284
- package/src/session.ts +55 -1306
- package/src/shared.ts +117 -38
- package/src/tool-bash.ts +110 -0
- package/src/tool-edit.ts +133 -0
- package/src/tool-read.ts +80 -293
- package/src/tui-components.ts +150 -0
- package/src/tui-conversation.ts +271 -0
- package/src/tui-editor.ts +29 -0
- package/src/tui-overlay.ts +403 -0
- package/src/tui.ts +228 -0
- package/src/types.ts +164 -0
- package/tsconfig.json +17 -0
- package/BENCHMARK.md +0 -107
- package/LICENSE +0 -9
- package/PROGRESS.md +0 -5
- package/assets/icon-1-minimal.svg +0 -31
- package/assets/icon-2-dark-terminal.svg +0 -48
- package/assets/icon-3-gradient-modern.svg +0 -45
- package/assets/icon-4-filled-bold.svg +0 -54
- package/assets/icon-5-community-badge.svg +0 -63
- package/assets/mc-claude-smart.png +0 -0
- package/assets/mc-gpt-smart.png +0 -0
- package/assets/preview-0-5-0.png +0 -0
- package/assets/preview.gif +0 -0
- package/benchmark-baseline.sh +0 -15
- package/benchmark-loop.sh +0 -19
- package/skills-lock.json +0 -15
- package/src/assistant-output.ts +0 -73
- package/src/cli.ts +0 -134
- package/src/delegation.ts +0 -238
- package/src/errors.ts +0 -15
- package/src/git.ts +0 -247
- package/src/input.ts +0 -168
- package/src/mcp.ts +0 -609
- package/src/paths.ts +0 -37
- package/src/session-message.ts +0 -385
- package/src/settings.ts +0 -449
- package/src/skills.ts +0 -271
- package/src/submit.ts +0 -376
- package/src/text.ts +0 -71
- package/src/theme.ts +0 -330
- package/src/tool-common.ts +0 -93
- package/src/tool-delegate.ts +0 -125
- package/src/tool-grep.ts +0 -606
- package/src/tool-shell.ts +0 -1051
- package/src/tools.ts +0 -1179
- package/src/ui/agent.ts +0 -320
- package/src/ui/commands.test.ts +0 -957
- package/src/ui/commands.ts +0 -848
- package/src/ui/conversation.test.ts +0 -585
- package/src/ui/conversation.ts +0 -1836
- package/src/ui/help.ts +0 -158
- package/src/ui/input.test.ts +0 -64
- package/src/ui/input.ts +0 -138
- package/src/ui/overlay.ts +0 -59
- package/src/ui/runtime.ts +0 -69
- package/src/ui/status.ts +0 -220
- package/src/ui.ts +0 -1190
- package/src/version.ts +0 -48
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<!-- Mini Coder Icon v4: Filled Bold
|
|
2
|
-
Chunky solid brackets form a unified shape. Bolt cuts through like a slash.
|
|
3
|
-
Inspired by: "accurate and reliable", sturdy and trustworthy feel -->
|
|
4
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
|
5
|
-
<!-- Left bracket { — filled, bold, warm off-white bg -->
|
|
6
|
-
<rect x="0" y="0" width="100" height="100" rx="0" fill="#f7f3ee"/>
|
|
7
|
-
|
|
8
|
-
<!-- Left brace as thick filled path -->
|
|
9
|
-
<path
|
|
10
|
-
d="M40 10
|
|
11
|
-
C32 10 28 15 28 23
|
|
12
|
-
L28 39
|
|
13
|
-
C28 45 24 48 19 50
|
|
14
|
-
C24 52 28 55 28 61
|
|
15
|
-
L28 77
|
|
16
|
-
C28 85 32 90 40 90
|
|
17
|
-
L40 83
|
|
18
|
-
C35 83 35 79 35 75
|
|
19
|
-
L35 62
|
|
20
|
-
C35 55 31 52 28 50
|
|
21
|
-
C31 48 35 45 35 38
|
|
22
|
-
L35 25
|
|
23
|
-
C35 21 35 17 40 17
|
|
24
|
-
Z"
|
|
25
|
-
fill="#1a1a2e"
|
|
26
|
-
/>
|
|
27
|
-
<!-- Right brace as thick filled path -->
|
|
28
|
-
<path
|
|
29
|
-
d="M60 10
|
|
30
|
-
C68 10 72 15 72 23
|
|
31
|
-
L72 39
|
|
32
|
-
C72 45 76 48 81 50
|
|
33
|
-
C76 52 72 55 72 61
|
|
34
|
-
L72 77
|
|
35
|
-
C72 85 68 90 60 90
|
|
36
|
-
L60 83
|
|
37
|
-
C65 83 65 79 65 75
|
|
38
|
-
L65 62
|
|
39
|
-
C65 55 69 52 72 50
|
|
40
|
-
C69 48 65 45 65 38
|
|
41
|
-
L65 25
|
|
42
|
-
C65 21 65 17 60 17
|
|
43
|
-
Z"
|
|
44
|
-
fill="#1a1a2e"
|
|
45
|
-
/>
|
|
46
|
-
<!-- Lightning bolt — punchy orange-red -->
|
|
47
|
-
<path
|
|
48
|
-
d="M58 16 L41 51 L52 51 L42 84 L64 47 L53 47 Z"
|
|
49
|
-
fill="#e8390e"
|
|
50
|
-
stroke="#f7f3ee"
|
|
51
|
-
stroke-width="1.5"
|
|
52
|
-
stroke-linejoin="round"
|
|
53
|
-
/>
|
|
54
|
-
</svg>
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
<!-- Mini Coder Icon v5: Community Badge
|
|
2
|
-
Hexagonal badge shape (nod to community/open-source), teal + amber palette.
|
|
3
|
-
Bolt doubles as a "play / run" symbol. Inspired by: "community oriented",
|
|
4
|
-
open source tools like Codex & opencode. -->
|
|
5
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
|
6
|
-
<defs>
|
|
7
|
-
<linearGradient id="hexGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
8
|
-
<stop offset="0%" stop-color="#0a3d62"/>
|
|
9
|
-
<stop offset="100%" stop-color="#0c6e6e"/>
|
|
10
|
-
</linearGradient>
|
|
11
|
-
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
|
|
12
|
-
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="rgba(0,0,0,0.35)"/>
|
|
13
|
-
</filter>
|
|
14
|
-
</defs>
|
|
15
|
-
|
|
16
|
-
<!-- Hexagon background -->
|
|
17
|
-
<polygon
|
|
18
|
-
points="50,4 93,27 93,73 50,96 7,73 7,27"
|
|
19
|
-
fill="url(#hexGrad)"
|
|
20
|
-
filter="url(#shadow)"
|
|
21
|
-
/>
|
|
22
|
-
<!-- Subtle inner hex ring -->
|
|
23
|
-
<polygon
|
|
24
|
-
points="50,10 88,30 88,70 50,90 12,70 12,30"
|
|
25
|
-
fill="none"
|
|
26
|
-
stroke="rgba(255,255,255,0.1)"
|
|
27
|
-
stroke-width="1.5"
|
|
28
|
-
/>
|
|
29
|
-
|
|
30
|
-
<!-- Left bracket { — teal/white -->
|
|
31
|
-
<path
|
|
32
|
-
d="M37 18 C29 18 26 23 26 30 L26 43 C26 48 22 51 18 52 C22 53 26 56 26 61 L26 71 C26 78 29 83 37 83"
|
|
33
|
-
fill="none"
|
|
34
|
-
stroke="#7fffd4"
|
|
35
|
-
stroke-width="4"
|
|
36
|
-
stroke-linecap="round"
|
|
37
|
-
stroke-linejoin="round"
|
|
38
|
-
/>
|
|
39
|
-
<!-- Right bracket } — teal/white -->
|
|
40
|
-
<path
|
|
41
|
-
d="M63 18 C71 18 74 23 74 30 L74 43 C74 48 78 51 82 52 C78 53 74 56 74 61 L74 71 C74 78 71 83 63 83"
|
|
42
|
-
fill="none"
|
|
43
|
-
stroke="#7fffd4"
|
|
44
|
-
stroke-width="4"
|
|
45
|
-
stroke-linecap="round"
|
|
46
|
-
stroke-linejoin="round"
|
|
47
|
-
/>
|
|
48
|
-
|
|
49
|
-
<!-- Lightning bolt — amber gold -->
|
|
50
|
-
<path
|
|
51
|
-
d="M56 22 L42 52 L52 52 L44 78 L62 46 L51 46 Z"
|
|
52
|
-
fill="#ffc107"
|
|
53
|
-
stroke="#fff8e1"
|
|
54
|
-
stroke-width="1"
|
|
55
|
-
stroke-linejoin="round"
|
|
56
|
-
/>
|
|
57
|
-
<!-- Tiny speed lines left of bolt -->
|
|
58
|
-
<line x1="28" y1="46" x2="36" y2="46" stroke="#7fffd4" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
|
|
59
|
-
<line x1="30" y1="51" x2="36" y2="51" stroke="#7fffd4" stroke-width="1.5" stroke-linecap="round" opacity="0.35"/>
|
|
60
|
-
<!-- Tiny speed lines right of bolt -->
|
|
61
|
-
<line x1="64" y1="46" x2="72" y2="46" stroke="#7fffd4" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
|
|
62
|
-
<line x1="64" y1="51" x2="70" y2="51" stroke="#7fffd4" stroke-width="1.5" stroke-linecap="round" opacity="0.35"/>
|
|
63
|
-
</svg>
|
|
Binary file
|
package/assets/mc-gpt-smart.png
DELETED
|
Binary file
|
package/assets/preview-0-5-0.png
DELETED
|
Binary file
|
package/assets/preview.gif
DELETED
|
Binary file
|
package/benchmark-baseline.sh
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
cd -- "$(dirname -- "${BASH_SOURCE[0]}")"
|
|
5
|
-
|
|
6
|
-
PYTHONPATH="$PWD/terminal-bench" harbor run -y \
|
|
7
|
-
--job-name benchmark-baseline-full-$(date +%F__%H-%M-%S) \
|
|
8
|
-
--jobs-dir "$PWD/terminal-bench/jobs" \
|
|
9
|
-
--agent-import-path mini_coder_agent:MiniCoderAgent \
|
|
10
|
-
--ak version=0.5.12 \
|
|
11
|
-
--model openai-codex/gpt-5.4 \
|
|
12
|
-
--dataset terminal-bench@2.0 \
|
|
13
|
-
--n-attempts 2 \
|
|
14
|
-
--n-concurrent 2 \
|
|
15
|
-
--max-retries 0
|
package/benchmark-loop.sh
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
stop() {
|
|
5
|
-
echo "stopping loop"
|
|
6
|
-
exit 0
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
trap stop INT TERM
|
|
10
|
-
|
|
11
|
-
while :; do
|
|
12
|
-
echo ""
|
|
13
|
-
echo "> Running step"
|
|
14
|
-
echo ""
|
|
15
|
-
mc -p "See BENCHMARK.md and PROGRESS.md first. Do the next step in the process. Once the step is complete update PROGRESS.md"
|
|
16
|
-
|
|
17
|
-
sleep 60
|
|
18
|
-
done
|
|
19
|
-
|
package/skills-lock.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"skills": {
|
|
4
|
-
"bun-development": {
|
|
5
|
-
"source": "sickn33/antigravity-awesome-skills",
|
|
6
|
-
"sourceType": "github",
|
|
7
|
-
"computedHash": "87e59a1e7f7fe512333aad64022955501a4e36be6e65487196dfc311e5b786b7"
|
|
8
|
-
},
|
|
9
|
-
"typescript-advanced-types": {
|
|
10
|
-
"source": "wshobson/agents",
|
|
11
|
-
"sourceType": "github",
|
|
12
|
-
"computedHash": "3a3be8c925f96ac4e1280db28a01677e7ca5197f3792de2bbf35ab3bb324b6dd"
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
package/src/assistant-output.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared helpers for extracting user-visible assistant output.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
|
8
|
-
import {
|
|
9
|
-
collapseWhitespaceToNull,
|
|
10
|
-
joinTextBlocks,
|
|
11
|
-
truncateText,
|
|
12
|
-
} from "./text.ts";
|
|
13
|
-
|
|
14
|
-
const ASSISTANT_ACTIVITY_MAX_CHARS = 160;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Extract concatenated text blocks from an assistant message.
|
|
18
|
-
*
|
|
19
|
-
* @param message - Assistant message to inspect.
|
|
20
|
-
* @returns The combined text content, or an empty string when none exists.
|
|
21
|
-
*/
|
|
22
|
-
export function extractAssistantText(message: AssistantMessage | null): string {
|
|
23
|
-
if (!message) {
|
|
24
|
-
return "";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return message.content
|
|
28
|
-
.filter(
|
|
29
|
-
(
|
|
30
|
-
block,
|
|
31
|
-
): block is Extract<
|
|
32
|
-
AssistantMessage["content"][number],
|
|
33
|
-
{ type: "text" }
|
|
34
|
-
> => {
|
|
35
|
-
return block.type === "text";
|
|
36
|
-
},
|
|
37
|
-
)
|
|
38
|
-
.map((block) => block.text)
|
|
39
|
-
.join("");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Extract a short assistant activity snippet from a tool-using message.
|
|
44
|
-
*
|
|
45
|
-
* @param message - Assistant message to inspect.
|
|
46
|
-
* @returns A collapsed activity snippet, or `null` when no snippet applies.
|
|
47
|
-
*/
|
|
48
|
-
export function extractAssistantActivitySnippet(
|
|
49
|
-
message: AssistantMessage,
|
|
50
|
-
): string | null {
|
|
51
|
-
if (!message.content.some((block) => block.type === "toolCall")) {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const text = collapseWhitespaceToNull(joinTextBlocks(message.content));
|
|
56
|
-
return text ? truncateText(text, ASSISTANT_ACTIVITY_MAX_CHARS) : null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Extract terminal assistant error text from a failed assistant message.
|
|
61
|
-
*
|
|
62
|
-
* @param message - Assistant message to inspect.
|
|
63
|
-
* @returns Collapsed error text, or `null` when the message is not terminally errored.
|
|
64
|
-
*/
|
|
65
|
-
export function extractAssistantErrorText(
|
|
66
|
-
message: AssistantMessage | null,
|
|
67
|
-
): string | null {
|
|
68
|
-
if (!message || message.stopReason !== "error" || !message.errorMessage) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return collapseWhitespaceToNull(message.errorMessage) ?? null;
|
|
73
|
-
}
|
package/src/cli.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI argument parsing and launch-mode selection.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Types
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
/** Parsed CLI options for the current launch. */
|
|
12
|
-
export interface CliOptions {
|
|
13
|
-
/** One-shot prompt text, or `null` when not provided. */
|
|
14
|
-
prompt: string | null;
|
|
15
|
-
/** Whether to stream headless output as NDJSON instead of final text. */
|
|
16
|
-
json: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** TTY availability for stdin/stdout. */
|
|
20
|
-
export interface TtyState {
|
|
21
|
-
/** Whether stdin is attached to a TTY. */
|
|
22
|
-
stdinIsTTY: boolean;
|
|
23
|
-
/** Whether stdout is attached to a TTY. */
|
|
24
|
-
stdoutIsTTY: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Argument parsing
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Parse supported CLI arguments.
|
|
33
|
-
*
|
|
34
|
-
* Supports `-p, --prompt <text>` for headless one-shot mode and `--json`
|
|
35
|
-
* to stream NDJSON events instead of the default final-text mode
|
|
36
|
-
* (stdout final answer plus stderr activity snippets).
|
|
37
|
-
* Unknown flags and positional arguments fail eagerly.
|
|
38
|
-
*
|
|
39
|
-
* @param argv - Process arguments excluding the Bun executable and script path.
|
|
40
|
-
* @returns The parsed CLI options.
|
|
41
|
-
*/
|
|
42
|
-
export function parseCliArgs(argv: readonly string[]): CliOptions {
|
|
43
|
-
let prompt: string | null = null;
|
|
44
|
-
let json = false;
|
|
45
|
-
|
|
46
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
47
|
-
const arg = argv[index];
|
|
48
|
-
if (!arg) {
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (arg === "-p" || arg === "--prompt") {
|
|
53
|
-
const value = argv[index + 1];
|
|
54
|
-
if (value == null) {
|
|
55
|
-
throw new Error("Missing value for -p/--prompt.");
|
|
56
|
-
}
|
|
57
|
-
prompt = value;
|
|
58
|
-
index += 1;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (arg.startsWith("--prompt=")) {
|
|
63
|
-
prompt = arg.slice("--prompt=".length);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (arg === "--json") {
|
|
68
|
-
json = true;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (arg.startsWith("-")) {
|
|
73
|
-
throw new Error(`Unknown argument: ${arg}`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
throw new Error(`Unexpected positional argument: ${arg}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return { prompt, json };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
// Mode selection
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Return whether the current launch should run in headless mode.
|
|
88
|
-
*
|
|
89
|
-
* Headless mode is selected when a one-shot prompt was provided or when
|
|
90
|
-
* either stdin or stdout is not attached to a TTY.
|
|
91
|
-
*
|
|
92
|
-
* @param options - Parsed CLI options.
|
|
93
|
-
* @param tty - Current TTY availability.
|
|
94
|
-
* @returns `true` when headless mode should be used.
|
|
95
|
-
*/
|
|
96
|
-
export function shouldUseHeadlessMode(
|
|
97
|
-
options: CliOptions,
|
|
98
|
-
tty: TtyState,
|
|
99
|
-
): boolean {
|
|
100
|
-
return options.prompt !== null || !tty.stdinIsTTY || !tty.stdoutIsTTY;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Resolve the raw prompt text for headless mode.
|
|
105
|
-
*
|
|
106
|
-
* Prefers the explicit CLI prompt. Otherwise reads all of stdin. Empty or
|
|
107
|
-
* whitespace-only input is rejected.
|
|
108
|
-
*
|
|
109
|
-
* @param options - Parsed CLI options.
|
|
110
|
-
* @param tty - Current TTY availability.
|
|
111
|
-
* @param readStdin - Callback that returns the full stdin contents.
|
|
112
|
-
* @returns The raw prompt text to submit.
|
|
113
|
-
*/
|
|
114
|
-
export async function resolveHeadlessPrompt(
|
|
115
|
-
options: CliOptions,
|
|
116
|
-
tty: TtyState,
|
|
117
|
-
readStdin: () => Promise<string>,
|
|
118
|
-
): Promise<string> {
|
|
119
|
-
const rawPrompt =
|
|
120
|
-
options.prompt !== null
|
|
121
|
-
? options.prompt
|
|
122
|
-
: tty.stdinIsTTY
|
|
123
|
-
? null
|
|
124
|
-
: await readStdin();
|
|
125
|
-
|
|
126
|
-
if (rawPrompt === null) {
|
|
127
|
-
throw new Error("Headless mode requires -p/--prompt or piped stdin.");
|
|
128
|
-
}
|
|
129
|
-
if (rawPrompt.trim().length === 0) {
|
|
130
|
-
throw new Error("Headless input is empty.");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return rawPrompt;
|
|
134
|
-
}
|
package/src/delegation.ts
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subagent delegation safeguards.
|
|
3
|
-
*
|
|
4
|
-
* Tracks a shallow delegation depth plus a small per-run delegation budget so
|
|
5
|
-
* first-class `delegate` tool runs and shell-authored `mc -p` child processes
|
|
6
|
-
* cannot recurse forever.
|
|
7
|
-
*
|
|
8
|
-
* @module
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const SHELL_DELEGATION_COMMAND =
|
|
12
|
-
/(?:^|[;&|()\s])(?:[^\s;&|()]+\/)?mc\s+(?:-p\b|--prompt(?:\b|=))/g;
|
|
13
|
-
|
|
14
|
-
/** Environment variable carrying the current subagent-delegation depth. */
|
|
15
|
-
export const SHELL_DELEGATION_DEPTH_ENV = "MC_SUBAGENT_DEPTH";
|
|
16
|
-
|
|
17
|
-
/** Environment variable carrying the remaining subagent-delegation budget. */
|
|
18
|
-
export const SHELL_DELEGATION_BUDGET_ENV = "MC_SUBAGENT_BUDGET";
|
|
19
|
-
|
|
20
|
-
/** Maximum allowed subagent delegation depth. */
|
|
21
|
-
export const MAX_SHELL_DELEGATION_DEPTH = 1;
|
|
22
|
-
|
|
23
|
-
/** Default number of delegated subagent launches allowed per agent run. */
|
|
24
|
-
export const DEFAULT_SHELL_DELEGATION_BUDGET = 4;
|
|
25
|
-
|
|
26
|
-
/** Current subagent-delegation context for one app run. */
|
|
27
|
-
export interface ShellDelegationContext {
|
|
28
|
-
/** Current subagent-delegation depth for this app process. */
|
|
29
|
-
depth: number;
|
|
30
|
-
/** Remaining delegated-subagent launches available in the active run. */
|
|
31
|
-
remainingBudget: number;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Result of reserving subagent-delegation budget for one launch. */
|
|
35
|
-
export interface ShellDelegationReservation {
|
|
36
|
-
/** Number of delegated-subagent launches reserved by the request. */
|
|
37
|
-
launchCount: number;
|
|
38
|
-
/** Updated parent-run context after reserving any delegated launches. */
|
|
39
|
-
updatedContext: ShellDelegationContext;
|
|
40
|
-
/** Context that delegated child runs should inherit for this request. */
|
|
41
|
-
childContext: ShellDelegationContext;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Successful or blocked delegation-reservation outcome. */
|
|
45
|
-
export type ReserveShellDelegationResult =
|
|
46
|
-
| {
|
|
47
|
-
/** Whether the delegation request may proceed. */
|
|
48
|
-
ok: true;
|
|
49
|
-
/** Reserved delegation details for the request. */
|
|
50
|
-
reservation: ShellDelegationReservation;
|
|
51
|
-
}
|
|
52
|
-
| {
|
|
53
|
-
/** Whether the delegation request may proceed. */
|
|
54
|
-
ok: false;
|
|
55
|
-
/** Human-readable tool error for the blocked delegation attempt. */
|
|
56
|
-
error: string;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
type DelegationReservationError = "nested" | "exhausted" | "over_budget";
|
|
60
|
-
|
|
61
|
-
function readNonNegativeInteger(
|
|
62
|
-
value: string | undefined,
|
|
63
|
-
fallback: number,
|
|
64
|
-
): number {
|
|
65
|
-
if (value == null || value === "") {
|
|
66
|
-
return fallback;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const parsed = Number.parseInt(value, 10);
|
|
70
|
-
return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function reserveDelegationBudget(
|
|
74
|
-
context: ShellDelegationContext,
|
|
75
|
-
launchCount: number,
|
|
76
|
-
):
|
|
77
|
-
| {
|
|
78
|
-
ok: true;
|
|
79
|
-
reservation: ShellDelegationReservation;
|
|
80
|
-
}
|
|
81
|
-
| {
|
|
82
|
-
ok: false;
|
|
83
|
-
reason: DelegationReservationError;
|
|
84
|
-
} {
|
|
85
|
-
if (launchCount === 0) {
|
|
86
|
-
const unchanged = { ...context };
|
|
87
|
-
return {
|
|
88
|
-
ok: true,
|
|
89
|
-
reservation: {
|
|
90
|
-
launchCount,
|
|
91
|
-
updatedContext: unchanged,
|
|
92
|
-
childContext: unchanged,
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (context.depth >= MAX_SHELL_DELEGATION_DEPTH) {
|
|
98
|
-
return {
|
|
99
|
-
ok: false,
|
|
100
|
-
reason: "nested",
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (context.remainingBudget === 0) {
|
|
105
|
-
return {
|
|
106
|
-
ok: false,
|
|
107
|
-
reason: "exhausted",
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (launchCount > context.remainingBudget) {
|
|
112
|
-
return {
|
|
113
|
-
ok: false,
|
|
114
|
-
reason: "over_budget",
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const remainingBudget = context.remainingBudget - launchCount;
|
|
119
|
-
return {
|
|
120
|
-
ok: true,
|
|
121
|
-
reservation: {
|
|
122
|
-
launchCount,
|
|
123
|
-
updatedContext: {
|
|
124
|
-
depth: context.depth,
|
|
125
|
-
remainingBudget,
|
|
126
|
-
},
|
|
127
|
-
childContext: {
|
|
128
|
-
depth: context.depth + 1,
|
|
129
|
-
remainingBudget,
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Read the current subagent-delegation context from environment variables.
|
|
137
|
-
*
|
|
138
|
-
* Missing or invalid values fall back to the safe root-run defaults.
|
|
139
|
-
*
|
|
140
|
-
* @param env - Environment variables to inspect.
|
|
141
|
-
* @returns The parsed subagent-delegation context.
|
|
142
|
-
*/
|
|
143
|
-
export function readShellDelegationContext(
|
|
144
|
-
env: Readonly<Record<string, string | undefined>>,
|
|
145
|
-
): ShellDelegationContext {
|
|
146
|
-
return {
|
|
147
|
-
depth: readNonNegativeInteger(env[SHELL_DELEGATION_DEPTH_ENV], 0),
|
|
148
|
-
remainingBudget: readNonNegativeInteger(
|
|
149
|
-
env[SHELL_DELEGATION_BUDGET_ENV],
|
|
150
|
-
DEFAULT_SHELL_DELEGATION_BUDGET,
|
|
151
|
-
),
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Build the environment-variable overrides for a subagent-delegation context.
|
|
157
|
-
*
|
|
158
|
-
* @param context - Subagent-delegation context to serialize.
|
|
159
|
-
* @returns Environment overrides for child shell processes.
|
|
160
|
-
*/
|
|
161
|
-
export function buildShellDelegationEnv(
|
|
162
|
-
context: ShellDelegationContext,
|
|
163
|
-
): Record<string, string> {
|
|
164
|
-
return {
|
|
165
|
-
[SHELL_DELEGATION_DEPTH_ENV]: String(context.depth),
|
|
166
|
-
[SHELL_DELEGATION_BUDGET_ENV]: String(context.remainingBudget),
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Count likely shell-authored `mc -p` / `mc --prompt` launches in one command.
|
|
172
|
-
*
|
|
173
|
-
* The detector is intentionally narrow and optimized for the prompt-guided
|
|
174
|
-
* `mc -p "subtask"` pattern mini-coder still supports for CLI-level testing.
|
|
175
|
-
*
|
|
176
|
-
* @param command - Raw shell command.
|
|
177
|
-
* @returns Number of likely `mc -p` launches in the command.
|
|
178
|
-
*/
|
|
179
|
-
export function countShellDelegationLaunches(command: string): number {
|
|
180
|
-
return command.match(SHELL_DELEGATION_COMMAND)?.length ?? 0;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Reserve first-class `delegate` tool budget for one delegated subagent run.
|
|
185
|
-
*
|
|
186
|
-
* @param context - Current subagent-delegation context for the active run.
|
|
187
|
-
* @returns Reservation details, or a blocking error when this delegation would
|
|
188
|
-
* exceed the allowed subagent-delegation policy.
|
|
189
|
-
*/
|
|
190
|
-
export function reserveToolDelegation(
|
|
191
|
-
context: ShellDelegationContext,
|
|
192
|
-
): ReserveShellDelegationResult {
|
|
193
|
-
const reservation = reserveDelegationBudget(context, 1);
|
|
194
|
-
if (!reservation.ok) {
|
|
195
|
-
return {
|
|
196
|
-
ok: false,
|
|
197
|
-
error:
|
|
198
|
-
reservation.reason === "nested"
|
|
199
|
-
? "Tool delegation blocked: delegated `delegate` tool runs may not delegate again."
|
|
200
|
-
: "Tool delegation blocked: this run has no remaining `delegate` delegation budget.",
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return reservation;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Reserve shell-level delegation budget for a command before execution.
|
|
209
|
-
*
|
|
210
|
-
* Non-delegating commands pass through unchanged. Commands that would exceed
|
|
211
|
-
* the per-run delegation budget or the maximum allowed depth are rejected with
|
|
212
|
-
* a user-visible tool error.
|
|
213
|
-
*
|
|
214
|
-
* @param command - Raw shell command to inspect.
|
|
215
|
-
* @param context - Current subagent-delegation context for the active run.
|
|
216
|
-
* @returns Reservation details, or a blocking error when the command would
|
|
217
|
-
* exceed the allowed shell-level delegation policy.
|
|
218
|
-
*/
|
|
219
|
-
export function reserveShellDelegation(
|
|
220
|
-
command: string,
|
|
221
|
-
context: ShellDelegationContext,
|
|
222
|
-
): ReserveShellDelegationResult {
|
|
223
|
-
const launchCount = countShellDelegationLaunches(command);
|
|
224
|
-
const reservation = reserveDelegationBudget(context, launchCount);
|
|
225
|
-
if (!reservation.ok) {
|
|
226
|
-
return {
|
|
227
|
-
ok: false,
|
|
228
|
-
error:
|
|
229
|
-
reservation.reason === "nested"
|
|
230
|
-
? "Shell delegation blocked: delegated `mc -p` runs may not launch more `mc -p` subagents."
|
|
231
|
-
: reservation.reason === "exhausted"
|
|
232
|
-
? "Shell delegation blocked: this run has no remaining `mc -p` delegation budget."
|
|
233
|
-
: "Shell delegation blocked: this command appears to launch more `mc -p` subagents than the remaining delegation budget allows.",
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return reservation;
|
|
238
|
-
}
|
package/src/errors.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared error-formatting helpers.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Convert an unknown thrown value into a readable message string.
|
|
9
|
-
*
|
|
10
|
-
* @param error - The thrown value to format.
|
|
11
|
-
* @returns The error message when available, otherwise the stringified value.
|
|
12
|
-
*/
|
|
13
|
-
export function getErrorMessage(error: unknown): string {
|
|
14
|
-
return error instanceof Error ? error.message : String(error);
|
|
15
|
-
}
|