@rahularya01/pi-cursor 1.1.0 → 1.2.0

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.
@@ -1,69 +0,0 @@
1
- /**
2
- * Cursor agent URL resolution (env → CLI config → default).
3
- */
4
- import { readFileSync } from "node:fs";
5
- import { homedir } from "node:os";
6
- import { join as pathJoin } from "node:path";
7
- import { assertSafeCursorBaseUrl } from "../utils/security.js";
8
-
9
- export const DEFAULT_CURSOR_AGENT_URL = "https://agentn.us.api5.cursor.sh";
10
- export const DEFAULT_CURSOR_CLIENT_VERSION = "cli-2026.05.01-eea359f";
11
-
12
- let cachedCursorAgentUrl: string | undefined;
13
-
14
- export function normalizeCursorUrl(value: unknown): string | undefined {
15
- if (typeof value !== "string") return undefined;
16
- const trimmed = value.trim();
17
- if (!trimmed) return undefined;
18
- try {
19
- const url = new URL(trimmed);
20
- if (url.protocol !== "https:" && url.protocol !== "http:") return undefined;
21
- url.pathname = url.pathname.replace(/\/+$/, "");
22
- url.search = "";
23
- url.hash = "";
24
- return url.toString().replace(/\/$/, "");
25
- } catch {
26
- return undefined;
27
- }
28
- }
29
-
30
- export function readCursorCliAgentUrl(): string | undefined {
31
- const configDir = process.env.CURSOR_CONFIG_DIR?.trim() || pathJoin(homedir(), ".cursor");
32
- try {
33
- const config = JSON.parse(readFileSync(pathJoin(configDir, "cli-config.json"), "utf8")) as {
34
- serverConfigCache?: {
35
- agentUrlConfig?: { agentnUrl?: unknown; agentUrl?: unknown };
36
- };
37
- };
38
- return (
39
- normalizeCursorUrl(config.serverConfigCache?.agentUrlConfig?.agentnUrl) ??
40
- normalizeCursorUrl(config.serverConfigCache?.agentUrlConfig?.agentUrl)
41
- );
42
- } catch {
43
- return undefined;
44
- }
45
- }
46
-
47
- export function getCursorClientVersion(): string {
48
- return process.env.PI_CURSOR_CLIENT_VERSION?.trim() || DEFAULT_CURSOR_CLIENT_VERSION;
49
- }
50
-
51
- /** Resolve the agent base URL, validating host against the allowlist. */
52
- export function getCursorAgentUrl(): string {
53
- const envUrl =
54
- normalizeCursorUrl(process.env.PI_CURSOR_AGENT_URL) ??
55
- normalizeCursorUrl(process.env.CURSOR_AGENT_URL);
56
- if (envUrl) {
57
- cachedCursorAgentUrl = assertSafeCursorBaseUrl(envUrl);
58
- return cachedCursorAgentUrl;
59
- }
60
- if (cachedCursorAgentUrl) return cachedCursorAgentUrl;
61
- const resolved = readCursorCliAgentUrl() ?? DEFAULT_CURSOR_AGENT_URL;
62
- cachedCursorAgentUrl = assertSafeCursorBaseUrl(resolved);
63
- return cachedCursorAgentUrl;
64
- }
65
-
66
- /** Test helper: clear cached URL between cases. */
67
- export function resetCursorAgentUrlCacheForTests(): void {
68
- cachedCursorAgentUrl = undefined;
69
- }
@@ -1,104 +0,0 @@
1
- /**
2
- * Normalize context-mode / session side-channel user messages into the system
3
- * prompt so Cursor treats the real user turn as the task.
4
- */
5
-
6
- export type OpenAIRole = "system" | "user" | "assistant" | "tool";
7
-
8
- export interface OpenAIContentPart {
9
- type: string;
10
- text?: string;
11
- data?: string;
12
- mimeType?: string;
13
- image_url?: { url?: string };
14
- }
15
-
16
- export interface OpenAIMessage {
17
- role: OpenAIRole;
18
- content?: string | OpenAIContentPart[] | null;
19
- tool_call_id?: string;
20
- name?: string;
21
- tool_calls?: unknown[];
22
- }
23
-
24
- const CONTEXT_MODE_SIDE_CHANNEL_PRIORITY =
25
- "Provider infrastructure context only. Prioritize the user's actual request above. " +
26
- "Do not run compaction recovery, session investigation, or ctx_doctor/ctx_stats rituals " +
27
- "unless the user explicitly asked for that.";
28
-
29
- export function textContent(content: OpenAIMessage["content"]): string {
30
- if (content == null) return "";
31
- if (typeof content === "string") return content;
32
- return content
33
- .filter((p) => p.type === "text" && p.text)
34
- .map((p) => p.text as string)
35
- .join("\n");
36
- }
37
-
38
- export function contentHasImageParts(content: OpenAIMessage["content"]): boolean {
39
- if (!Array.isArray(content)) return false;
40
- return content.some(
41
- (part) =>
42
- part.type === "image_url" ||
43
- part.type === "image" ||
44
- (typeof part.mimeType === "string" && part.mimeType.startsWith("image/")),
45
- );
46
- }
47
-
48
- export function isContextModeSideChannelText(text: string): boolean {
49
- const t = text.trim();
50
- if (!t) return false;
51
- return (
52
- /^context-mode active\b/i.test(t) ||
53
- t.includes("<session_state") ||
54
- t.includes("<session_resume") ||
55
- t.includes("<active_memory>") ||
56
- t.includes("Hierarchy: ctx_batch_execute") ||
57
- /<\/?session_mode\b/i.test(t)
58
- );
59
- }
60
-
61
- export function frameContextModeSideChannel(text: string): string {
62
- return (
63
- `<provider_context source="context-mode">\n${text.trim()}\n</provider_context>\n\n` +
64
- CONTEXT_MODE_SIDE_CHANNEL_PRIORITY
65
- );
66
- }
67
-
68
- /**
69
- * Fold pure side-channel user messages into the system prompt and keep the
70
- * real user turns as the task.
71
- */
72
- export function normalizeMessagesForCursor(messages: OpenAIMessage[]): OpenAIMessage[] {
73
- const systemParts: string[] = [];
74
- const sideParts: string[] = [];
75
- const rest: OpenAIMessage[] = [];
76
-
77
- for (const msg of messages) {
78
- if (msg.role === "system") {
79
- const text = textContent(msg.content);
80
- if (text) systemParts.push(text);
81
- continue;
82
- }
83
-
84
- if (msg.role === "user") {
85
- const text = textContent(msg.content);
86
- // Keep multimodal user turns intact — only pure text side-channels move.
87
- if (isContextModeSideChannelText(text) && !contentHasImageParts(msg.content)) {
88
- sideParts.push(text);
89
- continue;
90
- }
91
- }
92
-
93
- rest.push(msg);
94
- }
95
-
96
- if (sideParts.length === 0) {
97
- if (systemParts.length === 0) return messages;
98
- return [{ role: "system", content: systemParts.join("\n") }, ...rest];
99
- }
100
-
101
- const framed = frameContextModeSideChannel(sideParts.join("\n\n"));
102
- const system = systemParts.length > 0 ? `${systemParts.join("\n")}\n\n${framed}` : framed;
103
- return [{ role: "system", content: system }, ...rest];
104
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * Public stream surface for the Cursor provider.
3
- *
4
- * The legacy OpenAI-compatible local proxy (`startProxy`) remains inside
5
- * native-core for internal/debug use but is intentionally not re-exported here.
6
- */
7
- export {
8
- createCursorNativeStream,
9
- getCursorModels,
10
- getCursorParameterizedModels,
11
- cleanupSessionState,
12
- cleanupAllSessionState,
13
- type CursorModel,
14
- type CursorNativeStreamConfig,
15
- } from "./native-core.js";
16
-
17
- export { getCursorAgentUrl, getCursorClientVersion } from "./config.js";
18
- export {
19
- resolveModelId,
20
- resolveRequestedModelId,
21
- type CursorNativeModelRouting,
22
- } from "./model-routing.js";
23
- export {
24
- isContextModeSideChannelText,
25
- normalizeMessagesForCursor,
26
- frameContextModeSideChannel,
27
- } from "./context-normalize.js";
28
- export {
29
- planRecovery,
30
- fingerprintCompletedTurns,
31
- wrapRecoveredToolResults,
32
- lostToolContinuationErrorBody,
33
- formatLostToolContinuationDiagnostic,
34
- type RecoveryDecision,
35
- type PlanRecoveryInput,
36
- type StoredConversation,
37
- } from "./recovery.js";
38
- export {
39
- enhanceCursorStreamError,
40
- isAuthErrorMessage,
41
- isProtocolMismatchMessage,
42
- } from "./protocol.js";
@@ -1,100 +0,0 @@
1
- /**
2
- * Model ID effort suffix routing for Cursor runtime variants.
3
- */
4
-
5
- export interface CursorNativeModelRouting {
6
- modelId: string;
7
- parameters?: Array<{ id: string; value: string }>;
8
- requiresMaxMode?: boolean;
9
- requestedMaxMode?: boolean;
10
- }
11
-
12
- export interface ResolvedCursorModelRouting extends CursorNativeModelRouting {
13
- maxMode: boolean;
14
- }
15
-
16
- type CursorModelRoutingByEffort = Record<string, CursorNativeModelRouting>;
17
-
18
- export interface CursorResolvableModel {
19
- id: string;
20
- [key: string]: unknown;
21
- }
22
-
23
- /**
24
- * Insert reasoning effort into model ID, before -fast/-thinking suffix.
25
- * e.g. model="gpt-5.4" + effort="medium" → "gpt-5.4-medium"
26
- * model="gpt-5.4-fast" + effort="high" → "gpt-5.4-high-fast"
27
- * If no effort provided, returns model as-is.
28
- */
29
- export function resolveModelId(model: string, reasoningEffort?: string): string {
30
- if (!reasoningEffort) return model;
31
-
32
- let suffix = "";
33
- let base = model;
34
- if (base.endsWith("-fast")) {
35
- suffix = "-fast";
36
- base = base.slice(0, -5);
37
- } else if (base.endsWith("-thinking")) {
38
- suffix = "-thinking";
39
- base = base.slice(0, -9);
40
- }
41
-
42
- return `${base}-${reasoningEffort}${suffix}`;
43
- }
44
-
45
- function isCursorModelRouting(value: unknown): value is CursorNativeModelRouting {
46
- return (
47
- !!value &&
48
- typeof value === "object" &&
49
- typeof (value as { modelId?: unknown }).modelId === "string"
50
- );
51
- }
52
-
53
- export function resolveRequestedModelId(
54
- model: string,
55
- reasoningEffort?: string,
56
- cursorModelId?: string,
57
- ): string;
58
- export function resolveRequestedModelId(
59
- model: CursorResolvableModel,
60
- reasoningEffort?: string,
61
- routingByModelId?: Map<string, CursorModelRoutingByEffort | CursorNativeModelRouting>,
62
- ): ResolvedCursorModelRouting;
63
- export function resolveRequestedModelId(
64
- model: string | CursorResolvableModel,
65
- reasoningEffort?: string,
66
- cursorModelIdOrRoutingByModelId?:
67
- string | Map<string, CursorModelRoutingByEffort | CursorNativeModelRouting>,
68
- ): string | ResolvedCursorModelRouting {
69
- if (typeof model === "string") {
70
- const trimmedCursorModelId =
71
- typeof cursorModelIdOrRoutingByModelId === "string"
72
- ? cursorModelIdOrRoutingByModelId.trim()
73
- : "";
74
- if (trimmedCursorModelId) return trimmedCursorModelId;
75
- return resolveModelId(model, reasoningEffort);
76
- }
77
-
78
- const routingByModelId =
79
- cursorModelIdOrRoutingByModelId instanceof Map ? cursorModelIdOrRoutingByModelId : undefined;
80
- const configured = routingByModelId?.get(model.id);
81
- let routing: CursorNativeModelRouting | undefined;
82
- if (isCursorModelRouting(configured)) {
83
- routing = configured;
84
- } else if (configured) {
85
- routing =
86
- configured[reasoningEffort ?? ""] ??
87
- configured.none ??
88
- configured.medium ??
89
- configured.high ??
90
- Object.values(configured).find(isCursorModelRouting);
91
- }
92
-
93
- return {
94
- modelId: routing?.modelId ?? resolveModelId(model.id, reasoningEffort),
95
- maxMode: Boolean(routing?.requestedMaxMode ?? routing?.requiresMaxMode),
96
- parameters: routing?.parameters,
97
- requestedMaxMode: routing?.requestedMaxMode,
98
- requiresMaxMode: routing?.requiresMaxMode,
99
- };
100
- }