claude-overnight 1.25.22 → 1.25.24

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.
@@ -1 +1 @@
1
- export declare const VERSION = "1.25.22";
1
+ export declare const VERSION = "1.25.24";
package/dist/_version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by build — do not edit manually.
2
- export const VERSION = "1.25.22";
2
+ export const VERSION = "1.25.24";
@@ -5,7 +5,7 @@
5
5
  // segfaults on some setups — the proxy inherits this bug).
6
6
  //
7
7
  // Update this list when Cursor adds/removes models. Run:
8
- // node ~/.local/share/cursor-agent/versions/*/index.js --list-models
8
+ // agent --list-models
9
9
  // to get the current list.
10
10
  //
11
11
  // The `priority` models always appear at the top of the picker in this order.
@@ -13,11 +13,19 @@
13
13
  // that isn't in this list goes into a "more..." sub-menu.
14
14
  import { modelDisplayName, formatContextWindow } from "./models.js";
15
15
  export const CURSOR_PRIORITY_MODELS = [
16
- { id: "composer-2", label: "composer-2", hint: "Cursor Composer 2 — latest, strongest Cursor model" },
16
+ { id: "claude-opus-4-7", label: "claude-opus-4-7", hint: "Claude Opus 4.7 — latest Anthropic flagship, best agentic coder" },
17
+ { id: "gpt-5.4", label: "gpt-5.4", hint: "GPT-5.4 — latest OpenAI flagship, 1M context" },
18
+ { id: "gemini-3.1-pro", label: "gemini-3.1-pro", hint: "Gemini 3.1 Pro — latest Google flagship" },
19
+ { id: "claude-sonnet-4-6", label: "claude-sonnet-4-6", hint: "Claude Sonnet 4.6 — best speed/intelligence balance" },
20
+ { id: "composer-2", label: "composer-2", hint: "Cursor Composer 2 — latest Cursor-native model" },
17
21
  { id: "auto", label: "auto", hint: "auto-delegates to the best available model" },
18
22
  ];
19
23
  export const CURSOR_KNOWN_MODELS = [
20
- { id: "composer", label: "composer", hint: "Cursor Composer — previous generation" },
24
+ { id: "composer-2-fast", label: "composer-2-fast", hint: "Cursor Composer 2 Fast faster, cheaper variant" },
25
+ { id: "composer-1.5", label: "composer-1.5", hint: "Cursor Composer 1.5 — previous generation" },
26
+ { id: "gpt-5.3-codex", label: "gpt-5.3-codex", hint: "Codex 5.3 — OpenAI's best agentic coder" },
27
+ { id: "grok-4-20", label: "grok-4-20", hint: "Grok 4.20 — xAI model" },
28
+ { id: "kimi-k2.5", label: "kimi-k2.5", hint: "Kimi K2.5 — Moonshot model" },
21
29
  ];
22
30
  /** All known model IDs as a Set for quick membership checks. */
