pi-cursor-sdk 0.2.0 → 0.3.6

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 (168) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +54 -50
  3. package/dist/bundled-context-windows.js +39 -0
  4. package/dist/context-window-cache.js +92 -0
  5. package/dist/context.js +338 -0
  6. package/dist/cursor-active-tools.js +3 -0
  7. package/dist/cursor-agent-message-web-tools.js +165 -0
  8. package/dist/cursor-agents-context-registration.js +17 -0
  9. package/dist/cursor-agents-context.js +113 -0
  10. package/dist/cursor-api-key.js +34 -0
  11. package/dist/cursor-bridge-contract.js +27 -0
  12. package/dist/cursor-cloud-lifecycle.js +650 -0
  13. package/dist/cursor-cloud-local-state.js +460 -0
  14. package/dist/cursor-cloud-options.js +145 -0
  15. package/dist/cursor-cloud-reporting.js +222 -0
  16. package/dist/cursor-compact-tool-summary.js +75 -0
  17. package/dist/cursor-config.js +613 -0
  18. package/dist/cursor-context-tools.js +4 -0
  19. package/dist/cursor-display-only-trace.js +11 -0
  20. package/dist/cursor-display-text.js +18 -0
  21. package/dist/cursor-durable-fs.js +53 -0
  22. package/dist/cursor-edit-diff.js +13 -0
  23. package/dist/cursor-env-boolean.js +19 -0
  24. package/dist/cursor-fallback-models.generated.js +7760 -0
  25. package/dist/cursor-fallback-warning.js +15 -0
  26. package/dist/cursor-http1.js +45 -0
  27. package/dist/cursor-incomplete-tool-visibility.js +83 -0
  28. package/dist/cursor-live-run-accounting.js +55 -0
  29. package/dist/cursor-live-run-coordinator.js +406 -0
  30. package/dist/cursor-mcp-timeout-override.js +116 -0
  31. package/dist/cursor-model-lifecycle.js +32 -0
  32. package/dist/cursor-model.js +5 -0
  33. package/dist/cursor-native-replay-routing.js +35 -0
  34. package/dist/cursor-native-replay-trace.js +28 -0
  35. package/dist/cursor-native-tool-display-registration.js +102 -0
  36. package/dist/cursor-native-tool-display-replay.js +428 -0
  37. package/dist/cursor-native-tool-display-state.js +70 -0
  38. package/dist/cursor-native-tool-display-tools.js +123 -0
  39. package/dist/cursor-native-tool-names.js +11 -0
  40. package/dist/cursor-partial-content-emitter.js +118 -0
  41. package/dist/cursor-pi-tool-bridge-abort.js +114 -0
  42. package/dist/cursor-pi-tool-bridge-constants.js +6 -0
  43. package/dist/cursor-pi-tool-bridge-diagnostics.js +125 -0
  44. package/dist/cursor-pi-tool-bridge-env.js +18 -0
  45. package/dist/cursor-pi-tool-bridge-mcp.js +100 -0
  46. package/dist/cursor-pi-tool-bridge-run.js +355 -0
  47. package/dist/cursor-pi-tool-bridge-server.js +163 -0
  48. package/dist/cursor-pi-tool-bridge-snapshot.js +63 -0
  49. package/dist/cursor-pi-tool-bridge-types.js +1 -0
  50. package/dist/cursor-pi-tool-bridge.js +123 -0
  51. package/dist/cursor-provider-errors.js +311 -0
  52. package/dist/cursor-provider-lazy.js +39 -0
  53. package/dist/cursor-provider-live-run-drain.js +412 -0
  54. package/dist/cursor-provider-overflow.js +90 -0
  55. package/dist/cursor-provider-run-finalizer.js +176 -0
  56. package/dist/cursor-provider-run-outcome.js +76 -0
  57. package/dist/cursor-provider-turn-coordinator.js +275 -0
  58. package/dist/cursor-provider-turn-display-router.js +158 -0
  59. package/dist/cursor-provider-turn-emit.js +35 -0
  60. package/dist/cursor-provider-turn-finalize.js +154 -0
  61. package/dist/cursor-provider-turn-lifecycle-emitter.js +126 -0
  62. package/dist/cursor-provider-turn-prepare.js +374 -0
  63. package/dist/cursor-provider-turn-runner.js +146 -0
  64. package/dist/cursor-provider-turn-sdk-normalizer.js +74 -0
  65. package/dist/cursor-provider-turn-send.js +138 -0
  66. package/dist/cursor-provider-turn-shell-output.js +117 -0
  67. package/dist/cursor-provider-turn-tool-ledger.js +99 -0
  68. package/dist/cursor-provider-turn-types.js +1 -0
  69. package/dist/cursor-provider.js +72 -0
  70. package/dist/cursor-question-tool.js +194 -0
  71. package/dist/cursor-record-utils.js +61 -0
  72. package/dist/cursor-replay-activity-builders.js +114 -0
  73. package/dist/cursor-replay-source-names.js +27 -0
  74. package/dist/cursor-replay-summary-args.js +105 -0
  75. package/dist/cursor-replay-tool-details.js +167 -0
  76. package/dist/cursor-ripgrep-path.js +27 -0
  77. package/dist/cursor-run-final-text.js +63 -0
  78. package/dist/cursor-runtime-state.js +380 -0
  79. package/dist/cursor-sdk-billed-usage.js +86 -0
  80. package/dist/cursor-sdk-event-debug-constants.js +33 -0
  81. package/dist/cursor-sdk-event-debug-session.js +107 -0
  82. package/dist/cursor-sdk-event-debug.js +461 -0
  83. package/dist/cursor-sdk-output-filter.js +1 -0
  84. package/dist/cursor-sdk-process-error-guard.js +221 -0
  85. package/dist/cursor-sdk-runtime.js +3 -0
  86. package/dist/cursor-sensitive-text.js +23 -0
  87. package/dist/cursor-session-agent-cleanup.js +311 -0
  88. package/dist/cursor-session-agent-lifecycle.js +32 -0
  89. package/dist/cursor-session-agent-lineage.js +103 -0
  90. package/dist/cursor-session-agent-resume.js +417 -0
  91. package/dist/cursor-session-agent.js +507 -0
  92. package/dist/cursor-session-compaction-prep.js +21 -0
  93. package/dist/cursor-session-scope.js +113 -0
  94. package/dist/cursor-session-send-policy.js +18 -0
  95. package/dist/cursor-session-store.js +105 -0
  96. package/dist/cursor-session-turn-queue.js +50 -0
  97. package/dist/cursor-setting-sources.js +15 -0
  98. package/dist/cursor-skill-tool.js +230 -0
  99. package/dist/cursor-state.js +519 -0
  100. package/dist/cursor-task-presentation.js +59 -0
  101. package/dist/cursor-tool-lifecycle.js +94 -0
  102. package/dist/cursor-tool-manifest.js +34 -0
  103. package/dist/cursor-tool-presentation-registry.js +305 -0
  104. package/dist/cursor-tool-result-display-readers.js +158 -0
  105. package/dist/cursor-tool-transcript.js +32 -0
  106. package/dist/cursor-tool-visibility.js +28 -0
  107. package/dist/cursor-transcript-tool-formatters.js +618 -0
  108. package/dist/cursor-transcript-tool-specs.js +284 -0
  109. package/dist/cursor-transcript-utils.js +170 -0
  110. package/dist/cursor-usage-accounting.js +166 -0
  111. package/dist/cursor-web-tool-activity.js +23 -0
  112. package/dist/cursor-web-tool-args.js +12 -0
  113. package/dist/index.js +82 -0
  114. package/dist/model-discovery.js +305 -0
  115. package/dist/model-list-cache.js +156 -0
  116. package/docs/cursor-dogfood-checklist.md +2 -1
  117. package/docs/cursor-live-smoke-checklist.md +13 -12
  118. package/docs/cursor-model-ux-spec.md +25 -6
  119. package/docs/cursor-native-tool-replay.md +13 -13
  120. package/docs/cursor-native-tool-visual-audit.md +6 -3
  121. package/docs/cursor-testing-lessons.md +7 -7
  122. package/docs/cursor-tool-surfaces.md +5 -5
  123. package/docs/platform-smoke.md +3 -3
  124. package/package.json +19 -7
  125. package/platform-smoke.config.mjs +1 -1
  126. package/scripts/build.mjs +126 -0
  127. package/scripts/cloud-runtime-smoke.mjs +3 -1
  128. package/scripts/debug-provider-events.mjs +3 -1
  129. package/scripts/debug-sdk-events.mjs +1 -1
  130. package/scripts/isolated-cursor-smoke.sh +5 -4
  131. package/scripts/lib/cloud-smoke-cleanup-evidence.mjs +1 -1
  132. package/scripts/lib/cursor-visual-render.mjs +1 -1
  133. package/scripts/lib/ensure-built.mjs +16 -0
  134. package/scripts/lib/local-resume-smoke-harness.mjs +1 -1
  135. package/scripts/local-resume-smoke.mjs +3 -1
  136. package/scripts/platform-smoke/card-detect.mjs +1 -1
  137. package/scripts/platform-smoke/live-suite-runner.mjs +1 -1
  138. package/scripts/platform-smoke/render-ansi.mjs +1 -1
  139. package/scripts/platform-smoke/targets.mjs +2 -2
  140. package/scripts/prepare.mjs +102 -0
  141. package/scripts/steering-rpc-smoke.mjs +4 -2
  142. package/scripts/tmux-live-smoke.sh +11 -11
  143. package/scripts/visual-tui-smoke-self-test.mjs +4 -1
  144. package/scripts/visual-tui-smoke.mjs +51 -7
  145. package/src/bundled-context-windows.ts +4 -1
  146. package/src/cursor-agents-context-registration.ts +2 -2
  147. package/src/cursor-agents-context.ts +0 -2
  148. package/src/cursor-api-key.ts +5 -4
  149. package/src/cursor-cloud-reporting.ts +23 -2
  150. package/src/cursor-fallback-models.generated.ts +592 -204
  151. package/src/cursor-live-run-coordinator.ts +1 -0
  152. package/src/cursor-native-tool-display-registration.ts +10 -11
  153. package/src/cursor-provider-errors.ts +1 -1
  154. package/src/cursor-provider-lazy.ts +5 -4
  155. package/src/cursor-provider-live-run-drain.ts +1 -0
  156. package/src/cursor-provider-run-finalizer.ts +1 -0
  157. package/src/cursor-provider-turn-finalize.ts +12 -0
  158. package/src/cursor-provider-turn-types.ts +2 -0
  159. package/src/cursor-sdk-billed-usage.ts +107 -0
  160. package/src/cursor-sdk-process-error-guard.ts +6 -4
  161. package/src/cursor-session-agent-lifecycle.ts +8 -9
  162. package/src/cursor-session-agent-resume.ts +23 -0
  163. package/src/cursor-session-compaction-prep.ts +4 -0
  164. package/src/cursor-tool-manifest.ts +1 -1
  165. package/src/cursor-usage-accounting.ts +70 -9
  166. package/src/index.ts +1 -1
  167. package/tsconfig.build.json +8 -0
  168. package/tsconfig.json +16 -0
