clawmoney 0.15.67 → 0.15.68
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/dist/relay/upstream/claude-api.js +106 -21
- package/package.json +1 -1
|
@@ -994,6 +994,56 @@ function mergeBetas(required, clientBeta) {
|
|
|
994
994
|
}
|
|
995
995
|
return out.join(",");
|
|
996
996
|
}
|
|
997
|
+
// Scan a passthrough body for any `cache_control: {type: "ephemeral",
|
|
998
|
+
// ttl: "1h"}` block across tools / system / messages. The presence of
|
|
999
|
+
// even one 1h block forces us to upgrade our own injected CC marker in
|
|
1000
|
+
// system to 1h too, because Anthropic rejects requests where a 1h
|
|
1001
|
+
// block appears after any 5m block in the global tools→system→messages
|
|
1002
|
+
// ordering (see long comment in ensureClaudeCodeShell).
|
|
1003
|
+
//
|
|
1004
|
+
// Returns true on the first 1h block found — this is a detect-only
|
|
1005
|
+
// walk, not a rewrite. Safe on malformed bodies (returns false).
|
|
1006
|
+
function bodyHasExtendedCacheBlock(body) {
|
|
1007
|
+
const isExtendedBlock = (block) => {
|
|
1008
|
+
if (!block || typeof block !== "object")
|
|
1009
|
+
return false;
|
|
1010
|
+
const cc = block
|
|
1011
|
+
.cache_control;
|
|
1012
|
+
if (!cc || typeof cc !== "object")
|
|
1013
|
+
return false;
|
|
1014
|
+
return cc.ttl === "1h";
|
|
1015
|
+
};
|
|
1016
|
+
if (Array.isArray(body.tools)) {
|
|
1017
|
+
for (const t of body.tools) {
|
|
1018
|
+
if (isExtendedBlock(t))
|
|
1019
|
+
return true;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (Array.isArray(body.system)) {
|
|
1023
|
+
for (const b of body.system) {
|
|
1024
|
+
if (isExtendedBlock(b))
|
|
1025
|
+
return true;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (Array.isArray(body.messages)) {
|
|
1029
|
+
for (const m of body.messages) {
|
|
1030
|
+
if (!m || typeof m !== "object")
|
|
1031
|
+
continue;
|
|
1032
|
+
const content = m.content;
|
|
1033
|
+
// Anthropic messages can carry content either as a string (no
|
|
1034
|
+
// cache_control possible) or as an array of content blocks
|
|
1035
|
+
// (each of which can carry cache_control). Only the array form
|
|
1036
|
+
// matters for this check.
|
|
1037
|
+
if (Array.isArray(content)) {
|
|
1038
|
+
for (const c of content) {
|
|
1039
|
+
if (isExtendedBlock(c))
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
997
1047
|
// Ensure a passthrough body carries the full Claude Code fingerprint
|
|
998
1048
|
// shell that Anthropic's OAuth-endpoint validator expects. Called from
|
|
999
1049
|
// doCallClaudeApiPassthrough as the last body-munging step before the
|
|
@@ -1057,34 +1107,69 @@ function ensureClaudeCodeShell(body, fingerprint) {
|
|
|
1057
1107
|
const firstUserMsg = extractFirstUserMessageText(body.messages);
|
|
1058
1108
|
const freshHeader = buildClaudeAttributionHeader(firstUserMsg, fingerprint.cc_version, fingerprint.cc_entrypoint);
|
|
1059
1109
|
// ── Inject CC marker if missing ──
|
|
1060
|
-
// Position: right after the billing header slot (idx 1), or right
|
|
1061
|
-
// after any buyer-prefixed system blocks (at head) if we're also
|
|
1062
|
-
// inserting the billing header.
|
|
1063
1110
|
//
|
|
1064
|
-
// Anthropic has a hard ordering rule
|
|
1065
|
-
//
|
|
1066
|
-
// `ttl="
|
|
1067
|
-
//
|
|
1068
|
-
//
|
|
1069
|
-
//
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1072
|
-
//
|
|
1073
|
-
//
|
|
1074
|
-
//
|
|
1111
|
+
// Anthropic has a hard GLOBAL ordering rule across the whole request:
|
|
1112
|
+
// within the linear processing order `tools → system → messages`,
|
|
1113
|
+
// any block with `cache_control.ttl="1h"` MUST come before any block
|
|
1114
|
+
// with `ttl="5m"`. Not just within one section — globally. A 5m block
|
|
1115
|
+
// in system comes before any 1h block in messages and that's a 400.
|
|
1116
|
+
//
|
|
1117
|
+
// Our injected CC marker lives in system. Its default TTL is 5m
|
|
1118
|
+
// (what real Claude Code uses). When a buyer request carries any 1h
|
|
1119
|
+
// cache_control block ANYWHERE (their own system, or inside any
|
|
1120
|
+
// message content block, or in tools), naively injecting a 5m marker
|
|
1121
|
+
// in system causes:
|
|
1122
|
+
// system.N.cache_control.ttl — when the 1h is in system below us
|
|
1123
|
+
// messages.N.content.M.cache_control.ttl — when the 1h is in messages
|
|
1124
|
+
// Anthropic 400s with:
|
|
1125
|
+
// a ttl='1h' cache_control block must not come after a ttl='5m'
|
|
1126
|
+
// cache_control block. Note that blocks are processed in the
|
|
1127
|
+
// following order: `tools`, `system`, `messages`.
|
|
1128
|
+
//
|
|
1129
|
+
// Fix: detect whether the buyer's body touches 1h cache anywhere.
|
|
1130
|
+
// If yes, upgrade our marker's TTL to 1h too — then the whole request
|
|
1131
|
+
// is uniformly 1h from our side, no 1h-after-5m violation possible.
|
|
1132
|
+
// If no, keep the default 5m (matches real Claude Code fingerprint).
|
|
1133
|
+
//
|
|
1134
|
+
// The 1h TTL won't actually materialise extra cost for our marker
|
|
1135
|
+
// because our system block is < 1024 tokens and below Anthropic's
|
|
1136
|
+
// minimum cache token threshold, so neither 5m nor 1h actually
|
|
1137
|
+
// produces a cache write or read. The TTL label is purely a shape
|
|
1138
|
+
// marker that unblocks the ordering validator.
|
|
1075
1139
|
if (!hasCcMarker) {
|
|
1140
|
+
const buyerUsesExtendedCache = bodyHasExtendedCacheBlock(body);
|
|
1076
1141
|
const markerBlock = {
|
|
1077
1142
|
type: "text",
|
|
1078
1143
|
text: `${CLAUDE_CODE_SYSTEM_PROMPT_LEAD}\n\n${RELAY_INSTRUCTIONS}`,
|
|
1079
|
-
cache_control:
|
|
1144
|
+
cache_control: buyerUsesExtendedCache
|
|
1145
|
+
? { type: "ephemeral", ttl: "1h" }
|
|
1146
|
+
: { type: "ephemeral" },
|
|
1080
1147
|
};
|
|
1148
|
+
// Insert position inside system:
|
|
1149
|
+
// - If our marker is 5m: put it AFTER any existing 1h block in
|
|
1150
|
+
// system so system-internal ordering holds (1h-before-5m).
|
|
1151
|
+
// - If our marker is 1h: put it BEFORE any existing 5m block in
|
|
1152
|
+
// system for the same reason (1h-before-5m). No 5m block →
|
|
1153
|
+
// default slot.
|
|
1081
1154
|
let insertAt = hasBillingHeaderFirst ? 1 : 0;
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1155
|
+
if (buyerUsesExtendedCache) {
|
|
1156
|
+
for (let i = 0; i < system.length; i++) {
|
|
1157
|
+
const cc = system[i]
|
|
1158
|
+
?.cache_control;
|
|
1159
|
+
if (cc && typeof cc === "object" && (cc.ttl ?? "5m") === "5m") {
|
|
1160
|
+
insertAt = i;
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
else {
|
|
1166
|
+
for (let i = system.length - 1; i >= 0; i--) {
|
|
1167
|
+
const cc = system[i]
|
|
1168
|
+
?.cache_control;
|
|
1169
|
+
if (cc && typeof cc === "object" && cc.ttl === "1h") {
|
|
1170
|
+
insertAt = i + 1;
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1088
1173
|
}
|
|
1089
1174
|
}
|
|
1090
1175
|
system.splice(insertAt, 0, markerBlock);
|