@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.9
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 +88 -88
- package/dist/opencode-anthropic-auth-cli.mjs +804 -507
- package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
- package/package.json +67 -59
- package/src/__tests__/billing-edge-cases.test.ts +59 -59
- package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
- package/src/__tests__/cc-comparison.test.ts +87 -87
- package/src/__tests__/cc-credentials.test.ts +254 -250
- package/src/__tests__/cch-drift-checker.test.ts +51 -51
- package/src/__tests__/cch-native-style.test.ts +56 -56
- package/src/__tests__/debug-gating.test.ts +42 -42
- package/src/__tests__/decomposition-smoke.test.ts +68 -68
- package/src/__tests__/fingerprint-regression.test.ts +575 -566
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
- package/src/__tests__/helpers/conversation-history.ts +119 -119
- package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
- package/src/__tests__/helpers/deferred.ts +69 -69
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
- package/src/__tests__/helpers/in-memory-storage.ts +88 -88
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
- package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
- package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
- package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
- package/src/__tests__/helpers/sse.ts +209 -209
- package/src/__tests__/index.parallel.test.ts +605 -595
- package/src/__tests__/sanitization-regex.test.ts +112 -112
- package/src/__tests__/state-bounds.test.ts +90 -90
- package/src/account-identity.test.ts +197 -192
- package/src/account-identity.ts +69 -67
- package/src/account-state.test.ts +86 -86
- package/src/account-state.ts +25 -25
- package/src/accounts/matching.test.ts +335 -0
- package/src/accounts/matching.ts +167 -0
- package/src/accounts/persistence.test.ts +345 -0
- package/src/accounts/persistence.ts +432 -0
- package/src/accounts/repair.test.ts +276 -0
- package/src/accounts/repair.ts +407 -0
- package/src/accounts.dedup.test.ts +621 -621
- package/src/accounts.test.ts +933 -929
- package/src/accounts.ts +633 -989
- package/src/backoff.test.ts +345 -345
- package/src/backoff.ts +219 -219
- package/src/betas.ts +124 -124
- package/src/bun-fetch.test.ts +345 -342
- package/src/bun-fetch.ts +424 -424
- package/src/bun-proxy.test.ts +25 -25
- package/src/bun-proxy.ts +209 -209
- package/src/cc-credentials.ts +111 -111
- package/src/circuit-breaker.test.ts +184 -184
- package/src/circuit-breaker.ts +169 -169
- package/src/cli/commands/auth.ts +963 -0
- package/src/cli/commands/config.ts +547 -0
- package/src/cli/formatting.test.ts +406 -0
- package/src/cli/formatting.ts +219 -0
- package/src/cli.ts +255 -2022
- package/src/commands/handlers/betas.ts +100 -0
- package/src/commands/handlers/config.ts +99 -0
- package/src/commands/handlers/files.ts +375 -0
- package/src/commands/oauth-flow.ts +181 -166
- package/src/commands/prompts.ts +61 -61
- package/src/commands/router.test.ts +421 -0
- package/src/commands/router.ts +143 -635
- package/src/config.test.ts +482 -482
- package/src/config.ts +412 -404
- package/src/constants.ts +48 -48
- package/src/drift/cch-constants.ts +95 -95
- package/src/env.ts +111 -105
- package/src/headers/billing.ts +33 -33
- package/src/headers/builder.ts +130 -130
- package/src/headers/cch.ts +75 -75
- package/src/headers/stainless.ts +25 -25
- package/src/headers/user-agent.ts +23 -23
- package/src/index.ts +436 -828
- package/src/models.ts +27 -27
- package/src/oauth.test.ts +102 -102
- package/src/oauth.ts +178 -178
- package/src/parent-pid-watcher.test.ts +148 -148
- package/src/parent-pid-watcher.ts +69 -69
- package/src/plugin-helpers.ts +82 -82
- package/src/refresh-helpers.ts +145 -139
- package/src/refresh-lock.test.ts +94 -94
- package/src/refresh-lock.ts +93 -93
- package/src/request/body.history.test.ts +579 -571
- package/src/request/body.ts +255 -255
- package/src/request/metadata.ts +65 -65
- package/src/request/retry.test.ts +156 -156
- package/src/request/retry.ts +67 -67
- package/src/request/url.ts +21 -21
- package/src/request-orchestration-helpers.ts +648 -0
- package/src/response/index.ts +5 -5
- package/src/response/mcp.ts +58 -58
- package/src/response/streaming.test.ts +313 -311
- package/src/response/streaming.ts +412 -410
- package/src/rotation.test.ts +304 -301
- package/src/rotation.ts +205 -205
- package/src/storage.test.ts +547 -547
- package/src/storage.ts +315 -291
- package/src/system-prompt/builder.ts +38 -38
- package/src/system-prompt/index.ts +5 -5
- package/src/system-prompt/normalize.ts +60 -60
- package/src/system-prompt/sanitize.ts +30 -30
- package/src/thinking.ts +21 -20
- package/src/token-refresh.test.ts +265 -265
- package/src/token-refresh.ts +219 -214
- package/src/types.ts +30 -30
- package/dist/bun-proxy.mjs +0 -291
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
// ===========================================================================
|
|
35
35
|
|
|
36
36
|
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
CLAUDE_CODE_IDENTITY_STRING,
|
|
38
|
+
COMPACT_TITLE_GENERATOR_SYSTEM_PROMPT,
|
|
39
|
+
KNOWN_IDENTITY_STRINGS,
|
|
40
40
|
} from "../constants.js";
|
|
41
41
|
import { buildAnthropicBillingHeader } from "../headers/billing.js";
|
|
42
42
|
import type { SignatureConfig, SystemBlock } from "../types.js";
|
|
@@ -44,47 +44,47 @@ import { dedupeSystemBlocks, isTitleGeneratorSystemBlocks } from "./normalize.js
|
|
|
44
44
|
import { compactSystemText, sanitizeSystemText } from "./sanitize.js";
|
|
45
45
|
|
|
46
46
|
export function buildSystemPromptBlocks(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
system: SystemBlock[],
|
|
48
|
+
signature: SignatureConfig,
|
|
49
|
+
messages: unknown[],
|
|
50
50
|
): SystemBlock[] {
|
|
51
|
-
|
|
51
|
+
const titleGeneratorRequest = isTitleGeneratorSystemBlocks(system);
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
let sanitized: SystemBlock[] = system.map((item) => ({
|
|
54
|
+
...item,
|
|
55
|
+
text: compactSystemText(
|
|
56
|
+
sanitizeSystemText(item.text, signature.sanitizeSystemPrompt === true),
|
|
57
|
+
signature.promptCompactionMode,
|
|
58
|
+
),
|
|
59
|
+
}));
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
if (titleGeneratorRequest) {
|
|
62
|
+
sanitized = [{ type: "text", text: COMPACT_TITLE_GENERATOR_SYSTEM_PROMPT }];
|
|
63
|
+
} else if (signature.promptCompactionMode !== "off") {
|
|
64
|
+
sanitized = dedupeSystemBlocks(sanitized);
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
if (!signature.enabled) {
|
|
68
|
+
return sanitized;
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const filtered = sanitized.filter(
|
|
72
|
+
(item) => !item.text.startsWith("x-anthropic-billing-header:") && !KNOWN_IDENTITY_STRINGS.has(item.text),
|
|
73
|
+
);
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
const blocks: SystemBlock[] = [];
|
|
76
|
+
const billingHeader = buildAnthropicBillingHeader(signature.claudeCliVersion, messages);
|
|
77
|
+
if (billingHeader) {
|
|
78
|
+
blocks.push({ type: "text", text: billingHeader });
|
|
79
|
+
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
// CC 2.1.98 sends only {"type":"ephemeral"} — no scope or ttl fields.
|
|
82
|
+
blocks.push({
|
|
83
|
+
type: "text",
|
|
84
|
+
text: CLAUDE_CODE_IDENTITY_STRING,
|
|
85
|
+
cache_control: { type: "ephemeral" },
|
|
86
|
+
});
|
|
87
|
+
blocks.push(...filtered);
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
return blocks;
|
|
90
90
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { buildSystemPromptBlocks } from "./builder.js";
|
|
2
2
|
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
dedupeSystemBlocks,
|
|
4
|
+
isTitleGeneratorSystemBlocks,
|
|
5
|
+
isTitleGeneratorSystemText,
|
|
6
|
+
normalizeSystemTextBlocks,
|
|
7
|
+
normalizeSystemTextForComparison,
|
|
8
8
|
} from "./normalize.js";
|
|
9
9
|
export { compactSystemText, sanitizeSystemText } from "./sanitize.js";
|
|
@@ -5,80 +5,80 @@
|
|
|
5
5
|
import type { SystemBlock } from "../types.js";
|
|
6
6
|
|
|
7
7
|
export function normalizeSystemTextForComparison(text: string): string {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
return text
|
|
9
|
+
.replace(/\r\n/g, "\n")
|
|
10
|
+
.split("\n")
|
|
11
|
+
.map((line) => line.trim())
|
|
12
|
+
.join("\n")
|
|
13
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
14
|
+
.trim();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function dedupeSystemBlocks(system: SystemBlock[]): SystemBlock[] {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const normalizedBlocks = exactDeduped.map((item) => normalizeSystemTextForComparison(item.text));
|
|
30
|
-
return exactDeduped.filter((_, index) => {
|
|
31
|
-
const current = normalizedBlocks[index];
|
|
32
|
-
if (current.length < 80) return true;
|
|
33
|
-
|
|
34
|
-
for (let otherIndex = 0; otherIndex < normalizedBlocks.length; otherIndex += 1) {
|
|
35
|
-
if (otherIndex === index) continue;
|
|
36
|
-
const other = normalizedBlocks[otherIndex];
|
|
37
|
-
if (other.length <= current.length + 20) continue;
|
|
38
|
-
if (other.includes(current)) return false;
|
|
18
|
+
const exactSeen = new Set<string>();
|
|
19
|
+
const exactDeduped: SystemBlock[] = [];
|
|
20
|
+
|
|
21
|
+
for (const item of system) {
|
|
22
|
+
const normalized = normalizeSystemTextForComparison(item.text);
|
|
23
|
+
const key = `${item.type}:${normalized}`;
|
|
24
|
+
if (exactSeen.has(key)) continue;
|
|
25
|
+
exactSeen.add(key);
|
|
26
|
+
exactDeduped.push(item);
|
|
39
27
|
}
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
const normalizedBlocks = exactDeduped.map((item) => normalizeSystemTextForComparison(item.text));
|
|
30
|
+
return exactDeduped.filter((_, index) => {
|
|
31
|
+
const current = normalizedBlocks[index];
|
|
32
|
+
if (current.length < 80) return true;
|
|
33
|
+
|
|
34
|
+
for (let otherIndex = 0; otherIndex < normalizedBlocks.length; otherIndex += 1) {
|
|
35
|
+
if (otherIndex === index) continue;
|
|
36
|
+
const other = normalizedBlocks[otherIndex];
|
|
37
|
+
if (other.length <= current.length + 20) continue;
|
|
38
|
+
if (other.includes(current)) return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function isTitleGeneratorSystemText(text: string): boolean {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const normalized = text.trim().toLowerCase();
|
|
47
|
+
return normalized.includes("you are a title generator") || normalized.includes("generate a brief title");
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export function isTitleGeneratorSystemBlocks(system: SystemBlock[]): boolean {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
return system.some(
|
|
52
|
+
(item) => item.type === "text" && typeof item.text === "string" && isTitleGeneratorSystemText(item.text),
|
|
53
|
+
);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export function normalizeSystemTextBlocks(system: unknown[] | undefined): SystemBlock[] {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
const output: SystemBlock[] = [];
|
|
58
|
+
if (!Array.isArray(system)) return output;
|
|
59
|
+
|
|
60
|
+
for (const item of system) {
|
|
61
|
+
if (typeof item === "string") {
|
|
62
|
+
output.push({ type: "text", text: item });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!item || typeof item !== "object") continue;
|
|
67
|
+
const obj = item as Record<string, unknown>;
|
|
68
|
+
if (typeof obj.text !== "string") continue;
|
|
69
|
+
|
|
70
|
+
const normalized: SystemBlock = {
|
|
71
|
+
type: typeof obj.type === "string" ? obj.type : "text",
|
|
72
|
+
text: obj.text,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Intentionally strip cache_control from incoming system blocks.
|
|
76
|
+
// The plugin controls cache placement: only the identity block gets
|
|
77
|
+
// cache_control (added in buildSystemPromptBlocks). Passing through
|
|
78
|
+
// upstream markers causes "maximum of 4 blocks with cache_control" errors.
|
|
79
|
+
|
|
80
|
+
output.push(normalized);
|
|
64
81
|
}
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
const obj = item as Record<string, unknown>;
|
|
68
|
-
if (typeof obj.text !== "string") continue;
|
|
69
|
-
|
|
70
|
-
const normalized: SystemBlock = {
|
|
71
|
-
type: typeof obj.type === "string" ? obj.type : "text",
|
|
72
|
-
text: obj.text,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Intentionally strip cache_control from incoming system blocks.
|
|
76
|
-
// The plugin controls cache placement: only the identity block gets
|
|
77
|
-
// cache_control (added in buildSystemPromptBlocks). Passing through
|
|
78
|
-
// upstream markers causes "maximum of 4 blocks with cache_control" errors.
|
|
79
|
-
|
|
80
|
-
output.push(normalized);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return output;
|
|
83
|
+
return output;
|
|
84
84
|
}
|
|
@@ -17,41 +17,41 @@ import type { PromptCompactionMode } from "../types.js";
|
|
|
17
17
|
* `opencode-anthropic-fix` or `/path/to/opencode/dist`.
|
|
18
18
|
*/
|
|
19
19
|
export function sanitizeSystemText(text: string, enabled = false): string {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
if (!enabled) return text;
|
|
21
|
+
return text
|
|
22
|
+
.replace(/(?<![\w\-/])OpenCode(?![\w\-/])/g, "Claude Code")
|
|
23
|
+
.replace(/(?<![\w\-/])opencode(?![\w\-/])/gi, "Claude")
|
|
24
|
+
.replace(/OhMyClaude\s*Code/gi, "Claude Code")
|
|
25
|
+
.replace(/OhMyClaudeCode/gi, "Claude Code")
|
|
26
|
+
.replace(/(?<![\w\-/])Sisyphus(?![\w\-/])/g, "Claude Code Agent")
|
|
27
|
+
.replace(/(?<![\w\-/])Morph\s+plugin(?![\w\-/])/gi, "edit plugin")
|
|
28
|
+
.replace(/(?<![\w\-/])morph_edit(?![\w\-/])/g, "edit")
|
|
29
|
+
.replace(/(?<![\w\-/])morph_/g, "")
|
|
30
|
+
.replace(/(?<![\w\-/])OhMyClaude(?![\w\-/])/gi, "Claude");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function compactSystemText(text: string, mode: PromptCompactionMode): string {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const withoutDuplicateIdentityPrefix = text.startsWith(`${CLAUDE_CODE_IDENTITY_STRING}\n`)
|
|
35
|
+
? text.slice(CLAUDE_CODE_IDENTITY_STRING.length).trimStart()
|
|
36
|
+
: text;
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
if (mode === "off") {
|
|
39
|
+
return withoutDuplicateIdentityPrefix.trim();
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const compacted = withoutDuplicateIdentityPrefix.replace(/<example>[\s\S]*?<\/example>/gi, "\n");
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
const dedupedLines: string[] = [];
|
|
45
|
+
let prevNormalized = "";
|
|
46
|
+
for (const line of compacted.split("\n")) {
|
|
47
|
+
const normalized = line.trim();
|
|
48
|
+
if (normalized && normalized === prevNormalized) continue;
|
|
49
|
+
dedupedLines.push(line);
|
|
50
|
+
prevNormalized = normalized;
|
|
51
|
+
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
return dedupedLines
|
|
54
|
+
.join("\n")
|
|
55
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
56
|
+
.trim();
|
|
57
57
|
}
|
package/src/thinking.ts
CHANGED
|
@@ -10,16 +10,16 @@ import type { ThinkingEffort } from "./types.ts";
|
|
|
10
10
|
* Used when an Opus 4.6 request arrives with the legacy budgetTokens shape.
|
|
11
11
|
*/
|
|
12
12
|
export function budgetTokensToEffort(budgetTokens: number): ThinkingEffort {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if (budgetTokens <= 1024) return "low";
|
|
14
|
+
if (budgetTokens <= 8000) return "medium";
|
|
15
|
+
return "high";
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Validate that a given value is a valid ThinkingEffort string.
|
|
20
20
|
*/
|
|
21
21
|
export function isValidEffort(value: unknown): value is ThinkingEffort {
|
|
22
|
-
|
|
22
|
+
return value === "low" || value === "medium" || value === "high";
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -33,25 +33,26 @@ export function isValidEffort(value: unknown): value is ThinkingEffort {
|
|
|
33
33
|
* 3. Absent / disabled: no transform
|
|
34
34
|
*/
|
|
35
35
|
export function normalizeThinkingBlock(thinking: unknown, model: string): unknown {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if (!thinking || typeof thinking !== "object" || (thinking as Record<string, unknown>).type !== "enabled") {
|
|
37
|
+
return thinking;
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
if (!isAdaptiveThinkingModel(model)) {
|
|
41
|
+
// Older models: pass through unchanged (may have budget_tokens)
|
|
42
|
+
return thinking;
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
const t = thinking as Record<string, unknown>;
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
// Adaptive thinking models: use adaptive thinking with effort
|
|
48
|
+
if (isValidEffort(t.effort)) {
|
|
49
|
+
// Already in adaptive shape — just strip any legacy budget_tokens field
|
|
50
|
+
const { budget_tokens: _dropped, ...rest } = t;
|
|
51
|
+
return rest;
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
const effort: ThinkingEffort =
|
|
55
|
+
typeof t.budget_tokens === "number" ? budgetTokensToEffort(t.budget_tokens) : "medium"; // v2.1.68 default
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
return { type: "enabled", effort };
|
|
57
58
|
}
|