@caeliq/llms 1.0.57 → 1.0.59

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 (75) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +82 -685
  3. package/dist/api/routes.d.ts +2 -0
  4. package/dist/cjs/server.cjs +279 -226
  5. package/dist/cjs/server.cjs.map +4 -4
  6. package/dist/cursor-sdk/hooks-template.d.ts +2 -1
  7. package/dist/cursor-sdk/host-env.d.ts +38 -0
  8. package/dist/cursor-sdk/lifecycle-planner.d.ts +69 -0
  9. package/dist/cursor-sdk/prompt.d.ts +33 -3
  10. package/dist/cursor-sdk/runner.d.ts +29 -0
  11. package/dist/cursor-sdk/session.d.ts +55 -1
  12. package/dist/cursor-sdk/shared.d.ts +14 -0
  13. package/dist/cursor-sdk/tool-paths.d.ts +26 -0
  14. package/dist/cursor-sdk/tools.d.ts +12 -1
  15. package/dist/cursor-sdk/turn-identity.d.ts +46 -0
  16. package/dist/cursor-sdk/turn-output.d.ts +77 -0
  17. package/dist/esm/server.mjs +280 -227
  18. package/dist/esm/server.mjs.map +4 -4
  19. package/dist/routing/inbound-pipeline.d.ts +31 -0
  20. package/dist/routing/protocol-adapter.d.ts +37 -0
  21. package/dist/routing/protocol-endpoints.d.ts +77 -0
  22. package/dist/routing/protocol-errors.d.ts +10 -0
  23. package/dist/server.d.ts +3 -1
  24. package/dist/services/provider.d.ts +1 -1
  25. package/dist/tests/anthropic.provider-wire.d.ts +1 -0
  26. package/dist/tests/anthropic.tool-call-id-sanitize.d.ts +1 -0
  27. package/dist/tests/anthropic.turn-intent.d.ts +1 -0
  28. package/dist/tests/claude-auth.identity.d.ts +1 -0
  29. package/dist/tests/client-abort-classification.test.d.ts +1 -0
  30. package/dist/tests/cursor-sdk.host-env.d.ts +1 -0
  31. package/dist/tests/cursor-sdk.interrupt-reentry.d.ts +1 -0
  32. package/dist/tests/cursor-sdk.lifecycle-planner.d.ts +1 -0
  33. package/dist/tests/cursor-sdk.pending-next-rejection.d.ts +1 -0
  34. package/dist/tests/cursor-sdk.scratch-paths.d.ts +1 -0
  35. package/dist/tests/cursor-sdk.scratch-report.d.ts +1 -0
  36. package/dist/tests/cursor-sdk.tool-call-id.d.ts +1 -0
  37. package/dist/tests/cursor-sdk.trailing-turn-fallback.d.ts +1 -0
  38. package/dist/tests/cursor-sdk.turn-coordination.d.ts +1 -0
  39. package/dist/tests/cursor-sdk.turn-identity.d.ts +1 -0
  40. package/dist/tests/inbound-protocol-routes.d.ts +1 -0
  41. package/dist/tests/inbound-routing-pipeline.d.ts +1 -0
  42. package/dist/tests/openai.inbound-chat.d.ts +1 -0
  43. package/dist/tests/openai.inbound-responses.d.ts +1 -0
  44. package/dist/tests/protocol-endpoints.d.ts +1 -0
  45. package/dist/tests/redact.body-for-log.d.ts +1 -0
  46. package/dist/tests/responses.call-id-sanitize.d.ts +1 -0
  47. package/dist/tests/responses.parallel-and-failure.d.ts +1 -0
  48. package/dist/tests/router-scenario-precedence.d.ts +1 -0
  49. package/dist/tests/stream.upstream-terminated.d.ts +1 -0
  50. package/dist/tests/system-instructions-fold.d.ts +1 -0
  51. package/dist/tests/upstream-error-semantics.d.ts +1 -0
  52. package/dist/transformer/anthropic.transformer.d.ts +16 -3
  53. package/dist/transformer/claude-auth.transformer.d.ts +36 -4
  54. package/dist/transformer/cursor-sdk.transformer.d.ts +4 -2
  55. package/dist/transformer/openai.responses.transformer.d.ts +17 -1
  56. package/dist/transformer/openai.transformer.d.ts +26 -32
  57. package/dist/transformer/tooluse.transformer.d.ts +1 -1
  58. package/dist/transformer/vercel.transformer.d.ts +1 -1
  59. package/dist/transformer/vertex-claude.transformer.d.ts +1 -1
  60. package/dist/transformer/vertex-gemini.transformer.d.ts +1 -1
  61. package/dist/types/transformer.d.ts +15 -0
  62. package/dist/types/turn-intent.d.ts +26 -0
  63. package/dist/utils/claude-auth.d.ts +9 -2
  64. package/dist/utils/claude-billing.d.ts +58 -0
  65. package/dist/utils/claude-model-catalog.d.ts +49 -0
  66. package/dist/utils/headers.d.ts +10 -0
  67. package/dist/utils/mistral.util.d.ts +1 -1
  68. package/dist/utils/openai.responses.util.d.ts +53 -0
  69. package/dist/utils/redact.d.ts +18 -0
  70. package/dist/utils/request.d.ts +1 -1
  71. package/dist/utils/retry.d.ts +7 -0
  72. package/dist/utils/router.d.ts +7 -0
  73. package/dist/utils/stream.d.ts +2 -0
  74. package/dist/utils/toolCallId.d.ts +23 -0
  75. package/package.json +25 -21
