@theokit/sdk 2.15.0 → 2.15.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/dist/eval.js CHANGED
@@ -11170,11 +11170,16 @@ function sanitizeToolInput(input, options) {
11170
11170
  // src/internal/llm/hermes-tool-extract.ts
11171
11171
  var HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
11172
11172
  var HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
11173
- function extractHermesToolCalls(content, makeId) {
11173
+ function extractHermesToolCalls(content, makeId, allowedToolNames) {
11174
+ const isPromoted = (name) => name.length > 0 && (allowedToolNames === void 0 || allowedToolNames.has(name));
11174
11175
  const toolCalls = [];
11176
+ const droppedNames = [];
11175
11177
  for (const block of content.matchAll(HERMES_BLOCK)) {
11176
11178
  const name = (block[1] ?? "").trim();
11177
- if (name.length === 0) continue;
11179
+ if (!isPromoted(name)) {
11180
+ if (name.length > 0 && allowedToolNames !== void 0) droppedNames.push(name);
11181
+ continue;
11182
+ }
11178
11183
  toolCalls.push({
11179
11184
  type: "tool_use",
11180
11185
  id: makeId(),
@@ -11182,8 +11187,11 @@ function extractHermesToolCalls(content, makeId) {
11182
11187
  input: parseHermesParams(block[2] ?? "")
11183
11188
  });
11184
11189
  }
11185
- const residualText = toolCalls.length === 0 ? content : content.replace(HERMES_BLOCK, "").trim();
11186
- return { toolCalls, residualText };
11190
+ const residualText = toolCalls.length === 0 ? content : content.replace(
11191
+ HERMES_BLOCK,
11192
+ (full, rawName) => isPromoted((rawName ?? "").trim()) ? "" : full
11193
+ ).trim();
11194
+ return { toolCalls, residualText, droppedNames };
11187
11195
  }
11188
11196
  function parseHermesParams(inner) {
11189
11197
  const input = {};
@@ -11267,7 +11275,10 @@ var OpenAIClient = class {
11267
11275
  }
11268
11276
  const accumulator = new OpenAIStreamAccumulator(
11269
11277
  this.options.extractToolCallsFromContent ?? false,
11270
- providerId
11278
+ providerId,
11279
+ // R5: request-scoped allowlist — leaked recovery only promotes a block whose name is a tool the
11280
+ // model was actually given. Empty set (no tools) recovers nothing.
11281
+ new Set(request.tools?.map((tool) => tool.name) ?? [])
11271
11282
  );
11272
11283
  for await (const record of parseSseStream(response.body, signal)) {
11273
11284
  if (record.data === "[DONE]") break;
@@ -11297,13 +11308,18 @@ var OpenAIStreamAccumulator = class {
11297
11308
  /**
11298
11309
  * @param extractFromContent opt-in leaked-dialect safe-parse (theokit#58). Default false.
11299
11310
  * @param providerName provider id, used only to label the recovery log line.
11311
+ * @param allowedToolNames R5 request-scoped allowlist — built from `request.tools` at `stream()`;
11312
+ * leaked recovery in `finish()` only promotes a block whose name is in this set. `undefined`
11313
+ * (direct construction) recovers all (back-compat); an empty set recovers nothing.
11300
11314
  */
11301
- constructor(extractFromContent = false, providerName = "openai") {
11315
+ constructor(extractFromContent = false, providerName = "openai", allowedToolNames) {
11302
11316
  this.extractFromContent = extractFromContent;
11303
11317
  this.providerName = providerName;
11318
+ this.allowedToolNames = allowedToolNames;
11304
11319
  }
11305
11320
  extractFromContent;
11306
11321
  providerName;
11322
+ allowedToolNames;
11307
11323
  text = "";
11308
11324
  stopReason = "end_turn";
11309
11325
  inputTokens;
@@ -11374,7 +11390,8 @@ var OpenAIStreamAccumulator = class {
11374
11390
  if (this.extractFromContent && toolCalls.length === 0) {
11375
11391
  const recovered = extractHermesToolCalls(
11376
11392
  this.text,
11377
- () => `hermes-${globalThis.crypto.randomUUID()}`
11393
+ () => `hermes-${globalThis.crypto.randomUUID()}`,
11394
+ this.allowedToolNames
11378
11395
  );
11379
11396
  if (recovered.toolCalls.length > 0) {
11380
11397
  toolCalls.push(...recovered.toolCalls);
@@ -11382,6 +11399,12 @@ var OpenAIStreamAccumulator = class {
11382
11399
  stopReason = "tool_use";
11383
11400
  process.stderr.write(
11384
11401
  `[theokit-sdk] recovered ${recovered.toolCalls.length} leaked tool call(s) from assistant content (provider="${this.providerName}", names=${recovered.toolCalls.map((c) => c.name).join(",")})
11402
+ `
11403
+ );
11404
+ }
11405
+ if (recovered.droppedNames.length > 0) {
11406
+ process.stderr.write(
11407
+ `[theokit-sdk] dropped ${recovered.droppedNames.length} leaked block(s) whose name is not a tool in the request (provider="${this.providerName}", names=${recovered.droppedNames.join(",")})
11385
11408
  `
11386
11409
  );
11387
11410
  }