@cortexkit/anthropic-auth-core 1.21.0 → 1.23.0

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 (59) hide show
  1. package/README.md +1 -1
  2. package/dist/accounts.d.ts +100 -3
  3. package/dist/accounts.js +1099 -169
  4. package/dist/auth.js +6 -10
  5. package/dist/cachekeep.d.ts +18 -1
  6. package/dist/cachekeep.js +108 -82
  7. package/dist/cch.d.ts +23 -9
  8. package/dist/cch.js +104 -21
  9. package/dist/claude-code.d.ts +8 -3
  10. package/dist/claude-code.js +24 -7
  11. package/dist/claustrum-enrollment-registry.d.ts +17 -0
  12. package/dist/claustrum-enrollment-registry.js +194 -0
  13. package/dist/claustrum-enrollment.d.ts +70 -0
  14. package/dist/claustrum-enrollment.js +485 -0
  15. package/dist/claustrum-scoped-roster.d.ts +23 -0
  16. package/dist/claustrum-scoped-roster.js +194 -0
  17. package/dist/claustrum-scoped-runtime.d.ts +28 -0
  18. package/dist/claustrum-scoped-runtime.js +230 -0
  19. package/dist/claustrum-scoped.d.ts +39 -0
  20. package/dist/claustrum-scoped.js +193 -0
  21. package/dist/claustrum.d.ts +252 -0
  22. package/dist/claustrum.js +1471 -0
  23. package/dist/commands/account.d.ts +43 -11
  24. package/dist/commands/account.js +147 -4
  25. package/dist/constants.d.ts +5 -5
  26. package/dist/constants.js +5 -5
  27. package/dist/custom-headers.d.ts +3 -0
  28. package/dist/custom-headers.js +102 -0
  29. package/dist/dump.js +2 -1
  30. package/dist/fast.js +1 -1
  31. package/dist/index.d.ts +11 -0
  32. package/dist/index.js +11 -0
  33. package/dist/json.d.ts +1 -0
  34. package/dist/json.js +10 -0
  35. package/dist/logger.js +3 -1
  36. package/dist/mid-conversation-output-config.d.ts +13 -0
  37. package/dist/mid-conversation-output-config.js +89 -0
  38. package/dist/model-remap.d.ts +27 -0
  39. package/dist/model-remap.js +72 -0
  40. package/dist/models.d.ts +46 -1
  41. package/dist/models.js +87 -8
  42. package/dist/network-errors.d.ts +1 -0
  43. package/dist/network-errors.js +27 -0
  44. package/dist/oauth-profile.d.ts +2 -1
  45. package/dist/oauth-profile.js +6 -0
  46. package/dist/prime.d.ts +11 -3
  47. package/dist/prime.js +25 -7
  48. package/dist/quota-header-feed.d.ts +32 -7
  49. package/dist/quota-header-feed.js +93 -10
  50. package/dist/quota-headers.js +59 -6
  51. package/dist/quota-manager.d.ts +20 -2
  52. package/dist/quota-manager.js +101 -24
  53. package/dist/relay.d.ts +8 -0
  54. package/dist/relay.js +72 -12
  55. package/dist/sticky-routing.d.ts +2 -0
  56. package/dist/sticky-routing.js +19 -1
  57. package/dist/thinking-binding.d.ts +7 -0
  58. package/dist/thinking-binding.js +54 -0
  59. package/package.json +4 -1
@@ -1,7 +1,12 @@
1
- import type { AccountStorage } from '../accounts.ts';
1
+ import type { AccountStorage, ClaustrumMode, FallbackAccount } from '../accounts.ts';
2
+ import type { ClaustrumDetection, CustodyHandleResolution } from '../claustrum.ts';
3
+ import type { ClaustrumEnrollmentStatus } from '../claustrum-enrollment.ts';
2
4
  export declare const CLAUDE_ACCOUNT_COMMAND_NAME = "claude-account";