@@ -1 +1,2 @@
1
- export declare function ensureDenyHooksWorkspace(workspaceDir: string): void;
1
+ import { type HostEnvironment } from "./host-env";
2
+ export declare function ensureDenyHooksWorkspace(workspaceDir: string, hostEnv?: HostEnvironment): void;
@@ -0,0 +1,38 @@
1
+ import type { UnifiedChatRequest } from "../types/llm";
2
+ /**
3
+ * Facts about the machine where host tools actually execute.
4
+ *
5
+ * CCR (and therefore the Cursor SDK agent) may run in a container with no
6
+ * access to the user's filesystem, while Claude Code executes every tool call
7
+ * on the user's real machine. Cursor builds its harness prompt server-side from
8
+ * the SDK's workspace root, so the model is told — at system level — that it
9
+ * lives in the scratch workspace. These facts are the counter-evidence: they are
10
+ * lifted verbatim from the host's own environment block.
11
+ */
12
+ export type HostEnvironment = {
13
+ /** Absolute path of the host project root, when the host advertised one. */
14
+ projectRoot?: string;
15
+ /** Extra roots the host declared as in-scope. */
16
+ additionalRoots: string[];
17
+ platform?: string;
18
+ osVersion?: string;
19
+ /** Every `Key: value` pair discovered, in discovery order, verbatim. */
20
+ facts: Array<{
21
+ key: string;
22
+ value: string;
23
+ }>;
24
+ /** True when anything at all was discovered. */
25
+ known: boolean;
26
+ /** Stable id of the resolved environment; changes only when the facts change. */
27
+ fingerprint: string;
28
+ };
29
+ /** Nothing discovered — guidance falls back to conservative path wording. */
30
+ export declare const EMPTY_HOST_ENVIRONMENT: HostEnvironment;
31
+ export declare function extractHostEnvironment(request: UnifiedChatRequest): HostEnvironment;
32
+ /** Bullet lines describing the host machine, for prompt/rules injection. */
33
+ export declare function describeHostEnvironment(env: HostEnvironment): string[];
34
+ /**
35
+ * Path rule for the model. Positive and concrete when the host root is known,
36
+ * conservative when it is not — never guess a root.
37
+ */
38
+ export declare function hostPathRule(env: HostEnvironment): string;
@@ -0,0 +1,69 @@
1
+ export type CursorContextAlignment = "strict" | "unknown" | "divergent";
2
+ export type CursorParkedToolRef = {
3
+ id: string;
4
+ };
5
+ export type CursorToolResultRef = {
6
+ toolCallId: string;
7
+ };
8
+ export type ExactParkedResultMatch<TParked extends CursorParkedToolRef = CursorParkedToolRef, TResult extends CursorToolResultRef = CursorToolResultRef> = ReadonlyArray<{
9
+ parked: TParked;
10
+ result: TResult;
11
+ }>;
12
+ /**
13
+ * Pair every parked tool with exactly one result by id.
14
+ *
15
+ * Empty, missing, extra, blank, or duplicate ids are not exact matches. The
16
+ * returned pairs follow parked-tool order, and neither input array is mutated.
17
+ */
18
+ export declare function matchParkedToolResultsExactly<TParked extends CursorParkedToolRef, TResult extends CursorToolResultRef>(parkedTools: readonly TParked[], toolResults: readonly TResult[]): ExactParkedResultMatch<TParked, TResult> | undefined;
19
+ export type CursorRunSnapshot<TParked extends CursorParkedToolRef = CursorParkedToolRef> = {
20
+ kind: "idle";
21
+ }
22
+ /**
23
+ * Identical active turns must be coalesced before invoking this planner.
24
+ * This state therefore always represents a different, superseding turn.
25
+ */
26
+ | {
27
+ kind: "active-different-turn";
28
+ } | {
29
+ kind: "parked";
30
+ /** False when the prior iterator/run can no longer be continued. */
31
+ live: boolean;
32
+ tools: readonly TParked[];
33
+ };
34
+ export type CursorLifecycleInput<TParked extends CursorParkedToolRef = CursorParkedToolRef, TResult extends CursorToolResultRef = CursorToolResultRef> = {
35
+ session: {
36
+ hasSentPrompt: boolean;
37
+ poisoned: boolean;
38
+ alignment: CursorContextAlignment;
39
+ run: CursorRunSnapshot<TParked>;
40
+ };
41
+ turn: {
42
+ hasMeaningfulSteering: boolean;
43
+ toolResults: readonly TResult[];
44
+ };
45
+ };
46
+ export type CursorLifecycleRetirementReason = "poisoned-session" | "inconsistent-unused-session" | "unknown-context-alignment" | "divergent-context-alignment" | "active-different-turn" | "dead-parked-run" | "parked-turn-has-steering" | "parked-tool-results-mismatch" | "orphaned-tool-results";
47
+ export type CursorLifecycleRetirementPlan = {
48
+ action: "retire-and-replay-full";
49
+ reason: CursorLifecycleRetirementReason;
50
+ };
51
+ export type CursorLifecyclePlan<TParked extends CursorParkedToolRef = CursorParkedToolRef, TResult extends CursorToolResultRef = CursorToolResultRef> = {
52
+ action: "resume-parked";
53
+ reason: "exact-parked-tool-results";
54
+ matches: ExactParkedResultMatch<TParked, TResult>;
55
+ } | {
56
+ action: "send-full";
57
+ reason: "unused-session";
58
+ } | {
59
+ action: "send-incremental";
60
+ reason: "strictly-aligned-idle-session";
61
+ } | CursorLifecycleRetirementPlan;
62
+ /**
63
+ * Choose the only safe lifecycle action for a new, non-coalesced host turn.
64
+ *
65
+ * This function is intentionally pure. It neither resolves parked tools nor
66
+ * changes session state; the caller executes the returned plan under its
67
+ * per-session coordinator.
68
+ */
69
+ export declare function planCursorLifecycle<TParked extends CursorParkedToolRef, TResult extends CursorToolResultRef>(input: CursorLifecycleInput<TParked, TResult>): CursorLifecyclePlan<TParked, TResult>;
@@ -1,6 +1,18 @@
1
1
  import type { UnifiedChatRequest } from "../types/llm";