23
31
  export const KNOWN_CURSOR_MODEL_IDS = new Set([
@@ -43,5 +51,9 @@ export function cursorModelHint(modelId) {
43
51
  return "Gemini model via Cursor";
44
52
  if (m.startsWith("grok"))
45
53
  return "Grok model via Cursor";
54
+ if (m.startsWith("kimi"))
55
+ return "Kimi model via Cursor";
56
+ if (m.startsWith("claude"))
57
+ return "Claude model via Cursor";
46
58
  return "Cursor model";
47
59
  }
@@ -0,0 +1,40 @@
1
+ export type PanelMode = "debrief" | "ask" | "custom" | "none";
2
+ /** Mutable state of the interactive panel. */
3
+ export interface PanelState {
4
+ mode: PanelMode;
5
+ expanded: boolean;
6
+ scrollOffset: number;
7
+ /** Short title shown in the header bar. */
8
+ header: string;
9
+ /** One-line summary shown when collapsed. */
10
+ preview: string;
11
+ /** Multi-line body shown when expanded. */
12
+ body: string;
13
+ /** Whether to show a text input at the bottom (ask/steer). */
14
+ inputActive: boolean;
15
+ inputPlaceholder?: string;
16
+ }
17
+ export declare class InteractivePanel {
18
+ state: PanelState;
19
+ /** Cached non-empty body lines — rebuilt when body changes. */
20
+ private _bodyLines;
21
+ /** Set or clear the panel content. Mode "none" hides it. */
22
+ set(params: {
23
+ mode: PanelMode;
24
+ header: string;
25
+ preview: string;
26
+ body: string;
27
+ }): void;
28
+ /** Collapse the panel back to the compact bar. */
29
+ collapse(): void;
30
+ /** Toggle expanded/collapsed state. */
31
+ toggle(): void;
32
+ /** Scroll up/down within the expanded body. */
33
+ scroll(direction: "up" | "down", visibleRows: number): void;
34
+ /** Whether the panel is currently visible (any mode other than none). */
35
+ get visible(): boolean;
36
+ /** Render the collapsed compact bar. Returns empty string if no content. */
37
+ renderCollapsed(width: number): string;
38
+ /** Render the expanded panel as an array of lines for the content area. */
39
+ renderExpanded(width: number, maxRows: number): string[];
40
+ }
@@ -0,0 +1,111 @@
1
+ import chalk from "chalk";
2
+ const DARK_GREEN_BG = "\x1B[48;5;22m";
3
+ const LIGHT_GREEN_FG = "\x1B[38;5;156m";
4
+ const RESET = "\x1B[0m";
5
+ function greenBg(text) {
6
+ return `${DARK_GREEN_BG}${LIGHT_GREEN_FG} ${text} ${RESET}`;
7
+ }
8
+ function greenBgLine(text, width) {
9
+ const padded = text.padEnd(Math.max(0, width));
10
+ return `${DARK_GREEN_BG}${LIGHT_GREEN_FG}${padded}${RESET}`;
11
+ }
12
+ function truncate(s, max) {
13
+ return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
14
+ }
15
+ export class InteractivePanel {
16
+ state = {
17
+ mode: "none",
18
+ expanded: false,
19
+ scrollOffset: 0,
20
+ header: "",
21
+ preview: "",
22
+ body: "",
23
+ inputActive: false,
24
+ };
25
+ /** Cached non-empty body lines — rebuilt when body changes. */
26
+ _bodyLines = [];
27
+ /** Set or clear the panel content. Mode "none" hides it. */
28
+ set(params) {
29
+ this.state.mode = params.mode;
30
+ this.state.header = params.header;
31
+ this.state.preview = params.preview;
32
+ this.state.body = params.body;
33
+ // Rebuild cached lines and reset scroll only when content changes
34
+ this._bodyLines = params.body.split("\n").filter(l => l.length > 0);
35
+ this.state.scrollOffset = 0;
36
+ }
37
+ /** Collapse the panel back to the compact bar. */
38
+ collapse() {
39
+ this.state.expanded = false;
40
+ this.state.scrollOffset = 0;
41
+ }
42
+ /** Toggle expanded/collapsed state. */
43
+ toggle() {
44
+ if (this.state.mode === "none")
45
+ return;
46
+ this.state.expanded = !this.state.expanded;
47
+ if (!this.state.expanded)
48
+ this.state.scrollOffset = 0;
49
+ }
50
+ /** Scroll up/down within the expanded body. */
51
+ scroll(direction, visibleRows) {
52
+ if (!this.state.expanded)
53
+ return;
54
+ const maxScroll = Math.max(0, this._bodyLines.length - visibleRows);
55
+ if (direction === "up") {
56
+ this.state.scrollOffset = Math.max(0, this.state.scrollOffset - 1);
57
+ }
58
+ else {
59
+ this.state.scrollOffset = Math.min(maxScroll, this.state.scrollOffset + 1);
60
+ }
61
+ }
62
+ /** Whether the panel is currently visible (any mode other than none). */
63
+ get visible() {
64
+ return this.state.mode !== "none";
65
+ }
66
+ /** Render the collapsed compact bar. Returns empty string if no content. */
67
+ renderCollapsed(width) {
68
+ if (this.state.mode === "none" || !this.state.preview)
69
+ return "";
70
+ const icon = this.state.expanded ? "\u25BC" : "\u25B6";
71
+ const modeLabel = this.state.header;
72
+ const hint = chalk.dim(`[Ctrl-O expand]`);
73
+ const content = truncate(this.state.preview, width - modeLabel.length - hint.length - 8);
74
+ return ` ${greenBg(`${icon} ${modeLabel}`)} ${content} ${hint}`;
75
+ }
76
+ /** Render the expanded panel as an array of lines for the content area. */
77
+ renderExpanded(width, maxRows) {
78
+ if (this.state.mode === "none")
79
+ return [];
80
+ const innerW = Math.max(20, width - 6);
81
+ const lines = [];
82
+ // Header bar — full-width dark green bg
83
+ const headerText = ` ${this.state.header} ${chalk.dim("[Ctrl-O] collapse")}${this.state.inputActive ? chalk.dim(" [Esc] cancel") : ""}`;
84
+ lines.push(greenBgLine(headerText, Math.min(width - 4, innerW + 2)));
85
+ // Body content — scrolled
86
+ const headerSpace = this.state.inputActive ? 3 : 2; // header + footer + optional input
87
+ const visibleRows = Math.max(2, maxRows - headerSpace);
88
+ const start = this.state.scrollOffset;
89
+ const end = Math.min(start + visibleRows, this._bodyLines.length);
90
+ for (let i = start; i < end; i++) {
91
+ const ln = truncate(this._bodyLines[i], innerW);
92
+ lines.push(` ${chalk.greenBright(ln)}`);
93
+ }
94
+ if (end < this._bodyLines.length) {
95
+ lines.push(chalk.dim(` \u2026 +${this._bodyLines.length - end} more`));
96
+ }
97
+ if (this._bodyLines.length === 0) {
98
+ lines.push(chalk.dim(" (empty)"));
99
+ }
100
+ // Footer hint
101
+ if (this.state.inputActive && this.state.inputPlaceholder) {
102
+ lines.push("");
103
+ lines.push(` ${chalk.cyan(">")} ${this.state.inputPlaceholder}`);
104
+ }
105
+ else if (!this.state.inputActive) {
106
+ lines.push("");
107
+ lines.push(chalk.dim(" \u2191\u2193 scroll [Ctrl-O] collapse"));
108
+ }
109
+ return lines;
110
+ }
111
+ }
package/dist/models.js CHANGED
@@ -19,20 +19,70 @@
19
19
  // Chroma hallucination study. "relaxed" = 95%+ on all three axes.
20
20
  export const MODEL_CAPABILITIES = {
21
21
  // ── Anthropic Claude (Apr 2026) ──
22
- // Opus: only model that earns "relaxed". 100% on 38-task routing, 95%+ IFEval.
22
+ // Opus 4.7: only model that earns "relaxed". 100% on 38-task routing, 95%+ IFEval.
23
+ // Step-change agentic coding over Opus 4.6. 1M tokens, 128K output.
23
24
  "claude-opus-4-7": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7" },
24
- "claude-opus-4-6": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.6" },
25
- // Sonnet: good but loses thread more than Opus on autonomous multi-file work.
26
- "claude-sonnet-4-6": { contextWindow: 1_000_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "Sonnet 4.6" },
27
- // Haiku: cheapest Claude. Skips steps more often. No 1M upgrade.
25
+ "claude-opus-4-7-low": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "moderate", displayName: "Opus 4.7 Low" },
26
+ "claude-opus-4-7-medium": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Medium" },
27
+ "claude-opus-4-7-high": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 High" },
28
+ "claude-opus-4-7-xhigh": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Extra High" },
29
+ "claude-opus-4-7-max": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Max" },
30
+ "claude-opus-4-7-thinking": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Thinking" },
31
+ "claude-opus-4-7-thinking-low": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "moderate", displayName: "Opus 4.7 Low Thinking" },
32
+ "claude-opus-4-7-thinking-medium": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Medium Thinking" },
33
+ "claude-opus-4-7-thinking-high": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 High Thinking" },
34
+ "claude-opus-4-7-thinking-xhigh": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Extra High Thinking" },
35
+ "claude-opus-4-7-thinking-max": { contextWindow: 1_000_000, safeContext: 400_000, contextConstraint: "relaxed", displayName: "Opus 4.7 Max Thinking" },
36
+ // Sonnet 4.6: 200K context, tight constraint.
37
+ "claude-sonnet-4-6": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "tight", displayName: "Sonnet 4.6" },
38
+ "claude-4.6-sonnet-medium": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "tight", displayName: "Sonnet 4.6 Medium" },
39
+ "claude-sonnet-4-6-thinking": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "tight", displayName: "Sonnet 4.6 Thinking" },
40
+ "claude-4.6-sonnet-medium-thinking": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "tight", displayName: "Sonnet 4.6 Medium Thinking" },
41
+ // Sonnet 4.5: 200K context.
42
+ "claude-sonnet-4-5": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4.5" },
43
+ "claude-4.5-sonnet": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4.5" },
44
+ "claude-sonnet-4-5-thinking": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4.5 Thinking" },
45
+ "claude-4.5-sonnet-thinking": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4.5 Thinking" },
46
+ // Claude 4 Opus/Sonnet (original): deprecated June 2026. 200K context.
47
+ "claude-opus-4": { contextWindow: 200_000, safeContext: 80_000, contextConstraint: "moderate", displayName: "Opus 4.0" },
48
+ "claude-sonnet-4": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4.0" },
49
+ "claude-4-sonnet": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4" },
50
+ "claude-4-sonnet-1m": { contextWindow: 1_000_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "Sonnet 4 1M" },
51
+ "claude-sonnet-4-thinking": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4 Thinking" },
52
+ "claude-4-sonnet-thinking": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Sonnet 4 Thinking" },
53
+ "claude-4-sonnet-1m-thinking": { contextWindow: 1_000_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "Sonnet 4 1M Thinking" },
54
+ // Haiku 4.5: cheapest Claude. 200K context, near-frontier smarts.
28
55
  "claude-haiku-4-5": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Haiku 4.5" },
