@magpie-community/opencode-workbuddy-auth 0.2.21 → 0.2.23

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 -1
  2. package/index.mjs +104 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -339,6 +339,19 @@ and says `filtersModels`, so while it is on magpie says under the models
339
339
  that the list is filtered (#590). Both are magpie's own fields; an older
340
340
  magpie shows the English words and no note.
341
341
 
342
+ ### Hide DeepSeek Harness's name
343
+
344
+ Since 0.2.23 each site also has a "Hide DeepSeek Harness's name" switch, on
345
+ by default, and on in OpenCode, which has no switches. A user reported a
346
+ WorkBuddy account banned after use from DeepSeek Harness, with a reply saying
347
+ its built-in prompt is recognised. With the switch on, the harness's name is
348
+ taken out of the system prompt, the user's and assistant's text and the
349
+ tools' descriptions: "You are an AI agent powered by DeepSeek Harness."
350
+ becomes "You are an AI agent.", and the runtime lines naming its checkout and
351
+ Web GUI say "agent implementation checkout" and "Web GUI". Tools' results,
352
+ paths, commands and names such as `DSH_HOME` or `~/.dsh` go as they were.
353
+ Whether this keeps an account from being banned is not known.
354
+
342
355
  ## Credits in magpie
343
356
 
344
357
  magpie's usage card shows the account's credits from the resource summary.
@@ -359,7 +372,10 @@ credit packs) and platform rewards (gifted and activity credits,
359
372
  against plan_bonus, addon and benefit); a summary without them is split
360
373
  by package code as WorkBuddy's client does. Only one of the two stops the
361
374
  account, since WorkBuddy serves it while any credits are left: the extra
362
- window is set aside until the plan's credits are spent. A summary with
375
+ window is set aside until the plan's credits are spent, then the plan's
376
+ is. Extra credits only the detail lists carry (a China-site free
377
+ account's summary can list the plan's base alone) are their own "Extra
378
+ credits" window by the same rule (ferock on Discord). A summary with
363
379
  only one kind keeps the one "Credits" window (akic404 on Discord; since
364
380
  0.2.16 the plan's bonus is extra too, Jiabubu on Discord).
365
381
 
package/index.mjs CHANGED
@@ -3466,6 +3466,76 @@ function replyAfterToolsOn(b) {
3466
3466
  return true
3467
3467
  }
3468
3468
 
3469
+ // HIDE_DSH is the switch, on unless the user turns it off, that takes
3470
+ // DeepSeek Harness's name out of what it sends (NancyTom on X: an account
3471
+ // used from dsh through magpie was banned, and a reply there had Tencent
3472
+ // recognize dsh's built-in prompt). FLAGGED's comma gets a chat past the
3473
+ // live 11128 check; this is for a check that reads the prompt afterwards.
3474
+ // The lines are dsh 0.2.1's own (@deepseek-ai/dsh-system-prompt and
3475
+ // dsh-harness-dev-context): the opener, the checkout and Web GUI lines in
3476
+ // the system prompt and in the runtime context dsh sends as a user message,
3477
+ // and their earlier wording (LEGACY_SOURCE, LEGACY_WEB_SURFACE). Only the
3478
+ // name in prose goes: lower-case dsh, which names paths (~/.dsh), commands,
3479
+ // URIs (dsh-session:) and skills the agent calls, stays, as does a tool's
3480
+ // result, which is what a file or command said. Case counts, so the name
3481
+ // in an identifier (DSH_HOME) is left too.
3482
+ const HIDE_DSH = "hide_dsh"
3483
+ const DSH_NAMED = [
3484
+ [/You are an AI agent,? powered by DeepSeek Harness\.?/g, "You are an AI agent."],
3485
+ [/DeepSeek Harness (Web GUI)/g, "$1"],
3486
+ [/DeepSeek Harness (implementation checkout)/g, "agent $1"],
3487
+ [/\bDSH itself\b/g, "the agent itself"],
3488
+ [/\b([Tt]he )?DeepSeek Harness\b/g, (_, the) => (the ?? "the ") + "agent harness"],
3489
+ [/\bDSH\b(?!_)/g, "the agent harness"],
3490
+ ]
3491
+
3492
+ function dshHidden(text) {
3493
+ for (const [re, to] of DSH_NAMED) text = text.replace(re, to)
3494
+ return text
3495
+ }
3496
+
3497
+ // hideDsh puts dshHidden on the text of a chat's messages (Chat
3498
+ // Completions or Messages: a string, or a part of type text), on Messages'
3499
+ // system, and on the tools' descriptions; true when it changed any.
3500
+ // A tool's result (role tool, or a tool_result part) is left as it came.
3501
+ function hideDsh(chat) {
3502
+ if (!chat || typeof chat !== "object") return false
3503
+ let changed = false
3504
+ const parts = (content) => {
3505
+ if (typeof content === "string") {
3506
+ const t = dshHidden(content)
3507
+ if (t !== content) changed = true
3508
+ return t
3509
+ }
3510
+ if (!Array.isArray(content)) return content
3511
+ return content.map((p) => {
3512
+ if (!p || typeof p.text !== "string" || (p.type !== undefined && p.type !== "text")) return p
3513
+ const t = dshHidden(p.text)
3514
+ if (t === p.text) return p
3515
+ changed = true
3516
+ return { ...p, text: t }
3517
+ })
3518
+ }
3519
+ if (Array.isArray(chat.messages))
3520
+ for (const m of chat.messages) if (m && typeof m === "object" && m.role !== "tool") m.content = parts(m.content)
3521
+ if (chat.system !== undefined) chat.system = parts(chat.system)
3522
+ const described = (o) => {
3523
+ if (!o || typeof o !== "object") return
3524
+ if (Array.isArray(o)) return o.forEach(described)
3525
+ for (const k of Object.keys(o)) {
3526
+ if (k === "description" && typeof o[k] === "string") {
3527
+ const t = dshHidden(o[k])
3528
+ if (t !== o[k]) {
3529
+ o[k] = t
3530
+ changed = true
3531
+ }
3532
+ } else described(o[k])
3533
+ }
3534
+ }
3535
+ if (Array.isArray(chat.tools)) described(chat.tools)
3536
+ return changed
3537
+ }
3538
+
3469
3539
  // FREE_ONLY is the switch that keeps an account off credits (#430): its
3470
3540
  // list holds only the models WorkBuddy serves at no cost (priceOf's free),
3471
3541
  // and a request for any other is answered here, never sent. A price that
@@ -3536,6 +3606,9 @@ function makePlugin(site) {
3536
3606
  }
3537
3607
  }
