@magpie-community/opencode-workbuddy-auth 0.2.14 → 0.2.16

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 +16 -8
  2. package/index.mjs +45 -24
  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
@@ -345,15 +349,19 @@ midnight on every account. magpie's smart routing spends the account whose
345
349
  credits run out first.
346
350
 
347
351
  The plan's own credits and the rest are two windows. "Credits" is the
348
- packages whose credits come back each cycle, as WorkBuddy's client lists
349
- them (Pro monthly or yearly, youth, advanced, flagship, the plan's bonus,
350
- the free plan's monthly base, the Pro trials, or the subscription the
351
- summary names), with the plan's cycle, so its pace line is the month's.
352
- "Extra credits" is the packages that only lapse: gifted and activity
353
- credits (赠送积分) and bought credit packs. Only one of the two stops the
352
+ plan's base credits: what the subscription grants each cycle (Pro monthly
353
+ or yearly, youth, advanced, flagship, the Pro trials, the free plan's
354
+ monthly base, or the subscription the summary names), with the plan's
355
+ cycle, so its pace line is the month's. "Extra credits" is every other
356
+ kind WorkBuddy's own usage panel lists: the plan's bonus, top-ups (bought
357
+ credit packs) and platform rewards (gifted and activity credits,
358
+ 赠送积分). The summary's CreditGroups say which is which (plan_base
359
+ against plan_bonus, addon and benefit); a summary without them is split
360
+ by package code as WorkBuddy's client does. Only one of the two stops the
354
361
  account, since WorkBuddy serves it while any credits are left: the extra
355
362
  window is set aside until the plan's credits are spent. A summary with
