@gajae-code/coding-agent 0.13.2 → 0.13.3

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +33 -8
  2. package/dist/types/config/model-registry.d.ts +1 -0
  3. package/dist/types/config/settings-schema.d.ts +3 -3
  4. package/dist/types/main.d.ts +1 -0
  5. package/dist/types/modes/components/dynamic-theme-text.d.ts +9 -0
  6. package/dist/types/modes/components/gajae-pet-widget.d.ts +5 -5
  7. package/dist/types/modes/components/pet-capability.d.ts +5 -5
  8. package/dist/types/modes/components/settings-selector.d.ts +5 -0
  9. package/dist/types/modes/theme/defaults/index.d.ts +118 -0
  10. package/dist/types/session/session-manager.d.ts +7 -0
  11. package/dist/types/tools/image-gen.d.ts +1 -0
  12. package/package.json +8 -8
  13. package/src/config/model-profile-activation.ts +3 -1
  14. package/src/config/model-profiles.ts +59 -2
  15. package/src/config/model-registry.ts +16 -1
  16. package/src/config/settings-schema.ts +1 -1
  17. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
  18. package/src/internal-urls/docs-index.generated.ts +3 -2
  19. package/src/main.ts +4 -0
  20. package/src/modes/components/chrome.ts +4 -3
  21. package/src/modes/components/dynamic-theme-text.ts +22 -0
  22. package/src/modes/components/gajae-pet-widget.ts +130 -42
  23. package/src/modes/components/model-selector.ts +1 -6
  24. package/src/modes/components/pet-capability.ts +10 -5
  25. package/src/modes/components/settings-selector.ts +20 -5
  26. package/src/modes/components/status-line/segments.ts +3 -3
  27. package/src/modes/controllers/selector-controller.ts +61 -5
  28. package/src/modes/theme/defaults/index.ts +2 -0
  29. package/src/modes/theme/defaults/ouroboros.json +118 -0
  30. package/src/session/agent-session.ts +39 -0
  31. package/src/session/internal/managed-session-storage.ts +26 -19
  32. package/src/session/session-manager.ts +28 -0
  33. package/src/slash-commands/builtin-registry.ts +19 -3
  34. package/src/tools/image-gen.ts +322 -126
  35. package/src/tools/todo-contract.ts +55 -3
  36. package/src/tools/todo-write.ts +4 -3
