@rulvar/anthropic 1.27.0 → 1.29.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.
package/dist/index.d.ts CHANGED
@@ -165,8 +165,14 @@ interface TurnMapping {
165
165
  * detached work). The generator's RETURN value carries the accumulated
166
166
  * turn state the adapter needs for pause_turn continuation. Yields an
167
167
  * early usage event from message_start (the input side is known
168
- * immediately) and exactly one terminal finish unless the turn paused
169
- * (pause_turn) or errored. `carryRetained` holds thinking blocks from
168
+ * immediately) and exactly one terminal finish when the stream reaches
169
+ * message_stop. A stream that pauses (pause_turn) or ends before
170
+ * message_stop yields NO terminal event of its own: the return value's
171
+ * pauseTurn and finished flags report which case happened, and the
172
+ * `anthropic()` adapter turns a truncated read (finished false without
173
+ * a pause) into the retryable transport error the contract requires,
174
+ * so a direct mapper consumer must check the flags rather than wait
175
+ * for an error event. `carryRetained` holds thinking blocks from
170
176
  * earlier pause_turn continuations of the same turn so the terminal
171
177
  * finish ships the whole turn's retention payload (M4-T02).
172
178
  */
package/dist/index.js CHANGED
@@ -442,8 +442,14 @@ function normalizeAnthropicUsage(raw) {
442
442
  * detached work). The generator's RETURN value carries the accumulated
443
443
  * turn state the adapter needs for pause_turn continuation. Yields an
444
444
  * early usage event from message_start (the input side is known
445
- * immediately) and exactly one terminal finish unless the turn paused
446
- * (pause_turn) or errored. `carryRetained` holds thinking blocks from
445
+ * immediately) and exactly one terminal finish when the stream reaches
446
+ * message_stop. A stream that pauses (pause_turn) or ends before
447
+ * message_stop yields NO terminal event of its own: the return value's
448
+ * pauseTurn and finished flags report which case happened, and the
449
+ * `anthropic()` adapter turns a truncated read (finished false without
450
+ * a pause) into the retryable transport error the contract requires,
451
+ * so a direct mapper consumer must check the flags rather than wait
452
+ * for an error event. `carryRetained` holds thinking blocks from
447
453
  * earlier pause_turn continuations of the same turn so the terminal
448
454
  * finish ships the whole turn's retention payload (M4-T02).
449
455
  */
@@ -603,6 +609,24 @@ async function* mapAnthropicStream(stream, ids, options) {
603
609
  return mapping;
604
610
  }
605
611
  /**
612
+ * Largest delay a Node timer represents exactly; a bigger value would
613
+ * overflow into an almost immediate (storm inducing) retry.
614
+ */
615
+ const MAX_RETRY_AFTER_MS = 2147483647;
616
+ /**
617
+ * Parses a Retry-After header into milliseconds. Only the delta
618
+ * seconds form is honored: the HTTP date form and any other
619
+ * unparsable value return undefined so the engine falls back to its
620
+ * computed policy backoff instead of receiving NaN, and a huge but
621
+ * finite value is clamped to the Node timer maximum (v1.28.0 review
622
+ * P2).
623
+ */
624
+ function retryAfterMsFrom(header) {
625
+ const seconds = Number(header);
626
+ if (!Number.isFinite(seconds) || seconds < 0) return;
627
+ return Math.min(Math.round(seconds * 1e3), MAX_RETRY_AFTER_MS);
628
+ }
629
+ /**
606
630
  * Projects an SDK/API error into the retryable WireError vocabulary:
607
631
  * 429 rate limits surface retryAfterMs and the x-ratelimit-* buckets; 529
608
632
  * overloaded and 5xx are retryable transport; everything else is terminal
@@ -619,7 +643,8 @@ function anthropicErrorToWire(error) {
619
643
  return headers[name];
620
644
  };
621
645
  if (status === 429) {
622
- const retryAfter = headerGet("retry-after");
646
+ const retryAfterHeader = headerGet("retry-after");
647
+ const retryAfterMs = retryAfterHeader === void 0 ? void 0 : retryAfterMsFrom(retryAfterHeader);
623
648
  const buckets = {};
624
649
  for (const name of [
625
650
  "x-ratelimit-limit-requests",
@@ -638,7 +663,7 @@ function anthropicErrorToWire(error) {
638
663
  retryable: true,
639
664
  data: {
640
665
  kind: "rate-limit",
641
- ...retryAfter === void 0 ? {} : { retryAfterMs: Number(retryAfter) * 1e3 },
666
+ ...retryAfterMs === void 0 ? {} : { retryAfterMs },
642
667
  ...Object.keys(buckets).length > 0 ? { buckets } : {},
643
668
  status: 429
644
669
  }
@@ -766,6 +791,7 @@ function anthropic(options = {}) {
766
791
  stream: true
767
792
  }, signal === void 0 ? void 0 : { signal });
768
793
  } catch (thrown) {
794
+ if (signal?.aborted === true) return;
769
795
  yield {
770
796
  type: "error",
771
797
  error: anthropicErrorToWire(thrown)
@@ -791,7 +817,20 @@ function anthropic(options = {}) {
791
817
  };
792
818
  return;
793
819
  }
794
- if (!mapping.pauseTurn) return;
820
+ if (mapping.finished) return;
821
+ if (!mapping.pauseTurn) {
822
+ if (signal?.aborted === true) return;
823
+ yield {
824
+ type: "error",
825
+ error: {
826
+ code: "agent",
827
+ message: "Messages stream ended before message_stop; the read was truncated",
828
+ retryable: true,
829
+ data: { kind: "transport" }
830
+ }
831
+ };
832
+ return;
833
+ }
795
834
  carryRetained.push(...mapping.assistantContent.filter((block) => block.type === "thinking" || block.type === "redacted_thinking"));
796
835
  continuations += 1;
797
836
  if (continuations > pauseCap) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/anthropic",
3
- "version": "1.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "Rulvar first-class provider adapter over @anthropic-ai/sdk.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -23,13 +23,13 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@anthropic-ai/sdk": "^0.110.0",
26
- "@rulvar/core": "1.27.0"
26
+ "@rulvar/core": "1.29.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.20.0",
30
30
  "tsdown": "^0.22.3",
31
31
  "typescript": "~6.0.3",
32
- "@rulvar/testing": "1.27.0"
32
+ "@rulvar/testing": "1.29.0"
33
33
  },
34
34
  "repository": {
35
35
  "type": "git",