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