@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/CHANGELOG.md +9 -0
- package/dist/a2a/index.cjs +49 -11
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +49 -11
- package/dist/a2a/index.js.map +1 -1
- package/dist/cron.cjs +49 -11
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.js +49 -11
- package/dist/cron.js.map +1 -1
- package/dist/eval.cjs +49 -11
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +49 -11
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +49 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +49 -11
- package/dist/index.js.map +1 -1
- package/dist/internal/persistence/pagination.d.cts +14 -1
- package/dist/internal/persistence/pagination.d.ts +14 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1770aec: Fix (#59) — a stdio MCP client no longer permanently wedges after a transient outage exceeds the reconnect attempt bound. The bound is now LOCAL to each reconnect cycle (a bounded retry loop with backoff), so a later request re-arms a fresh cycle and reconnects once the server recovers — while a genuinely-broken server still surfaces a typed `mcp_disconnected` "reconnect exhausted". Adds the previously-missing HTTP-transport recovery test (stateless reconnect on the next request after a transport failure).
|
|
8
|
+
- cda0542: Fix (#60) — `Retry-After` now also parses the RFC-7231 HTTP-date form (`Retry-After: Wed, 21 Oct 2025 07:28:00 GMT`), converting it to seconds-until-then (clamped at 0 for a past date). Previously only the numeric-seconds form was honored; a date-form header was silently dropped. Clarified that the same-key 429 retry deliberately does not block on `Retry-After` (a multi-key pool rotates to a fresh key immediately; the cooldown is honored at pool-selection level).
|
|
9
|
+
- 510041b: Fix (#61) — the Anthropic streaming client now detects a truncated stream. A stream that closes cleanly-but-early (server FIN / proxy hiccup before the terminal `message_delta` carrying `stop_reason`) previously committed silently as a clean `end_turn`; it now throws a typed `NetworkError{code:"stream_truncated"}`, matching the OpenAI client's guard.
|
|
10
|
+
- f7d39c8: Fix (#63) — pagination now fails fast on invalid cursors. `paginate({ offset, limit })` rejected a `NaN` offset by silently returning the WHOLE list (and negative by returning empty); it now throws `ConfigurationError{code:"pagination_invalid"}` for any non-negative-integer offset/limit. Also adds real cross-process evidence for the conversation-storage file lock: two separate OS processes taking `withFileLock` on the same file are proven to serialize (previously only in-process concurrency was tested).
|
|
11
|
+
|
|
3
12
|
## 3.2.2
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/dist/a2a/index.cjs
CHANGED
|
@@ -5159,12 +5159,23 @@ var init_objective_coerce = __esm({
|
|
|
5159
5159
|
// src/internal/persistence/pagination.ts
|
|
5160
5160
|
function paginate(items, opts) {
|
|
5161
5161
|
if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
|
|
5162
|
-
const start =
|
|
5163
|
-
const
|
|
5162
|
+
const start = opts.offset === void 0 ? 0 : requireNonNegativeInt(opts.offset, "offset");
|
|
5163
|
+
const limit = opts.limit === void 0 ? void 0 : requireNonNegativeInt(opts.limit, "limit");
|
|
5164
|
+
const end = limit === void 0 ? items.length : start + limit;
|
|
5164
5165
|
return items.slice(start, end);
|
|
5165
5166
|
}
|
|
5167
|
+
function requireNonNegativeInt(value, field) {
|
|
5168
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
5169
|
+
throw new ConfigurationError(
|
|
5170
|
+
`Invalid pagination ${field}: expected a non-negative integer, got ${value}`,
|
|
5171
|
+
{ code: "pagination_invalid" }
|
|
5172
|
+
);
|
|
5173
|
+
}
|
|
5174
|
+
return value;
|
|
5175
|
+
}
|
|
5166
5176
|
var init_pagination = __esm({
|
|
5167
5177
|
"src/internal/persistence/pagination.ts"() {
|
|
5178
|
+
init_errors();
|
|
5168
5179
|
}
|
|
5169
5180
|
});
|
|
5170
5181
|
|
|
@@ -10165,6 +10176,8 @@ function parseRetryAfter(headers) {
|
|
|
10165
10176
|
if (raw === null) return void 0;
|
|
10166
10177
|
const n = Number(raw);
|
|
10167
10178
|
if (Number.isFinite(n) && n >= 0) return Math.ceil(n);
|
|
10179
|
+
const dateMs = Date.parse(raw);
|
|
10180
|
+
if (Number.isFinite(dateMs)) return Math.max(0, Math.ceil((dateMs - Date.now()) / 1e3));
|
|
10168
10181
|
return void 0;
|
|
10169
10182
|
}
|
|
10170
10183
|
function truncateRaw(body) {
|
|
@@ -10485,6 +10498,7 @@ function buildAnthropicBody(request) {
|
|
|
10485
10498
|
var AnthropicClient, AnthropicStreamAccumulator;
|
|
10486
10499
|
var init_anthropic3 = __esm({
|
|
10487
10500
|
"src/internal/llm/anthropic.ts"() {
|
|
10501
|
+
init_errors();
|
|
10488
10502
|
init_anthropic2();
|
|
10489
10503
|
init_anthropic_shared();
|
|
10490
10504
|
init_finish();
|
|
@@ -10538,12 +10552,23 @@ var init_anthropic3 = __esm({
|
|
|
10538
10552
|
const events = accumulator.consume(parsed);
|
|
10539
10553
|
for (const event of events) yield event;
|
|
10540
10554
|
}
|
|
10555
|
+
if (!accumulator.finishReasonSeen) {
|
|
10556
|
+
throw new NetworkError("Anthropic SSE stream truncated (no stop_reason)", {
|
|
10557
|
+
code: "stream_truncated"
|
|
10558
|
+
});
|
|
10559
|
+
}
|
|
10541
10560
|
return accumulator.finish();
|
|
10542
10561
|
}
|
|
10543
10562
|
};
|
|
10544
10563
|
AnthropicStreamAccumulator = class {
|
|
10545
10564
|
text = "";
|
|
10546
10565
|
stopReason = "end_turn";
|
|
10566
|
+
/**
|
|
10567
|
+
* M2 #61 — whether a `message_delta` carrying a real `stop_reason` was seen.
|
|
10568
|
+
* A stream that closes before it is a truncation (server FIN / proxy hiccup),
|
|
10569
|
+
* not a clean `end_turn` — the caller throws `stream_truncated` on `false`.
|
|
10570
|
+
*/
|
|
10571
|
+
sawStopReason = false;
|
|
10547
10572
|
inputTokens;
|
|
10548
10573
|
outputTokens;
|
|
10549
10574
|
cacheReadTokens;
|
|
@@ -10590,6 +10615,9 @@ var init_anthropic3 = __esm({
|
|
|
10590
10615
|
* spinning the SSE parser.
|
|
10591
10616
|
*/
|
|
10592
10617
|
handleMessageDelta(md) {
|
|
10618
|
+
if (md.delta.stop_reason !== void 0 && md.delta.stop_reason !== null) {
|
|
10619
|
+
this.sawStopReason = true;
|
|
10620
|
+
}
|
|
10593
10621
|
this.stopReason = mapAnthropicStopReason(md.delta.stop_reason);
|
|
10594
10622
|
if (md.usage?.input_tokens !== void 0) this.inputTokens = md.usage.input_tokens;
|
|
10595
10623
|
if (md.usage?.output_tokens !== void 0) this.outputTokens = md.usage.output_tokens;
|
|
@@ -10600,6 +10628,10 @@ var init_anthropic3 = __esm({
|
|
|
10600
10628
|
this.cacheReadTokens = md.usage.cache_read_input_tokens;
|
|
10601
10629
|
}
|
|
10602
10630
|
}
|
|
10631
|
+
/** M2 #61 — whether the terminal `message_delta` (stop_reason) was seen. */
|
|
10632
|
+
get finishReasonSeen() {
|
|
10633
|
+
return this.sawStopReason;
|
|
10634
|
+
}
|
|
10603
10635
|
finish() {
|
|
10604
10636
|
const toolCalls = [];
|
|
10605
10637
|
for (const [index, tool] of this.toolCalls.entries()) {
|
|
@@ -12833,7 +12865,6 @@ var init_client = __esm({
|
|
|
12833
12865
|
// concurrent request so parallel tool dispatch after a drop awaits one handshake
|
|
12834
12866
|
// instead of racing (or spuriously failing with mcp_not_init).
|
|
12835
12867
|
dropped = false;
|
|
12836
|
-
reconnectAttempts = 0;
|
|
12837
12868
|
reconnectPromise;
|
|
12838
12869
|
get timeoutMs() {
|
|
12839
12870
|
return this.config.requestTimeoutMs ?? DEFAULT_MCP_TIMEOUT_MS;
|
|
@@ -12886,15 +12917,22 @@ var init_client = __esm({
|
|
|
12886
12917
|
return this.reconnectPromise;
|
|
12887
12918
|
}
|
|
12888
12919
|
async reconnect() {
|
|
12889
|
-
|
|
12890
|
-
|
|
12920
|
+
let lastErr;
|
|
12921
|
+
for (let attempt = 0; attempt < MAX_RECONNECT_ATTEMPTS; attempt += 1) {
|
|
12922
|
+
await reconnectDelay(attempt);
|
|
12923
|
+
this.spawnChild();
|
|
12924
|
+
try {
|
|
12925
|
+
await super.initialize();
|
|
12926
|
+
this.dropped = false;
|
|
12927
|
+
return;
|
|
12928
|
+
} catch (err) {
|
|
12929
|
+
lastErr = err;
|
|
12930
|
+
}
|
|
12891
12931
|
}
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
this.dropped = false;
|
|
12897
|
-
this.reconnectAttempts = 0;
|
|
12932
|
+
throw new NetworkError(`MCP ${this.name} reconnect exhausted`, {
|
|
12933
|
+
code: "mcp_disconnected",
|
|
12934
|
+
...lastErr instanceof Error ? { cause: lastErr } : {}
|
|
12935
|
+
});
|
|
12898
12936
|
}
|
|
12899
12937
|
async close() {
|
|
12900
12938
|
this.rejectAllPending(new NetworkError(`MCP ${this.name} closed`, { code: "mcp_closed" }));
|