@combycode/llm-sdk 1.0.0 → 1.2.0

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.
@@ -22177,6 +22177,7 @@ var ModelCatalog = class {
22177
22177
  family: info.family,
22178
22178
  version: info.version,
22179
22179
  status: info.status,
22180
+ availability: info.availability,
22180
22181
  active: info.active,
22181
22182
  deprecation: info.deprecation
22182
22183
  });
@@ -22264,6 +22265,168 @@ var ModelCatalog = class {
22264
22265
  }
22265
22266
  };
22266
22267
 
22268
+ // src/llm/providers/openai/moderations.ts
22269
+ var OPENAI_MODERATION_BASE_URL = "https://api.openai.com";
22270
+ var OPENAI_MODERATION_PATH = "/v1/moderations";
22271
+ var OPENAI_MODERATION_DEFAULT_MODEL = "omni-moderation-latest";
22272
+ var OpenAIModerationAdapter = class {
22273
+ apiKey;
22274
+ baseURL;
22275
+ constructor(config) {
22276
+ this.apiKey = config.apiKey;
22277
+ this.baseURL = config.baseURL ?? OPENAI_MODERATION_BASE_URL;
22278
+ }
22279
+ async moderate(input, model, fetch2) {
22280
+ const res = await fetch2({
22281
+ url: `${this.baseURL}${OPENAI_MODERATION_PATH}`,
22282
+ method: "POST",
22283
+ headers: { authorization: `Bearer ${this.apiKey}`, "content-type": "application/json" },
22284
+ body: { model, input },
22285
+ provider: "openai",
22286
+ model,
22287
+ responseType: "json"
22288
+ });
22289
+ if (res.status >= 400) {
22290
+ throw new Error(`OpenAI moderations failed (${res.status}): ${JSON.stringify(res.body)}`);
22291
+ }
22292
+ const data = res.body;
22293
+ return (data.results ?? []).map(parseRawResult);
22294
+ }
22295
+ };
22296
+ function parseRawResult(r) {
22297
+ return {
22298
+ flagged: r.flagged,
22299
+ categories: r.categories,
22300
+ categoryScores: r.category_scores,
22301
+ categoryAppliedInputTypes: r.category_applied_input_types
22302
+ };
22303
+ }
22304
+
22305
+ // src/llm/moderation/types.ts
22306
+ var MODERATION_DEFAULT_MODEL = "omni-moderation-latest";
22307
+ var MODERATION_DEFAULT_STRATEGY = "buffer";
22308
+ var MODERATION_DEFAULT_INTERVAL = 400;
22309
+
22310
+ // src/llm/moderation/runner.ts
22311
+ function resolveModerationMode(provider, mod) {
22312
+ if (mod.mode) return mod.mode;
22313
+ return provider === "openai" ? "native" : "emulate";
22314
+ }
22315
+ function moderationInputText(messages) {
22316
+ const lastUser = [...messages].reverse().find((m) => m.role === "user");
22317
+ if (!lastUser) return "";
22318
+ return typeof lastUser.content === "string" ? lastUser.content : contentText(lastUser.content);
22319
+ }
22320
+ function emptyResult() {
22321
+ return {
22322
+ flagged: false,
22323
+ categories: {},
22324
+ categoryScores: {}
22325
+ };
22326
+ }
22327
+ async function runModeration(text, cfg) {
22328
+ if (!text) return emptyResult();
22329
+ try {
22330
+ const adapter = new OpenAIModerationAdapter({ apiKey: cfg.apiKey });
22331
+ const results = await adapter.moderate(text, cfg.model, cfg.fetch);
22332
+ return results[0] ?? emptyResult();
22333
+ } catch (e) {
22334
+ return { error: e instanceof Error ? e.message : String(e) };
22335
+ }
22336
+ }
22337
+ function moderationModel(mod) {
22338
+ return mod.model ?? MODERATION_DEFAULT_MODEL;
22339
+ }
22340
+ function emitModerationZeroCost(hooks, model) {
22341
+ const entry = {
22342
+ id: crypto.randomUUID(),
22343
+ timestamp: Date.now(),
22344
+ provider: "openai",
22345
+ model,
22346
+ tokens: { input: 0, output: 0, cached: 0, cacheWrite: 0, reasoning: 0 },
22347
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0, total: 0, source: "calculated" },
22348
+ providerEvidence: { note: "free: moderations endpoint not billed" },
22349
+ tags: { provider: "openai", model, type: "moderation" }
22350
+ };
22351
+ hooks.emitSync("onCostEntry", { entry, runningTotal: 0 });
22352
+ }
22353
+ function outputEvent(result) {
22354
+ return { type: "moderation", phase: "output", result, source: "emulated" };
22355
+ }
22356
+ function wrapModeratedStream(raw, strategy, interval, moderate2) {
22357
+ const step = interval > 0 ? interval : MODERATION_DEFAULT_INTERVAL;
22358
+ if (strategy === "post") return wrapPost(raw, moderate2);
22359
+ if (strategy === "parallel") return wrapParallel(raw, step, moderate2);
22360
+ return wrapBuffer(raw, step, moderate2);
22361
+ }
22362
+ async function* wrapPost(raw, moderate2) {
22363
+ let acc = "";
22364
+ for await (const ev of raw) {
22365
+ if (ev.type === "text") acc += ev.text;
22366
+ yield ev;
22367
+ }
22368
+ if (acc) yield outputEvent(await moderate2(acc));
22369
+ }
22370
+ async function* wrapBuffer(raw, interval, moderate2) {
22371
+ let acc = "";
22372
+ let checkedAt = 0;
22373
+ let hold = [];
22374
+ for await (const ev of raw) {
22375
+ hold.push(ev);
22376
+ if (ev.type === "text") {
22377
+ acc += ev.text;
22378
+ if (acc.length - checkedAt >= interval || ev.text.includes("\n")) {
22379
+ checkedAt = acc.length;
22380
+ yield outputEvent(await moderate2(acc));
22381
+ for (const h of hold) yield h;
22382
+ hold = [];
22383
+ }
22384
+ }
22385
+ }
22386
+ if (acc.length > checkedAt) yield outputEvent(await moderate2(acc));
22387
+ for (const h of hold) yield h;
22388
+ }
22389
+ async function* wrapParallel(raw, interval, moderate2) {
22390
+ const iter = raw[Symbol.asyncIterator]();
22391
+ const pending = /* @__PURE__ */ new Set();
22392
+ let acc = "";
22393
+ let checkedAt = 0;
22394
+ let rawDone = false;
22395
+ let rawNext = iter.next();
22396
+ const schedule = (text) => {
22397
+ const p = moderate2(text).then((result) => {
22398
+ pending.delete(p);
22399
+ return { tag: "mod", event: outputEvent(result) };
22400
+ });
22401
+ pending.add(p);
22402
+ };
22403
+ while (!rawDone || pending.size > 0) {
22404
+ const racers = [];
22405
+ if (!rawDone) racers.push(rawNext.then((result) => ({ tag: "raw", result })));
22406
+ for (const p of pending) racers.push(p);
22407
+ const winner = await Promise.race(racers);
22408
+ if (winner.tag === "mod") {
22409
+ yield winner.event;
22410
+ continue;
22411
+ }
22412
+ if (winner.result.done) {
22413
+ rawDone = true;
22414
+ if (acc.length > checkedAt) schedule(acc);
22415
+ continue;
22416
+ }
22417
+ const ev = winner.result.value;
22418
+ yield ev;
22419
+ if (ev.type === "text") {
22420
+ acc += ev.text;
22421
+ if (acc.length - checkedAt >= interval) {
22422
+ checkedAt = acc.length;
22423
+ schedule(acc);
22424
+ }
22425
+ }
22426
+ rawNext = iter.next();
22427
+ }
22428
+ }
22429
+
22267
22430
  // src/util/duration.ts
