pi-cursor-sdk 0.1.55 → 0.1.57
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 +60 -2
- package/README.md +100 -28
- package/docs/cursor-live-smoke-checklist.md +7 -7
- package/docs/cursor-model-ux-spec.md +48 -41
- package/docs/cursor-native-tool-replay.md +4 -4
- package/docs/cursor-native-tool-visual-audit.md +1 -1
- package/docs/cursor-testing-lessons.md +20 -7
- package/docs/cursor-tool-surfaces.md +13 -2
- package/docs/platform-smoke-implementation.md +220 -0
- package/docs/platform-smoke.md +183 -247
- package/package.json +39 -7
- package/platform-smoke.config.mjs +5 -1
- package/scripts/cloud-runtime-smoke.d.mts +3 -0
- package/scripts/cloud-runtime-smoke.mjs +502 -0
- package/scripts/debug-provider-events.mjs +7 -2
- package/scripts/isolated-cursor-smoke.sh +4 -6
- package/scripts/lib/cursor-child-process.d.mts +1 -0
- package/scripts/lib/cursor-child-process.mjs +137 -7
- package/scripts/lib/local-resume-smoke-harness.mjs +543 -0
- package/scripts/local-resume-cleanup-smoke.mjs +108 -0
- package/scripts/local-resume-smoke.d.mts +1 -0
- package/scripts/local-resume-smoke.mjs +642 -0
- package/scripts/platform-smoke/artifact-anchored-extract.d.mts +10 -0
- package/scripts/platform-smoke/artifact-anchored-extract.mjs +111 -0
- package/scripts/platform-smoke/artifact-bundle-chunk.mjs +57 -0
- package/scripts/platform-smoke/artifact-bundle-contract.mjs +34 -0
- package/scripts/platform-smoke/artifact-fs-safety.mjs +311 -0
- package/scripts/platform-smoke/artifact-openat-extract.c +335 -0
- package/scripts/platform-smoke/artifact-secrets.mjs +155 -0
- package/scripts/platform-smoke/artifacts.mjs +293 -65
- package/scripts/platform-smoke/card-detect.mjs +16 -4
- package/scripts/platform-smoke/crabbox-runner.mjs +45 -3
- package/scripts/platform-smoke/doctor.mjs +20 -10
- package/scripts/platform-smoke/live-suite-runner.mjs +18 -57
- package/scripts/platform-smoke/local-resume-runner.mjs +252 -0
- package/scripts/platform-smoke/local-resume-suites.d.mts +15 -0
- package/scripts/platform-smoke/local-resume-suites.mjs +104 -0
- package/scripts/platform-smoke/scenarios.mjs +16 -2
- package/scripts/platform-smoke/target-runtime.mjs +206 -0
- package/scripts/platform-smoke/targets.mjs +33 -141
- package/scripts/platform-smoke/visual-evidence.mjs +6 -7
- package/scripts/platform-smoke/wrapped-line-match.mjs +9 -0
- package/scripts/platform-smoke.mjs +40 -27
- package/scripts/refresh-cursor-model-snapshots.mjs +18 -6
- package/scripts/steering-rpc-smoke.mjs +12 -2
- package/scripts/tmux-live-smoke.sh +3 -5
- package/shared/cursor-cloud-lifecycle-constants.d.mts +3 -0
- package/shared/cursor-cloud-lifecycle-constants.mjs +7 -0
- package/shared/cursor-sensitive-text.mjs +7 -1
- package/src/context.ts +5 -2
- package/src/cursor-agents-context-registration.ts +7 -0
- package/src/cursor-agents-context.ts +3 -1
- package/src/cursor-api-key.ts +15 -1
- package/src/cursor-bridge-contract.ts +3 -0
- package/src/cursor-cloud-lifecycle.ts +733 -0
- package/src/cursor-cloud-options.ts +206 -0
- package/src/cursor-cloud-reporting.ts +246 -0
- package/src/cursor-config.ts +659 -0
- package/src/cursor-display-only-trace.ts +14 -0
- package/src/cursor-display-text.ts +8 -2
- package/src/cursor-durable-fs.ts +49 -0
- package/src/cursor-fallback-models.generated.ts +2045 -485
- package/src/cursor-live-run-accounting.ts +7 -1
- package/src/cursor-live-run-coordinator.ts +1 -0
- package/src/cursor-pi-tool-bridge-run.ts +14 -4
- package/src/cursor-provider-errors.ts +31 -5
- package/src/cursor-provider-live-run-drain.ts +5 -0
- package/src/cursor-provider-run-finalizer.ts +37 -28
- package/src/cursor-provider-run-outcome.ts +7 -2
- package/src/cursor-provider-turn-finalize.ts +77 -7
- package/src/cursor-provider-turn-prepare.ts +228 -12
- package/src/cursor-provider-turn-runner.ts +42 -13
- package/src/cursor-provider-turn-send.ts +59 -16
- package/src/cursor-provider-turn-types.ts +44 -10
- package/src/cursor-runtime-state.ts +478 -0
- package/src/cursor-sdk-event-debug.ts +46 -6
- package/src/cursor-sdk-process-error-guard.ts +101 -30
- package/src/cursor-session-agent-cleanup.ts +328 -0
- package/src/cursor-session-agent-resume.ts +439 -0
- package/src/cursor-session-agent.ts +109 -13
- package/src/cursor-session-scope.ts +35 -2
- package/src/cursor-session-send-policy.ts +1 -1
- package/src/cursor-skill-tool.ts +30 -11
- package/src/cursor-state.ts +112 -69
- package/src/cursor-tool-manifest.ts +1 -1
- package/src/cursor-usage-accounting.ts +14 -4
- package/src/index.ts +11 -2
- package/src/model-discovery.ts +10 -56
|
@@ -8,7 +8,12 @@ import { accessSync, chmodSync, constants, mkdirSync, mkdtempSync, rmSync, write
|
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
CHILD_PROCESS_TREE_SPAWN_OPTIONS,
|
|
13
|
+
parseJsonLines,
|
|
14
|
+
terminateChild,
|
|
15
|
+
waitForChildClose,
|
|
16
|
+
} from "./lib/cursor-child-process.mjs";
|
|
12
17
|
import { apiKeySecretsFromProcess } from "./lib/cursor-cli-args.mjs";
|
|
13
18
|
import { buildCursorSmokeEnv, CURSOR_SDK_EVENT_DEBUG_ENV_NAMES } from "./lib/cursor-smoke-env.mjs";
|
|
14
19
|
import { scrubSensitiveText } from "../shared/cursor-sensitive-text.mjs";
|
|
@@ -168,7 +173,12 @@ async function runPiRpcSmoke(sessionDir, piBin) {
|
|
|
168
173
|
const args = ["--approve", "-e", root, "--cursor-no-fast", "--model", "cursor/composer-2-5", "--mode", "rpc", "--session-dir", sessionDir];
|
|
169
174
|
const env = buildPiRpcEnv();
|
|
170
175
|
|
|
171
|
-
const child = spawn(piBin, args, {
|
|
176
|
+
const child = spawn(piBin, args, {
|
|
177
|
+
cwd: root,
|
|
178
|
+
env,
|
|
179
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
180
|
+
...CHILD_PROCESS_TREE_SPAWN_OPTIONS,
|
|
181
|
+
});
|
|
172
182
|
let closed = false;
|
|
173
183
|
let stdout = "";
|
|
174
184
|
let stderr = "";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/
|
|
1
|
+
#!/bin/bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
@@ -33,8 +33,7 @@ cleanup() {
|
|
|
33
33
|
trap cleanup EXIT
|
|
34
34
|
|
|
35
35
|
print_help() {
|
|
36
|
-
|
|
37
|
-
Partial live smoke runner for pi-cursor-sdk (subset of docs/cursor-live-smoke-checklist.md).
|
|
36
|
+
printf '%s\n' 'Partial live smoke runner for pi-cursor-sdk (subset of docs/cursor-live-smoke-checklist.md).
|
|
38
37
|
|
|
39
38
|
Usage:
|
|
40
39
|
./scripts/tmux-live-smoke.sh
|
|
@@ -73,8 +72,7 @@ Options:
|
|
|
73
72
|
|
|
74
73
|
Exit codes:
|
|
75
74
|
0 all partial checks passed
|
|
76
|
-
1 prerequisite, smoke, safety, or JSONL validation failure
|
|
77
|
-
EOF
|
|
75
|
+
1 prerequisite, smoke, safety, or JSONL validation failure'
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
log() { smoke_log "$@"; }
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Canonical Cursor Cloud lifecycle contract shared by the runtime and maintainer scripts. */
|
|
2
|
+
|
|
3
|
+
export const CLOUD_AGENT_ID_PATTERN = /^bc-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4
|
+
|
|
5
|
+
export const CLOUD_LIFECYCLE_ENTRY_TYPE = "cursor-cloud-lifecycle";
|
|
6
|
+
|
|
7
|
+
export const CLOUD_LIFECYCLE_JOURNAL_PREFIX = ".cursor-cloud-lifecycle";
|
|
@@ -9,6 +9,12 @@ const BRIDGE_ENDPOINT_TOKEN_PATTERN = "[^/\\s\"'<>]+";
|
|
|
9
9
|
const BRIDGE_LOOPBACK_HOST_PATTERN = "127\\.0\\.0\\.1(?::\\d+)?";
|
|
10
10
|
const BRIDGE_ENDPOINT_PATH_PATTERN = `${escapeRegExp(BRIDGE_ENDPOINT_ROOT)}/${BRIDGE_ENDPOINT_TOKEN_PATTERN}/mcp`;
|
|
11
11
|
|
|
12
|
+
function scrubUrlUserInfo(text) {
|
|
13
|
+
return text
|
|
14
|
+
.replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/?#]*@(?=[^\s/?#]+)/gi, "$1[redacted]@")
|
|
15
|
+
.replace(/\b[^\s/:@]+:[^\s/]+@(?=[A-Za-z0-9.-]+(?::|\/|\s|$))/g, "[redacted]@");
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
function scrubBridgeEndpointMaterial(text) {
|
|
13
19
|
return text
|
|
14
20
|
.replace(
|
|
@@ -29,7 +35,7 @@ export function scrubSensitiveText(text, apiKey) {
|
|
|
29
35
|
scrubbed = scrubbed.replace(new RegExp(escapeRegExp(trimmedKey), "g"), "[redacted]");
|
|
30
36
|
}
|
|
31
37
|
return scrubBridgeEndpointMaterial(
|
|
32
|
-
scrubbed
|
|
38
|
+
scrubUrlUserInfo(scrubbed)
|
|
33
39
|
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [redacted]")
|
|
34
40
|
.replace(/((?:^|[\s,{])cookie["']?\s*[:=]\s*["']?)[^\n]+/gi, "$1[redacted]")
|
|
35
41
|
.replace(
|
package/src/context.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import type { Context, Message, ToolCall } from "@earendil-works/pi-ai/compat";
|
|
3
3
|
import { convertToLlm } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import type { AgentModeOption, SDKImage } from "@cursor/sdk";
|
|
5
|
+
import { CURSOR_PI_BRIDGE_PREFERENCE_TEXT } from "./cursor-bridge-contract.js";
|
|
5
6
|
import { getCursorReplayPromptLabel } from "./cursor-tool-presentation-registry.js";
|
|
6
7
|
|
|
7
8
|
export interface CursorPrompt {
|
|
@@ -48,6 +49,7 @@ export function getCursorToolTailGuardText(
|
|
|
48
49
|
: getCursorPlanModeToolGuidanceText(options.agentMode, { includePiBridgeGuidance: options.includePiBridgeGuidance }),
|
|
49
50
|
"Exact-output requests: output exactly the requested text; no preamble or checks unless asked.",
|
|
50
51
|
"Tools: call available Cursor SDK/MCP tools; never print tool cards as assistant text.",
|
|
52
|
+
options.includePiBridgeGuidance === false ? undefined : CURSOR_PI_BRIDGE_PREFERENCE_TEXT,
|
|
51
53
|
].filter((line): line is string => line !== undefined).join("\n");
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -73,7 +75,9 @@ function getCursorToolBoundaryText(
|
|
|
73
75
|
return lines.join("\n");
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
function getCursorBootstrapTailSections(
|
|
78
|
+
function getCursorBootstrapTailSections(
|
|
79
|
+
options: Pick<CursorPromptOptions, "agentMode" | "includePiBridgeGuidance"> = {},
|
|
80
|
+
): string[] {
|
|
77
81
|
return [
|
|
78
82
|
"Answer the latest user request above using the instructions and Cursor SDK capabilities available in this run.",
|
|
79
83
|
getCursorToolTailGuardText({ ...options, includePlanModeGuidance: false }),
|
|
@@ -438,6 +442,5 @@ export function buildCursorPrompt(context: Context, options: CursorPromptOptions
|
|
|
438
442
|
);
|
|
439
443
|
const text = parts.join(SECTION_SEPARATOR);
|
|
440
444
|
|
|
441
|
-
|
|
442
445
|
return { text, images };
|
|
443
446
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
+
import { isCursorModel } from "./cursor-model.js";
|
|
1
2
|
import { registerCursorModelLifecycle, type CursorModelLifecycleExtensionApi } from "./cursor-model-lifecycle.js";
|
|
3
|
+
import { resolveEffectiveCursorConfigForContext } from "./cursor-runtime-state.js";
|
|
2
4
|
|
|
3
5
|
export type CursorAgentsContextExtensionApi = CursorModelLifecycleExtensionApi;
|
|
4
6
|
|
|
5
7
|
export function registerCursorAgentsContextDedup(pi: CursorAgentsContextExtensionApi): void {
|
|
6
8
|
registerCursorModelLifecycle(pi, {
|
|
7
9
|
beforeAgentStart: async (event, ctx) => {
|
|
10
|
+
if (!isCursorModel(ctx.model)) return undefined;
|
|
8
11
|
const { resolveCursorFacingSystemPrompt } = await import("./cursor-agents-context.js");
|
|
12
|
+
const runtime = resolveEffectiveCursorConfigForContext(ctx).runtime.value;
|
|
9
13
|
const resolved = resolveCursorFacingSystemPrompt(
|
|
10
14
|
event.systemPrompt,
|
|
11
15
|
ctx.model,
|
|
12
16
|
event.systemPromptOptions,
|
|
17
|
+
undefined,
|
|
18
|
+
undefined,
|
|
19
|
+
runtime,
|
|
13
20
|
);
|
|
14
21
|
if (resolved === event.systemPrompt) return undefined;
|
|
15
22
|
return { systemPrompt: resolved };
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resolveCursorSettingSources,
|
|
12
12
|
} from "./cursor-setting-sources.js";
|
|
13
13
|
import type { SettingSource } from "@cursor/sdk";
|
|
14
|
+
import type { CursorRuntime } from "./cursor-config.js";
|
|
14
15
|
export { registerCursorAgentsContextDedup, type CursorAgentsContextExtensionApi } from "./cursor-agents-context-registration.js";
|
|
15
16
|
|
|
16
17
|
export const CURSOR_PRESERVE_PI_AGENTS_MD_ENV = "PI_CURSOR_PRESERVE_PI_AGENTS_MD";
|
|
@@ -153,8 +154,9 @@ export function resolveCursorFacingSystemPrompt(
|
|
|
153
154
|
systemPromptOptions?: BuildSystemPromptOptions,
|
|
154
155
|
settingSourcesRaw?: string,
|
|
155
156
|
agentDir?: string,
|
|
157
|
+
runtime: CursorRuntime = "local",
|
|
156
158
|
): string {
|
|
157
|
-
if (!systemPromptOptions) return systemPrompt;
|
|
159
|
+
if (runtime === "cloud" || !systemPromptOptions) return systemPrompt;
|
|
158
160
|
const contextFiles = systemPromptOptions.contextFiles ?? [];
|
|
159
161
|
const settingSources =
|
|
160
162
|
settingSourcesRaw === undefined
|
package/src/cursor-api-key.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export const CURSOR_API_KEY_ENV_VAR = "CURSOR_API_KEY";
|
|
2
|
+
const CURSOR_PROVIDER_ID = "cursor";
|
|
2
3
|
|
|
3
4
|
// Non-secret literal sentinel for pi's provider registry. Pi 0.77 treats `$ENV_VAR`
|
|
4
5
|
// values as unconfigured when the env var is absent, which hides fallback models
|
|
5
6
|
// before `/login`. Keep the provider available and resolve the real key in the
|
|
6
|
-
// Cursor provider turn path from pi auth
|
|
7
|
+
// Cursor provider turn path from pi auth or CURSOR_API_KEY.
|
|
7
8
|
export const CURSOR_API_KEY_CONFIG_VALUE = "pi-cursor-sdk-cursor-api-key-placeholder";
|
|
8
9
|
|
|
9
10
|
const CURSOR_API_KEY_PLACEHOLDERS = new Set([
|
|
@@ -19,3 +20,16 @@ export function resolveCursorApiKey(apiKey?: string): string | undefined {
|
|
|
19
20
|
if (CURSOR_API_KEY_PLACEHOLDERS.has(trimmed)) return process.env.CURSOR_API_KEY?.trim() || undefined;
|
|
20
21
|
return trimmed;
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
async function getStoredCursorApiKey(): Promise<string | undefined> {
|
|
25
|
+
try {
|
|
26
|
+
const { AuthStorage } = await import("@earendil-works/pi-coding-agent");
|
|
27
|
+
return resolveCursorApiKey(await AuthStorage.create().getApiKey(CURSOR_PROVIDER_ID, { includeFallback: false }));
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function resolveCursorRuntimeApiKey(): Promise<string | undefined> {
|
|
34
|
+
return (await getStoredCursorApiKey()) ?? resolveCursorApiKey(process.env.CURSOR_API_KEY);
|
|
35
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export const CURSOR_PI_BRIDGE_MCP_TOOL_PREFIX = "pi__";
|
|
2
|
+
export const CURSOR_PI_BRIDGE_PREFERENCE_TEXT =
|
|
3
|
+
"When exposed, prefer pi__mcp for MCP work and pi__subagent for delegation; use Cursor-configured MCP or Cursor-native subagents only when the matching pi__ tool is not exposed or unavailable.";
|
|
2
4
|
|
|
3
5
|
const CURSOR_PI_BRIDGE_CONTRACT_LINES = [
|
|
4
6
|
"Pi bridge contract:",
|
|
5
7
|
`${CURSOR_PI_BRIDGE_MCP_TOOL_PREFIX}* names are live Cursor MCP bridge tool names only when exposed in the current run.`,
|
|
6
8
|
`Call the ${CURSOR_PI_BRIDGE_MCP_TOOL_PREFIX}* MCP tool name, not the real pi tool name shown in pi history or transcripts.`,
|
|
9
|
+
CURSOR_PI_BRIDGE_PREFERENCE_TEXT,
|
|
7
10
|
"Bridged calls execute through normal pi tool flow, so pi shows the real pi tool name and returns a normal pi tool result.",
|
|
8
11
|
"Replay IDs, replay labels, and transcript tool names are display-only/context-only, not callable tools.",
|
|
9
12
|
"Cursor-native host tools, settings, plugins, and configured MCP servers are separate from the pi bridge.",
|