cursor-opencode-provider 0.8.0 → 0.8.3
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 +174 -0
- package/README.md +230 -44
- package/SECURITY.md +60 -12
- package/dist/activity.d.ts +73 -1
- package/dist/activity.js +231 -5
- package/dist/agent-url.js +37 -9
- package/dist/auth-renewal.d.ts +1 -1
- package/dist/auth-renewal.js +58 -26
- package/dist/billing.d.ts +105 -0
- package/dist/billing.js +212 -0
- package/dist/classic-tool-results.d.ts +1 -0
- package/dist/classic-tool-results.js +42 -0
- package/dist/context/build.d.ts +23 -4
- package/dist/context/build.js +65 -10
- package/dist/context/dynamic-catalog.js +6 -1
- package/dist/context/env.js +27 -3
- package/dist/context/epoch.js +9 -5
- package/dist/context/frozen.js +6 -2
- package/dist/context/git.d.ts +5 -2
- package/dist/context/git.js +17 -7
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +2 -0
- package/dist/context/paths.d.ts +45 -18
- package/dist/context/paths.js +72 -22
- package/dist/context/skills-bridge.d.ts +22 -0
- package/dist/context/skills-bridge.js +20 -0
- package/dist/context/skills.d.ts +34 -0
- package/dist/context/skills.js +211 -0
- package/dist/debug.js +13 -2
- package/dist/early-tool-rows.d.ts +60 -0
- package/dist/early-tool-rows.js +96 -0
- package/dist/errors.d.ts +49 -7
- package/dist/errors.js +163 -8
- package/dist/host-agent-mode.d.ts +53 -4
- package/dist/host-agent-mode.js +117 -13
- package/dist/host-interrupt.d.ts +12 -0
- package/dist/host-interrupt.js +18 -0
- package/dist/host-plan-file.d.ts +28 -0
- package/dist/host-plan-file.js +76 -0
- package/dist/host-steer.d.ts +71 -0
- package/dist/host-steer.js +232 -0
- package/dist/image-input.d.ts +18 -0
- package/dist/image-input.js +122 -30
- package/dist/image-save.d.ts +10 -2
- package/dist/image-save.js +33 -21
- package/dist/index.d.ts +2 -2
- package/dist/language-model.d.ts +149 -30
- package/dist/language-model.js +3127 -673
- package/dist/lost-request.d.ts +39 -0
- package/dist/lost-request.js +111 -0
- package/dist/model-config.d.ts +12 -1
- package/dist/model-config.js +78 -30
- package/dist/models.d.ts +26 -3
- package/dist/models.js +101 -45
- package/dist/opencode2/catalog.d.ts +1 -0
- package/dist/opencode2/catalog.js +2 -0
- package/dist/opencode2/image-save-tool.d.ts +8 -0
- package/dist/opencode2/image-save-tool.js +84 -0
- package/dist/opencode2/tool-results.d.ts +18 -0
- package/dist/opencode2/tool-results.js +66 -0
- package/dist/opencode2/types.d.ts +15 -0
- package/dist/parallel-step.d.ts +65 -0
- package/dist/parallel-step.js +102 -0
- package/dist/plugin-opencode2.js +233 -65
- package/dist/plugin.js +97 -23
- package/dist/pricing-data.d.ts +40 -3
- package/dist/pricing-data.js +40 -3
- package/dist/pricing.d.ts +1 -1
- package/dist/pricing.js +7 -4
- package/dist/process-shared.d.ts +14 -0
- package/dist/process-shared.js +18 -0
- package/dist/protocol/apply-patch.d.ts +25 -13
- package/dist/protocol/apply-patch.js +79 -30
- package/dist/protocol/ask-question.d.ts +12 -1
- package/dist/protocol/ask-question.js +77 -3
- package/dist/protocol/blob-store.d.ts +5 -1
- package/dist/protocol/blob-store.js +10 -6
- package/dist/protocol/client-version.js +6 -4
- package/dist/protocol/conversation-cache-header.d.ts +23 -0
- package/dist/protocol/conversation-cache-header.js +149 -0
- package/dist/protocol/conversation-persistence.d.ts +27 -1
- package/dist/protocol/conversation-persistence.js +628 -126
- package/dist/protocol/conversation-state.d.ts +8 -1
- package/dist/protocol/conversation-state.js +28 -7
- package/dist/protocol/create-plan.d.ts +47 -25
- package/dist/protocol/create-plan.js +33 -23
- package/dist/protocol/host-notes.d.ts +31 -0
- package/dist/protocol/host-notes.js +85 -0
- package/dist/protocol/interactions.d.ts +26 -17
- package/dist/protocol/interactions.js +80 -31
- package/dist/protocol/messages.js +164 -21
- package/dist/protocol/request.d.ts +40 -19
- package/dist/protocol/request.js +55 -28
- package/dist/protocol/sent-history-images.d.ts +49 -0
- package/dist/protocol/sent-history-images.js +102 -0
- package/dist/protocol/switch-mode.d.ts +66 -27
- package/dist/protocol/switch-mode.js +147 -73
- package/dist/protocol/token-details.d.ts +2 -0
- package/dist/protocol/token-details.js +11 -1
- package/dist/protocol/tool-call-bridge.d.ts +6 -0
- package/dist/protocol/tool-call-bridge.js +24 -0
- package/dist/protocol/tools.d.ts +90 -9
- package/dist/protocol/tools.js +543 -151
- package/dist/protocol/turn-provenance.d.ts +19 -4
- package/dist/protocol/turn-provenance.js +77 -20
- package/dist/replay-safety.d.ts +22 -4
- package/dist/replay-safety.js +86 -27
- package/dist/retry-budget.d.ts +21 -0
- package/dist/retry-budget.js +94 -0
- package/dist/runtime-files.d.ts +23 -0
- package/dist/runtime-files.js +581 -0
- package/dist/session.d.ts +129 -10
- package/dist/session.js +198 -35
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/shell-timeout.d.ts +54 -10
- package/dist/shell-timeout.js +362 -92
- package/dist/stream-write.d.ts +20 -0
- package/dist/stream-write.js +87 -0
- package/dist/terminal-runner.d.ts +15 -0
- package/dist/terminal-runner.js +528 -0
- package/dist/todo-store.d.ts +2 -0
- package/dist/todo-store.js +19 -0
- package/dist/transport/connect.d.ts +10 -1
- package/dist/transport/connect.js +124 -27
- package/dist/usage.d.ts +37 -17
- package/dist/usage.js +85 -34
- package/package.json +5 -3
- package/dist/plan-execution-kickoff.d.ts +0 -57
- package/dist/plan-execution-kickoff.js +0 -186
- package/dist/protocol/thinking.d.ts +0 -15
- package/dist/protocol/thinking.js +0 -17
package/dist/session.d.ts
CHANGED
|
@@ -3,11 +3,18 @@ import { type CursorProviderError } from "./errors.js";
|
|
|
3
3
|
import { type SessionActivitySource } from "./activity.js";
|
|
4
4
|
import type { HostSubagentCatalog, HostToolDialect, OpencodeToolDef, ToolAliasRegistry } from "./protocol/tools.js";
|
|
5
5
|
import type { CursorConversationTokenDetails } from "./protocol/token-details.js";
|
|
6
|
+
import type { HostNoteBatch } from "./protocol/host-notes.js";
|
|
7
|
+
import type { RunHistoryImages } from "./protocol/sent-history-images.js";
|
|
8
|
+
import type { ParallelStepState } from "./parallel-step.js";
|
|
9
|
+
import type { OpenCodeModelCost } from "./pricing.js";
|
|
10
|
+
import type { RunCostEstimate } from "./billing.js";
|
|
11
|
+
import type { ReplaySafetyState } from "./replay-safety.js";
|
|
6
12
|
export type Frame = {
|
|
7
13
|
flags: number;
|
|
8
14
|
payload: Uint8Array;
|
|
9
15
|
};
|
|
10
16
|
export type CursorContinuationOptions = {
|
|
17
|
+
/** A Run that owes the host nothing fails after this long without any inbound frame. */
|
|
11
18
|
semanticIdleMs?: number;
|
|
12
19
|
/** @deprecated Use semanticIdleMs. Kept as a strict alias for compatibility. */
|
|
13
20
|
softHealthMs?: number;
|
|
@@ -50,8 +57,14 @@ export type PendingExec = {
|
|
|
50
57
|
* exec result back to Cursor — just clear pending and keep pumping.
|
|
51
58
|
*/
|
|
52
59
|
bridged?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Cursor withdrew the exec (`ExecServerControlMessage.abort`). As in Cursor
|
|
62
|
+
* CLI, nothing is written back for it, but the host's late result still
|
|
63
|
+
* finds this Run so the turn goes on.
|
|
64
|
+
*/
|
|
65
|
+
aborted?: boolean;
|
|
53
66
|
};
|
|
54
|
-
export type ContinuationTerminalReason = "hard-cap-expired" | "remote-clean-close" | "remote-error" | "result-write-failed" | "ambiguous-partial-write" | "heartbeat-write-failed" | "reply-write-failed" | "process-disposed" | "superseded-by-new-run" | "open-session-cap-exceeded";
|
|
67
|
+
export type ContinuationTerminalReason = "host-interrupted" | "hard-cap-expired" | "remote-clean-close" | "remote-error" | "result-write-failed" | "ambiguous-partial-write" | "heartbeat-write-failed" | "reply-write-failed" | "process-disposed" | "superseded-by-new-run" | "open-session-cap-exceeded";
|
|
55
68
|
export type SessionCloseReason = ContinuationTerminalReason | "ordinary-cleanup" | "turn-ended" | "initial-write-failed";
|
|
56
69
|
export type ContinuationClaim = {
|
|
57
70
|
session: CursorSession;
|
|
@@ -78,7 +91,7 @@ export type DeliveryOutcome = {
|
|
|
78
91
|
} | {
|
|
79
92
|
kind: "duplicate";
|
|
80
93
|
reason: "in-flight" | "delivered";
|
|
81
|
-
framesWritten:
|
|
94
|
+
framesWritten: number;
|
|
82
95
|
} | {
|
|
83
96
|
kind: "terminal";
|
|
84
97
|
reason: ContinuationTerminalReason;
|
|
@@ -86,7 +99,7 @@ export type DeliveryOutcome = {
|
|
|
86
99
|
} | {
|
|
87
100
|
kind: "missing";
|
|
88
101
|
reason: "missing-process-local-state";
|
|
89
|
-
framesWritten:
|
|
102
|
+
framesWritten: number;
|
|
90
103
|
};
|
|
91
104
|
export type CursorSession = {
|
|
92
105
|
/**
|
|
@@ -95,6 +108,10 @@ export type CursorSession = {
|
|
|
95
108
|
* (Cursor resets them per stream) can't cross-deliver results.
|
|
96
109
|
*/
|
|
97
110
|
sessionId: string;
|
|
111
|
+
/** AgentRunRequest.run_id, for context injections bound to this Run. */
|
|
112
|
+
runId?: string;
|
|
113
|
+
/** Host notes consumed by this Run's initial user action, retained for a rebase. */
|
|
114
|
+
userTurnHostNotes?: HostNoteBatch[];
|
|
98
115
|
/**
|
|
99
116
|
* Cursor conversation_id for this Run — used to store/echo
|
|
100
117
|
* conversation_checkpoint_update (CLI parity).
|
|
@@ -104,10 +121,32 @@ export type CursorSession = {
|
|
|
104
121
|
cacheDir?: string;
|
|
105
122
|
/** Latest eligible checkpoint emitted by this Run attempt. */
|
|
106
123
|
resumeCheckpoint?: Uint8Array;
|
|
124
|
+
/**
|
|
125
|
+
* This Run sent a user message or tool result after its last checkpoint:
|
|
126
|
+
* recovery must not resume earlier state that lacks the delivered input.
|
|
127
|
+
*/
|
|
128
|
+
checkpointRevoked?: boolean;
|
|
129
|
+
/** Only a delivered host result, rather than an unrepresented follow-up, revoked it. */
|
|
130
|
+
checkpointRevokedByHostResult?: boolean;
|
|
131
|
+
/** Ignore queued checkpoints received before the latest message/result delivery. */
|
|
132
|
+
resumeCheckpointAfterFrame?: number;
|
|
133
|
+
/** Stateful replay barriers survive host tool-step boundaries on this Run. */
|
|
134
|
+
replaySafety?: ReplaySafetyState;
|
|
135
|
+
/** History images this Run's checkpoints hold or may hold; absent for a Run outside an OpenCode session. */
|
|
136
|
+
historyImages?: RunHistoryImages;
|
|
107
137
|
/** Last checkpoint-derived context snapshot, independent of retry eligibility. */
|
|
108
138
|
tokenDetails?: CursorConversationTokenDetails;
|
|
109
139
|
/** True only after this Run receives a checkpoint containing token details. */
|
|
110
140
|
tokenDetailsFresh?: boolean;
|
|
141
|
+
/** Cost reporting for this Run's steps (`src/billing.ts`). */
|
|
142
|
+
billing: RunCostEstimate & {
|
|
143
|
+
/** Ledger key: the OpenCode session, or this Run for an ephemeral one. */
|
|
144
|
+
key: string;
|
|
145
|
+
/** Catalog cost entry; absent for a model Cursor publishes no rate for. */
|
|
146
|
+
cost?: OpenCodeModelCost;
|
|
147
|
+
/** Context the previous billed step already sent: read from cache on the next one. */
|
|
148
|
+
prefixTokens: number;
|
|
149
|
+
};
|
|
111
150
|
/**
|
|
112
151
|
* Run-lifetime cache evidence. Cursor exposes only aggregate cache counters at
|
|
113
152
|
* TurnEnded, so retain the inputs and protocol activity needed to explain
|
|
@@ -121,6 +160,8 @@ export type CursorSession = {
|
|
|
121
160
|
modelId?: string;
|
|
122
161
|
priorTokenDetails?: CursorConversationTokenDetails;
|
|
123
162
|
startedWithCheckpoint: boolean;
|
|
163
|
+
/** Why a Run started without a checkpoint: a reset reason, `ephemeral`, or `no-checkpoint`. */
|
|
164
|
+
coldReason?: string;
|
|
124
165
|
requestContextReused: boolean;
|
|
125
166
|
requestContextHash: string;
|
|
126
167
|
systemPromptHash?: string;
|
|
@@ -136,6 +177,8 @@ export type CursorSession = {
|
|
|
136
177
|
};
|
|
137
178
|
/** OpenCode session whose own or descendant activity renews tool leases. */
|
|
138
179
|
openCodeSessionId?: string;
|
|
180
|
+
/** OpenCode session whose Stop cancels this Run, also for a helper isolated from it. */
|
|
181
|
+
stoppedWithSessionId?: string;
|
|
139
182
|
/** Host primary agent whose prompt/permissions this Run was seeded with. */
|
|
140
183
|
hostAgent?: string;
|
|
141
184
|
/** Stable host-system + provider-guidance identity for restart validation. */
|
|
@@ -152,8 +195,30 @@ export type CursorSession = {
|
|
|
152
195
|
toolCatalog?: OpencodeToolDef[];
|
|
153
196
|
/** Configured MCP server ids used to split flattened host tool names. */
|
|
154
197
|
knownMcpServers?: string[];
|
|
198
|
+
/** The Run's model accepts images, so held-Run exec results may carry them. */
|
|
199
|
+
supportsImages?: boolean;
|
|
155
200
|
stream: BidiStream;
|
|
156
201
|
frames: AsyncIterator<Frame>;
|
|
202
|
+
/** An iterator read retained across an idle-guard timeout. */
|
|
203
|
+
pendingFrameRead?: Promise<IteratorResult<Frame>>;
|
|
204
|
+
/**
|
|
205
|
+
* One-slot pushback for a terminal update that must start the next pump pass
|
|
206
|
+
* after the host settles emitted tools. Timed-out reads use pendingFrameRead.
|
|
207
|
+
*/
|
|
208
|
+
pushbackFrame?: Frame;
|
|
209
|
+
/**
|
|
210
|
+
* Frames the held-Run reader took while the host ran tools and the next pump
|
|
211
|
+
* must see, in arrival order. They follow `pushbackFrame` and precede any
|
|
212
|
+
* unread frame.
|
|
213
|
+
*/
|
|
214
|
+
heldFrames?: Frame[];
|
|
215
|
+
/**
|
|
216
|
+
* In-progress parallel tool-call step (field 27 hold). Cleared when the step
|
|
217
|
+
* finishes; added to priorParallelStepCallIds for late-completion filtering.
|
|
218
|
+
*/
|
|
219
|
+
parallelStep?: ParallelStepState;
|
|
220
|
+
/** Resolved call ids from prior steps of this Run — ignore late completions. */
|
|
221
|
+
priorParallelStepCallIds?: Set<string>;
|
|
157
222
|
pending: Map<number, PendingExec>;
|
|
158
223
|
/**
|
|
159
224
|
* Cursor display tool calls (tool_call_started) awaiting either an exec or a
|
|
@@ -167,6 +232,11 @@ export type CursorSession = {
|
|
|
167
232
|
* completed list so host todos still receive a replace-all snapshot.
|
|
168
233
|
*/
|
|
169
234
|
mirroredTodos?: Array<Record<string, unknown>>;
|
|
235
|
+
/**
|
|
236
|
+
* CreatePlan display calls whose plan was deferred to the host plan agent.
|
|
237
|
+
* Their completed display carries no recorded plan, so it is not mirrored.
|
|
238
|
+
*/
|
|
239
|
+
deferredCreatePlanCalls?: Set<string>;
|
|
170
240
|
/**
|
|
171
241
|
* Legacy edit calls whose authoritative exec path is still in progress.
|
|
172
242
|
* Cursor implements these as read -> whole-file write; retaining the path
|
|
@@ -231,13 +301,18 @@ export type CursorSession = {
|
|
|
231
301
|
deferredTerminalReason: "remote-clean-close" | "remote-error" | null;
|
|
232
302
|
policy: CursorContinuationPolicy;
|
|
233
303
|
createdAt: number;
|
|
304
|
+
/** When the host's model call that opened this Run started; it may open the Run after a stop. */
|
|
305
|
+
requestedAt?: number;
|
|
234
306
|
lastInboundAt: number;
|
|
235
307
|
lastHeartbeatWriteAt: number;
|
|
236
308
|
semanticDeadlineAt: number;
|
|
237
309
|
closeError: CursorProviderError | null;
|
|
238
310
|
closed: boolean;
|
|
239
|
-
|
|
311
|
+
/** The host stopped the turn (OpenCode 2 interrupt); the Run is being cancelled and must not be continued. */
|
|
312
|
+
hostInterrupted?: string;
|
|
313
|
+
reopenWithUserMessage?: (text: string, signal?: AbortSignal) => Promise<void>;
|
|
240
314
|
};
|
|
315
|
+
export declare function stoppedWith(session: CursorSession): string | undefined;
|
|
241
316
|
type SessionManagerOptions = {
|
|
242
317
|
now?: () => number;
|
|
243
318
|
setTimer?: (callback: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
|
|
@@ -248,10 +323,14 @@ type SessionManagerOptions = {
|
|
|
248
323
|
maxOpenSessions?: number;
|
|
249
324
|
};
|
|
250
325
|
export declare class SessionManager {
|
|
326
|
+
/** A claimed result has one writer even while that writer awaits drain. */
|
|
327
|
+
private readonly delivering;
|
|
251
328
|
private byExecId;
|
|
252
329
|
private sessions;
|
|
253
330
|
private byOpenCodeSessionId;
|
|
254
331
|
private tombstones;
|
|
332
|
+
/** Early-row tool call id → the exec that bound it. Bounded like the tombstones. */
|
|
333
|
+
private toolCallRoutes;
|
|
255
334
|
private readonly now;
|
|
256
335
|
private readonly setTimer;
|
|
257
336
|
private readonly clearTimer;
|
|
@@ -264,12 +343,24 @@ export declare class SessionManager {
|
|
|
264
343
|
/** Live open Run for this OpenCode session id, if any. */
|
|
265
344
|
findOpenByOpenCodeSessionId(openCodeSessionId: string | undefined): CursorSession | undefined;
|
|
266
345
|
isActivelyPumping(session: CursorSession): boolean;
|
|
346
|
+
/** Every open Run a Stop of this OpenCode session cancels, including one a newer Run did not supersede. */
|
|
347
|
+
openSessionsStoppedWith(openCodeSessionId: string | undefined): CursorSession[];
|
|
348
|
+
/**
|
|
349
|
+
* Forget every result the host still owed this Run, without writing any: the
|
|
350
|
+
* host stopped the turn, and the Run is about to be cancelled.
|
|
351
|
+
*
|
|
352
|
+
* @returns number of pendings dropped
|
|
353
|
+
*/
|
|
354
|
+
abandonPending(session: CursorSession): number;
|
|
355
|
+
/** Cursor withdrew this exec; returns false when it is not one the host still owes. */
|
|
356
|
+
markExecAborted(session: CursorSession, execId: number): boolean;
|
|
267
357
|
/**
|
|
268
|
-
* Clear
|
|
269
|
-
* Used when the host starts a fresh user turn
|
|
270
|
-
*
|
|
358
|
+
* Clear pendings that do not require a Cursor exec write: display-only bridged
|
|
359
|
+
* ones and execs Cursor aborted. Used when the host starts a fresh user turn
|
|
360
|
+
* instead of returning the result (e.g. human `continue` while a todowrite
|
|
361
|
+
* mirror is outstanding).
|
|
271
362
|
*
|
|
272
|
-
* @returns number of
|
|
363
|
+
* @returns number of pendings settled
|
|
273
364
|
*/
|
|
274
365
|
settleBridgedPending(session: CursorSession): number;
|
|
275
366
|
private isPumping;
|
|
@@ -289,7 +380,9 @@ export declare class SessionManager {
|
|
|
289
380
|
pendingFor(sessionId: string, execId: number): PendingExec | undefined;
|
|
290
381
|
classify(sessionId: string, execId: number): ContinuationClassification;
|
|
291
382
|
claim(sessionId: string, execId: number): ContinuationClaim | ContinuationClassification;
|
|
292
|
-
deliverClaim(claim: ContinuationClaim, frames: readonly Uint8Array[]
|
|
383
|
+
deliverClaim(claim: ContinuationClaim, frames: readonly Uint8Array[],
|
|
384
|
+
/** Called at each actual serialized write, before write() and its drain. */
|
|
385
|
+
beforeWrite?: () => void): Promise<DeliveryOutcome>;
|
|
293
386
|
/** Find the live session awaiting one of the given exec ids. */
|
|
294
387
|
findByExecIds(sessionId: string, execIds: number[]): CursorSession | undefined;
|
|
295
388
|
/** Mark an exec id as resolved (its result has been delivered). */
|
|
@@ -302,7 +395,7 @@ export declare class SessionManager {
|
|
|
302
395
|
* Callers must cancel the session heartbeat and wait for the old stream's
|
|
303
396
|
* write chain before this, so an in-flight heartbeat cannot close the session.
|
|
304
397
|
*/
|
|
305
|
-
replaceStream(session: CursorSession, next: BidiStream): void;
|
|
398
|
+
replaceStream(session: CursorSession, next: BidiStream, runId?: string): void;
|
|
306
399
|
private subscribeTerminal;
|
|
307
400
|
private key;
|
|
308
401
|
close(session: CursorSession, reason?: SessionCloseReason, error?: CursorProviderError): void;
|
|
@@ -318,6 +411,22 @@ export declare class SessionManager {
|
|
|
318
411
|
dispose(): void;
|
|
319
412
|
sweepHardDeadlines(): void;
|
|
320
413
|
private onStreamTerminal;
|
|
414
|
+
/**
|
|
415
|
+
* The host tool-call id a continuation will send for this exec, when it is
|
|
416
|
+
* not `cursor_<session>_<execId>` (an early row bound to the real exec).
|
|
417
|
+
* The route outlives its Run, like a tombstone, so a result that arrives
|
|
418
|
+
* after the Run closed still names its exec and rebuilds from host history.
|
|
419
|
+
*/
|
|
420
|
+
bindToolCall(toolCallId: string, sessionId: string, execId: number): void;
|
|
421
|
+
toolCallExec(toolCallId: string): {
|
|
422
|
+
sessionId: string;
|
|
423
|
+
execId: number;
|
|
424
|
+
} | undefined;
|
|
425
|
+
/**
|
|
426
|
+
* Leases only grow. Session activity renews from its own time; a running
|
|
427
|
+
* host tool renews from now, but never past that tool's own ceiling. The
|
|
428
|
+
* ceiling limits the tool's extension, not the lease activity already gave.
|
|
429
|
+
*/
|
|
321
430
|
private refreshHardDeadline;
|
|
322
431
|
private hardDeadlineExpired;
|
|
323
432
|
private scheduleHardDeadline;
|
|
@@ -325,5 +434,15 @@ export declare class SessionManager {
|
|
|
325
434
|
private putTombstone;
|
|
326
435
|
private isTerminalReason;
|
|
327
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Read one frame, retaining ownership of the iterator read when a timeout
|
|
439
|
+
* wins. Every reader uses this function so a continuation cannot overtake a
|
|
440
|
+
* late frame (or discard its EOF/error).
|
|
441
|
+
*/
|
|
442
|
+
export declare function readSessionFrame(session: CursorSession): Promise<IteratorResult<Frame>>;
|
|
443
|
+
export declare function readSessionFrame(session: CursorSession, timeoutMs: number): Promise<IteratorResult<Frame> | {
|
|
444
|
+
done: true;
|
|
445
|
+
timedOut: true;
|
|
446
|
+
}>;
|
|
328
447
|
export declare const sessionManager: SessionManager;
|
|
329
448
|
export {};
|
package/dist/session.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { trace } from "./debug.js";
|
|
2
|
+
import { releaseUnstartedTerminal } from "./terminal-runner.js";
|
|
2
3
|
import { CursorProtocolError } from "./errors.js";
|
|
3
|
-
import {
|
|
4
|
+
import { StreamWriteDrainError, writeStreamMessage } from "./stream-write.js";
|
|
5
|
+
import { RUNNING_TOOL_CAP_MS, sessionActivity } from "./activity.js";
|
|
4
6
|
export const DEFAULT_CONTINUATION_POLICY = {
|
|
5
|
-
|
|
7
|
+
// Cursor CLI aborts an active attempt after 30 s without an inbound frame.
|
|
8
|
+
semanticIdleMs: 30_000,
|
|
6
9
|
hardCapMs: 600_000,
|
|
7
10
|
heartbeatMs: 5_000,
|
|
8
11
|
};
|
|
@@ -33,13 +36,15 @@ export function resolveContinuationPolicy(options) {
|
|
|
33
36
|
throw new CursorProtocolError(`Unknown Cursor continuation option: ${key}`);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
// Read the deprecated alias once, without the deprecated typed accessor.
|
|
40
|
+
const legacySemanticIdleMs = options?.softHealthMs;
|
|
36
41
|
if (options?.semanticIdleMs !== undefined &&
|
|
37
|
-
|
|
38
|
-
options.semanticIdleMs !==
|
|
42
|
+
legacySemanticIdleMs !== undefined &&
|
|
43
|
+
options.semanticIdleMs !== legacySemanticIdleMs) {
|
|
39
44
|
throw new CursorProtocolError("Cursor continuation semanticIdleMs and deprecated softHealthMs must match when both are set");
|
|
40
45
|
}
|
|
41
46
|
const heartbeatMs = positiveInteger("heartbeatMs", options?.heartbeatMs, DEFAULT_CONTINUATION_POLICY.heartbeatMs);
|
|
42
|
-
const semanticIdleMs = positiveInteger("semanticIdleMs", options?.semanticIdleMs ??
|
|
47
|
+
const semanticIdleMs = positiveInteger("semanticIdleMs", options?.semanticIdleMs ?? legacySemanticIdleMs, DEFAULT_CONTINUATION_POLICY.semanticIdleMs);
|
|
43
48
|
const hardCapMs = positiveInteger("hardCapMs", options?.hardCapMs, DEFAULT_CONTINUATION_POLICY.hardCapMs);
|
|
44
49
|
if (heartbeatMs >= semanticIdleMs) {
|
|
45
50
|
throw new CursorProtocolError("Cursor continuation heartbeatMs must be less than semanticIdleMs");
|
|
@@ -49,7 +54,12 @@ export function resolveContinuationPolicy(options) {
|
|
|
49
54
|
}
|
|
50
55
|
return { heartbeatMs, semanticIdleMs, hardCapMs };
|
|
51
56
|
}
|
|
57
|
+
export function stoppedWith(session) {
|
|
58
|
+
return session.stoppedWithSessionId ?? session.openCodeSessionId;
|
|
59
|
+
}
|
|
52
60
|
export class SessionManager {
|
|
61
|
+
/** A claimed result has one writer even while that writer awaits drain. */
|
|
62
|
+
delivering = new WeakSet();
|
|
53
63
|
// Composite key `${sessionId}:${execId}` → owning session. Composite keying
|
|
54
64
|
// means two Run streams that both register an execId of 1 (Cursor resets
|
|
55
65
|
// counters per stream) coexist instead of overwriting each other.
|
|
@@ -60,6 +70,8 @@ export class SessionManager {
|
|
|
60
70
|
// of continuing the held-open one — the prior entry is stale and superseded.
|
|
61
71
|
byOpenCodeSessionId = new Map();
|
|
62
72
|
tombstones = new Map();
|
|
73
|
+
/** Early-row tool call id → the exec that bound it. Bounded like the tombstones. */
|
|
74
|
+
toolCallRoutes = new Map();
|
|
63
75
|
now;
|
|
64
76
|
setTimer;
|
|
65
77
|
clearTimer;
|
|
@@ -146,19 +158,54 @@ export class SessionManager {
|
|
|
146
158
|
isActivelyPumping(session) {
|
|
147
159
|
return this.isPumping(session);
|
|
148
160
|
}
|
|
161
|
+
/** Every open Run a Stop of this OpenCode session cancels, including one a newer Run did not supersede. */
|
|
162
|
+
openSessionsStoppedWith(openCodeSessionId) {
|
|
163
|
+
return [...this.sessions].filter((session) => !session.closed && (openCodeSessionId === undefined || stoppedWith(session) === openCodeSessionId));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Forget every result the host still owed this Run, without writing any: the
|
|
167
|
+
* host stopped the turn, and the Run is about to be cancelled.
|
|
168
|
+
*
|
|
169
|
+
* @returns number of pendings dropped
|
|
170
|
+
*/
|
|
171
|
+
abandonPending(session) {
|
|
172
|
+
if (session.closed)
|
|
173
|
+
return 0;
|
|
174
|
+
const dropped = session.pending.size;
|
|
175
|
+
for (const [execId, pending] of session.pending) {
|
|
176
|
+
const directory = pending.resultMetadata?.managed_job_directory;
|
|
177
|
+
if (typeof directory === "string")
|
|
178
|
+
releaseUnstartedTerminal(directory);
|
|
179
|
+
const key = this.key(session.sessionId, execId);
|
|
180
|
+
this.byExecId.delete(key);
|
|
181
|
+
this.putTombstone(key, "host-interrupted");
|
|
182
|
+
}
|
|
183
|
+
session.pending.clear();
|
|
184
|
+
this.scheduleHardDeadline(session);
|
|
185
|
+
return dropped;
|
|
186
|
+
}
|
|
187
|
+
/** Cursor withdrew this exec; returns false when it is not one the host still owes. */
|
|
188
|
+
markExecAborted(session, execId) {
|
|
189
|
+
const pending = session.pending.get(execId);
|
|
190
|
+
if (session.closed || !pending || pending.bridged || pending.aborted || pending.state !== "pending")
|
|
191
|
+
return false;
|
|
192
|
+
pending.aborted = true;
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
149
195
|
/**
|
|
150
|
-
* Clear
|
|
151
|
-
* Used when the host starts a fresh user turn
|
|
152
|
-
*
|
|
196
|
+
* Clear pendings that do not require a Cursor exec write: display-only bridged
|
|
197
|
+
* ones and execs Cursor aborted. Used when the host starts a fresh user turn
|
|
198
|
+
* instead of returning the result (e.g. human `continue` while a todowrite
|
|
199
|
+
* mirror is outstanding).
|
|
153
200
|
*
|
|
154
|
-
* @returns number of
|
|
201
|
+
* @returns number of pendings settled
|
|
155
202
|
*/
|
|
156
203
|
settleBridgedPending(session) {
|
|
157
204
|
if (session.closed)
|
|
158
205
|
return 0;
|
|
159
206
|
let settled = 0;
|
|
160
207
|
for (const [execId, pending] of [...session.pending.entries()]) {
|
|
161
|
-
if (!pending.bridged)
|
|
208
|
+
if (!pending.bridged && !pending.aborted)
|
|
162
209
|
continue;
|
|
163
210
|
if (pending.state === "claimed")
|
|
164
211
|
continue;
|
|
@@ -192,8 +239,9 @@ export class SessionManager {
|
|
|
192
239
|
if (candidate === justRegistered || this.isPumping(candidate))
|
|
193
240
|
continue;
|
|
194
241
|
// Held-open Runs with pending tools must not be cap-evicted — that is
|
|
195
|
-
// mid-exec supersede by another name and throws away the prefix.
|
|
196
|
-
|
|
242
|
+
// mid-exec supersede by another name and throws away the prefix. An
|
|
243
|
+
// exec Cursor aborted no longer waits on the host, so it does not hold.
|
|
244
|
+
if ([...candidate.pending.values()].some((pending) => !pending.aborted))
|
|
197
245
|
continue;
|
|
198
246
|
if (!oldest || candidate.createdAt < oldest.createdAt)
|
|
199
247
|
oldest = candidate;
|
|
@@ -291,20 +339,25 @@ export class SessionManager {
|
|
|
291
339
|
pending: classification.pending,
|
|
292
340
|
};
|
|
293
341
|
}
|
|
294
|
-
deliverClaim(claim, frames
|
|
342
|
+
async deliverClaim(claim, frames,
|
|
343
|
+
/** Called at each actual serialized write, before write() and its drain. */
|
|
344
|
+
beforeWrite) {
|
|
295
345
|
const { session, execId, pending } = claim;
|
|
296
346
|
const key = this.key(session.sessionId, execId);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
347
|
+
const unavailable = (framesWritten) => {
|
|
348
|
+
if (!session.closed &&
|
|
349
|
+
this.byExecId.get(key) === session &&
|
|
350
|
+
session.pending.get(execId) === pending)
|
|
351
|
+
return undefined;
|
|
300
352
|
const current = this.classify(session.sessionId, execId);
|
|
301
|
-
if (current.kind === "duplicate")
|
|
302
|
-
return { ...current, framesWritten
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
353
|
+
if (current.kind === "duplicate" || current.kind === "terminal")
|
|
354
|
+
return { ...current, framesWritten };
|
|
355
|
+
return { kind: "missing", reason: "missing-process-local-state", framesWritten };
|
|
356
|
+
};
|
|
357
|
+
const invalid = unavailable(0);
|
|
358
|
+
if (invalid)
|
|
359
|
+
return invalid;
|
|
360
|
+
if (pending.state !== "claimed" || this.delivering.has(pending)) {
|
|
308
361
|
return {
|
|
309
362
|
kind: "duplicate",
|
|
310
363
|
reason: pending.state === "delivered" ? "delivered" : "in-flight",
|
|
@@ -315,17 +368,37 @@ export class SessionManager {
|
|
|
315
368
|
this.close(session, "hard-cap-expired");
|
|
316
369
|
return { kind: "terminal", reason: "hard-cap-expired", framesWritten: 0 };
|
|
317
370
|
}
|
|
371
|
+
this.delivering.add(pending);
|
|
318
372
|
let framesWritten = 0;
|
|
319
373
|
try {
|
|
320
|
-
if (!pending.bridged && frames.length === 0) {
|
|
374
|
+
if (!pending.bridged && !pending.aborted && frames.length === 0) {
|
|
321
375
|
throw new CursorProtocolError("No result frames were produced");
|
|
322
376
|
}
|
|
323
377
|
for (const frame of frames) {
|
|
324
|
-
|
|
378
|
+
try {
|
|
379
|
+
await writeStreamMessage(session.stream, frame, "continuation result", beforeWrite ? () => {
|
|
380
|
+
beforeWrite();
|
|
381
|
+
return true;
|
|
382
|
+
} : undefined);
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
// write() returned false before the drain failed: the bytes are buffered.
|
|
386
|
+
if (error instanceof StreamWriteDrainError)
|
|
387
|
+
framesWritten++;
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
325
390
|
framesWritten++;
|
|
391
|
+
// The Run can close or replace this pending exec while drain is awaited.
|
|
392
|
+
// Never send its remaining frames or apply approval/state to that owner.
|
|
393
|
+
const invalid = unavailable(framesWritten);
|
|
394
|
+
if (invalid)
|
|
395
|
+
return invalid;
|
|
326
396
|
}
|
|
327
397
|
}
|
|
328
398
|
catch {
|
|
399
|
+
const invalid = unavailable(framesWritten);
|
|
400
|
+
if (invalid)
|
|
401
|
+
return invalid;
|
|
329
402
|
const reason = framesWritten === 0 ? "result-write-failed" : "ambiguous-partial-write";
|
|
330
403
|
this.close(session, reason);
|
|
331
404
|
return { kind: "terminal", reason, framesWritten };
|
|
@@ -377,7 +450,8 @@ export class SessionManager {
|
|
|
377
450
|
session.pumpActive = false;
|
|
378
451
|
const deferred = session.deferredTerminalReason;
|
|
379
452
|
session.deferredTerminalReason = null;
|
|
380
|
-
|
|
453
|
+
// A Run the host stopped is closed by its cancel, after it has read what Cursor sent last.
|
|
454
|
+
if (deferred && !session.hostInterrupted)
|
|
381
455
|
this.close(session, deferred);
|
|
382
456
|
return true;
|
|
383
457
|
}
|
|
@@ -386,7 +460,7 @@ export class SessionManager {
|
|
|
386
460
|
* Callers must cancel the session heartbeat and wait for the old stream's
|
|
387
461
|
* write chain before this, so an in-flight heartbeat cannot close the session.
|
|
388
462
|
*/
|
|
389
|
-
replaceStream(session, next) {
|
|
463
|
+
replaceStream(session, next, runId) {
|
|
390
464
|
if (session.closed)
|
|
391
465
|
throw new CursorProtocolError("Cannot replace stream on a closed Cursor session");
|
|
392
466
|
session.heartbeatCancel?.();
|
|
@@ -395,7 +469,17 @@ export class SessionManager {
|
|
|
395
469
|
session.deferredTerminalReason = null;
|
|
396
470
|
const old = session.stream;
|
|
397
471
|
session.stream = next;
|
|
472
|
+
// Preserve session totals but give the replacement Run its own estimates.
|
|
473
|
+
session.billing.estimatedUsd = 0;
|
|
474
|
+
delete session.billing.ledgerEpoch;
|
|
475
|
+
if (runId !== undefined)
|
|
476
|
+
session.runId = runId;
|
|
398
477
|
session.frames = next.frames()[Symbol.asyncIterator]();
|
|
478
|
+
session.pendingFrameRead = undefined;
|
|
479
|
+
session.pushbackFrame = undefined;
|
|
480
|
+
session.heldFrames = undefined;
|
|
481
|
+
session.parallelStep = undefined;
|
|
482
|
+
session.priorParallelStepCallIds = undefined;
|
|
399
483
|
this.subscribeTerminal(session);
|
|
400
484
|
try {
|
|
401
485
|
old.destroy();
|
|
@@ -437,13 +521,21 @@ export class SessionManager {
|
|
|
437
521
|
session.terminalUnsubscribe?.();
|
|
438
522
|
session.terminalUnsubscribe = null;
|
|
439
523
|
session.deferredTerminalReason = null;
|
|
440
|
-
for (const id of session.pending
|
|
524
|
+
for (const [id, pending] of session.pending) {
|
|
525
|
+
const directory = pending.resultMetadata?.managed_job_directory;
|
|
526
|
+
if (typeof directory === "string")
|
|
527
|
+
releaseUnstartedTerminal(directory);
|
|
441
528
|
const key = this.key(session.sessionId, id);
|
|
442
529
|
this.byExecId.delete(key);
|
|
443
530
|
if (this.isTerminalReason(reason))
|
|
444
531
|
this.putTombstone(key, reason);
|
|
445
532
|
}
|
|
446
533
|
session.pending.clear();
|
|
534
|
+
session.pendingFrameRead = undefined;
|
|
535
|
+
session.pushbackFrame = undefined;
|
|
536
|
+
session.heldFrames = undefined;
|
|
537
|
+
session.parallelStep = undefined;
|
|
538
|
+
session.priorParallelStepCallIds = undefined;
|
|
447
539
|
session.pumpOwner = null;
|
|
448
540
|
session.pumpActive = false;
|
|
449
541
|
session.displayToolCalls?.clear();
|
|
@@ -482,6 +574,7 @@ export class SessionManager {
|
|
|
482
574
|
for (const session of [...this.sessions])
|
|
483
575
|
this.close(session, "process-disposed");
|
|
484
576
|
this.byExecId.clear();
|
|
577
|
+
this.toolCallRoutes.clear();
|
|
485
578
|
}
|
|
486
579
|
sweepHardDeadlines() {
|
|
487
580
|
for (const session of [...this.sessions]) {
|
|
@@ -497,22 +590,55 @@ export class SessionManager {
|
|
|
497
590
|
const reason = event.kind === "remote-error" ? "remote-error" : "remote-clean-close";
|
|
498
591
|
if (event.kind === "remote-error")
|
|
499
592
|
session.closeError = event.error;
|
|
500
|
-
if (session.pumpOwner !== null) {
|
|
593
|
+
if (session.pumpOwner !== null || session.hostInterrupted) {
|
|
501
594
|
session.deferredTerminalReason = reason;
|
|
502
595
|
return;
|
|
503
596
|
}
|
|
504
597
|
this.close(session, reason, event.kind === "remote-error" ? event.error : undefined);
|
|
505
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* The host tool-call id a continuation will send for this exec, when it is
|
|
601
|
+
* not `cursor_<session>_<execId>` (an early row bound to the real exec).
|
|
602
|
+
* The route outlives its Run, like a tombstone, so a result that arrives
|
|
603
|
+
* after the Run closed still names its exec and rebuilds from host history.
|
|
604
|
+
*/
|
|
605
|
+
bindToolCall(toolCallId, sessionId, execId) {
|
|
606
|
+
this.toolCallRoutes.delete(toolCallId);
|
|
607
|
+
this.toolCallRoutes.set(toolCallId, { sessionId, execId });
|
|
608
|
+
while (this.toolCallRoutes.size > this.tombstoneLimit) {
|
|
609
|
+
const oldest = this.toolCallRoutes.keys().next().value;
|
|
610
|
+
if (oldest === undefined)
|
|
611
|
+
break;
|
|
612
|
+
this.toolCallRoutes.delete(oldest);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
toolCallExec(toolCallId) {
|
|
616
|
+
return this.toolCallRoutes.get(toolCallId);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Leases only grow. Session activity renews from its own time; a running
|
|
620
|
+
* host tool renews from now, but never past that tool's own ceiling. The
|
|
621
|
+
* ceiling limits the tool's extension, not the lease activity already gave.
|
|
622
|
+
*/
|
|
506
623
|
refreshHardDeadline(session, pending) {
|
|
507
624
|
if (!session.openCodeSessionId)
|
|
508
625
|
return pending.hardDeadlineAt;
|
|
626
|
+
const now = this.now();
|
|
627
|
+
const toolCapAt = this.activitySource.runningToolDeadline?.(session.openCodeSessionId, now, RUNNING_TOOL_CAP_MS);
|
|
509
628
|
const activityAt = this.activitySource.lastActivityAt(session.openCodeSessionId);
|
|
510
|
-
if (activityAt
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
629
|
+
if (activityAt !== undefined && activityAt > pending.registeredAt) {
|
|
630
|
+
const renewedDeadline = activityAt + session.policy.hardCapMs;
|
|
631
|
+
if (renewedDeadline > pending.hardDeadlineAt) {
|
|
632
|
+
pending.hardDeadlineAt = renewedDeadline;
|
|
633
|
+
trace("continuation lease renewed from OpenCode session activity");
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (toolCapAt !== undefined) {
|
|
637
|
+
const renewedDeadline = Math.min(now + session.policy.hardCapMs, toolCapAt);
|
|
638
|
+
if (renewedDeadline > pending.hardDeadlineAt) {
|
|
639
|
+
pending.hardDeadlineAt = renewedDeadline;
|
|
640
|
+
trace("continuation lease renewed while a host tool is running");
|
|
641
|
+
}
|
|
516
642
|
}
|
|
517
643
|
return pending.hardDeadlineAt;
|
|
518
644
|
}
|
|
@@ -570,6 +696,43 @@ export class SessionManager {
|
|
|
570
696
|
return !["ordinary-cleanup", "turn-ended", "initial-write-failed"].includes(reason);
|
|
571
697
|
}
|
|
572
698
|
}
|
|
699
|
+
export async function readSessionFrame(session, timeoutMs) {
|
|
700
|
+
if (session.pushbackFrame) {
|
|
701
|
+
const value = session.pushbackFrame;
|
|
702
|
+
session.pushbackFrame = undefined;
|
|
703
|
+
return { done: false, value };
|
|
704
|
+
}
|
|
705
|
+
const held = session.heldFrames?.shift();
|
|
706
|
+
if (held)
|
|
707
|
+
return { done: false, value: held };
|
|
708
|
+
const pending = session.pendingFrameRead ??= session.frames.next();
|
|
709
|
+
// A timed-out read can reject while no pump is consuming it. Preserve the
|
|
710
|
+
// rejection for the next reader without an unhandled detached promise.
|
|
711
|
+
void pending.catch(() => { });
|
|
712
|
+
let timer;
|
|
713
|
+
try {
|
|
714
|
+
const result = timeoutMs === undefined ? await pending : await Promise.race([
|
|
715
|
+
pending,
|
|
716
|
+
new Promise(resolve => {
|
|
717
|
+
timer = setTimeout(() => resolve({ done: true, timedOut: true }), timeoutMs);
|
|
718
|
+
timer.unref?.();
|
|
719
|
+
}),
|
|
720
|
+
]);
|
|
721
|
+
if (!("timedOut" in result) && session.pendingFrameRead === pending) {
|
|
722
|
+
session.pendingFrameRead = undefined;
|
|
723
|
+
}
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
if (session.pendingFrameRead === pending)
|
|
728
|
+
session.pendingFrameRead = undefined;
|
|
729
|
+
throw error;
|
|
730
|
+
}
|
|
731
|
+
finally {
|
|
732
|
+
if (timer)
|
|
733
|
+
clearTimeout(timer);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
573
736
|
export const sessionManager = new SessionManager();
|
|
574
737
|
function installProcessCleanup() {
|
|
575
738
|
const dispose = () => {
|
package/dist/shared.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare const AVAILABLE_MODELS_PATH = "/aiserver.v1.AiService/AvailableMo
|
|
|
21
21
|
export declare const SERVER_CONFIG_PATH = "/aiserver.v1.ServerConfigService/GetServerConfig";
|
|
22
22
|
export declare const MODEL_CACHE_FILE = "cursor-models.json";
|
|
23
23
|
/** Bumped when the on-disk model cache shape/semantics change (forces refetch). */
|
|
24
|
-
export declare const MODEL_CACHE_SCHEMA_VERSION =
|
|
24
|
+
export declare const MODEL_CACHE_SCHEMA_VERSION = 6;
|
|
25
25
|
export declare const MODEL_CACHE_TTL_MS = 86400000;
|
|
26
26
|
export declare const CONVERSATION_CACHE_DIR = "cursor-conversations";
|
|
27
27
|
export declare const CONVERSATION_CACHE_SCHEMA_VERSION = 4;
|
package/dist/shared.js
CHANGED
|
@@ -21,7 +21,7 @@ export const AVAILABLE_MODELS_PATH = "/aiserver.v1.AiService/AvailableModels";
|
|
|
21
21
|
export const SERVER_CONFIG_PATH = "/aiserver.v1.ServerConfigService/GetServerConfig";
|
|
22
22
|
export const MODEL_CACHE_FILE = "cursor-models.json";
|
|
23
23
|
/** Bumped when the on-disk model cache shape/semantics change (forces refetch). */
|
|
24
|
-
export const MODEL_CACHE_SCHEMA_VERSION =
|
|
24
|
+
export const MODEL_CACHE_SCHEMA_VERSION = 6;
|
|
25
25
|
export const MODEL_CACHE_TTL_MS = 86_400_000;
|
|
26
26
|
export const CONVERSATION_CACHE_DIR = "cursor-conversations";
|
|
27
27
|
export const CONVERSATION_CACHE_SCHEMA_VERSION = 4;
|