3538
3608
  const freeOnly = () => magpie?.option?.(site.id, FREE_ONLY) === true
3609
+ // on by default: magpie hands over the user's value, else the default;
3610
+ // OpenCode, with no magpie, has it on
3611
+ const dshOff = () => magpie?.option?.(site.id, HIDE_DSH) !== false
3539
3612
  // onlyFree is a list with the switch on: its free models, or a failure
3540
3613
  // when there are none to tell (magpie then keeps the list it had)
3541
3614
  const onlyFree = (ms) => {
@@ -3644,6 +3717,19 @@ function makePlugin(site) {
3644
3717
  },
3645
3718
  filtersModels: true,
3646
3719
  },
3720
+ {
3721
+ key: HIDE_DSH,
3722
+ label: "Hide DeepSeek Harness's name",
3723
+ description: `Takes DeepSeek Harness's name out of what it sends to ${site.name}: its opening line, and the lines naming its checkout and Web GUI. Paths, commands and tools stay as they are.`,
3724
+ default: true,
3725
+ labels: { zh: "隐去 DeepSeek Harness 的名字", "zh-TW": "隱去 DeepSeek Harness 的名字", ja: "DeepSeek Harness の名前を伏せる", de: "Namen von DeepSeek Harness ausblenden" },
3726
+ descriptions: {
3727
+ zh: `把 DeepSeek Harness 的名字从发给 ${site.name} 的内容里去掉:开头那句,以及说明它源码目录和 Web GUI 的几行。路径、命令和工具保持不变。`,
3728
+ "zh-TW": `把 DeepSeek Harness 的名字從送給 ${site.name} 的內容裡去掉:開頭那句,以及說明它原始碼目錄和 Web GUI 的幾行。路徑、指令和工具保持不變。`,
3729
+ ja: `${site.name} に送る内容から DeepSeek Harness の名前を外します。冒頭の一文と、チェックアウトや Web GUI を示す行が対象です。パス、コマンド、ツールはそのままです。`,
3730
+ de: `Entfernt den Namen von DeepSeek Harness aus dem, was an ${site.name} gesendet wird: die erste Zeile sowie die Zeilen zu seinem Checkout und seiner Web-GUI. Pfade, Befehle und Tools bleiben unverändert.`,
3731
+ },
3732
+ },
3647
3733
  ],
