@magpie-community/opencode-workbuddy-auth 0.1.28 → 0.1.30
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.
- package/README.md +14 -3
- package/index.mjs +82 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,9 +86,20 @@ default one, as the app does.
|
|
|
86
86
|
WorkBuddy refuses some agents' identity lines as an "unapproved channel"
|
|
87
87
|
(11128), so those lines are reworded before a chat goes out. Since 0.1.27
|
|
88
88
|
(#279) that includes DeepSeek Harness's opening line. A refusal of words
|
|
89
|
-
not reworded yet
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
not reworded yet is sent once more, since 0.1.29, with the system prompt
|
|
90
|
+
moved into the first user message (inside `<instructions>` tags) and
|
|
91
|
+
WorkBuddy's default system prompt in its place: WorkBuddy checks only the
|
|
92
|
+
system and assistant messages, and the line it matched is then in neither.
|
|
93
|
+
A system message later in the chat becomes a user message where it stands.
|
|
94
|
+
The plugin remembers each refused system prompt while it is loaded, so that
|
|
95
|
+
agent's next chats go moved at once. The move can't reach an assistant
|
|
96
|
+
message, so words the model quotes back into one keep that chat refused
|
|
97
|
+
until they are in the list reworded in every message (since 0.1.30, the "Web GUI whose URL the runtime
|
|
98
|
+
context names" line, #291); otherwise only a new chat clears it. A refusal
|
|
99
|
+
that stands after the move ends in a hint that names no one client
|
|
100
|
+
(since 0.1.28): WorkBuddy refused words in this client's prompt; use it from
|
|
101
|
+
Hermes, OpenCode or Pi, or add another provider to the group. Neither
|
|
102
|
+
refusal rests the account.
|
|
92
103
|
|
|
93
104
|
The loader also translates OpenAI-compatible request spellings WorkBuddy
|
|
94
105
|
doesn't take: `max_completion_tokens` becomes `max_tokens`, `developer`
|
package/index.mjs
CHANGED
|
@@ -467,6 +467,11 @@ const FLAGGED = [
|
|
|
467
467
|
// WorkBuddy refuses as 11128 (magpie-community/plugins#279; the same
|
|
468
468
|
// refusal traced in smart-open/TraeWorkAssistant#125).
|
|
469
469
|
[/You are an AI agent powered by DeepSeek Harness\.?/gi, "You are an AI agent that helps the user with programming tasks."],
|
|
470
|
+
// A line WorkBuddy refuses in a long DeepSeek Harness chat, measured 400
|
|
471
|
+
// then 200 with this wording by tearslee (magpie-community/plugins#291). The
|
|
472
|
+
// model can quote it back into an assistant message, which the move to
|
|
473
|
+
// the user's instructions can't reach, so it is reworded in every message.
|
|
474
|
+
[/Web GUI whose URL the runtime context names\.\s+When the user refers/gi, "Web GUI whose URL the runtime context names. The interface, when the user refers"],
|
|
470
475
|
]
|
|
471
476
|
|
|
472
477
|
function unflagged(text) {
|
|
@@ -542,6 +547,61 @@ function withSystem(body) {
|
|
|
542
547
|
}
|
|
543
548
|
}
|
|
544
549
|
|
|
550
|
+
// WorkBuddy's channel check reads the system and assistant messages only:
|
|
551
|
+
// tried on 2026-10-21 with real accounts on both builds (glm-5.3,
|
|
552
|
+
// deepseek-v4.1-flash, gpt-5.5), each refused line was served in the user's
|
|
553
|
+
// message, a tool's result and a user message's parts, and refused again in
|
|
554
|
+
// the system or an assistant message. It matches a known line whole and
|
|
555
|
+
// exactly (one extra space lets it through), so FLAGGED only ever knows the
|
|
556
|
+
// lines met so far, and another agent's opening line is refused until one is
|
|
557
|
+
// added (Wing on Discord, 10-21, with an agent we couldn't name).
|
|
558
|
+
//
|
|
559
|
+
// systemAsUser is body with its system prompt read to the model as the
|
|
560
|
+
// first thing the user says, in INSTRUCTIONS' tags, and WorkBuddy's default
|
|
561
|
+
// system prompt first, as both builds want a system message there (11128
|
|
562
|
+
// "first message is not system prompt"). A system message later in the chat
|
|
563
|
+
// becomes the user's where it stands. null when there's nothing to move.
|
|
564
|
+
const DEFAULT_SYSTEM = "You are a helpful assistant."
|
|
565
|
+
const INSTRUCTIONS = ["<instructions>\n", "\n</instructions>"]
|
|
566
|
+
|
|
567
|
+
function textOf(content) {
|
|
568
|
+
if (typeof content === "string") return content
|
|
569
|
+
if (!Array.isArray(content)) return ""
|
|
570
|
+
return content.map((p) => (typeof p?.text === "string" ? p.text : "")).filter(Boolean).join("\n")
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// systemKey is the text of a chat's system messages, which a chat refused
|
|
574
|
+
// once is known by: the same agent's next chat goes with systemAsUser at once
|
|
575
|
+
function systemKey(body) {
|
|
576
|
+
const msgs = chatOf(body)?.messages
|
|
577
|
+
if (!Array.isArray(msgs)) return ""
|
|
578
|
+
const key = msgs.filter((m) => m?.role === "system").map((m) => textOf(m.content)).join("\n")
|
|
579
|
+
return key.trim() === DEFAULT_SYSTEM ? "" : key
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function systemAsUser(body) {
|
|
583
|
+
const b = chatOf(body)
|
|
584
|
+
const msgs = b?.messages
|
|
585
|
+
if (!Array.isArray(msgs)) return null
|
|
586
|
+
let i = 0
|
|
587
|
+
const lead = []
|
|
588
|
+
for (; i < msgs.length && msgs[i]?.role === "system"; i++) lead.push(textOf(msgs[i].content))
|
|
589
|
+
const said = lead.join("\n\n").trim()
|
|
590
|
+
const rest = msgs.slice(i).map((m) => (m?.role === "system" ? { ...m, role: "user" } : m))
|
|
591
|
+
const moved = said && said !== DEFAULT_SYSTEM
|
|
592
|
+
if (!moved && rest.every((m, k) => m === msgs[i + k])) return null
|
|
593
|
+
if (moved) {
|
|
594
|
+
const text = INSTRUCTIONS[0] + said + INSTRUCTIONS[1]
|
|
595
|
+
const u = rest.findIndex((m) => m?.role === "user")
|
|
596
|
+
if (u < 0) rest.unshift({ role: "user", content: text })
|
|
597
|
+
else if (typeof rest[u].content === "string") rest[u] = { ...rest[u], content: text + "\n\n" + rest[u].content }
|
|
598
|
+
else if (Array.isArray(rest[u].content)) rest[u] = { ...rest[u], content: [{ type: "text", text }, ...rest[u].content] }
|
|
599
|
+
else rest[u] = { ...rest[u], content: text }
|
|
600
|
+
}
|
|
601
|
+
b.messages = [{ role: "system", content: DEFAULT_SYSTEM }, ...rest]
|
|
602
|
+
return JSON.stringify(b)
|
|
603
|
+
}
|
|
604
|
+
|
|
545
605
|
// normalizeToolChoice converts the object form emitted by OpenAI clients to
|
|
546
606
|
// WorkBuddy's string form. It runs after fitted(), so a named choice follows a
|
|
547
607
|
// tool name that was made safe for WorkBuddy.
|
|
@@ -1559,6 +1619,9 @@ function makePlugin(site) {
|
|
|
1559
1619
|
// the models of this site that refused tool results then the user's
|
|
1560
1620
|
// message, for this run: their next chats go with replyAfterTools at once
|
|
1561
1621
|
const endsAfterTools = new Set()
|
|
1622
|
+
// the system prompts this run WorkBuddy refused as an unapproved channel:
|
|
1623
|
+
// their next chats go with systemAsUser at once, not refused first
|
|
1624
|
+
const refusedSystems = new Set()
|
|
1562
1625
|
return async ({ client }) => ({
|
|
1563
1626
|
config: async (config) => {
|
|
1564
1627
|
config.provider ??= {}
|
|
@@ -1629,15 +1692,27 @@ function makePlugin(site) {
|
|
|
1629
1692
|
// a model known to turn tool results then the user's message away
|
|
1630
1693
|
// gets the empty reply between them up front (see endsAfterTools)
|
|
1631
1694
|
const model = chatOf(body)?.model
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1695
|
+
// a system prompt WorkBuddy refused before goes as the user's
|
|
1696
|
+
// words at once (see systemAsUser)
|
|
1697
|
+
const sysKey = systemKey(body)
|
|
1698
|
+
if (sysKey && refusedSystems.has(sysKey)) body = systemAsUser(body) ?? body
|
|
1699
|
+
const shaped = (b) => (endsAfterTools.has(model) ? replyAfterTools(b) ?? b : b)
|
|
1700
|
+
let res = await send(shaped(body))
|
|
1701
|
+
if (res.status === 400) {
|
|
1702
|
+
let text = await res.text()
|
|
1703
|
+
const moved = REFUSED.test(text) && sysKey && !refusedSystems.has(sysKey) ? systemAsUser(body) : null
|
|
1704
|
+
if (moved) {
|
|
1705
|
+
refusedSystems.add(sysKey)
|
|
1706
|
+
if (refusedSystems.size > 256) refusedSystems.delete(refusedSystems.values().next().value)
|
|
1707
|
+
body = moved
|
|
1708
|
+
res = await send(shaped(body))
|
|
1709
|
+
if (res.status === 400) text = await res.text()
|
|
1710
|
+
}
|
|
1711
|
+
const again = res.status === 400 && !endsAfterTools.has(model) && AFTER_TOOLS_REFUSED.test(text) ? replyAfterTools(body) : null
|
|
1637
1712
|
if (again) {
|
|
1638
1713
|
endsAfterTools.add(model)
|
|
1639
1714
|
res = await send(again)
|
|
1640
|
-
} else {
|
|
1715
|
+
} else if (res.status === 400) {
|
|
1641
1716
|
res = new Response(text, { status: res.status, statusText: res.statusText, headers: res.headers })
|
|
1642
1717
|
}
|
|
1643
1718
|
}
|
|
@@ -1785,4 +1860,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
|
|
|
1785
1860
|
export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
|
|
1786
1861
|
|
|
1787
1862
|
// for tests
|
|
1788
|
-
export const _internal = { EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, normalizeToolChoice, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
|
|
1863
|
+
export const _internal = { systemAsUser, systemKey, DEFAULT_SYSTEM, EDGE_READS, fitShape, replyAfterTools, AFTER_TOOLS_REFUSED, enterpriseUsageOf, notDesktop, textOnly, runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, normalizeToolChoice, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, RESTRICTED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, priceOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-workbuddy-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
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",
|