@theokit/sdk 3.2.2 → 3.2.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.
package/dist/index.cjs CHANGED
@@ -5173,12 +5173,23 @@ var init_objective_coerce = __esm({
5173
5173
  // src/internal/persistence/pagination.ts
5174
5174
  function paginate(items, opts) {
5175
5175
  if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
5176
- const start = Math.max(0, opts.offset ?? 0);
5177
- const end = opts.limit === void 0 ? items.length : start + Math.max(0, opts.limit);
5176
+ const start = opts.offset === void 0 ? 0 : requireNonNegativeInt(opts.offset, "offset");
5177
+ const limit = opts.limit === void 0 ? void 0 : requireNonNegativeInt(opts.limit, "limit");
5178
+ const end = limit === void 0 ? items.length : start + limit;
5178
5179
  return items.slice(start, end);
5179
5180
  }
5181
+ function requireNonNegativeInt(value, field) {
5182
+ if (!Number.isInteger(value) || value < 0) {
5183
+ throw new exports.ConfigurationError(
5184
+ `Invalid pagination ${field}: expected a non-negative integer, got ${value}`,
5185
+ { code: "pagination_invalid" }
5186
+ );
5187
+ }
5188
+ return value;
5189
+ }
5180
5190
  var init_pagination = __esm({
5181
5191
  "src/internal/persistence/pagination.ts"() {
5192
+ init_errors();
5182
5193
  }
5183
5194
  });
5184
5195
 
@@ -10350,6 +10361,8 @@ function parseRetryAfter(headers) {
10350
10361
  if (raw === null) return void 0;
10351
10362
  const n = Number(raw);
10352
10363
  if (Number.isFinite(n) && n >= 0) return Math.ceil(n);
10364
+ const dateMs = Date.parse(raw);
10365
+ if (Number.isFinite(dateMs)) return Math.max(0, Math.ceil((dateMs - Date.now()) / 1e3));
10353
10366
  return void 0;
10354
10367
  }
10355
10368
  function truncateRaw(body) {
@@ -10670,6 +10683,7 @@ function buildAnthropicBody(request) {
10670
10683
  var AnthropicClient, AnthropicStreamAccumulator;
10671
10684
  var init_anthropic3 = __esm({
10672
10685
  "src/internal/llm/anthropic.ts"() {
10686
+ init_errors();
10673
10687
  init_anthropic2();
10674
10688
  init_anthropic_shared();
10675
10689
  init_finish();
@@ -10723,12 +10737,23 @@ var init_anthropic3 = __esm({
10723
10737
  const events = accumulator.consume(parsed);
10724
10738
  for (const event of events) yield event;
10725
10739
  }
10740
+ if (!accumulator.finishReasonSeen) {
10741
+ throw new exports.NetworkError("Anthropic SSE stream truncated (no stop_reason)", {
10742
+ code: "stream_truncated"
10743
+ });
10744
+ }
10726
10745
  return accumulator.finish();
10727
10746
  }
10728
10747
  };
10729
10748
  AnthropicStreamAccumulator = class {
10730
10749
  text = "";
10731
10750
  stopReason = "end_turn";
10751
+ /**
10752
+ * M2 #61 — whether a `message_delta` carrying a real `stop_reason` was seen.
10753
+ * A stream that closes before it is a truncation (server FIN / proxy hiccup),
10754
+ * not a clean `end_turn` — the caller throws `stream_truncated` on `false`.
10755
+ */
10756
+ sawStopReason = false;
10732
10757
  inputTokens;
10733
10758
  outputTokens;
10734
10759
  cacheReadTokens;
@@ -10775,6 +10800,9 @@ var init_anthropic3 = __esm({
10775
10800
  * spinning the SSE parser.
10776
10801
  */
10777
10802
  handleMessageDelta(md) {
10803
+ if (md.delta.stop_reason !== void 0 && md.delta.stop_reason !== null) {
10804
+ this.sawStopReason = true;
10805
+ }
10778
10806
  this.stopReason = mapAnthropicStopReason(md.delta.stop_reason);
10779
10807
  if (md.usage?.input_tokens !== void 0) this.inputTokens = md.usage.input_tokens;
10780
10808
  if (md.usage?.output_tokens !== void 0) this.outputTokens = md.usage.output_tokens;
@@ -10785,6 +10813,10 @@ var init_anthropic3 = __esm({
10785
10813
  this.cacheReadTokens = md.usage.cache_read_input_tokens;
10786
10814
  }
10787
10815
  }
10816
+ /** M2 #61 — whether the terminal `message_delta` (stop_reason) was seen. */
10817
+ get finishReasonSeen() {
10818
+ return this.sawStopReason;
10819
+ }
10788
10820
  finish() {
10789
10821
  const toolCalls = [];
10790
10822
  for (const [index, tool] of this.toolCalls.entries()) {
@@ -13018,7 +13050,6 @@ var init_client = __esm({
13018
13050
  // concurrent request so parallel tool dispatch after a drop awaits one handshake
13019
13051
  // instead of racing (or spuriously failing with mcp_not_init).
13020
13052
  dropped = false;
13021
- reconnectAttempts = 0;
13022
13053
  reconnectPromise;
13023
13054
  get timeoutMs() {
13024
13055
  return this.config.requestTimeoutMs ?? DEFAULT_MCP_TIMEOUT_MS;
@@ -13071,15 +13102,22 @@ var init_client = __esm({
13071
13102
  return this.reconnectPromise;
13072
13103
  }
13073
13104
  async reconnect() {
13074
- if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
13075
- throw new exports.NetworkError(`MCP ${this.name} reconnect exhausted`, { code: "mcp_disconnected" });
13105
+ let lastErr;
13106
+ for (let attempt = 0; attempt < MAX_RECONNECT_ATTEMPTS; attempt += 1) {
13107
+ await reconnectDelay(attempt);
13108
+ this.spawnChild();
13109
+ try {
13110
+ await super.initialize();
13111
+ this.dropped = false;
13112
+ return;
13113
+ } catch (err) {
13114
+ lastErr = err;
13115
+ }
13076
13116
  }
13077
- await reconnectDelay(this.reconnectAttempts);
13078
- this.reconnectAttempts += 1;
13079
- this.spawnChild();
13080
- await super.initialize();
13081
- this.dropped = false;
13082
- this.reconnectAttempts = 0;
13117
+ throw new exports.NetworkError(`MCP ${this.name} reconnect exhausted`, {
13118
+ code: "mcp_disconnected",
13119
+ ...lastErr instanceof Error ? { cause: lastErr } : {}
13120
+ });
13083
13121
  }
13084
13122
  async close() {
13085
13123
  this.rejectAllPending(new exports.NetworkError(`MCP ${this.name} closed`, { code: "mcp_closed" }));