@magpie-community/opencode-workbuddy-auth 0.2.13 → 0.2.15

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 +17 -5
  2. package/index.mjs +40 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -286,7 +286,11 @@ that names no level goes at the model's own default from the config
286
286
  (`reasoning.defaultEffort`, "high" for DeepSeek-V4.1-Flash), as WorkBuddy's
287
287
  client sends it: WorkBuddy doesn't think when a chat names none, so
288
288
  OpenCode's default variant got answers without thinking. An effort the
289
- client names, `none` too, goes as it came.
289
+ client names, `none` too, goes as it came. A chat whose `max_tokens` is
290
+ 1024 or less gets no default (since 0.2.15, #643): a title or a
291
+ classification asked at 64 tokens spent all 64 on reasoning and answered
292
+ nothing. Anthropic's API takes no thinking under a 1024-token budget, so
293
+ no client means to think in one that small.
290
294
 
291
295
  One value differs from the config, measured with real requests:
292
296
  DeepSeek-V4.1-Flash (and its paid `-sg` id) lists `none`, though the
@@ -399,6 +403,14 @@ What is measured, and what is easy to read wrongly:
399
403
  429 whose named reset is still ahead makes the window full — `used: 100`,
400
404
  `resetsAt` the reset the vendor named — whatever the rows add up to. The
401
405
  sum carries the window only until the first refusal.
406
+ - **The sum is shown, not routed on.** Until the vendor refuses, the card is
407
+ set aside (`aside: true`): magpie draws it beside the account and never
408
+ counts it as the account's allowance, so an estimate near its cap does not
409
+ stop the model. The refusal's card does (`refused: true`), and magpie's
410
+ gateway rests this model alone on this account until the reset the
411
+ refusal names. Before 0.2.14 the sum stopped the model too, and a paid
412
+ China account read 99% of a cap it had never been refused at, with 2,457
413
+ credits left (plugins#641).
402
414
  - **The card names both ids**, `deepseek-v4.1-flash` and
403
415
  `deepseek-v4.1-flash-sg`: magpie matches a window's models by exact id
404
416
  (only an `@size` suffix folds into the bare id), so one alone would leave
@@ -435,10 +447,10 @@ exhausted, and the next request opens a fresh one. The China build
435
447
  (`copilot.tencent.com`) carries the same window: two Free-plan accounts
436
448
  refused on 2026-11-02 at 200,389,043 and 200,485,923 tokens, so its cap is
437
449
  measured at about 2e8 — and its refusals name the reset in Chinese
438
- (`将在 … UTC+8 重置`), which reads as an edge the same way. Its card stops
439
- at a **soft cap of 1.9e8**, 5% short of the measured one, so routing rests
440
- the model before the vendor refuses it; the headroom absorbs the account's
441
- spending on other machines, which this ledger cannot see.
450
+ (`将在 … UTC+8 重置`), which reads as an edge the same way. Its card reads
451
+ against 2e8. In 0.2.12 and 0.2.13 it read against a soft cap of 1.9e8, so
452
+ that routing rested the model before the vendor refused it; with the sum set
453
+ aside that headroom only stopped the model early, so it is gone.
442
454
 
443
455
  ## When free models work but a credit model does not
444
456
 
package/index.mjs CHANGED
@@ -2276,6 +2276,17 @@ const compact = (n) => (Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/
2276
2276
  // full whatever the sum says. That is the one exact signal there is,
2277
2277
  // and routing must not pass over it either.
2278
2278
  //
2279
+ // So the sum is an estimate, and it is shown, not routed on: its card is set
2280
+ // aside (aside: true), which magpie draws beside the account and never counts
2281
+ // as its allowance. Only a refusal's card stops the model. The estimate used
2282
+ // to stop it as well, and it read a paid China account at 99% of a cap it had
2283
+ // never been refused at, with 2,457 credits left (XiaoFeiyu9, plugins#641):
2284
+ // the cap is two accounts' measurement, the ledger is one machine's, and
2285
+ // neither is the vendor's word for this account. Nothing is lost by waiting
2286
+ // for that word: magpie's gateway reads the refusal's reset ("将在 … 重置",
2287
+ // "reset at …") and its "switch to the other models", and rests this model
2288
+ // alone, on this account alone, until the time the vendor named.
2289
+ //
2279
2290
  // The ledger is magpie's own, in magpie's config folder (appdir.Config()):
2280
2291
  // the folder the plugin host runs in, which is the portable data folder
2281
2292
  // when magpie runs portable, $XDG_CONFIG_HOME/magpie when that is set, and
@@ -2287,12 +2298,12 @@ const compact = (n) => (Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/
2287
2298
  // caps: the same phone number can be signed in to both, and their ledgers
2288
2299
  // stay apart — one build's spending must not fill the other's window.
2289
2300
  //
2290
- // The China cap is a soft one. Measured at about 2e8 (two accounts refusing
2291
- // at 200.39M and 200.49M), the card stops the model at 1.9e8: 5% of headroom
2292
- // for the account's spending on other machines (which this ledger cannot
2293
- // see), so routing rests the model before the vendor's refusal does. A
2294
- // refusal in force still reads the window full whatever the sum says.
2295
- const FREE_WINDOW_CN = { provider: "workbuddy", cap: 1.9e8, short: "190M" }
2301
+ // The China cap is the measured one, 2e8 (two accounts refused at 200.39M
2302
+ // and 200.49M). It was a soft 1.9e8 in 0.2.12 and 0.2.13, so that routing
2303
+ // rested the model before the vendor refused it; since the sum is set aside
2304
+ // (freeWindowOf's card), the cap only scales what the card says, and the
2305
+ // headroom bought nothing but a model stopped 10M tokens early (plugins#641).
2306
+ const FREE_WINDOW_CN = { provider: "workbuddy", cap: 2e8, short: "200M" }
2296
2307
  const FREE_WINDOW_AI = { provider: "workbuddy-ai", cap: 1e8, short: "100M" }
2297
2308
  const FREE_WINDOW_SPAN = 24 * 3600 * 1000
2298
2309
  // The lead: how far back of an edge the read goes on. A chain read from a row
@@ -2438,7 +2449,9 @@ function freeWindowOf(rows, account, now = Date.now(), win = FREE_WINDOW_AI) {
2438
2449
  resetsAt: new Date(resetsAt).toISOString(),
2439
2450
  span: FREE_WINDOW_SPAN / 1000,
2440
2451
  models: FREE_WINDOW_MODELS.slice(),
2441
- ...(refused ? { refused: true } : {}),
2452
+ // the ledger's sum is an estimate, shown and set aside; the vendor's
2453
+ // refusal is the one that stops the model (see the window above)
2454
+ ...(refused ? { refused: true } : { aside: true }),
2442
2455
  })
2443
2456
 
2444
2457
  // A refusal still in force is exact: the account is spending this window
@@ -3246,11 +3259,27 @@ function defaultEffortOf(d, efforts) {
3246
3259
  return typeof own === "string" && efforts.includes(own) ? own : undefined
3247
3260
  }
3248
3261
 
3262
+ // SMALL_BUDGET is the largest output budget a chat can name and still be
3263
+ // one no client meant to think in: Anthropic's API takes thinking only with
3264
+ // a budget of at least 1024 tokens and a max_tokens above it, so a request
3265
+ // of 1024 or less can't have asked for thinking there, and a client's own
3266
+ // short asks (a session title, a classification) name such budgets. A
3267
+ // default effort spends them on reasoning: itspig (#643) saw 230+ title
3268
+ // requests at max_tokens 64 on deepseek-v4-pro end out=64, reasoning=64,
3269
+ // no text. Measured 2026-11-03 on www.workbuddy.ai, deepseek-v4.1-flash-sg
3270
+ // at its default high: max_tokens 64 ended "length" with 64 reasoning
3271
+ // tokens and no text; without an effort it answered the title.
3272
+ const SMALL_BUDGET = 1024
3273
+
3249
3274
  // withDefaultEffort gives a chat that names no effort the model's default
3250
3275
  // (defaultEffortOf), as WorkBuddy's client sends it; true when it did. An
3251
- // effort the client named, "none" too, goes as it came.
3276
+ // effort the client named, "none" too, goes as it came. A chat whose
3277
+ // max_tokens is SMALL_BUDGET or less is given none: WorkBuddy doesn't
3278
+ // think without one, so its budget goes to the answer.
3252
3279
  function withDefaultEffort(chat, list) {
3253
3280
  if (!chat || typeof chat !== "object" || namesEffort(chat)) return false
3281
+ const limit = chat.max_tokens ?? chat.max_completion_tokens
3282
+ if (typeof limit === "number" && limit > 0 && limit <= SMALL_BUDGET) return false
3254
3283
  const m = list?.find((x) => x.id === chat.model)
3255
3284
  if (!m?.effort) return false
3256
3285
  chat.reasoning_effort = m.effort
@@ -3788,9 +3817,8 @@ function makePlugin(site) {
3788
3817
  // the free model's own window, which the credit meter above does
3789
3818
  // not show: what actually refuses a chat long before 160 credits
3790
3819
  // are spent. Both builds: WorkBuddy AI at 1e8 (ten days of
3791
- // refusals), the China build's refusals measured at about 2e8 on
3792
- // 2026-11-02, its card stopping at the 1.9e8 soft cap (see the
3793
- // window definitions above)
3820
+ // refusals), the China build's at about 2e8 (2026-11-02). Set
3821
+ // aside until the vendor refuses (see the window definitions above)
3794
3822
  {
3795
3823
  const account = a.accountId || a.user || a.uid || ""
3796
3824
  const win = site.ai ? FREE_WINDOW_AI : FREE_WINDOW_CN
@@ -3940,4 +3968,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
3940
3968
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
3941
3969
 
3942
3970
  // for tests
3943
- export const _internal = { planCodes, idFor, OFF_TAKEN, replyAfterToolsOn, withDefaultEffort, defaultEffortOf, gzipped, FREE_ONLY, PRICE_TTL, keyAccount, remoteConfig, cycleOf, typeUnions, systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, accountName, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, asReasoning, normalizeToolChoice, fitTools, fitted, renamed, unsaid, FILTER_SAYS, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, REFUSED_DOC, commaPaused, commaPausedOn, opened, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, scopeOf, turnKey, conversationKey, signatureOf, isMessages, withoutUnsigned, withoutSignedThinking, prepared, answeredBefore, nativeOf, modelOf, configModels, freeWindowOf, tokensShort, freeWindowRows, freeWindowLedger, readLedgerTail, pinned, pinning, rowTime, rowAccount, ofAccount, windowOffset, windowReset, FREE_WINDOW_MODELS, FREE_WINDOW_CN, FREE_WINDOW_AI, FREE_WINDOW_SPAN, FREE_WINDOW_GAP, FREE_WINDOW_LEAD, checkinOf, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
3971
+ export const _internal = { planCodes, idFor, OFF_TAKEN, replyAfterToolsOn, withDefaultEffort, SMALL_BUDGET, defaultEffortOf, gzipped, FREE_ONLY, PRICE_TTL, keyAccount, remoteConfig, cycleOf, typeUnions, systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, accountName, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, asReasoning, normalizeToolChoice, fitTools, fitted, renamed, unsaid, FILTER_SAYS, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, REFUSED_DOC, commaPaused, commaPausedOn, opened, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, scopeOf, turnKey, conversationKey, signatureOf, isMessages, withoutUnsigned, withoutSignedThinking, prepared, answeredBefore, nativeOf, modelOf, configModels, freeWindowOf, tokensShort, freeWindowRows, freeWindowLedger, readLedgerTail, pinned, pinning, rowTime, rowAccount, ofAccount, windowOffset, windowReset, FREE_WINDOW_MODELS, FREE_WINDOW_CN, FREE_WINDOW_AI, FREE_WINDOW_SPAN, FREE_WINDOW_GAP, FREE_WINDOW_LEAD, checkinOf, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-workbuddy-auth",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "OpenCode provider plugins for WorkBuddy's plans: WorkBuddy (CodeBuddy, copilot.tencent.com) and WorkBuddy AI (www.workbuddy.ai)",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",