mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
fix(codex): read the model above codex 0.162's new footer hint row
Every codex tile and tab showed no model, unlike claude and deepseek. Codex reports its model only in the footer under the composer (` GPT-6-Luna default · ~/codeman-cases/testcase`), and the registry read the pane's LAST row for it. Codex 0.162.0 added a hint row under the footer at rest (` ← for agents · ? for shortcuts`, or ` ? for shortcuts`), so the last row was always the hint and `displayModel` stayed null. Measured live on the 1.36.0 beta: the hint is there at rest and after a turn, and gone while a prompt is being typed (the footer is the last row again then). The codex modelDetect window is now two rows, and the footer must be either the last row or followed by exactly one more two-space-indented row. Anchoring to the end of the window keeps the guard the one-row rule had: with the footer hidden, the last two rows are a transcript line and the `›` composer, so a footer-shaped line the agent printed is not read. Tests cover both 0.162 hint variants, the typing layout, the hint never read as a model, and a forged footer-plus-indented pair above the composer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -645,13 +645,23 @@ const CODEX: CliEntry = {
|
|||||||
},
|
},
|
||||||
// The footer under the composer, measured on a live 0.147.0 pane:
|
// The footer under the composer, measured on a live 0.147.0 pane:
|
||||||
// ` gpt-5.6-terra default · ~/codeman-cases/th-scratch` (model, reasoning effort,
|
// ` gpt-5.6-terra default · ~/codeman-cases/th-scratch` (model, reasoning effort,
|
||||||
// cwd). It is the pane's LAST row, below the composer, so the transcript never
|
// cwd). It sits below the composer, so the transcript never reaches it, and the
|
||||||
// reaches it, and the effort word right after the model is codex's own format: an
|
// effort word right after the model is codex's own format: an open slash-command
|
||||||
// open slash-command popup or a bare line of prose does not have that shape. A
|
// popup or a bare line of prose does not have that shape. A footer without an effort
|
||||||
// footer without an effort word (a model with no reasoning setting) is not read,
|
// word (a model with no reasoning setting) is not read, and the session keeps its
|
||||||
// and the session keeps its last known or launch model.
|
// last known or launch model.
|
||||||
|
// ⚠️ It is not always the LAST row. 0.162.0 (measured 2026-10-09) adds a hint row
|
||||||
|
// under it at rest, ` ← for agents · ? for shortcuts` or ` ? for shortcuts`, and
|
||||||
|
// drops it again while a prompt is being typed. With a one-row window the footer was
|
||||||
|
// never seen and every codex tile showed no model. So the window is two rows and the
|
||||||
|
// footer is either the last one or followed by exactly one more two-space-indented
|
||||||
|
// row. The `$` (no `m` flag: the end of the window) is what keeps the guard the
|
||||||
|
// one-row rule had: with the footer hidden, the last two rows are a transcript line
|
||||||
|
// and the `›` composer, and a forged footer-shaped transcript line is not followed by
|
||||||
|
// an indented row, so it is not read.
|
||||||
modelDetect: {
|
modelDetect: {
|
||||||
screenLine: String.raw`^ {2}([A-Za-z0-9][\w.:/@+-]{0,79}) (?:none|minimal|low|medium|high|xhigh|max|default) · `,
|
screenLine: String.raw`(?:^|\n) {2}([A-Za-z0-9][\w.:/@+-]{0,79}) (?:none|minimal|low|medium|high|xhigh|max|default) · [^\n]*(?:\n {2}[^\n]*)?$`,
|
||||||
|
screenLines: 2,
|
||||||
},
|
},
|
||||||
// Two columns, like claude's, measured on a live 0.154.0 answer: the `•`/`›`/`⚠`
|
// Two columns, like claude's, measured on a live 0.154.0 answer: the `•`/`›`/`⚠`
|
||||||
// markers sit in the gutter, prose continuations sit at 2, and a nested YAML block
|
// markers sit in the gutter, prose continuations sit at 2, and a nested YAML block
|
||||||
|
|||||||
@@ -51,8 +51,11 @@ function dshPane(statusLine: string | null, transcript: string[] = []): string {
|
|||||||
}
|
}
|
||||||
const DSH_LIVE = dshPane(' qwen3.8-27b · medium · th-scratch');
|
const DSH_LIVE = dshPane(' qwen3.8-27b · medium · th-scratch');
|
||||||
|
|
||||||
/** The foot of a codex pane: transcript, composer, then the status line on the last row. */
|
/**
|
||||||
function codexPane(statusLine: string | null, transcript: string[] = []): string {
|
* The foot of a codex pane: transcript, composer, then the status line, and from 0.162.0
|
||||||
|
* on a hint row under it at rest (`hint`; absent while a prompt is being typed).
|
||||||
|
*/
|
||||||
|
function codexPane(statusLine: string | null, transcript: string[] = [], hint: string | null = null): string {
|
||||||
return [
|
return [
|
||||||
'│ directory: ~/codeman-cases/th-scratch │',
|
'│ directory: ~/codeman-cases/th-scratch │',
|
||||||
'│ permissions: YOLO mode │',
|
'│ permissions: YOLO mode │',
|
||||||
@@ -61,7 +64,9 @@ function codexPane(statusLine: string | null, transcript: string[] = []): string
|
|||||||
' plan for free – let’s build together.',
|
' plan for free – let’s build together.',
|
||||||
...transcript,
|
...transcript,
|
||||||
'› Explain this codebase',
|
'› Explain this codebase',
|
||||||
|
'',
|
||||||
...(statusLine === null ? [] : [statusLine]),
|
...(statusLine === null ? [] : [statusLine]),
|
||||||
|
...(hint === null ? [] : [hint]),
|
||||||
'',
|
'',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
}
|
}
|
||||||
@@ -69,6 +74,8 @@ const CODEX_LIVE = codexPane(' gpt-5.6-terra default · ~/codeman-cases/th-scra
|
|||||||
const CODEX_154 = codexPane(
|
const CODEX_154 = codexPane(
|
||||||
' gpt-5.6-sol medium · Context 98% left · ~/codeman-cases/codex-probe · 5h 99% left · weekly 94% left'
|
' gpt-5.6-sol medium · Context 98% left · ~/codeman-cases/codex-probe · 5h 99% left · weekly 94% left'
|
||||||
);
|
);
|
||||||
|
/** 0.162.0 at rest, verbatim from the 1.36.0 beta pane whose tile showed no model. */
|
||||||
|
const CODEX_162 = codexPane(' GPT-6-Luna default · ~/codeman-cases/testcase', [], ' ← for agents · ? for shortcuts');
|
||||||
|
|
||||||
describe('the registry patterns', () => {
|
describe('the registry patterns', () => {
|
||||||
it('compile through compileVersionRegex() with exactly one capture group', () => {
|
it('compile through compileVersionRegex() with exactly one capture group', () => {
|
||||||
@@ -181,6 +188,17 @@ describe('readScreenModel', () => {
|
|||||||
expect(readScreenModel(CODEX_154, CODEX, CODEX_ROWS)).toBe('gpt-5.6-sol');
|
expect(readScreenModel(CODEX_154, CODEX, CODEX_ROWS)).toBe('gpt-5.6-sol');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reads codex's model above the 0.162.0 hint row, and with the hint gone while typing", () => {
|
||||||
|
expect(readScreenModel(CODEX_162, CODEX, CODEX_ROWS)).toBe('GPT-6-Luna');
|
||||||
|
const shortHint = codexPane(' GPT-6-Luna default · ~/codeman-cases/testcase', [], ' ? for shortcuts');
|
||||||
|
expect(readScreenModel(shortHint, CODEX, CODEX_ROWS)).toBe('GPT-6-Luna');
|
||||||
|
// Typing hides the hint, which puts the footer back on the last row.
|
||||||
|
const typing = codexPane(' GPT-6-Luna default · ~/codeman-cases/testcase');
|
||||||
|
expect(readScreenModel(typing, CODEX, CODEX_ROWS)).toBe('GPT-6-Luna');
|
||||||
|
// Neither hint row is ever read as a model.
|
||||||
|
expect(readScreenModel(codexPane(null, [], ' ← for agents · ? for shortcuts'), CODEX, CODEX_ROWS)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('never takes a transcript line shaped like the footer', () => {
|
it('never takes a transcript line shaped like the footer', () => {
|
||||||
// The agent printed a line exactly like each CLI's footer, and the real footer is
|
// The agent printed a line exactly like each CLI's footer, and the real footer is
|
||||||
// hidden (a dsh status bar switched off; a codex popup over its last row). The
|
// hidden (a dsh status bar switched off; a codex popup over its last row). The
|
||||||
@@ -200,6 +218,16 @@ describe('readScreenModel', () => {
|
|||||||
' evil-model high · ~/codeman-cases/th-scratch',
|
' evil-model high · ~/codeman-cases/th-scratch',
|
||||||
]);
|
]);
|
||||||
expect(readScreenModel(codexBoth, CODEX, CODEX_ROWS)).toBe('gpt-5.6-terra');
|
expect(readScreenModel(codexBoth, CODEX, CODEX_ROWS)).toBe('gpt-5.6-terra');
|
||||||
|
// The two-row window must not open the door either: a forged line followed by an
|
||||||
|
// indented transcript row still has the `›` composer under it, so it is never read.
|
||||||
|
const codexForgedPair = codexPane(null, [' evil-model high · ~/codeman-cases/th-scratch', ' ? for shortcuts']);
|
||||||
|
expect(readScreenModel(codexForgedPair, CODEX, CODEX_ROWS)).toBeUndefined();
|
||||||
|
const codexBoth162 = codexPane(
|
||||||
|
' GPT-6-Luna default · ~/codeman-cases/testcase',
|
||||||
|
[' evil-model high · ~/codeman-cases/th-scratch'],
|
||||||
|
' ? for shortcuts'
|
||||||
|
);
|
||||||
|
expect(readScreenModel(codexBoth162, CODEX, CODEX_ROWS)).toBe('GPT-6-Luna');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not read a popup under the composer as a model', () => {
|
it('does not read a popup under the composer as a model', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user