3648
3734
  async loader(getAuth) {
3649
3735
  const auth = await getAuth()
@@ -3684,6 +3770,7 @@ function makePlugin(site) {
3684
3770
  let chat, changed, names
3685
3771
  ;({ body, chat, changed, names } = prepared(body, native))
3686
3772
  if (native) names = {}
3773
+ if (dshOff() && hideDsh(chat)) changed = true
3687
3774
  // the switch is checked before anything is sent: a model not
3688
3775
  // free, or one whose price can't be read, never reaches
3689
3776
  // WorkBuddy. The model is read off the chat parsed above; a body
@@ -3903,24 +3990,30 @@ function makePlugin(site) {
3903
3990
  const aside = extra ? (stops === credits ? extra : credits) : null
3904
3991
  if (aside) Object.assign(aside, runsOutOf(sum, packages, Date.now(), (code) => ofPlan(code) === (aside === credits)) ?? {})
3905
3992
  // the summary of the plan's credits alone: the extra ones
3906
- // only the detail lists have (listedExtra) are their window,
3907
- // always set aside. Whether WorkBuddy spends them once the
3908
- // plan's are gone isn't seen, so the plan's window stops the
3909
- // account as it did before.
3993
+ // only the detail lists have (listedExtra) are their window.
3994
+ // As with the summary's split (usageOf), one of the two stops
3995
+ // the account: the plan's, until its credits are spent with
3996
+ // extra ones left. WorkBuddy goes on serving such an account on
3997
+ // its extra credits (ferock on Discord, a China-site account
3998
+ // at "500 / 500" moved off while its extra ones were left).
3910
3999
  const kinds = !extra && kindsOf(sum, site)
3911
4000
  const more = kinds && kinds.plan.total > 0 && kinds.extra.total === 0 ? listedExtra(sum, packages, site) : null
3912
4001
  if (more) {
3913
4002
  const { runsOutAt, runsOut, ...counts } = more
3914
- const w = { ...creditWindow("Extra credits", counts), aside: true, ...(runsOutAt ? { runsOutAt, runsOut } : {}) }
4003
+ const w = { ...creditWindow("Extra credits", counts), ...(runsOutAt ? { runsOutAt, runsOut } : {}) }
4004
+ const planSpent = credits.amount >= credits.limit && counts.used < counts.total
4005
+ if (planSpent) credits.aside = true
4006
+ else w.aside = true
3915
4007
  out.windows.splice(out.windows.indexOf(credits) + 1, 0, w)
3916
- // and, as with the split's, the soonest of them to lapse is
4008
+ // and, as with the split's, the soonest credits to lapse are
3917
4009
  // the account's too, on the window that stops it: routing
3918
4010
  // reads none set aside, so an account whose extra credits
3919
4011
  // expire first went unknown to it (Kayphoon on Discord)
3920
- const at = runsOutAt ? Date.parse(runsOutAt) : 0
3921
- const was = credits.runsOutAt ? Date.parse(credits.runsOutAt) : 0
3922
- if (at > 0 && (!was || at < was)) Object.assign(credits, { runsOutAt, runsOut })
3923
- else if (at > 0 && at === was) credits.runsOut = (credits.runsOut ?? 0) + runsOut
4012
+ const [stops, other] = planSpent ? [w, credits] : [credits, w]
4013
+ const at = other.runsOutAt ? Date.parse(other.runsOutAt) : 0
4014
+ const was = stops.runsOutAt ? Date.parse(stops.runsOutAt) : 0
4015
+ if (at > 0 && (!was || at < was)) Object.assign(stops, { runsOutAt: other.runsOutAt, runsOut: other.runsOut })
4016
+ else if (at > 0 && at === was) stops.runsOut = (stops.runsOut ?? 0) + other.runsOut
3924
4017
  }
3925
4018
  } catch {}
3926
4019
  }
@@ -4083,4 +4176,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
4083
4176
  export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
4084
4177
 
4085
4178
  // for tests
4086
- export const _internal = { planCodes, listedExtra, 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, CN_ESTIMATED_WINDOWS, FREE_WINDOW_AI, FREE_WINDOW_SPAN, FREE_WINDOW_GAP, FREE_WINDOW_LEAD, checkinOf, API_MESSAGES: NPM_MESSAGES, API_CHAT: NPM }
4179
+ export const _internal = { HIDE_DSH, hideDsh, dshHidden, planCodes, listedExtra, 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, CN_ESTIMATED_WINDOWS, 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.21",
3
+ "version": "0.2.23",
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": "./opencode.mjs",