3
5
  export type AccountCommandAction = {
4
6
  type: 'status';
7
+ } | {
8
+ type: 'claustrum-mode';
9
+ mode: ClaustrumMode;
5
10
  } | {
6
11
  type: 'enable';
7
12
  id: string;
@@ -17,6 +22,10 @@ export type AccountCommandAction = {
17
22
  } | {
18
23
  type: 'move-down';
19
24
  id: string;
25
+ } | {
26
+ type: 'reset-backoff';
27
+ } | {
28
+ type: 'enrollment-reset';
20
29
  } | {
21
30
  type: 'add-apikey';
22
31
  apiKey: string;
@@ -32,6 +41,17 @@ export type AccountCommandAction = {
32
41
  } | {
33
42
  type: 'usage';
34
43
  };
44
+ export type AccountCommandResult = {
45
+ text: string;
46
+ updated?: {
47
+ id: string;
48
+ action: 'enable' | 'disable' | 'remove' | 'reorder' | 'reset-backoff';
49
+ enabled?: boolean;
50
+ previousOrder?: string[];
51
+ newOrder?: string[];
52
+ };
53
+ };
54
+ export type ClaustrumModeTransition = (mode: ClaustrumMode) => Promise<AccountCommandResult>;
35
55
  export declare function parseAccountCommandAction(argumentsText: string): AccountCommandAction;
36
56
  export interface AccountListItem {
37
57
  id: string;
@@ -41,17 +61,29 @@ export interface AccountListItem {
41
61
  quotaPercent: number | null;
42
62
  tierLabel?: string;
43
63
  }
64
+ export type CustodyStatusState = 'na' | 'off' | 'on-vault-served' | 'on-vault-reauth' | 'on-cold' | 'unknown-identity' | 'on-identity-mismatch' | 'on-corrupt-binding';
65
+ export declare function custodyStatusLabel(state: CustodyStatusState): string;
66
+ export declare function formatEnrollmentStatus(status: ClaustrumEnrollmentStatus, scopedServing?: boolean): string[];
67
+ export type AccountCommandStatusProjection = {
68
+ claustrumDetection: string;
69
+ claustrumEnrollment?: ClaustrumEnrollmentStatus;
70
+ custodyMode?: 'local' | 'claustrum';
71
+ custodyModeKnown?: boolean;
72
+ accounts: Array<AccountListItem & {
73
+ claustrumGate: 'on' | 'off' | 'na';
74
+ vaultServed: boolean;
75
+ vaultReauth: boolean;
76
+ custodyState: CustodyStatusState;
77
+ }>;
78
+ };
44
79
  export declare function buildAccountList(storage: AccountStorage): AccountListItem[];
45
80
  export declare function executeAccountCommand(input: {
46
81
  argumentsText: string;
47
82
  storage: AccountStorage;
48
- }): {
49
- text: string;
50
- updated?: {
51
- id: string;
52
- action: 'enable' | 'disable' | 'remove' | 'reorder';
53
- enabled?: boolean;
54
- previousOrder?: string[];
55
- newOrder?: string[];
56
- };
57
- };
83
+ claustrum?: ClaustrumDetection;
84
+ statusProjection?: AccountCommandStatusProjection;
85
+ resolveCustodyBinding?: (account: FallbackAccount) => CustodyHandleResolution;
86
+ path?: string;
87
+ transition?: ClaustrumModeTransition;
88
+ resetEnrollment?: () => Promise<AccountCommandResult>;
89
+ }): Promise<AccountCommandResult>;
@@ -1,3 +1,4 @@
1
+ import { getClaustrumMode, isOAuthAccount, isOAuthAccountVaultOwned, } from '../accounts.js';
1
2
  import { formatOAuthAccountTier } from '../oauth-profile.js';
2
3
  export const CLAUDE_ACCOUNT_COMMAND_NAME = 'claude-account';
3
4
  export function parseAccountCommandAction(argumentsText) {
@@ -16,6 +17,16 @@ export function parseAccountCommandAction(argumentsText) {
16
17
  return { type: 'move-up', id: rest };
17
18
  if (action === 'move-down' && rest)
18
19
  return { type: 'move-down', id: rest };
20
+ if (action === 'reset-backoff' && !rest)
21
+ return { type: 'reset-backoff' };
22
+ if (action === 'enrollment-reset' && !rest)
23
+ return { type: 'enrollment-reset' };
24
+ if (action === 'claustrum' && !rest) {
25
+ return { type: 'claustrum-mode', mode: 'claustrum' };
26
+ }
27
+ if (action === 'local' && !rest) {
28
+ return { type: 'claustrum-mode', mode: 'local' };
29
+ }
19
30
  if (action === 'add-apikey' && rest) {
20
31
  let remaining = rest;
21
32
  let baseURL;
@@ -81,6 +92,71 @@ export function parseAccountCommandAction(argumentsText) {
81
92
  }
82
93
  return { type: 'usage' };
83
94
  }
95
+ export function custodyStatusLabel(state) {
96
+ switch (state) {
97
+ case 'na':
98
+ return 'n/a (OpenCode-managed)';
99
+ case 'off':
100
+ return 'not enrolled';
101
+ case 'on-vault-served':
102
+ return 'vault-served';
103
+ case 'on-vault-reauth':
104
+ return 'vault reauth';
105
+ case 'on-cold':
106
+ return 'vault cold';
107
+ case 'unknown-identity':
108
+ return 'unknown identity';
109
+ case 'on-identity-mismatch':
110
+ return 'identity mismatch';
111
+ case 'on-corrupt-binding':
112
+ return 'corrupt binding';
113
+ }
114
+ }
115
+ export function formatEnrollmentStatus(status, scopedServing = false) {
116
+ switch (status.state) {
117
+ case 'idle':
118
+ return ['- Enrollment: not enrolled'];
119
+ case 'busy':
120
+ return ['- Enrollment: another plugin process is reconciling'];
121
+ case 'unavailable':
122
+ return ['- Enrollment: temporarily unavailable'];
123
+ case 'pending':
124
+ return status.requestId && /^[A-Za-z0-9_-]{1,128}$/.test(status.requestId)
125
+ ? [
126
+ `- Enrollment: pending approval (${status.requestId})`,
127
+ `- Approve: \`ck auth enroll approve --request-id ${status.requestId}\``,
128
+ ]
129
+ : status.requestId
130
+ ? [
131
+ '- Enrollment: pending approval; inspect it with `ck auth enroll list`',
132
+ ]
133
+ : ['- Enrollment: preparing request'];
134
+ case 'approved': {
135
+ const name = status.approvedName ?? status.proposedName;
136
+ return /^[A-Za-z0-9_-]{1,128}$/.test(name)
137
+ ? [
138
+ `- Enrollment: approved as enrolled:${name} (generation ${status.tokenGeneration}${scopedServing ? '' : '; scoped serving not active yet'})`,
139
+ ...(scopedServing
140
+ ? []
141
+ : [
142
+ `- Grant: \`ck auth grant --principal enrolled:${name} --selector-kind category --selector anthropic-native --operation read\``,
143
+ ]),
144
+ ]
145
+ : [
146
+ `- Enrollment: approved (generation ${status.tokenGeneration}${scopedServing ? '' : '; scoped serving not active yet'})`,
147
+ ...(scopedServing
148
+ ? []
149
+ : [
150
+ '- Inspect the approved name with `ck auth enroll list` before granting access.',
151
+ ]),
152
+ ];
153
+ }
154
+ case 'denied':
155
+ return ['- Enrollment: denied by operator'];
156
+ case 'blocked':
157
+ return [`- Enrollment: blocked (${status.code})`];
158
+ }
159
+ }
84
160
  export function buildAccountList(storage) {
85
161
  const list = [];
86
162
  const fiveHour = storage.quota?.mainQuota?.five_hour;
@@ -113,24 +189,52 @@ const USAGE_TEXT = [
113
189
  ' /claude-account enable <id> Enable a fallback account',
114
190
  ' /claude-account disable <id> Disable a fallback account',
115
191
  ' /claude-account remove <id> Remove a fallback account',
192
+ ' /claude-account claustrum Enable Claustrum mode',
193
+ ' /claude-account local Enable local mode',
116
194
  ' /claude-account move-up <id> Move a fallback account up',
117
195
  ' /claude-account move-down <id> Move a fallback account down',
196
+ ' /claude-account reset-backoff Clear main OAuth refresh and quota backoff',
197
+ ' /claude-account enrollment-reset Retry a denied/blocked enrollment',
118
198
  ' /claude-account add-apikey <key> Add an API key fallback account',
119
199
  ' /claude-account add-oauth-start Start OAuth device flow',
120
200
  ' /claude-account add-oauth-finish <code> Complete OAuth flow',
121
201
  ].join('\n');
122
- export function executeAccountCommand(input) {
202
+ export async function executeAccountCommand(input) {
123
203
  const action = parseAccountCommandAction(input.argumentsText);
124
204
  const accounts = input.storage.accounts;
125
205
  const mainId = 'main';
126
206
  if (action.type === 'status') {
127
- const list = buildAccountList(input.storage);
128
- const lines = ['## Claude Accounts', ''];
207
+ const list = input.statusProjection?.accounts ?? buildAccountList(input.storage);
208
+ const detection = input.statusProjection?.claustrumDetection ??
209
+ input.claustrum?.status ??
210
+ 'unknown';
211
+ const lines = [
212
+ '## Claude Accounts',
213
+ '',
214
+ `- Custody mode: ${getClaustrumMode(input.storage)}`,
215
+ `- Claustrum: ${detection}`,
216
+ ...(input.statusProjection?.claustrumEnrollment
217
+ ? formatEnrollmentStatus(input.statusProjection.claustrumEnrollment)
218
+ : []),
219
+ '',
220
+ ];
129
221
  for (const a of list) {
130
222
  const pct = a.quotaPercent != null ? ` ${Math.round(a.quotaPercent)}%` : '';
131
223
  const status = !a.enabled ? ' (disabled)' : '';
132
224
  const tier = a.tierLabel ? ` · ${a.tierLabel}` : '';
133
- lines.push(`- **${a.label}** [${a.role}]${tier}${status}${pct}`);
225
+ const projected = input.statusProjection?.accounts.find((account) => account.id === a.id);
226
+ const storedAccount = input.storage.accounts.find((account) => account.id === a.id);
227
+ const binding = storedAccount
228
+ ? input.resolveCustodyBinding?.(storedAccount)
229
+ : undefined;
230
+ const custody = projected
231
+ ? custodyStatusLabel(projected.custodyState)
232
+ : a.id !== mainId &&
233
+ storedAccount &&
234
+ isOAuthAccountVaultOwned(input.storage, storedAccount, binding)
235
+ ? custodyStatusLabel('on-cold')
236
+ : 'local';
237
+ lines.push(`- **${a.label}** [${a.role}]${tier}${status}${pct} · ${custody}`);
134
238
  }
135
239
  lines.push('', USAGE_TEXT);
136
240
  return { text: lines.join('\n') };
@@ -138,6 +242,23 @@ export function executeAccountCommand(input) {
138
242
  if (action.type === 'usage') {
139
243
  return { text: USAGE_TEXT };
140
244
  }
245
+ if (action.type === 'claustrum-mode') {
246
+ if (!input.transition) {
247
+ return { text: 'Claustrum mode transition is unavailable.' };
248
+ }
249
+ return input.transition(action.mode);
250
+ }
251
+ if (action.type === 'enrollment-reset') {
252
+ if (getClaustrumMode(input.storage) !== 'claustrum') {
253
+ return {
254
+ text: 'Claustrum enrollment is available only in Claustrum mode.',
255
+ };
256
+ }
257
+ if (!input.resetEnrollment) {
258
+ return { text: 'Claustrum enrollment reset is unavailable.' };
259
+ }
260
+ return input.resetEnrollment();
261
+ }
141
262
  if (action.type === 'add-apikey') {
142
263
  return { text: 'add-apikey' };
143
264
  }
@@ -147,6 +268,12 @@ export function executeAccountCommand(input) {
147
268
  if (action.type === 'add-oauth-finish') {
148
269
  return { text: 'add-oauth-finish' };
149
270
  }
271
+ if (action.type === 'reset-backoff') {
272
+ return {
273
+ text: 'Main OAuth refresh and quota backoff cleared.',
274
+ updated: { id: 'main', action: 'reset-backoff' },
275
+ };
276
+ }
150
277
  const id = action.id;
151
278
  if (id === mainId) {
152
279
  return {
@@ -172,6 +299,22 @@ export function executeAccountCommand(input) {
172
299
  if (!target) {
173
300
  return { text: `Account "${id}" not found.` };
174
301
  }
302
+ if (getClaustrumMode(input.storage) === 'claustrum' &&
303
+ isOAuthAccount(target) &&
304
+ target.claustrumScopedCredentialId) {
305
+ return {
306
+ text: `Account "${target.label ?? id}" is managed by Claustrum. Disable it to stop routing, or remove it from the vault.`,
307
+ };
308
+ }
309
+ const binding = input.resolveCustodyBinding?.(target);
310
+ if (getClaustrumMode(input.storage) === 'claustrum' &&
311
+ isOAuthAccount(target) &&
312
+ binding?.status === 'resolved' &&
313
+ binding.source === 'manifest') {
314
+ return {
315
+ text: `Account "${target.label ?? id}" is bound by the Claustrum manifest. Disable it to stop routing, or remove its Claustrum binding before removing the routing row.`,
316
+ };
317
+ }
175
318
  return {
176
319
  text: `Account "${target.label ?? id}" removed.`,
177
320
  updated: { id, action: 'remove' },
@@ -5,6 +5,7 @@ export declare const AUTHORIZE_URLS: {
5
5
  };
6
6
  export declare const CODE_CALLBACK_URL = "https://platform.claude.com/oauth/code/callback";
7
7
  export declare const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
8
+ export declare const CUSTODY_HANDLE_PATTERN: RegExp;
8
9
  export declare const AXIOS_USER_AGENT = "axios/1.15.2";
9
10
  export declare const OAUTH_SCOPES: string[];
10
11
  export declare const REFRESH_SCOPE = "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
@@ -18,12 +19,11 @@ export declare const CLAUDE_CODE_IDENTITY = "You are Claude Code, Anthropic's of
18
19
  export declare const PARALLEL_TOOL_CALLS_SYSTEM_PROMPT: string;
19
20
  export declare const CCH_SALT = "59cf53e54c78";
20
21
  export declare const CCH_POSITIONS: number[];
21
- export declare const CLAUDE_CODE_VERSION = "2.1.177";
22
- export declare const CLAUDE_CODE_BUILD_HASH = "3bf";
22
+ export declare const CLAUDE_CODE_VERSION = "2.1.280";
23
23
  export declare const CLAUDE_CODE_ENTRYPOINT = "cli";
24
- export declare const CLAUDE_CODE_STAINLESS_PACKAGE_VERSION = "0.94.0";
25
- export declare const CLAUDE_CODE_STAINLESS_RUNTIME_VERSION = "v24.3.0";
26
- export declare const USER_AGENT = "claude-cli/2.1.177 (external, cli)";
24
+ export declare const CLAUDE_CODE_STAINLESS_PACKAGE_VERSION = "0.112.1";
25
+ export declare const CLAUDE_CODE_STAINLESS_RUNTIME_VERSION = "v26.3.0";
26
+ export declare const USER_AGENT = "claude-cli/2.1.280 (external, cli)";
27
27
  export declare const CACHE_1H_MODES: readonly ['explicit', 'automatic', 'hybrid'];
28
28
  export type Cache1hMode = (typeof CACHE_1H_MODES)[number];
29
29
  export declare const DEFAULT_CACHE_1H_MODE: Cache1hMode;
package/dist/constants.js CHANGED
@@ -5,6 +5,7 @@ export const AUTHORIZE_URLS = {
5
5
  };
6
6
  export const CODE_CALLBACK_URL = 'https://platform.claude.com/oauth/code/callback';
7
7
  export const TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';
8
+ export const CUSTODY_HANDLE_PATTERN = /^ckh_[A-Za-z0-9_-]{43}$/;
8
9
  export const AXIOS_USER_AGENT = 'axios/1.15.2';
9
10
  export const OAUTH_SCOPES = [
10
11
  'org:create_api_key',
@@ -46,12 +47,11 @@ export const PARALLEL_TOOL_CALLS_SYSTEM_PROMPT = [
46
47
  ].join('\n');
47
48
  export const CCH_SALT = '59cf53e54c78';
48
49
  export const CCH_POSITIONS = [4, 7, 20];
49
- export const CLAUDE_CODE_VERSION = '2.1.177';
50
- export const CLAUDE_CODE_BUILD_HASH = '3bf';
50
+ export const CLAUDE_CODE_VERSION = '2.1.280';
51
51
  export const CLAUDE_CODE_ENTRYPOINT = 'cli';
52
- export const CLAUDE_CODE_STAINLESS_PACKAGE_VERSION = '0.94.0';
53
- export const CLAUDE_CODE_STAINLESS_RUNTIME_VERSION = 'v24.3.0';
54
- export const USER_AGENT = 'claude-cli/2.1.177 (external, cli)';
52
+ export const CLAUDE_CODE_STAINLESS_PACKAGE_VERSION = '0.112.1';
53
+ export const CLAUDE_CODE_STAINLESS_RUNTIME_VERSION = 'v26.3.0';
54
+ export const USER_AGENT = `claude-cli/${CLAUDE_CODE_VERSION} (external, ${CLAUDE_CODE_ENTRYPOINT})`;
55
55
  export const CACHE_1H_MODES = ['explicit', 'automatic', 'hybrid'];
56
56
  export const DEFAULT_CACHE_1H_MODE = 'explicit';
57
57
  /**
@@ -0,0 +1,3 @@
1
+ export declare const ANTHROPIC_CUSTOM_HEADERS_ENV = "ANTHROPIC_CUSTOM_HEADERS";
2
+ export declare function parseCustomHeaders(raw: string | undefined): Headers;
3
+ export declare function applyCustomHeaders(headers: Headers, raw?: string | undefined): Headers;
@@ -0,0 +1,102 @@
1
+ import { parseJsonRedacted } from './json.js';
2
+ import { logger } from './logger.js';
3
+ export const ANTHROPIC_CUSTOM_HEADERS_ENV = 'ANTHROPIC_CUSTOM_HEADERS';
4
+ const CUSTOM_HEADER_CACHE_LIMIT = 64;
5
+ const parsedHeadersByRawValue = new Map();
6
+ const warnedMalformedRawValues = new Set();
7
+ // Proxy-specific metadata is allowed, but custom configuration must never
8
+ // replace route authentication, protocol negotiation, body framing, or
9
+ // plugin-internal correlation headers after those guards have run.
10
+ const PROTECTED_HEADERS = new Set([
11
+ 'authorization',
12
+ 'proxy-authorization',
13
+ 'x-api-key',
14
+ 'host',
15
+ 'content-length',
16
+ 'content-type',
17
+ 'connection',
18
+ 'transfer-encoding',
19
+ 'upgrade',
20
+ 'keep-alive',
21
+ 'te',
22
+ 'trailer',
23
+ 'anthropic-version',
24
+ 'anthropic-beta',
25
+ 'x-parent-session-id',
26
+ 'x-session-affinity',
27
+ 'x-opencode-session',
28
+ 'x-anthropic-effort-plan',
29
+ 'x-cortexkit-billing-lineage',
30
+ ]);
31
+ function cacheParsed(raw, entries) {
32
+ parsedHeadersByRawValue.delete(raw);
33
+ parsedHeadersByRawValue.set(raw, entries);
34
+ while (parsedHeadersByRawValue.size > CUSTOM_HEADER_CACHE_LIMIT) {
35
+ const oldest = parsedHeadersByRawValue.keys().next().value;
36
+ if (oldest === undefined)
37
+ break;
38
+ parsedHeadersByRawValue.delete(oldest);
39
+ warnedMalformedRawValues.delete(oldest);
40
+ }
41
+ }
42
+ function setCustomHeader(headers, name, value) {
43
+ if (PROTECTED_HEADERS.has(name.trim().toLowerCase())) {
44
+ throw new TypeError('protected header');
45
+ }
46
+ headers.set(name, value);
47
+ }
48
+ export function parseCustomHeaders(raw) {
49
+ if (!raw?.trim())
50
+ return new Headers();
51
+ const cached = parsedHeadersByRawValue.get(raw);
52
+ if (cached !== undefined || parsedHeadersByRawValue.has(raw)) {
53
+ return new Headers(cached ?? []);
54
+ }
55
+ try {
56
+ const headers = new Headers();
57
+ const trimmed = raw.trim();
58
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
59
+ for (const entry of trimmed
60
+ .split(/\r?\n|,\s*(?=[^,\s:]+:)/)
61
+ .map((value) => value.trim())
62
+ .filter(Boolean)) {
63
+ const separator = entry.indexOf(':');
64
+ if (separator <= 0) {
65
+ throw new TypeError('invalid header entry');
66
+ }
67
+ setCustomHeader(headers, entry.slice(0, separator).trim(), entry.slice(separator + 1).trim());
68
+ }
69
+ }
70
+ else {
71
+ const parsed = parseJsonRedacted(trimmed);
72
+ if (parsed == null ||
73
+ typeof parsed !== 'object' ||
74
+ Array.isArray(parsed)) {
75
+ throw new TypeError('invalid header object');
76
+ }
77
+ for (const [key, value] of Object.entries(parsed)) {
78
+ if (value == null)
79
+ continue;
80
+ setCustomHeader(headers, key, Array.isArray(value) ? value.map(String).join(', ') : String(value));
81
+ }
82
+ }
83
+ const entries = [...headers.entries()];
84
+ cacheParsed(raw, entries);
85
+ return new Headers(entries);
86
+ }
87
+ catch {
88
+ cacheParsed(raw, null);
89
+ if (!warnedMalformedRawValues.has(raw)) {
90
+ warnedMalformedRawValues.add(raw);
91
+ logger.warn('custom-headers', 'ignoring malformed ANTHROPIC_CUSTOM_HEADERS', { reason: 'invalid or protected header configuration' });
92
+ }
93
+ return new Headers();
94
+ }
95
+ }
96
+ export function applyCustomHeaders(headers, raw = process.env[ANTHROPIC_CUSTOM_HEADERS_ENV]) {
97
+ const customHeaders = parseCustomHeaders(raw);
98
+ customHeaders.forEach((value, key) => {
99
+ headers.set(key, value);
100
+ });
101
+ return headers;
102
+ }
package/dist/dump.js CHANGED
@@ -3,6 +3,7 @@ import { lstat, mkdir, readdir, readFile, rename, unlink, writeFile, } from 'nod
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { extractBillingHeaderCCH } from './cch.js';
6
+ import { parseJsonRedacted } from './json.js';
6
7
  import { isSecretKey, logger, relayLog } from './logger.js';
7
8
  export const CLAUDE_DUMP_COMMAND_NAME = 'claude-dump';
8
9
  const DUMP_STATUS_TITLE = '## Claude Dump Status';
@@ -425,7 +426,7 @@ function diffSummary(previousBodyText, bodyText) {
425
426
  }
426
427
  function parseBody(bodyText) {
427
428
  try {
428
- const parsed = JSON.parse(bodyText);
429
+ const parsed = parseJsonRedacted(bodyText);
429
430
  return parsed != null &&
430
431
  typeof parsed === 'object' &&
431
432
  !Array.isArray(parsed)
package/dist/fast.js CHANGED
@@ -32,7 +32,7 @@ export function buildFastModeStatusSummary(input) {
32
32
  `- Enabled: ${enabled ? 'enabled' : 'disabled'}`,
33
33
  '- Persisted: ~/.config/opencode/anthropic-auth.json',
34
34
  '- Scope: adds Anthropic fast mode to supported Opus requests',
35
- '- Supported models: claude-opus-4-6, claude-opus-4-7, claude-opus-4-8, and claude-opus-5',
35
+ '- Supported models: claude-opus-4-6, claude-opus-4-7, claude-opus-4-8, claude-opus-5, and claude-opus-5-5',
36
36
  '- Request changes: adds `speed: "fast"` and the `fast-mode-2026-02-01` beta header',
37
37
  '- Note: fast and standard speeds do not share prompt-cache prefixes',
38
38
  ].join('\n');
package/dist/index.d.ts CHANGED
@@ -5,13 +5,23 @@ export * from './cachekeep.ts';
5
5
  export * from './cachekeep-registry.ts';
6
6
  export * from './cch.ts';
7
7
  export * from './claude-code.ts';
8
+ export * from './claustrum.ts';
9
+ export * from './claustrum-enrollment.ts';
10
+ export * from './claustrum-enrollment-registry.ts';
11
+ export * from './claustrum-scoped.ts';
12
+ export * from './claustrum-scoped-roster.ts';
13
+ export * from './claustrum-scoped-runtime.ts';
8
14
  export * from './commands/account.ts';
9
15
  export * from './constants.ts';
16
+ export * from './custom-headers.ts';
10
17
  export * from './dump.ts';
11
18
  export * from './fast.ts';
19
+ export * from './json.ts';
12
20
  export * from './killswitch.ts';
13
21
  export * from './logger.ts';
14
22
  export * from './logging.ts';
23
+ export * from './mid-conversation-output-config.ts';
24
+ export * from './model-remap.ts';
15
25
  export * from './models.ts';
16
26
  export * from './oauth-profile.ts';
17
27
  export * from './pkce.ts';
@@ -25,3 +35,4 @@ export * from './relay.ts';
25
35
  export * from './routing.ts';
26
36
  export * from './start.ts';
27
37
  export * from './sticky-routing.ts';
38
+ export * from './thinking-binding.ts';
package/dist/index.js CHANGED
@@ -5,13 +5,23 @@ export * from './cachekeep.js';
5
5
  export * from './cachekeep-registry.js';
6
6
  export * from './cch.js';
7
7
  export * from './claude-code.js';
8
+ export * from './claustrum.js';
9
+ export * from './claustrum-enrollment.js';
10
+ export * from './claustrum-enrollment-registry.js';
11
+ export * from './claustrum-scoped.js';
12
+ export * from './claustrum-scoped-roster.js';
13
+ export * from './claustrum-scoped-runtime.js';
8
14
  export * from './commands/account.js';
9
15
  export * from './constants.js';
16
+ export * from './custom-headers.js';
10
17
  export * from './dump.js';
11
18
  export * from './fast.js';
19
+ export * from './json.js';
12
20
  export * from './killswitch.js';
13
21
  export * from './logger.js';
14
22
  export * from './logging.js';
23
+ export * from './mid-conversation-output-config.js';
24
+ export * from './model-remap.js';
15
25
  export * from './models.js';
16
26
  export * from './oauth-profile.js';
17
27
  export * from './pkce.js';
@@ -25,3 +35,4 @@ export * from './relay.js';
25
35
  export * from './routing.js';
26
36
  export * from './start.js';
27
37
  export * from './sticky-routing.js';
38
+ export * from './thinking-binding.js';
package/dist/json.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function parseJsonRedacted(text: string): unknown;
package/dist/json.js ADDED
@@ -0,0 +1,10 @@
1
+ export function parseJsonRedacted(text) {
2
+ try {
3
+ return JSON.parse(text);
4
+ }
5
+ catch (error) {
6
+ if (error instanceof SyntaxError)
7
+ throw new SyntaxError('invalid JSON');
8
+ throw error;
9
+ }
10
+ }
package/dist/logger.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import * as path from 'node:path';
4
+ import { CUSTODY_HANDLE_PATTERN } from './constants.js';
4
5
  const LEVEL_ORDER = {
5
6
  error: 0,
6
7
  warn: 1,
@@ -75,7 +76,7 @@ function rotateLogFile() {
75
76
  }
76
77
  }
77
78
  // -- Redaction ------------------------------------------------------------
78
- export const SECRET_KEY_EXACT = /^(authorization|x-api-key|cookie|set-cookie|refresh|access|token)$/i;
79
+ export const SECRET_KEY_EXACT = /^(authorization|x-api-key|cookie|set-cookie|refresh|access|token|handle)$/i;
79
80
  export function isSecretKey(key) {
80
81
  if (SECRET_KEY_EXACT.test(key))
81
82
  return true;
@@ -92,6 +93,7 @@ const REDACT_VALUE_PATTERNS = [
92
93
  /Bearer\s+\S+/,
93
94
  /sk-[A-Za-z0-9-]+/,
94
95
  /eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/,
96
+ CUSTODY_HANDLE_PATTERN,
95
97
  ];
96
98
  function isRedactableValue(value) {
97
99
  if (typeof value !== 'string')
@@ -0,0 +1,13 @@
1
+ export declare const MID_CONVERSATION_OUTPUT_CONFIG_BETA = "mid-conversation-output-config-2026-07-01";
2
+ export type AdaptiveEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
3
+ export type MidConversationEffortTransition = {
4
+ /** Number of assistant messages preceding the user boundary for this effort. */
5
+ afterAssistantMessages: number;
6
+ effort: AdaptiveEffort;
7
+ };
8
+ export declare function normalizeAdaptiveEffort(value: unknown): AdaptiveEffort | undefined;
9
+ export declare function usesMidConversationOutputConfig(body: Record<string, unknown>): boolean;
10
+ export declare function applyMidConversationOutputConfig(body: Record<string, unknown>, transitions: readonly MidConversationEffortTransition[]): {
11
+ applied: boolean;
12
+ inserted: number;
13
+ };