mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 15:02:32 +00:00
Add xhigh thinking level for OpenAI codex-max models
- Add 'xhigh' to ThinkingLevel type in ai and agent packages - Map xhigh to reasoning_effort: 'max' for OpenAI providers - Add thinkingXhigh color token to theme schema and built-in themes - Show xhigh option only when using codex-max models - Update CHANGELOG for both ai and coding-agent packages closes #143
This commit is contained in:
parent
87a1a9ded4
commit
00370cab39
19 changed files with 300 additions and 54 deletions
|
|
@ -2,10 +2,18 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- **Custom themes require new color tokens**: Themes must now include `thinkingXhigh` and `bashMode` color tokens. The theme loader provides helpful error messages listing missing tokens. See built-in themes (dark.json, light.json) for reference values.
|
||||
|
||||
### Added
|
||||
|
||||
- **OpenAI compatibility overrides in models.json**: Custom models using `openai-completions` API can now specify a `compat` object to override provider quirks (`supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`, `maxTokensField`). Useful for LiteLLM, custom proxies, and other non-standard endpoints. ([#133](https://github.com/badlogic/pi-mono/issues/133), thanks @fink-andreas for the initial idea and PR)
|
||||
|
||||
- **xhigh thinking level**: Added `xhigh` thinking level for OpenAI codex-max models. Cycle through thinking levels with Shift+Tab; `xhigh` appears only when using a codex-max model. ([#143](https://github.com/badlogic/pi-mono/issues/143))
|
||||
|
||||
- **Collapse changelog setting**: Add `"collapseChangelog": true` to `~/.pi/agent/settings.json` to show a condensed "Updated to vX.Y.Z" message instead of the full changelog after updates. Use `/changelog` to view the full changelog. ([#148](https://github.com/badlogic/pi-mono/issues/148))
|
||||
|
||||
## [0.13.2] - 2025-12-07
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
|
|
@ -112,12 +112,19 @@ function parseArgs(args: string[]): Args {
|
|||
result.tools = validTools;
|
||||
} else if (arg === "--thinking" && i + 1 < args.length) {
|
||||
const level = args[++i];
|
||||
if (level === "off" || level === "minimal" || level === "low" || level === "medium" || level === "high") {
|
||||
if (
|
||||
level === "off" ||
|
||||
level === "minimal" ||
|
||||
level === "low" ||
|
||||
level === "medium" ||
|
||||
level === "high" ||
|
||||
level === "xhigh"
|
||||
) {
|
||||
result.thinking = level;
|
||||
} else {
|
||||
console.error(
|
||||
chalk.yellow(
|
||||
`Warning: Invalid thinking level "${level}". Valid values: off, minimal, low, medium, high`,
|
||||
`Warning: Invalid thinking level "${level}". Valid values: off, minimal, low, medium, high, xhigh`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -248,7 +255,7 @@ ${chalk.bold("Options:")}
|
|||
--models <patterns> Comma-separated model patterns for quick cycling with Ctrl+P
|
||||
--tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)
|
||||
Available: read, bash, edit, write, grep, find, ls
|
||||
--thinking <level> Set thinking level: off, minimal, low, medium, high
|
||||
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
||||
--export <file> Export session file to HTML and exit
|
||||
--help, -h Show this help
|
||||
|
||||
|
|
@ -593,7 +600,14 @@ async function resolveModelScope(
|
|||
|
||||
if (parts.length > 1) {
|
||||
const level = parts[1];
|
||||
if (level === "off" || level === "minimal" || level === "low" || level === "medium" || level === "high") {
|
||||
if (
|
||||
level === "off" ||
|
||||
level === "minimal" ||
|
||||
level === "low" ||
|
||||
level === "medium" ||
|
||||
level === "high" ||
|
||||
level === "xhigh"
|
||||
) {
|
||||
thinkingLevel = level;
|
||||
} else {
|
||||
console.warn(
|
||||
|
|
@ -716,6 +730,7 @@ async function runInteractiveMode(
|
|||
settingsManager: SettingsManager,
|
||||
version: string,
|
||||
changelogMarkdown: string | null = null,
|
||||
collapseChangelog = false,
|
||||
modelFallbackMessage: string | null = null,
|
||||
versionCheckPromise: Promise<string | null>,
|
||||
scopedModels: Array<{ model: Model<Api>; thinkingLevel: ThinkingLevel }> = [],
|
||||
|
|
@ -730,6 +745,7 @@ async function runInteractiveMode(
|
|||
settingsManager,
|
||||
version,
|
||||
changelogMarkdown,
|
||||
collapseChangelog,
|
||||
scopedModels,
|
||||
fdPath,
|
||||
);
|
||||
|
|
@ -1385,12 +1401,14 @@ export async function main(args: string[]) {
|
|||
const fdPath = await ensureTool("fd");
|
||||
|
||||
// Interactive mode - use TUI (may have initial messages from CLI args)
|
||||
const collapseChangelog = settingsManager.getCollapseChangelog();
|
||||
await runInteractiveMode(
|
||||
agent,
|
||||
sessionManager,
|
||||
settingsManager,
|
||||
VERSION,
|
||||
changelogMarkdown,
|
||||
collapseChangelog,
|
||||
modelFallbackMessage,
|
||||
versionCheckPromise,
|
||||
scopedModels,
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ export interface Settings {
|
|||
lastChangelogVersion?: string;
|
||||
defaultProvider?: string;
|
||||
defaultModel?: string;
|
||||
defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high";
|
||||
defaultThinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
queueMode?: "all" | "one-at-a-time";
|
||||
theme?: string;
|
||||
compaction?: CompactionSettings;
|
||||
hideThinkingBlock?: boolean;
|
||||
shellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)
|
||||
collapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)
|
||||
}
|
||||
|
||||
export class SettingsManager {
|
||||
|
|
@ -109,11 +110,11 @@ export class SettingsManager {
|
|||
this.save();
|
||||
}
|
||||
|
||||
getDefaultThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high" | undefined {
|
||||
getDefaultThinkingLevel(): "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | undefined {
|
||||
return this.settings.defaultThinkingLevel;
|
||||
}
|
||||
|
||||
setDefaultThinkingLevel(level: "off" | "minimal" | "low" | "medium" | "high"): void {
|
||||
setDefaultThinkingLevel(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"): void {
|
||||
this.settings.defaultThinkingLevel = level;
|
||||
this.save();
|
||||
}
|
||||
|
|
@ -163,4 +164,13 @@ export class SettingsManager {
|
|||
this.settings.shellPath = path;
|
||||
this.save();
|
||||
}
|
||||
|
||||
getCollapseChangelog(): boolean {
|
||||
return this.settings.collapseChangelog ?? false;
|
||||
}
|
||||
|
||||
setCollapseChangelog(collapse: boolean): void {
|
||||
this.settings.collapseChangelog = collapse;
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@
|
|||
"thinkingMinimal": "#6e6e6e",
|
||||
"thinkingLow": "#5f87af",
|
||||
"thinkingMedium": "#81a2be",
|
||||
"thinkingHigh": "#b294bb"
|
||||
"thinkingHigh": "#b294bb",
|
||||
"thinkingXhigh": "#d183e8",
|
||||
|
||||
"bashMode": "green"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@
|
|||
"thinkingMinimal": "#9e9e9e",
|
||||
"thinkingLow": "#5f87af",
|
||||
"thinkingMedium": "#5f8787",
|
||||
"thinkingHigh": "#875f87"
|
||||
"thinkingHigh": "#875f87",
|
||||
"thinkingXhigh": "#8b008b",
|
||||
|
||||
"bashMode": "green"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,6 +221,34 @@
|
|||
"syntaxPunctuation": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Syntax highlighting: punctuation"
|
||||
},
|
||||
"thinkingOff": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: off"
|
||||
},
|
||||
"thinkingMinimal": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: minimal"
|
||||
},
|
||||
"thinkingLow": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: low"
|
||||
},
|
||||
"thinkingMedium": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: medium"
|
||||
},
|
||||
"thinkingHigh": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: high"
|
||||
},
|
||||
"thinkingXhigh": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Thinking level border: xhigh (OpenAI codex-max only)"
|
||||
},
|
||||
"bashMode": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Editor border color in bash mode"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
|||
|
|
@ -66,12 +66,15 @@ const ThemeJsonSchema = Type.Object({
|
|||
syntaxType: ColorValueSchema,
|
||||
syntaxOperator: ColorValueSchema,
|
||||
syntaxPunctuation: ColorValueSchema,
|
||||
// Thinking Level Borders (5 colors)
|
||||
// Thinking Level Borders (6 colors)
|
||||
thinkingOff: ColorValueSchema,
|
||||
thinkingMinimal: ColorValueSchema,
|
||||
thinkingLow: ColorValueSchema,
|
||||
thinkingMedium: ColorValueSchema,
|
||||
thinkingHigh: ColorValueSchema,
|
||||
thinkingXhigh: ColorValueSchema,
|
||||
// Bash Mode (1 color)
|
||||
bashMode: ColorValueSchema,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -119,7 +122,9 @@ export type ThemeColor =
|
|||
| "thinkingMinimal"
|
||||
| "thinkingLow"
|
||||
| "thinkingMedium"
|
||||
| "thinkingHigh";
|
||||
| "thinkingHigh"
|
||||
| "thinkingXhigh"
|
||||
| "bashMode";
|
||||
|
||||
export type ThemeBg = "userMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
|
||||
|
||||
|
|
@ -295,7 +300,7 @@ export class Theme {
|
|||
return this.mode;
|
||||
}
|
||||
|
||||
getThinkingBorderColor(level: "off" | "minimal" | "low" | "medium" | "high"): (str: string) => string {
|
||||
getThinkingBorderColor(level: "off" | "minimal" | "low" | "medium" | "high" | "xhigh"): (str: string) => string {
|
||||
// Map thinking levels to dedicated theme colors
|
||||
switch (level) {
|
||||
case "off":
|
||||
|
|
@ -308,10 +313,16 @@ export class Theme {
|
|||
return (str: string) => this.fg("thinkingMedium", str);
|
||||
case "high":
|
||||
return (str: string) => this.fg("thinkingHigh", str);
|
||||
case "xhigh":
|
||||
return (str: string) => this.fg("thinkingXhigh", str);
|
||||
default:
|
||||
return (str: string) => this.fg("thinkingOff", str);
|
||||
}
|
||||
}
|
||||
|
||||
getBashModeBorderColor(): (str: string) => string {
|
||||
return (str: string) => this.fg("bashMode", str);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -366,8 +377,31 @@ function loadThemeJson(name: string): ThemeJson {
|
|||
}
|
||||
if (!validateThemeJson.Check(json)) {
|
||||
const errors = Array.from(validateThemeJson.Errors(json));
|
||||
const errorMessages = errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n");
|
||||
throw new Error(`Invalid theme ${name}:\n${errorMessages}`);
|
||||
const missingColors: string[] = [];
|
||||
const otherErrors: string[] = [];
|
||||
|
||||
for (const e of errors) {
|
||||
// Check for missing required color properties
|
||||
const match = e.path.match(/^\/colors\/(\w+)$/);
|
||||
if (match && e.message.includes("Required")) {
|
||||
missingColors.push(match[1]);
|
||||
} else {
|
||||
otherErrors.push(` - ${e.path}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
let errorMessage = `Invalid theme "${name}":\n`;
|
||||
if (missingColors.length > 0) {
|
||||
errorMessage += `\nMissing required color tokens:\n`;
|
||||
errorMessage += missingColors.map((c) => ` - ${c}`).join("\n");
|
||||
errorMessage += `\n\nPlease add these colors to your theme's "colors" object.`;
|
||||
errorMessage += `\nSee the built-in themes (dark.json, light.json) for reference values.`;
|
||||
}
|
||||
if (otherErrors.length > 0) {
|
||||
errorMessage += `\n\nOther errors:\n${otherErrors.join("\n")}`;
|
||||
}
|
||||
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
return json as ThemeJson;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export class TuiRenderer {
|
|||
private lastSigintTime = 0;
|
||||
private lastEscapeTime = 0;
|
||||
private changelogMarkdown: string | null = null;
|
||||
private collapseChangelog = false;
|
||||
|
||||
// Message queueing
|
||||
private queuedMessages: string[] = [];
|
||||
|
|
@ -126,6 +127,7 @@ export class TuiRenderer {
|
|||
settingsManager: SettingsManager,
|
||||
version: string,
|
||||
changelogMarkdown: string | null = null,
|
||||
collapseChangelog = false,
|
||||
scopedModels: Array<{ model: Model<any>; thinkingLevel: ThinkingLevel }> = [],
|
||||
fdPath: string | null = null,
|
||||
) {
|
||||
|
|
@ -134,6 +136,7 @@ export class TuiRenderer {
|
|||
this.settingsManager = settingsManager;
|
||||
this.version = version;
|
||||
this.changelogMarkdown = changelogMarkdown;
|
||||
this.collapseChangelog = collapseChangelog;
|
||||
this.scopedModels = scopedModels;
|
||||
this.ui = new TUI(new ProcessTerminal());
|
||||
this.chatContainer = new Container();
|
||||
|
|
@ -304,10 +307,18 @@ export class TuiRenderer {
|
|||
// Add changelog if provided
|
||||
if (this.changelogMarkdown) {
|
||||
this.ui.addChild(new DynamicBorder());
|
||||
this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
|
||||
this.ui.addChild(new Spacer(1));
|
||||
this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
|
||||
this.ui.addChild(new Spacer(1));
|
||||
if (this.collapseChangelog) {
|
||||
// Show condensed version with hint to use /changelog
|
||||
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
|
||||
const latestVersion = versionMatch ? versionMatch[1] : this.version;
|
||||
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
|
||||
this.ui.addChild(new Text(condensedText, 1, 0));
|
||||
} else {
|
||||
this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
|
||||
this.ui.addChild(new Spacer(1));
|
||||
this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
|
||||
this.ui.addChild(new Spacer(1));
|
||||
}
|
||||
this.ui.addChild(new DynamicBorder());
|
||||
}
|
||||
|
||||
|
|
@ -1019,7 +1030,12 @@ export class TuiRenderer {
|
|||
return;
|
||||
}
|
||||
|
||||
const levels: ThinkingLevel[] = ["off", "minimal", "low", "medium", "high"];
|
||||
// xhigh is only available for codex-max models
|
||||
const modelId = this.agent.state.model?.id || "";
|
||||
const supportsXhigh = modelId.includes("codex-max");
|
||||
const levels: ThinkingLevel[] = supportsXhigh
|
||||
? ["off", "minimal", "low", "medium", "high", "xhigh"]
|
||||
: ["off", "minimal", "low", "medium", "high"];
|
||||
const currentLevel = this.agent.state.thinkingLevel || "off";
|
||||
const currentIndex = levels.indexOf(currentLevel);
|
||||
const nextIndex = (currentIndex + 1) % levels.length;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue