@prestyj/cli 5.1.1 → 5.3.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/README.md +2 -2
- package/dist/app-sidecar.js +506 -34
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +19 -2
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +80 -27
- package/dist/cli.js.map +1 -1
- package/dist/config.js +32 -30
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +1 -1
- 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 +25 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +68 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agent-tools-allowlist.test.d.ts +2 -0
- package/dist/core/agent-tools-allowlist.test.d.ts.map +1 -0
- package/dist/core/agent-tools-allowlist.test.js +72 -0
- package/dist/core/agent-tools-allowlist.test.js.map +1 -0
- package/dist/core/auth-providers.d.ts +21 -0
- package/dist/core/auth-providers.d.ts.map +1 -1
- package/dist/core/auth-providers.js +15 -2
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/autopilot-cycle.d.ts +67 -0
- package/dist/core/autopilot-cycle.d.ts.map +1 -0
- package/dist/core/autopilot-cycle.js +50 -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 +179 -0
- package/dist/core/autopilot-cycle.test.js.map +1 -0
- package/dist/core/autopilot-gate.d.ts +83 -0
- package/dist/core/autopilot-gate.d.ts.map +1 -0
- package/dist/core/autopilot-gate.js +96 -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 +159 -0
- package/dist/core/autopilot-gate.test.js.map +1 -0
- package/dist/core/autopilot-verdict.d.ts +44 -0
- package/dist/core/autopilot-verdict.d.ts.map +1 -0
- package/dist/core/autopilot-verdict.js +110 -0
- package/dist/core/autopilot-verdict.js.map +1 -0
- package/dist/core/autopilot-verdict.test.d.ts +2 -0
- package/dist/core/autopilot-verdict.test.d.ts.map +1 -0
- package/dist/core/autopilot-verdict.test.js +90 -0
- package/dist/core/autopilot-verdict.test.js.map +1 -0
- package/dist/core/compaction/compactor.test.js +2 -2
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/token-estimator.test.js +3 -3
- package/dist/core/compaction/token-estimator.test.js.map +1 -1
- package/dist/core/event-bus.d.ts +4 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +6 -0
- package/dist/core/event-bus.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/nolan-context.d.ts +35 -0
- package/dist/core/nolan-context.d.ts.map +1 -1
- package/dist/core/nolan-context.js +63 -3
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-context.test.js +144 -1
- 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 +10 -0
- package/dist/core/nolan-prompt.d.ts.map +1 -1
- package/dist/core/nolan-prompt.js +60 -0
- 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 +43 -0
- package/dist/core/nolan-prompt.test.js.map +1 -0
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +17 -3
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/prompt-commands.test.js +27 -1
- package/dist/core/prompt-commands.test.js.map +1 -1
- package/dist/core/session-manager.test.js +3 -1
- package/dist/core/session-manager.test.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/core/task-store.d.ts +7 -0
- package/dist/core/task-store.d.ts.map +1 -1
- package/dist/core/task-store.js +13 -0
- package/dist/core/task-store.js.map +1 -1
- package/dist/modes/json-mode.d.ts +7 -0
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +4 -0
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/system-prompt.test.js +4 -4
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/tools/plan-mode.test.js +1 -1
- 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 +4 -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 -2
- package/dist/tools/subagent.js.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +21 -5
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +1 -0
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/Footer.js +1 -1
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useContextCompaction.d.ts.map +1 -1
- package/dist/ui/hooks/useContextCompaction.js +4 -2
- package/dist/ui/hooks/useContextCompaction.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +6 -2
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/thinking-level-cycle.test.js +1 -1
- package/dist/ui/thinking-level-cycle.test.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autopilot gate — pure decision logic for whether Nolan's auto-review cycle may
|
|
3
|
+
* start after a finished EZ Coder turn.
|
|
4
|
+
*
|
|
5
|
+
* Autopilot must NOT review every turn. The concrete leak cases this gate
|
|
6
|
+
* closes (each has a matching unit test in autopilot-gate.test.ts):
|
|
7
|
+
*
|
|
8
|
+
* - Workflow slash commands (`/compare`, `/bullet-proof`, `/expand`, custom
|
|
9
|
+
* `.ezcoder/commands/*.md`) end with reports or A/B/C choices that are reserved
|
|
10
|
+
* for the USER. Nolan reviewing them reads "findings" as "something real is
|
|
11
|
+
* wrong" and injects fix prompts the user never approved.
|
|
12
|
+
* - Registry commands (`/help`, `/session`, unknown `/foo`) and failed runs
|
|
13
|
+
* add no assistant work at all — a review would judge the PREVIOUS turn
|
|
14
|
+
* again (Nolan's cycle memory is wiped per turn) and can flip a settled
|
|
15
|
+
* ALL_CLEAR into a fresh PROMPT.
|
|
16
|
+
* - A turn that ended in plan mode has a pending Accept/Reject modal; Nolan must
|
|
17
|
+
* never inject a prompt into a read-only plan-mode session.
|
|
18
|
+
*
|
|
19
|
+
* Kept pure + dependency-light so it's unit-testable without booting the
|
|
20
|
+
* sidecar (which runs `main()` at import time).
|
|
21
|
+
*/
|
|
22
|
+
/** The exact separator AgentSession.prompt() inserts between a command's
|
|
23
|
+
* template and the user's extra args (see agent-session.ts prompt expansion).
|
|
24
|
+
* Must stay byte-identical or expanded-command detection silently breaks. */
|
|
25
|
+
export const USER_INSTRUCTIONS_HEADER = "\n\n## User Instructions\n\n";
|
|
26
|
+
/** Extract the `/name` token from raw input, or null when it isn't a slash
|
|
27
|
+
* invocation. */
|
|
28
|
+
function parseSlashName(text) {
|
|
29
|
+
const trimmed = text.trim();
|
|
30
|
+
if (!trimmed.startsWith("/"))
|
|
31
|
+
return null;
|
|
32
|
+
const name = trimmed.slice(1).split(/\s/, 1)[0]?.toLowerCase() ?? "";
|
|
33
|
+
return name.length > 0 ? name : null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* True when `text` invokes a known workflow command (first token, name or
|
|
37
|
+
* alias, case-insensitive). Registry/UI commands and unknown `/foo` return
|
|
38
|
+
* false — those add no assistant work and are caught by the
|
|
39
|
+
* `no-assistant-output` gate instead.
|
|
40
|
+
*/
|
|
41
|
+
export function isWorkflowCommandText(text, commands) {
|
|
42
|
+
const name = parseSlashName(text);
|
|
43
|
+
if (!name)
|
|
44
|
+
return false;
|
|
45
|
+
return commands.some((c) => c.name.toLowerCase() === name || (c.aliases ?? []).some((a) => a.toLowerCase() === name));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Match a transcript user-message body back to the workflow command it was
|
|
49
|
+
* expanded from. AgentSession stores the EXPANDED template as a plain user
|
|
50
|
+
* message, so without this Nolan's digest renders 400-line templates as
|
|
51
|
+
* `**User:** …` and treats them as user-authored asks.
|
|
52
|
+
*
|
|
53
|
+
* Returns the matched command plus any trailing user args, or null.
|
|
54
|
+
*/
|
|
55
|
+
export function matchExpandedCommand(text, commands) {
|
|
56
|
+
for (const command of commands) {
|
|
57
|
+
if (!command.prompt)
|
|
58
|
+
continue;
|
|
59
|
+
if (text === command.prompt)
|
|
60
|
+
return { command, args: null };
|
|
61
|
+
const prefix = command.prompt + USER_INSTRUCTIONS_HEADER;
|
|
62
|
+
if (text.startsWith(prefix)) {
|
|
63
|
+
const args = text.slice(prefix.length).trim();
|
|
64
|
+
return { command, args: args.length > 0 ? args : null };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/** Count assistant messages — the "did this run produce reviewable work"
|
|
70
|
+
* signal. Compared before/after a run by the sidecar. */
|
|
71
|
+
export function countAssistantMessages(messages) {
|
|
72
|
+
let count = 0;
|
|
73
|
+
for (const m of messages)
|
|
74
|
+
if (m.role === "assistant")
|
|
75
|
+
count++;
|
|
76
|
+
return count;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Decide whether the autopilot cycle may start for a just-finished turn.
|
|
80
|
+
* Checks are ordered cheapest/most-fundamental first; the reason is logged by
|
|
81
|
+
* the sidecar so skips are debuggable from ezcoder-app-sidecar.log.
|
|
82
|
+
*/
|
|
83
|
+
export function shouldStartAutopilotCycle(input) {
|
|
84
|
+
if (!input.enabled)
|
|
85
|
+
return { start: false, reason: "disabled" };
|
|
86
|
+
if (input.cancelled)
|
|
87
|
+
return { start: false, reason: "cancelled" };
|
|
88
|
+
if (input.planMode)
|
|
89
|
+
return { start: false, reason: "plan-mode" };
|
|
90
|
+
if (input.workflowCommand)
|
|
91
|
+
return { start: false, reason: "workflow-command" };
|
|
92
|
+
if (input.assistantMessagesAdded <= 0)
|
|
93
|
+
return { start: false, reason: "no-assistant-output" };
|
|
94
|
+
return { start: true };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=autopilot-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-gate.js","sourceRoot":"","sources":["../../src/core/autopilot-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-gate.test.d.ts","sourceRoot":"","sources":["../../src/core/autopilot-gate.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isWorkflowCommandText, matchExpandedCommand, countAssistantMessages, shouldStartAutopilotCycle, USER_INSTRUCTIONS_HEADER, } from "./autopilot-gate.js";
|
|
3
|
+
import { PROMPT_COMMANDS } from "./prompt-commands.js";
|
|
4
|
+
const COMMANDS = [
|
|
5
|
+
{ name: "compare", aliases: [], prompt: "Compare the code you just created…" },
|
|
6
|
+
{ name: "bullet-proof", aliases: ["bp"], prompt: "Audit the project…" },
|
|
7
|
+
];
|
|
8
|
+
describe("isWorkflowCommandText", () => {
|
|
9
|
+
it("matches a bare command", () => {
|
|
10
|
+
expect(isWorkflowCommandText("/compare", COMMANDS)).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it("matches a command with trailing args", () => {
|
|
13
|
+
expect(isWorkflowCommandText("/compare src/foo.ts please", COMMANDS)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it("matches aliases and is case-insensitive", () => {
|
|
16
|
+
expect(isWorkflowCommandText("/bp", COMMANDS)).toBe(true);
|
|
17
|
+
expect(isWorkflowCommandText("/COMPARE", COMMANDS)).toBe(true);
|
|
18
|
+
expect(isWorkflowCommandText("/Bullet-Proof", COMMANDS)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
it("tolerates leading whitespace (matches trim semantics of the prompt path)", () => {
|
|
21
|
+
expect(isWorkflowCommandText(" /compare", COMMANDS)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("rejects unknown commands, non-slash text, and prefix collisions", () => {
|
|
24
|
+
expect(isWorkflowCommandText("/help", COMMANDS)).toBe(false);
|
|
25
|
+
expect(isWorkflowCommandText("/comparex", COMMANDS)).toBe(false);
|
|
26
|
+
expect(isWorkflowCommandText("compare the files", COMMANDS)).toBe(false);
|
|
27
|
+
expect(isWorkflowCommandText("run /compare for me", COMMANDS)).toBe(false);
|
|
28
|
+
expect(isWorkflowCommandText("/", COMMANDS)).toBe(false);
|
|
29
|
+
expect(isWorkflowCommandText("", COMMANDS)).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it("recognizes EVERY built-in workflow command (the real leak set)", () => {
|
|
32
|
+
// The reported bug: /compare fired an autopilot review of a report-only
|
|
33
|
+
// command. Prove the gate recognizes every shipped PROMPT_COMMAND (and its
|
|
34
|
+
// aliases) so none of them can leak into a review.
|
|
35
|
+
for (const cmd of PROMPT_COMMANDS) {
|
|
36
|
+
expect(isWorkflowCommandText(`/${cmd.name}`, PROMPT_COMMANDS)).toBe(true);
|
|
37
|
+
expect(isWorkflowCommandText(`/${cmd.name} with args`, PROMPT_COMMANDS)).toBe(true);
|
|
38
|
+
for (const alias of cmd.aliases) {
|
|
39
|
+
expect(isWorkflowCommandText(`/${alias}`, PROMPT_COMMANDS)).toBe(true);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("matchExpandedCommand", () => {
|
|
45
|
+
it("matches an exact template body", () => {
|
|
46
|
+
const m = matchExpandedCommand("Compare the code you just created…", COMMANDS);
|
|
47
|
+
expect(m?.command.name).toBe("compare");
|
|
48
|
+
expect(m?.args).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
it("matches template + user-instructions suffix and extracts the args", () => {
|
|
51
|
+
const text = `Audit the project…${USER_INSTRUCTIONS_HEADER}only the auth module`;
|
|
52
|
+
const m = matchExpandedCommand(text, COMMANDS);
|
|
53
|
+
expect(m?.command.name).toBe("bullet-proof");
|
|
54
|
+
expect(m?.args).toBe("only the auth module");
|
|
55
|
+
});
|
|
56
|
+
it("uses the exact separator AgentSession.prompt() inserts", () => {
|
|
57
|
+
// Lockstep guard: if agent-session.ts ever changes its expansion format,
|
|
58
|
+
// this literal must change with it or digest labeling silently breaks.
|
|
59
|
+
expect(USER_INSTRUCTIONS_HEADER).toBe("\n\n## User Instructions\n\n");
|
|
60
|
+
});
|
|
61
|
+
it("returns null for ordinary user text and near-misses", () => {
|
|
62
|
+
expect(matchExpandedCommand("compare my code to the repo", COMMANDS)).toBeNull();
|
|
63
|
+
// Template body with arbitrary extra text but no separator is NOT an expansion.
|
|
64
|
+
expect(matchExpandedCommand("Compare the code you just created… and also do X", COMMANDS)).toBeNull();
|
|
65
|
+
expect(matchExpandedCommand("", COMMANDS)).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
it("matches every real built-in template round-tripped through expansion", () => {
|
|
68
|
+
for (const cmd of PROMPT_COMMANDS) {
|
|
69
|
+
// Mirrors AgentSession.prompt(): bare command → template verbatim.
|
|
70
|
+
expect(matchExpandedCommand(cmd.prompt, PROMPT_COMMANDS)?.command.name).toBe(cmd.name);
|
|
71
|
+
// Command with args → template + header + args.
|
|
72
|
+
const withArgs = `${cmd.prompt}${USER_INSTRUCTIONS_HEADER}focus on src/`;
|
|
73
|
+
const m = matchExpandedCommand(withArgs, PROMPT_COMMANDS);
|
|
74
|
+
expect(m?.command.name).toBe(cmd.name);
|
|
75
|
+
expect(m?.args).toBe("focus on src/");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("countAssistantMessages", () => {
|
|
80
|
+
it("counts only assistant-role messages", () => {
|
|
81
|
+
expect(countAssistantMessages([
|
|
82
|
+
{ role: "system" },
|
|
83
|
+
{ role: "user" },
|
|
84
|
+
{ role: "assistant" },
|
|
85
|
+
{ role: "tool" },
|
|
86
|
+
{ role: "assistant" },
|
|
87
|
+
])).toBe(2);
|
|
88
|
+
expect(countAssistantMessages([])).toBe(0);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("shouldStartAutopilotCycle", () => {
|
|
92
|
+
const reviewable = {
|
|
93
|
+
enabled: true,
|
|
94
|
+
cancelled: false,
|
|
95
|
+
planMode: false,
|
|
96
|
+
workflowCommand: false,
|
|
97
|
+
assistantMessagesAdded: 1,
|
|
98
|
+
};
|
|
99
|
+
it("starts for a normal reviewable turn", () => {
|
|
100
|
+
expect(shouldStartAutopilotCycle(reviewable)).toEqual({ start: true });
|
|
101
|
+
});
|
|
102
|
+
it("skips when autopilot is off", () => {
|
|
103
|
+
expect(shouldStartAutopilotCycle({ ...reviewable, enabled: false })).toEqual({
|
|
104
|
+
start: false,
|
|
105
|
+
reason: "disabled",
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
it("skips when the turn was cancelled", () => {
|
|
109
|
+
expect(shouldStartAutopilotCycle({ ...reviewable, cancelled: true })).toEqual({
|
|
110
|
+
start: false,
|
|
111
|
+
reason: "cancelled",
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
it("skips when the turn ended in plan mode (pending Accept/Reject modal)", () => {
|
|
115
|
+
expect(shouldStartAutopilotCycle({ ...reviewable, planMode: true })).toEqual({
|
|
116
|
+
start: false,
|
|
117
|
+
reason: "plan-mode",
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
it("skips workflow-command turns (/compare — the reported bug)", () => {
|
|
121
|
+
expect(shouldStartAutopilotCycle({ ...reviewable, workflowCommand: true })).toEqual({
|
|
122
|
+
start: false,
|
|
123
|
+
reason: "workflow-command",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
it("skips turns that added no assistant output (/help, unknown /foo, failed runs)", () => {
|
|
127
|
+
expect(shouldStartAutopilotCycle({ ...reviewable, assistantMessagesAdded: 0 })).toEqual({
|
|
128
|
+
start: false,
|
|
129
|
+
reason: "no-assistant-output",
|
|
130
|
+
});
|
|
131
|
+
// A run that somehow REMOVED messages (defensive) also never reviews.
|
|
132
|
+
expect(shouldStartAutopilotCycle({ ...reviewable, assistantMessagesAdded: -1 })).toEqual({
|
|
133
|
+
start: false,
|
|
134
|
+
reason: "no-assistant-output",
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
it("resolves multiple skip conditions in fixed priority order", () => {
|
|
138
|
+
// disabled beats everything; plan-mode beats workflow-command beats no-output.
|
|
139
|
+
expect(shouldStartAutopilotCycle({
|
|
140
|
+
enabled: false,
|
|
141
|
+
cancelled: true,
|
|
142
|
+
planMode: true,
|
|
143
|
+
workflowCommand: true,
|
|
144
|
+
assistantMessagesAdded: 0,
|
|
145
|
+
})).toEqual({ start: false, reason: "disabled" });
|
|
146
|
+
expect(shouldStartAutopilotCycle({
|
|
147
|
+
...reviewable,
|
|
148
|
+
planMode: true,
|
|
149
|
+
workflowCommand: true,
|
|
150
|
+
assistantMessagesAdded: 0,
|
|
151
|
+
})).toEqual({ start: false, reason: "plan-mode" });
|
|
152
|
+
expect(shouldStartAutopilotCycle({
|
|
153
|
+
...reviewable,
|
|
154
|
+
workflowCommand: true,
|
|
155
|
+
assistantMessagesAdded: 0,
|
|
156
|
+
})).toEqual({ start: false, reason: "workflow-command" });
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
//# sourceMappingURL=autopilot-gate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-gate.test.js","sourceRoot":"","sources":["../../src/core/autopilot-gate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autopilot Nolan's verdict contract.
|
|
3
|
+
*
|
|
4
|
+
* In autopilot mode Nolan never talks to the user — he auto-reviews EZ Coder's
|
|
5
|
+
* work and replies with exactly one of four machine-parseable verdicts. The
|
|
6
|
+
* first non-empty line carries the keyword; anything after is the payload.
|
|
7
|
+
*
|
|
8
|
+
* PROMPT
|
|
9
|
+
* <runnable EZ Coder prompt body, 1-3 lines>
|
|
10
|
+
*
|
|
11
|
+
* ALL_CLEAR
|
|
12
|
+
*
|
|
13
|
+
* IGNORE
|
|
14
|
+
*
|
|
15
|
+
* HUMAN
|
|
16
|
+
* <one short reason the human is needed>
|
|
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
|
+
*
|
|
26
|
+
* Parsing is forgiving and safe-by-default: any reply we can't confidently map
|
|
27
|
+
* to PROMPT, ALL_CLEAR, or IGNORE becomes a HUMAN stop, never a blind loop.
|
|
28
|
+
*/
|
|
29
|
+
export type AutopilotVerdict = {
|
|
30
|
+
kind: "prompt";
|
|
31
|
+
body: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "all_clear";
|
|
34
|
+
} | {
|
|
35
|
+
kind: "ignore";
|
|
36
|
+
} | {
|
|
37
|
+
kind: "human";
|
|
38
|
+
reason: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Parse Autopilot Nolan's raw reply into a verdict. Never throws.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseAutopilotVerdict(reply: string): AutopilotVerdict;
|
|
44
|
+
//# sourceMappingURL=autopilot-verdict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-verdict.d.ts","sourceRoot":"","sources":["../../src/core/autopilot-verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA0BtC;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAiErE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autopilot Nolan's verdict contract.
|
|
3
|
+
*
|
|
4
|
+
* In autopilot mode Nolan never talks to the user — he auto-reviews EZ Coder's
|
|
5
|
+
* work and replies with exactly one of four machine-parseable verdicts. The
|
|
6
|
+
* first non-empty line carries the keyword; anything after is the payload.
|
|
7
|
+
*
|
|
8
|
+
* PROMPT
|
|
9
|
+
* <runnable EZ Coder prompt body, 1-3 lines>
|
|
10
|
+
*
|
|
11
|
+
* ALL_CLEAR
|
|
12
|
+
*
|
|
13
|
+
* IGNORE
|
|
14
|
+
*
|
|
15
|
+
* HUMAN
|
|
16
|
+
* <one short reason the human is needed>
|
|
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
|
+
*
|
|
26
|
+
* Parsing is forgiving and safe-by-default: any reply we can't confidently map
|
|
27
|
+
* to PROMPT, ALL_CLEAR, or IGNORE becomes a HUMAN stop, never a blind loop.
|
|
28
|
+
*/
|
|
29
|
+
/** Cap on the raw-reply text we echo back as a HUMAN reason when Nolan's output
|
|
30
|
+
* is unrecognized — keeps a garbage/huge reply from bloating the transcript. */
|
|
31
|
+
const RAW_REASON_CAP = 500;
|
|
32
|
+
const DEFAULT_HUMAN_REASON = "Nolan flagged this for a human but gave no reason.";
|
|
33
|
+
/** Strip a leading/trailing ``` fence (optionally ```prompt) Nolan may have wrapped
|
|
34
|
+
* the prompt body in out of chat habit. */
|
|
35
|
+
function stripPromptFence(body) {
|
|
36
|
+
const trimmed = body.trim();
|
|
37
|
+
const fenceOpen = /^```[^\n]*\n?/;
|
|
38
|
+
const fenceClose = /\n?```$/;
|
|
39
|
+
if (fenceOpen.test(trimmed) && fenceClose.test(trimmed)) {
|
|
40
|
+
return trimmed.replace(fenceOpen, "").replace(fenceClose, "").trim();
|
|
41
|
+
}
|
|
42
|
+
return trimmed;
|
|
43
|
+
}
|
|
44
|
+
/** Truncate long text and note how much was dropped. */
|
|
45
|
+
function cap(text, max = RAW_REASON_CAP) {
|
|
46
|
+
if (text.length <= max)
|
|
47
|
+
return text;
|
|
48
|
+
return `${text.slice(0, max)} […${text.length - max} more chars]`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse Autopilot Nolan's raw reply into a verdict. Never throws.
|
|
52
|
+
*/
|
|
53
|
+
export function parseAutopilotVerdict(reply) {
|
|
54
|
+
const raw = (reply ?? "").trim();
|
|
55
|
+
if (!raw) {
|
|
56
|
+
return { kind: "human", reason: DEFAULT_HUMAN_REASON };
|
|
57
|
+
}
|
|
58
|
+
const lines = raw.split("\n");
|
|
59
|
+
// First non-empty line holds the keyword.
|
|
60
|
+
let keywordLineIdx = 0;
|
|
61
|
+
while (keywordLineIdx < lines.length && lines[keywordLineIdx].trim() === "") {
|
|
62
|
+
keywordLineIdx++;
|
|
63
|
+
}
|
|
64
|
+
const keywordLine = (lines[keywordLineIdx] ?? "").trim();
|
|
65
|
+
// Normalize the leading keyword: uppercase, drop a trailing colon, collapse a
|
|
66
|
+
// space between words so "ALL CLEAR" and "ALL_CLEAR" both match.
|
|
67
|
+
const normalized = keywordLine
|
|
68
|
+
.toUpperCase()
|
|
69
|
+
.replace(/[:.]+\s*$/, "")
|
|
70
|
+
.trim();
|
|
71
|
+
const collapsed = normalized.replace(/\s+/g, "_");
|
|
72
|
+
const rest = lines
|
|
73
|
+
.slice(keywordLineIdx + 1)
|
|
74
|
+
.join("\n")
|
|
75
|
+
.trim();
|
|
76
|
+
if (collapsed === "ALL_CLEAR" || collapsed.startsWith("ALL_CLEAR")) {
|
|
77
|
+
return { kind: "all_clear" };
|
|
78
|
+
}
|
|
79
|
+
// IGNORE / SKIP: the turn wasn't real work — nothing to say, nothing to show.
|
|
80
|
+
if (collapsed === "IGNORE" ||
|
|
81
|
+
collapsed.startsWith("IGNORE") ||
|
|
82
|
+
collapsed === "SKIP" ||
|
|
83
|
+
collapsed.startsWith("SKIP")) {
|
|
84
|
+
return { kind: "ignore" };
|
|
85
|
+
}
|
|
86
|
+
if (collapsed === "PROMPT" || collapsed.startsWith("PROMPT")) {
|
|
87
|
+
// Prefer the body on the following lines; if the keyword line itself carried
|
|
88
|
+
// inline text after "PROMPT:", fall back to that.
|
|
89
|
+
let body = stripPromptFence(rest);
|
|
90
|
+
if (!body) {
|
|
91
|
+
const inline = keywordLine.replace(/^prompt[:\s]*/i, "").trim();
|
|
92
|
+
body = stripPromptFence(inline);
|
|
93
|
+
}
|
|
94
|
+
if (!body) {
|
|
95
|
+
return { kind: "human", reason: "Nolan said to continue but gave no prompt." };
|
|
96
|
+
}
|
|
97
|
+
return { kind: "prompt", body };
|
|
98
|
+
}
|
|
99
|
+
if (collapsed === "HUMAN" || collapsed.startsWith("HUMAN")) {
|
|
100
|
+
let reason = rest;
|
|
101
|
+
if (!reason) {
|
|
102
|
+
const inline = keywordLine.replace(/^human[:\s]*/i, "").trim();
|
|
103
|
+
reason = inline;
|
|
104
|
+
}
|
|
105
|
+
return { kind: "human", reason: cap(reason) || DEFAULT_HUMAN_REASON };
|
|
106
|
+
}
|
|
107
|
+
// Unrecognized → stop and ask the human, echoing the raw reply for context.
|
|
108
|
+
return { kind: "human", reason: cap(raw) };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=autopilot-verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-verdict.js","sourceRoot":"","sources":["../../src/core/autopilot-verdict.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-verdict.test.d.ts","sourceRoot":"","sources":["../../src/core/autopilot-verdict.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseAutopilotVerdict } from "./autopilot-verdict.js";
|
|
3
|
+
describe("parseAutopilotVerdict", () => {
|
|
4
|
+
it("parses ALL_CLEAR", () => {
|
|
5
|
+
expect(parseAutopilotVerdict("ALL_CLEAR")).toEqual({ kind: "all_clear" });
|
|
6
|
+
});
|
|
7
|
+
it("parses fuzzy ALL CLEAR (space + lowercase)", () => {
|
|
8
|
+
expect(parseAutopilotVerdict("all clear")).toEqual({ kind: "all_clear" });
|
|
9
|
+
expect(parseAutopilotVerdict("All Clear\nlooks good")).toEqual({ kind: "all_clear" });
|
|
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
|
+
});
|
|
21
|
+
it("parses PROMPT with a multi-line body", () => {
|
|
22
|
+
const reply = "PROMPT\nAdd a test for the login flow.\nRun it and confirm it passes.";
|
|
23
|
+
expect(parseAutopilotVerdict(reply)).toEqual({
|
|
24
|
+
kind: "prompt",
|
|
25
|
+
body: "Add a test for the login flow.\nRun it and confirm it passes.",
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it("tolerates a PROMPT: keyword with a colon", () => {
|
|
29
|
+
expect(parseAutopilotVerdict("PROMPT:\nFix the off-by-one in pagination.")).toEqual({
|
|
30
|
+
kind: "prompt",
|
|
31
|
+
body: "Fix the off-by-one in pagination.",
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it("parses inline PROMPT body on the keyword line", () => {
|
|
35
|
+
expect(parseAutopilotVerdict("PROMPT: Fix the failing build.")).toEqual({
|
|
36
|
+
kind: "prompt",
|
|
37
|
+
body: "Fix the failing build.",
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
it("strips a ```prompt fence Nolan wrapped the body in", () => {
|
|
41
|
+
const reply = "PROMPT\n```prompt\nWire the button to the handler.\n```";
|
|
42
|
+
expect(parseAutopilotVerdict(reply)).toEqual({
|
|
43
|
+
kind: "prompt",
|
|
44
|
+
body: "Wire the button to the handler.",
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it("strips a plain ``` fence", () => {
|
|
48
|
+
const reply = "PROMPT\n```\nAdd error handling to the fetch call.\n```";
|
|
49
|
+
expect(parseAutopilotVerdict(reply)).toEqual({
|
|
50
|
+
kind: "prompt",
|
|
51
|
+
body: "Add error handling to the fetch call.",
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
it("downgrades an empty PROMPT to human", () => {
|
|
55
|
+
const v = parseAutopilotVerdict("PROMPT\n\n");
|
|
56
|
+
expect(v.kind).toBe("human");
|
|
57
|
+
});
|
|
58
|
+
it("parses HUMAN with a reason", () => {
|
|
59
|
+
expect(parseAutopilotVerdict("HUMAN\nThe requirement is ambiguous.")).toEqual({
|
|
60
|
+
kind: "human",
|
|
61
|
+
reason: "The requirement is ambiguous.",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it("parses inline HUMAN reason on the keyword line", () => {
|
|
65
|
+
expect(parseAutopilotVerdict("HUMAN: need a design decision")).toEqual({
|
|
66
|
+
kind: "human",
|
|
67
|
+
reason: "need a design decision",
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
it("gives HUMAN a default reason when none provided", () => {
|
|
71
|
+
const v = parseAutopilotVerdict("HUMAN");
|
|
72
|
+
expect(v.kind).toBe("human");
|
|
73
|
+
if (v.kind === "human")
|
|
74
|
+
expect(v.reason.length).toBeGreaterThan(0);
|
|
75
|
+
});
|
|
76
|
+
it("falls back to human on an unrecognized reply", () => {
|
|
77
|
+
const v = parseAutopilotVerdict("looks great, ship it!");
|
|
78
|
+
expect(v.kind).toBe("human");
|
|
79
|
+
if (v.kind === "human")
|
|
80
|
+
expect(v.reason).toContain("looks great");
|
|
81
|
+
});
|
|
82
|
+
it("falls back to human on an empty reply", () => {
|
|
83
|
+
expect(parseAutopilotVerdict("").kind).toBe("human");
|
|
84
|
+
expect(parseAutopilotVerdict(" \n ").kind).toBe("human");
|
|
85
|
+
});
|
|
86
|
+
it("skips leading blank lines before the keyword", () => {
|
|
87
|
+
expect(parseAutopilotVerdict("\n\nALL_CLEAR")).toEqual({ kind: "all_clear" });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=autopilot-verdict.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot-verdict.test.js","sourceRoot":"","sources":["../../src/core/autopilot-verdict.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QAC1E,MAAM,CAAC,qBAAqB,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,eAAe,EAAE,GAAG,EAAE;QACvB,MAAM,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,qBAAqB,CAAC,4BAA4B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,KAAK,GAAG,uEAAuE,CAAC;QACtF,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3C,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,+DAA+D;SACtE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,qBAAqB,CAAC,4CAA4C,CAAC,CAAC,CAAC,OAAO,CAAC;YAClF,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,mCAAmC;SAC1C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,qBAAqB,CAAC,gCAAgC,CAAC,CAAC,CAAC,OAAO,CAAC;YACtE,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,wBAAwB;SAC/B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,yDAAyD,CAAC;QACxE,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3C,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,iCAAiC;SACxC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,KAAK,GAAG,yDAAyD,CAAC;QACxE,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3C,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,uCAAuC;SAC9C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;QAC9C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,qBAAqB,CAAC,sCAAsC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5E,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,+BAA+B;SACxC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,qBAAqB,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAAC;YACrE,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,wBAAwB;SACjC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;YAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,GAAG,qBAAqB,CAAC,uBAAuB,CAAC,CAAC;QACzD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;YAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -150,7 +150,7 @@ describe("compaction thresholds across all models", () => {
|
|
|
150
150
|
}
|
|
151
151
|
const modelThresholds = [
|
|
152
152
|
{ model: "claude-opus-4-8", contextWindow: 1_000_000 },
|
|
153
|
-
{ model: "claude-sonnet-
|
|
153
|
+
{ model: "claude-sonnet-5", contextWindow: 1_000_000 },
|
|
154
154
|
{ model: "claude-haiku-4-5-20251001", contextWindow: 200_000 },
|
|
155
155
|
{ model: "gpt-5.3-codex", contextWindow: 400_000 },
|
|
156
156
|
{ model: "gpt-5.1-codex-mini", contextWindow: 200_000 },
|
|
@@ -450,7 +450,7 @@ import { stream } from "@prestyj/ai";
|
|
|
450
450
|
describe("compact", () => {
|
|
451
451
|
const baseOptions = {
|
|
452
452
|
provider: "anthropic",
|
|
453
|
-
model: "claude-sonnet-
|
|
453
|
+
model: "claude-sonnet-5",
|
|
454
454
|
apiKey: "test-key",
|
|
455
455
|
contextWindow: 200_000,
|
|
456
456
|
};
|