@prestyj/cli 5.2.0 → 5.3.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/dist/app-sidecar.js +856 -91
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +89 -0
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-queue.test.d.ts +2 -0
- package/dist/core/agent-session-queue.test.d.ts.map +1 -0
- package/dist/core/agent-session-queue.test.js +122 -0
- package/dist/core/agent-session-queue.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +79 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +268 -47
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autopilot-cycle.d.ts +119 -0
- package/dist/core/autopilot-cycle.d.ts.map +1 -0
- package/dist/core/autopilot-cycle.js +119 -0
- package/dist/core/autopilot-cycle.js.map +1 -0
- package/dist/core/autopilot-cycle.test.d.ts +2 -0
- package/dist/core/autopilot-cycle.test.d.ts.map +1 -0
- package/dist/core/autopilot-cycle.test.js +375 -0
- package/dist/core/autopilot-cycle.test.js.map +1 -0
- package/dist/core/autopilot-gate.d.ts +109 -0
- package/dist/core/autopilot-gate.d.ts.map +1 -0
- package/dist/core/autopilot-gate.js +205 -0
- package/dist/core/autopilot-gate.js.map +1 -0
- package/dist/core/autopilot-gate.test.d.ts +2 -0
- package/dist/core/autopilot-gate.test.d.ts.map +1 -0
- package/dist/core/autopilot-gate.test.js +295 -0
- package/dist/core/autopilot-gate.test.js.map +1 -0
- package/dist/core/autopilot-verdict.d.ts +20 -2
- package/dist/core/autopilot-verdict.d.ts.map +1 -1
- package/dist/core/autopilot-verdict.js +94 -3
- package/dist/core/autopilot-verdict.js.map +1 -1
- package/dist/core/autopilot-verdict.test.js +73 -0
- package/dist/core/autopilot-verdict.test.js.map +1 -1
- package/dist/core/bash-spawn-benchmark.d.ts +2 -0
- package/dist/core/bash-spawn-benchmark.d.ts.map +1 -0
- package/dist/core/bash-spawn-benchmark.js +139 -0
- package/dist/core/bash-spawn-benchmark.js.map +1 -0
- package/dist/core/hashline-edit-benchmark.d.ts +33 -1
- package/dist/core/hashline-edit-benchmark.d.ts.map +1 -1
- package/dist/core/hashline-edit-benchmark.js +193 -21
- package/dist/core/hashline-edit-benchmark.js.map +1 -1
- package/dist/core/hashline-edit-benchmark.test.js +51 -2
- package/dist/core/hashline-edit-benchmark.test.js.map +1 -1
- package/dist/core/json-mode-flag-parity.test.d.ts +2 -0
- package/dist/core/json-mode-flag-parity.test.d.ts.map +1 -0
- package/dist/core/json-mode-flag-parity.test.js +66 -0
- package/dist/core/json-mode-flag-parity.test.js.map +1 -0
- package/dist/core/mcp/deferred-catalog.d.ts +28 -0
- package/dist/core/mcp/deferred-catalog.d.ts.map +1 -0
- package/dist/core/mcp/deferred-catalog.js +72 -0
- package/dist/core/mcp/deferred-catalog.js.map +1 -0
- package/dist/core/mcp/resolve-stdio.d.ts +19 -3
- package/dist/core/mcp/resolve-stdio.d.ts.map +1 -1
- package/dist/core/mcp/resolve-stdio.js +136 -5
- package/dist/core/mcp/resolve-stdio.js.map +1 -1
- package/dist/core/mcp/resolve-stdio.test.js +82 -1
- package/dist/core/mcp/resolve-stdio.test.js.map +1 -1
- package/dist/core/nolan-context.d.ts +43 -2
- package/dist/core/nolan-context.d.ts.map +1 -1
- package/dist/core/nolan-context.js +91 -8
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-context.test.js +167 -5
- package/dist/core/nolan-context.test.js.map +1 -1
- package/dist/core/nolan-model.d.ts +46 -0
- package/dist/core/nolan-model.d.ts.map +1 -0
- package/dist/core/nolan-model.js +30 -0
- package/dist/core/nolan-model.js.map +1 -0
- package/dist/core/nolan-model.test.d.ts +2 -0
- package/dist/core/nolan-model.test.d.ts.map +1 -0
- package/dist/core/nolan-model.test.js +51 -0
- package/dist/core/nolan-model.test.js.map +1 -0
- package/dist/core/nolan-prompt.d.ts +5 -18
- package/dist/core/nolan-prompt.d.ts.map +1 -1
- package/dist/core/nolan-prompt.js +112 -19
- package/dist/core/nolan-prompt.js.map +1 -1
- package/dist/core/nolan-prompt.test.d.ts +2 -0
- package/dist/core/nolan-prompt.test.d.ts.map +1 -0
- package/dist/core/nolan-prompt.test.js +138 -0
- package/dist/core/nolan-prompt.test.js.map +1 -0
- package/dist/core/persistent-shell.d.ts +27 -0
- package/dist/core/persistent-shell.d.ts.map +1 -0
- package/dist/core/persistent-shell.js +150 -0
- package/dist/core/persistent-shell.js.map +1 -0
- package/dist/core/persistent-shell.test.d.ts +2 -0
- package/dist/core/persistent-shell.test.d.ts.map +1 -0
- package/dist/core/persistent-shell.test.js +84 -0
- package/dist/core/persistent-shell.test.js.map +1 -0
- package/dist/core/process-manager-dev-server-repro.test.js +34 -6
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
- package/dist/core/progress/engine.d.ts +23 -0
- package/dist/core/progress/engine.d.ts.map +1 -0
- package/dist/core/progress/engine.js +131 -0
- package/dist/core/progress/engine.js.map +1 -0
- package/dist/core/progress/engine.test.d.ts +2 -0
- package/dist/core/progress/engine.test.d.ts.map +1 -0
- package/dist/core/progress/engine.test.js +136 -0
- package/dist/core/progress/engine.test.js.map +1 -0
- package/dist/core/progress/git-xp.d.ts +16 -0
- package/dist/core/progress/git-xp.d.ts.map +1 -0
- package/dist/core/progress/git-xp.js +106 -0
- package/dist/core/progress/git-xp.js.map +1 -0
- package/dist/core/progress/git-xp.test.d.ts +2 -0
- package/dist/core/progress/git-xp.test.d.ts.map +1 -0
- package/dist/core/progress/git-xp.test.js +88 -0
- package/dist/core/progress/git-xp.test.js.map +1 -0
- package/dist/core/progress/ranks.d.ts +21 -0
- package/dist/core/progress/ranks.d.ts.map +1 -0
- package/dist/core/progress/ranks.js +141 -0
- package/dist/core/progress/ranks.js.map +1 -0
- package/dist/core/progress/ranks.test.d.ts +2 -0
- package/dist/core/progress/ranks.test.d.ts.map +1 -0
- package/dist/core/progress/ranks.test.js +59 -0
- package/dist/core/progress/ranks.test.js.map +1 -0
- package/dist/core/progress/rebuild.d.ts +7 -0
- package/dist/core/progress/rebuild.d.ts.map +1 -0
- package/dist/core/progress/rebuild.js +106 -0
- package/dist/core/progress/rebuild.js.map +1 -0
- package/dist/core/progress/rebuild.test.d.ts +2 -0
- package/dist/core/progress/rebuild.test.d.ts.map +1 -0
- package/dist/core/progress/rebuild.test.js +72 -0
- package/dist/core/progress/rebuild.test.js.map +1 -0
- package/dist/core/progress/store.d.ts +35 -0
- package/dist/core/progress/store.d.ts.map +1 -0
- package/dist/core/progress/store.js +200 -0
- package/dist/core/progress/store.js.map +1 -0
- package/dist/core/progress/store.test.d.ts +2 -0
- package/dist/core/progress/store.test.d.ts.map +1 -0
- package/dist/core/progress/store.test.js +108 -0
- package/dist/core/progress/store.test.js.map +1 -0
- package/dist/core/progress/types.d.ts +108 -0
- package/dist/core/progress/types.d.ts.map +1 -0
- package/dist/core/progress/types.js +3 -0
- package/dist/core/progress/types.js.map +1 -0
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +40 -9
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-discovery.test.d.ts +2 -0
- package/dist/core/project-discovery.test.d.ts.map +1 -0
- package/dist/core/project-discovery.test.js +104 -0
- package/dist/core/project-discovery.test.js.map +1 -0
- package/dist/core/session-history.d.ts +51 -0
- package/dist/core/session-history.d.ts.map +1 -0
- package/dist/core/session-history.js +156 -0
- package/dist/core/session-history.js.map +1 -0
- package/dist/core/session-history.test.d.ts +2 -0
- package/dist/core/session-history.test.d.ts.map +1 -0
- package/dist/core/session-history.test.js +101 -0
- package/dist/core/session-history.test.js.map +1 -0
- package/dist/core/session-manager.d.ts +34 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +67 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +140 -1
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/speed-benchmark.test.js +2 -3
- package/dist/core/speed-benchmark.test.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +12 -8
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +15 -20
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/tools/bash.d.ts +1 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +27 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +9 -2
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +91 -10
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/edit.test.js +97 -0
- package/dist/tools/edit.test.js.map +1 -1
- package/dist/tools/plan-mode.test.js +25 -0
- package/dist/tools/plan-mode.test.js.map +1 -1
- package/dist/tools/prompt-hints.d.ts.map +1 -1
- package/dist/tools/prompt-hints.js +3 -0
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +24 -1
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/tool-search.d.ts +15 -0
- package/dist/tools/tool-search.d.ts.map +1 -0
- package/dist/tools/tool-search.js +35 -0
- package/dist/tools/tool-search.js.map +1 -0
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +22 -9
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/package.json +5 -5
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Autopilot Nolan's verdict contract.
|
|
3
3
|
*
|
|
4
4
|
* In autopilot mode Nolan never talks to the user — he auto-reviews EZ Coder's
|
|
5
|
-
* work and replies with exactly one of
|
|
5
|
+
* work and replies with exactly one of four machine-parseable verdicts. The
|
|
6
6
|
* first non-empty line carries the keyword; anything after is the payload.
|
|
7
7
|
*
|
|
8
8
|
* PROMPT
|
|
@@ -10,11 +10,27 @@
|
|
|
10
10
|
*
|
|
11
11
|
* ALL_CLEAR
|
|
12
12
|
*
|
|
13
|
+
* IGNORE
|
|
14
|
+
*
|
|
13
15
|
* HUMAN
|
|
14
16
|
* <one short reason the human is needed>
|
|
15
17
|
*
|
|
18
|
+
* ALL_CLEAR and IGNORE both stop the cycle with nothing left to do, but they
|
|
19
|
+
* mean different things to the UI: ALL_CLEAR is a verdict on real work ("GG
|
|
20
|
+
* Coder built/changed something and it checks out") and renders a one-line Nolan
|
|
21
|
+
* marker. IGNORE means the turn was never worth reviewing in the first place
|
|
22
|
+
* (small talk, a question, an ack, a mechanical operation with no code
|
|
23
|
+
* changes) — it renders NOTHING, not even a marker, so trivial turns don't
|
|
24
|
+
* spam the transcript with a Nolan bubble that adds no information.
|
|
25
|
+
*
|
|
16
26
|
* Parsing is forgiving and safe-by-default: any reply we can't confidently map
|
|
17
|
-
* to PROMPT or
|
|
27
|
+
* to PROMPT, ALL_CLEAR, or IGNORE becomes a HUMAN stop, never a blind loop.
|
|
28
|
+
*
|
|
29
|
+
* Recovery order for prose-then-keyword drift (Nolan writing commentary before
|
|
30
|
+
* the verdict despite the contract): bare trailing ALL_CLEAR/IGNORE → buried
|
|
31
|
+
* HUMAN → buried line-start PROMPT (uppercase, line-start only, so prose like
|
|
32
|
+
* "prompt the user" can never match). HUMAN wins over PROMPT when both appear
|
|
33
|
+
* — recovering a stop is always safe, recovering an action must lose ties.
|
|
18
34
|
*/
|
|
19
35
|
/** Cap on the raw-reply text we echo back as a HUMAN reason when Nolan's output
|
|
20
36
|
* is unrecognized — keeps a garbage/huge reply from bloating the transcript. */
|
|
@@ -37,6 +53,37 @@ function cap(text, max = RAW_REASON_CAP) {
|
|
|
37
53
|
return text;
|
|
38
54
|
return `${text.slice(0, max)} […${text.length - max} more chars]`;
|
|
39
55
|
}
|
|
56
|
+
/** Same keyword normalization as the primary line check: uppercase, drop a
|
|
57
|
+
* trailing colon/period, collapse "ALL CLEAR" → "ALL_CLEAR". */
|
|
58
|
+
function normalizeKeywordLine(line) {
|
|
59
|
+
return line
|
|
60
|
+
.trim()
|
|
61
|
+
.toUpperCase()
|
|
62
|
+
.replace(/[:.]+\s*$/, "")
|
|
63
|
+
.trim()
|
|
64
|
+
.replace(/\s+/g, "_");
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fallback for when Nolan ignores the "keyword-first, nothing before it"
|
|
68
|
+
* instruction and buries a bare ALL_CLEAR/IGNORE/SKIP line after a recap or
|
|
69
|
+
* explanation (a real drift pattern models fall into despite the system
|
|
70
|
+
* prompt). Only matches a line that is EXACTLY one of these bare keywords.
|
|
71
|
+
* PROMPT/HUMAN carry payloads and get their own dedicated recovery passes in
|
|
72
|
+
* parseAutopilotVerdict. Returns the LAST such line (the verdict
|
|
73
|
+
* conventionally lands at the end of the drift), or null if none/ambiguous
|
|
74
|
+
* multiple different keywords are present.
|
|
75
|
+
*/
|
|
76
|
+
function findTrailingBareVerdict(lines) {
|
|
77
|
+
let found = null;
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
const normalized = normalizeKeywordLine(line);
|
|
80
|
+
if (normalized === "ALL_CLEAR")
|
|
81
|
+
found = "all_clear";
|
|
82
|
+
else if (normalized === "IGNORE" || normalized === "SKIP")
|
|
83
|
+
found = "ignore";
|
|
84
|
+
}
|
|
85
|
+
return found;
|
|
86
|
+
}
|
|
40
87
|
/**
|
|
41
88
|
* Parse Autopilot Nolan's raw reply into a verdict. Never throws.
|
|
42
89
|
*/
|
|
@@ -66,6 +113,13 @@ export function parseAutopilotVerdict(reply) {
|
|
|
66
113
|
if (collapsed === "ALL_CLEAR" || collapsed.startsWith("ALL_CLEAR")) {
|
|
67
114
|
return { kind: "all_clear" };
|
|
68
115
|
}
|
|
116
|
+
// IGNORE / SKIP: the turn wasn't real work — nothing to say, nothing to show.
|
|
117
|
+
if (collapsed === "IGNORE" ||
|
|
118
|
+
collapsed.startsWith("IGNORE") ||
|
|
119
|
+
collapsed === "SKIP" ||
|
|
120
|
+
collapsed.startsWith("SKIP")) {
|
|
121
|
+
return { kind: "ignore" };
|
|
122
|
+
}
|
|
69
123
|
if (collapsed === "PROMPT" || collapsed.startsWith("PROMPT")) {
|
|
70
124
|
// Prefer the body on the following lines; if the keyword line itself carried
|
|
71
125
|
// inline text after "PROMPT:", fall back to that.
|
|
@@ -87,7 +141,44 @@ export function parseAutopilotVerdict(reply) {
|
|
|
87
141
|
}
|
|
88
142
|
return { kind: "human", reason: cap(reason) || DEFAULT_HUMAN_REASON };
|
|
89
143
|
}
|
|
90
|
-
// Unrecognized
|
|
144
|
+
// Unrecognized first line → before giving up, check for a bare verdict Nolan
|
|
145
|
+
// buried later in the reply (prose-then-keyword drift). This is the exact
|
|
146
|
+
// shape that used to leak raw commentary + "ALL_CLEAR" into a HUMAN bubble
|
|
147
|
+
// instead of rendering the normal all-clear/ignore marker.
|
|
148
|
+
const trailing = findTrailingBareVerdict(lines);
|
|
149
|
+
if (trailing === "all_clear")
|
|
150
|
+
return { kind: "all_clear" };
|
|
151
|
+
if (trailing === "ignore")
|
|
152
|
+
return { kind: "ignore" };
|
|
153
|
+
// A buried bare HUMAN line (Nolan wrote his reasoning first, THEN the verdict).
|
|
154
|
+
// Take the lines after the LAST exact-"HUMAN" line as the reason and drop the
|
|
155
|
+
// leading prose. Safe to recover unlike a buried PROMPT: HUMAN stops the cycle
|
|
156
|
+
// either way, so this can't trigger autonomous action — it only keeps Nolan's
|
|
157
|
+
// internal reasoning out of the human-facing reason bubble (the drift that
|
|
158
|
+
// dumped his whole recap into the transcript alongside the real reason).
|
|
159
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
160
|
+
if (normalizeKeywordLine(lines[i]) === "HUMAN") {
|
|
161
|
+
const reason = lines
|
|
162
|
+
.slice(i + 1)
|
|
163
|
+
.join("\n")
|
|
164
|
+
.trim();
|
|
165
|
+
return { kind: "human", reason: cap(reason) || DEFAULT_HUMAN_REASON };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Buried PROMPT (the drift that used to dump a valid runnable prompt into a
|
|
169
|
+
// HUMAN bubble): recover a line that STARTS with the uppercase keyword and
|
|
170
|
+
// re-parse from there, so both `PROMPT <inline body>` and `PROMPT\n<body>`
|
|
171
|
+
// shapes work. Uppercase + line-start (never mid-line, never lowercase)
|
|
172
|
+
// keeps prose like "prompt the user" or "a prompt for EZ Coder" from ever
|
|
173
|
+
// matching. Takes the LAST such line, and runs AFTER the HUMAN recovery so
|
|
174
|
+
// a reply carrying both stops instead of acting.
|
|
175
|
+
const BURIED_PROMPT_RE = /^PROMPT(?=$|[\s:.,])/;
|
|
176
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
177
|
+
if (BURIED_PROMPT_RE.test(lines[i].trim())) {
|
|
178
|
+
return parseAutopilotVerdict(lines.slice(i).join("\n"));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Truly unrecognized → stop and ask the human, echoing the raw reply for context.
|
|
91
182
|
return { kind: "human", reason: cap(raw) };
|
|
92
183
|
}
|
|
93
184
|
//# sourceMappingURL=autopilot-verdict.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autopilot-verdict.js","sourceRoot":"","sources":["../../src/core/autopilot-verdict.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"autopilot-verdict.js","sourceRoot":"","sources":["../../src/core/autopilot-verdict.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,16 @@ describe("parseAutopilotVerdict", () => {
|
|
|
8
8
|
expect(parseAutopilotVerdict("all clear")).toEqual({ kind: "all_clear" });
|
|
9
9
|
expect(parseAutopilotVerdict("All Clear\nlooks good")).toEqual({ kind: "all_clear" });
|
|
10
10
|
});
|
|
11
|
+
it("parses IGNORE", () => {
|
|
12
|
+
expect(parseAutopilotVerdict("IGNORE")).toEqual({ kind: "ignore" });
|
|
13
|
+
});
|
|
14
|
+
it("parses fuzzy IGNORE (lowercase, trailing colon, extra text)", () => {
|
|
15
|
+
expect(parseAutopilotVerdict("ignore")).toEqual({ kind: "ignore" });
|
|
16
|
+
expect(parseAutopilotVerdict("Ignore:\nnothing to review")).toEqual({ kind: "ignore" });
|
|
17
|
+
});
|
|
18
|
+
it("parses SKIP as an alias for IGNORE", () => {
|
|
19
|
+
expect(parseAutopilotVerdict("SKIP")).toEqual({ kind: "ignore" });
|
|
20
|
+
});
|
|
11
21
|
it("parses PROMPT with a multi-line body", () => {
|
|
12
22
|
const reply = "PROMPT\nAdd a test for the login flow.\nRun it and confirm it passes.";
|
|
13
23
|
expect(parseAutopilotVerdict(reply)).toEqual({
|
|
@@ -76,5 +86,68 @@ describe("parseAutopilotVerdict", () => {
|
|
|
76
86
|
it("skips leading blank lines before the keyword", () => {
|
|
77
87
|
expect(parseAutopilotVerdict("\n\nALL_CLEAR")).toEqual({ kind: "all_clear" });
|
|
78
88
|
});
|
|
89
|
+
it("recovers ALL_CLEAR when Nolan adds commentary before a trailing bare keyword line", () => {
|
|
90
|
+
const reply = "The label is now a plain non-clickable <span>, model name is the separate " +
|
|
91
|
+
"clickable button. Matches the request exactly. Typecheck passed.\nALL_CLEAR";
|
|
92
|
+
expect(parseAutopilotVerdict(reply)).toEqual({ kind: "all_clear" });
|
|
93
|
+
});
|
|
94
|
+
it("recovers IGNORE from a trailing bare keyword line after commentary", () => {
|
|
95
|
+
const reply = "Just a formatting fix, nothing to review here.\nIGNORE";
|
|
96
|
+
expect(parseAutopilotVerdict(reply)).toEqual({ kind: "ignore" });
|
|
97
|
+
});
|
|
98
|
+
it("recovers a buried PROMPT with the body on following lines", () => {
|
|
99
|
+
const reply = "Some commentary about the change.\nPROMPT\nFix the bug.";
|
|
100
|
+
expect(parseAutopilotVerdict(reply)).toEqual({ kind: "prompt", body: "Fix the bug." });
|
|
101
|
+
});
|
|
102
|
+
it("recovers a buried PROMPT with an inline body (the real drift shape)", () => {
|
|
103
|
+
// The exact drift that stalled a live autopilot cycle: reasoning prose
|
|
104
|
+
// first, then `PROMPT <body>` on one line — used to parse as a HUMAN stop
|
|
105
|
+
// and dump the whole reply into the chat as a Nolan bubble.
|
|
106
|
+
const reply = "The diagnosis is solid and confirmed on disk. The fix is mechanically " +
|
|
107
|
+
"implied by the original ask. Safe to proceed without new user input.\n" +
|
|
108
|
+
"PROMPT Apply the fix: guard AgentSession.compact() on this.opts.transient.";
|
|
109
|
+
const v = parseAutopilotVerdict(reply);
|
|
110
|
+
expect(v.kind).toBe("prompt");
|
|
111
|
+
if (v.kind === "prompt") {
|
|
112
|
+
expect(v.body).toBe("Apply the fix: guard AgentSession.compact() on this.opts.transient.");
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
it("never matches lowercase or mid-line 'prompt' prose as a buried PROMPT", () => {
|
|
116
|
+
// Buried recovery is uppercase + line-start only — casual prose mentioning
|
|
117
|
+
// "prompt" after an unrecognized first line must stay a HUMAN stop.
|
|
118
|
+
expect(parseAutopilotVerdict("Waiting on input.\nprompt the user for their API key first.").kind).toBe("human");
|
|
119
|
+
expect(parseAutopilotVerdict("Unclear ask.\nWe should write a prompt for EZ Coder here.").kind).toBe("human");
|
|
120
|
+
});
|
|
121
|
+
it("prefers a buried HUMAN over a buried PROMPT — stops beat actions on ties", () => {
|
|
122
|
+
const reply = "Recap first.\nPROMPT Fix the thing.\nHUMAN\nActually the user must decide.";
|
|
123
|
+
const v = parseAutopilotVerdict(reply);
|
|
124
|
+
expect(v.kind).toBe("human");
|
|
125
|
+
if (v.kind === "human")
|
|
126
|
+
expect(v.reason).toBe("Actually the user must decide.");
|
|
127
|
+
});
|
|
128
|
+
it("downgrades a buried bare PROMPT with no body to human", () => {
|
|
129
|
+
const v = parseAutopilotVerdict("Recap of what happened.\nPROMPT");
|
|
130
|
+
expect(v.kind).toBe("human");
|
|
131
|
+
});
|
|
132
|
+
it("recovers a buried HUMAN, dropping the leading reasoning prose", () => {
|
|
133
|
+
// Nolan drifted: he wrote his whole recap first, THEN the verdict. Recover the
|
|
134
|
+
// reason after the keyword and drop the prose — HUMAN stops either way.
|
|
135
|
+
const reply = "The screenshot shows a clean squared inward spiral that matches the " +
|
|
136
|
+
"reference. Tests green. EZ Coder asked whether to dress it up with art — " +
|
|
137
|
+
"that's a taste/product call the user should own.\nHUMAN\nStructural spiral " +
|
|
138
|
+
"is done; dressing it up with art is a taste call only you can make.";
|
|
139
|
+
const v = parseAutopilotVerdict(reply);
|
|
140
|
+
expect(v.kind).toBe("human");
|
|
141
|
+
if (v.kind === "human") {
|
|
142
|
+
expect(v.reason).toBe("Structural spiral is done; dressing it up with art is a taste call only you can make.");
|
|
143
|
+
expect(v.reason).not.toContain("The screenshot shows");
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
it("gives a buried bare HUMAN with no trailing reason the default reason", () => {
|
|
147
|
+
const v = parseAutopilotVerdict("Long recap of the work with no verdict payload.\nHUMAN");
|
|
148
|
+
expect(v.kind).toBe("human");
|
|
149
|
+
if (v.kind === "human")
|
|
150
|
+
expect(v.reason.length).toBeGreaterThan(0);
|
|
151
|
+
});
|
|
79
152
|
});
|
|
80
153
|
//# sourceMappingURL=autopilot-verdict.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autopilot-verdict.test.js","sourceRoot":"","sources":["../../src/core/autopilot-verdict.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"autopilot-verdict.test.js","sourceRoot":"","sources":["../../src/core/autopilot-verdict.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-spawn-benchmark.d.ts","sourceRoot":"","sources":["../../src/core/bash-spawn-benchmark.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-call spawn (what tools/bash.ts does today) vs a persistent shell session
|
|
3
|
+
* (what oh-my-pi's embedded "brush" bash gives them) — local measurement, no
|
|
4
|
+
* LLM calls needed.
|
|
5
|
+
*
|
|
6
|
+
* Strategy A — SPAWN (current): every command is `spawn("bash", ["-c", cmd])`,
|
|
7
|
+
* a fresh process with fresh env/cwd. Cost = fork/exec + shell startup per call.
|
|
8
|
+
*
|
|
9
|
+
* Strategy B — PERSISTENT: one long-lived `bash` process; commands are written
|
|
10
|
+
* to stdin and delimited with a sentinel echo. Cost ≈ pipe round-trip.
|
|
11
|
+
* Bonus property (qualitative): cd / env / shell state survive across calls.
|
|
12
|
+
*
|
|
13
|
+
* We run the same command list through both and report total + per-call wall
|
|
14
|
+
* time. Commands are trivial on purpose — we are measuring HARNESS overhead,
|
|
15
|
+
* not the commands themselves.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* npx tsx src/core/bash-spawn-benchmark.ts
|
|
19
|
+
*
|
|
20
|
+
* Env overrides:
|
|
21
|
+
* GG_BASH_BENCH_N (commands per strategy, default 30)
|
|
22
|
+
*/
|
|
23
|
+
import { spawn } from "node:child_process";
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
25
|
+
const COMMANDS = ["echo hello", "pwd", "ls / > /dev/null", "printf '%s' done", "true"];
|
|
26
|
+
function buildCommandList(n) {
|
|
27
|
+
const out = [];
|
|
28
|
+
for (let i = 0; i < n; i++)
|
|
29
|
+
out.push(COMMANDS[i % COMMANDS.length]);
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
// ── Strategy A: fresh spawn per call (mirrors tools/bash.ts) ──
|
|
33
|
+
function runSpawned(cmd) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const start = performance.now();
|
|
36
|
+
const child = spawn("bash", ["-c", cmd], { stdio: ["ignore", "pipe", "pipe"] });
|
|
37
|
+
child.stdout.on("data", () => { });
|
|
38
|
+
child.stderr.on("data", () => { });
|
|
39
|
+
child.on("error", reject);
|
|
40
|
+
child.on("close", () => resolve(performance.now() - start));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// ── Strategy B: one persistent bash, sentinel-delimited ──
|
|
44
|
+
class PersistentShell {
|
|
45
|
+
child = spawn("bash", ["--norc", "--noprofile"], {
|
|
46
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
47
|
+
});
|
|
48
|
+
buffer = "";
|
|
49
|
+
waiter = null;
|
|
50
|
+
constructor() {
|
|
51
|
+
this.child.stdout.on("data", (d) => {
|
|
52
|
+
this.buffer += d.toString();
|
|
53
|
+
if (this.waiter && this.buffer.includes(this.waiter.sentinel)) {
|
|
54
|
+
const w = this.waiter;
|
|
55
|
+
this.waiter = null;
|
|
56
|
+
this.buffer = "";
|
|
57
|
+
w.resolve();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
this.child.stderr.on("data", () => { });
|
|
61
|
+
}
|
|
62
|
+
run(cmd) {
|
|
63
|
+
const start = performance.now();
|
|
64
|
+
const sentinel = `__GG_DONE_${randomUUID()}__`;
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
this.waiter = { sentinel, resolve: () => resolve(performance.now() - start) };
|
|
67
|
+
this.child.stdin.write(`${cmd}\necho ${sentinel}\n`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** Prove state persistence: cd + env var survive across run() calls. */
|
|
71
|
+
async statePersists() {
|
|
72
|
+
await this.run("cd /tmp && export GG_BENCH_STATE=alive");
|
|
73
|
+
const sentinel = `__GG_STATE_${randomUUID()}__`;
|
|
74
|
+
const out = await new Promise((resolve) => {
|
|
75
|
+
let acc = "";
|
|
76
|
+
const onData = (d) => {
|
|
77
|
+
acc += d.toString();
|
|
78
|
+
if (acc.includes(sentinel)) {
|
|
79
|
+
this.child.stdout.off("data", onData);
|
|
80
|
+
resolve(acc);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
this.child.stdout.on("data", onData);
|
|
84
|
+
this.child.stdin.write(`pwd; echo "$GG_BENCH_STATE"; echo ${sentinel}\n`);
|
|
85
|
+
});
|
|
86
|
+
return out.includes("/tmp") && out.includes("alive");
|
|
87
|
+
}
|
|
88
|
+
close() {
|
|
89
|
+
this.child.stdin.end();
|
|
90
|
+
this.child.kill();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ── Stats ──
|
|
94
|
+
function stats(samples) {
|
|
95
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
96
|
+
const total = samples.reduce((s, x) => s + x, 0);
|
|
97
|
+
const at = (q) => sorted[Math.min(sorted.length - 1, Math.floor(q * sorted.length))];
|
|
98
|
+
return { total, mean: total / samples.length, p50: at(0.5), p95: at(0.95) };
|
|
99
|
+
}
|
|
100
|
+
function fmt(s) {
|
|
101
|
+
return (`total ${s.total.toFixed(0)}ms | mean ${s.mean.toFixed(1)}ms | ` +
|
|
102
|
+
`p50 ${s.p50.toFixed(1)}ms | p95 ${s.p95.toFixed(1)}ms`);
|
|
103
|
+
}
|
|
104
|
+
async function main() {
|
|
105
|
+
const n = Math.max(5, parseInt(process.env.GG_BASH_BENCH_N ?? "30", 10));
|
|
106
|
+
const cmds = buildCommandList(n);
|
|
107
|
+
console.log(`\n🐚 Bash harness overhead — ${n} commands per strategy\n`);
|
|
108
|
+
// Warm both paths once so first-touch costs don't skew either side.
|
|
109
|
+
await runSpawned("true");
|
|
110
|
+
const shell = new PersistentShell();
|
|
111
|
+
await shell.run("true");
|
|
112
|
+
const spawnTimes = [];
|
|
113
|
+
for (const c of cmds)
|
|
114
|
+
spawnTimes.push(await runSpawned(c));
|
|
115
|
+
const persistTimes = [];
|
|
116
|
+
for (const c of cmds)
|
|
117
|
+
persistTimes.push(await shell.run(c));
|
|
118
|
+
const persists = await shell.statePersists();
|
|
119
|
+
shell.close();
|
|
120
|
+
const a = stats(spawnTimes);
|
|
121
|
+
const b = stats(persistTimes);
|
|
122
|
+
console.log(`SPAWN-PER-CALL (current): ${fmt(a)}`);
|
|
123
|
+
console.log(`PERSISTENT SHELL (theirs): ${fmt(b)}`);
|
|
124
|
+
const saved = a.mean - b.mean;
|
|
125
|
+
console.log(`\nPer-call overhead saved: ${saved.toFixed(1)}ms ` +
|
|
126
|
+
`(${((saved / a.mean) * 100).toFixed(0)}% of mean call cost)`);
|
|
127
|
+
console.log(`State persistence across calls (cd/env): ${persists ? "YES" : "NO"}`);
|
|
128
|
+
console.log(`\nVerdict: persistent shell wins if per-call saving is meaningful at agent scale ` +
|
|
129
|
+
`(a long session runs hundreds of bash calls) AND state persistence is a UX win.\n`);
|
|
130
|
+
}
|
|
131
|
+
const isDirectRun = process.argv[1]?.endsWith("bash-spawn-benchmark.ts") ||
|
|
132
|
+
process.argv[1]?.endsWith("bash-spawn-benchmark.js");
|
|
133
|
+
if (isDirectRun) {
|
|
134
|
+
main().catch((err) => {
|
|
135
|
+
console.error("Benchmark failed:", err);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=bash-spawn-benchmark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-spawn-benchmark.js","sourceRoot":"","sources":["../../src/core/bash-spawn-benchmark.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,11 +21,16 @@
|
|
|
21
21
|
* the edit applied cleanly + produced the correct file, and an anchor-uniqueness
|
|
22
22
|
* / safety check. Edits are graded deterministically — no second model needed.
|
|
23
23
|
*
|
|
24
|
+
* The BASELINE apply path runs ezcoder's REAL edit ladder (fuzzy match +
|
|
25
|
+
* indent-flex + blank-edge strip + dotdotdots — the same edit-diff functions
|
|
26
|
+
* tools/edit.ts uses), so baseline numbers reflect what our tool actually
|
|
27
|
+
* recovers, not a naive exact-replace.
|
|
28
|
+
*
|
|
24
29
|
* Usage:
|
|
25
30
|
* npx tsx src/core/hashline-edit-benchmark.ts
|
|
26
31
|
*
|
|
27
32
|
* Env overrides:
|
|
28
|
-
* GG_HL_PROVIDER / GG_HL_MODEL (default
|
|
33
|
+
* GG_HL_PROVIDER / GG_HL_MODEL (default anthropic / claude-sonnet-5)
|
|
29
34
|
* GG_HL_REPEAT (runs per task, default 1 — raise to average noise)
|
|
30
35
|
*/
|
|
31
36
|
import { type AnchoredFile } from "./hashline.js";
|
|
@@ -38,8 +43,12 @@ export interface EditTask {
|
|
|
38
43
|
mustContain: string[];
|
|
39
44
|
/** Substrings that MUST still be present (unchanged anchors far from the edit). */
|
|
40
45
|
mustPreserve: string[];
|
|
46
|
+
/** Optional extra validator for tasks where substring checks are too weak. */
|
|
47
|
+
validate?: (file: string) => boolean;
|
|
41
48
|
}
|
|
42
49
|
export declare function genFile(lines: number): string;
|
|
50
|
+
/** Near-identical handler blocks — uniqueness pressure for string matching. */
|
|
51
|
+
export declare function genRepetitiveFile(blocks: number): string;
|
|
43
52
|
export declare function buildTasks(): EditTask[];
|
|
44
53
|
export interface ApplyOutcome {
|
|
45
54
|
applied: boolean;
|
|
@@ -49,7 +58,30 @@ export interface ApplyOutcome {
|
|
|
49
58
|
parsedEdits: number;
|
|
50
59
|
}
|
|
51
60
|
export declare function stripFence(s: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Parse the model's JSON envelope, tolerating literal (unescaped) newlines and
|
|
63
|
+
* tabs inside string values. In production, edits arrive as STRUCTURED
|
|
64
|
+
* tool-call arguments — the JSON envelope is a benchmark artifact — so both
|
|
65
|
+
* strategies get the same repair to keep the comparison about the EDIT FORMAT,
|
|
66
|
+
* not about JSON string-escaping discipline.
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseEnvelope<T>(raw: string): T | null;
|
|
52
69
|
export declare function checkAnchors(file: string, task: EditTask): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* ezcoder's REAL apply ladder, mirroring tools/edit.ts:
|
|
72
|
+
* 1. exact + smart-quote/dash fuzzy
|
|
73
|
+
* 2. indent-flex
|
|
74
|
+
* 3. blank-edge strip, retry 1+2
|
|
75
|
+
* 4. dotdotdots elision
|
|
76
|
+
* Returns the new working buffer or a failure reason.
|
|
77
|
+
*/
|
|
78
|
+
export declare function applyLadder(working: string, oldText: string, newText: string): {
|
|
79
|
+
ok: true;
|
|
80
|
+
working: string;
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
reason: "not_found" | "ambiguous";
|
|
84
|
+
};
|
|
53
85
|
export declare function applyBaseline(raw: string, task: EditTask): ApplyOutcome;
|
|
54
86
|
export declare function applyHashline(raw: string, task: EditTask, anchored: AnchoredFile): ApplyOutcome;
|
|
55
87
|
//# sourceMappingURL=hashline-edit-benchmark.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hashline-edit-benchmark.d.ts","sourceRoot":"","sources":["../../src/core/hashline-edit-benchmark.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hashline-edit-benchmark.d.ts","sourceRoot":"","sources":["../../src/core/hashline-edit-benchmark.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAW9D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mFAAmF;IACnF,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqC7C;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBxD;AAED,wBAAgB,UAAU,IAAI,QAAQ,EAAE,CAwDvC;AA2CD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM5C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAkDtD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAKlE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAAA;CAAE,CAiClF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,YAAY,CA0BvE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,GAAG,YAAY,CAmC/F"}
|