2
+ import { type UnifiedTurnIntent } from "../types/turn-intent";
2
3
  import type { SDKUserMessage } from "@cursor/sdk";
3
- export declare function buildBridgeSystemGuidance(request: UnifiedChatRequest, workspaceDir: string): string;
4
+ import { type HostEnvironment } from "./host-env";
5
+ export declare function buildBridgeSystemGuidance(request: UnifiedChatRequest, workspaceDir: string, hostEnv?: HostEnvironment): string;
6
+ /**
7
+ * Short restatement appended after the transcript. The guidance above is far
8
+ * from the generation point once history is flattened in; this is the last
9
+ * thing the model reads before answering.
10
+ */
11
+ export declare function buildBridgeTailReminder(workspaceDir: string, hostEnv: HostEnvironment): string;
12
+ /** Rules document for the sandbox workspace, injected by Cursor as project rules. */
13
+ export declare function buildWorkspaceRulesDocument(workspaceDir: string, hostEnv: HostEnvironment): string;
14
+ /** One-line rationale attached to Cursor built-in denials. */
15
+ export declare function buildDenyGuidance(hostEnv: HostEnvironment): string;
4
16
  /**
5
17
  * Detect a short terminal message that only announces work which never occurred.
6
18
  * Keep this deliberately narrow: it is a recovery signal, not a general quality score.
@@ -12,7 +24,7 @@ export declare function shouldContinueProgressOnlyTurn(input: {
12
24
  emittedHostTools: number;
13
25
  continuationAttempts: number;
14
26
  }): boolean;
15
- export declare function progressOnlyContinuationPrompt(): SDKUserMessage;
27
+ export declare function progressOnlyContinuationPrompt(hostEnv?: HostEnvironment): SDKUserMessage;
16
28
  /**
17
29
  * Flatten Unified chat history into a single SDK prompt for a fresh/continued send.
18
30
  * Live parked tool results are resolved via customTools.execute — not only this text.
@@ -22,8 +34,26 @@ export declare function toSdkPrompt(request: UnifiedChatRequest, options: {
22
34
  workspaceDir: string;
23
35
  /** When true, only the latest user turn (+ guidance) is sent — session already has history. */
