pi-cursor-sdk 0.1.18 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +37 -0
  3. package/docs/cursor-live-smoke-checklist.md +3 -0
  4. package/docs/cursor-model-ux-spec.md +4 -3
  5. package/docs/cursor-native-tool-replay.md +96 -2
  6. package/docs/cursor-testing-lessons.md +234 -5
  7. package/package.json +8 -2
  8. package/scripts/debug-provider-events.mjs +403 -0
  9. package/scripts/debug-sdk-events.mjs +413 -0
  10. package/scripts/lib/cursor-probe-utils.mjs +52 -0
  11. package/scripts/lib/cursor-sdk-output-filter.mjs +86 -0
  12. package/scripts/validate-smoke-jsonl.mjs +27 -3
  13. package/src/context.ts +45 -32
  14. package/src/cursor-agent-message-web-tools.ts +172 -0
  15. package/src/cursor-agents-context.ts +176 -0
  16. package/src/cursor-incomplete-tool-visibility.ts +118 -0
  17. package/src/cursor-live-run-coordinator.ts +18 -7
  18. package/src/cursor-model.ts +12 -0
  19. package/src/cursor-native-tool-display-registration.ts +1 -4
  20. package/src/cursor-native-tool-display-replay.ts +63 -5
  21. package/src/cursor-native-tool-display-tools.ts +20 -0
  22. package/src/cursor-pi-tool-bridge-diagnostics.ts +11 -1
  23. package/src/cursor-pi-tool-bridge-run.ts +16 -1
  24. package/src/cursor-pi-tool-bridge-types.ts +3 -0
  25. package/src/cursor-provider-errors.ts +96 -0
  26. package/src/cursor-provider-live-run-drain.ts +181 -62
  27. package/src/cursor-provider-turn-coordinator.ts +198 -32
  28. package/src/cursor-provider.ts +270 -83
  29. package/src/cursor-question-tool.ts +1 -4
  30. package/src/cursor-sdk-abort-error-guard.ts +109 -0
  31. package/src/cursor-sdk-event-debug-constants.ts +40 -0
  32. package/src/cursor-sdk-event-debug-session.ts +163 -0
  33. package/src/cursor-sdk-event-debug.ts +597 -0
  34. package/src/cursor-sensitive-text.ts +27 -7
  35. package/src/cursor-session-agent.ts +25 -3
  36. package/src/cursor-session-send-policy.ts +43 -0
  37. package/src/cursor-setting-sources.ts +29 -0
  38. package/src/cursor-state.ts +1 -5
  39. package/src/cursor-tool-lifecycle.ts +111 -0
  40. package/src/cursor-tool-names.ts +12 -0
  41. package/src/cursor-tool-transcript.ts +4 -2
  42. package/src/cursor-transcript-tool-formatters.ts +228 -5
  43. package/src/cursor-transcript-tool-specs.ts +113 -14
  44. package/src/cursor-transcript-utils.ts +12 -0
  45. package/src/cursor-web-tool-activity.ts +84 -0
  46. package/src/index.ts +4 -1
