pi-cursor-sdk 0.1.49 → 0.1.50

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.50 - 2026-06-24
6
+
7
+ ### Added
8
+
9
+ - Normalize Cursor context-overflow failures into pi's `context_length_exceeded` form via a `message_end` handler so pi's auto-compact-retry recovery fires when a Cursor run fails on a too-large prompt. Scoped to the Cursor provider, limited to `stopReason === "error"`, never matches throttling/rate-limit signals, and idempotent. See pi `docs/custom-provider.md` "Context Overflow Errors".
10
+
11
+ ### Changed
12
+
13
+ - Bump the local pi validation baseline (`@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`) from `0.80.1` to `0.80.2` after reviewing current installed Pi 0.80.2 docs and types. Published pi core peer dependencies remain `"*"`.
14
+ - Reconcile the README recommended/validated pi baseline wording (previously `0.80.1` recommended vs `0.79.10` validated) to the current `0.80.2` baseline.
15
+
16
+ ### Validation
17
+
18
+ - `npm run typecheck`, `npm test` (790 tests), and `npm pack --dry-run` pass against the `0.80.2` pi core local validation baseline.
19
+ - `npm run smoke:platform:all` passes on macOS, Ubuntu, and Windows native (native-replay visual matrix, bridge visual matrix, abort cleanup; packed installs, PTY/ConPTY capture, JSONL assertions, redaction scans) with Cursor auth.
20
+
5
21
  ## 0.1.49 - 2026-06-23
6
22
 
7
23
  ### Changed
@@ -11,7 +27,7 @@
11
27
 
12
28
  ### Validation
13
29
 
14
- - Pending in this release train.
30
+ - `npm test`, `npm run typecheck`, and `npm pack --dry-run` pass against the `0.80.1` pi core local validation baseline.
15
31
 
16
32
  ## 0.1.48 - 2026-06-22
17
33
 
package/README.md CHANGED
@@ -51,10 +51,10 @@ If pi started without a key, run `/cursor-refresh-models` after `/login` to refr
51
51
  ## Requirements
52
52
 
53
53
  - Node.js 22.19+
54
- - pi 0.80.1 or newer recommended; pi core peer metadata is intentionally unpinned so newer pi releases are not blocked
54
+ - pi 0.80.2 or newer recommended; pi core peer metadata is intentionally unpinned so newer pi releases are not blocked
55
55
  - a Cursor SDK API key saved through `/login`, available as `CURSOR_API_KEY`, or passed with pi's `--api-key`
56
56
 
57
- No global `@cursor/sdk` install is required. This package depends on exact `@cursor/sdk@1.0.19`, so normal package installation brings in the SDK version this extension was built and tested against. It also declares and bundles `@connectrpc/connect-node@1.7.0` with `undici@7.28.0` because Cursor SDK 1.0.19 still dynamically loads the Node ConnectRPC transport during local-agent runs, and npm dependency consumers do not inherit this package's root-only `overrides`. Cursor SDK 1.0.19 removes the older `sqlite3 -> node-gyp@8` dependency chain, so deprecated install warnings for `inflight`, `rimraf`, `glob@7`, `npmlog`, `gauge`, `are-we-there-yet`, and `tar@6` from that chain are no longer expected. This package follows pi package guidance by declaring pi core package peers with `"*"` ranges, so users who update pi before this extension is republished are not blocked by peer metadata. The current recommended and validated pi baseline is 0.79.10 plus Cursor SDK 1.0.19; older pi compatibility paths are best-effort and older Cursor SDK compatibility paths are not maintained.
57
+ No global `@cursor/sdk` install is required. This package depends on exact `@cursor/sdk@1.0.19`, so normal package installation brings in the SDK version this extension was built and tested against. It also declares and bundles `@connectrpc/connect-node@1.7.0` with `undici@7.28.0` because Cursor SDK 1.0.19 still dynamically loads the Node ConnectRPC transport during local-agent runs, and npm dependency consumers do not inherit this package's root-only `overrides`. Cursor SDK 1.0.19 removes the older `sqlite3 -> node-gyp@8` dependency chain, so deprecated install warnings for `inflight`, `rimraf`, `glob@7`, `npmlog`, `gauge`, `are-we-there-yet`, and `tar@6` from that chain are no longer expected. This package follows pi package guidance by declaring pi core package peers with `"*"` ranges, so users who update pi before this extension is republished are not blocked by peer metadata. The current recommended and validated pi baseline is 0.80.2 plus Cursor SDK 1.0.19; older pi compatibility paths are best-effort and older Cursor SDK compatibility paths are not maintained.
58
58
 
