pi-cursor-sdk 0.1.27 → 0.1.28

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,16 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.28 - 2026-05-29
6
+
7
+ ### Changed
8
+
9
+ - Update the local pi validation baseline to `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` `0.78.0` after reviewing the 0.78.0 changelog; peer dependency ranges remain minimum-only at `>=0.76.0` (#108).
10
+
11
+ ### Fixed
12
+
13
+ - Prevent Cursor SDK ConnectRPC network resets such as `ConnectError: [aborted] read ECONNRESET` from escaping as process-level uncaught exceptions during active Cursor turns; pi now surfaces the existing scrubbed retry guidance and remains available for the next turn (#107).
14
+
5
15
  ## 0.1.27 - 2026-05-29
6
16
 
7
17
  ### Changed
package/README.md CHANGED
@@ -34,7 +34,7 @@ If pi started without a key, run `/cursor-refresh-models` after `/login` to refr
34
34
  - pi 0.76.0 or newer
35
35
  - a Cursor SDK API key saved through `/login`, available as `CURSOR_API_KEY`, or passed with pi's `--api-key`
36
36
 
37
- No global `@cursor/sdk` install is required. This package depends on exact `@cursor/sdk@1.0.16`, so normal package installation brings in the SDK version this extension was built and tested against. This package declares a pi **minimum** of 0.76.0 with no maximum peer version, so users who update pi before this extension is republished are not blocked from trying the existing extension. The current validation baseline is pi 0.77.0 plus Cursor SDK 1.0.16; older pi or Cursor SDK compatibility paths are not maintained.
37
+ No global `@cursor/sdk` install is required. This package depends on exact `@cursor/sdk@1.0.16`, so normal package installation brings in the SDK version this extension was built and tested against. This package declares a pi **minimum** of 0.76.0 with no maximum peer version, so users who update pi before this extension is republished are not blocked from trying the existing extension. The current validation baseline is pi 0.78.0 plus Cursor SDK 1.0.16; older pi or Cursor SDK compatibility paths are not maintained.
38
38
 
39
39
  ## Install
40
40
 
@@ -293,7 +293,7 @@ On bootstrap sends, a compact **callable tool surfaces** block is injected into
293
293
 
294
294
  ### Maintainer live smoke release gate
295
295
 
296
- For Cursor provider/runtime changes, follow the manual [Cursor live smoke checklist](docs/cursor-live-smoke-checklist.md) before release. For a faster minimal-surface pass first, see [Cursor dogfood checklist](docs/cursor-dogfood-checklist.md). See [Cursor testing lessons](docs/cursor-testing-lessons.md) for auth.json seeding, isolated `/tmp` harness layout, JSONL replay-error scans, and other regression traps. Assume every runtime surface is in scope. The checklist uses real `pi -e . --cursor-no-fast --model cursor/composer-2.5` runs with temporary session dirs, pi 0.77.0 `--session-id`, sealed smoke-runner PATH/env wrappers, Cursor SDK `plan` mode, and mandatory visual TUI card/color inspection. The canonical visual path is `npm run smoke:visual`: offscreen PTY capture rendered through a browser/xterm view and saved as PNG screenshots with Playwright, or with `agent_browser` from the generated HTML when available. Its default matrix is native replay only: native replay registration is forced on, Cursor setting sources are disabled, the pi bridge is off, overlapping built-in pi tools are not exposed, and inherited Cursor SDK event-debug artifact env is cleared; `--event-debug` writes to a deterministic debug directory under the visual output directory. The visible TUI/output, rendered screenshots, scrubbed diagnostics, and persisted JSONL must agree. Do not mark a release ready with optional, deferred, mostly-passing, or unobserved smoke checks outstanding.
296
+ For Cursor provider/runtime changes, follow the manual [Cursor live smoke checklist](docs/cursor-live-smoke-checklist.md) before release. For a faster minimal-surface pass first, see [Cursor dogfood checklist](docs/cursor-dogfood-checklist.md). See [Cursor testing lessons](docs/cursor-testing-lessons.md) for auth.json seeding, isolated `/tmp` harness layout, JSONL replay-error scans, and other regression traps. Assume every runtime surface is in scope. The checklist uses real `pi -e . --cursor-no-fast --model cursor/composer-2.5` runs with temporary session dirs, pi 0.78.0 `--session-id`, sealed smoke-runner PATH/env wrappers, Cursor SDK `plan` mode, and mandatory visual TUI card/color inspection. The canonical visual path is `npm run smoke:visual`: offscreen PTY capture rendered through a browser/xterm view and saved as PNG screenshots with Playwright, or with `agent_browser` from the generated HTML when available. Its default matrix is native replay only: native replay registration is forced on, Cursor setting sources are disabled, the pi bridge is off, overlapping built-in pi tools are not exposed, and inherited Cursor SDK event-debug artifact env is cleared; `--event-debug` writes to a deterministic debug directory under the visual output directory. The visible TUI/output, rendered screenshots, scrubbed diagnostics, and persisted JSONL must agree. Do not mark a release ready with optional, deferred, mostly-passing, or unobserved smoke checks outstanding.
297
297
 
298
298
  ### Maintainer Cursor SDK event capture
299
299
 
@@ -333,7 +333,7 @@ When a Cursor run fails after auth is configured, pi now surfaces scrubbed provi
333
333
 
334
334
  Aborted runs now include a likely cause when determinable, for example `Cancelled: prompt interrupted.` for user cancel or `Cancelled: Cursor SDK run was cancelled.` for SDK-side cancellation.
335
335
 
336
- Network timeouts from the Cursor SDK connect layer (for example `ConnectError: read ETIMEDOUT`) surface as a scrubbed retry hint instead of crashing pi. Check your connection and retry; persistent timeouts may indicate a transient Cursor service or network issue.
336
+ Network failures from the Cursor SDK connect layer (for example `ConnectError: read ETIMEDOUT` or `ConnectError: [aborted] read ECONNRESET`) surface as a scrubbed retry hint instead of crashing pi. Check your connection and retry; persistent failures may indicate a transient Cursor service or network issue.
337
337
 
338
338
  You can also restart pi with a key in the same shell or launcher that starts pi:
339
339
 
@@ -440,7 +440,7 @@ This usually needs session JSONL to classify. Common cases:
440
440
  - **Stale replay routing / plan-strip:** Error `toolResult` or error assistant messages contain `Tool grep/cursor/find/ls not found`, or provider debug shows `inactive_trace` after plan-mode execute stripped active tools — tracked in **#52** (distinct from model text echo and #55).
441
441
  - **Replay vs execution:** `cursor-replay-*` IDs and neutral **Cursor MCP** activity cards are display-only recorded Cursor results; they do not re-run browser/MCP work. See [Cursor native tool replay](docs/cursor-native-tool-replay.md).
442
442
  - **Run failure / discarded tools:** A red toast with scrubbed detail may indicate an SDK failure (#55). Started-but-never-completed Cursor tools surface neutral **Cursor … did not complete** activity cards with a bounded reason when the run failed/aborted, produced no assistant text, or involved external/side-effectful tools. Incomplete fast local discovery starts (`read`, `grep`, `glob`, `ls`) are debug-only after a successful text-producing run so stale SDK start events do not create red post-answer cards; maintainer debug for the same gap remains in **#52** (`PI_CURSOR_SDK_EVENT_DEBUG=1`).
443
- - **Hard network crash:** pi exited with uncaught `ConnectError` / `ETIMEDOUT`**#43**, not #40 text echo.
443
+ - **Hard network crash:** pi exited with an uncaught Cursor SDK `ConnectError` instead of showing a scrubbed retry/auth errorcapture the stack/session tail as a process-guard regression, not #40 text echo.
444
444
 
445
445
  Capture `pi --version`, extension version, model, flags, the exact prompt, and a redacted session dir before filing bugs.
446
446
 
@@ -65,8 +65,8 @@ The replay scan flags only error `toolResult` / error assistant messages with `T
65
65
 
66
66
  Pass criteria:
67
67
 
68
- - `pi --version` reports pi 0.77.0 for this cutover baseline.
69
- - `npm ls` shows `@cursor/sdk@1.0.16` and local `@earendil-works/*@0.77.0` packages.
68
+ - `pi --version` reports pi 0.78.0 for this cutover baseline.
69
+ - `npm ls` shows `@cursor/sdk@1.0.16` and local `@earendil-works/*@0.78.0` packages.
70
70
  - `cursor/composer-2.5` appears in the model list.
71
71
  - No Cursor key or auth token is printed.
72
72
  - If neither `~/.pi/agent/auth.json` cursor auth nor `CURSOR_API_KEY` is available, stop and report the live smoke as blocked.
@@ -123,7 +123,7 @@ Observe with `tmux capture-pane -pt "$SESSION"` or attach manually.
123
123
  Pass criteria:
124
124
 
125
125
  - Footer shows `(cursor) composer-2.5`. With `--cursor-no-fast`, Cursor fast mode is off and the Cursor extension status should not show `cursor fast`; ignore unrelated status text from other extensions.
126
- - The run uses pi 0.77.0 `--session-id` successfully.
126
+ - The run uses pi 0.78.0 `--session-id` successfully.
127
127
  - Assistant answer appears correctly.
128
128
  - `/session` shows one user and one assistant message for the simple run.
129
129
  - Persisted JSONL has one assistant message. If the screen appears duplicated, inspect JSONL before deciding whether it is a rendering bug.
@@ -131,7 +131,7 @@ Pass criteria:
131
131
 
132
132
  ## 4. Mandatory visual card/color rendering check
133
133
 
134
- This is the canonical visual release path for Cursor provider/runtime changes. It requires offscreen TUI visual inspection, not only JSONL or code review. Use pi 0.77.0, `@cursor/sdk@1.0.16`, a fresh temporary session dir, Cursor SDK `plan` mode, native replay enabled, and the checked-in visual runner. The runner resolves `pi` by directly walking the parent `PATH`, uses `process.execPath` for Node, and prepends that Node directory for both prereq checks and tmux launches so `#!/usr/bin/env node` shims use the validated Node. The default matrix is native replay only: native replay registration is forced on, settings sources are `none`, the pi bridge is off, overlapping built-in pi tools are not exposed, and inherited Cursor SDK event-debug artifact env is cleared. With `--event-debug`, debug capture writes to a deterministic directory under `VISUAL_DIR`.
134
+ This is the canonical visual release path for Cursor provider/runtime changes. It requires offscreen TUI visual inspection, not only JSONL or code review. Use pi 0.78.0, `@cursor/sdk@1.0.16`, a fresh temporary session dir, Cursor SDK `plan` mode, native replay enabled, and the checked-in visual runner. The runner resolves `pi` by directly walking the parent `PATH`, uses `process.execPath` for Node, and prepends that Node directory for both prereq checks and tmux launches so `#!/usr/bin/env node` shims use the validated Node. The default matrix is native replay only: native replay registration is forced on, settings sources are `none`, the pi bridge is off, overlapping built-in pi tools are not exposed, and inherited Cursor SDK event-debug artifact env is cleared. With `--event-debug`, debug capture writes to a deterministic directory under `VISUAL_DIR`.
135
135
 
136
136
  ```bash
137
137
  VISUAL_DIR="$(mktemp -d /tmp/pi-cursor-sdk-1016-visual.XXXXXX)"
@@ -15,7 +15,7 @@ Current implementation notes:
15
15
  - Cursor status uses one coordinated `ctx.ui.setStatus("cursor", ...)` value for fast and non-default plan mode; the default pi footer remains intact.
16
16
  - Installed `@cursor/sdk` user messages accept images, and Cursor models are treated as image-capable; registered input metadata is `text` plus `image`.
17
17
  - Image payload forwarding sends images only from the latest user message. If the latest user turn is plain text after an earlier image turn, the transcript keeps an `[image omitted from transcript]` placeholder but no image bytes are sent to Cursor. The prompt explicitly tells Cursor that prior image bytes are unavailable and to ask the user to reattach or describe a prior image when needed. Carrying images forward across turns remains a future product decision because it affects token cost, privacy, stale visual context, and expected multimodal follow-up behavior.
18
- - Exact `@cursor/sdk@1.0.16` is a package dependency of this extension; users should not need a global SDK install. pi 0.77.0 is the current validation baseline, while published pi peer dependencies are minimum-only `>=0.76.0` ranges with no upper bound. Newer pi versions are allowed to attempt loading this extension before a matching extension release exists; compatibility is best-effort until validated.
18
+ - Exact `@cursor/sdk@1.0.16` is a package dependency of this extension; users should not need a global SDK install. pi 0.78.0 is the current validation baseline, while published pi peer dependencies are minimum-only `>=0.76.0` ranges with no upper bound. Newer pi versions are allowed to attempt loading this extension before a matching extension release exists; compatibility is best-effort until validated.
19
19
  - Cursor auth uses pi-native API-key resolution for provider `cursor`: CLI `--api-key`, stored `~/.pi/agent/auth.json` API key from `/login`, then `CURSOR_API_KEY`. The extension config file stores only non-secret Cursor-only state such as fast defaults.
20
20
  - Local agents pass `settingSources: ["all"]` by default so Cursor MCP servers, plugin tools, project/user settings, and related Cursor-native capabilities are available. Users can narrow loading with a comma-separated list such as `PI_CURSOR_SETTING_SOURCES=project,user,plugins`, or disable ambient setting sources with `PI_CURSOR_SETTING_SOURCES=none`. The provider suppresses direct Cursor SDK bootstrap stdout/stderr/console noise (including late first-send workspace loading such as hook compatibility warnings) so it does not pollute pi's TUI.
21
21
  - On `cursor/*` models, pi-cursor-sdk removes only pi-generated `<project_instructions>` blocks that overlap the effective Cursor `settingSources`: `user` for `~/.pi/agent/AGENTS.md`; `project` for discovered repo/parent `AGENTS.md` and `CLAUDE.md` (verified Cursor behavior: local agents load project `AGENTS.md` and `CLAUDE.md`). `~/.pi/agent/CLAUDE.md` is not removed (Cursor user layer uses `~/.claude/CLAUDE.md`). Blocks are removed by exact pi serialization match from structured `contextFiles` via the `before_agent_start` hook, not in `buildCursorPrompt` sanitization. Suppression is skipped with `-nc`, `PI_CURSOR_SETTING_SOURCES=none`, narrowed sources such as `plugins` that omit the matching layer, or `PI_CURSOR_PRESERVE_PI_AGENTS_MD=1`. Switching away from a Cursor model restores pi's full context block on the next user message.
@@ -4,16 +4,16 @@ This workflow is the canonical repo path for verifying Cursor SDK tool replay th
4
4
 
5
5
  Use it before accepting replay-card commits or PRs, and for every Cursor provider/runtime release where TUI card/color behavior could regress. Text logs and JSONL are necessary, but they are not enough when the claim is visual parity: always keep PNGs for the exact prompt, and keep before/after PNGs when reviewing a rendering change.
6
6
 
7
- Current validation baseline: pi 0.77.0, exact `@cursor/sdk@1.0.16`, local validation packages `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` at 0.77.0. Published peer dependencies remain minimum-only at pi 0.76.0+ with no upper bound, so newer pi installs can try the extension before a matching validation release exists.
7
+ Current validation baseline: pi 0.78.0, exact `@cursor/sdk@1.0.16`, local validation packages `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` at 0.78.0. Published peer dependencies remain minimum-only at pi 0.76.0+ with no upper bound, so newer pi installs can try the extension before a matching validation release exists.
8
8
 
9
- ## Cursor SDK 1.0.16 / pi 0.77.0 cutover visual record
9
+ ## Cursor SDK 1.0.16 / pi 0.78.0 cutover visual record
10
10
 
11
11
  Record the required cutover validation here or in the final release handoff. The default matrix is native replay only: the runner forces native replay registration on, forces Cursor setting sources off, disables the pi bridge, disables overlapping built-in pi tool exposure, and clears inherited Cursor SDK event-debug artifact env. With `--event-debug`, debug capture writes to a deterministic directory under the visual output directory. Do not commit raw ANSI logs, screenshots, terminal recordings, debug artifacts, or `.debug/visual-smoke` scratch files.
12
12
 
13
13
  | Field | Required value / evidence |
14
14
  | --- | --- |
15
15
  | Command/session used | `npm run smoke:visual -- --ext "$PWD" --cwd "$PWD" --mode plan --out-dir <fresh /tmp dir> --label <matrix label> --prompt <matrix prompt>` with default native-replay isolation |
16
- | Baseline versions | `pi --version` = 0.77.0; `npm ls` = `@cursor/sdk@1.0.16` and local `@earendil-works/*@0.77.0` |
16
+ | Baseline versions | `pi --version` = 0.78.0; `npm ls` = `@cursor/sdk@1.0.16` and local `@earendil-works/*@0.78.0` |
17
17
  | Card categories checked | Claim only categories proven by both PNG and JSONL. Required cutover categories are read, grep/search, find/glob, list, shell success, write, edit/diff, and true read failure. Neutral Cursor plan/todo/task/mode activity is optional/opportunistic and only counts when JSONL contains a completed Cursor workflow event. |
18
18
  | Observed status/card colors | Confirm native-looking cards use native pi styling; neutral Cursor activity is not red; true errors are distinct; diff previews show red/green; plan status is readable |
19
19
  | Screenshot/ANSI evidence location | External path only, for example `/tmp/pi-cursor-sdk-1016-visual.*/read-package.{ansi,txt,html,png,jsonl.path}` |
@@ -238,7 +238,7 @@ The script writes timestamped artifacts under `--out` (default `/tmp/pi-cursor-s
238
238
 
239
239
  Stdout prints artifact paths and summary counts only. Raw payloads stay on disk and may contain local paths, project text, tool args/results, or secrets — do not commit or share them.
240
240
 
241
- Hard repo rule: Cursor SDK behavior claims must come from the installed `@cursor/sdk` package and/or https://cursor.com/docs/sdk/typescript, not from memory or ad-hoc probes alone. Current cutover validation targets exact `@cursor/sdk@1.0.16` and pi 0.77.0 local packages.
241
+ Hard repo rule: Cursor SDK behavior claims must come from the installed `@cursor/sdk` package and/or https://cursor.com/docs/sdk/typescript, not from memory or ad-hoc probes alone. Current cutover validation targets exact `@cursor/sdk@1.0.16` and pi 0.78.0 local packages.
242
242
 
243
243
  ## Pi provider SDK event capture
244
244
 
@@ -394,7 +394,7 @@ npm run debug:sdk-events -- \
394
394
 
395
395
  Start with whether pi stayed alive:
396
396
 
397
- 0. **pi process exited / shell returned with uncaught `ConnectError` (`ETIMEDOUT`, code 14, `read ETIMEDOUT`)** — hard network crash bypassing provider error surfacing. Route to **#43** (coordinate with #55 for caught-failure messaging). If tools were mid-flight, note whether session JSONL ends abruptly; do not classify as #40 model text echo.
397
+ 0. **pi process exited / shell returned with uncaught `ConnectError` (for example `ETIMEDOUT`, `ECONNRESET`, `read ETIMEDOUT`, or `[aborted] read ECONNRESET`)** — hard network crash bypassing provider error surfacing. Current code guards observed Cursor SDK/network-reset shapes during active Cursor turns and should show scrubbed retry guidance instead; treat a fresh process exit as a process-guard regression, capture the stack/session tail, and route to **#43/#107** rather than #40 model text echo. If tools were mid-flight, note whether session JSONL ends abruptly.
398
398
 
399
399
  Then inspect the failing assistant turn in `$SMOKE_DIR/session/*.jsonl`:
400
400
 
@@ -414,7 +414,7 @@ rg '"type": "toolCall"|Tool call \(Cursor|cursor-replay-' "$SMOKE_DIR/session"/*
414
414
 
415
415
  ### When to file follow-ups
416
416
 
417
- - **#43** — pi exited from uncaught `ConnectError` / `ETIMEDOUT` during Cursor SDK HTTP traffic (hard crash, not a scrubbed #55 toast).
417
+ - **#43/#107** — pi exited from uncaught Cursor SDK `ConnectError` / network reset during HTTP traffic (hard crash, not a scrubbed #55 toast). Observed `ETIMEDOUT` and `ECONNRESET` shapes should be guarded during active Cursor turns; new exits need stack/session evidence.
418
418
  - **#55** — caught SDK run failure or abort with missing/opaque detail (already addressed on main for surfacing).
419
419
  - **#52** — stale/inactive native replay routing after plan-strip or stale `context.tools` snapshot (`Tool * not found` in JSONL, `inactive_trace` in `display-decisions.jsonl`); or maintainer needs an explicit "started X, never completed" debug line when JSONL shows no completion and no model text echo.
420
420
  - **New issue** — bridge dispatch failure with `[pi-cursor-sdk:bridge]` evidence, or proven provider bug with JSONL showing missing `toolCall` despite SDK `tool-call-completed` in `on-delta.jsonl` from `debug:provider-events` or `debug:sdk-events` artifacts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cursor-sdk",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
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",
@@ -92,9 +92,9 @@
92
92
  "typebox": "*"
93
93
  },
94
94
  "devDependencies": {
95
- "@earendil-works/pi-ai": "0.77.0",
96
- "@earendil-works/pi-coding-agent": "0.77.0",
97
- "@earendil-works/pi-tui": "0.77.0",
95
+ "@earendil-works/pi-ai": "0.78.0",
96
+ "@earendil-works/pi-coding-agent": "0.78.0",
97
+ "@earendil-works/pi-tui": "0.78.0",
98
98
  "@xterm/xterm": "^6.0.0",
99
99
  "playwright": "^1.60.0",
100
100
  "typebox": "^1.1.38",
@@ -47,9 +47,14 @@ function isUnauthenticatedConnectCode(code: unknown): boolean {
47
47
  return code === 16 || (typeof code === "string" && /^(?:16|unauthenticated)$/i.test(code));
48
48
  }
49
49
 
50
+ function isCursorExtensionConnectStack(stack: string): boolean {
51
+ return stack.includes("@connectrpc/connect-node") && /(?:^|[\\/])pi-cursor-sdk(?:[\\/]|$)/.test(stack);
52
+ }
53
+
50
54
  function getCursorConnectSource(error: unknown, record: Record<string, unknown> | undefined): CursorConnectErrorSource {
51
55
  const stack = getErrorStack(error, record);
52
56
  if (stack.includes("@cursor/sdk")) return "cursor-sdk-stack";
57
+ if (isCursorExtensionConnectStack(stack)) return "cursor-extension-connect-stack";
53
58
  const details = Array.isArray(record?.details) ? record.details : [];
54
59
  const hasCursorBackendDetails = details.some((detail) => {
55
60
  const type = getErrorStringField(asRecord(detail), "type");
@@ -58,11 +63,16 @@ function getCursorConnectSource(error: unknown, record: Record<string, unknown>
58
63
  return hasCursorBackendDetails ? "cursor-backend-details" : "generic-connect";
59
64
  }
60
65
 
61
- export type CursorConnectErrorSource = "cursor-sdk-stack" | "cursor-backend-details" | "generic-connect";
66
+ export type CursorConnectErrorSource =
67
+ | "cursor-sdk-stack"
68
+ | "cursor-extension-connect-stack"
69
+ | "cursor-backend-details"
70
+ | "generic-connect";
62
71
 
63
72
  export type CursorConnectErrorClassification =
64
73
  | { kind: "abort"; source: "cursor-sdk-stack" }
65
- | { kind: "unauthenticated"; source: CursorConnectErrorSource };
74
+ | { kind: "unauthenticated"; source: CursorConnectErrorSource }
75
+ | { kind: "network"; source: CursorConnectErrorSource };
66
76
 
67
77
  export function classifyCursorConnectError(error: unknown): CursorConnectErrorClassification | undefined {
68
78
  const record = asRecord(error);
@@ -89,6 +99,12 @@ export function classifyCursorConnectError(error: unknown): CursorConnectErrorCl
89
99
  return { kind: "unauthenticated", source: getCursorConnectSource(error, record) };
90
100
  }
91
101
 
102
+ const causeCode = getErrorStringField(cause, "code");
103
+ const causeSyscall = getErrorStringField(cause, "syscall");
104
+ if (isLikelyNetworkTimeout(`${message}\n${rawMessage}\n${causeCode ?? ""}\n${causeSyscall ?? ""}`)) {
105
+ return { kind: "network", source: getCursorConnectSource(error, record) };
106
+ }
107
+
92
108
  return undefined;
93
109
  }
94
110
 
@@ -26,7 +26,7 @@ function hasActiveAbortSuppression(): boolean {
26
26
  }
27
27
 
28
28
  function isCursorProvenance(source: string): boolean {
29
- return source === "cursor-sdk-stack" || source === "cursor-backend-details";
29
+ return source === "cursor-sdk-stack" || source === "cursor-extension-connect-stack" || source === "cursor-backend-details";
30
30
  }
31
31
 
32
32
  function shouldSuppressProcessError(event: string | symbol, args: readonly unknown[]): boolean {