@@ -0,0 +1,107 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { basename, join, resolve } from "node:path";
4
+ import { CURSOR_SDK_EVENT_DEBUG_RUN_DIR_ENV, CURSOR_SDK_EVENT_DEBUG_SESSION_DIR_ENV, SESSION_MANIFEST, resolveCursorSdkEventDebugBaseDir, } from "./cursor-sdk-event-debug-constants.js";
5
+ import { getCursorSessionFile, getCursorSessionScopeKey } from "./cursor-session-scope.js";
6
+ const ANONYMOUS_SESSION_SCOPE_KEY = "__anonymous__";
7
+ const sessionDebugStates = new Map();
8
+ function sanitizePathSegment(value) {
9
+ return value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "session";
10
+ }
11
+ export function slugSessionKey(scopeKey) {
12
+ if (scopeKey === ANONYMOUS_SESSION_SCOPE_KEY) {
13
+ return `anonymous-${process.pid}`;
14
+ }
15
+ const fileBase = sanitizePathSegment(basename(scopeKey).replace(/\.jsonl?$/i, "") || "session");
16
+ const hash = createHash("sha256").update(scopeKey).digest("hex").slice(0, 8);
17
+ return `${fileBase}-${hash}`;
18
+ }
19
+ function resolvePinnedRunArtifactDir(runDirOverride) {
20
+ const trimmed = runDirOverride?.trim();
21
+ if (!trimmed)
22
+ return undefined;
23
+ const dir = resolve(trimmed);
24
+ mkdirSync(dir, { recursive: true });
25
+ return dir;
26
+ }
27
+ function readSessionManifest(sessionDir) {
28
+ try {
29
+ return JSON.parse(readFileSync(join(sessionDir, SESSION_MANIFEST), "utf8"));
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ function writeSessionManifest(sessionDir, manifest) {
36
+ writeFileSync(join(sessionDir, SESSION_MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`);
37
+ }
38
+ function maxTurnFromManifest(manifest) {
39
+ if (!manifest || manifest.turns.length === 0)
40
+ return 0;
41
+ return manifest.turns.reduce((max, entry) => Math.max(max, entry.turn), 0);
42
+ }
43
+ function resolveSessionDebugDir(cwd, env, scopeKey) {
44
+ const pinned = env[CURSOR_SDK_EVENT_DEBUG_SESSION_DIR_ENV]?.trim();
45
+ if (pinned)
46
+ return resolve(pinned);
47
+ return join(resolveCursorSdkEventDebugBaseDir(cwd, env), "sessions", slugSessionKey(scopeKey));
48
+ }
49
+ export function allocateCursorSdkEventDebugTurn(cwd, env) {
50
+ const pinnedRunDir = resolvePinnedRunArtifactDir(env[CURSOR_SDK_EVENT_DEBUG_RUN_DIR_ENV]);
51
+ if (pinnedRunDir) {
52
+ return { artifactDir: pinnedRunDir, pinnedRun: true };
53
+ }
54
+ const scopeKey = getCursorSessionScopeKey();
55
+ const sessionDir = resolveSessionDebugDir(cwd, env, scopeKey);
56
+ mkdirSync(sessionDir, { recursive: true });
57
+ let state = sessionDebugStates.get(scopeKey);
58
+ if (!state || state.sessionDir !== sessionDir) {
59
+ const existing = readSessionManifest(sessionDir);
60
+ state = { sessionKey: scopeKey, sessionDir, turnCounter: maxTurnFromManifest(existing) };
61
+ sessionDebugStates.set(scopeKey, state);
62
+ }
63
+ state.turnCounter += 1;
64
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
65
+ const artifactDir = join(sessionDir, `turn-${String(state.turnCounter).padStart(3, "0")}-${stamp}`);
66
+ mkdirSync(artifactDir, { recursive: true });
67
+ const existing = readSessionManifest(sessionDir);
68
+ const manifest = existing ?? {
69
+ sessionKey: scopeKey,
70
+ sessionFile: getCursorSessionFile(),
71
+ sessionDir,
72
+ createdAt: new Date().toISOString(),
73
+ updatedAt: new Date().toISOString(),
74
+ turns: [],
75
+ };
76
+ manifest.sessionFile = getCursorSessionFile();
77
+ manifest.updatedAt = new Date().toISOString();
78
+ manifest.turns.push({
79
+ turn: state.turnCounter,
80
+ artifactDir,
81
+ startedAt: new Date().toISOString(),
82
+ });
83
+ writeSessionManifest(sessionDir, manifest);
84
+ return {
85
+ artifactDir,
86
+ sessionDir,
87
+ turn: state.turnCounter,
88
+ sessionKey: scopeKey,
89
+ pinnedRun: false,
90
+ };
91
+ }
92
+ export function updateCursorSdkEventDebugSessionManifest(sessionDir, artifactDir, summary) {
93
+ const manifest = readSessionManifest(sessionDir);
94
+ if (!manifest)
95
+ return;
96
+ const turnEntry = manifest.turns.find((entry) => entry.artifactDir === artifactDir);
97
+ if (!turnEntry)
98
+ return;
99
+ turnEntry.finalizedAt = new Date().toISOString();
100
+ turnEntry.summary = summary;
101
+ manifest.updatedAt = new Date().toISOString();
102
+ manifest.sessionFile = getCursorSessionFile();
103
+ writeSessionManifest(sessionDir, manifest);
104
+ }
105
+ export function resetCursorSdkEventDebugSessionStateForTests() {
106
+ sessionDebugStates.clear();
107
+ }
@@ -0,0 +1,461 @@
1
+ import { createHash } from "node:crypto";
2
+ import { copyFileSync, existsSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { serializeCursorPiToolBridgeDiagnostic } from "./cursor-pi-tool-bridge-diagnostics.js";
5
+ import { asRecord } from "./cursor-record-utils.js";
6
+ import { getCursorSessionFile } from "./cursor-session-scope.js";
7
+ import { parseEnvBoolean } from "./cursor-env-boolean.js";
8
+ import { ARTIFACTS, CURSOR_SDK_EVENT_DEBUG_ENV, CURSOR_SDK_EVENT_DEBUG_LOG_PREFIX, CURSOR_SDK_EVENT_DEBUG_STDERR_ENV, SESSION_MANIFEST, SESSION_PI_SESSION_SNAPSHOT, } from "./cursor-sdk-event-debug-constants.js";
9
+ import { allocateCursorSdkEventDebugTurn, resetCursorSdkEventDebugSessionStateForTests, slugSessionKey, updateCursorSdkEventDebugSessionManifest, } from "./cursor-sdk-event-debug-session.js";
10
+ export { CURSOR_SDK_EVENT_DEBUG_DIR_ENV, CURSOR_SDK_EVENT_DEBUG_ENV, CURSOR_SDK_EVENT_DEBUG_LOG_PREFIX, CURSOR_SDK_EVENT_DEBUG_RUN_DIR_ENV, CURSOR_SDK_EVENT_DEBUG_SESSION_DIR_ENV, CURSOR_SDK_EVENT_DEBUG_STDERR_ENV, resolveCursorSdkEventDebugBaseDir, } from "./cursor-sdk-event-debug-constants.js";
11
+ const MAX_CURSOR_SDK_EVENT_DEBUG_JSONL_BYTES = 2 * 1024 * 1024;
12
+ function eventType(value) {
13
+ const record = asRecord(value);
14
+ if (typeof record?.type === "string")
15
+ return record.type;
16
+ if (typeof record?.event === "string")
17
+ return record.event;
18
+ if (typeof record?.kind === "string")
19
+ return record.kind;
20
+ return "unknown";
21
+ }
22
+ function resolveCursorSdkEventDebugStderrEnabled(env = process.env) {
23
+ return parseEnvBoolean(env[CURSOR_SDK_EVENT_DEBUG_STDERR_ENV], false);
24
+ }
25
+ function isNodeErrorWithCode(error, code) {
26
+ return asRecord(error)?.code === code;
27
+ }
28
+ function snapshotCursorSdkEventDebugRecord(record) {
29
+ try {
30
+ return structuredClone(record);
31
+ }
32
+ catch {
33
+ try {
34
+ return JSON.parse(JSON.stringify(record));
35
+ }
36
+ catch {
37
+ return record;
38
+ }
39
+ }
40
+ }
41
+ function serializeCursorSdkEventDebugRecord(record) {
42
+ try {
43
+ const seen = new WeakSet();
44
+ return JSON.stringify(snapshotCursorSdkEventDebugRecord(record), (_key, value) => {
45
+ if (typeof value === "bigint")
46
+ return value.toString();
47
+ if (value && typeof value === "object") {
48
+ if (seen.has(value))
49
+ return "[Circular]";
50
+ seen.add(value);
51
+ }
52
+ return value;
53
+ }) ?? JSON.stringify({ type: "artifact_serialization_error" });
54
+ }
55
+ catch {
56
+ return JSON.stringify({ type: "artifact_serialization_error" });
57
+ }
58
+ }
59
+ export function resolveCursorSdkEventDebugEnabled(env = process.env) {
60
+ return parseEnvBoolean(env[CURSOR_SDK_EVENT_DEBUG_ENV], false);
61
+ }
62
+ export const DISCARDED_INCOMPLETE_TOOL_CALL_REASON = "no-completion-at-run-end";
63
+ export function hashCursorSdkCallId(callId) {
64
+ return createHash("sha256").update(callId).digest("hex").slice(0, 8);
65
+ }
66
+ export function serializeDiscardedIncompleteStartedToolCall(record) {
67
+ return {
68
+ event: "discarded-incomplete-started-tool-call",
69
+ toolName: record.toolName,
70
+ callIdHash: hashCursorSdkCallId(record.callId),
71
+ reason: record.reason ?? DISCARDED_INCOMPLETE_TOOL_CALL_REASON,
72
+ };
73
+ }
74
+ export function recordDiscardedIncompleteStartedToolCall(recorder, env, record) {
75
+ if (!recorder && !resolveCursorSdkEventDebugEnabled(env))
76
+ return;
77
+ try {
78
+ const payload = serializeDiscardedIncompleteStartedToolCall(record);
79
+ recorder?.recordCoordinatorEvent("discarded-incomplete-started-tool-call", payload);
80
+ if (resolveCursorSdkEventDebugStderrEnabled(env) && resolveCursorSdkEventDebugEnabled(env)) {
81
+ process.stderr.write(`${CURSOR_SDK_EVENT_DEBUG_LOG_PREFIX} ${JSON.stringify(payload)}\n`);
82
+ }
83
+ }
84
+ catch {
85
+ // Debug logging must never affect provider execution.
86
+ }
87
+ }
88
+ export function attachCursorSdkEventDebugPiStreamTap(stream, sinkRef) {
89
+ if (!resolveCursorSdkEventDebugEnabled())
90
+ return;
91
+ const originalPush = stream.push.bind(stream);
92
+ stream.push = (event) => {
93
+ try {
94
+ sinkRef.current?.recordPiStreamEvent(event);
95
+ }
96
+ catch {
97
+ // Debug capture must never block the underlying stream.
98
+ }
99
+ return originalPush(event);
100
+ };
101
+ }
102
+ export class CursorSdkEventDebugSink {
103
+ artifactDir;
104
+ sessionDir;
105
+ turn;
106
+ sessionKey;
107
+ pinnedRun;
108
+ env;
109
+ startedAt = Date.now();
110
+ counts = {
111
+ onDelta: {},
112
+ onStep: {},
113
+ stream: {},
114
+ piStream: {},
115
+ provider: {},
116
+ liveRun: {},
117
+ bridge: {},
118
+ bridgeRaw: {},
119
+ displayDecisions: {},
120
+ coordinator: {},
121
+ drain: {},
122
+ timeline: {},
123
+ errors: 0,
124
+ };
125
+ metadata;
126
+ jsonlBuffers = new Map();
127
+ jsonlBufferBytes = new Map();
128
+ truncatedJsonlFiles = new Set();
129
+ finalized = false;
130
+ finalizationPromise;
131
+ waitResultRecorded = false;
132
+ streamCapturePromise;
133
+ streamCaptureErrors = [];
134
+ static maybeCreate(options) {
135
+ const env = options.env ?? process.env;
136
+ if (!resolveCursorSdkEventDebugEnabled(env))
137
+ return undefined;
138
+ const allocation = allocateCursorSdkEventDebugTurn(options.cwd, env);
139
+ return new CursorSdkEventDebugSink(allocation, options, env);
140
+ }
141
+ constructor(allocation, options, env) {
142
+ this.artifactDir = allocation.artifactDir;
143
+ this.sessionDir = allocation.sessionDir;
144
+ this.turn = allocation.turn;
145
+ this.sessionKey = allocation.sessionKey;
146
+ this.pinnedRun = allocation.pinnedRun;
147
+ this.env = env;
148
+ this.metadata = {
149
+ capturedAt: new Date().toISOString(),
150
+ modelId: options.modelId,
151
+ provider: options.provider,
152
+ cwd: options.cwd,
153
+ sessionDir: allocation.sessionDir,
154
+ sessionKey: allocation.sessionKey,
155
+ sessionFile: getCursorSessionFile(),
156
+ turn: allocation.turn,
157
+ pinnedRun: allocation.pinnedRun,
158
+ artifacts: ARTIFACTS,
159
+ warnings: [
160
+ "Raw artifact files may contain local paths, project text, tool args/results, or secrets from the workspace. Do not commit or share them.",
161
+ ],
162
+ };
163
+ this.clearKnownArtifactFiles();
164
+ writeFileSync(join(this.artifactDir, ARTIFACTS.metadata), `${JSON.stringify(this.metadata, null, 2)}\n`);
165
+ }
166
+ recordProviderMeta(meta) {
167
+ this.metadata = {
168
+ ...this.metadata,
169
+ providerMeta: meta,
170
+ };
171
+ writeFileSync(join(this.artifactDir, ARTIFACTS.metadata), `${JSON.stringify(this.metadata, null, 2)}\n`);
172
+ }
173
+ recordSendMeta(meta) {
174
+ this.metadata = {
175
+ ...this.metadata,
176
+ send: meta,
177
+ };
178
+ writeFileSync(join(this.artifactDir, ARTIFACTS.metadata), `${JSON.stringify(this.metadata, null, 2)}\n`);
179
+ }
180
+ recordSendPayload(payload) {
181
+ writeFileSync(join(this.artifactDir, ARTIFACTS.sendPayload), `${JSON.stringify(payload, null, 2)}\n`);
182
+ }
183
+ recordContextSnapshot(context) {
184
+ writeFileSync(join(this.artifactDir, ARTIFACTS.contextSnapshot), `${JSON.stringify(context, null, 2)}\n`);
185
+ }
186
+ recordRunMeta(meta) {
187
+ this.metadata = {
188
+ ...this.metadata,
189
+ run: meta,
190
+ };
191
+ writeFileSync(join(this.artifactDir, ARTIFACTS.metadata), `${JSON.stringify(this.metadata, null, 2)}\n`);
192
+ }
193
+ recordOnDelta(update) {
194
+ this.appendJsonl(ARTIFACTS.onDelta, "update", update, this.counts.onDelta);
195
+ }
196
+ recordOnStep(step) {
197
+ this.appendJsonl(ARTIFACTS.onStep, "step", step, this.counts.onStep);
198
+ }
199
+ recordStreamEvent(event) {
200
+ this.appendJsonl(ARTIFACTS.streamEvents, "event", event, this.counts.stream);
201
+ }
202
+ recordPiStreamEvent(event) {
203
+ this.appendJsonl(ARTIFACTS.piStreamEvents, "event", event, this.counts.piStream);
204
+ }
205
+ recordProviderEvent(phase, payload) {
206
+ this.appendProviderJsonl(phase, payload);
207
+ }
208
+ recordLiveRunEvent(event) {
209
+ this.appendJsonl(ARTIFACTS.liveRunEvents, "event", event, this.counts.liveRun);
210
+ }
211
+ recordBridgeDiagnostic(event) {
212
+ const serialized = serializeCursorPiToolBridgeDiagnostic(event);
213
+ this.appendJsonl(ARTIFACTS.bridgeEvents, "event", serialized, this.counts.bridge, String(serialized.event));
214
+ }
215
+ recordBridgeRaw(payload) {
216
+ this.appendJsonl(ARTIFACTS.bridgeRaw, "bridgeRaw", payload, this.counts.bridgeRaw, payload.kind);
217
+ }
218
+ recordDisplayDecision(decision) {
219
+ this.appendJsonl(ARTIFACTS.displayDecisions, "decision", decision, this.counts.displayDecisions, decision.action);
220
+ }
221
+ recordCoordinatorEvent(phase, payload) {
222
+ this.appendCoordinatorJsonl(phase, payload);
223
+ }
224
+ recordDrainEvent(phase, payload) {
225
+ this.appendDrainJsonl(phase, payload);
226
+ }
227
+ recordFinalPartial(partial) {
228
+ writeFileSync(join(this.artifactDir, ARTIFACTS.finalPartial), `${JSON.stringify(partial, null, 2)}\n`);
229
+ this.recordTimeline("finalPartial", "snapshot", partial);
230
+ }
231
+ recordError(label, error) {
232
+ this.counts.errors += 1;
233
+ const payload = {
234
+ label,
235
+ message: error instanceof Error ? error.message : String(error),
236
+ stack: error instanceof Error ? error.stack : undefined,
237
+ value: error,
238
+ };
239
+ this.appendJsonl(ARTIFACTS.errors, "error", payload, { [label]: 1 }, label);
240
+ }
241
+ attachRunStream(run) {
242
+ const sdkRun = run;
243
+ if (typeof sdkRun.stream !== "function") {
244
+ this.recordProviderEvent("run_stream_unavailable", { runId: sdkRun.id, requestId: sdkRun.requestId });
245
+ return;
246
+ }
247
+ this.streamCapturePromise = (async () => {
248
+ try {
249
+ for await (const event of sdkRun.stream()) {
250
+ this.recordStreamEvent(event);
251
+ }
252
+ }
253
+ catch (error) {
254
+ this.streamCaptureErrors.push(error);
255
+ this.recordError("run_stream", error);
256
+ }
257
+ })();
258
+ }
259
+ async captureRunArtifacts(run) {
260
+ const sdkRun = run;
261
+ if (this.streamCapturePromise) {
262
+ await this.streamCapturePromise.catch(() => undefined);
263
+ }
264
+ if (typeof sdkRun.conversation === "function" && sdkRun.supports?.("conversation")) {
265
+ try {
266
+ const conversation = await sdkRun.conversation();
267
+ writeFileSync(join(this.artifactDir, ARTIFACTS.conversation), `${JSON.stringify(conversation, null, 2)}\n`);
268
+ this.recordProviderEvent("conversation_captured", { supported: true });
269
+ }
270
+ catch (error) {
271
+ this.recordError("conversation", error);
272
+ }
273
+ }
274
+ else {
275
+ writeFileSync(join(this.artifactDir, ARTIFACTS.conversation), `${JSON.stringify({
276
+ skipped: true,
277
+ reason: sdkRun.unsupportedReason?.("conversation") ?? "conversation unsupported",
278
+ }, null, 2)}\n`);
279
+ }
280
+ }
281
+ recordWaitResult(result) {
282
+ if (this.waitResultRecorded)
283
+ return;
284
+ this.waitResultRecorded = true;
285
+ writeFileSync(join(this.artifactDir, ARTIFACTS.waitResult), `${JSON.stringify(result, null, 2)}\n`);
286
+ }
287
+ capturePiSessionSnapshot() {
288
+ const sessionFile = getCursorSessionFile();
289
+ if (!sessionFile) {
290
+ return { copied: false, reason: "session file unknown" };
291
+ }
292
+ if (!existsSync(sessionFile)) {
293
+ return { copied: false, sessionFile, reason: "session file not found at debug finalization" };
294
+ }
295
+ try {
296
+ copyFileSync(sessionFile, join(this.artifactDir, ARTIFACTS.piSessionSnapshot));
297
+ if (this.sessionDir) {
298
+ copyFileSync(sessionFile, join(this.sessionDir, SESSION_PI_SESSION_SNAPSHOT));
299
+ }
300
+ this.recordTimeline("piSession", "snapshot", { sessionFile, artifact: ARTIFACTS.piSessionSnapshot });
301
+ return { copied: true, sessionFile };
302
+ }
303
+ catch (error) {
304
+ if (isNodeErrorWithCode(error, "ENOENT")) {
305
+ return { copied: false, sessionFile, reason: "session file not found at debug finalization" };
306
+ }
307
+ this.recordError("pi_session_snapshot", error);
308
+ return {
309
+ copied: false,
310
+ sessionFile,
311
+ reason: error instanceof Error ? error.message : String(error),
312
+ };
313
+ }
314
+ }
315
+ updateSessionManifest(summary) {
316
+ if (this.pinnedRun || !this.sessionDir || this.turn === undefined)
317
+ return;
318
+ updateCursorSdkEventDebugSessionManifest(this.sessionDir, this.artifactDir, summary);
319
+ }
320
+ clearKnownArtifactFiles() {
321
+ for (const fileName of Object.values(ARTIFACTS)) {
322
+ try {
323
+ unlinkSync(join(this.artifactDir, fileName));
324
+ }
325
+ catch {
326
+ // Ignore missing prior artifacts when reusing a pinned run directory.
327
+ }
328
+ }
329
+ }
330
+ async finalize() {
331
+ this.finalizationPromise ??= this.finalizeOnce();
332
+ await this.finalizationPromise;
333
+ }
334
+ async finalizeOnce() {
335
+ if (this.finalized)
336
+ return;
337
+ if (this.streamCapturePromise) {
338
+ await this.streamCapturePromise.catch(() => undefined);
339
+ }
340
+ const piSessionSnapshot = this.capturePiSessionSnapshot();
341
+ const summary = {
342
+ artifactDir: this.artifactDir,
343
+ sessionDir: this.sessionDir,
344
+ sessionKey: this.sessionKey,
345
+ sessionFile: getCursorSessionFile(),
346
+ turn: this.turn,
347
+ elapsedMs: Date.now() - this.startedAt,
348
+ counts: {
349
+ onDelta: { ...this.counts.onDelta },
350
+ onStep: { ...this.counts.onStep },
351
+ stream: { ...this.counts.stream },
352
+ piStream: { ...this.counts.piStream },
353
+ provider: { ...this.counts.provider },
354
+ liveRun: { ...this.counts.liveRun },
355
+ bridge: { ...this.counts.bridge },
356
+ bridgeRaw: { ...this.counts.bridgeRaw },
357
+ displayDecisions: { ...this.counts.displayDecisions },
358
+ coordinator: { ...this.counts.coordinator },
359
+ drain: { ...this.counts.drain },
360
+ timeline: { ...this.counts.timeline },
361
+ errors: this.counts.errors,
362
+ },
363
+ piSessionSnapshot,
364
+ artifacts: Object.fromEntries(Object.entries(ARTIFACTS).map(([key, name]) => [key, join(this.artifactDir, name)])),
365
+ waitResultRecorded: this.waitResultRecorded,
366
+ streamCaptureErrors: this.streamCaptureErrors.map((error) => error instanceof Error ? error.message : String(error)),
367
+ jsonlByteLimit: MAX_CURSOR_SDK_EVENT_DEBUG_JSONL_BYTES,
368
+ truncatedJsonlFiles: [...this.truncatedJsonlFiles].sort(),
369
+ };
370
+ this.flushJsonlBuffers();
371
+ writeFileSync(join(this.artifactDir, ARTIFACTS.summary), `${JSON.stringify(summary, null, 2)}\n`);
372
+ this.updateSessionManifest(summary);
373
+ if (resolveCursorSdkEventDebugStderrEnabled(this.env)) {
374
+ process.stderr.write(`${CURSOR_SDK_EVENT_DEBUG_LOG_PREFIX} ${JSON.stringify(summary)}\n`);
375
+ }
376
+ this.finalized = true;
377
+ }
378
+ appendProviderJsonl(phase, payload) {
379
+ const elapsedMs = Date.now() - this.startedAt;
380
+ const record = { ts: new Date().toISOString(), elapsedMs, turn: this.turn, phase, payload };
381
+ this.bufferJsonl(ARTIFACTS.providerEvents, record);
382
+ this.counts.provider[phase] = (this.counts.provider[phase] ?? 0) + 1;
383
+ this.recordTimeline("provider", phase, payload);
384
+ }
385
+ appendCoordinatorJsonl(phase, payload) {
386
+ const elapsedMs = Date.now() - this.startedAt;
387
+ const record = { ts: new Date().toISOString(), elapsedMs, turn: this.turn, phase, payload };
388
+ this.bufferJsonl(ARTIFACTS.coordinatorEvents, record);
389
+ this.counts.coordinator[phase] = (this.counts.coordinator[phase] ?? 0) + 1;
390
+ this.recordTimeline("coordinator", phase, payload);
391
+ }
392
+ appendDrainJsonl(phase, payload) {
393
+ const elapsedMs = Date.now() - this.startedAt;
394
+ const record = { ts: new Date().toISOString(), elapsedMs, turn: this.turn, phase, payload };
395
+ this.bufferJsonl(ARTIFACTS.drainEvents, record);
396
+ this.counts.drain[phase] = (this.counts.drain[phase] ?? 0) + 1;
397
+ this.recordTimeline("drain", phase, payload);
398
+ }
399
+ recordTimeline(layer, kind, payload) {
400
+ const elapsedMs = Date.now() - this.startedAt;
401
+ const record = {
402
+ ts: new Date().toISOString(),
403
+ elapsedMs,
404
+ turn: this.turn,
405
+ layer,
406
+ kind,
407
+ payload,
408
+ };
409
+ this.bufferJsonl(ARTIFACTS.timeline, record);
410
+ const timelineKey = `${layer}:${kind}`;
411
+ this.counts.timeline[timelineKey] = (this.counts.timeline[timelineKey] ?? 0) + 1;
412
+ }
413
+ appendJsonl(fileName, recordKey, value, counts, countKey) {
414
+ const elapsedMs = Date.now() - this.startedAt;
415
+ const record = {
416
+ ts: new Date().toISOString(),
417
+ elapsedMs,
418
+ turn: this.turn,
419
+ [recordKey]: value,
420
+ };
421
+ this.bufferJsonl(fileName, record);
422
+ const type = countKey ?? eventType(value);
423
+ counts[type] = (counts[type] ?? 0) + 1;
424
+ const layer = fileName.replace(/\.jsonl$/, "");
425
+ this.recordTimeline(layer, type, value);
426
+ }
427
+ bufferJsonl(fileName, record) {
428
+ if (this.finalized || this.truncatedJsonlFiles.has(fileName))
429
+ return;
430
+ const line = `${serializeCursorSdkEventDebugRecord(record)}\n`;
431
+ const marker = `${JSON.stringify({ type: "artifact_truncated", limitBytes: MAX_CURSOR_SDK_EVENT_DEBUG_JSONL_BYTES })}\n`;
432
+ const usedBytes = this.jsonlBufferBytes.get(fileName) ?? 0;
433
+ const lineBytes = Buffer.byteLength(line);
434
+ const markerBytes = Buffer.byteLength(marker);
435
+ const records = this.jsonlBuffers.get(fileName) ?? [];
436
+ if (usedBytes + lineBytes > MAX_CURSOR_SDK_EVENT_DEBUG_JSONL_BYTES - markerBytes) {
437
+ records.push(marker);
438
+ this.jsonlBuffers.set(fileName, records);
439
+ this.jsonlBufferBytes.set(fileName, usedBytes + markerBytes);
440
+ this.truncatedJsonlFiles.add(fileName);
441
+ return;
442
+ }
443
+ records.push(line);
444
+ this.jsonlBuffers.set(fileName, records);
445
+ this.jsonlBufferBytes.set(fileName, usedBytes + lineBytes);
446
+ }
447
+ flushJsonlBuffers() {
448
+ for (const [fileName, records] of this.jsonlBuffers) {
449
+ writeFileSync(join(this.artifactDir, fileName), records.join(""));
450
+ }
451
+ this.jsonlBuffers.clear();
452
+ this.jsonlBufferBytes.clear();
453
+ }
454
+ }
455
+ export const __testUtils = {
456
+ ARTIFACTS,
457
+ SESSION_MANIFEST,
458
+ MAX_CURSOR_SDK_EVENT_DEBUG_JSONL_BYTES,
459
+ slugSessionKey,
460
+ resetSessionDebugState: resetCursorSdkEventDebugSessionStateForTests,
461
+ };
@@ -0,0 +1 @@
1
+ export { CURSOR_SDK_STARTUP_NOISE_PATTERNS, installCursorSdkOutputFilter, isCursorSdkOutputSuppressed, isCursorSdkStartupNoise, suppressCursorSdkOutput, } from "../shared/cursor-sdk-output-filter.mjs";