59
59
  ## Install
60
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cursor-sdk",
3
- "version": "0.1.49",
3
+ "version": "0.1.50",
4
4
  "description": "pi provider extension backed by @cursor/sdk local agents",
5
5
  "author": "Mitch Fultz (https://github.com/fitchmultz)",
6
6
  "license": "MIT",
@@ -125,9 +125,9 @@
125
125
  "typebox": "*"
126
126
  },
127
127
  "devDependencies": {
128
- "@earendil-works/pi-ai": "0.80.1",
129
- "@earendil-works/pi-coding-agent": "0.80.1",
130
- "@earendil-works/pi-tui": "0.80.1",
128
+ "@earendil-works/pi-ai": "0.80.2",
129
+ "@earendil-works/pi-coding-agent": "0.80.2",
130
+ "@earendil-works/pi-tui": "0.80.2",
131
131
  "@xterm/xterm": "^6.0.0",
132
132
  "node-pty": "^1.1.0",
133
133
  "playwright": "^1.60.0",
@@ -0,0 +1,96 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import type { AssistantMessage } from "@earendil-works/pi-ai/compat";
3
+ import { CURSOR_PROVIDER } from "./cursor-model.js";
4
+
5
+ /**
6
+ * Pi recognizes `context_length_exceeded` (its `OVERFLOW_PATTERNS` includes
7
+ * `/context[_ ]length[_ ]exceeded/i`) as a context-overflow signal and, on
8
+ * detecting it, drops the failed assistant message, compacts, and retries once.
9
+ *
10
+ * The Cursor provider sanitizes SDK failures into auth/network/generic messages
11
+ * that pi does NOT treat as overflow, so a genuine Cursor context-overflow
12
+ * failure would otherwise surface as a plain provider error and bypass pi's
13
+ * auto-compact-retry recovery. This handler rewrites only Cursor context-window
14
+ * failures into the `context_length_exceeded` form pi recognizes.
15
+ *
16
+ * See pi `docs/custom-provider.md` -> "Context Overflow Errors".
17
+ */
18
+ export const CURSOR_OVERFLOW_MARKER = "context_length_exceeded";
19
+
20
+ /**
21
+ * Context-overflow phrases. These overlap pi's own `OVERFLOW_PATTERNS` so the
22
+ * false-positive surface matches pi's built-in detection for other providers.
23
+ *
24
+ * ponytail: intentionally narrow and textual. We do NOT match bare gRPC
25
+ * `resource_exhausted` (code 8) or `too many tokens` because pi documents those
26
+ * can false-match throttling/quota errors and trigger an unwanted compaction.
27
+ * If a live Cursor probe reveals a distinct overflow phrase, add it here; this
28
+ * pattern set is the single place to extend without touching the handler.
29
+ */
30
+ const CURSOR_OVERFLOW_PATTERNS = [
31
+ /context[_ ]?length/i,
32
+ /context[_ ]?window/i,
33
+ /maximum context/i,
34
+ /prompt is too long/i,
35
+ /too large for model/i,
36
+ /exceed(?:s|ed).{0,30}context/i,
37
+ ] as const;
38
+
39
+ /** Never treat rate-limit/throttle signals as overflow (pi retries those separately). */
40
+ const CURSOR_THROTTLE_PATTERN = /too many requests|rate.?limit|throttl|retry.?after/i;
41
+
42
+ /**
43
+ * Map a finalized Cursor assistant `errorMessage` to pi's overflow form.
44
+ * Returns `undefined` when the message is not a Cursor context-overflow failure.
45
+ *
46
+ * Pure and idempotent so it is safe to call repeatedly and unit-test in isolation.
47
+ */
48
+ export function normalizeCursorOverflowErrorMessage(errorMessage: string | undefined): string | undefined {
49
+ const message = errorMessage?.trim();
50
+ if (!message) return undefined;
51
+ // Idempotent: never double-prefix an already-normalized message.
52
+ if (message.includes(CURSOR_OVERFLOW_MARKER)) return undefined;
53
+ if (CURSOR_THROTTLE_PATTERN.test(message)) return undefined;
54
+ if (CURSOR_OVERFLOW_PATTERNS.some((pattern) => pattern.test(message))) {
55
+ return `${CURSOR_OVERFLOW_MARKER}: ${message}`;
56
+ }
57
+ return undefined;
58
+ }
59
+
60
+ /**
61
+ * Rewrite a finalized assistant message into pi's overflow form when it is a
62
+ * Cursor context-overflow failure. Returns the replacement message (for the
63
+ * `message_end` result) or `undefined` to leave it unchanged.
64
+ *
65
+ * `isCursorProvider` carries the provider scoping decision out of the handler so
66
+ * the full logic is unit-testable without a pi event harness.
67
+ */
68
+ export function rewriteCursorOverflowAssistantMessage(
69
+ message: AssistantMessage,
70
+ isCursorProvider: boolean,
71
+ ): AssistantMessage | undefined {
72
+ if (!isCursorProvider || message.stopReason !== "error") return undefined;
73
+ const rewritten = normalizeCursorOverflowErrorMessage(message.errorMessage);
74
+ if (!rewritten) return undefined;
75
+ return { ...message, errorMessage: rewritten };
76
+ }
77
+
78
+ export type CursorOverflowExtensionApi = Pick<ExtensionAPI, "on">;
79
+
80
+ /**
81
+ * Register a `message_end` handler that rewrites Cursor context-overflow
82
+ * failures into the `context_length_exceeded` form pi auto-compacts on.
83
+ *
84
+ * Guarded exactly as pi's provider docs require: scoped to the Cursor provider,
85
+ * only for `stopReason === "error"`, never for throttling, and idempotent.
86
+ */
87
+ export function registerCursorOverflowNormalization(pi: CursorOverflowExtensionApi): void {
88
+ pi.on("message_end", (event, ctx) => {
89
+ const message = event.message;
90
+ if (message.role !== "assistant") return undefined;
91
+ const isCursorProvider = message.provider === CURSOR_PROVIDER || ctx.model?.provider === CURSOR_PROVIDER;
92
+ const rewritten = rewriteCursorOverflowAssistantMessage(message, isCursorProvider);
93
+ if (!rewritten) return undefined;
94
+ return { message: rewritten };
95
+ });
96
+ }
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ import { streamCursorLazy } from "./cursor-provider-lazy.js";
11
11
  import { CURSOR_API_KEY_CONFIG_VALUE } from "./cursor-api-key.js";
12
12
  import { registerCursorFallbackIssueWarning } from "./cursor-fallback-warning.js";
13
13
  import { registerCursorAgentsContextDedup } from "./cursor-agents-context-registration.js";
14
+ import { registerCursorOverflowNormalization } from "./cursor-provider-overflow.js";
14
15
 
15
16
  type CursorExtensionApi =
16
17
  & Pick<ExtensionAPI, "registerProvider" | "registerCommand" | "on">
@@ -22,7 +23,8 @@ type CursorExtensionApi =
22
23
  & Parameters<typeof registerCursorSkillTool>[0]
23
24
  & Parameters<typeof registerCursorPiToolBridge>[0]
24
25
  & Parameters<typeof registerCursorFallbackIssueWarning>[0]
25
- & Parameters<typeof registerCursorAgentsContextDedup>[0];
26
+ & Parameters<typeof registerCursorAgentsContextDedup>[0]
27
+ & Parameters<typeof registerCursorOverflowNormalization>[0];
26
28
 
27
29
  function createCursorProviderConfig(models: ProviderModelConfig[]): ProviderConfig {
28
30
  return {
@@ -53,6 +55,7 @@ export default async function (pi: CursorExtensionApi) {
53
55
  registerCursorSkillTool(pi);
54
56
  registerCursorPiToolBridge(pi);
55
57
  registerCursorAgentsContextDedup(pi);
58
+ registerCursorOverflowNormalization(pi);
56
59
  let fallbackIssue: CursorModelFallbackIssue | undefined;
57
60
  const models = await discoverModels({
58
61
  onFallback: (issue) => {