356
- only one kind keeps the one "Credits" window (akic404 on Discord).
363
+ only one kind keeps the one "Credits" window (akic404 on Discord; since
364
+ 0.2.16 the plan's bonus is extra too, Jiabubu on Discord).
357
365
 
358
366
  An enterprise (team) account's resource summary lists no packages. Its
359
367
  credits are read from the enterprise meter
package/index.mjs CHANGED
@@ -2868,12 +2868,15 @@ function pinning() {
2868
2868
  // The plan's own credits and the rest are two windows (akic404 on
2869
2869
  // Discord): summed, the gifted and activity packages' credits used long
2870
2870
  // ago put the card at most of its allowance on the first day of the
2871
- // month, and the plan's pace line said nothing. "Credits" is the packages
2872
- // whose credits come back each cycle — WorkBuddy's client's own
2873
- // onlyRefreshCodes (PLAN_CODES), or the subscription the summary names —
2874
- // and "Extra credits" the ones that only lapse (gift, activity, bought
2875
- // credit packs, any code the client doesn't list). A summary with only
2876
- // one kind keeps the one "Credits" window, as before.
2871
+ // month, and the plan's pace line said nothing. "Credits" is the plan's
2872
+ // base credits and "Extra credits" every other kind, as WorkBuddy's own
2873
+ // usage panel breaks them down (Jiabubu on Discord: a plan's bonus and
2874
+ // bought credits were nowhere on the card). The summary's CreditGroups
2875
+ // say it in WorkBuddy's words: plan_base ("Plan Base Credits") is the
2876
+ // plan's, plan_bonus, addon ("Top-up Credits") and benefit ("Platform
2877
+ // Reward Credits") are extra. A summary without them is split by code as
2878
+ // the client's resolveCreditGroup does (PLAN_CODES, see planCodes). A
2879
+ // summary with only one kind keeps the one "Credits" window, as before.
2877
2880
  //
2878
2881
  // Only one of the two stops the account at a time: WorkBuddy serves it
2879
2882
  // while any credits are left, so the one set aside is the one whose
@@ -2884,12 +2887,13 @@ function usageOf(sum, plan, site) {
2884
2887
  const ofPlan = planCodes(sum, site)
2885
2888
  const kinds = { plan: { total: 0, used: 0 }, extra: { total: 0, used: 0 } }
2886
2889
  let left = 0, bags = 0
2890
+ const hasTotal = (p) => p?.CycleTotalCapacity != null && p.CycleTotalCapacity !== ""
2891
+ const add = (k, p) => ((k.total += capacity(p.CycleTotalCapacity)), (k.used += capacity(p?.CycleUsedCapacity)))
2892
+ const groups = (Array.isArray(sum?.CreditGroups) ? sum.CreditGroups : []).filter(hasTotal)
2893
+ for (const g of groups) add(kinds[g?.Kind === "plan_base" ? "plan" : "extra"], g)
2887
2894
  for (const p of sum?.Packages ?? []) {
2888
- const hasTotal = p?.CycleTotalCapacity != null && p.CycleTotalCapacity !== ""
2889
- if (hasTotal) {
2890
- const k = kinds[ofPlan(p?.PackageCode) ? "plan" : "extra"]
2891
- k.total += capacity(p.CycleTotalCapacity)
2892
- k.used += capacity(p?.CycleUsedCapacity)
2895
+ if (hasTotal(p)) {
2896
+ if (!groups.length) add(kinds[ofPlan(p?.PackageCode) ? "plan" : "extra"], p)
2893
2897
  } else if (p?.CycleRemainCapacity != null && p.CycleRemainCapacity !== "") {
2894
2898
  left += capacity(p.CycleRemainCapacity)
2895
2899
  bags++
@@ -2953,7 +2957,7 @@ async function spareFree(site, a, windows) {
2953
2957
  // detail list without them (10001 "PackageCodes required").
2954
2958
  const CODE = (n, s) => `TCACA_code_${n}_${s}`
2955
2959
  const PAID_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("003", "FAnt7lcmRT"), CODE("023", "4xbGhMrE6q"),
2956
- CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("009", "0XmEQc2xOf"), CODE("038", "OhvqZtiPKr"), CODE("036", "lupO5WgNdG")]
2960
+ CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("043", "6oIkxT7Z0S"), CODE("009", "0XmEQc2xOf"), CODE("038", "OhvqZtiPKr"), CODE("036", "lupO5WgNdG")]
2957
2961
  const DAILY_CODE = CODE("001", "PqouKr6QWV")
2958
2962
  const FREE_CODES = [DAILY_CODE, CODE("008", "cfWoLwvjU4"), CODE("035", "ArVxJcGDsm"), CODE("006", "DbXS0lrypC"), CODE("039", "KRcQj7wUat"),
2959
2963
  CODE("040", "mi9rCYg46x"), CODE("007", "nzdH5h4Nl0"), CODE("028", "NtpWi0jzXs"), CODE("037", "WxOD3MpI2o"), CODE("029", "6wCGEWquYy"),
@@ -2962,17 +2966,18 @@ const FREE_CODES = [DAILY_CODE, CODE("008", "cfWoLwvjU4"), CODE("035", "ArVxJcGD
2962
2966
  // lost at the same midnight on every account, so they tell none apart
2963
2967
  const SLICE = "4"
2964
2968
 
2965
- // PLAN_CODES are the packages whose credits come back each cycle, as
2966
- // WorkBuddy's client (5.9.x) lists them (onlyRefreshCodes): Pro monthly,
2967
- // Pro monthly plus, Pro yearly, youth, advanced, flagship, the plan's
2968
- // bonus (bonus28; bonusIntl, "Plan Bonus Quota"), the international free
2969
- // plan (freeMonIntl), the Pro trials, and on the China build alone the
2970
- // free plan's monthly base (freeMon, 套餐基础积分). Every other code only
2971
- // lapses: gift (006, "Bonus Pack" / Pro trial), activity (007, 赠送积分),
2972
- // bonus29/30, and the credit packs (009, 036, 038).
2969
+ // PLAN_CODES are the plan's base credits, as WorkBuddy's client (WorkBuddy
2970
+ // AI 5.9.4) groups them in its usage panel (resolveCreditGroup's
2971
+ // planBase): Pro monthly, Pro monthly plus, Pro yearly, youth, advanced,
2972
+ // flagship, buddyOneVip, the international free plan (freeMonIntl), the
2973
+ // Pro trials, and on the China build alone the free plan's monthly base
2974
+ // (freeMon, 套餐基础积分). The plan's bonus (bonus28; bonusIntl, "Plan
2975
+ // Bonus Quota") comes back each cycle too, but the client says it apart
2976
+ // (planBonus), as it does the credit packs (009, 036, 038: addon) and
2977
+ // activity, bonus29/30 and gift (benefit).
2973
2978
  const PLAN_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("003", "FAnt7lcmRT"), CODE("023", "4xbGhMrE6q"),
2974
- CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("028", "NtpWi0jzXs"), CODE("035", "ArVxJcGDsm"),
2975
- CODE("039", "KRcQj7wUat"), CODE("040", "mi9rCYg46x"), CODE("037", "WxOD3MpI2o")]
2979
+ CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("043", "6oIkxT7Z0S"), CODE("035", "ArVxJcGDsm"),
2980
+ CODE("039", "KRcQj7wUat"), CODE("040", "mi9rCYg46x")]
2976
2981
  const FREE_MON = CODE("008", "cfWoLwvjU4")
2977
2982
 
2978
2983
  // planCodes says whether a package of the summary is the plan's own: one
@@ -3259,11 +3264,27 @@ function defaultEffortOf(d, efforts) {
3259
3264
  return typeof own === "string" && efforts.includes(own) ? own : undefined
3260
3265
  }
3261
3266
 
3267
+ // SMALL_BUDGET is the largest output budget a chat can name and still be
3268
+ // one no client meant to think in: Anthropic's API takes thinking only with
3269
+ // a budget of at least 1024 tokens and a max_tokens above it, so a request
3270
+ // of 1024 or less can't have asked for thinking there, and a client's own
3271
+ // short asks (a session title, a classification) name such budgets. A
3272
+ // default effort spends them on reasoning: itspig (#643) saw 230+ title
3273
+ // requests at max_tokens 64 on deepseek-v4-pro end out=64, reasoning=64,
3274
+ // no text. Measured 2026-11-03 on www.workbuddy.ai, deepseek-v4.1-flash-sg
3275
+ // at its default high: max_tokens 64 ended "length" with 64 reasoning
3276
+ // tokens and no text; without an effort it answered the title.
3277
+ const SMALL_BUDGET = 1024
3278
+
3262
3279
  // withDefaultEffort gives a chat that names no effort the model's default
3263
3280
  // (defaultEffortOf), as WorkBuddy's client sends it; true when it did. An
3264
- // effort the client named, "none" too, goes as it came.
3281
+ // effort the client named, "none" too, goes as it came. A chat whose
3282
+ // max_tokens is SMALL_BUDGET or less is given none: WorkBuddy doesn't
3283
+ // think without one, so its budget goes to the answer.
3265
3284
  function withDefaultEffort(chat, list) {
3266
3285
  if (!chat || typeof chat !== "object" || namesEffort(chat)) return false
3286
+ const limit = chat.max_tokens ?? chat.max_completion_tokens
3287
+ if (typeof limit === "number" && limit > 0 && limit <= SMALL_BUDGET) return false
3267
3288
  const m = list?.find((x) => x.id === chat.model)
3268
3289
  if (!m?.effort) return false
3269
3290
  chat.reasoning_effort = m.effort
@@ -3952,4 +3973,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
3952
3973
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
3953
3974
 
3954
3975
  // for tests
3955
- 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 }
3976
+ 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.14",
3
+ "version": "0.2.16",
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",