cursor-opencode-provider 0.8.1 → 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.
Files changed (96) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +204 -37
  3. package/SECURITY.md +20 -8
  4. package/dist/activity.d.ts +73 -1
  5. package/dist/activity.js +231 -5
  6. package/dist/agent-url.js +37 -9
  7. package/dist/auth-renewal.d.ts +1 -1
  8. package/dist/auth-renewal.js +58 -26
  9. package/dist/billing.d.ts +105 -0
  10. package/dist/billing.js +212 -0
  11. package/dist/classic-tool-results.d.ts +1 -0
  12. package/dist/classic-tool-results.js +42 -0
  13. package/dist/context/build.d.ts +1 -0
  14. package/dist/context/build.js +32 -5
  15. package/dist/context/env.js +27 -3
  16. package/dist/context/epoch.js +9 -5
  17. package/dist/context/git.d.ts +5 -2
  18. package/dist/context/git.js +17 -7
  19. package/dist/context/paths.d.ts +1 -1
  20. package/dist/context/paths.js +7 -4
  21. package/dist/debug.js +13 -2
  22. package/dist/early-tool-rows.d.ts +60 -0
  23. package/dist/early-tool-rows.js +96 -0
  24. package/dist/errors.d.ts +49 -7
  25. package/dist/errors.js +163 -8
  26. package/dist/host-interrupt.d.ts +12 -0
  27. package/dist/host-interrupt.js +18 -0
  28. package/dist/host-steer.d.ts +71 -0
  29. package/dist/host-steer.js +232 -0
  30. package/dist/image-input.js +4 -3
  31. package/dist/index.d.ts +2 -2
  32. package/dist/language-model.d.ts +114 -23
  33. package/dist/language-model.js +2290 -469
  34. package/dist/lost-request.d.ts +39 -0
  35. package/dist/lost-request.js +111 -0
  36. package/dist/model-config.d.ts +3 -1
  37. package/dist/model-config.js +50 -29
  38. package/dist/models.d.ts +26 -3
  39. package/dist/models.js +101 -45
  40. package/dist/opencode2/tool-results.d.ts +18 -0
  41. package/dist/opencode2/tool-results.js +66 -0
  42. package/dist/plugin-opencode2.js +185 -25
  43. package/dist/plugin.js +28 -5
  44. package/dist/pricing-data.d.ts +26 -2
  45. package/dist/pricing-data.js +26 -2
  46. package/dist/pricing.d.ts +1 -1
  47. package/dist/pricing.js +7 -4
  48. package/dist/process-shared.d.ts +14 -0
  49. package/dist/process-shared.js +18 -0
  50. package/dist/protocol/apply-patch.d.ts +25 -13
  51. package/dist/protocol/apply-patch.js +79 -30
  52. package/dist/protocol/blob-store.d.ts +5 -1
  53. package/dist/protocol/blob-store.js +10 -6
  54. package/dist/protocol/client-version.js +6 -4
  55. package/dist/protocol/conversation-cache-header.d.ts +23 -0
  56. package/dist/protocol/conversation-cache-header.js +149 -0
  57. package/dist/protocol/conversation-persistence.d.ts +27 -1
  58. package/dist/protocol/conversation-persistence.js +628 -126
  59. package/dist/protocol/conversation-state.d.ts +8 -1
  60. package/dist/protocol/conversation-state.js +28 -7
  61. package/dist/protocol/host-notes.d.ts +31 -0
  62. package/dist/protocol/host-notes.js +85 -0
  63. package/dist/protocol/messages.js +91 -8
  64. package/dist/protocol/request.d.ts +35 -19
  65. package/dist/protocol/request.js +53 -28
  66. package/dist/protocol/sent-history-images.d.ts +49 -0
  67. package/dist/protocol/sent-history-images.js +102 -0
  68. package/dist/protocol/tool-call-bridge.d.ts +2 -0
  69. package/dist/protocol/tool-call-bridge.js +10 -0
  70. package/dist/protocol/tools.d.ts +74 -8
  71. package/dist/protocol/tools.js +431 -141
  72. package/dist/protocol/turn-provenance.d.ts +19 -4
  73. package/dist/protocol/turn-provenance.js +77 -20
  74. package/dist/replay-safety.d.ts +16 -4
  75. package/dist/replay-safety.js +34 -25
  76. package/dist/retry-budget.d.ts +21 -0
  77. package/dist/retry-budget.js +94 -0
  78. package/dist/runtime-files.d.ts +23 -0
  79. package/dist/runtime-files.js +581 -0
  80. package/dist/session.d.ts +94 -9
  81. package/dist/session.js +153 -34
  82. package/dist/shared.d.ts +1 -1
  83. package/dist/shared.js +1 -1
  84. package/dist/shell-timeout.d.ts +54 -10
  85. package/dist/shell-timeout.js +362 -92
  86. package/dist/stream-write.d.ts +20 -0
  87. package/dist/stream-write.js +87 -0
  88. package/dist/terminal-runner.d.ts +15 -0
  89. package/dist/terminal-runner.js +528 -0
  90. package/dist/transport/connect.d.ts +8 -1
  91. package/dist/transport/connect.js +103 -23
  92. package/dist/usage.d.ts +37 -17
  93. package/dist/usage.js +63 -29
  94. package/package.json +3 -2
  95. package/dist/protocol/thinking.d.ts +0 -15
  96. package/dist/protocol/thinking.js +0 -17
