pi-cursor-sdk 0.0.0 → 0.1.1

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.
@@ -0,0 +1,667 @@
1
+ # Cursor Model UX Spec
2
+
3
+ > Maintainer note: this is an internal design and behavior spec for pi-cursor-sdk. If you are trying to install or use the extension, start with the main [README](../README.md) instead.
4
+
5
+ ## Status
6
+
7
+ Implemented design target. This file describes the intended Cursor model UX and should stay aligned with the current code in `src/`.
8
+
9
+ Current implementation notes:
10
+
11
+ - Cursor context variants use `base@context` pi model IDs.
12
+ - Cursor `reasoning`, `effort`, and boolean `thinking` parameters are driven by pi native thinking when the Cursor SDK exposes those controls.
13
+ - Cursor `fast` is extension state, not model identity.
14
+ - Cursor fast status uses `ctx.ui.setStatus()`; the default pi footer remains intact.
15
+ - Installed `@cursor/sdk` user messages accept images, and Cursor models are treated as image-capable; registered input metadata is `text` plus `image`.
16
+ - `@cursor/sdk` is a package dependency of this extension; users should not need a global SDK install.
17
+ - Cursor auth uses the `CURSOR_API_KEY` environment variable. The extension config file stores only non-secret Cursor-only state such as fast defaults.
18
+ - Local agents do not pass `settingSources` by default because the current Cursor SDK writes setting/rule loading INFO logs directly to terminal output, which corrupts pi's TUI.
19
+ - Cursor SDK models are treated as thinking-capable even when pi reports `thinking=no`; that pi column only means the SDK did not expose a pi-controllable thinking parameter for that model.
20
+ - Cursor-side thinking and tool activity are surfaced as trace content before final text. Cursor text deltas are buffered until the run finishes so the saved message order is trace first, final answer second.
21
+ - For models without a catalog `context` parameter, context windows are not hardcoded. The extension ships a bundled SDK-derived default/non-Max cache generated from `createAgentPlatform().checkpointStore.loadLatest(agentId).tokenDetails.maxTokens`. Successful runs can update a local override cache, but model discovery does not probe models at startup.
22
+ - Max Mode context windows are distinct from default/non-Max context windows. `@cursor/sdk` 1.0.12 exposes internal protobuf fields named `maxMode`/`max_mode`, but the public `ModelSelection` type and the local executor path do not pass a Max Mode selector for local agent runs. Do not advertise Max Mode context windows unless the SDK catalog exposes an exact parameter/variant or the SDK public API adds a Max Mode selector that the extension actually sends.
23
+
24
+ ## Goal
25
+
26
+ Make Cursor models feel native in pi by leaning on pi's existing model, thinking, footer, and session behavior instead of building a parallel Cursor parameter system.
27
+
28
+ Main outcomes:
29
+
30
+ - `pi --list-models` shows pi-native Cursor models with accurate `contextWindow`, pi-controllable thinking metadata, and conservative defaults where the Cursor SDK does not expose limits or capabilities.
31
+ - `shift+tab` is pi's native thinking control and drives Cursor `reasoning` or `effort`.
32
+ - Cursor context options are represented as pi-visible model variants when they change native model metadata.
33
+ - Cursor-only state, currently `fast`, is controlled by extension commands and shown through native status text.
34
+ - The default pi footer remains intact.
35
+ - Model capabilities are discovered from the Cursor SDK, not hardcoded per model.
36
+
37
+ Native tradeoff: context-capable Cursor models intentionally use context-qualified pi model IDs. This gives up one completely clean row per Cursor base model, but it lets pi's native `contextWindow`, footer context usage, context overflow checks, compaction behavior, session restore, model selection, and `--list-models` metadata stay accurate.
38
+
39
+ ## Non-goals
40
+
41
+ Not building now:
42
+
43
+ - verbosity support
44
+ - custom UI panels
45
+ - generic pi model-parameter system for all providers
46
+ - full custom footer replacement
47
+ - independent Claude `thinking` toggle separate from pi thinking
48
+ - multi-parameter CLI suffixes such as `--model cursor/gpt-5.5:medium:272k:fast`
49
+
50
+ ## Source of Truth
51
+
52
+ Cursor SDK is the source of truth for Cursor model IDs and Cursor-supported parameters.
53
+
54
+ At startup, the extension calls:
55
+
56
+ ```ts
57
+ Cursor.models.list({ apiKey });
58
+ ```
59
+
60
+ For each model, use:
61
+
62
+ - `model.id`
63
+ - `model.displayName`
64
+ - `model.parameters`
65
+ - `model.variants`
66
+ - default variant: `variant.isDefault === true`, else first variant
67
+
68
+ This means new Cursor models and changed Cursor parameters are picked up after reload/restart.
69
+
70
+ Pi model metadata is also a source of truth for pi-native behavior:
71
+
72
+ - `ProviderModelConfig.id`
73
+ - `ProviderModelConfig.name`
74
+ - `ProviderModelConfig.reasoning`: means pi-controllable thinking, not whether a Cursor model is thinking-capable
75
+ - `ProviderModelConfig.thinkingLevelMap`
76
+ - `ProviderModelConfig.contextWindow`
77
+ - `ProviderModelConfig.maxTokens`
78
+ - `ProviderModelConfig.input`
79
+
80
+ If a Cursor parameter changes any of those pi-native fields, model registration must expose that change to pi.
81
+
82
+ ### Refresh Current Cursor Matrix
83
+
84
+ Run this whenever Cursor releases or changes models:
85
+
86
+ ```bash
87
+ node --input-type=module <<'EOF'
88
+ import { Cursor } from '@cursor/sdk';
89
+
90
+ const models = await Cursor.models.list({ apiKey: process.env.CURSOR_API_KEY });
91
+ for (const model of models) {
92
+ const options = (model.parameters ?? [])
93
+ .map((param) => `${param.id}: ${param.values.map((value) => value.value).join(', ')}`)
94
+ .join(' | ') || 'none';
95
+ const defaultVariant = model.variants?.find((variant) => variant.isDefault) ?? model.variants?.[0];
96
+ const defaults = defaultVariant?.params?.map((param) => `${param.id}=${param.value}`).join('; ') || 'none';
97
+ console.log(`${model.id}\t${model.displayName}\t${options}\t${defaults}`);
98
+ }
99
+ EOF
100
+ ```
101
+
102
+ ## Design Direction
103
+
104
+ Use native pi abstractions wherever possible:
105
+
106
+ | Concern | Representation |
107
+ |---|---|
108
+ | Cursor base model | pi provider model |
109
+ | Cursor `context` | pi-visible model variant because it changes `contextWindow` |
110
+ | Cursor `reasoning` | pi native thinking via `thinkingLevelMap` |
111
+ | Cursor `effort` | pi native thinking via `thinkingLevelMap` |
112
+ | Cursor `thinking=false` | pi native `off` |
113
+ | Cursor `fast` | extension state, not model identity |
114
+ | Footer | default pi footer plus optional extension status |
115
+
116
+ Reason:
117
+
118
+ - pi already persists model and thinking selection.
119
+ - pi already clamps unsupported thinking levels from `thinkingLevelMap`.
120
+ - pi context display, context overflow, and compaction depend on `contextWindow`.
121
+ - extension APIs can replace the whole footer but cannot partially mutate the default model text.
122
+
123
+ ## Model Registration
124
+
125
+ Register a `cursor` provider with `pi.registerProvider()`.
126
+
127
+ Rules:
128
+
129
+ - Register one pi model for each Cursor base model when there is no Cursor `context` parameter.
130
+ - Register one pi model per Cursor `context` value when the model exposes a `context` parameter.
131
+ - Do not encode `reasoning`, `effort`, `thinking`, or `fast` into pi model IDs.
132
+ - Prefer stable, readable `@<context>` suffixes that do not conflict with pi's final `:<thinking>` suffix parser.
133
+ - Sort Cursor models by base ID, then context value in Cursor SDK order before calling `pi.registerProvider()`. Registration order matters for `/model` display and model cycling; `--list-models` sorts output separately.
134
+
135
+ Recommended context-variant ID format:
136
+
137
+ ```text
138
+ cursor/gpt-5.5@1m
139
+ cursor/gpt-5.5@272k
140
+ cursor/claude-opus-4-7@1m
141
+ cursor/claude-opus-4-7@300k
142
+ cursor/composer-2
143
+ ```
144
+
145
+ Avoid colon-based context IDs in the first implementation unless this spec is intentionally changed:
146
+
147
+ ```text
148
+ cursor/gpt-5.5:1m
149
+ cursor/gpt-5.5:1m:medium
150
+ ```
151
+
152
+ Those can work technically because pi parses only the final `:<thinking>` suffix, but they overload pi's documented thinking shorthand.
153
+
154
+ Avoid this old parameter encoding:
155
+
156
+ ```text
157
+ cursor/gpt-5.5:context=1m;fast=false;reasoning=medium
158
+ cursor/claude-opus-4-7:context=1m;effort=xhigh;thinking=true
159
+ ```
160
+
161
+ Reason:
162
+
163
+ - `@1m` keeps context visually separate from pi's native `:medium` thinking suffix.
164
+ - Context variants make `contextWindow` accurate in `--list-models`, the native footer, context overflow checks, and compaction logic.
165
+ - `fast` is intentionally not a model variant because it does not affect pi model metadata and would double list noise.
166
+
167
+ ### Metadata Per Registered Model
168
+
169
+ Each registered model must set:
170
+
171
+ - `id`: context-qualified pi model ID when needed.
172
+ - `name`: human-readable Cursor display name plus context when useful.
173
+ - `reasoning`: `true` only if a Cursor `reasoning`, `effort`, or `thinking` parameter can map to pi thinking. This controls pi's thinking UI and `pi --list-models` `thinking` column; it must not be used to claim whether the Cursor model can think internally. Cursor SDK models are thinking-capable even when this is `false`.
174
+ - `thinkingLevelMap`: model-specific pi-to-Cursor mapping for pi UI, clamping, persistence, and footer display.
175
+ - `contextWindow`: parsed from context variant, else conservative fallback.
176
+ - `maxTokens`: conservative explicit value until Cursor SDK exposes output limits.
177
+ - `input`: supported input types. The installed Cursor SDK accepts `SDKUserMessage.images`, and Cursor models are expected to support image input, so advertise `["text", "image"]`.
178
+ - `cost`: zeroed unless reliable Cursor costs are available.
179
+
180
+ The extension stores runtime metadata in an internal map keyed by registered pi model ID. That map records the Cursor base model ID, selected context param, default params, and discovered capabilities. `ProviderModelConfig` has no dedicated metadata field, so do not rely on hidden custom fields for this state.
181
+
182
+ ## Dynamic Capabilities
183
+
184
+ No per-model hardcoded control list.
185
+
186
+ Infer behavior from discovered params:
187
+
188
+ | Cursor param | Extension behavior |
189
+ |---|---|
190
+ | `context` with values | register pi-visible context variants |
191
+ | `reasoning` | populate `thinkingLevelMap` |
192
+ | `effort` | populate `thinkingLevelMap` |
193
+ | `thinking` with `true/false` | map `false` to pi `off`; map `true` to the enabled pi level chosen for boolean-only thinking |
194
+ | `fast` with `true/false` | enable fast extension setting |
195
+
196
+ Unsupported Cursor-only actions are no-op plus a short notification.
197
+
198
+ Example:
199
+
200
+ ```text
201
+ Fast mode not supported by gemini-3.1-pro
202
+ ```
203
+
204
+ ## Keybindings And Commands
205
+
206
+ Native pi keybindings:
207
+
208
+ | Action | Keybinding | Owner |
209
+ |---|---:|---|
210
+ | Cycle thinking / reasoning / effort | `shift+tab` | pi native `app.thinking.cycle` |
211
+ | Select model / context variant | `/model`, `ctrl+l`, scoped model cycling | pi native model selection |
212
+
213
+ Cursor extension controls:
214
+
215
+ | Action | Preferred control | Applies when |
216
+ |---|---:|---|
217
+ | Toggle fast | `/cursor-fast` | model has `fast` |
218
+
219
+ Do not register a shortcut for `shift+tab`. Pi reserves the native thinking keybinding, and the extension should only influence it through model metadata.
220
+
221
+ Do not add a context-cycle shortcut in the first pass. Context is a pi model variant, so users should change it through native model selection/cycling.
222
+
223
+ ## Thinking / Reasoning / Effort Mapping
224
+
225
+ Important distinction:
226
+
227
+ - **Cursor thinking support** applies to all Cursor SDK models. The extension should assume Cursor models can think and may emit thinking deltas.
228
+ - **Pi-controllable thinking** means Cursor exposes a `reasoning`, `effort`, or `thinking` parameter that the extension can set from pi's native thinking level. These models register `reasoning: true` and show `thinking=yes` in `pi --list-models`.
229
+ - **Cursor SDK thinking-control gap** means the model can still think, but the SDK does not expose a user-controllable thinking parameter for that model. These models register `reasoning: false` and show `thinking=no` in `pi --list-models` because pi cannot control a level for them. The extension still parses Cursor `thinking-delta` events if they are emitted.
230
+
231
+ Do not mark a model `reasoning: true` only because it can think. That would make pi show controls such as `--thinking`, `:medium`, and shift+tab even though the extension cannot translate them into Cursor SDK params.
232
+
233
+ Pi levels:
234
+
235
+ ```text
236
+ off, minimal, low, medium, high, xhigh
237
+ ```
238
+
239
+ Cursor values vary by model. Build `thinkingLevelMap` from the values Cursor exposes.
240
+
241
+ Mapping rules:
242
+
243
+ | pi level | Cursor value preference |
244
+ |---|---|
245
+ | `off` | `none`, else `off`, else `false`, else unsupported |
246
+ | `minimal` | `minimal`, else unsupported |
247
+ | `low` | `low` |
248
+ | `medium` | `medium` |
249
+ | `high` | `high`, else `true` for boolean-only thinking |
250
+ | `xhigh` | `xhigh`, else `max`, else `extra-high` |
251
+
252
+ Important details:
253
+
254
+ - Use `null` for unsupported pi levels so pi hides/skips/clamps them natively.
255
+ - Include `xhigh` only when Cursor exposes a real value for it.
256
+ - Prefer exact `xhigh` over `max`. Cursor currently exposes both on some Claude models, and exact `xhigh` is the closer native mapping.
257
+ - If Cursor exposes `reasoning=none`, map pi `off` to `none`.
258
+ - If Cursor exposes `thinking=false`, map pi `off` to `false`.
259
+ - `thinkingLevelMap` does not create Cursor SDK params by itself. It only controls pi-native behavior. The Cursor stream implementation must use the active pi thinking level plus the extension's discovered Cursor metadata to build `ModelSelection.params` for `Agent.create()`.
260
+
261
+ For boolean-only `thinking`, unsupported pi levels must be explicit `null`; otherwise pi treats omitted non-`xhigh` levels as supported. Use this shape unless Cursor exposes richer values:
262
+
263
+ ```ts
264
+ {
265
+ off: "false",
266
+ minimal: null,
267
+ low: null,
268
+ medium: null,
269
+ high: "true",
270
+ xhigh: null,
271
+ }
272
+ ```
273
+
274
+ ## Claude Behavior
275
+
276
+ Some Claude models support both:
277
+
278
+ ```text
279
+ thinking=true|false
280
+ effort=low|medium|high|xhigh|max
281
+ ```
282
+
283
+ Rules:
284
+
285
+ - Pi `off` sends `thinking=false`.
286
+ - Pi enabled levels send `thinking=true` and the mapped `effort`.
287
+ - `shift+tab` changes pi thinking, which changes Cursor `effort`.
288
+ - There is no separate `thinking` toggle.
289
+
290
+ Reason:
291
+
292
+ - This matches pi's single thinking mental model.
293
+ - It avoids an independent Cursor `thinking` state that the native footer, CLI, and session thinking persistence cannot represent.
294
+ - Users can still disable Claude thinking with pi `off`.
295
+
296
+ ## Context Behavior
297
+
298
+ If a Cursor model supports `context`, register one pi model variant per context value.
299
+
300
+ Examples:
301
+
302
+ ```text
303
+ cursor/gpt-5.5@272k
304
+ cursor/gpt-5.5@1m
305
+
306
+ cursor/claude-opus-4-7@300k
307
+ cursor/claude-opus-4-7@1m
308
+
309
+ cursor/grok-4.3@200k
310
+ cursor/grok-4.3@1m
311
+ ```
312
+
313
+ Each variant must:
314
+
315
+ - have an entry in the extension metadata map that points back to the same Cursor base model ID,
316
+ - include the selected Cursor `context` param when calling `Agent.create()`,
317
+ - set pi `contextWindow` from that context value,
318
+ - share the same `thinkingLevelMap` as the base model unless Cursor reports otherwise.
319
+
320
+ Reason:
321
+
322
+ - pi context display and overflow logic must match the actual Cursor context.
323
+ - pi has no generic provider-parameter system that can change `contextWindow` while keeping the same model ID.
324
+
325
+ ## Fast Behavior
326
+
327
+ If a model supports `fast`:
328
+
329
+ ```text
330
+ fast=false <-> fast=true
331
+ ```
332
+
333
+ Rules:
334
+
335
+ - `fast` is extension state, not pi model identity.
336
+ - Toggle with `/cursor-fast`.
337
+ - Store per-session and global per-base-model preferences.
338
+ - When calling `Agent.create()`, include the selected `fast` value in Cursor model params.
339
+ - Show `fast` through `ctx.ui.setStatus()` when enabled.
340
+ - Support a first-pass CLI flag, `--cursor-fast`, to force fast mode for one run when the selected model supports it.
341
+
342
+ Reason:
343
+
344
+ - `fast` does not affect pi `contextWindow`, thinking levels, or input support.
345
+ - Registering fast/non-fast variants would make `--list-models` noisy without improving native pi behavior.
346
+
347
+ Status example:
348
+
349
+ ```text
350
+ cursor fast
351
+ ```
352
+
353
+ ## Footer Behavior
354
+
355
+ Hard requirement:
356
+
357
+ - Leave pi's default footer intact.
358
+ - Do not use `ctx.ui.setFooter()` for the first pass.
359
+ - Use `ctx.ui.setStatus()` only for Cursor-only state that pi cannot show natively, such as `fast`.
360
+ - Non-cursor models must have no Cursor status.
361
+
362
+ Reason:
363
+
364
+ - `ctx.ui.setFooter()` replaces the entire built-in footer.
365
+ - pi has no public extension API to mutate only the model text in the default footer.
366
+ - Reimplementing the default footer would create drift with pi's native footer behavior.
367
+
368
+ Expected native footer behavior:
369
+
370
+ - provider/model is shown by pi from the selected `cursor` model,
371
+ - thinking level is shown by pi when `reasoning` is true,
372
+ - context usage is computed from `contextWindow`,
373
+ - extension status adds only Cursor-only text such as `cursor fast`.
374
+
375
+ `ctx.ui.setStatus()` adds an extension status line in the default footer. It does not patch the built-in model segment. The native shape is closer to:
376
+
377
+ ```text
378
+ ... (cursor) gpt-5.5@1m • medium
379
+ cursor fast
380
+ ```
381
+
382
+ not:
383
+
384
+ ```text
385
+ (cursor) gpt-5.5 • 1M • medium • fast
386
+ ```
387
+
388
+ ## State And Persistence
389
+
390
+ Match pi's native mental model:
391
+
392
+ ### Native pi state
393
+
394
+ Let pi persist:
395
+
396
+ - selected model, including context variant,
397
+ - selected thinking level,
398
+ - session model restore,
399
+ - global default thinking behavior.
400
+
401
+ ### Extension state
402
+
403
+ The extension persists only Cursor-only state:
404
+
405
+ - `fast` per session,
406
+ - `fast` global default per Cursor base model,
407
+ - any future Cursor-only parameter that does not map to pi model metadata.
408
+
409
+ Use:
410
+
411
+ - `pi.appendEntry()` for session state that must survive resume/fork/reload,
412
+ - an extension-owned global config file for cross-session defaults,
413
+ - in-memory state only as a cache rebuilt from persisted state on `session_start`.
414
+
415
+ ### New Install
416
+
417
+ Use Cursor default variants:
418
+
419
+ ```text
420
+ gpt-5.5 -> cursor/gpt-5.5@1m, thinking medium, fast=false
421
+ composer-2 -> cursor/composer-2, fast=true
422
+ ```
423
+
424
+ ### Resume Session
425
+
426
+ Restore:
427
+
428
+ - pi model, including context variant,
429
+ - pi thinking level,
430
+ - session Cursor-only state such as `fast`.
431
+
432
+ ### New Session
433
+
434
+ Use:
435
+
436
+ 1. pi's selected/default model and thinking level,
437
+ 2. global saved Cursor-only defaults for the selected base model,
438
+ 3. else Cursor default variant params.
439
+
440
+ ## CLI / Print Mode
441
+
442
+ Guaranteed first-pass support:
443
+
444
+ ```bash
445
+ pi --model cursor/gpt-5.5@1m --thinking medium
446
+ pi --model cursor/gpt-5.5@1m:medium
447
+ pi --model cursor/gpt-5.5@272k:xhigh
448
+ ```
449
+
450
+ These use pi's native thinking parser. `--thinking` wins over a `:<thinking>` suffix when both are present.
451
+
452
+ Not first-pass support:
453
+
454
+ ```bash
455
+ pi --model cursor/gpt-5.5:medium:272k:fast
456
+ ```
457
+
458
+ Reason:
459
+
460
+ - pi supports one final `:<thinking>` suffix.
461
+ - Cursor-only parameters are not generic pi CLI parameters.
462
+ - Context is already represented by the registered pi model ID.
463
+ - `fast` is controlled by saved extension defaults or the first-pass `--cursor-fast` extension flag.
464
+
465
+ For print mode:
466
+
467
+ - no keybindings,
468
+ - use selected context model variant,
469
+ - use `--thinking` or `:medium` for reasoning/effort,
470
+ - use saved global `fast` defaults unless `--cursor-fast` is present.
471
+
472
+ Fast flag example:
473
+
474
+ ```bash
475
+ pi --model cursor/gpt-5.5@1m --cursor-fast -p "Say ok only"
476
+ ```
477
+
478
+ ## Current Discovered Model Capability Examples
479
+
480
+ Current live Cursor data says:
481
+
482
+ | Model | Cursor controls | Pi representation |
483
+ |---|---|---|
484
+ | `default` | none | plain model |
485
+ | `composer-2` | fast | plain model + fast extension state |
486
+ | `composer-1.5` | none | plain model |
487
+ | `gpt-5.5` | context, reasoning, fast | context variants + native thinking + fast state |
488
+ | `gpt-5.4` | context, reasoning, fast | context variants + native thinking + fast state |
489
+ | `gpt-5.4-mini` | reasoning | plain model + native thinking |
490
+ | `gpt-5.4-nano` | reasoning | plain model + native thinking |
491
+ | `gpt-5.3-codex` | reasoning, fast | plain model + native thinking + fast state |
492
+ | `gpt-5.3-codex-spark` | reasoning | plain model + native thinking |
493
+ | `gpt-5.2` | reasoning, fast | plain model + native thinking + fast state |
494
+ | `gpt-5.2-codex` | reasoning, fast | plain model + native thinking + fast state |
495
+ | `gpt-5.1-codex-max` | reasoning, fast | plain model + native thinking + fast state |
496
+ | `gpt-5.1-codex-mini` | reasoning | plain model + native thinking |
497
+ | `gpt-5.1` | reasoning | plain model + native thinking |
498
+ | `claude-opus-4-7` | thinking, context, effort | context variants + native thinking |
499
+ | `claude-opus-4-6` | thinking, context, effort, fast | context variants + native thinking + fast state |
500
+ | `claude-opus-4-5` | thinking | plain model + native thinking |
501
+ | `claude-sonnet-4-6` | thinking, context, effort | context variants + native thinking |
502
+ | `claude-sonnet-4-5` | thinking, context | context-qualified model + native thinking |
503
+ | `claude-sonnet-4` | thinking, context | context-qualified model + native thinking |
504
+ | `claude-haiku-4-5` | thinking | plain model + native thinking |
505
+ | `grok-4.3` | context | context variants |
506
+ | `grok-4-20` | thinking | plain model + native thinking |
507
+ | `gemini-3.1-pro` | none | plain model |
508
+ | `gemini-3-flash` | none | plain model |
509
+ | `gemini-2.5-flash` | none | plain model |
510
+ | `gpt-5-mini` | none | plain model |
511
+ | `kimi-k2.5` | none | plain model |
512
+
513
+ If Cursor later adds `fast`, `context`, `reasoning`, or `effort` to a model, the extension picks it up dynamically.
514
+
515
+ ## Detailed Examples
516
+
517
+ ### `composer-2`
518
+
519
+ Initial Cursor default:
520
+
521
+ ```text
522
+ pi model: cursor/composer-2
523
+ Cursor params: fast=true
524
+ pi thinking: off
525
+ Cursor status: cursor fast
526
+ ```
527
+
528
+ Toggle fast:
529
+
530
+ ```text
531
+ Cursor params: fast=false
532
+ Cursor status: cleared
533
+ ```
534
+
535
+ `shift+tab`: no-op because the model is not reasoning-capable.
536
+
537
+ ### `gpt-5.5`
538
+
539
+ Initial Cursor default:
540
+
541
+ ```text
542
+ pi model: cursor/gpt-5.5@1m
543
+ Cursor params: context=1m; reasoning=medium; fast=false
544
+ pi thinking: medium
545
+ Cursor status: cleared
546
+ ```
547
+
548
+ After selecting the 272k variant:
549
+
550
+ ```text
551
+ pi model: cursor/gpt-5.5@272k
552
+ Cursor params: context=272k; reasoning=medium; fast=false
553
+ pi contextWindow: 272000
554
+ ```
555
+
556
+ After fast toggle:
557
+
558
+ ```text
559
+ Cursor params: context=272k; reasoning=medium; fast=true
560
+ Cursor status: cursor fast
561
+ ```
562
+
563
+ After `shift+tab` to xhigh:
564
+
565
+ ```text
566
+ pi thinking: xhigh
567
+ Cursor params: context=272k; reasoning=extra-high; fast=true
568
+ ```
569
+
570
+ ### `gpt-5.3-codex`
571
+
572
+ Initial Cursor default:
573
+
574
+ ```text
575
+ pi model: cursor/gpt-5.3-codex
576
+ Cursor params: reasoning=high; fast=true
577
+ pi thinking: high
578
+ Cursor status: cursor fast
579
+ ```
580
+
581
+ After `shift+tab` to low:
582
+
583
+ ```text
584
+ pi thinking: low
585
+ Cursor params: reasoning=low; fast=true
586
+ ```
587
+
588
+ No context variant.
589
+
590
+ ### `claude-opus-4-7`
591
+
592
+ Initial Cursor default:
593
+
594
+ ```text
595
+ pi model: cursor/claude-opus-4-7@1m
596
+ Cursor params: thinking=true; context=1m; effort=xhigh
597
+ pi thinking: xhigh
598
+ ```
599
+
600
+ After selecting the 300k variant:
601
+
602
+ ```text
603
+ pi model: cursor/claude-opus-4-7@300k
604
+ Cursor params: thinking=true; context=300k; effort=xhigh
605
+ pi contextWindow: 300000
606
+ ```
607
+
608
+ After `shift+tab` to high:
609
+
610
+ ```text
611
+ pi thinking: high
612
+ Cursor params: thinking=true; context=300k; effort=high
613
+ ```
614
+
615
+ After `shift+tab` to off:
616
+
617
+ ```text
618
+ pi thinking: off
619
+ Cursor params: thinking=false; context=300k
620
+ ```
621
+
622
+ ### `grok-4.3`
623
+
624
+ Supports context only.
625
+
626
+ ```text
627
+ cursor/grok-4.3@1m
628
+ cursor/grok-4.3@200k
629
+ ```
630
+
631
+ Fast toggle: no-op.
632
+
633
+ `shift+tab`: no-op because the model is not reasoning-capable.
634
+
635
+ ## Validation Plan
636
+
637
+ Before calling done:
638
+
639
+ 1. Unit tests:
640
+ - context-variant model IDs
641
+ - dynamic capability discovery
642
+ - context variant registration and decoding
643
+ - fast extension state and status behavior
644
+ - `reasoning` mapping
645
+ - `effort` mapping
646
+ - boolean `thinking` maps to pi `off` / enabled levels
647
+ - pi `xhigh` preference order: `xhigh`, then `max`, then `extra-high`
648
+ - session restore for Cursor-only state
649
+ - global default state for Cursor-only state
650
+ - unsupported no-op notifications
651
+
652
+ 2. Runtime checks:
653
+ - `pi --list-models cursor`
654
+ - confirm context variants show expected `context` column
655
+ - launch interactive with Cursor
656
+ - verify default pi footer remains unchanged
657
+ - verify Cursor `fast` status appears only when enabled
658
+ - verify non-cursor footer/status unchanged
659
+ - verify `shift+tab` uses pi native thinking
660
+ - verify context changes through native model selection
661
+ - verify resume restores model, thinking, and Cursor-only state
662
+
663
+ 3. Print mode:
664
+ - `pi --model cursor/gpt-5.5@1m:medium -p "Say ok only"`
665
+ - `pi --model cursor/gpt-5.5@272k --thinking xhigh -p "Say ok only"`
666
+ - `pi --model cursor/gpt-5.5@1m --cursor-fast -p "Say ok only"`
667
+ - confirm requests use selected context, pi thinking, and fast flag state