29
56
  "claude-haiku-4-5-20251001": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Haiku 4.5" },
57
+ "claude-haiku-4-6": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Haiku 4.6" },
58
+ "claude-haiku-4": { contextWindow: 200_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Haiku 4" },
30
59
  // ── OpenAI (Apr 2026 — GPT-4.1/o3/o4-mini retired Feb 2026) ──
31
60
  // GPT-5.4: current flagship. 1M context, 128K output. Good but literal.
32
61
  "gpt-5.4": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4" },
33
- "gpt-5.4-mini": { contextWindow: 1_050_000, safeContext: 200_000, contextConstraint: "moderate", displayName: "GPT-5.4 Mini" },
62
+ "gpt-5.4-low": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 Low" },
63
+ "gpt-5.4-medium": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 Medium" },
64
+ "gpt-5.4-medium-fast": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 Fast" },
65
+ "gpt-5.4-high": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 High" },
66
+ "gpt-5.4-high-fast": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 High Fast" },
67
+ "gpt-5.4-xhigh": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 Extra High" },
68
+ "gpt-5.4-xhigh-fast": { contextWindow: 1_050_000, safeContext: 300_000, contextConstraint: "moderate", displayName: "GPT-5.4 Extra High Fast" },
69
+ "gpt-5.4-mini": { contextWindow: 1_050_000, safeContext: 200_000, contextConstraint: "tight", displayName: "GPT-5.4 Mini" },
70
+ "gpt-5.4-mini-low": { contextWindow: 1_050_000, safeContext: 200_000, contextConstraint: "tight", displayName: "GPT-5.4 Mini Low" },
71
+ "gpt-5.4-mini-medium": { contextWindow: 1_050_000, safeContext: 200_000, contextConstraint: "tight", displayName: "GPT-5.4 Mini Medium" },
72
+ "gpt-5.4-mini-high": { contextWindow: 1_050_000, safeContext: 200_000, contextConstraint: "tight", displayName: "GPT-5.4 Mini High" },
73
+ "gpt-5.4-mini-xhigh": { contextWindow: 1_050_000, safeContext: 200_000, contextConstraint: "tight", displayName: "GPT-5.4 Mini Extra High" },
34
74
  // Codex 5.3: best agentic coder from OpenAI. 400K context, 128K output.
35
75
  "gpt-5.3-codex": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.3" },
76
+ "gpt-5.3-codex-low": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.3 Low" },
77
+ "gpt-5.3-codex-high": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.3 High" },
78
+ "gpt-5.3-codex-xhigh": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.3 Extra High" },
79
+ "gpt-5.3-codex-fast": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.3 Fast" },
80
+ // Older OpenAI
81
+ "gpt-5.2": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "GPT-5.2" },
82
+ "gpt-5.2-codex": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.2" },
83
+ "gpt-5.1": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "GPT-5.1" },
84
+ "gpt-5": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "GPT-5" },
85
+ "gpt-5.1-codex-mini": { contextWindow: 400_000, safeContext: 160_000, contextConstraint: "moderate", displayName: "Codex 5.1 Mini" },
36
86
  // ── Google Gemini 3 (Apr 2026 — Gemini 2.5 deprecated June 2026) ──