24
36
  followUpOnly?: boolean;
37
+ hostEnv?: HostEnvironment;
38
+ /** Protocol semantics recovered before Anthropic content was flattened. */
39
+ turnIntent?: UnifiedTurnIntent;
25
40
  }): SDKUserMessage;
26
- export declare function extractTrailingToolResults(request: UnifiedChatRequest): Array<{
41
+ export type TrailingCursorToolTurn = {
42
+ toolResults: Array<{
43
+ toolCallId: string;
44
+ content: string;
45
+ isError?: boolean;
46
+ }>;
47
+ /**
48
+ * Claude Code can reject a tool and append a replacement instruction in the
49
+ * same Anthropic user block. AnthropicTransformer splits that block into
50
+ * Unified `tool` then `user` messages. Only meaningful replacement content
51
+ * counts here; Claude Code's synthetic interruption marker alone does not.
52
+ */
53
+ hasTrailingUserInput: boolean;
54
+ };
55
+ export declare function analyzeTrailingCursorToolTurn(request: UnifiedChatRequest, turnIntent?: UnifiedTurnIntent): TrailingCursorToolTurn;
56
+ export declare function extractTrailingToolResults(request: UnifiedChatRequest, turnIntent?: UnifiedTurnIntent): Array<{
27
57
  toolCallId: string;
28
58
  content: string;
29
59
  }>;
@@ -1,4 +1,7 @@
1
+ import { type SDKMessage } from "@cursor/sdk";
1
2
  import type { UnifiedChatRequest } from "../types/llm";
3
+ import type { UnifiedTurnIntent } from "../types/turn-intent";
4
+ import { type CursorSdkSession } from "./session";
2
5
  import { type CursorSdkMode } from "./shared";
3
6
  export interface CursorSdkRunnerOptions {
4
7
  cursorMode?: CursorSdkMode;
@@ -7,5 +10,31 @@ export interface CursorSdkRunnerOptions {
7
10
  sandboxEnabled?: boolean;
8
11
  abortSignal?: AbortSignal;
9
12
  logger?: any;
13
+ /** Request-local protocol semantics; never serialized into the provider body. */
14
+ turnIntent?: UnifiedTurnIntent;
15
+ /** Stable source conversation identity recovered by the protocol adapter. */
16
+ sourceSessionIdentity?: string;
10
17
  }
18
+ export declare function streamSessionEvents(session: CursorSdkSession, mode: CursorSdkMode, runToken: symbol, abortSignal?: AbortSignal): AsyncGenerator<{
19
+ kind: "sdk";
20
+ message: SDKMessage;
21
+ source: "stream" | "delta";
22
+ } | {
23
+ kind: "host_tool";
24
+ tool: {
25
+ id: string;
26
+ name: string;
27
+ args: Record<string, unknown>;
28
+ };
29
+ } | {
30
+ kind: "end";
31
+ aborted?: boolean;
32
+ }>;
33
+ /**
34
+ * Coordinate identical host retries before entering the session lifecycle.
35
+ *
36
+ * The shared turn owns the one response producer. Every caller receives an
37
+ * independent bounded replay stream, so no two HTTP responses can consume the
38
+ * same Cursor iterator.
39
+ */
11
40
  export declare function runCursor(request: UnifiedChatRequest, provider: any, context: any, options?: CursorSdkRunnerOptions): Promise<Response>;
@@ -1,5 +1,7 @@
1
1
  import { type ModelSelection, type Run, type SDKAgent, type SDKMessage } from "@cursor/sdk";
2
2
  import type { OpenAiUsage } from "./usage";
3
+ import type { CursorTranscriptCommit } from "./turn-identity";
4
+ import { type HostEnvironment } from "./host-env";
3
5
  import { type CursorSdkMode } from "./shared";
4
6
  export type ParkedTool = {
5
7
  id: string;
@@ -16,8 +18,17 @@ export type CursorSdkSession = {
16
18
  agentId: string;
17
19
  mode: CursorSdkMode;
18
20
  workspaceDir: string;
21
+ /** Host machine facts for this session, refreshed each turn. */
22
+ hostEnv: HostEnvironment;
19
23
  run?: Run;
20
24
  streamIterator?: AsyncIterator<any>;
25
+ /**
26
+ * The one outstanding `iterator.next()` owned by this SDK run. It survives
27
+ * the HTTP tool-call boundary so a continuation never starts a second
28
+ * consumer on Cursor's single-consumer iterator.
29
+ */
30
+ streamNext?: Promise<IteratorResult<any>>;
31
+ streamNextRunToken?: symbol;
21
32
  activeRunToken?: symbol;
22
33
  lastSdkUsageRaw?: OpenAiUsage;
23
34
  parked: ParkedTool[];
@@ -43,6 +54,15 @@ export type CursorSdkSession = {
43
54
  sendChain: Promise<void>;
44
55
  /** True after at least one successful `agent.send` on this session. */
45
56
  hasSentPrompt: boolean;
57
+ /**
58
+ * Exact host-visible transcript represented by this agent after the last
59
+ * completed response. Unknown means tail-only reuse is forbidden.
60
+ */
61
+ transcriptCommit?: CursorTranscriptCommit;
62
+ /** Agent/model/workspace/tool configuration paired with transcriptCommit. */
63
+ compatibilityStamp?: string;
64
+ /** Fingerprint of the host env baked into the workspace rules/deny hooks. */
65
+ guidanceFingerprint?: string;
46
66
  /**
47
67
  * Set when the local SDK handles are no longer trustworthy. The manager must
48
68
  * not hand this agent out for a future request.
@@ -53,6 +73,10 @@ export type CursorSdkSession = {
53
73
  metrics: {
54
74
  customToolCalls: number;
55
75
  builtinToolCallsSeen: number;
76
+ /** Host tool calls whose arguments pointed at the scratch workspace. */
77
+ scratchPathViolations: number;
78
+ /** Violations answered with a corrective result instead of execution. */
79
+ scratchPathCorrections: number;
56
80
  };
57
81
  parkHostTool: (tool: {
58
82
  id: string;
@@ -69,6 +93,8 @@ export type CancelActiveRunResult = {
69
93
  skipped: boolean;
70
94
  hadRun: boolean;
71
95
  hadIterator: boolean;
96
+ runCancelFailed: boolean;
97
+ iteratorReturnFailed: boolean;
72
98
  failed: boolean;
73
99
  timedOut: boolean;
74
100
  };
@@ -100,6 +126,18 @@ export declare function withSessionSendLock<T>(session: CursorSdkSession, fn: ()
100
126
  * only on supported hosts.
101
127
  */
102
128
  export declare function shouldEnableCursorSandbox(requested?: boolean): boolean;
129
+ /**
130
+ * Adopt this turn's host facts and re-stamp workspace rules when they changed.
131
+ * Cheap: files are only rewritten when their content actually differs.
132
+ *
133
+ * Cursor loads workspace rules when the agent's rules service is constructed,
134
+ * i.e. once per session — verified by appending a canary line to a live
135
+ * session's AGENTS.md, which the model did not see. So this rewrite serves the
136
+ * *next* agent created against this directory; the live turn picks up changed
137
+ * host facts through the prompt, and `session.hostEnv` (used by the scratch
138
+ * path correction) is updated here regardless.
139
+ */
140
+ export declare function refreshWorkspaceGuidance(session: CursorSdkSession, hostEnv: HostEnvironment): boolean;
103
141
  export declare function buildSessionKey(input: {
104
142
  headerSession?: string;
105
143
  metadataUserId?: string;
@@ -112,10 +150,17 @@ export declare function touchSession(session: CursorSdkSession): void;
112
150
  export declare class SessionManager {
113
151
  private logger?;
114
152
  private sessions;
153
+ /** Per-key teardown barriers so reconnects cannot overtake SDK cancellation. */
154
+ private retirements;
155
+ /** Per-key creation barriers so retirement waiters cannot create sibling agents. */
156
+ private creations;
115
157
  private cleanupTimer?;
158
+ /** Epoch 0 so the first eviction tick performs a sweep. */
159
+ private lastSweepAt;
116
160
  constructor(logger?: any | undefined);
117
161
  setLogger(logger: any): void;
118
162
  get(key: string): CursorSdkSession | undefined;
163
+ retireSession(session: CursorSdkSession, reason: string, cleanup: () => Promise<boolean>): Promise<boolean>;
119
164
  getOrCreate(options: {
120
165
  key: string;
121
166
  apiKey: string;
@@ -123,6 +168,7 @@ export declare class SessionManager {
123
168
  mode: CursorSdkMode;
124
169
  cursorCwd?: string;
125
170
  sandboxEnabled?: boolean;
171
+ hostEnv?: HostEnvironment;
126
172
  }): Promise<CursorSdkSession>;
127
173
  resume(options: {
128
174
  key: string;
@@ -132,15 +178,23 @@ export declare class SessionManager {
132
178
  mode: CursorSdkMode;
133
179
  workspaceDir: string;
134
180
  sandboxEnabled?: boolean;
181
+ hostEnv?: HostEnvironment;
135
182
  }): Promise<CursorSdkSession>;
136
183
  resolveParkedTools(session: CursorSdkSession, results: Array<{
137
184
  toolCallId: string;
138
185
  content: string;
139
186
  }>): number;
140
187
  dispose(key: string): Promise<void>;
141
- invalidate(sessionOrKey: CursorSdkSession | string, reason: string): void;
188
+ invalidate(sessionOrKey: CursorSdkSession | string, reason: string): Promise<void>;
189
+ private disposeAgent;
142
190
  private createSessionRecord;
143
191
  private evictIfNeeded;
144
192
  private evictIdle;
193
+ /**
194
+ * Remove scratch workspaces left by earlier processes. `dispose` handles the
195
+ * live case; this collects what a crash, kill, or pre-cleanup build left on
196
+ * the volume. Rate limited and restricted to managed directories.
197
+ */
198
+ sweepOrphanWorkspaces(now?: number, root?: string): number;
145
199
  }
146
200
  export declare const globalSessionManager: SessionManager;
@@ -1,3 +1,4 @@
1
+ import type { UnifiedMessage } from "../types/llm";
1
2
  export declare const CURSOR_SDK_TRANSFORMER_NAME = "cursor-sdk";
2
3
  export type CursorSdkMode = "bridge" | "plan" | "agent";
3
4
  export declare const DEFAULT_CURSOR_MODE: CursorSdkMode;
@@ -5,10 +6,23 @@ export declare const CCR_HOME: string;
5
6
  export declare const CURSOR_SDK_WORKSPACES_ROOT: string;
6
7
  export declare const SESSION_IDLE_TTL_MS: number;
7
8
  export declare const SESSION_LRU_MAX = 32;
9
+ /** Orphan scratch workspaces are swept after this long without modification. */
10
+ export declare const ORPHAN_WORKSPACE_TTL_MS: number;
11
+ /** How often the sweep may run, regardless of eviction tick frequency. */
12
+ export declare const WORKSPACE_SWEEP_INTERVAL_MS: number;
13
+ /**
14
+ * True only for a directory this module created for a session.
15
+ *
16
+ * Guards every removal: `cursorMode: "agent"` can point the session at a user
17
+ * supplied `cursorCwd`, which must never be swept.
18
+ */
19
+ export declare function isManagedWorkspacePath(dir: string, root?: string): boolean;
8
20
  /** Cursor built-ins we try to deny so Claude Code remains the tool host. */
9
21
  export declare const CURSOR_BUILTIN_DENY_LIST: readonly ["Shell", "Read", "Write", "Delete", "Grep", "Glob", "Edit", "ApplyPatch", "Task", "SemanticSearch", "SemSearch", "ReadLints", "LS", "CreatePlan", "UpdateTodos", "Await", "WebFetch", "WebSearch", "GenerateImage"];
10
22
  export declare const CUSTOM_USER_TOOLS_SERVER = "custom-user-tools";
11
23
  export declare function ensureCcrHomePaths(): string[];
24
+ /** Flatten Unified message content (string or content parts) into plain text. */
25
+ export declare function contentToText(content: UnifiedMessage["content"]): string;
12
26
  export declare function hashSessionFingerprint(parts: string[]): string;
13
27
  export declare function coerceThinkingText(value: unknown): string;
14
28
  export declare function extractEffort(request: any): string | undefined;
@@ -0,0 +1,26 @@
1
+ import { type HostEnvironment } from "./host-env";
2
+ export type ScratchPathHit = {
3
+ /** Dotted path to the offending argument, e.g. `edits.0.file_path`. */
4
+ argPath: string;
5
+ /** The offending value, truncated. */
6
+ value: string;
7
+ };
8
+ /**
9
+ * Detect host tool arguments pointing at the sandbox workspace.
10
+ *
11
+ * This is the observable symptom of the model believing it is confined to its
12
+ * own cwd. Claude Code would execute the call on the host, where that path does
13
+ * not exist (or, worse, exists as an unrelated directory).
14
+ */
15
+ export declare function findScratchPaths(args: unknown, workspaceDir: string): ScratchPathHit[];
16
+ /**
17
+ * Guard against a pathological case: if the user's project genuinely lives
18
+ * under the scratch root, every path would look like a violation.
19
+ */
20
+ export declare function scratchDetectionApplies(hostEnv: HostEnvironment): boolean;
21
+ /**
22
+ * Tool result returned in place of executing the call. Deterministic correction
23
+ * where the prompt guidance is only preventive — the model gets the topology
24
+ * again at the exact moment it acted on the wrong belief.
25
+ */
26
+ export declare function buildScratchPathCorrection(hits: ScratchPathHit[], toolName: string, workspaceDir: string, hostEnv: HostEnvironment): string;
@@ -1,4 +1,15 @@
1
1
  import type { SDKCustomTool } from "@cursor/sdk";
2
2
  import type { UnifiedChatRequest } from "../types/llm";
3
3
  import type { CursorSdkSession } from "./session";
4
- export declare function toCustomTools(request: UnifiedChatRequest, session: CursorSdkSession): Record<string, SDKCustomTool>;
4
+ /**
5
+ * Per-request tally. Session metrics are cumulative and the SDK may invoke
6
+ * `execute` as soon as `agent.send` resolves — before the response stream is
7
+ * even constructed — so a start/end delta on the session counter silently
8
+ * misses the very first violation of a turn.
9
+ */
10
+ export type TurnToolMetrics = {
11
+ scratchViolations: number;
12
+ scratchCorrections: number;
13
+ };
14
+ export declare function createTurnToolMetrics(): TurnToolMetrics;
15
+ export declare function toCustomTools(request: UnifiedChatRequest, session: CursorSdkSession, logger?: any, turn?: TurnToolMetrics): Record<string, SDKCustomTool>;
@@ -0,0 +1,46 @@
1
+ import type { UnifiedChatRequest, UnifiedMessage, UnifiedTool } from "../types/llm";
2
+ import type { UnifiedTurnIntent } from "../types/turn-intent";
3
+ import type { CursorSdkMode } from "./shared";
4
+ /**
5
+ * Fingerprint a logical model turn independently of streaming, request tracing,
6
+ * cache hints, object insertion order, and JSON argument formatting.
7
+ */
8
+ export declare function fingerprintCursorTurn(request: UnifiedChatRequest, runtime?: {
9
+ compatibilityStamp?: string;
10
+ turnIntent?: UnifiedTurnIntent;
11
+ }): string;
12
+ export type CursorCompatibilityInput = {
13
+ model: unknown;
14
+ mode: CursorSdkMode;
15
+ workspaceDir: string;
16
+ guidanceFingerprint?: string;
17
+ sandboxEnabled?: boolean;
18
+ /**
19
+ * A one-way credential/account fingerprint supplied by the caller. Never pass
20
+ * a raw API key.
21
+ */
22
+ credentialFingerprint?: string;
23
+ tools?: UnifiedTool[];
24
+ };
25
+ /**
26
+ * Fingerprint the properties that must remain compatible when an SDK agent is
27
+ * reused. The turn fingerprint separately covers prompt/tool semantics.
28
+ */
29
+ export declare function createCursorCompatibilityStamp(input: CursorCompatibilityInput): string;
30
+ export type CursorTranscriptCommit = Readonly<{
31
+ transcriptHash: string;
32
+ messageCount: number;
33
+ }>;
34
+ /**
35
+ * Commit the host-visible transcript after a successful assistant turn.
36
+ *
37
+ * The caller supplies the assistant message assembled from what was actually
38
+ * emitted to the host, rather than relying on Cursor's opaque checkpoint.
39
+ */
40
+ export declare function createCursorTranscriptCommit(request: UnifiedChatRequest, assistantMessage?: UnifiedMessage): CursorTranscriptCommit;
41
+ /**
42
+ * True only when the incoming transcript contains the exact committed prefix
43
+ * and at least one additional semantic message.
44
+ */
45
+ export declare function getStrictCursorTranscriptSuffix(commit: CursorTranscriptCommit, request: UnifiedChatRequest): readonly UnifiedMessage[] | undefined;
46
+ export declare function isStrictCursorTranscriptExtension(commit: CursorTranscriptCommit, request: UnifiedChatRequest): boolean;
@@ -0,0 +1,77 @@
1
+ declare class SharedCursorTurn {
2
+ readonly sessionKey: string;
3
+ readonly fingerprint: string;
4
+ readonly responseKind: "stream" | "json";
5
+ private readonly onTerminal;
6
+ readonly abortController: AbortController;
7
+ private phase;
8
+ private responseMetadata?;
9
+ private sourceReader?;
10
+ private chunks;
11
+ private bufferedBytes;
12
+ private replayable;
13
+ private readySettled;
14
+ private completedAt?;
15
+ private nextInterestId;
16
+ private nextSubscriberId;
17
+ private interests;
18
+ private subscribers;
19
+ private failure?;
20
+ private readyResolve;
21
+ private readyReject;
22
+ private settledResolve;
23
+ private readonly ready;
24
+ private readonly settled;
25
+ constructor(sessionKey: string, fingerprint: string, responseKind: "stream" | "json", onTerminal: (turn: SharedCursorTurn, outcome: "completed" | "failed") => void);
26
+ get canReplay(): boolean;
27
+ get replayAgeMs(): number;
28
+ get isJoinable(): boolean;
29
+ get isProducing(): boolean;
30
+ reserve(signal: AbortSignal | undefined, kind: "leader" | "join" | "replay"): CursorTurnLease;
31
+ releaseInterest(interestId: number): Promise<void> | undefined;
32
+ attach(response: Response, leaderInterestId: number, leaderSignal?: AbortSignal): Promise<Response | undefined>;
33
+ fail(error: unknown): void;
34
+ supersede(reason: string): Promise<void>;
35
+ responseFor(interestId: number, signal?: AbortSignal): Promise<Response>;
36
+ private responseForReadyInterest;
37
+ private openStream;
38
+ private pumpStarted;
39
+ private ensurePumpStarted;
40
+ private pump;
41
+ private abortIfUnobserved;
42
+ private removeSubscriber;
43
+ private closeSubscriber;
44
+ private errorSubscribers;
45
+ private releaseSourceReader;
46
+ private finishCompleted;
47
+ private finishFailed;
48
+ }
49
+ export declare class CursorTurnLease {
50
+ private readonly turn;
51
+ private readonly interestId;
52
+ private readonly signal?;
53
+ readonly kind: "leader" | "join" | "replay";
54
+ private attachedResponse?;
55
+ private removeReservationAbortListener?;
56
+ constructor(turn: SharedCursorTurn, interestId: number, signal?: AbortSignal | undefined, kind?: "leader" | "join" | "replay");
57
+ get producerSignal(): AbortSignal;
58
+ attach(response: Response): Promise<void>;
59
+ fail(error: unknown): void;
60
+ response(): Promise<Response>;
61
+ }
62
+ export declare class CursorTurnRegistry {
63
+ private sessions;
64
+ /** FIFO admission barrier; lifecycle supersession contains awaited cleanup. */
65
+ private admissionGates;
66
+ private forgetExpiredSuperseded;
67
+ private rememberSuperseded;
68
+ admit(input: {
69
+ sessionKey: string;
70
+ fingerprint: string;
71
+ responseKind: "stream" | "json";
72
+ signal?: AbortSignal;
73
+ }): Promise<CursorTurnLease>;
74
+ clear(): void;
75
+ }
76
+ export declare const globalCursorTurnRegistry: CursorTurnRegistry;
77
+ export {};