@ex-machina/opencode-anthropic-auth 2.0.0-next.1 → 2.0.0-next.3

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.
@@ -0,0 +1,10 @@
1
+ export declare const MAX_JSON_TOOL_NAME_BYTES = 64;
2
+ export declare const MAX_JSON_STRING_BYTES: number;
3
+ export declare const MAX_JSON_NUMBER_BYTES = 128;
4
+ export declare const MAX_JSON_DEPTH = 256;
5
+ export declare const MAX_JSON_OBJECT_KEYS = 100000;
6
+ export declare const MAX_JSON_RETAINED_KEY_BYTES: number;
7
+ export declare const MAX_JSON_NODES = 100000;
8
+ export declare const MAX_JSON_PENDING_BLOCK_BYTES: number;
9
+ export declare function assertWellFormedUtf16(value: string): void;
10
+ export declare function createBoundedJsonToolNameStream(body: ReadableStream<Uint8Array>, toolPrefix: string, rewriteName: (name: string) => string | undefined): ReadableStream<Uint8Array>;
@@ -0,0 +1,638 @@
1
+ import { Tokenizer, TokenType } from '@streamparser/json';
2
+ export const MAX_JSON_TOOL_NAME_BYTES = 64;
3
+ export const MAX_JSON_STRING_BYTES = 8 * 1024 * 1024;
4
+ export const MAX_JSON_NUMBER_BYTES = 128;
5
+ export const MAX_JSON_DEPTH = 256;
6
+ export const MAX_JSON_OBJECT_KEYS = 100_000;
7
+ export const MAX_JSON_RETAINED_KEY_BYTES = 8 * 1024 * 1024;
8
+ export const MAX_JSON_NODES = 100_000;
9
+ export const MAX_JSON_PENDING_BLOCK_BYTES = 8 * 1024 * 1024;
10
+ const TOKENIZER_SLICE_BYTES = 1024;
11
+ const encoder = new TextEncoder();
12
+ class ByteQueue {
13
+ chunks = [];
14
+ head = 0;
15
+ offset = 0;
16
+ length = 0;
17
+ append(bytes) {
18
+ if (bytes.byteLength === 0)
19
+ return;
20
+ this.chunks.push({
21
+ start: this.offset + this.length,
22
+ bytes: bytes.slice(),
23
+ });
24
+ this.length += bytes.byteLength;
25
+ }
26
+ get start() {
27
+ return this.offset;
28
+ }
29
+ get size() {
30
+ return this.length;
31
+ }
32
+ takeTo(end) {
33
+ const count = end - this.offset;
34
+ if (count < 0 || count > this.length)
35
+ throw malformedJson();
36
+ const output = new Uint8Array(count);
37
+ let written = 0;
38
+ while (written < count) {
39
+ const entry = this.chunks[this.head];
40
+ if (!entry)
41
+ throw malformedJson();
42
+ const begin = this.offset - entry.start;
43
+ const available = entry.bytes.byteLength - begin;
44
+ const consumed = Math.min(available, count - written);
45
+ output.set(entry.bytes.subarray(begin, begin + consumed), written);
46
+ written += consumed;
47
+ this.offset += consumed;
48
+ this.length -= consumed;
49
+ if (begin + consumed === entry.bytes.byteLength)
50
+ this.head += 1;
51
+ }
52
+ if (this.head >= 1024 && this.head * 2 >= this.chunks.length) {
53
+ this.chunks = this.chunks.slice(this.head);
54
+ this.head = 0;
55
+ }
56
+ return output;
57
+ }
58
+ bytesFrom(start) {
59
+ if (start < this.offset || start > this.offset + this.length) {
60
+ throw malformedJson();
61
+ }
62
+ const output = new Uint8Array(this.offset + this.length - start);
63
+ let outputOffset = 0;
64
+ let low = this.head;
65
+ let high = this.chunks.length;
66
+ while (low < high) {
67
+ const middle = low + ((high - low) >> 1);
68
+ const entry = this.chunks[middle];
69
+ if (!entry)
70
+ throw malformedJson();
71
+ if (entry.start + entry.bytes.byteLength <= start)
72
+ low = middle + 1;
73
+ else
74
+ high = middle;
75
+ }
76
+ for (let index = low; index < this.chunks.length; index += 1) {
77
+ const entry = this.chunks[index];
78
+ if (!entry)
79
+ throw malformedJson();
80
+ const begin = Math.max(0, start - entry.start);
81
+ output.set(entry.bytes.subarray(begin), outputOffset);
82
+ outputOffset += entry.bytes.byteLength - begin;
83
+ }
84
+ return output;
85
+ }
86
+ }
87
+ function malformedJson(detail) {
88
+ return new Error(detail
89
+ ? `Malformed Anthropic response JSON: ${detail}`
90
+ : 'Malformed Anthropic response JSON');
91
+ }
92
+ export function assertWellFormedUtf16(value) {
93
+ for (let index = 0; index < value.length; index += 1) {
94
+ const unit = value.charCodeAt(index);
95
+ if (unit >= 0xd800 && unit <= 0xdbff) {
96
+ if (index + 1 >= value.length) {
97
+ throw new Error('Tool names must contain well-formed UTF-16');
98
+ }
99
+ const next = value.charCodeAt(index + 1);
100
+ if (next < 0xdc00 || next > 0xdfff) {
101
+ throw new Error('Tool names must contain well-formed UTF-16');
102
+ }
103
+ index += 1;
104
+ }
105
+ else if (unit >= 0xdc00 && unit <= 0xdfff) {
106
+ throw new Error('Tool names must contain well-formed UTF-16');
107
+ }
108
+ }
109
+ }
110
+ function rawTokenLength(bytes, token) {
111
+ if (token === TokenType.STRING) {
112
+ let escaped = false;
113
+ for (let index = 1; index < bytes.byteLength; index += 1) {
114
+ const byte = bytes[index];
115
+ if (escaped)
116
+ escaped = false;
117
+ else if (byte === 0x5c)
118
+ escaped = true;
119
+ else if (byte === 0x22)
120
+ return index + 1;
121
+ }
122
+ return 0;
123
+ }
124
+ if (token === TokenType.LEFT_BRACE ||
125
+ token === TokenType.RIGHT_BRACE ||
126
+ token === TokenType.LEFT_BRACKET ||
127
+ token === TokenType.RIGHT_BRACKET ||
128
+ token === TokenType.COLON ||
129
+ token === TokenType.COMMA) {
130
+ return 1;
131
+ }
132
+ if (token === TokenType.TRUE || token === TokenType.NULL)
133
+ return 4;
134
+ if (token === TokenType.FALSE)
135
+ return 5;
136
+ if (token === TokenType.NUMBER) {
137
+ let index = 0;
138
+ while (index < bytes.byteLength) {
139
+ const byte = bytes[index];
140
+ if (byte === 0x20 ||
141
+ byte === 0x09 ||
142
+ byte === 0x0a ||
143
+ byte === 0x0d ||
144
+ byte === 0x2c ||
145
+ byte === 0x5d ||
146
+ byte === 0x7d) {
147
+ break;
148
+ }
149
+ index += 1;
150
+ }
151
+ return index;
152
+ }
153
+ return 0;
154
+ }
155
+ function objectFrame(role) {
156
+ return {
157
+ mode: 'object',
158
+ state: 'key-or-end',
159
+ role,
160
+ typeSeen: false,
161
+ seenType: false,
162
+ seenName: false,
163
+ seenContent: false,
164
+ seenContentBlock: false,
165
+ seenKeys: new Set(),
166
+ retainedKeyBytes: 0,
167
+ };
168
+ }
169
+ function isPrimitive(token) {
170
+ return (token === TokenType.STRING ||
171
+ token === TokenType.NUMBER ||
172
+ token === TokenType.TRUE ||
173
+ token === TokenType.FALSE ||
174
+ token === TokenType.NULL);
175
+ }
176
+ export function createBoundedJsonToolNameStream(body, toolPrefix, rewriteName) {
177
+ const raw = new ByteQueue();
178
+ const stack = [];
179
+ let absoluteOffset = 0;
180
+ let rootComplete = false;
181
+ let pending;
182
+ let outputController;
183
+ let objectKeys = 0;
184
+ let retainedKeyBytes = 0;
185
+ let nodes = 0;
186
+ let holdingOutput = false;
187
+ let heldLength = 0;
188
+ let heldChunks = [];
189
+ let deferredNames = [];
190
+ const top = () => stack.at(-1);
191
+ const countNode = () => {
192
+ nodes += 1;
193
+ if (nodes > MAX_JSON_NODES) {
194
+ throw new Error('Anthropic response JSON exceeds traversal limits');
195
+ }
196
+ };
197
+ const rootFrame = () => {
198
+ const root = stack[0];
199
+ return root?.mode === 'object' && root.role === 'root' ? root : undefined;
200
+ };
201
+ const emit = (bytes) => {
202
+ if (bytes.byteLength === 0)
203
+ return;
204
+ if (!holdingOutput) {
205
+ outputController.enqueue(bytes);
206
+ return;
207
+ }
208
+ if (heldLength + bytes.byteLength > MAX_JSON_PENDING_BLOCK_BYTES) {
209
+ throw new Error(`Anthropic response JSON exceeds ${MAX_JSON_PENDING_BLOCK_BYTES} byte pending-block limit`);
210
+ }
211
+ heldChunks.push(bytes);
212
+ heldLength += bytes.byteLength;
213
+ };
214
+ const isSemanticToolUse = (frame, root) => frame.objectType === 'tool_use' &&
215
+ ((frame.role === 'message-block' && root.objectType === 'message') ||
216
+ (frame.role === 'content-block' &&
217
+ root.objectType === 'content_block_start'));
218
+ const validateToolName = (name) => {
219
+ assertWellFormedUtf16(name);
220
+ if (encoder.encode(name).byteLength > MAX_JSON_TOOL_NAME_BYTES) {
221
+ throw new Error(`JSON tool name exceeds ${MAX_JSON_TOOL_NAME_BYTES} byte limit`);
222
+ }
223
+ };
224
+ const resolveDeferredNames = (root) => {
225
+ if (!holdingOutput)
226
+ return;
227
+ const held = new Uint8Array(heldLength);
228
+ let copied = 0;
229
+ for (const chunk of heldChunks) {
230
+ held.set(chunk, copied);
231
+ copied += chunk.byteLength;
232
+ }
233
+ const edits = [];
234
+ for (const deferred of deferredNames) {
235
+ if (deferred.root !== root || !isSemanticToolUse(deferred.frame, root)) {
236
+ continue;
237
+ }
238
+ validateToolName(deferred.name);
239
+ if (!deferred.name.startsWith(toolPrefix))
240
+ continue;
241
+ const rewritten = rewriteName(deferred.name);
242
+ if (rewritten === undefined)
243
+ continue;
244
+ edits.push({
245
+ start: deferred.offset,
246
+ end: deferred.offset + deferred.tokenLength,
247
+ replacement: encoder.encode(JSON.stringify(rewritten)),
248
+ });
249
+ }
250
+ let outputLength = held.byteLength;
251
+ for (const edit of edits) {
252
+ outputLength += edit.replacement.byteLength - (edit.end - edit.start);
253
+ }
254
+ if (outputLength > MAX_JSON_PENDING_BLOCK_BYTES) {
255
+ throw new Error(`Anthropic response JSON exceeds ${MAX_JSON_PENDING_BLOCK_BYTES} byte pending-block limit`);
256
+ }
257
+ const output = new Uint8Array(outputLength);
258
+ let sourceOffset = 0;
259
+ let outputOffset = 0;
260
+ for (const edit of edits.sort((left, right) => left.start - right.start)) {
261
+ output.set(held.subarray(sourceOffset, edit.start), outputOffset);
262
+ outputOffset += edit.start - sourceOffset;
263
+ output.set(edit.replacement, outputOffset);
264
+ outputOffset += edit.replacement.byteLength;
265
+ sourceOffset = edit.end;
266
+ }
267
+ output.set(held.subarray(sourceOffset), outputOffset);
268
+ outputController.enqueue(output);
269
+ holdingOutput = false;
270
+ heldLength = 0;
271
+ heldChunks = [];
272
+ deferredNames = [];
273
+ };
274
+ const expectingConfirmedToolName = () => {
275
+ const frame = top();
276
+ const root = rootFrame();
277
+ return (frame?.mode === 'object' &&
278
+ root !== undefined &&
279
+ isSemanticToolUse(frame, root) &&
280
+ frame.state === 'value' &&
281
+ frame.key === 'name');
282
+ };
283
+ const finishPending = (nextOffset) => {
284
+ if (!pending) {
285
+ emit(raw.takeTo(nextOffset));
286
+ return;
287
+ }
288
+ const segmentStart = raw.start;
289
+ const segment = raw.takeTo(nextOffset);
290
+ const tokenStart = pending.offset - segmentStart;
291
+ if (tokenStart < 0 || tokenStart > segment.byteLength)
292
+ throw malformedJson();
293
+ if (!pending.replacement) {
294
+ emit(segment);
295
+ pending = undefined;
296
+ return;
297
+ }
298
+ const tokenLength = rawTokenLength(segment.subarray(tokenStart), pending.token);
299
+ if (tokenLength === 0)
300
+ throw malformedJson('missing replacement token');
301
+ const outputLength = segment.byteLength - tokenLength + pending.replacement.byteLength;
302
+ if (outputLength > MAX_JSON_PENDING_BLOCK_BYTES) {
303
+ throw new Error(`Anthropic response JSON exceeds ${MAX_JSON_PENDING_BLOCK_BYTES} byte rewritten-segment limit`);
304
+ }
305
+ const output = new Uint8Array(outputLength);
306
+ output.set(segment.subarray(0, tokenStart));
307
+ output.set(pending.replacement, tokenStart);
308
+ output.set(segment.subarray(tokenStart + tokenLength), tokenStart + pending.replacement.byteLength);
309
+ emit(output);
310
+ pending = undefined;
311
+ };
312
+ const completeValue = () => {
313
+ const frame = top();
314
+ if (!frame) {
315
+ if (rootComplete)
316
+ throw malformedJson('multiple root values');
317
+ rootComplete = true;
318
+ return;
319
+ }
320
+ if (frame.mode === 'object') {
321
+ if (frame.state !== 'value')
322
+ throw malformedJson('unexpected value');
323
+ frame.state = 'comma-or-end';
324
+ frame.key = undefined;
325
+ }
326
+ else {
327
+ if (frame.state !== 'value' && frame.state !== 'value-or-end') {
328
+ throw malformedJson('unexpected array value');
329
+ }
330
+ frame.state = 'comma-or-end';
331
+ }
332
+ };
333
+ const requireValuePosition = () => {
334
+ const frame = top();
335
+ if (!frame) {
336
+ if (rootComplete)
337
+ throw malformedJson('multiple root values');
338
+ return undefined;
339
+ }
340
+ if (frame.mode === 'object' && frame.state !== 'value') {
341
+ throw malformedJson('object value is out of place');
342
+ }
343
+ if (frame.mode === 'array' &&
344
+ frame.state !== 'value' &&
345
+ frame.state !== 'value-or-end') {
346
+ throw malformedJson('array value is out of place');
347
+ }
348
+ return frame;
349
+ };
350
+ const containerRole = (parent, token) => {
351
+ if (!parent)
352
+ return 'root';
353
+ if (parent.mode === 'array' &&
354
+ parent.role === 'content-array' &&
355
+ token === TokenType.LEFT_BRACE) {
356
+ return 'message-block';
357
+ }
358
+ if (parent.mode !== 'object' || parent.role !== 'root')
359
+ return 'other';
360
+ if (parent.key === 'content' && token === TokenType.LEFT_BRACKET) {
361
+ return 'content-array';
362
+ }
363
+ if (parent.key === 'content_block' && token === TokenType.LEFT_BRACE) {
364
+ return 'content-block';
365
+ }
366
+ return 'other';
367
+ };
368
+ const startContainer = (token) => {
369
+ const parent = requireValuePosition();
370
+ countNode();
371
+ if (stack.length >= MAX_JSON_DEPTH) {
372
+ throw new Error('Anthropic response JSON exceeds traversal limits');
373
+ }
374
+ const role = containerRole(parent, token);
375
+ stack.push(token === TokenType.LEFT_BRACE
376
+ ? objectFrame(role)
377
+ : { mode: 'array', state: 'value-or-end', role });
378
+ };
379
+ const recordKey = (frame, key) => {
380
+ objectKeys += 1;
381
+ if (objectKeys > MAX_JSON_OBJECT_KEYS) {
382
+ throw new Error('Anthropic response JSON exceeds object-key limit');
383
+ }
384
+ if (frame.seenKeys.has(key)) {
385
+ throw malformedJson('duplicate object key');
386
+ }
387
+ const keyBytes = encoder.encode(key).byteLength;
388
+ if (keyBytes > MAX_JSON_RETAINED_KEY_BYTES - retainedKeyBytes) {
389
+ throw new Error('Anthropic response JSON exceeds retained-key byte limit');
390
+ }
391
+ frame.seenKeys.add(key);
392
+ frame.retainedKeyBytes += keyBytes;
393
+ retainedKeyBytes += keyBytes;
394
+ if (frame.role === 'root') {
395
+ if (key === 'type') {
396
+ if (frame.seenType)
397
+ throw malformedJson('duplicate root type');
398
+ frame.seenType = true;
399
+ }
400
+ else if (key === 'content') {
401
+ if (frame.seenContent)
402
+ throw malformedJson('duplicate root content');
403
+ frame.seenContent = true;
404
+ }
405
+ else if (key === 'content_block') {
406
+ if (frame.seenContentBlock) {
407
+ throw malformedJson('duplicate root content_block');
408
+ }
409
+ frame.seenContentBlock = true;
410
+ }
411
+ }
412
+ else if (frame.role === 'message-block' ||
413
+ frame.role === 'content-block') {
414
+ if (key === 'type') {
415
+ if (frame.seenType)
416
+ throw malformedJson('duplicate block type');
417
+ frame.seenType = true;
418
+ }
419
+ else if (key === 'name') {
420
+ if (frame.seenName)
421
+ throw malformedJson('duplicate block name');
422
+ frame.seenName = true;
423
+ }
424
+ }
425
+ frame.key = key;
426
+ frame.state = 'colon';
427
+ };
428
+ const processPrimitive = (info) => {
429
+ const frame = requireValuePosition();
430
+ countNode();
431
+ let replacement;
432
+ if (frame?.mode === 'object') {
433
+ if (frame.key === 'type' &&
434
+ (frame.role === 'root' ||
435
+ frame.role === 'message-block' ||
436
+ frame.role === 'content-block')) {
437
+ frame.typeSeen = true;
438
+ frame.objectType =
439
+ info.token === TokenType.STRING ? String(info.value) : undefined;
440
+ }
441
+ if ((frame.role === 'message-block' || frame.role === 'content-block') &&
442
+ frame.key === 'name') {
443
+ if (info.token === TokenType.STRING) {
444
+ const name = String(info.value);
445
+ const root = rootFrame();
446
+ if (!root)
447
+ throw malformedJson('tool block has no object root');
448
+ if (frame.typeSeen && root.typeSeen) {
449
+ if (isSemanticToolUse(frame, root)) {
450
+ validateToolName(name);
451
+ if (name.startsWith(toolPrefix)) {
452
+ const rewritten = rewriteName(name);
453
+ if (rewritten !== undefined) {
454
+ replacement = encoder.encode(JSON.stringify(rewritten));
455
+ }
456
+ }
457
+ }
458
+ }
459
+ else {
460
+ if (!holdingOutput)
461
+ holdingOutput = true;
462
+ const source = raw.bytesFrom(info.offset);
463
+ const tokenLength = rawTokenLength(source, info.token);
464
+ if (tokenLength === 0) {
465
+ throw malformedJson('missing deferred name token');
466
+ }
467
+ deferredNames.push({
468
+ frame,
469
+ root,
470
+ offset: heldLength,
471
+ tokenLength,
472
+ name,
473
+ });
474
+ }
475
+ }
476
+ }
477
+ }
478
+ completeValue();
479
+ return replacement;
480
+ };
481
+ const closeContainer = (token) => {
482
+ const frame = top();
483
+ if (!frame)
484
+ throw malformedJson('unexpected container close');
485
+ const expectedMode = token === TokenType.RIGHT_BRACE
486
+ ? 'object'
487
+ : token === TokenType.RIGHT_BRACKET
488
+ ? 'array'
489
+ : undefined;
490
+ if (frame.mode !== expectedMode)
491
+ throw malformedJson('mismatched close');
492
+ const canClose = frame.mode === 'object'
493
+ ? frame.state === 'key-or-end' || frame.state === 'comma-or-end'
494
+ : frame.state === 'value-or-end' || frame.state === 'comma-or-end';
495
+ if (!canClose)
496
+ throw malformedJson('incomplete container');
497
+ stack.pop();
498
+ if (frame.mode === 'object') {
499
+ retainedKeyBytes -= frame.retainedKeyBytes;
500
+ frame.retainedKeyBytes = 0;
501
+ frame.seenKeys.clear();
502
+ }
503
+ if (frame.mode === 'object' && frame.role === 'root') {
504
+ resolveDeferredNames(frame);
505
+ }
506
+ completeValue();
507
+ };
508
+ const onToken = (info) => {
509
+ if (info.partial) {
510
+ const sourceBytes = absoluteOffset - info.offset;
511
+ const candidateNameBytes = expectingConfirmedToolName()
512
+ ? encoder.encode(String(info.value)).byteLength
513
+ : 0;
514
+ if (info.token === TokenType.STRING &&
515
+ (expectingConfirmedToolName()
516
+ ? candidateNameBytes > MAX_JSON_TOOL_NAME_BYTES
517
+ : sourceBytes > MAX_JSON_STRING_BYTES)) {
518
+ throw new Error(expectingConfirmedToolName()
519
+ ? `JSON tool name exceeds ${MAX_JSON_TOOL_NAME_BYTES} byte limit`
520
+ : `Anthropic response JSON exceeds ${MAX_JSON_STRING_BYTES} byte string limit`);
521
+ }
522
+ if (info.token === TokenType.NUMBER &&
523
+ sourceBytes > MAX_JSON_NUMBER_BYTES) {
524
+ throw new Error(`Anthropic response JSON exceeds ${MAX_JSON_NUMBER_BYTES} byte number limit`);
525
+ }
526
+ return;
527
+ }
528
+ const source = raw.bytesFrom(info.offset);
529
+ const sourceLength = rawTokenLength(source, info.token);
530
+ if (sourceLength === 0)
531
+ throw malformedJson('incomplete token');
532
+ if (info.token === TokenType.STRING &&
533
+ sourceLength > MAX_JSON_STRING_BYTES) {
534
+ throw new Error(`Anthropic response JSON exceeds ${MAX_JSON_STRING_BYTES} byte string limit`);
535
+ }
536
+ if (info.token === TokenType.NUMBER &&
537
+ sourceLength > MAX_JSON_NUMBER_BYTES) {
538
+ throw new Error(`Anthropic response JSON exceeds ${MAX_JSON_NUMBER_BYTES} byte number limit`);
539
+ }
540
+ finishPending(info.offset);
541
+ pending = { offset: info.offset, token: info.token };
542
+ const frame = top();
543
+ if (info.token === TokenType.STRING &&
544
+ frame?.mode === 'object' &&
545
+ (frame.state === 'key-or-end' || frame.state === 'key')) {
546
+ recordKey(frame, String(info.value));
547
+ return;
548
+ }
549
+ if (info.token === TokenType.LEFT_BRACE ||
550
+ info.token === TokenType.LEFT_BRACKET) {
551
+ startContainer(info.token);
552
+ }
553
+ else if (isPrimitive(info.token)) {
554
+ pending.replacement = processPrimitive(info);
555
+ }
556
+ else if (info.token === TokenType.COLON) {
557
+ if (frame?.mode !== 'object' || frame.state !== 'colon') {
558
+ throw malformedJson('unexpected colon');
559
+ }
560
+ frame.state = 'value';
561
+ }
562
+ else if (info.token === TokenType.COMMA) {
563
+ if (frame?.state !== 'comma-or-end') {
564
+ throw malformedJson('unexpected comma');
565
+ }
566
+ frame.state = frame.mode === 'object' ? 'key' : 'value';
567
+ }
568
+ else if (info.token === TokenType.RIGHT_BRACE ||
569
+ info.token === TokenType.RIGHT_BRACKET) {
570
+ closeContainer(info.token);
571
+ }
572
+ else {
573
+ throw malformedJson('unexpected token');
574
+ }
575
+ };
576
+ const tokenizer = new Tokenizer({
577
+ emitPartialTokens: true,
578
+ numberBufferSize: MAX_JSON_NUMBER_BYTES,
579
+ stringBufferSize: 64 * 1024,
580
+ });
581
+ tokenizer.onToken = onToken;
582
+ const preamble = [];
583
+ let preambleChecked = false;
584
+ const feed = (chunk) => {
585
+ for (let offset = 0; offset < chunk.byteLength; offset += TOKENIZER_SLICE_BYTES) {
586
+ const slice = chunk.subarray(offset, Math.min(offset + TOKENIZER_SLICE_BYTES, chunk.byteLength));
587
+ raw.append(slice);
588
+ absoluteOffset += slice.byteLength;
589
+ tokenizer.write(slice);
590
+ if (raw.size > MAX_JSON_STRING_BYTES + TOKENIZER_SLICE_BYTES * 2) {
591
+ throw new Error('Anthropic response JSON exceeds bounded token buffer');
592
+ }
593
+ }
594
+ };
595
+ const rejectBom = () => {
596
+ if (preamble[0] === 0xef && preamble[1] === 0xbb && preamble[2] === 0xbf) {
597
+ throw malformedJson('UTF-8 BOM is not accepted');
598
+ }
599
+ };
600
+ const accept = (chunk) => {
601
+ let offset = 0;
602
+ if (!preambleChecked) {
603
+ while (preamble.length < 3 && offset < chunk.byteLength) {
604
+ preamble.push(chunk[offset++] ?? 0);
605
+ }
606
+ if (preamble.length < 3)
607
+ return;
608
+ rejectBom();
609
+ preambleChecked = true;
610
+ feed(Uint8Array.from(preamble));
611
+ preamble.length = 0;
612
+ }
613
+ feed(chunk.subarray(offset));
614
+ };
615
+ return body.pipeThrough(new TransformStream({
616
+ start(controller) {
617
+ outputController = controller;
618
+ },
619
+ transform(chunk) {
620
+ accept(chunk);
621
+ },
622
+ flush() {
623
+ if (!preambleChecked) {
624
+ rejectBom();
625
+ preambleChecked = true;
626
+ feed(Uint8Array.from(preamble));
627
+ preamble.length = 0;
628
+ }
629
+ tokenizer.end();
630
+ if (!rootComplete || stack.length > 0 || !pending) {
631
+ throw new Error('Malformed or truncated Anthropic response JSON');
632
+ }
633
+ finishPending(absoluteOffset);
634
+ if (raw.size !== 0)
635
+ throw malformedJson('unflushed bytes');
636
+ },
637
+ }));
638
+ }
@@ -0,0 +1,18 @@
1
+ export type RateLimitCategory = 'subscription-usage' | 'transient-rate-limit' | 'unknown-rate-limit';
2
+ export type RateLimitEnhancement = {
3
+ readonly response: Response;
4
+ readonly category: RateLimitCategory;
5
+ };
6
+ type ConnectionInfo = {
7
+ readonly type: string;
8
+ readonly id?: string;
9
+ readonly label?: string;
10
+ };
11
+ type RateLimitOptions = {
12
+ readonly probeTimeoutMs?: number;
13
+ };
14
+ export declare function createConnectionLabel(entropy?: Uint8Array): string;
15
+ export declare function describeConnection(connection: ConnectionInfo): string;
16
+ export declare function isSubscriptionUsageDiagnostic(message: string): boolean;
17
+ export declare function enhanceRateLimitResponse(response: Response, connection: string, options?: RateLimitOptions): Promise<RateLimitEnhancement>;
18
+ export {};