37
87
  // Large context but terrible at agentic coding: 13.5% SWE-bench (vs Sonnet 31.2%).
38
88
  // Good for reading lots of code, bad at following through. Needs surgical tasks.
@@ -40,6 +90,11 @@ export const MODEL_CAPABILITIES = {
40
90
  "gemini-3-pro": { contextWindow: 1_000_000, safeContext: 350_000, contextConstraint: "tight", displayName: "Gemini 3 Pro" },
41
91
  // Flash: 8.2% SWE-bench. Essentially unusable for autonomous agent work.
42
92
  "gemini-3-flash": { contextWindow: 1_000_000, safeContext: 250_000, contextConstraint: "tight", displayName: "Gemini 3 Flash" },
93
+ // ── xAI Grok ──
94
+ "grok-4-20": { contextWindow: 256_000, safeContext: 80_000, contextConstraint: "moderate", displayName: "Grok 4.20" },
95
+ "grok-4-20-thinking": { contextWindow: 256_000, safeContext: 80_000, contextConstraint: "moderate", displayName: "Grok 4.20 Thinking" },
96
+ // ── Moonshot ──
97
+ "kimi-k2.5": { contextWindow: 128_000, safeContext: 40_000, contextConstraint: "tight", displayName: "Kimi K2.5" },
43
98
  // ── DeepSeek V3.2 (Apr 2026 — V3/R1 superseded, V4 not yet out) ──
44
99
  "deepseek-chat": { contextWindow: 128_000, safeContext: 40_000, contextConstraint: "tight", displayName: "DeepSeek V3.2" },
45
100
  "deepseek-reasoner": { contextWindow: 128_000, safeContext: 45_000, contextConstraint: "moderate", displayName: "DeepSeek V3.2 Reasoner" },
@@ -50,7 +105,9 @@ export const MODEL_CAPABILITIES = {
50
105
  // ── Cursor models (opaque routing) ──
51
106
  "auto": { contextWindow: 256_000, safeContext: 60_000, contextConstraint: "moderate", displayName: "Cursor Auto" },
52
107
  "composer-2": { contextWindow: 200_000, safeContext: 40_000, contextConstraint: "tight", displayName: "Composer 2" },
108
+ "composer-2-fast": { contextWindow: 200_000, safeContext: 40_000, contextConstraint: "tight", displayName: "Composer 2 Fast" },
53
109
  "composer": { contextWindow: 128_000, safeContext: 30_000, contextConstraint: "tight", displayName: "Composer" },
110
+ "composer-1.5": { contextWindow: 128_000, safeContext: 30_000, contextConstraint: "tight", displayName: "Composer 1.5" },
54
111
  // ── Qwen (Apr 2026 — qwen3.6-plus is newest flagship) ──
55
112
  "qwen3.6-plus": { contextWindow: 1_000_000, safeContext: 200_000, contextConstraint: "moderate", displayName: "Qwen 3.6 Plus" },
56
113
  "qwen3-coder-plus": { contextWindow: 1_000_000, safeContext: 200_000, contextConstraint: "moderate", displayName: "Qwen 3 Coder Plus" },
@@ -13,6 +13,10 @@ export interface PlannerRateLimitInfo {
13
13
  windows: Map<string, RateLimitWindow>;
14
14
  resetsAt?: number;
15
15
  costUsd: number;
16
+ /** Peak total input tokens (input + cache) in any single planner turn — proxy for context-window occupancy. */
17
+ contextTokens?: number;
18
+ /** Model used by the current planner query (for safeContext lookup). */
19
+ model?: string;
16
20
  }
17
21
  export interface PlannerOpts {
18
22
  cwd: string;
@@ -30,14 +34,16 @@ export interface PlannerOpts {
30
34
  */
31
35
  maxTurns?: number;
32
36
  /**
33
- * Tools the planner agent may use. Defaults to read-only + Write (for outFile
34
- * resilience). Deliberately excludes Bash/Agent/TodoWrite/WebFetch to prevent
35
- * the multi-turn tool loops that cause error_max_turns with thinking models.
37
+ * Tools the planner agent may use. Defaults to the full Claude tool suite.
36
38
  */
37
39
  tools?: string[];
38
40
  }
39
41
  export declare function setPlannerEnvResolver(fn: ((model?: string) => Record<string, string> | undefined) | undefined): void;
40
42
  export declare function getTotalPlannerCost(): number;
43
+ export declare function getPeakPlannerContext(): {
44
+ tokens: number;
45
+ model?: string;
46
+ };
41
47
  export declare function getPlannerRateLimitInfo(): PlannerRateLimitInfo;
42
48
  export declare function runPlannerQuery(prompt: string, opts: PlannerOpts, onLog: PlannerLog): Promise<string>;
43
49
  export declare function postProcess(raw: Task[], budget: number | undefined, onLog: (text: string) => void): Task[];
@@ -1,7 +1,13 @@
1
1
  import { query } from "@anthropic-ai/claude-agent-sdk";
2
2
  import { readFileSync } from "fs";
3
- import { NudgeError } from "./types.js";
3
+ import { NudgeError, extractToolTarget, sumUsageTokens } from "./types.js";
4
4
  import { writeTranscriptEvent } from "./transcripts.js";
5
+ /** Log a tool invocation with a short target for planner queries. */
6
+ const logTool = (label, input) => {
7
+ const target = extractToolTarget(input);
8
+ return target ? `${label} \u2192 ${target}` : label;
9
+ };
10
+ const DEFAULT_TOOLS = ["Read", "Glob", "Grep", "Write", "Bash", "WebFetch", "WebSearch", "TodoWrite", "Agent"];
5
11
  const DEFAULT_MAX_TURNS = 20;
6
12
  // ── Shared env resolver (set once at run start, used by every planner query) ──
7
13
  //
@@ -20,6 +26,11 @@ function isRateLimitError(err) {
20
26
  }
21
27
  let _totalPlannerCostUsd = 0;
22
28
  export function getTotalPlannerCost() { return _totalPlannerCostUsd; }
29
+ let _peakPlannerContextTokens = 0;
30
+ let _peakPlannerContextModel;
31
+ export function getPeakPlannerContext() {
32
+ return { tokens: _peakPlannerContextTokens, model: _peakPlannerContextModel };
33
+ }
23
34
  let _plannerRateLimitInfo = {
24
35
  utilization: 0, status: "", isUsingOverage: false, windows: new Map(), costUsd: 0,
25
36
  };
@@ -65,22 +76,6 @@ async function throttlePlanner(onLog, aborted) {
65
76
  }
66
77
  // Exhausted backoffs — proceed anyway, the retry loop will catch a rejection.
67
78
  }
68
- /**
69
- * Pick a short, human-readable target for a tool invocation (Read/Grep/Bash/…).
70
- * Prefers explicit file paths; falls back to the first few tokens of a shell
71
- * command. Returns `""` when the input has no useful identifier.
72
- */
73
- function extractToolTarget(input) {
74
- if (!input)
75
- return "";
76
- const p = input.path ?? input.file_path ?? input.pattern;
77
- if (typeof p === "string" && p)
78
- return p;
79
- if (typeof input.command === "string" && input.command) {
80
- return input.command.split(" ").slice(0, 3).join(" ");
81
- }
82
- return "";
83
- }
84
79
  // ── Query execution ──
85
80
  const NUDGE_MS = 15 * 60 * 1000;
86
81
  const HARD_TIMEOUT_MS = 30 * 60 * 1000;
@@ -122,7 +117,7 @@ export async function runPlannerQuery(prompt, opts, onLog) {
122
117
  throw new Error("Planner query failed after retries");
123
118
  }
124
119
  async function runPlannerQueryOnce(prompt, opts, onLog) {
125
- _plannerRateLimitInfo = { utilization: 0, status: "", isUsingOverage: false, windows: new Map(), costUsd: 0 };
120
+ _plannerRateLimitInfo = { utilization: 0, status: "", isUsingOverage: false, windows: new Map(), costUsd: 0, contextTokens: 0, model: opts.model };
126
121
  let resultText = "";
127
122
  let structuredOutput;
128
123
  const startedAt = Date.now();
@@ -144,8 +139,8 @@ async function runPlannerQueryOnce(prompt, opts, onLog) {
144
139
  options: {
145
140
  cwd: opts.cwd,
146
141
  model: opts.model,
147
- tools: opts.tools ?? ["Read", "Glob", "Grep", "Write"],
148
- allowedTools: opts.tools ?? ["Read", "Glob", "Grep", "Write"],
142
+ tools: opts.tools ?? DEFAULT_TOOLS,
143
+ allowedTools: opts.tools ?? DEFAULT_TOOLS,
149
144
  permissionMode: opts.permissionMode,
150
145
  ...(opts.permissionMode === "bypassPermissions" && { allowDangerouslySkipPermissions: true }),
151
146
  persistSession: true,
@@ -309,6 +304,16 @@ async function runPlannerQueryOnce(prompt, opts, onLog) {
309
304
  // turn message for tool_use / thinking / text so the ticker still moves
310
305
  // every ~6-15s instead of sitting silent for minutes.
311
306
  if (msg.type === "assistant") {
307
+ const u = msg.message?.usage;
308
+ if (u) {
309
+ const turnTotal = sumUsageTokens(u);
310
+ if (turnTotal > (_plannerRateLimitInfo.contextTokens ?? 0))
311
+ _plannerRateLimitInfo.contextTokens = turnTotal;
312
+ if (turnTotal > _peakPlannerContextTokens) {
313
+ _peakPlannerContextTokens = turnTotal;
314
+ _peakPlannerContextModel = opts.model;
315
+ }
316
+ }
312
317
  const content = msg.message?.content;
313
318
  if (Array.isArray(content)) {
314
319
  for (const part of content) {
package/dist/providers.js CHANGED
@@ -11,6 +11,28 @@ import { getBearerToken, clearTokenCache } from "./auth.js";
11
11
  import { DEFAULT_MODEL } from "./models.js";
12
12
  import { CURSOR_PRIORITY_MODELS, CURSOR_KNOWN_MODELS, KNOWN_CURSOR_MODEL_IDS, cursorModelHint, } from "./cursor-models.js";
13
13
  import { VERSION } from "./_version.js";
14
+ /** Cached system Node.js and agent script paths — resolved once, reused across envFor calls. */
15
+ let _cachedAgentNode = null;
16
+ let _cachedAgentScript = null;
17
+ /** Resolve system Node.js and agent index.js paths. Returns [nodePath, scriptPath] or [null, null]. */
18
+ function resolveAgentPaths(timeoutMs = 2_000) {
19
+ let nodePath = null;
20
+ let agentJs = null;
21
+ try {
22
+ nodePath = execSync("which node 2>/dev/null", { timeout: timeoutMs, encoding: "utf-8", shell: "bash" }).trim() || null;
23
+ const agentPath = execSync("command -v agent 2>/dev/null || command -v cursor-agent 2>/dev/null", {
24
+ timeout: timeoutMs, encoding: "utf-8", shell: "bash",
25
+ }).trim();
26
+ if (agentPath) {
27
+ const agentDir = dirname(realpathSync(agentPath));
28
+ const indexPath = `${agentDir}/index.js`;
29
+ if (existsSync(indexPath))
30
+ agentJs = indexPath;
31
+ }
32
+ }
33
+ catch { }
34
+ return [nodePath, agentJs];
35
+ }
14
36
  /** Run the installed package CLI with `node` (avoids npx/npm invoking extra tooling on macOS). */
15
37
  function resolveCursorComposerCli() {
16
38
  try {
@@ -124,6 +146,23 @@ export function envFor(p) {
124
146
  // SDK replaces env for subprocesses — force these so nothing inherits a bad CI / skip flag.
125
147
  base.CI = "true";
126
148
  base.CURSOR_SKIP_KEYCHAIN = "1";
149
+ // Bridge mode controls the agent behavior: "plan" enables tool use (Read,
150
+ // Glob, Grep, Write, Bash), "ask" gives a chat-only assistant. Planner
151
+ // agents and workers must use "plan" so they actually interact with the codebase.
152
+ base.CURSOR_BRIDGE_MODE = "plan";
153
+ // Use system Node.js for agent subprocess to avoid macOS segfaults with
154
+ // bundled Node.js. Resolve lazily.
155
+ if (!_cachedAgentNode || !_cachedAgentScript) {
156
+ const [node, script] = resolveAgentPaths(2_000);
157
+ _cachedAgentNode = node;
158
+ _cachedAgentScript = script;
159
+ }
160
+ if (_cachedAgentNode) {
161
+ base.CURSOR_AGENT_NODE = _cachedAgentNode;
162
+ }
163
+ if (_cachedAgentScript) {
164
+ base.CURSOR_AGENT_SCRIPT = _cachedAgentScript;
165
+ }
127
166
  return base;
128
167
  }
129
168
  const key = resolveKey(p);
@@ -814,21 +853,7 @@ async function startProxyProcess(baseUrl, url, port) {
814
853
  console.log(chalk.yellow(`\n Proxy not running at ${baseUrl} — starting it for you…`));
815
854
  // Resolve system node and agent index.js so the proxy uses system Node.js
816
855
  // for the agent subprocess (avoids segfaults with --list-models on macOS).
817
- let sysNode = null;
818
- let agentJs = null;
819
- try {
820
- sysNode = execSync("which node 2>/dev/null", { timeout: 3_000, encoding: "utf-8", shell: "bash" }).trim() || null;
821
- const agentPath = execSync("command -v agent 2>/dev/null || command -v cursor-agent 2>/dev/null", {
822
- timeout: 3_000, encoding: "utf-8", shell: "bash",
823
- }).trim();
824
- if (agentPath) {
825
- const agentDir = dirname(realpathSync(agentPath));
826
- const indexPath = `${agentDir}/index.js`;
827
- if (existsSync(indexPath))
828
- agentJs = indexPath;
829
- }
830
- }
831
- catch { }
856
+ const [sysNode, agentJs] = resolveAgentPaths(3_000);
832
857
  const apiKeyStored = loadProviders().find(p => p.cursorProxy)?.cursorApiKey;
833
858
  const agentToken = resolveCursorAgentToken();
834
859
  if (!agentToken) {
@@ -878,6 +903,9 @@ async function startProxyProcess(baseUrl, url, port) {
878
903
  // the CLI path injects keychain-shim-inject.js via NODE_OPTIONS which no-ops
879
904
  // /usr/bin/security calls on macOS (cursor-composer/dist/lib/process.js).
880
905
  CURSOR_BRIDGE_USE_ACP: "0",
906
+ // Default bridge mode: "plan" enables tool use (Read, Glob, Grep, Write, Bash).
907
+ // "ask" gives a chat-only assistant that doesn't interact with the codebase.
908
+ CURSOR_BRIDGE_MODE: "plan",
881
909
  // cursor-composer chat-only mode fakes HOME to a temp dir; on macOS the agent still waits on
882
910
  // Keychain (~30s) for `cursor-user` despite CURSOR_API_KEY. Use the real workspace profile.
883
911
  CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE: "false",
@@ -905,6 +933,7 @@ async function startProxyProcess(baseUrl, url, port) {
905
933
  childEnv: {
906
934
  CI: proxyEnv.CI,
907
935
  CURSOR_SKIP_KEYCHAIN: proxyEnv.CURSOR_SKIP_KEYCHAIN,
936
+ CURSOR_BRIDGE_MODE: proxyEnv.CURSOR_BRIDGE_MODE,
908
937
  CURSOR_BRIDGE_USE_ACP: proxyEnv.CURSOR_BRIDGE_USE_ACP,
909
938
  CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE: proxyEnv.CURSOR_BRIDGE_CHAT_ONLY_WORKSPACE,
910
939
  CURSOR_API_KEY: "(set)",
package/dist/render.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import chalk from "chalk";
1
2
  import type { Swarm } from "./swarm.js";
2
- import type { RateLimitWindow } from "./types.js";
3
+ import type { RLGetter } from "./types.js";
3
4
  import type { RunInfo, SteeringContext, SteeringEvent } from "./ui.js";
5
+ import { InteractivePanel } from "./interactive-panel.js";
4
6
  export interface Section {
5
7
  title: string;
6
8
  rows: string[];
@@ -14,6 +16,11 @@ export interface ContentRenderer {
14
16
  export declare function truncate(s: string, max: number): string;
15
17
  export declare function fmtTokens(n: number): string;
16
18
  export declare function fmtDur(ms: number): string;
19
+ /** Context-fill percentage and color function for a token count vs safe limit. */
20
+ export declare function contextFillInfo(tokens: number, safe: number): {
21
+ pct: number;
22
+ color: typeof chalk;
23
+ };
17
24
  export declare function renderUnifiedFrame(params: {
18
25
  model?: string;
19
26
  phase: string;
@@ -36,13 +43,7 @@ export declare function renderUnifiedFrame(params: {
36
43
  extraFooterRows?: string[];
37
44
  maxRows?: number;
38
45
  }): string;
39
- type RLGetter = () => {
40
- utilization: number;
41
- isUsingOverage: boolean;
42
- windows: Map<string, RateLimitWindow>;
43
- resetsAt?: number;
44
- };
45
- export declare function renderFrame(swarm: Swarm, showHotkeys: boolean, runInfo?: RunInfo, selectedAgentId?: number, maxRows?: number, debrief?: string): string;
46
+ export declare function renderFrame(swarm: Swarm, showHotkeys: boolean, runInfo?: RunInfo, selectedAgentId?: number, maxRows?: number, panel?: InteractivePanel): string;
46
47
  export interface SteeringViewData {
47
48
  /** The ephemeral ticker heartbeat -- elapsed, tool count, cost, current reasoning snippet. */
48
49
  statusLine: string;
@@ -51,6 +52,5 @@ export interface SteeringViewData {
51
52
  /** Optional context read from disk at setSteering() time. */
52
53
  context?: SteeringContext;
53
54
  }
54
- export declare function renderSteeringFrame(runInfo: RunInfo, data: SteeringViewData, showHotkeys: boolean, rlGetter?: RLGetter, maxRows?: number, debrief?: string): string;
55
+ export declare function renderSteeringFrame(runInfo: RunInfo, data: SteeringViewData, showHotkeys: boolean, rlGetter?: RLGetter, maxRows?: number, panel?: InteractivePanel): string;
55
56
  export declare function renderSummary(swarm: Swarm): string;
56
- export {};