@theokit/sdk 2.12.0 → 2.13.1

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.13.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 65763b9: Trim leaked-dialect tool-call parameter values during recovery. Models that leak the Hermes dialect as text (qwen3-coder) emit each parameter on its own line, so the recovered value carried the formatting newlines: `<parameter=path>\npackage.json\n</parameter>` produced `{ path: "\npackage.json\n" }`. Untrimmed, `read_file` / `glob_files` / `search_text` received a path/pattern wrapped in newlines and failed `not_found` (only `shell_exec` tolerated it, since bash ignores blank lines), so a multi-read investigation loop kept re-reading, never converged, and appeared to hang. The recovery extractor now trims each parameter value (leading/trailing whitespace only — internal newlines of a legitimate multi-line command survive), matching the native `tool_calls` path where such formatting noise never occurs. Values remain strings; downstream schema coercion is unchanged.
8
+
9
+ ## 2.13.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 958a81f: Add per-route opt-in for leaked-dialect safe-parse (`ProviderRoute.extractToolCallsFromContent`, default off). The 2.12.0 release exposed the recovery flag only on a static `ProviderProfile`; enabling it required redeclaring a provider profile. This adds the same flag at the routing layer, so a consumer can opt a single chat route into recovery without cloning the built-in provider: `providers: { routes: [{ capability: "chat", provider: "openrouter", extractToolCallsFromContent: true }] }`. The router clones the resolved profile with the flag for that run (built-in profiles still ship the flag off), so the OpenAI-compatible transport recovers the Hermes `<function=…></tool_call>` dialect leaked as text by models like qwen3-coder. Derived from `routes[0]` (mirrors how the primary provider is derived) and applied to the resolved chat chain; fail-open and default-off, so a non-leaking route is unaffected. This is the enablement path consumed by `@theokit/agents`' `recoverLeakedToolCalls` knob.
14
+
3
15
  ## 2.12.0
4
16
 
5
17
  ### Minor Changes
@@ -10119,7 +10119,7 @@ function parseHermesParams(inner) {
10119
10119
  const key = param[1];
10120
10120
  const value = param[2];
10121
10121
  if (key === void 0 || value === void 0) continue;
10122
- input[key.trim()] = value;
10122
+ input[key.trim()] = value.trim();
10123
10123
  }
10124
10124
  return input;
10125
10125
  }
@@ -10255,6 +10255,14 @@ var init_openai2 = __esm({
10255
10255
  name = "openai";
10256
10256
  baseUrl;
10257
10257
  fetchImpl;
10258
+ /**
10259
+ * Whether this client recovers leaked Hermes tool-call dialect from assistant
10260
+ * text (theokit#58 follow-up). Observability for the route→client wiring of
10261
+ * `extractToolCallsFromContent`. @internal
10262
+ */
10263
+ get recoversLeakedToolCalls() {
10264
+ return this.options.extractToolCallsFromContent ?? false;
10265
+ }
10258
10266
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: HTTP+SSE handshake + accumulator is intentionally one block
10259
10267
  async *stream(request, signal) {
10260
10268
  const headers = {
@@ -10840,8 +10848,9 @@ function buildChain(options) {
10840
10848
  return clients;
10841
10849
  }
10842
10850
  function buildClient(name, routerOptions) {
10843
- const profile = getProviderProfile(name);
10844
- if (profile === void 0) return void 0;
10851
+ const baseProfile = getProviderProfile(name);
10852
+ if (baseProfile === void 0) return void 0;
10853
+ const profile = routerOptions.extractToolCallsFromContent === true && baseProfile.extractToolCallsFromContent !== true ? { ...baseProfile, extractToolCallsFromContent: true } : baseProfile;
10845
10854
  const ambient = currentCredentialPool(name);
10846
10855
  if (ambient !== void 0) {
10847
10856
  return new PoolAwareLlmClient(ambient, (apiKey) => selectTransport(profile, apiKey));
@@ -11270,11 +11279,13 @@ function buildLoopInputs(options, runId, userText) {
11270
11279
  const fallback = options.agentOptions.providers?.fallback;
11271
11280
  const apiKeys = options.agentOptions.providers?.apiKeys;
11272
11281
  const credentialPoolStrategy = options.agentOptions.providers?.credentialPoolStrategy;
11282
+ const extractToolCallsFromContent = options.agentOptions.providers?.routes?.[0]?.extractToolCallsFromContent;
11273
11283
  const chain = resolveProviderChain({
11274
11284
  primary,
11275
11285
  ...fallback !== void 0 ? { fallback } : {},
11276
11286
  ...apiKeys !== void 0 ? { apiKeys } : {},
11277
- ...credentialPoolStrategy !== void 0 ? { credentialPoolStrategy } : {}
11287
+ ...credentialPoolStrategy !== void 0 ? { credentialPoolStrategy } : {},
11288
+ ...extractToolCallsFromContent === true ? { extractToolCallsFromContent: true } : {}
11278
11289
  });
11279
11290
  const llm = chain.length === 1 ? chain[0] : new FallbackLlmClient(chain);
11280
11291
  return {