22268
22431
  var UNIT_MS = {
22269
22432
  s: 1e3,
@@ -22400,6 +22563,7 @@ var LLMClient = class {
22400
22563
  mode;
22401
22564
  batchable;
22402
22565
  adapter;
22566
+ apiKey;
22403
22567
  fetchFn;
22404
22568
  fetchStreamFn;
22405
22569
  priority;
@@ -22423,6 +22587,7 @@ var LLMClient = class {
22423
22587
  this.provider = config.provider;
22424
22588
  this.model = config.model;
22425
22589
  this.system = config.system;
22590
+ this.apiKey = config.apiKey;
22426
22591
  this.hooks = config.hooks ?? new HookBus();
22427
22592
  this.fetchFn = config.fetch;
22428
22593
  this.fetchStreamFn = config.fetchStream ?? null;
@@ -22476,6 +22641,34 @@ var LLMClient = class {
22476
22641
  }
22477
22642
  };
22478
22643
  }
22644
+ // ─── Moderation helpers ───────────────────────────────────────────────
22645
+ /** Resolve the OpenAI key the emulated moderation path needs. Reuses the
22646
+ * client's own key when the provider is OpenAI; otherwise requires an explicit
22647
+ * one. Throws when none is resolvable (report-only feature, but the call still
22648
+ * needs a key to reach the moderations endpoint). */
22649
+ resolveModerationKey(mod) {
22650
+ const key = mod.apiKey ?? (this.provider === "openai" ? this.apiKey : void 0);
22651
+ if (!key) {
22652
+ throw new Error(
22653
+ "moderation: emulated moderation requires an OpenAI API key (the only public moderations endpoint). Pass moderation.apiKey, or use the OpenAI provider."
22654
+ );
22655
+ }
22656
+ return key;
22657
+ }
22658
+ moderationConfig(mod) {
22659
+ return {
22660
+ apiKey: this.resolveModerationKey(mod),
22661
+ model: moderationModel(mod),
22662
+ fetch: this.fetchFn
22663
+ };
22664
+ }
22665
+ /** Fold a moderation stream event into the accumulating report. */
22666
+ mergeModeration(prev, phase, result, source) {
22667
+ const next = prev && prev.source === source ? { ...prev } : { source };
22668
+ if (phase === "input") next.input = result;
22669
+ else next.output = result;
22670
+ return next;
22671
+ }
22479
22672
  /** Submit a request. Returns the parsed CompletionResponse. */
22480
22673
  async complete(input, options = {}) {
22481
22674
  const rawMessages = normalizeInput(input);
@@ -22496,6 +22689,7 @@ var LLMClient = class {
22496
22689
  thinking: options.thinking,
22497
22690
  cache: options.cache,
22498
22691
  serviceTier: options.serviceTier,
22692
+ moderation: options.moderation,
22499
22693
  providerOptions: options.providerOptions,
22500
22694
  audio: options.audio,
22501
22695
  outputModalities: options.outputModalities,
@@ -22551,6 +22745,8 @@ var LLMClient = class {
22551
22745
  await this.hooks.emit("onBeforeSubmit", submitCtx);
22552
22746
  const inputChars = JSON.stringify(normalized.messages).length;
22553
22747
  const estimatedInputTokens = Math.ceil(inputChars / 4);
22748
+ const mod = options.moderation;
22749
+ const moderationCfg = mod && resolveModerationMode(this.provider, mod) === "emulate" ? this.moderationConfig(mod) : void 0;
22554
22750
  const start = performance.now();
22555
22751
  let response;
22556
22752
  if (submitCtx.intercepted && submitCtx.resultPromise) {
@@ -22576,6 +22772,22 @@ var LLMClient = class {
22576
22772
  }
22577
22773
  const latencyMs = performance.now() - start;
22578
22774
  const result = this.adapter.parseResponse(response.body, latencyMs);
22775
+ if (mod && moderationCfg) {
22776
+ const cfg = moderationCfg;
22777
+ const doInput = mod.input ?? true;
22778
+ const doOutput = mod.output ?? true;
22779
+ const [inputEntry, outputEntry] = await Promise.all([
22780
+ doInput ? runModeration(moderationInputText(normalized.messages), cfg) : Promise.resolve(void 0),
22781
+ doOutput ? runModeration(result.text, cfg) : Promise.resolve(void 0)
22782
+ ]);
22783
+ if (doInput) emitModerationZeroCost(this.hooks, cfg.model);
22784
+ if (doOutput) emitModerationZeroCost(this.hooks, cfg.model);
22785
+ result.moderation = {
22786
+ source: "emulated",
22787
+ ...inputEntry ? { input: inputEntry } : {},
22788
+ ...outputEntry ? { output: outputEntry } : {}
22789
+ };
22790
+ }
22579
22791
  await this.hooks.emit("onCompletion", {
22580
22792
  provider: this.provider,
22581
22793
  model: this.model,
@@ -22623,6 +22835,7 @@ var LLMClient = class {
22623
22835
  thinking: options.thinking,
22624
22836
  cache: options.cache,
22625
22837
  serviceTier: options.serviceTier,
22838
+ moderation: options.moderation,
22626
22839
  providerOptions: options.providerOptions,
22627
22840
  audio: options.audio,
22628
22841
  outputModalities: options.outputModalities,
@@ -22666,29 +22879,65 @@ var LLMClient = class {
22666
22879
  let thinking = "";
22667
22880
  let usage = emptyUsage();
22668
22881
  let finishReason = "stop";
22669
- for await (const sseEvent of this.fetchStreamFn(httpReq, {
22670
- queueName: this.queueName,
22671
- priority: this.priority,
22672
- ctx
22673
- })) {
22674
- const events = this.adapter.parseStreamEvent(sseEvent);
22675
- for (const event of events) {
22676
- switch (event.type) {
22677
- case "text":
22678
- text += event.text;
22679
- break;
22680
- case "thinking":
22681
- thinking += event.text;
22682
- break;
22683
- case "usage":
22684
- usage = event.usage;
22685
- break;
22686
- case "done":
22687
- finishReason = event.finishReason;
22688
- break;
22689
- }
22690
- yield event;
22882
+ let moderationReport;
22883
+ const fetchStream = this.fetchStreamFn;
22884
+ const adapter = this.adapter;
22885
+ const queueName = this.queueName;
22886
+ const priority = this.priority;
22887
+ async function* rawEvents() {
22888
+ for await (const sseEvent of fetchStream(httpReq, {
22889
+ queueName,
22890
+ priority,
22891
+ ctx
22892
+ })) {
22893
+ for (const ev of adapter.parseStreamEvent(sseEvent)) yield ev;
22894
+ }
22895
+ }
22896
+ const mod = options.moderation;
22897
+ const emulate = !!mod && resolveModerationMode(this.provider, mod) === "emulate";
22898
+ let eventStream = rawEvents();
22899
+ if (mod && emulate && (mod.output ?? true)) {
22900
+ const cfg = this.moderationConfig(mod);
22901
+ const hooks = this.hooks;
22902
+ const strategy = mod.stream?.strategy ?? MODERATION_DEFAULT_STRATEGY;
22903
+ const interval = mod.stream?.interval ?? MODERATION_DEFAULT_INTERVAL;
22904
+ eventStream = wrapModeratedStream(eventStream, strategy, interval, async (t) => {
22905
+ const r = await runModeration(t, cfg);
22906
+ emitModerationZeroCost(hooks, cfg.model);
22907
+ return r;
22908
+ });
22909
+ }
22910
+ if (mod && emulate && (mod.input ?? true)) {
22911
+ const cfg = this.moderationConfig(mod);
22912
+ const inputEntry = await runModeration(moderationInputText(normalized.messages), cfg);
22913
+ emitModerationZeroCost(this.hooks, cfg.model);
22914
+ moderationReport = this.mergeModeration(moderationReport, "input", inputEntry, "emulated");
22915
+ yield { type: "moderation", phase: "input", result: inputEntry, source: "emulated" };
22916
+ }
22917
+ for await (const event of eventStream) {
22918
+ switch (event.type) {
22919
+ case "text":
22920
+ text += event.text;
22921
+ break;
22922
+ case "thinking":
22923
+ thinking += event.text;
22924
+ break;
22925
+ case "usage":
22926
+ usage = event.usage;
22927
+ break;
22928
+ case "done":
22929
+ finishReason = event.finishReason;
22930
+ break;
22931
+ case "moderation":
22932
+ moderationReport = this.mergeModeration(
22933
+ moderationReport,
22934
+ event.phase,
22935
+ event.result,
22936
+ event.source
22937
+ );
22938
+ break;
22691
22939
  }
22940
+ yield event;
22692
22941
  }
22693
22942
  const inputChars = JSON.stringify(normalized.messages).length;
22694
22943
  const response = {
@@ -22701,6 +22950,7 @@ var LLMClient = class {
22701
22950
  toolCalls: [],
22702
22951
  thinking: thinking || null,
22703
22952
  media: [],
22953
+ ...moderationReport ? { moderation: moderationReport } : {},
22704
22954
  latencyMs: performance.now() - start,
22705
22955
  raw: null
22706
22956
  };
@@ -23009,6 +23259,9 @@ var AnthropicAdapter = class {
23009
23259
  if (t.type === "web_search") {
23010
23260
  return { type: "web_search_20250305", name: "web_search", max_uses: 5 };
23011
23261
  }
23262
+ if (t.type === "code_interpreter") {
23263
+ return { type: "code_execution_20260521", name: "code_execution" };
23264
+ }
23012
23265
  return null;
23013
23266
  }
23014
23267
  const tool = {
@@ -23052,7 +23305,10 @@ var AnthropicAdapter = class {
23052
23305
  });
23053
23306
  const headers = {};
23054
23307
  if (hasFileRef) headers["anthropic-beta"] = "files-api-2025-04-14";
23055
- return { body, headers };
23308
+ const usesCodeExec = req.tools?.some(
23309
+ (t) => !isFunctionTool(t) && t.type === "code_interpreter"
23310
+ );
23311
+ return { body, headers, ...usesCodeExec ? { path: "/v1/messages?beta=true" } : {} };
23056
23312
  }
23057
23313
  enableStreaming(providerReq, _req) {
23058
23314
  providerReq.body.stream = true;
@@ -23119,6 +23375,7 @@ var AnthropicAdapter = class {
23119
23375
  const content = [];
23120
23376
  let thinking = null;
23121
23377
  const toolCalls = [];
23378
+ const files = [];
23122
23379
  for (const block of contentBlocks) {
23123
23380
  if (block.type === "text") {
23124
23381
  content.push({ type: "text", text: block.text });
@@ -23133,6 +23390,15 @@ var AnthropicAdapter = class {
23133
23390
  };
23134
23391
  content.push(tc);
23135
23392
  toolCalls.push(tc);
23393
+ } else if (block.type === "bash_code_execution_tool_result" || block.type === "code_execution_tool_result") {
23394
+ const result = block.content;
23395
+ if (result && (result.type === "bash_code_execution_result" || result.type === "code_execution_result") && Array.isArray(result.content)) {
23396
+ for (const out of result.content) {
23397
+ if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
23398
+ files.push({ id: out.file_id, source: "code_execution" });
23399
+ }
23400
+ }
23401
+ }
23136
23402
  }
23137
23403
  }
23138
23404
  const finishReason = extractFinishReason(toolCalls.length > 0, r.stop_reason, {
@@ -23147,6 +23413,7 @@ var AnthropicAdapter = class {
23147
23413
  text: content.filter((p) => p.type === "text").map((p) => p.text).join(""),
23148
23414
  toolCalls,
23149
23415
  media: [],
23416
+ ...files.length ? { files } : {},
23150
23417
  thinking,
23151
23418
  latencyMs,
23152
23419
  raw
@@ -23452,6 +23719,17 @@ function resolveVoice(provider, voice) {
23452
23719
  return VOICE_ALIASES[provider]?.[voice] ?? voice;
23453
23720
  }
23454
23721
 
23722
+ // src/llm/providers/google/tiers.ts
23723
+ var REQUEST = /* @__PURE__ */ new Set(["flex", "standard", "priority"]);
23724
+ function googleRequestTier(t) {
23725
+ if (!t) return void 0;
23726
+ return REQUEST.has(t) ? t : void 0;
23727
+ }
23728
+ function googleBilledTier(raw) {
23729
+ if (typeof raw !== "string" || !raw) return {};
23730
+ return { serviceTier: raw, pricingTier: raw.toLowerCase() };
23731
+ }
23732
+
23455
23733
  // src/llm/providers/google/constants.ts
23456
23734
  var GOOGLE_THINKING_LEVELS = {
23457
23735
  low: "LOW",
@@ -23504,6 +23782,8 @@ var GoogleAdapter = class {
23504
23782
  contents,
23505
23783
  generationConfig: config
23506
23784
  };
23785
+ const tier = googleRequestTier(req.serviceTier);
23786
+ if (tier) body.serviceTier = tier;
23507
23787
  if (req.system) {
23508
23788
  body.systemInstruction = { parts: [{ text: req.system }] };
23509
23789
  }
@@ -23622,7 +23902,9 @@ var GoogleAdapter = class {
23622
23902
  const content = [];
23623
23903
  const toolCalls = [];
23624
23904
  const media = [];
23905
+ const files = [];
23625
23906
  let thinking = null;
23907
+ const hasCodeExec = parts.some((p) => p.executableCode || p.codeExecutionResult);
23626
23908
  for (const part of parts) {
23627
23909
  if (part.text !== void 0 && !part.thought) {
23628
23910
  content.push({ type: "text", text: part.text });
@@ -23633,7 +23915,9 @@ var GoogleAdapter = class {
23633
23915
  if (part.inlineData) {
23634
23916
  const inline = part.inlineData;
23635
23917
  const mime = inline.mimeType;
23636
- if (mime.startsWith("image/")) {
23918
+ if (hasCodeExec) {
23919
+ files.push({ data: inline.data, mimeType: mime, source: "code_execution" });
23920
+ } else if (mime.startsWith("image/")) {
23637
23921
  const p = {
23638
23922
  type: "image_output",
23639
23923
  mediaId: "",
@@ -23694,6 +23978,7 @@ var GoogleAdapter = class {
23694
23978
  toolCalls,
23695
23979
  thinking,
23696
23980
  media,
23981
+ ...files.length ? { files } : {},
23697
23982
  latencyMs,
23698
23983
  raw
23699
23984
  };
@@ -23762,7 +24047,9 @@ var GoogleAdapter = class {
23762
24047
  totalTokens: u.totalTokenCount ?? input + output,
23763
24048
  cachedTokens: u.cachedContentTokenCount ?? 0,
23764
24049
  cacheWriteTokens: 0,
23765
- reasoningTokens: u.thoughtsTokenCount ?? 0
24050
+ reasoningTokens: u.thoughtsTokenCount ?? 0,
24051
+ // Billed service tier (output-only `usageMetadata.serviceTier`).
24052
+ ...googleBilledTier(u.serviceTier)
23766
24053
  };
23767
24054
  }
23768
24055
  };
@@ -24637,8 +24924,48 @@ var OpenAIBatchAdapter = class {
24637
24924
  }
24638
24925
  };
24639
24926
 
24927
+ // src/llm/moderation/native.ts
24928
+ function buildNativeModeration(mod) {
24929
+ return { model: mod.model ?? MODERATION_DEFAULT_MODEL };
24930
+ }
24931
+ function parseNativeModeration(raw) {
24932
+ if (!raw || typeof raw !== "object") return void 0;
24933
+ const m = raw;
24934
+ const input = parseEntry(m.input);
24935
+ const output = parseEntry(m.output);
24936
+ if (!input && !output) return void 0;
24937
+ return {
24938
+ source: "native",
24939
+ ...input ? { input } : {},
24940
+ ...output ? { output } : {}
24941
+ };
24942
+ }
24943
+ function parseEntry(entry) {
24944
+ if (!entry || typeof entry !== "object") return void 0;
24945
+ const e = entry;
24946
+ if (e.type === "error") {
24947
+ return { error: String(e.message ?? e.code ?? "moderation error") };
24948
+ }
24949
+ if (e.type === "moderation_results" && Array.isArray(e.results)) {
24950
+ const first = e.results[0];
24951
+ return first ? toResult(first) : void 0;
24952
+ }
24953
+ if (e.type === "moderation_result" || e.categories) {
24954
+ return toResult(e);
24955
+ }
24956
+ return void 0;
24957
+ }
24958
+ function toResult(r) {
24959
+ return {
24960
+ flagged: Boolean(r.flagged),
24961
+ categories: r.categories ?? {},
24962
+ categoryScores: r.category_scores ?? {},
24963
+ categoryAppliedInputTypes: r.category_applied_input_types
24964
+ };
24965
+ }
24966
+
24640
24967
  // src/llm/providers/openai/tiers.ts
24641
- var REQUEST = {
24968
+ var REQUEST2 = {
24642
24969
  auto: "auto",
24643
24970
  standard: "default",
24644
24971
  priority: "priority",
@@ -24648,7 +24975,7 @@ var REQUEST = {
24648
24975
  var KNOWN = /* @__PURE__ */ new Set(["auto", "default", "flex", "scale", "priority"]);
24649
24976
  function openaiRequestTier(t) {
24650
24977
  if (!t) return void 0;
24651
- const mapped = REQUEST[t] ?? t;
24978
+ const mapped = REQUEST2[t] ?? t;
24652
24979
  return KNOWN.has(mapped) ? mapped : "auto";
24653
24980
  }
24654
24981
  function openaiBilledTier(raw) {
@@ -24707,6 +25034,9 @@ var OpenAIAdapter = class {
24707
25034
  if (req.stop) body.stop = req.stop;
24708
25035
  const tier = openaiRequestTier(req.serviceTier);
24709
25036
  if (tier) body.service_tier = tier;
25037
+ if (req.moderation && req.moderation.mode !== "emulate") {
25038
+ body.moderation = buildNativeModeration(req.moderation);
25039
+ }
24710
25040
  const hasAudioInput = req.messages.some(
24711
25041
  (m) => Array.isArray(m.content) && m.content.some((p) => p.type === "audio")
24712
25042
  );
@@ -24865,6 +25195,7 @@ var OpenAIAdapter = class {
24865
25195
  { tool_calls: "tool_use", length: "length", content_filter: "content_filter" }
24866
25196
  );
24867
25197
  const reasoningContent = message.reasoning_content ?? null;
25198
+ const moderation = parseNativeModeration(r.moderation);
24868
25199
  return {
24869
25200
  id: r.id,
24870
25201
  model: r.model,
@@ -24875,12 +25206,22 @@ var OpenAIAdapter = class {
24875
25206
  toolCalls,
24876
25207
  media,
24877
25208
  thinking: reasoningContent,
25209
+ ...moderation ? { moderation } : {},
24878
25210
  latencyMs,
24879
25211
  raw
24880
25212
  };
24881
25213
  }
24882
25214
  parseStreamEvent(event) {
24883
25215
  const data = JSON.parse(event.data);
25216
+ if (data.moderation) {
25217
+ const report = parseNativeModeration(data.moderation);
25218
+ const out = [];
25219
+ if (report?.input)
25220
+ out.push({ type: "moderation", phase: "input", result: report.input, source: "native" });
25221
+ if (report?.output)
25222
+ out.push({ type: "moderation", phase: "output", result: report.output, source: "native" });
25223
+ if (out.length) return out;
25224
+ }
24884
25225
  const choices = data.choices ?? [];
24885
25226
  const choice = choices[0];
24886
25227
  if (!choice) {
@@ -25481,6 +25822,9 @@ var OpenAIResponsesAdapter = class {
25481
25822
  if (req.topP !== void 0) body.top_p = req.topP;
25482
25823
  const tier = openaiRequestTier(req.serviceTier);
25483
25824
  if (tier) body.service_tier = tier;
25825
+ if (req.moderation && req.moderation.mode !== "emulate") {
25826
+ body.moderation = buildNativeModeration(req.moderation);
25827
+ }
25484
25828
  if (req.tools?.length) {
25485
25829
  body.tools = req.tools.map((t) => {
25486
25830
  if (isFunctionTool(t)) {
@@ -25519,7 +25863,9 @@ var OpenAIResponsesAdapter = class {
25519
25863
  if (req.thinking && req.thinking.mode !== "off") {
25520
25864
  body.reasoning = {
25521
25865
  effort: req.thinking.effort ?? "medium",
25522
- summary: "auto"
25866
+ summary: "auto",
25867
+ // Cross-turn reasoning persistence (gpt-5/o-series, Responses only).
25868
+ ...req.thinking.context ? { context: req.thinking.context } : {}
25523
25869
  };
25524
25870
  }
25525
25871
  return { body };
@@ -25611,6 +25957,7 @@ var OpenAIResponsesAdapter = class {
25611
25957
  const content = [];
25612
25958
  const toolCalls = [];
25613
25959
  const media = [];
25960
+ const files = [];
25614
25961
  let thinking = null;
25615
25962
  let text = "";
25616
25963
  for (const item of output) {
@@ -25622,6 +25969,22 @@ var OpenAIResponsesAdapter = class {
25622
25969
  const t = c.text;
25623
25970
  text += t;
25624
25971
  content.push({ type: "text", text: t });
25972
+ for (const a of c.annotations ?? []) {
25973
+ if (a.type === "container_file_citation" && typeof a.file_id === "string") {
25974
+ files.push({
25975
+ id: a.file_id,
25976
+ ...typeof a.filename === "string" ? { name: a.filename } : {},
25977
+ source: "code_execution"
25978
+ });
25979
+ }
25980
+ }
25981
+ }
25982
+ }
25983
+ }
25984
+ if (type === "code_interpreter_call") {
25985
+ for (const out of item.outputs ?? []) {
25986
+ if (out.type === "image" && typeof out.url === "string") {
25987
+ files.push({ url: out.url, source: "code_execution" });
25625
25988
  }
25626
25989
  }
25627
25990
  }
@@ -25663,6 +26026,7 @@ var OpenAIResponsesAdapter = class {
25663
26026
  text = r.output_text;
25664
26027
  if (text && content.length === 0) content.push({ type: "text", text });
25665
26028
  }
26029
+ const moderation = parseNativeModeration(r.moderation);
25666
26030
  return {
25667
26031
  id: r.id,
25668
26032
  model: r.model ?? "",
@@ -25673,6 +26037,8 @@ var OpenAIResponsesAdapter = class {
25673
26037
  toolCalls,
25674
26038
  thinking,
25675
26039
  media,
26040
+ ...files.length ? { files } : {},
26041
+ ...moderation ? { moderation } : {},
25676
26042
  latencyMs,
25677
26043
  raw
25678
26044
  };
@@ -25727,6 +26093,11 @@ var OpenAIResponsesAdapter = class {
25727
26093
  }
25728
26094
  if (type === "response.completed") {
25729
26095
  const response = data.response ?? data;
26096
+ const moderation = parseNativeModeration(response.moderation);
26097
+ if (moderation?.input)
26098
+ events.push({ type: "moderation", phase: "input", result: moderation.input, source: "native" });
26099
+ if (moderation?.output)
26100
+ events.push({ type: "moderation", phase: "output", result: moderation.output, source: "native" });
25730
26101
  const usage = response.usage;
25731
26102
  if (usage) events.push({ type: "usage", usage: this.parseUsage(usage) });
25732
26103
  events.push({
@@ -27393,6 +27764,7 @@ var AgentLoop = class _AgentLoop {
27393
27764
  _toolTimeout;
27394
27765
  _maxSteps;
27395
27766
  _guardrails;
27767
+ _toolInputGuardrails;
27396
27768
  _policy;
27397
27769
  _approve;
27398
27770
  _checkpoint;
@@ -27420,6 +27792,7 @@ var AgentLoop = class _AgentLoop {
27420
27792
  this._toolTimeout = config.toolTimeout ?? DEFAULT_TOOL_TIMEOUT_MS;
27421
27793
  this._maxSteps = config.maxSteps !== void 0 && config.maxSteps > 0 ? config.maxSteps : DEFAULT_MAX_STEPS;
27422
27794
  this._guardrails = config.guardrails ?? [];
27795
+ this._toolInputGuardrails = config.toolInputGuardrails ?? [];
27423
27796
  this._policy = config.policy ?? null;
27424
27797
  this._approve = config.approve ?? null;
27425
27798
  this._checkpoint = config.checkpoint ?? null;
@@ -27638,6 +28011,10 @@ var AgentLoop = class _AgentLoop {
27638
28011
  toolCalls: lastResponse?.toolCalls ?? [],
27639
28012
  thinking: lastResponse?.thinking ?? null,
27640
28013
  media: lastResponse?.media ?? [],
28014
+ // Propagate hosted-tool file outputs + inline-moderation result from the final
28015
+ // LLM response (e.g. code-execution files produced during the run).
28016
+ ...lastResponse?.files ? { files: lastResponse.files } : {},
28017
+ ...lastResponse?.moderation ? { moderation: lastResponse.moderation } : {},
27641
28018
  latencyMs: performance.now() - startPerf,
27642
28019
  raw: lastResponse?.raw ?? null
27643
28020
  };
@@ -27832,6 +28209,8 @@ var AgentLoop = class _AgentLoop {
27832
28209
  toolCalls: lastResponse?.toolCalls ?? [],
27833
28210
  thinking: lastResponse?.thinking ?? null,
27834
28211
  media: [],
28212
+ ...lastResponse?.files ? { files: lastResponse.files } : {},
28213
+ ...lastResponse?.moderation ? { moderation: lastResponse.moderation } : {},
27835
28214
  latencyMs: performance.now() - startPerf,
27836
28215
  raw: null
27837
28216
  };
@@ -27918,6 +28297,18 @@ var AgentLoop = class _AgentLoop {
27918
28297
  runTrace
27919
28298
  );
27920
28299
  if (!lookup.found) return lookup.errorResult;
28300
+ for (const g of this._toolInputGuardrails) {
28301
+ const decision = await g.check({
28302
+ toolName: tc.name,
28303
+ arguments: tc.arguments,
28304
+ callId: tc.id,
28305
+ step,
28306
+ trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id }
28307
+ });
28308
+ if (!decision.pass) {
28309
+ return this.buildDeniedResult(tc, decision.reason, reports);
28310
+ }
28311
+ }
27921
28312
  if (this._policy !== null) {
27922
28313
  const target = {
27923
28314
  kind: "tool",
@@ -27934,7 +28325,7 @@ var AgentLoop = class _AgentLoop {
27934
28325
  try {
27935
28326
  const baseCtx = { step, callId: tc.id, metrics, trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id } };
27936
28327
  const result = await executeWithTimeout(lookup.tool, tc, baseCtx, this._toolTimeout);
27937
- return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace);
28328
+ return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, lookup.tool, baseCtx);
27938
28329
  } catch (e) {
27939
28330
  return handleToolError(e, tc, this.hooks, runId, this.id, step, metrics, reports, toolStart, runTrace);
27940
28331
  }
@@ -28025,7 +28416,7 @@ var AgentLoop = class _AgentLoop {
28025
28416
  try {
28026
28417
  const baseCtx = { step, callId: tc.id, metrics, trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id } };
28027
28418
  const result = await executeWithTimeout(tool, tc, baseCtx, this._toolTimeout);
28028
- return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace);
28419
+ return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, tool, baseCtx);
28029
28420
  } catch (e) {
28030
28421
  return handleToolError(e, tc, this.hooks, runId, this.id, step, metrics, reports, toolStart, runTrace);
28031
28422
  }
@@ -28037,9 +28428,16 @@ var AgentLoop = class _AgentLoop {
28037
28428
  return this.buildDeniedResult(tc, denyMsg, reports);
28038
28429
  }
28039
28430
  /** Emit onToolCallComplete, push report, and return success content part. */
28040
- async buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace) {
28431
+ async buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, tool, context) {
28041
28432
  const latencyMs = performance.now() - toolStart;
28042
28433
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
28434
+ let customData;
28435
+ if (tool?.customDataExtractor && context) {
28436
+ try {
28437
+ customData = await tool.customDataExtractor(result, tc.arguments, context);
28438
+ } catch {
28439
+ }
28440
+ }
28043
28441
  await this.hooks.emit("onToolCallComplete", {
28044
28442
  runId,
28045
28443
  agentId: this.id,
@@ -28061,7 +28459,8 @@ var AgentLoop = class _AgentLoop {
28061
28459
  latencyMs,
28062
28460
  skipped: false,
28063
28461
  error: null,
28064
- metrics: Object.fromEntries(metrics)
28462
+ metrics: Object.fromEntries(metrics),
28463
+ ...customData !== void 0 ? { customData } : {}
28065
28464
  });
28066
28465
  return { type: "tool_result", id: tc.id, content: resultStr };
28067
28466
  }
@@ -33606,43 +34005,6 @@ function handoff(name, description, agent, opts = {}) {
33606
34005
  });
33607
34006
  }
33608
34007
 
33609
- // src/llm/providers/openai/moderations.ts
33610
- var OPENAI_MODERATION_BASE_URL = "https://api.openai.com";
33611
- var OPENAI_MODERATION_PATH = "/v1/moderations";
33612
- var OPENAI_MODERATION_DEFAULT_MODEL = "omni-moderation-latest";
33613
- var OpenAIModerationAdapter = class {
33614
- apiKey;
33615
- baseURL;
33616
- constructor(config) {
33617
- this.apiKey = config.apiKey;
33618
- this.baseURL = config.baseURL ?? OPENAI_MODERATION_BASE_URL;
33619
- }
33620
- async moderate(input, model, fetch2) {
33621
- const res = await fetch2({
33622
- url: `${this.baseURL}${OPENAI_MODERATION_PATH}`,
33623
- method: "POST",
33624
- headers: { authorization: `Bearer ${this.apiKey}`, "content-type": "application/json" },
33625
- body: { model, input },
33626
- provider: "openai",
33627
- model,
33628
- responseType: "json"
33629
- });
33630
- if (res.status >= 400) {
33631
- throw new Error(`OpenAI moderations failed (${res.status}): ${JSON.stringify(res.body)}`);
33632
- }
33633
- const data = res.body;
33634
- return (data.results ?? []).map(parseRawResult);
33635
- }
33636
- };
33637
- function parseRawResult(r) {
33638
- return {
33639
- flagged: r.flagged,
33640
- categories: r.categories,
33641
- categoryScores: r.category_scores,
33642
- categoryAppliedInputTypes: r.category_applied_input_types
33643
- };
33644
- }
33645
-
33646
34008
  // src/helpers/moderate.ts
33647
34009
  var MODERATION_COST_NOTE = "free: moderations endpoint not billed";
33648
34010
  var MODERATION_DEFAULT_PROVIDER = "openai";