opencode-anthropic-fix 0.1.12 → 0.1.14
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/index.mjs +37 -3
- package/lib/config.mjs +12 -0
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -3721,7 +3721,7 @@ export async function AnthropicAuthPlugin({ client, project, directory, worktree
|
|
|
3721
3721
|
cacheBreakState.lastAlertTurn !== sessionMetrics.turns
|
|
3722
3722
|
) {
|
|
3723
3723
|
const drop = cacheBreakState.prevCacheRead - cacheRead;
|
|
3724
|
-
let alertMsg = `Cache break detected (
|
|
3724
|
+
let alertMsg = `Cache break detected (−${drop.toLocaleString()} tokens)`;
|
|
3725
3725
|
|
|
3726
3726
|
// Identify changed sources if we have pending hashes
|
|
3727
3727
|
if (cacheBreakState._pendingHashes && cacheBreakState.sourceHashes.size > 0) {
|
|
@@ -4327,6 +4327,30 @@ function extractCacheSourceHashes(bodyStr, parsedBody = undefined) {
|
|
|
4327
4327
|
}
|
|
4328
4328
|
}
|
|
4329
4329
|
}
|
|
4330
|
+
|
|
4331
|
+
// Hash messages prefix (everything except the last message) so we can
|
|
4332
|
+
// detect when the conversation history prefix changes byte-for-byte
|
|
4333
|
+
// between turns — a common cause of prompt-cache invalidation that
|
|
4334
|
+
// system_prompt/tool hashes alone don't explain.
|
|
4335
|
+
if (Array.isArray(parsed.messages) && parsed.messages.length > 1) {
|
|
4336
|
+
const prefix = parsed.messages.slice(0, -1);
|
|
4337
|
+
// Strip cache_control markers before hashing — they're legitimately
|
|
4338
|
+
// re-stamped each turn and shouldn't trigger a false positive.
|
|
4339
|
+
const normalized = prefix.map((m) => {
|
|
4340
|
+
if (!Array.isArray(m.content)) return m;
|
|
4341
|
+
return {
|
|
4342
|
+
...m,
|
|
4343
|
+
content: m.content.map((b) => {
|
|
4344
|
+
if (b && typeof b === "object" && b.cache_control) {
|
|
4345
|
+
const { cache_control: _cc, ...rest } = b;
|
|
4346
|
+
return rest;
|
|
4347
|
+
}
|
|
4348
|
+
return b;
|
|
4349
|
+
}),
|
|
4350
|
+
};
|
|
4351
|
+
});
|
|
4352
|
+
hashes.set("messages_prefix", hashCacheSource(JSON.stringify(normalized)));
|
|
4353
|
+
}
|
|
4330
4354
|
} catch {
|
|
4331
4355
|
// Ignore parse errors
|
|
4332
4356
|
}
|
|
@@ -7424,7 +7448,17 @@ function transformRequestBody(body, signature, runtime, betaHeader, config) {
|
|
|
7424
7448
|
const tes = runtime?.tokenEconomySession;
|
|
7425
7449
|
const isMainRole = runtime?.requestRole === "main" || runtime?.requestRole == null;
|
|
7426
7450
|
|
|
7427
|
-
|
|
7451
|
+
// `conservative` (default ON) disables all history-rewriting and tool-array
|
|
7452
|
+
// transforms. These optimizations shrink each request body but cause the
|
|
7453
|
+
// prompt-cache prefix to change turn-to-turn, invalidating the 1h cache
|
|
7454
|
+
// and forcing a fresh cache_write each turn — which costs 2x base input
|
|
7455
|
+
// tokens. For long opencode sessions, cache reuse dominates; flip to
|
|
7456
|
+
// `false` only if you have measurements showing otherwise. Adaptive
|
|
7457
|
+
// thinking zero-out remains active (affects only the thinking budget,
|
|
7458
|
+
// not cached content).
|
|
7459
|
+
const conservative = te.conservative !== false;
|
|
7460
|
+
|
|
7461
|
+
if (!conservative && isMainRole && Array.isArray(parsed.messages) && tes) {
|
|
7428
7462
|
// (1) TTL-based thinking strip
|
|
7429
7463
|
if (te.ttl_thinking_strip !== false) {
|
|
7430
7464
|
const ttlMs = signature?.cachePolicy?.ttl === "5m" ? 5 * 60_000 : 60 * 60_000;
|
|
@@ -7474,7 +7508,7 @@ function transformRequestBody(body, signature, runtime, betaHeader, config) {
|
|
|
7474
7508
|
}
|
|
7475
7509
|
|
|
7476
7510
|
// === Token economy: tool-array transforms (stable ordering, deferral) ===
|
|
7477
|
-
if (isMainRole && Array.isArray(parsed.tools)) {
|
|
7511
|
+
if (!conservative && isMainRole && Array.isArray(parsed.tools)) {
|
|
7478
7512
|
if (te.stable_tool_ordering !== false) {
|
|
7479
7513
|
parsed.tools = applyStableToolOrdering(parsed.tools);
|
|
7480
7514
|
}
|
package/lib/config.mjs
CHANGED
|
@@ -201,6 +201,17 @@ export const DEFAULT_CONFIG = {
|
|
|
201
201
|
* classified as "main-thread" (see classifyRequestRole). */
|
|
202
202
|
context_hint: false,
|
|
203
203
|
|
|
204
|
+
/** Conservative mode (default ON). Disables every history-rewriting and
|
|
205
|
+
* tool-array transform below that can invalidate the prompt-cache prefix
|
|
206
|
+
* turn-to-turn. Rationale: every rewrite risks breaking the 1h cache,
|
|
207
|
+
* forcing a fresh cache_write (2x base input cost). For long opencode
|
|
208
|
+
* sessions, cache reuse dominates per-turn cost — the optimizations below
|
|
209
|
+
* save kB in the request body but cost much more in cache misses.
|
|
210
|
+
* Affects: ttl_thinking_strip, proactive_microcompact, trailing_summary_trim,
|
|
211
|
+
* tool_result_dedupe, stable_tool_ordering, deferred_tool_names.
|
|
212
|
+
* Unaffected: adaptive_thinking_zero_simple (only changes thinking budget). */
|
|
213
|
+
conservative: true,
|
|
214
|
+
|
|
204
215
|
/** TTL-based thinking strip (CC parity). When the time since the last
|
|
205
216
|
* thinking strip exceeds the cache TTL, drop all `thinking` /
|
|
206
217
|
* `redacted_thinking` blocks from prior assistant messages. Mirrors CC's
|
|
@@ -644,6 +655,7 @@ function validateConfig(raw) {
|
|
|
644
655
|
redact_thinking:
|
|
645
656
|
typeof te.redact_thinking === "boolean" ? te.redact_thinking : DEFAULT_CONFIG.token_economy.redact_thinking,
|
|
646
657
|
context_hint: typeof te.context_hint === "boolean" ? te.context_hint : DEFAULT_CONFIG.token_economy.context_hint,
|
|
658
|
+
conservative: typeof te.conservative === "boolean" ? te.conservative : DEFAULT_CONFIG.token_economy.conservative,
|
|
647
659
|
ttl_thinking_strip:
|
|
648
660
|
typeof te.ttl_thinking_strip === "boolean"
|
|
649
661
|
? te.ttl_thinking_strip
|