mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
fix(transport): read Claude's own-row "Restart to update" banner as a status line (#2662)
CLAUDE_STATUS_WARNINGS, the exact rows that may sit under Claude's mode footer, gains one alternative: "✔ Update installed · Restart to update", the wording Claude 2.1.319 prints on its own row in a narrow pane. The pattern stays anchored at both ends and still matches "/restart to apply" and "Restart to apply" exactly, so the footer search reaches the mode footer and the draft-aware reader, activity classification and send readiness read the prompt as before; a near miss still reads unknown. The versioned banner and the /rc row were already handled. Tests add the row to the warning-footer readiness matrix and an empty, typed and near-miss composer case.
This commit is contained in:
1 parent
6fa0ad5c6e
commit
c26af88dc7
3 files changed
+22
-4
No files matched your search
@@ -256,8 +256,9 @@ const CLAUDE_STATUS_WARNINGS = [
|
||||
/^Update available! Run: \S.*$/,
|
||||
// The context hint, as Claude prints it; reported as the last line below the mode bar on one seat.
|
||||
/^new task\? \/clear to save \d+(?:\.\d+)?k tokens$/,
|
||||
// After Claude installs an update: "/restart to apply" (a Claude seat, 2026-10-30) or "Restart to apply".
|
||||
/^✔ Update installed · (?:\/restart|Restart) to apply$/,
|
||||
// After Claude installs an update: "/restart to apply" (a Claude seat, 2026-10-30), "Restart to apply", or
|
||||
// "Restart to update" on its own row (Claude 2.1.319, #2605).
|
||||
/^✔ Update installed · (?:\/restart to apply|Restart to (?:apply|update))$/,
|
||||
];
|
||||
// Claude can draw noninteractive UI rows after its mode footer. They do not
|
||||
// replace the composer and should only be skipped when they match a known row.
|
||||
|
||||
@@ -214,8 +214,10 @@ const clearHint = "new task? /clear to save 324.1k tokens";
|
||||
// (2026-10-30) and "Restart to apply" in startup-submit-confirmation's screen.
|
||||
const installedSlash = "✔ Update installed · /restart to apply";
|
||||
const installedWord = "✔ Update installed · Restart to apply";
|
||||
// Claude 2.1.319's wording, on its own row under the footer in a 49-column pane (#2605).
|
||||
const installedUpdate = "✔ Update installed · Restart to update";
|
||||
const warningSets = [[update], [focus], [weekly], [update, focus], [updateBanner], [clearHint], [update, updateBanner],
|
||||
[installedSlash], [installedWord], [update, installedSlash]];
|
||||
[installedSlash], [installedWord], [installedUpdate], [update, installedSlash]];
|
||||
// Native, from a Claude seat running a hook mid-turn (2026-10-05): the timer follows a hook label.
|
||||
const hookRow = "✽ Sketching… (running PostToolUse hook · 3m 12s · ↓ 7.9k tokens)";
|
||||
function modePane(hint: string, trailers: string[], composer = "❯\u00a0", before = "● Ready.\n\n✻ Crunched for 2s") {
|
||||
@@ -286,7 +288,7 @@ describe("#808: Claude permission-mode footers below status warnings", () => {
|
||||
|
||||
// The installed-update footer last, as a Claude seat showed it (2026-10-30): an empty prompt under it read unknown,
|
||||
// so managed compaction never saw the idle seat. Only the exact line is a status line; a near miss stays unknown.
|
||||
it.each([installedSlash, installedWord])("reads an empty prompt under the installed-update footer as idle: %s", footer => {
|
||||
it.each([installedSlash, installedWord, installedUpdate])("reads an empty prompt under the installed-update footer as idle: %s", footer => {
|
||||
const content = modePane("⏵⏵ auto mode on (shift+tab to cycle)", [footer]);
|
||||
for (const options of [{ runtime: "claude-code" }, {}]) {
|
||||
expect(classifyPaneActivity(content, options)).toMatchObject({ state: "agent_idle", reason: "idle_prompt", evidence: "❯" });
|
||||
|
||||
@@ -53,6 +53,21 @@ describe("cursor-bound composer input", () => {
|
||||
expect(inspectComposerInput(frame, "codex").state).toBe("unknown");
|
||||
});
|
||||
|
||||
// #2605: Claude 2.1.319 prints "✔ Update installed · Restart to update" on its own row under the footer in a narrow
|
||||
// pane. The draft-aware guard read every idle prompt there as unknown and held each wake as draft_input_unknown.
|
||||
it("reads the input under Claude's own-row Restart to update banner, and nothing like it (#2605)", () => {
|
||||
const banner = (body: string, row = "✔ Update installed · Restart to update") => {
|
||||
const frame = claude(body);
|
||||
frame.screen = frame.screen.replace("? for shortcuts\n", `? for shortcuts\n${row}\n`);
|
||||
return frame;
|
||||
};
|
||||
expect(inspectComposerInput(banner(""), "claude-code").state).toBe("empty");
|
||||
expect(inspectComposerInput(banner("a real draft"), "claude-code").state).toBe("text");
|
||||
for (const nearMiss of ["✔ Update installed · Restart to update now", "Update installed · Restart to update", "✔ Update downloaded · Restart to update"]) {
|
||||
expect(inspectComposerInput(banner("", nearMiss), "claude-code")).toMatchObject({ state: "unknown", unknownReason: "unrecognized_composer" });
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps nested prompt examples inside the current draft, including when its head is clipped", () => {
|
||||
const body = "intro\n────────────────────\n❯ \n────────────────────\n? for shortcuts\n────────────────────\n❯ ";
|
||||
const snapshot = claude(body, { x: 4, y: 8 });
|
||||
|
||||
Reference in new issue
Block a user