package/CHANGELOG.md CHANGED
@@ -2,19 +2,44 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.13.3] - 2026-08-15
6
+
7
+ ### Added
8
+ - Added Grok 4.6 to the bundled Grok CLI model catalog with bounded reasoning-effort parsing.
9
+ - Added built-in `grok-46-{eco,medium,pro}` role profiles using the existing xAI OAuth/subscription provider, with supported reasoning-effort selection for Grok 4.5/4.6.
10
+ - Added three provider-agnostic Muse Spark 1.2 presets: Muse Spark across all roles, plus DeepSeek and Luna executor variants.
11
+ - Added the bundled Ouroboros theme and pet, including transactional theme selection and RedGajae fallback (#4468).
12
+ - Gajae Pet now renders in iTerm2 using inline PNG frames, with the same reserved composer-side layout and lifecycle cleanup used by Kitty and Sixel (#4499).
13
+
14
+ ### Fixed
15
+ - Managed sessions recover from `content_too_large` by rewriting live in-memory entries, and proactively compact before the managed transcript limit (#4411).
16
+ - Managed output publication no longer freezes the resident event loop when a no-replace rename stalls in the kernel; async publication uses blocking-pool native boundaries and per-session stores reap scrubbed protocol remnants (#4396).
17
+ - Print mode now requests a governed process exit after successful session teardown, so completed one-shot runs cannot be pinned by residual runtime handles; both stdout and stderr are drained first and the recorded exit code is preserved.
18
+ - `todo_write` no longer rejects a positional task handle without saying how to address a task. Callers reach for `id`/`index` because the tool result renders todos as a list, and raw validation rejects the unknown key before `execute` runs; positional-handle keys now explain that tasks are addressed by exact `task` content or by `phase`.
19
+ - Escaped-non-ASCII tool-call turns in managed fallback are retried on the same model without charging the fallback chain or advancing it — the wire defect is a sampling accident, not provider evidence — bounded to two consecutive retries before the run terminates (#4515).
20
+ - Image generation now redacts API keys, bearer tokens, JWTs, and other credential formats from provider error messages and surfaced metadata, bounds provider response bodies at 64 MiB before buffering, and cancels rejected bodies instead of retaining unbounded streams (#4302).
21
+ - A transient Windows `invalid_request` during managed replacement-receipt reconciliation is now resolved by capturing both the pending and canonical receipt paths: success when another reconciler already moved the pending receipt, fail-closed `managed_replace_cleanup_receipt_invalid` for conflicting destinations, byte-identical copies, or missing receipts (#4373).
22
+ - Status-line scratch roots are resolved at render time instead of process start, so path classification follows the active environment when `HOME`/`TMPDIR` are rewritten after module caching (#4452).
23
+
5
24
  ## [0.13.2] - 2026-08-13
6
25
 
7
26
  ### Added
8
- - `/theme <name>` now switches the theme immediately without opening the selector: the name is validated against built-in and custom themes, persisted to the detected `theme.dark`/`theme.light` slot, and applied to the running session in one step. Bare `/theme` keeps the existing live-preview selector, and an unknown name is rejected with the list of available themes.
27
+ - Added a portable Stream Deck integration for cmux sessions, including a plugin manifest, reusable profile pages, action icons, installation scripts, and a guided setup document (#4308 by @Yeachan-Heo).
28
+ - `/theme <name>` now switches the theme immediately without opening the selector. The name is validated against built-in and custom themes, persisted to the detected `theme.dark`/`theme.light` slot, and applied to the running session in one step (#4429 by @Yeachan-Heo).
9
29
 
10
30
  ### Fixed
11
- - The Telegram notification daemon now contains transient Windows state or ownership-lock read failures during steady heartbeat renewal instead of terminating its run loop. Staging files are cleaned in a `finally` block, and only a proven ownership mismatch stops the owner (#4200).
12
- - Coordinator MCP now answers the standard `ping` keepalive with an empty result instead of `method not found`, preventing liveness clients from reconnecting repeatedly.
13
- - Task-launched canonical subagents no longer receive an ACP exact-config `toolsOnly` MCP manager that they are forbidden to reuse. Planner, Architect, Critic, and Ralplan consensus lanes now start normally while reusable plugin MCP managers continue to inherit across sub-sessions.
14
- - Managed output publication no longer freezes the resident event loop when a no-replace rename stalls in the kernel (#4394). Async managed publication now uses async file operations and native blocking-pool rename/link boundaries, while per-session stores also reap scrubbed protocol remnants on a throttled, serialized schedule. Atomic no-replace semantics and the synchronous publication path are unchanged.
15
- - The SDK broker's Windows process-liveness probe no longer opens a console window on every poll. `runProcessIncarnationCommand` spawned `powershell.exe` without `windowsHide`, and on Windows 11 — where the default terminal delegation hands every new console to Windows Terminal — each probe therefore created a visible terminal window that stole focus. The PowerShell path runs whenever the native reader cannot bind the target pid, so one dead or inaccessible pid turned the broker's ~2s liveness polling into a continuous window flash for the life of the process. Every other internal spawn in the repo already passed `windowsHide: true`; this one did not.
16
- - Foreground Bash calls that finish before their auto-background deadline now cancel that deadline instead of leaving an uncancellable `Bun.sleep()` behind. In print mode the losing sleep kept the compiled CLI alive for the full command timeout after Bash and subagent work had already completed, which appeared as a shutdown hang with no active Node handles.
17
- - Prompt suggestions now remain visible after cancelling another interactive action and returning to the composer.
31
+
32
+ - Anthropic thinking blocks emptied by `clear_thinking` now drop stale signatures before replay, preventing every subsequent request from failing with an invalid thinking-block signature (#4247, reported by @probepark).
33
+ - Coalesced double-Esc and triple-Esc input now emits individual Escape presses under tmux and SSH, while split ambiguous Escape sequences stay buffered until a continuation or flush timeout resolves them (#4312 by @Yeachan-Heo).
34
+ - Apple Terminal.app now retains its default keyboard mode when Kitty keyboard support is unavailable, preserving Korean/Hangul IME composition (#4297 by @Yeachan-Heo).
35
+ - Managed output publication no longer freezes the resident event loop when a no-replace rename stalls in the kernel. Async publication now uses asynchronous file operations and native blocking-pool rename/link boundaries, while per-session stores reap scrubbed protocol remnants on a throttled, serialized schedule (#4396 by @Yeachan-Heo; fixes #4394).
36
+ - The Telegram notification daemon now contains transient Windows state and ownership-lock read failures during steady heartbeat renewal instead of terminating its run loop; staging files are cleaned in a `finally` block, and only a proven ownership mismatch stops the owner (by @Yeachan-Heo; issue #4200).
37
+ - Coordinator MCP now answers the standard `ping` keepalive with an empty result instead of `method not found`, preventing liveness clients from reconnecting repeatedly (#4412 by @developjik).
38
+ - Task-launched canonical subagents no longer receive an ACP exact-config `toolsOnly` MCP manager that they are forbidden to reuse, restoring Planner, Architect, Critic, and Ralplan consensus launches (#4419 by @chlee1001).
39
+ - The SDK broker's Windows process-liveness probe no longer opens a console window on every poll; its PowerShell fallback now passes `windowsHide` so a dead or inaccessible process cannot trigger a continuous focus-stealing terminal flash (#4346 by @Dayoooun).
40
+ - Foreground Bash calls that finish before their auto-background deadline now cancel that deadline instead of leaving a sleep behind that keeps compiled print-mode processes alive (#4256 by @probepark).
41
+ - Prompt suggestions now remain visible after cancelling another interactive action and returning to the composer (#4347 by @thegreatesthoneybee).
42
+
18
43
  ## [0.13.1] - 2026-08-11
19
44
 
20
45
  ### Added
@@ -34,6 +34,7 @@ export interface GjcModelAssignmentTargetInfo extends ModelRoleInfo {
34
34
  }
35
35
  export { GJC_MODEL_ASSIGNMENT_TARGET_IDS };
36
36
  export declare const GJC_MODEL_ASSIGNMENT_TARGETS: Record<GjcModelAssignmentTargetId, GjcModelAssignmentTargetInfo>;
37
+ export declare function requiresExplicitThinkingChoice(model: Model, role: GjcModelAssignmentTargetId | null): boolean;
37
38
  /** Alias for ModelRoleInfo - used for both built-in and custom roles */
38
39
  export type RoleInfo = ModelRoleInfo;
39
40
  /**
@@ -704,18 +704,18 @@ export declare const SETTINGS_SCHEMA: {
704
704
  };
705
705
  readonly "pet.mode": {
706
706
  readonly type: "enum";
707
- readonly values: readonly ["off", "red", "blue"];
707
+ readonly values: readonly ["off", "red", "blue", "ouroboros"];
708
708
  readonly default: "off";
709
709
  readonly ui: {
710
710
  readonly tab: "appearance";
711
711
  readonly label: "Gajae Pet";
712
- readonly description: "16x16 real-pixel gajae living beside the composer (sixel/kitty terminals)";
712
+ readonly description: "Real-pixel pet living beside the composer (sixel/kitty terminals)";
713
713
  readonly options: readonly [{
714
714
  readonly value: "off";
715
715
  readonly label: "Off";
716
716
  readonly description: "No pet";
717
717
  }, ...{
718
- value: "blue" | "red";
718
+ value: "blue" | "ouroboros" | "red";
719
719
  label: string;
720
720
  description: string;
721
721
  }[]];
@@ -132,6 +132,7 @@ export interface RunRootCommandDependencies {
132
132
  getChangelogForDisplay?: typeof getChangelogForDisplay;
133
133
  createInteractiveMode?: CreateInteractiveMode;
134
134
  runPrintMode?: RunPrintMode;
135
+ quit?: (code: number) => Promise<void>;
135
136
  isResumePickerTerminal?: ResumePickerTerminalCheck;
136
137
  listForResumePickerReadOnly?: ListForResumePickerReadOnly;
137
138
  listManagedForResumePickerReadOnly?: ListManagedForResumePickerReadOnly;
@@ -0,0 +1,9 @@
1
+ import { type Component } from "@gajae-code/tui";
2
+ /** Text whose styling is resolved at render time so live theme previews recolor open overlays. */
3
+ export declare class DynamicThemeText implements Component {
4
+ #private;
5
+ private readonly getText;
6
+ constructor(getText: () => string);
7
+ invalidate(): void;
8
+ render(width: number): string[];
9
+ }
@@ -16,8 +16,8 @@ export declare class PetFramedEditor implements Component {
16
16
  render(width: number): string[];
17
17
  }
18
18
  /**
19
- * The gajae pet: a 16x16 real-pixel sprite living in a reserved area beside
20
- * the composer. It is nearest-neighbor scaled to the two terminal rows occupied
19
+ * The gajae pet: a real-pixel sprite living in a reserved area beside the
20
+ * composer. It is nearest-neighbor scaled to the two terminal rows occupied
21
21
  * by an empty one-line composer and lifted one row so its feet meet the input
22
22
  * box's bottom edge.
23
23
  *
@@ -39,15 +39,15 @@ export declare class GajaePetWidget {
39
39
  isWorking: () => boolean;
40
40
  /** Rows rendered below the composer box (pet floor + hook widgets). */
41
41
  getComposerBottomOffset: () => number;
42
- forcePixelProtocol?: "sixel" | "kitty";
42
+ forcePixelProtocol?: "sixel" | "kitty" | "iterm2";
43
43
  /** Random [min, max] ms between auto-flexes; null disables. */
44
44
  autoFlexGapMs?: [number, number] | null;
45
45
  });
46
46
  /** Protocol available for the real-pixel pet, if any. */
47
- static pixelProtocol(): "sixel" | "kitty" | null;
47
+ static pixelProtocol(): "sixel" | "kitty" | "iterm2" | null;
48
48
  get mode(): PetMode;
49
49
  get isFlexing(): boolean;
50
- setMode(mode: PetMode): void;
50
+ setMode(mode: string): void;
51
51
  /** Live preview during a selector: change the sprite without re-mounting the
52
52
  * composer editor (that would tear down the open overlay). After a short idle
53
53
  * eye-roll it fires the signature burst once (RedGajae flex, BlueGajae para-para
@@ -1,10 +1,10 @@
1
1
  import { type SelectItem } from "@gajae-code/tui";
2
- export type PetPixelProtocol = "sixel" | "kitty";
3
- export declare const PET_UNAVAILABLE_DESCRIPTION = "Unavailable: requires compatible Kitty or Sixel overlay rendering";
4
- export declare const PET_SAVED_UNAVAILABLE_DESCRIPTION = "Saved, unavailable \u2014 requires compatible Kitty or Sixel overlay rendering";
5
- export declare const PET_UNAVAILABLE_WARNING = "\u26A0 Pets aren\u2019t available in this terminal. Its image support isn\u2019t compatible with Gajae Pet\u2019s overlay rendering yet. Try Kitty, Ghostty, WezTerm, or a terminal with compatible Sixel support.";
2
+ export type PetPixelProtocol = "sixel" | "kitty" | "iterm2";
3
+ export declare const PET_UNAVAILABLE_DESCRIPTION = "Unavailable: requires compatible Kitty, iTerm2, or Sixel overlay rendering";
4
+ export declare const PET_SAVED_UNAVAILABLE_DESCRIPTION = "Saved, unavailable \u2014 requires compatible Kitty, iTerm2, or Sixel overlay rendering";
5
+ export declare const PET_UNAVAILABLE_WARNING = "\u26A0 Pets aren\u2019t available in this terminal. Its image support isn\u2019t compatible with Gajae Pet\u2019s overlay rendering yet. Try Kitty, Ghostty, WezTerm, iTerm2, or a terminal with compatible Sixel support.";
6
6
  export declare function getPetUnavailableWarning(env?: NodeJS.ProcessEnv): string;
7
- export declare function getPetPixelProtocol(): PetPixelProtocol | null;
7
+ export declare function getPetPixelProtocol(env?: NodeJS.ProcessEnv): PetPixelProtocol | null;
8
8
  export declare function isPetAvailable(): boolean;
9
9
  export declare function createPetSelectItems(options: ReadonlyArray<SelectItem>, currentValue: string, available: boolean): SelectItem[];
10
10
  /**
@@ -52,6 +52,11 @@ export interface SettingsCallbacks {
52
52
  onThemePreview?: (theme: string) => void | Promise<void>;
53
53
  /** Called to restore the rendered theme when theme settings preview is cancelled */
54
54
  onThemePreviewCancel?: (theme: string) => void | Promise<void>;
55
+ /**
56
+ * Atomically apply and persist a theme selection. Returns false when the
57
+ * candidate could not be loaded, leaving the submenu open.
58
+ */
59
+ onThemeCommit?: (path: "theme.dark" | "theme.light", theme: string, previousTheme: string) => Promise<boolean>;
55
60
  /** Called to live-preview the gajae pet skin while browsing the pet setting. */
56
61
  onPetPreview?: (mode: string) => void;
57
62
  /**
@@ -526,6 +526,124 @@ export declare const defaultThemes: {
526
526
  preset: string;
527
527
  };
528
528
  };
529
+ ouroboros: {
530
+ $schema: string;
531
+ name: string;
532
+ vars: {
533
+ cyan: string;
534
+ blue: string;
535
+ green: string;
536
+ red: string;
537
+ yellow: string;
538
+ gray: string;
539
+ dimGray: string;
540
+ darkGray: string;
541
+ accent: string;
542
+ selectedBg: string;
543
+ userMsgBg: string;
544
+ toolPendingBg: string;
545
+ toolSuccessBg: string;
546
+ toolErrorBg: string;
547
+ customMsgBg: string;
548
+ abyss: string;
549
+ deepNavy: string;
550
+ mantle: string;
551
+ surface: string;
552
+ surfaceBright: string;
553
+ ouroTeal: string;
554
+ serpentGreen: string;
555
+ petLime: string;
556
+ ritualGold: string;
557
+ eyeGold: string;
558
+ coolScale: string;
559
+ foam: string;
560
+ mutedScale: string;
561
+ dimScale: string;
562
+ dangerRed: string;
563
+ diffRemovalRed: string;
564
+ };
565
+ colors: {
566
+ accent: string;
567
+ border: string;
568
+ borderAccent: string;
569
+ borderMuted: string;
570
+ success: string;
571
+ error: string;
572
+ warning: string;
573
+ muted: string;
574
+ dim: string;
575
+ text: string;
576
+ thinkingText: string;
577
+ selectedBg: string;
578
+ userMessageBg: string;
579
+ userMessageText: string;
580
+ customMessageBg: string;
581
+ customMessageText: string;
582
+ customMessageLabel: string;
583
+ toolPendingBg: string;
584
+ toolSuccessBg: string;
585
+ toolErrorBg: string;
586
+ toolTitle: string;
587
+ toolOutput: string;
588
+ mdHeading: string;
589
+ mdLink: string;
590
+ mdLinkUrl: string;
591
+ mdCode: string;
592
+ mdCodeBlock: string;
593
+ mdCodeBlockBorder: string;
594
+ mdQuote: string;
595
+ mdQuoteBorder: string;
596
+ mdHr: string;
597
+ mdListBullet: string;
598
+ toolDiffAdded: string;
599
+ toolDiffRemoved: string;
600
+ toolDiffContext: string;
601
+ syntaxComment: string;
602
+ syntaxKeyword: string;
603
+ syntaxFunction: string;
604
+ syntaxVariable: string;
605
+ syntaxString: string;
606
+ syntaxNumber: string;
607
+ syntaxType: string;
608
+ syntaxOperator: string;
609
+ syntaxPunctuation: string;
610
+ thinkingOff: string;
611
+ thinkingMinimal: string;
612
+ thinkingLow: string;
613
+ thinkingMedium: string;
614
+ thinkingHigh: string;
615
+ thinkingXhigh: string;
616
+ bashMode: string;
617
+ statusLineBg: string;
618
+ statusLineSep: string;
619
+ statusLineModel: string;
620
+ statusLinePath: string;
621
+ statusLineGitClean: string;
622
+ statusLineGitDirty: string;
623
+ statusLineContext: string;
624
+ statusLineSpend: string;
625
+ statusLineStaged: string;
626
+ statusLineDirty: string;
627
+ statusLineUntracked: string;
628
+ statusLineOutput: string;
629
+ statusLineCost: string;
630
+ statusLineSubagents: string;
631
+ pythonMode: string;
632
+ };
633
+ export: {
634
+ pageBg: string;
635
+ cardBg: string;
636
+ infoBg: string;
637
+ };
638
+ symbols: {
639
+ preset: string;
640
+ overrides: {
641
+ "icon.pi": string;
642
+ "status.running": string;
643
+ "md.bullet": string;
644
+ };
645
+ };
646
+ };
529
647
  "red-claw": {
530
648
  $schema: string;
531
649
  name: string;
@@ -1130,6 +1130,13 @@ export declare class SessionManager {
1130
1130
  listForResumePickerReadOnly(): Promise<SessionInfo[]>;
1131
1131
  getSessionId(): string;
1132
1132
  getSessionFile(): string | undefined;
1133
+ /**
1134
+ * On-disk transcript file size in bytes. Returns 0 when the file is
1135
+ * unavailable, unreadable, or no session file is set. The managed-storage
1136
+ * path reads through the descriptor (no full-file scan); the plain-file
1137
+ * path uses statSync.
1138
+ */
1139
+ getTranscriptFileBytes(): number;
1133
1140
  getSessionMemoryStats(): SessionMemoryStats;
1134
1141
  setSessionMemoryMode(mode: SessionMemoryMode): void;
1135
1142
  acquireMemoryGuardParticipantIngressLease(): MemoryGuardParticipantIngressLease;
@@ -3,6 +3,7 @@ import * as z from "zod/v4";
3
3
  import { type ModelRegistry } from "../config/model-registry";
4
4
  import type { Settings } from "../config/settings";
5
5
  import type { CustomTool } from "../extensibility/custom-tools/types";
6
+ export declare function redactImageProviderText(value: unknown, activeApiKey?: string): string;
6
7
  type ImageProvider = "alibaba" | "antigravity" | "gemini" | "openai" | "openai-codex" | "openrouter" | (string & {});
7
8
  declare const responseModalitySchema: z.ZodEnum<{
8
9
  IMAGE: "IMAGE";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.13.2",
4
+ "version": "0.13.3",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -54,13 +54,13 @@
54
54
  "@agentclientprotocol/sdk": "1.3.0",
55
55
  "@babel/parser": "^7.29.3",
56
56
  "@mozilla/readability": "^0.6.0",
57
- "@gajae-code/stats": "0.13.2",
58
- "@gajae-code/agent-core": "0.13.2",
59
- "@gajae-code/ai": "0.13.2",
60
- "@gajae-code/bridge-client": "0.13.2",
61
- "@gajae-code/natives": "0.13.2",
62
- "@gajae-code/tui": "0.13.2",
63
- "@gajae-code/utils": "0.13.2",
57
+ "@gajae-code/stats": "0.13.3",
58
+ "@gajae-code/agent-core": "0.13.3",
59
+ "@gajae-code/ai": "0.13.3",
60
+ "@gajae-code/bridge-client": "0.13.3",
61
+ "@gajae-code/natives": "0.13.3",
62
+ "@gajae-code/tui": "0.13.3",
63
+ "@gajae-code/utils": "0.13.3",
64
64
  "@puppeteer/browsers": "^2.13.0",
65
65
  "@types/turndown": "5.0.6",
66
66
  "@xterm/headless": "^6.0.0",
@@ -572,7 +572,9 @@ function rewriteSelectorForProxy(
572
572
  const proxyModels = allModels.filter(model => model.provider === proxyProvider);
573
573
  if (slash < 0) {
574
574
  if (proxyMode === "fallback") return selector;
575
- const matches = proxyModels.filter(model => model.id === baseSelector);
575
+ const exactMatches = proxyModels.filter(model => model.id === baseSelector);
576
+ const finalSegmentMatches = proxyModels.filter(model => model.id.split("/").at(-1) === baseSelector);
577
+ const matches = exactMatches.length > 0 ? exactMatches : finalSegmentMatches;
576
578
  if (matches.length !== 1) {
577
579
  throw new Error(
578
580
  `Configured proxy "${proxyProvider}" does not expose an unambiguous model for "${baseSelector}"`,
@@ -134,6 +134,27 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
134
134
  critic: "gpt-5.6-luna:xhigh",
135
135
  architect: "gpt-5.6-luna:xhigh",
136
136
  }),
137
+ profile("open-weights-spark", [], {
138
+ default: "muse-spark-1.2:medium",
139
+ executor: "muse-spark-1.2:low",
140
+ planner: "muse-spark-1.2:high",
141
+ critic: "muse-spark-1.2:high",
142
+ architect: "muse-spark-1.2:xhigh",
143
+ }),
144
+ profile("open-weights-spark-deepseek", [], {
145
+ default: "muse-spark-1.2:medium",
146
+ executor: "deepseek-v4-flash:high",
147
+ planner: "muse-spark-1.2:high",
148
+ critic: "muse-spark-1.2:high",
149
+ architect: "muse-spark-1.2:xhigh",
150
+ }),
151
+ profile("open-weights-spark-luna", [], {
152
+ default: "muse-spark-1.2:medium",
153
+ executor: "gpt-5.6-luna:high",
154
+ planner: "muse-spark-1.2:high",
155
+ critic: "muse-spark-1.2:high",
156
+ architect: "muse-spark-1.2:xhigh",
157
+ }),
137
158
  profile("open-weights-glm-deepseek", [], {
138
159
  default: "glm-5.2:medium",
139
160
  executor: "deepseek-v4-flash:high",
@@ -279,7 +300,7 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
279
300
  }),
280
301
  profile("grok-45-eco", ["xai"], {
281
302
  default: "xai/grok-4.5:low",
282
- executor: "xai/grok-4.5:minimal",
303
+ executor: "xai/grok-4.5:low",
283
304
  planner: "xai/grok-4.5:low",
284
305
  critic: "xai/grok-4.5:medium",
285
306
  architect: "xai/grok-4.5:high",
@@ -298,6 +319,27 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
298
319
  critic: "xai/grok-4.5:high",
299
320
  architect: "xai/grok-4.5:high",
300
321
  }),
322
+ profile("grok-46-eco", ["xai"], {
323
+ default: "xai/grok-4.6:low",
324
+ executor: "xai/grok-4.6:low",
325
+ planner: "xai/grok-4.6:low",
326
+ critic: "xai/grok-4.6:medium",
327
+ architect: "xai/grok-4.6:high",
328
+ }),
329
+ profile("grok-46-medium", ["xai"], {
330
+ default: "xai/grok-4.6:medium",
331
+ executor: "xai/grok-4.6:low",
332
+ planner: "xai/grok-4.6:medium",
333
+ critic: "xai/grok-4.6:high",
334
+ architect: "xai/grok-4.6:high",
335
+ }),
336
+ profile("grok-46-pro", ["xai"], {
337
+ default: "xai/grok-4.6:xhigh",
338
+ executor: "xai/grok-4.6:medium",
339
+ planner: "xai/grok-4.6:high",
340
+ critic: "xai/grok-4.6:xhigh",
341
+ architect: "xai/grok-4.6:xhigh",
342
+ }),
301
343
  profile("grok-build-pro", ["grok-build"], {
302
344
  default: "grok-build/grok-composer-2.5-fast",
303
345
  executor: "grok-build/grok-build",
@@ -444,6 +486,15 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
444
486
  },
445
487
  "open-weights-kimi": { displayName: "Kimi", providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)" },
446
488
  "open-weights-luna": { displayName: "Luna", providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)" },
489
+ "open-weights-spark": { displayName: "Muse Spark", providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)" },
490
+ "open-weights-spark-deepseek": {
491
+ displayName: "Muse Spark + DeepSeek",
492
+ providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)",
493
+ },
494
+ "open-weights-spark-luna": {
495
+ displayName: "Muse Spark + Luna",
496
+ providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)",
497
+ },
447
498
  "open-weights-glm-deepseek": {
448
499
  displayName: "GLM + DeepSeek",
449
500
  providerGroup: "OPEN WEIGHT MODELS (PROVIDER AGNOSTIC)",
@@ -481,6 +532,9 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
481
532
  "grok-45-eco": { displayName: "Grok 4.5 Eco", providerGroup: "GROK" },
482
533
  "grok-45-medium": { displayName: "Grok 4.5 Medium", providerGroup: "GROK" },
483
534
  "grok-45-pro": { displayName: "Grok 4.5 Pro", providerGroup: "GROK" },
535
+ "grok-46-eco": { displayName: "Grok 4.6 Eco", providerGroup: "GROK" },
536
+ "grok-46-medium": { displayName: "Grok 4.6 Medium", providerGroup: "GROK" },
537
+ "grok-46-pro": { displayName: "Grok 4.6 Pro", providerGroup: "GROK" },
484
538
  "grok-build-pro": { displayName: "Grok Build Pro", providerGroup: "GROK" },
485
539
  "cursor-eco": { displayName: "Cursor Eco", providerGroup: "CURSOR" },
486
540
  "cursor-medium": { displayName: "Cursor Medium", providerGroup: "CURSOR" },
@@ -518,6 +572,9 @@ const OPEN_WEIGHT_PROFILE_ORDER = [
518
572
  "open-weights-deepseek",
519
573
  "open-weights-kimi",
520
574
  "open-weights-luna",
575
+ "open-weights-spark",
576
+ "open-weights-spark-deepseek",
577
+ "open-weights-spark-luna",
521
578
  "open-weights-glm-deepseek",
522
579
  "open-weights-kimi-deepseek",
523
580
  "open-weights-kimi-glm",
@@ -536,7 +593,7 @@ const PROFILE_RECOMMENDATIONS: Record<string, string> = {
536
593
  "xiaomi-token-plan-sgp": "mimo-medium",
537
594
  "xiaomi-token-plan-ams": "mimo-medium",
538
595
  "xiaomi-token-plan-cn": "mimo-medium",
539
- xai: "grok-medium",
596
+ xai: "grok-46-medium",
540
597
  "grok-build": "grok-build-pro",
541
598
  cursor: "cursor-medium",
542
599
  "minimax-code": "minimax-medium",
@@ -5,6 +5,7 @@ import {
5
5
  type AssistantMessageEventStream,
6
6
  type AuthCredentialSelector,
7
7
  applyFinalCodexGpt56ContextCap,
8
+ applyGeneratedModelPolicies,
8
9
  type CacheRetention,
9
10
  CODEX_GPT_5_6_CONTEXT_CAP,
10
11
  type Context,
@@ -171,6 +172,17 @@ export const GJC_MODEL_ASSIGNMENT_TARGETS: Record<GjcModelAssignmentTargetId, Gj
171
172
  image: { id: "image", tag: "IMAGE", name: "Image", color: "accent", settingsPath: "modelRoles" },
172
173
  };
173
174
 
175
+ export function requiresExplicitThinkingChoice(model: Model, role: GjcModelAssignmentTargetId | null): boolean {
176
+ if (model.reasoning !== true) return false;
177
+ if (
178
+ model.provider === "openai" ||
179
+ model.provider === "openai-codex" ||
180
+ (model.provider === "xai" && (model.id === "grok-4.5" || model.id === "grok-4.6"))
181
+ )
182
+ return true;
183
+ return role !== null && GJC_MODEL_ASSIGNMENT_TARGETS[role].settingsPath === "task.agentModelOverrides";
184
+ }
185
+
174
186
  /** Alias for ModelRoleInfo - used for both built-in and custom roles */
175
187
  export type RoleInfo = ModelRoleInfo;
176
188
 
@@ -1634,7 +1646,9 @@ export class ModelRegistry {
1634
1646
  const withTransport = providerOverride
1635
1647
  ? models.map(model => this.#applyProviderTransportOverride(model, providerOverride))
1636
1648
  : models;
1637
- cachedModels.push(...this.#applyProviderModelOverrides(descriptor.providerId, withTransport));
1649
+ const normalized = this.#applyProviderModelOverrides(descriptor.providerId, withTransport);
1650
+ applyGeneratedModelPolicies(normalized);
1651
+ cachedModels.push(...normalized);
1638
1652
  }
1639
1653
  return cachedModels;
1640
1654
  }
@@ -1650,6 +1664,7 @@ export class ModelRegistry {
1650
1664
  this.#applyProviderCompat(providerConfig.compat, [...models]),
1651
1665
  ),
1652
1666
  );
1667
+ applyGeneratedModelPolicies(normalized);
1653
1668
  cachedModels.push(...normalized);
1654
1669
  }
1655
1670
  return cachedModels;
@@ -752,7 +752,7 @@ export const SETTINGS_SCHEMA = {
752
752
  ui: {
753
753
  tab: "appearance",
754
754
  label: "Gajae Pet",
755
- description: "16x16 real-pixel gajae living beside the composer (sixel/kitty terminals)",
755
+ description: "Real-pixel pet living beside the composer (sixel/kitty terminals)",
756
756
  options: [
757
757
  { value: "off", label: "Off", description: "No pet" },
758
758
  ...PET_SKIN_IDS.map(id => ({
@@ -9,6 +9,7 @@ const COST_BUILD = { input: 1, output: 2, cacheRead: 0.2, cacheWrite: 0.2 };
9
9
  const COST_COMPOSER_FAST = { input: 3, output: 15, cacheRead: 0.5, cacheWrite: 0 };
10
10
  const COST_43 = { input: 1.25, output: 2.5, cacheRead: 0.2, cacheWrite: 0 };
11
11
  const COST_45 = { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 };
12
+ const COST_46 = { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 };
12
13
  const COST_420 = { input: 2, output: 6, cacheRead: 0.2, cacheWrite: 0 };
13
14
 
14
15
  // ─── Model type ───────────────────────────────────────────────────────────────
@@ -73,6 +74,18 @@ const FALLBACK_MODELS: GrokCliModelConfig[] = [
73
74
  contextWindow: 1_000_000,
74
75
  maxTokens: 30_000,
75
76
  },
77
+ {
78
+ // Official metadata and pricing: https://docs.x.ai/developers/models/grok-4.6
79
+ id: 'grok-4.6',
80
+ name: 'Grok 4.6',
81
+ reasoning: true,
82
+ input: ['text', 'image'],
83
+ cost: COST_46,
84
+ contextWindow: 500_000,
85
+ maxTokens: 30_000,
86
+ // https://docs.x.ai/developers/model-capabilities/text/reasoning: xhigh is 4.6 and later.
87
+ maxReasoningEffort: Effort.XHigh,
88
+ },
76
89
  {
77
90
  // Official metadata and pricing: https://docs.x.ai/developers/models/grok-4.5
78
91
  id: 'grok-4.5',
@@ -122,8 +135,9 @@ const FALLBACK_MODELS: GrokCliModelConfig[] = [
122
135
  },
123
136
  ];
124
137
 
125
- // Official aliases: https://docs.x.ai/developers/models/grok-4.5
138
+ // Official aliases: https://docs.x.ai/developers/models/grok-4.6
126
139
  const MODEL_ALIASES: Readonly<Record<string, string>> = {
140
+ 'grok-4.6-latest': 'grok-4.6',
127
141
  'grok-4.5-latest': 'grok-4.5',
128
142
  'grok-build-latest': 'grok-4.5',
129
143
  };
@@ -138,18 +152,29 @@ function getCanonicalModelName(modelId: string): string {
138
152
  return MODEL_ALIASES[name] ?? name;
139
153
  }
140
154
 
155
+ function matchesModelFamily(name: string, modelId: string): boolean {
156
+ if (name === modelId) return true;
157
+ const suffix = name.slice(modelId.length + 1);
158
+ return name.startsWith(`${modelId}-`) && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(suffix);
159
+ }
160
+
141
161
  export function getMaxReasoningEffort(modelId: string): Effort | undefined {
142
162
  const name = getCanonicalModelName(modelId);
143
- return FALLBACK_MODELS.find(
144
- (model) => name === model.id.toLowerCase() || name.startsWith(`${model.id.toLowerCase()}-`),
145
- )?.maxReasoningEffort;
163
+ return FALLBACK_MODELS.find((model) => matchesModelFamily(name, model.id.toLowerCase()))
164
+ ?.maxReasoningEffort;
146
165
  }
147
166
 
148
- const EFFORT_CAPABLE_PREFIXES = ['grok-3-mini', 'grok-4.20-multi-agent', 'grok-4.3', 'grok-4.5'];
167
+ const EFFORT_CAPABLE_PREFIXES = [
168
+ 'grok-3-mini',
169
+ 'grok-4.20-multi-agent',
170
+ 'grok-4.3',
171
+ 'grok-4.5',
172
+ 'grok-4.6',
173
+ ];
149
174
 
150
175
  export function supportsReasoningEffort(modelId: string): boolean {
151
176
  const name = getCanonicalModelName(modelId);
152
- if (!EFFORT_CAPABLE_PREFIXES.some((prefix) => name.startsWith(prefix))) {
177
+ if (!EFFORT_CAPABLE_PREFIXES.some((prefix) => matchesModelFamily(name, prefix))) {
153
178
  return false;
154
179
  }
155
180
  const model = resolveModels().find((entry) => getCanonicalModelName(entry.id) === name);