@magpie-community/opencode-cursor-auth 0.2.28 → 0.2.29

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 (3) hide show
  1. package/README.md +4 -2
  2. package/index.mjs +43 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -51,8 +51,10 @@ plugin's `fetch` answers them on Cursor's agent API:
51
51
  (refused, no network) or a stream the server refused leaves the next Run
52
52
  to try HTTP/2 again. A region error over HTTP/1.1 sends the Run back to
53
53
  HTTP/2 at the region's agent host. BidiAppends go up numbered in order,
54
- up to 16 at a time as cursor-agent sends them (one at a time for a
55
- native session's Run, whose tool results must be certain). A BidiAppend
54
+ up to 16 at a time as cursor-agent sends them. A native session's Run
55
+ sends only its KV replies (the history's blobs) that way; a tool result
56
+ or anything else waits for the appends before it, goes alone, and is
57
+ written once Cursor answered it, so it is never uncertain. A BidiAppend
56
58
  with no answer in 60 s (more for a large one) ends the Run, as
57
59
  cursor-agent does.
58
60
  - A new Run imports the whole conversation as AI SDK messages kept as blobs
package/index.mjs CHANGED
@@ -2038,10 +2038,11 @@ async function agentURL(tok, fresh) {
2038
2038
  // H2.retryAfter, or until a region error sends one back (answerOf); a
2039
2039
  // connection that failed (refused, no network) or a stream the server
2040
2040
  // refused leaves the next Run to try HTTP/2 again. atOnce is how many
2041
- // BidiAppends openH1 may have on their way at once.
2041
+ // BidiAppends openH1 may have on their way at once, overlaps which messages
2042
+ // may be among them (openH1).
2042
2043
  let h1Until = 0
2043
2044
  const cancelledOpen = () => ({ error: { status: 410, message: "the request was cancelled" }, h2Failed: false })
2044
- async function open(base, headers, signal, atOnce = H1.atOnce) {
2045
+ async function open(base, headers, signal, atOnce = H1.atOnce, overlaps) {
2045
2046
  if (signal?.aborted) return cancelledOpen()
2046
2047
  if (Date.now() >= h1Until) {
2047
2048
  const o = await openH2(base, headers, signal)
@@ -2055,7 +2056,7 @@ async function open(base, headers, signal, atOnce = H1.atOnce) {
2055
2056
  if (o.blocked) h1Until = Date.now() + H2.retryAfter
2056
2057
  }
2057
2058
  if (signal?.aborted) return cancelledOpen()
2058
- return openH1(headers, signal, atOnce)
2059
+ return openH1(headers, signal, atOnce, overlaps)
2059
2060
  }
2060
2061
 
2061
2062
  const H2 = {
@@ -2128,10 +2129,19 @@ const H1 = {
2128
2129
  // keeps them: one at a time, a new Run's history (a blob a message,
2129
2130
  // hundreds for a long conversation, all asked for at once) took a round
2130
2131
  // trip to Cursor each before the model could start. A native session's
2131
- // Run keeps them one at a time (runNative).
2132
+ // Run sends only its KV replies that way (kvReply); each of its other
2133
+ // messages waits for every append before it, and goes alone (runNative).
2132
2134
  atOnce: 16,
2133
2135
  }
2134
2136
 
2137
+ // kvReply is whether a client message (an AgentClientMessage, unframed) is
2138
+ // a KV reply, kv_client_message: a blob Cursor asked for, which nothing
2139
+ // else waits on. Cursor asks for a new Run's whole history at once.
2140
+ function kvReply(msg) {
2141
+ const b = Buffer.isBuffer(msg) ? msg : Buffer.from(msg ?? EMPTY)
2142
+ return b.length > 0 && b[0] === 0x1a // field 3, length-delimited
2143
+ }
2144
+
2135
2145
  // connectCode is Connect's code for an HTTP status that came with no
2136
2146
  // Connect error of its own
2137
2147
  function connectCode(status) {
@@ -2144,8 +2154,11 @@ function connectCode(status) {
2144
2154
  // the stream it reads, Connect's frames as the HTTP/2 Run's. Its response
2145
2155
  // is a 200 at once; an error Cursor says later, or a call that fails,
2146
2156
  // ends the stream with Connect's end frame, which runOnce reads as the
2147
- // Run's own.
2148
- function openH1(headers, signal, atOnce = H1.atOnce) {
2157
+ // Run's own. overlaps says of a message whether it may go up while appends
2158
+ // before it are on their way (up to atOnce of them); one that may not waits
2159
+ // for all of them, goes alone, and its write's callback is its own append
2160
+ // answered.
2161
+ function openH1(headers, signal, atOnce = H1.atOnce, overlaps = () => true) {
2149
2162
  if (signal?.aborted) return Promise.resolve(cancelledOpen())
2150
2163
  const ac = new AbortController()
2151
2164
  const id = headers["x-request-id"]
@@ -2159,11 +2172,20 @@ function openH1(headers, signal, atOnce = H1.atOnce) {
2159
2172
  write(chunk, _enc, cb) {
2160
2173
  // runOnce writes one whole frame each time
2161
2174
  const msg = Buffer.from(chunk).subarray(5)
2162
- const n = seq++
2163
- const p = append(msg, n)
2164
- going.add(p)
2165
- const gone = () => going.delete(p)
2166
- p.then(gone, gone)
2175
+ const go = () => {
2176
+ const p = append(msg, seq++)
2177
+ going.add(p)
2178
+ const gone = () => going.delete(p)
2179
+ p.then(gone, gone)
2180
+ return p
2181
+ }
2182
+ if (!overlaps(msg)) {
2183
+ // alone: after every append before it is answered, and answered
2184
+ // itself before the next write
2185
+ Promise.allSettled([...going]).then(go).then(() => cb(), (e) => cb(e))
2186
+ return
2187
+ }
2188
+ go()
2167
2189
  // Up to atOnce appends go at once; past that the write stays
2168
2190
  // buffered until one finishes, so callbacks and drain reflect the
2169
2191
  // pending upload rather than an unbounded promise chain. At one,
@@ -2432,9 +2454,15 @@ async function runNative({ tok, base, id, tools, conv, convID, signal, maxMode,
2432
2454
  "x-cursor-agent-allowed-tools": "mcp_tool_call,get_mcp_tools_tool_call",
2433
2455
  },
2434
2456
  native.ac.signal,
2435
- // one at a time: a native session counts a write as done only when
2436
- // Cursor answered its own append, so a tool result is never uncertain
2437
- 1,
2457
+ // Over HTTP/1.1 the history's blobs go up H1.atOnce at a time, as
2458
+ // cursor-agent sends them; one at a time, a Run with a long history
2459
+ // waited a round trip to api2 a blob before its first token (ysm119 on
2460
+ // Discord: 135 blobs, 37 s against 3 s over HTTP/2). Everything else
2461
+ // goes alone, after the appends before it were answered, and counts as
2462
+ // written only once Cursor answered it, so a tool result is never
2463
+ // uncertain.
2464
+ H1.atOnce,
2465
+ kvReply,
2438
2466
  )
2439
2467
  if (o.error) {
2440
2468
  native.fail(o.error)
@@ -3167,4 +3195,4 @@ export async function CursorAuthPlugin() {
3167
3195
  }
3168
3196
 
3169
3197
  // for tests
3170
- export const _internal = { listFor, lists, keychain, keychainToken, nativeTransport, onDemandOf,resetH1: () => (h1Until = 0), onH1: () => Date.now() < h1Until, resetTeam: () => { teamRead.at = 0; teamRead.info = null }, expireTeam: () => (teamRead.at = 0),H2, H1, h2Blocked, open, openH1, openH2, STEP_WAIT, stepUsage, usageKey, ledger, owed, flights, misses, unreadable, catalog, usable, request, modelsOf, tokensOf, maxRequired, conversationID, usageOf, sessionOf, SESSION, errorResponse, kept, poolBase, conversation, buildRun, decode, exec, fields, pb, pbValue, pbAny, frame, frames, failure, toolsOf, expiry }
3198
+ export const _internal = { kvReply, listFor, lists, keychain, keychainToken, nativeTransport, onDemandOf,resetH1: () => (h1Until = 0), onH1: () => Date.now() < h1Until, resetTeam: () => { teamRead.at = 0; teamRead.info = null }, expireTeam: () => (teamRead.at = 0),H2, H1, h2Blocked, open, openH1, openH2, STEP_WAIT, stepUsage, usageKey, ledger, owed, flights, misses, unreadable, catalog, usable, request, modelsOf, tokensOf, maxRequired, conversationID, usageOf, sessionOf, SESSION, errorResponse, kept, poolBase, conversation, buildRun, decode, exec, fields, pb, pbValue, pbAny, frame, frames, failure, toolsOf, expiry }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-cursor-auth",
3
- "version": "0.2.28",
3
+ "version": "0.2.29",
4
4
  "description": "Cursor subscriptions (Pro, Pro+, Ultra, Teams) in OpenCode and magpie, through the API cursor-agent talks to",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",