@@ -0,0 +1,40 @@
1
+ import { resolve } from "node:path";
2
+
3
+ export const CURSOR_SDK_EVENT_DEBUG_ENV = "PI_CURSOR_SDK_EVENT_DEBUG";
4
+ export const CURSOR_SDK_EVENT_DEBUG_DIR_ENV = "PI_CURSOR_SDK_EVENT_DEBUG_DIR";
5
+ export const CURSOR_SDK_EVENT_DEBUG_RUN_DIR_ENV = "PI_CURSOR_SDK_EVENT_DEBUG_RUN_DIR";
6
+ export const CURSOR_SDK_EVENT_DEBUG_SESSION_DIR_ENV = "PI_CURSOR_SDK_EVENT_DEBUG_SESSION_DIR";
7
+ export const CURSOR_SDK_EVENT_DEBUG_STDERR_ENV = "PI_CURSOR_SDK_EVENT_DEBUG_STDERR";
8
+ export const CURSOR_SDK_EVENT_DEBUG_LOG_PREFIX = "[pi-cursor-sdk:sdk-events]";
9
+
10
+ export const SESSION_MANIFEST = "session.json";
11
+ export const SESSION_PI_SESSION_SNAPSHOT = "pi-session.jsonl";
12
+
13
+ export const ARTIFACTS = {
14
+ metadata: "metadata.json",
15
+ sendPayload: "send-payload.json",
16
+ contextSnapshot: "context-snapshot.json",
17
+ onDelta: "on-delta.jsonl",
18
+ onStep: "on-step.jsonl",
19
+ streamEvents: "stream-events.jsonl",
20
+ piStreamEvents: "pi-stream-events.jsonl",
21
+ providerEvents: "provider-events.jsonl",
22
+ liveRunEvents: "live-run-events.jsonl",
23
+ bridgeEvents: "bridge-events.jsonl",
24
+ bridgeRaw: "bridge-raw.jsonl",
25
+ displayDecisions: "display-decisions.jsonl",
26
+ coordinatorEvents: "coordinator-events.jsonl",
27
+ drainEvents: "drain-events.jsonl",
28
+ timeline: "timeline.jsonl",
29
+ piSessionSnapshot: "pi-session-snapshot.jsonl",
30
+ finalPartial: "final-partial.json",
31
+ errors: "errors.jsonl",
32
+ waitResult: "wait-result.json",
33
+ conversation: "conversation.json",
34
+ summary: "summary.json",
35
+ } as const;
36
+
37
+ export function resolveCursorSdkEventDebugBaseDir(cwd: string, env: Record<string, string | undefined> = process.env): string {
38
+ const raw = env[CURSOR_SDK_EVENT_DEBUG_DIR_ENV]?.trim();
39
+ return resolve(cwd, raw || ".debug/cursor-sdk-events");
40
+ }
@@ -0,0 +1,163 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { basename, join, resolve } from "node:path";
4
+ import {
5
+ CURSOR_SDK_EVENT_DEBUG_RUN_DIR_ENV,
6
+ CURSOR_SDK_EVENT_DEBUG_SESSION_DIR_ENV,
7
+ SESSION_MANIFEST,
8
+ resolveCursorSdkEventDebugBaseDir,
9
+ } from "./cursor-sdk-event-debug-constants.js";
10
+ import { getCursorSessionFile, getCursorSessionScopeKey } from "./cursor-session-scope.js";
11
+
12
+ const ANONYMOUS_SESSION_SCOPE_KEY = "__anonymous__";
13
+
14
+ interface CursorSdkEventDebugSessionState {
15
+ sessionKey: string;
16
+ sessionDir: string;
17
+ turnCounter: number;
18
+ }
19
+
20
+ interface CursorSdkEventDebugSessionManifest {
21
+ sessionKey: string;
22
+ sessionFile?: string;
23
+ sessionDir: string;
24
+ createdAt: string;
25
+ updatedAt: string;
26
+ turns: Array<{
27
+ turn: number;
28
+ artifactDir: string;
29
+ startedAt: string;
30
+ finalizedAt?: string;
31
+ summary?: Record<string, unknown>;
32
+ }>;
33
+ }
34
+
35
+ export interface CursorSdkEventDebugTurnAllocation {
36
+ artifactDir: string;
37
+ sessionDir?: string;
38
+ turn?: number;
39
+ sessionKey?: string;
40
+ pinnedRun: boolean;
41
+ }
42
+
43
+ const sessionDebugStates = new Map<string, CursorSdkEventDebugSessionState>();
44
+
45
+ function sanitizePathSegment(value: string): string {
46
+ return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "session";
47
+ }
48
+
49
+ export function slugSessionKey(scopeKey: string): string {
50
+ if (scopeKey === ANONYMOUS_SESSION_SCOPE_KEY) {
51
+ return `anonymous-${process.pid}`;
52
+ }
53
+ const fileBase = sanitizePathSegment(basename(scopeKey).replace(/\.jsonl?$/i, "") || "session");
54
+ const hash = createHash("sha256").update(scopeKey).digest("hex").slice(0, 8);
55
+ return `${fileBase}-${hash}`;
56
+ }
57
+
58
+ function resolvePinnedRunArtifactDir(runDirOverride: string | undefined): string | undefined {
59
+ const trimmed = runDirOverride?.trim();
60
+ if (!trimmed) return undefined;
61
+ const dir = resolve(trimmed);
62
+ mkdirSync(dir, { recursive: true });
63
+ return dir;
64
+ }
65
+
66
+ function readSessionManifest(sessionDir: string): CursorSdkEventDebugSessionManifest | undefined {
67
+ try {
68
+ return JSON.parse(readFileSync(join(sessionDir, SESSION_MANIFEST), "utf8")) as CursorSdkEventDebugSessionManifest;
69
+ } catch {
70
+ return undefined;
71
+ }
72
+ }
73
+
74
+ function writeSessionManifest(sessionDir: string, manifest: CursorSdkEventDebugSessionManifest): void {
75
+ writeFileSync(join(sessionDir, SESSION_MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`);
76
+ }
77
+
78
+ function maxTurnFromManifest(manifest: CursorSdkEventDebugSessionManifest | undefined): number {
79
+ if (!manifest || manifest.turns.length === 0) return 0;
80
+ return manifest.turns.reduce((max, entry) => Math.max(max, entry.turn), 0);
81
+ }
82
+
83
+ function resolveSessionDebugDir(
84
+ cwd: string,
85
+ env: Record<string, string | undefined>,
86
+ scopeKey: string,
87
+ ): string {
88
+ const pinned = env[CURSOR_SDK_EVENT_DEBUG_SESSION_DIR_ENV]?.trim();
89
+ if (pinned) return resolve(pinned);
90
+ return join(resolveCursorSdkEventDebugBaseDir(cwd, env), "sessions", slugSessionKey(scopeKey));
91
+ }
92
+
93
+ export function allocateCursorSdkEventDebugTurn(
94
+ cwd: string,
95
+ env: Record<string, string | undefined>,
96
+ ): CursorSdkEventDebugTurnAllocation {
97
+ const pinnedRunDir = resolvePinnedRunArtifactDir(env[CURSOR_SDK_EVENT_DEBUG_RUN_DIR_ENV]);
98
+ if (pinnedRunDir) {
99
+ return { artifactDir: pinnedRunDir, pinnedRun: true };
100
+ }
101
+
102
+ const scopeKey = getCursorSessionScopeKey();
103
+ const sessionDir = resolveSessionDebugDir(cwd, env, scopeKey);
104
+ mkdirSync(sessionDir, { recursive: true });
105
+
106
+ let state = sessionDebugStates.get(scopeKey);
107
+ if (!state || state.sessionDir !== sessionDir) {
108
+ const existing = readSessionManifest(sessionDir);
109
+ state = { sessionKey: scopeKey, sessionDir, turnCounter: maxTurnFromManifest(existing) };
110
+ sessionDebugStates.set(scopeKey, state);
111
+ }
112
+
113
+ state.turnCounter += 1;
114
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
115
+ const artifactDir = join(sessionDir, `turn-${String(state.turnCounter).padStart(3, "0")}-${stamp}`);
116
+ mkdirSync(artifactDir, { recursive: true });
117
+
118
+ const existing = readSessionManifest(sessionDir);
119
+ const manifest: CursorSdkEventDebugSessionManifest = existing ?? {
120
+ sessionKey: scopeKey,
121
+ sessionFile: getCursorSessionFile(),
122
+ sessionDir,
123
+ createdAt: new Date().toISOString(),
124
+ updatedAt: new Date().toISOString(),
125
+ turns: [],
126
+ };
127
+ manifest.sessionFile = getCursorSessionFile();
128
+ manifest.updatedAt = new Date().toISOString();
129
+ manifest.turns.push({
130
+ turn: state.turnCounter,
131
+ artifactDir,
132
+ startedAt: new Date().toISOString(),
133
+ });
134
+ writeSessionManifest(sessionDir, manifest);
135
+
136
+ return {
137
+ artifactDir,
138
+ sessionDir,
139
+ turn: state.turnCounter,
140
+ sessionKey: scopeKey,
141
+ pinnedRun: false,
142
+ };
143
+ }
144
+
145
+ export function updateCursorSdkEventDebugSessionManifest(
146
+ sessionDir: string,
147
+ artifactDir: string,
148
+ summary: Record<string, unknown>,
149
+ ): void {
150
+ const manifest = readSessionManifest(sessionDir);
151
+ if (!manifest) return;
152
+ const turnEntry = manifest.turns.find((entry) => entry.artifactDir === artifactDir);
153
+ if (!turnEntry) return;
154
+ turnEntry.finalizedAt = new Date().toISOString();
155
+ turnEntry.summary = summary;
156
+ manifest.updatedAt = new Date().toISOString();
157
+ manifest.sessionFile = getCursorSessionFile();
158
+ writeSessionManifest(sessionDir, manifest);
159
+ }
160
+
161
+ export function resetCursorSdkEventDebugSessionStateForTests(): void {
162
+ sessionDebugStates.clear();
163
+ }