@@ -1,13 +1,16 @@
1
+ import { type RetryBudget } from "./retry-budget.js";
1
2
  import type { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3StreamPart } from "@ai-sdk/provider";
2
3
  import type { CreateCursorOptions, CursorRetryOptions } from "./index.js";
4
+ import { type ToolRefusal } from "./opencode2/tool-results.js";
3
5
  import { type OpencodeToolDef } from "./protocol/tools.js";
4
6
  import { cursorContextUsageMetadata, type CursorContextUsageSource, type CursorConversationTokenDetails } from "./protocol/token-details.js";
5
7
  import { type ConversationBlobGraphStats } from "./protocol/blob-store.js";
6
8
  import { type CursorSession } from "./session.js";
7
9
  import { CursorProviderError } from "./errors.js";
8
- import { type ModelInfo } from "./models.js";
10
+ import { type ModelInfo, type ModelParameterValue } from "./models.js";
9
11
  import type { SeedHistoryMessage } from "./protocol/request.js";
10
12
  import { type CursorImageInput } from "./image-input.js";
13
+ import { type HostNoteBatch } from "./protocol/host-notes.js";
11
14
  type PromptIdentity = {
12
15
  hostAgent?: string;
13
16
  systemPromptHash?: string;
@@ -31,7 +34,7 @@ export declare function checkpointBlobGraphConcern(stats: ConversationBlobGraphS
31
34
  */
32
35
  export declare function isSoleControlFrame(payload: Uint8Array): boolean;
33
36
  export type CursorRetryPolicy = {
34
- maxAttempts: number;
37
+ maxAttempts?: number;
35
38
  baseDelayMs: number;
36
39
  maxDelayMs: number;
37
40
  };
@@ -49,8 +52,11 @@ export declare function snapshotMirroredTodosBySession(sessionKey: string | unde
49
52
  * churn have a single no-op definition.
50
53
  */
51
54
  export declare function promptIdentityWouldRemint(_previous: PromptIdentity, _current: PromptIdentity): boolean;
52
- /** Test helper: content hashes recorded after a successful held-Run image write. */
53
- export declare function snapshotSentHistoryImageHashesForTests(sessionKey: string): string[];
55
+ /** Test helper: the release `startSession` performs, then the host-note stores. */
56
+ export declare function releaseHostNoteInjectionsForTests(sessionKey: string): {
57
+ inFlight: string[][];
58
+ undelivered: string[];
59
+ };
54
60
  type V3Part = LanguageModelV3StreamPart;
55
61
  export declare function createCursorLanguageModel(modelId: string, providerId: string, options: CreateCursorOptions): LanguageModelV3;
56
62
  export declare function pumpWithRecovery(input: {
@@ -58,6 +64,9 @@ export declare function pumpWithRecovery(input: {
58
64
  controller: ReadableStreamDefaultController<V3Part>;
59
65
  abortSignal?: AbortSignal;
60
66
  retryPolicy?: CursorRetryPolicy;
67
+ retryBudget?: RetryBudget;
68
+ /** Proven completed host results can seed history without replaying their calls. */
69
+ canRebaseFromHistory?: (session: CursorSession) => boolean;
61
70
  recover: (recovery: CursorRunRecovery) => Promise<CursorSession>;
62
71
  onSession?: (session: CursorSession) => void;
63
72
  maxRecoveries?: number;
@@ -71,7 +80,7 @@ export declare function pumpWithRecovery(input: {
71
80
  }): Promise<CursorSession>;
72
81
  export type CursorRunRecovery = {
73
82
  kind: "rebase";
74
- reason?: "checkpoint-unusable";
83
+ reason?: "checkpoint-unusable" | "unfinished-prior-run" | "completed-tool-results";
75
84
  } | {
76
85
  kind: "resume";
77
86
  conversationId: string;
@@ -79,10 +88,6 @@ export type CursorRunRecovery = {
79
88
  };
80
89
  /** Start (or replace) the per-session heartbeat. In-flight writes from a prior attach are ignored. */
81
90
  export declare function attachSessionHeartbeat(session: CursorSession): void;
82
- /**
83
- * OpenCode re-sends the full tool-result history on every continuation. Prefer
84
- * the newest result that still has a live pending exec on its tagged session.
85
- */
86
91
  export declare function findContinuationSession(toolResults: Array<{
87
92
  sessionId: string;
88
93
  execId: number;
@@ -117,10 +122,10 @@ export declare const FRESH_TURN_DRAIN_TIMEOUT_MS = 8000;
117
122
  */
118
123
  export declare const FRESH_TURN_PENDING_CANCEL_REASON = "Host started a new user turn before this tool result was delivered";
119
124
  /**
120
- * Before opening a new user-turn Run, finish the prior held-open Run cleanly:
121
- * settle display-only bridged pendings, cancel any real execs still waiting on
122
- * the host, then drain for `turn_ended`. That preserves the same
123
- * conversation_id / checkpoint prefix instead of aborting mid-tool.
125
+ * Before opening a new user-turn Run, settle the held Run's pending results
126
+ * and drain its terminal checkpoint. The unseen follow-up to results settled
127
+ * here can finish through Cursor's cancellation contract; an unacknowledged
128
+ * turn must rebase.
124
129
  */
125
130
  export declare function preparePriorSessionForFreshTurn(openCodeSessionId: string | undefined, opts?: {
126
131
  timeoutMs?: number;
@@ -134,20 +139,84 @@ export declare function preparePriorSessionForFreshTurn(openCodeSessionId: strin
134
139
  *
135
140
  * @returns number of pendings successfully cancelled
136
141
  */
137
- export declare function cancelPendingExecsForFreshTurn(session: CursorSession): number;
142
+ export declare function cancelPendingExecsForFreshTurn(session: CursorSession): Promise<number>;
143
+ /**
144
+ * While the host runs a step's tools nothing pumps the Run, but Cursor keeps
145
+ * writing: right after a step it sends `tool_requests_listed` or a checkpoint,
146
+ * then KV blob writes, and it may abort an exec. Cursor CLI's single reader
147
+ * handles KV and exec control on their own channels at once; an unanswered KV
148
+ * write left Cursor waiting until the tool finished (seen live). Answer KV,
149
+ * apply an abort for an exec still waiting on the host, and skip heartbeats;
150
+ * queue every other frame in `heldFrames`, in arrival order, for the next pump.
151
+ * An abort whose exec request is still queued stays queued behind it.
152
+ *
153
+ * The reader peeks the shared `pendingFrameRead`: a pump that starts meanwhile
154
+ * awaits the same read, and the reader leaves that frame to it.
155
+ */
156
+ export declare function readHeldRun(session: CursorSession): void;
157
+ /** How long a Run must wait on host tools before its state is saved for a restart. */
158
+ export declare const HELD_RUN_SAVE_DELAY_MS = 1000;
159
+ /** How long a cancelled Run may take to end by itself before its stream is closed. */
160
+ export declare const HOST_INTERRUPT_GRACE_MS = 3000;
161
+ /**
162
+ * The host stopped the turn. OpenCode 2 never aborts `doStream` for that, so
163
+ * the Run would keep generating, and a held one would wait on results that
164
+ * never come. Cancel it as Cursor CLI's Stop does: forget what the host owed,
165
+ * send `cancel_action`, give Cursor a moment to end the Run, then close it.
166
+ * A Run that ends without `turn_ended` is saved with the last checkpoint
167
+ * Cursor sent, so the next turn resumes it and recognizes the stopped step.
168
+ */
169
+ export declare function cancelRunForHostInterrupt(session: CursorSession, reason: string, opts?: {
170
+ graceMs?: number;
171
+ }): Promise<void>;
172
+ /**
173
+ * Test helper: wait for host-interrupt cancels and in-progress Run saves, which
174
+ * write after the call that started them returned. Close the Runs first, so a
175
+ * held Run's save timer finds them closed instead of starting another save.
176
+ */
177
+ export declare function settleRunSavesForTests(): Promise<void>;
178
+ export declare function cancelIfHostStoppedSince(session: CursorSession): void;
138
179
  /**
139
180
  * Read remaining frames on an idle held-open Run until `turn_ended`, a
140
181
  * response-requiring request we cannot answer without the host, or timeout.
141
- * Used only on the fresh-turn settle path — not a general pump substitute.
182
+ * Used only to settle a Run the host left (a fresh turn, a host interrupt) —
183
+ * not a general pump substitute. `cancelReason` sends `cancel_action` first;
184
+ * `cancelled`: the caller already sent it. Either way nothing the Run still
185
+ * sends (display updates, an exec already in flight) needs the host, and
186
+ * Cursor's closing frames (the final checkpoint) are worth reading.
142
187
  */
143
188
  export declare function drainSessionUntilTurnEnded(session: CursorSession, opts?: {
144
189
  timeoutMs?: number;
145
- }): Promise<"turn-ended" | "busy" | "timeout" | "interrupted" | "skipped">;
146
- export declare function deliverContinuationResults(session: CursorSession, trailingToolResults: ExtractedToolResult[],
147
- /** Host primary agent of the request that carries these results. */
148
- delivery?: {
190
+ cancelReason?: string;
191
+ cancelled?: boolean;
192
+ }): Promise<"turn-ended" | "cancelled" | "busy" | "timeout" | "interrupted" | "skipped">;
193
+ export declare function deliverContinuationResults(session: CursorSession, trailingToolResults: ExtractedToolResult[], delivery?: {
149
194
  hostAgent?: string;
150
- }): CursorSession | undefined;
195
+ }): Promise<CursorSession | undefined>;
196
+ /** Notes the restart snapshot kept for this session's next user turn. */
197
+ export declare function restorePersistedHostNotes(sessionKey: string, persisted: string | undefined): void;
198
+ /**
199
+ * Host notes a new Run's user turn carries, as `<system_reminder>` blocks:
200
+ * notes no earlier Run delivered, then notes OpenCode placed before this user
201
+ * message (they exist only in host history, which a checkpointed Run does not
202
+ * resend). Call `sent()` once the Run request is written.
203
+ */
204
+ export declare function prepareUserTurnHostNotes(input: {
205
+ sessionKey?: string;
206
+ prompt: LanguageModelV3CallOptions["prompt"];
207
+ startedWithCheckpoint: boolean;
208
+ isCompaction: boolean;
209
+ ephemeralRun: boolean;
210
+ resuming: boolean;
211
+ /** The history actually seeded by a Run without a checkpoint. */
212
+ seedHistory?: readonly SeedHistoryMessage[];
213
+ /** Messages carried as the live action, rather than replayed in seed history. */
214
+ liveTurnStart?: number;
215
+ }): {
216
+ reminders: string[];
217
+ carriedNotes: HostNoteBatch[];
218
+ sent: () => void;
219
+ };
151
220
  /**
152
221
  * Read the held-open stream, emitting stream parts, until the turn boundary:
153
222
  * - tool call(s) for one Cursor generation → emit each tool-call as it
@@ -168,21 +237,34 @@ type ToolResultContent = {
168
237
  toolName: string;
169
238
  output: string;
170
239
  error?: string;
240
+ refusal?: ToolRefusal;
241
+ interrupted?: boolean;
171
242
  /** Host media parts of this result (own content, or the trailing media message). */
172
243
  media?: unknown[];
173
244
  /** `media` decoded for the exec result, with content hashes in the same order. */
174
245
  images?: CursorImageInput[];
175
246
  imageHashes?: string[];
176
- /** Host updates that a binary read cannot carry in its output oneof. */
177
- notes?: string;
247
+ /** Raw host note bodies that trailed the step (attached after typed result build). */
248
+ notes?: string[];
249
+ /** Notices about this result its typed shape cannot carry (hook context on this result). */
250
+ notices?: string[];
178
251
  };
179
252
  type ExtractedToolResult = ToolResultContent & {
180
253
  sessionId: string;
181
254
  execId: number;
182
255
  };
256
+ /**
257
+ * Host notes between the previous reply and the closing user message, e.g. an
258
+ * OpenCode 2 `<system-update>` after MCP servers reconnect. The host's leading
259
+ * system prompt is never one of them. Empty unless the prompt ends with a
260
+ * user message that is not itself a note.
261
+ */
262
+ export declare function hostNotesBeforeUserTurn(prompt: LanguageModelV3CallOptions["prompt"]): string[];
183
263
  export declare function extractTrailingToolResults(prompt: LanguageModelV3CallOptions["prompt"]): ExtractedToolResult[];
184
264
  /** Decode only image-bearing pending execs, under one continuation-wide budget. */
185
265
  export declare function decodeTrailingToolImages(session: CursorSession, results: ExtractedToolResult[], signal?: AbortSignal, maxBytes?: number): Promise<ExtractedToolResult[]>;
266
+ /** Debug summary of the last prompt messages: roles, part kinds, short text previews. */
267
+ export declare function describePromptTail(prompt: LanguageModelV3CallOptions["prompt"], count?: number): string;
186
268
  /** Detect a host-owned canonical plan review, excluding Cursor exec replies. */
187
269
  export declare function hasApprovedUncorrelatedPlanStageResult(prompt: LanguageModelV3CallOptions["prompt"]): boolean;
188
270
  /**
@@ -216,6 +298,13 @@ export declare function cursorTurnEndedProviderMetadata(te: Record<string, unkno
216
298
  context?: ReturnType<typeof cursorContextUsageMetadata>;
217
299
  };
218
300
  };
301
+ /** Partial assistant output may grow on a host retry; it is not a new user turn. */
302
+ export declare function retryTurnKey(prompt: LanguageModelV3CallOptions["prompt"], withoutLastInterruptionNotice?: boolean): string;
303
+ /** All user messages after the last assistant/tool boundary form one live user turn. */
304
+ export declare function liveUserTurn(prompt: LanguageModelV3CallOptions["prompt"]): {
305
+ start: number;
306
+ text: string;
307
+ } | undefined;
219
308
  /**
220
309
  * Prior prompt turns for a seed ConversationStateStructure. Tool results must
221
310
  * never be replayed as assistant-authored prose: that teaches the model to
@@ -227,6 +316,7 @@ export declare function cursorTurnEndedProviderMetadata(te: Record<string, unkno
227
316
  export declare function extractPromptHistory(prompt: LanguageModelV3CallOptions["prompt"], options?: {
228
317
  preserveTrailingUser?: boolean;
229
318
  toolResults?: "omit" | "all" | "trailing";
319
+ liveTurnStart?: number;
230
320
  }): SeedHistoryMessage[];
231
321
  /** Share of the target context a foreign-history rebase may fill before compaction. */
232
322
  export declare const FOREIGN_HISTORY_REBASE_CONTEXT_SHARE = 0.8;
@@ -239,6 +329,8 @@ export declare function assertForeignHistoryRebaseFits(input: {
239
329
  modelInfo: ModelInfo | undefined;
240
330
  cursorModelId: string;
241
331
  maxMode: boolean;
332
+ /** Resolved variant tuple; its `context` is the Run's window when present. */
333
+ parameters?: ModelParameterValue[];
242
334
  history: SeedHistoryMessage[];
243
335
  systemPrompt: string | undefined;
244
336
  userText: string;
@@ -270,7 +362,6 @@ export declare function spanEndParts(opts: {
270
362
  type: "text-end" | "reasoning-end";
271
363
  id: string;
272
364
  }>;
273
- /** Exported for tests — false for compaction/summary (no tools) and toolChoice none. */
274
365
  export declare function computeAllowTools(toolCount: number, toolChoice: LanguageModelV3CallOptions["toolChoice"] | undefined): boolean;
275
366
  /**
276
367
  * OpenCode title/summary take the first non-empty line of the answer. After a