@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.
package/dist/index.js CHANGED
@@ -22104,6 +22104,7 @@ var ModelCatalog = class {
22104
22104
  family: info.family,
22105
22105
  version: info.version,
22106
22106
  status: info.status,
22107
+ availability: info.availability,
22107
22108
  active: info.active,
22108
22109
  deprecation: info.deprecation
22109
22110
  });
@@ -22191,6 +22192,168 @@ var ModelCatalog = class {
22191
22192
  }
22192
22193
  };
22193
22194
 
22195
+ // src/llm/providers/openai/moderations.ts
22196
+ var OPENAI_MODERATION_BASE_URL = "https://api.openai.com";
22197
+ var OPENAI_MODERATION_PATH = "/v1/moderations";
22198
+ var OPENAI_MODERATION_DEFAULT_MODEL = "omni-moderation-latest";
22199
+ var OpenAIModerationAdapter = class {
22200
+ apiKey;
22201
+ baseURL;
22202
+ constructor(config) {
22203
+ this.apiKey = config.apiKey;
22204
+ this.baseURL = config.baseURL ?? OPENAI_MODERATION_BASE_URL;
22205
+ }
22206
+ async moderate(input, model, fetch2) {
22207
+ const res = await fetch2({
22208
+ url: `${this.baseURL}${OPENAI_MODERATION_PATH}`,
22209
+ method: "POST",
22210
+ headers: { authorization: `Bearer ${this.apiKey}`, "content-type": "application/json" },
22211
+ body: { model, input },
22212
+ provider: "openai",
22213
+ model,
22214
+ responseType: "json"
22215
+ });
22216
+ if (res.status >= 400) {
22217
+ throw new Error(`OpenAI moderations failed (${res.status}): ${JSON.stringify(res.body)}`);
22218
+ }
22219
+ const data = res.body;
22220
+ return (data.results ?? []).map(parseRawResult);
22221
+ }
22222
+ };
22223
+ function parseRawResult(r) {
22224
+ return {
22225
+ flagged: r.flagged,
22226
+ categories: r.categories,
22227
+ categoryScores: r.category_scores,
22228
+ categoryAppliedInputTypes: r.category_applied_input_types
22229
+ };
22230
+ }
22231
+
22232
+ // src/llm/moderation/types.ts
22233
+ var MODERATION_DEFAULT_MODEL = "omni-moderation-latest";
22234
+ var MODERATION_DEFAULT_STRATEGY = "buffer";
22235
+ var MODERATION_DEFAULT_INTERVAL = 400;
22236
+
22237
+ // src/llm/moderation/runner.ts
22238
+ function resolveModerationMode(provider, mod) {
22239
+ if (mod.mode) return mod.mode;
22240
+ return provider === "openai" ? "native" : "emulate";
22241
+ }
22242
+ function moderationInputText(messages) {
22243
+ const lastUser = [...messages].reverse().find((m) => m.role === "user");
22244
+ if (!lastUser) return "";
22245
+ return typeof lastUser.content === "string" ? lastUser.content : contentText(lastUser.content);
22246
+ }
22247
+ function emptyResult() {
22248
+ return {
22249
+ flagged: false,
22250
+ categories: {},
22251
+ categoryScores: {}
22252
+ };
22253
+ }
22254
+ async function runModeration(text, cfg) {
22255
+ if (!text) return emptyResult();
22256
+ try {
22257
+ const adapter = new OpenAIModerationAdapter({ apiKey: cfg.apiKey });
22258
+ const results = await adapter.moderate(text, cfg.model, cfg.fetch);
22259
+ return results[0] ?? emptyResult();
22260
+ } catch (e) {
22261
+ return { error: e instanceof Error ? e.message : String(e) };
22262
+ }
22263
+ }
22264
+ function moderationModel(mod) {
22265
+ return mod.model ?? MODERATION_DEFAULT_MODEL;
22266
+ }
22267
+ function emitModerationZeroCost(hooks, model) {
22268
+ const entry = {
22269
+ id: crypto.randomUUID(),
22270
+ timestamp: Date.now(),
22271
+ provider: "openai",
22272
+ model,
22273
+ tokens: { input: 0, output: 0, cached: 0, cacheWrite: 0, reasoning: 0 },
22274
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0, total: 0, source: "calculated" },
22275
+ providerEvidence: { note: "free: moderations endpoint not billed" },
22276
+ tags: { provider: "openai", model, type: "moderation" }
22277
+ };
22278
+ hooks.emitSync("onCostEntry", { entry, runningTotal: 0 });
22279
+ }
22280
+ function outputEvent(result) {
22281
+ return { type: "moderation", phase: "output", result, source: "emulated" };
22282
+ }
22283
+ function wrapModeratedStream(raw, strategy, interval, moderate2) {
22284
+ const step = interval > 0 ? interval : MODERATION_DEFAULT_INTERVAL;
22285
+ if (strategy === "post") return wrapPost(raw, moderate2);
22286
+ if (strategy === "parallel") return wrapParallel(raw, step, moderate2);
22287
+ return wrapBuffer(raw, step, moderate2);
22288
+ }
22289
+ async function* wrapPost(raw, moderate2) {
22290
+ let acc = "";
22291
+ for await (const ev of raw) {
22292
+ if (ev.type === "text") acc += ev.text;
22293
+ yield ev;
22294
+ }
22295
+ if (acc) yield outputEvent(await moderate2(acc));
22296
+ }
22297
+ async function* wrapBuffer(raw, interval, moderate2) {
22298
+ let acc = "";
22299
+ let checkedAt = 0;
22300
+ let hold = [];
22301
+ for await (const ev of raw) {
22302
+ hold.push(ev);
22303
+ if (ev.type === "text") {
22304
+ acc += ev.text;
22305
+ if (acc.length - checkedAt >= interval || ev.text.includes("\n")) {
22306
+ checkedAt = acc.length;
22307
+ yield outputEvent(await moderate2(acc));
22308
+ for (const h of hold) yield h;
22309
+ hold = [];
22310
+ }
22311
+ }
22312
+ }
22313
+ if (acc.length > checkedAt) yield outputEvent(await moderate2(acc));
22314
+ for (const h of hold) yield h;
22315
+ }
22316
+ async function* wrapParallel(raw, interval, moderate2) {
22317
+ const iter = raw[Symbol.asyncIterator]();
22318
+ const pending = /* @__PURE__ */ new Set();
22319
+ let acc = "";
22320
+ let checkedAt = 0;
22321
+ let rawDone = false;
22322
+ let rawNext = iter.next();
22323
+ const schedule = (text) => {
22324
+ const p = moderate2(text).then((result) => {
22325
+ pending.delete(p);
22326
+ return { tag: "mod", event: outputEvent(result) };
22327
+ });
22328
+ pending.add(p);
22329
+ };
22330
+ while (!rawDone || pending.size > 0) {
22331
+ const racers = [];
22332
+ if (!rawDone) racers.push(rawNext.then((result) => ({ tag: "raw", result })));
22333
+ for (const p of pending) racers.push(p);
22334
+ const winner = await Promise.race(racers);
22335
+ if (winner.tag === "mod") {
22336
+ yield winner.event;
22337
+ continue;
22338
+ }
22339
+ if (winner.result.done) {
22340
+ rawDone = true;
22341
+ if (acc.length > checkedAt) schedule(acc);
22342
+ continue;
22343
+ }
22344
+ const ev = winner.result.value;
22345
+ yield ev;
22346
+ if (ev.type === "text") {
22347
+ acc += ev.text;
22348
+ if (acc.length - checkedAt >= interval) {
22349
+ checkedAt = acc.length;
22350
+ schedule(acc);
22351
+ }
22352
+ }
22353
+ rawNext = iter.next();
22354
+ }
22355
+ }
22356
+
22194
22357
  // src/util/duration.ts
22195
22358
  var UNIT_MS = {
22196
22359
  s: 1e3,
@@ -22327,6 +22490,7 @@ var LLMClient = class {
22327
22490
  mode;
22328
22491
  batchable;
22329
22492
  adapter;
22493
+ apiKey;
22330
22494
  fetchFn;
22331
22495
  fetchStreamFn;
22332
22496
  priority;
@@ -22350,6 +22514,7 @@ var LLMClient = class {
22350
22514
  this.provider = config.provider;
22351
22515
  this.model = config.model;
22352
22516
  this.system = config.system;
22517
+ this.apiKey = config.apiKey;
22353
22518
  this.hooks = config.hooks ?? new HookBus();
22354
22519
  this.fetchFn = config.fetch;
22355
22520
  this.fetchStreamFn = config.fetchStream ?? null;
@@ -22403,6 +22568,34 @@ var LLMClient = class {
22403
22568
  }
22404
22569
  };
22405
22570
  }
22571
+ // ─── Moderation helpers ───────────────────────────────────────────────
22572
+ /** Resolve the OpenAI key the emulated moderation path needs. Reuses the
22573
+ * client's own key when the provider is OpenAI; otherwise requires an explicit
22574
+ * one. Throws when none is resolvable (report-only feature, but the call still
22575
+ * needs a key to reach the moderations endpoint). */
22576
+ resolveModerationKey(mod) {
22577
+ const key = mod.apiKey ?? (this.provider === "openai" ? this.apiKey : void 0);
22578
+ if (!key) {
22579
+ throw new Error(
22580
+ "moderation: emulated moderation requires an OpenAI API key (the only public moderations endpoint). Pass moderation.apiKey, or use the OpenAI provider."
22581
+ );
22582
+ }
22583
+ return key;
22584
+ }
22585
+ moderationConfig(mod) {
22586
+ return {
22587
+ apiKey: this.resolveModerationKey(mod),
22588
+ model: moderationModel(mod),
22589
+ fetch: this.fetchFn
22590
+ };
22591
+ }
22592
+ /** Fold a moderation stream event into the accumulating report. */
22593
+ mergeModeration(prev, phase, result, source) {
22594
+ const next = prev && prev.source === source ? { ...prev } : { source };
22595
+ if (phase === "input") next.input = result;
22596
+ else next.output = result;
22597
+ return next;
22598
+ }
22406
22599
  /** Submit a request. Returns the parsed CompletionResponse. */
22407
22600
  async complete(input, options = {}) {
22408
22601
  const rawMessages = normalizeInput(input);
@@ -22423,6 +22616,7 @@ var LLMClient = class {
22423
22616
  thinking: options.thinking,
22424
22617
  cache: options.cache,
22425
22618
  serviceTier: options.serviceTier,
22619
+ moderation: options.moderation,
22426
22620
  providerOptions: options.providerOptions,
22427
22621
  audio: options.audio,
22428
22622
  outputModalities: options.outputModalities,
@@ -22478,6 +22672,8 @@ var LLMClient = class {
22478
22672
  await this.hooks.emit("onBeforeSubmit", submitCtx);
22479
22673
  const inputChars = JSON.stringify(normalized.messages).length;
22480
22674
  const estimatedInputTokens = Math.ceil(inputChars / 4);
22675
+ const mod = options.moderation;
22676
+ const moderationCfg = mod && resolveModerationMode(this.provider, mod) === "emulate" ? this.moderationConfig(mod) : void 0;
22481
22677
  const start = performance.now();
22482
22678
  let response;
22483
22679
  if (submitCtx.intercepted && submitCtx.resultPromise) {
@@ -22503,6 +22699,22 @@ var LLMClient = class {
22503
22699
  }
22504
22700
  const latencyMs = performance.now() - start;
22505
22701
  const result = this.adapter.parseResponse(response.body, latencyMs);
22702
+ if (mod && moderationCfg) {
22703
+ const cfg = moderationCfg;
22704
+ const doInput = mod.input ?? true;
22705
+ const doOutput = mod.output ?? true;
22706
+ const [inputEntry, outputEntry] = await Promise.all([
22707
+ doInput ? runModeration(moderationInputText(normalized.messages), cfg) : Promise.resolve(void 0),
22708
+ doOutput ? runModeration(result.text, cfg) : Promise.resolve(void 0)
22709
+ ]);
22710
+ if (doInput) emitModerationZeroCost(this.hooks, cfg.model);
22711
+ if (doOutput) emitModerationZeroCost(this.hooks, cfg.model);
22712
+ result.moderation = {
22713
+ source: "emulated",
22714
+ ...inputEntry ? { input: inputEntry } : {},
22715
+ ...outputEntry ? { output: outputEntry } : {}
22716
+ };
22717
+ }
22506
22718
  await this.hooks.emit("onCompletion", {
22507
22719
  provider: this.provider,
22508
22720
  model: this.model,
@@ -22550,6 +22762,7 @@ var LLMClient = class {
22550
22762
  thinking: options.thinking,
22551
22763
  cache: options.cache,
22552
22764
  serviceTier: options.serviceTier,
22765
+ moderation: options.moderation,
22553
22766
  providerOptions: options.providerOptions,
22554
22767
  audio: options.audio,
22555
22768
  outputModalities: options.outputModalities,
@@ -22593,29 +22806,65 @@ var LLMClient = class {
22593
22806
  let thinking = "";
22594
22807
  let usage = emptyUsage();
22595
22808
  let finishReason = "stop";
22596
- for await (const sseEvent of this.fetchStreamFn(httpReq, {
22597
- queueName: this.queueName,
22598
- priority: this.priority,
22599
- ctx
22600
- })) {
22601
- const events = this.adapter.parseStreamEvent(sseEvent);
22602
- for (const event of events) {
22603
- switch (event.type) {
22604
- case "text":
22605
- text += event.text;
22606
- break;
22607
- case "thinking":
22608
- thinking += event.text;
22609
- break;
22610
- case "usage":
22611
- usage = event.usage;
22612
- break;
22613
- case "done":
22614
- finishReason = event.finishReason;
22615
- break;
22616
- }
22617
- yield event;
22809
+ let moderationReport;
22810
+ const fetchStream = this.fetchStreamFn;
22811
+ const adapter = this.adapter;
22812
+ const queueName = this.queueName;
22813
+ const priority = this.priority;
22814
+ async function* rawEvents() {
22815
+ for await (const sseEvent of fetchStream(httpReq, {
22816
+ queueName,
22817
+ priority,
22818
+ ctx
22819
+ })) {
22820
+ for (const ev of adapter.parseStreamEvent(sseEvent)) yield ev;
22821
+ }
22822
+ }
22823
+ const mod = options.moderation;
22824
+ const emulate = !!mod && resolveModerationMode(this.provider, mod) === "emulate";
22825
+ let eventStream = rawEvents();
22826
+ if (mod && emulate && (mod.output ?? true)) {
22827
+ const cfg = this.moderationConfig(mod);
22828
+ const hooks = this.hooks;
22829
+ const strategy = mod.stream?.strategy ?? MODERATION_DEFAULT_STRATEGY;
22830
+ const interval = mod.stream?.interval ?? MODERATION_DEFAULT_INTERVAL;
22831
+ eventStream = wrapModeratedStream(eventStream, strategy, interval, async (t) => {
22832
+ const r = await runModeration(t, cfg);
22833
+ emitModerationZeroCost(hooks, cfg.model);
22834
+ return r;
22835
+ });
22836
+ }
22837
+ if (mod && emulate && (mod.input ?? true)) {
22838
+ const cfg = this.moderationConfig(mod);
22839
+ const inputEntry = await runModeration(moderationInputText(normalized.messages), cfg);
22840
+ emitModerationZeroCost(this.hooks, cfg.model);
22841
+ moderationReport = this.mergeModeration(moderationReport, "input", inputEntry, "emulated");
22842
+ yield { type: "moderation", phase: "input", result: inputEntry, source: "emulated" };
22843
+ }
22844
+ for await (const event of eventStream) {
22845
+ switch (event.type) {
22846
+ case "text":
22847
+ text += event.text;
22848
+ break;
22849
+ case "thinking":
22850
+ thinking += event.text;
22851
+ break;
22852
+ case "usage":
22853
+ usage = event.usage;
22854
+ break;
22855
+ case "done":
22856
+ finishReason = event.finishReason;
22857
+ break;
22858
+ case "moderation":
22859
+ moderationReport = this.mergeModeration(
22860
+ moderationReport,
22861
+ event.phase,
22862
+ event.result,
22863
+ event.source
22864
+ );
22865
+ break;
22618
22866
  }
22867
+ yield event;
22619
22868
  }
22620
22869
  const inputChars = JSON.stringify(normalized.messages).length;
22621
22870
  const response = {
@@ -22628,6 +22877,7 @@ var LLMClient = class {
22628
22877
  toolCalls: [],
22629
22878
  thinking: thinking || null,
22630
22879
  media: [],
22880
+ ...moderationReport ? { moderation: moderationReport } : {},
22631
22881
  latencyMs: performance.now() - start,
22632
22882
  raw: null
22633
22883
  };
@@ -22936,6 +23186,9 @@ var AnthropicAdapter = class {
22936
23186
  if (t.type === "web_search") {
22937
23187
  return { type: "web_search_20250305", name: "web_search", max_uses: 5 };
22938
23188
  }
23189
+ if (t.type === "code_interpreter") {
23190
+ return { type: "code_execution_20260521", name: "code_execution" };
23191
+ }
22939
23192
  return null;
22940
23193
  }
22941
23194
  const tool = {
@@ -22979,7 +23232,10 @@ var AnthropicAdapter = class {
22979
23232
  });
22980
23233
  const headers = {};
22981
23234
  if (hasFileRef) headers["anthropic-beta"] = "files-api-2025-04-14";
22982
- return { body, headers };
23235
+ const usesCodeExec = req.tools?.some(
23236
+ (t) => !isFunctionTool(t) && t.type === "code_interpreter"
23237
+ );
23238
+ return { body, headers, ...usesCodeExec ? { path: "/v1/messages?beta=true" } : {} };
22983
23239
  }
22984
23240
  enableStreaming(providerReq, _req) {
22985
23241
  providerReq.body.stream = true;
@@ -23046,6 +23302,7 @@ var AnthropicAdapter = class {
23046
23302
  const content = [];
23047
23303
  let thinking = null;
23048
23304
  const toolCalls = [];
23305
+ const files = [];
23049
23306
  for (const block of contentBlocks) {
23050
23307
  if (block.type === "text") {
23051
23308
  content.push({ type: "text", text: block.text });
@@ -23060,6 +23317,15 @@ var AnthropicAdapter = class {
23060
23317
  };
23061
23318
  content.push(tc);
23062
23319
  toolCalls.push(tc);
23320
+ } else if (block.type === "bash_code_execution_tool_result" || block.type === "code_execution_tool_result") {
23321
+ const result = block.content;
23322
+ if (result && (result.type === "bash_code_execution_result" || result.type === "code_execution_result") && Array.isArray(result.content)) {
23323
+ for (const out of result.content) {
23324
+ if ((out.type === "bash_code_execution_output" || out.type === "code_execution_output") && typeof out.file_id === "string") {
23325
+ files.push({ id: out.file_id, source: "code_execution" });
23326
+ }
23327
+ }
23328
+ }
23063
23329
  }
23064
23330
  }
23065
23331
  const finishReason = extractFinishReason(toolCalls.length > 0, r.stop_reason, {
@@ -23074,6 +23340,7 @@ var AnthropicAdapter = class {
23074
23340
  text: content.filter((p) => p.type === "text").map((p) => p.text).join(""),
23075
23341
  toolCalls,
23076
23342
  media: [],
23343
+ ...files.length ? { files } : {},
23077
23344
  thinking,
23078
23345
  latencyMs,
23079
23346
  raw
@@ -23379,6 +23646,17 @@ function resolveVoice(provider, voice) {
23379
23646
  return VOICE_ALIASES[provider]?.[voice] ?? voice;
23380
23647
  }
23381
23648
 
23649
+ // src/llm/providers/google/tiers.ts
23650
+ var REQUEST = /* @__PURE__ */ new Set(["flex", "standard", "priority"]);
23651
+ function googleRequestTier(t) {
23652
+ if (!t) return void 0;
23653
+ return REQUEST.has(t) ? t : void 0;
23654
+ }
23655
+ function googleBilledTier(raw) {
23656
+ if (typeof raw !== "string" || !raw) return {};
23657
+ return { serviceTier: raw, pricingTier: raw.toLowerCase() };
23658
+ }
23659
+
23382
23660
  // src/llm/providers/google/constants.ts
23383
23661
  var GOOGLE_THINKING_LEVELS = {
23384
23662
  low: "LOW",
@@ -23431,6 +23709,8 @@ var GoogleAdapter = class {
23431
23709
  contents,
23432
23710
  generationConfig: config
23433
23711
  };
23712
+ const tier = googleRequestTier(req.serviceTier);
23713
+ if (tier) body.serviceTier = tier;
23434
23714
  if (req.system) {
23435
23715
  body.systemInstruction = { parts: [{ text: req.system }] };
23436
23716
  }
@@ -23549,7 +23829,9 @@ var GoogleAdapter = class {
23549
23829
  const content = [];
23550
23830
  const toolCalls = [];
23551
23831
  const media = [];
23832
+ const files = [];
23552
23833
  let thinking = null;
23834
+ const hasCodeExec = parts.some((p) => p.executableCode || p.codeExecutionResult);
23553
23835
  for (const part of parts) {
23554
23836
  if (part.text !== void 0 && !part.thought) {
23555
23837
  content.push({ type: "text", text: part.text });
@@ -23560,7 +23842,9 @@ var GoogleAdapter = class {
23560
23842
  if (part.inlineData) {
23561
23843
  const inline = part.inlineData;
23562
23844
  const mime = inline.mimeType;
23563
- if (mime.startsWith("image/")) {
23845
+ if (hasCodeExec) {
23846
+ files.push({ data: inline.data, mimeType: mime, source: "code_execution" });
23847
+ } else if (mime.startsWith("image/")) {
23564
23848
  const p = {
23565
23849
  type: "image_output",
23566
23850
  mediaId: "",
@@ -23621,6 +23905,7 @@ var GoogleAdapter = class {
23621
23905
  toolCalls,
23622
23906
  thinking,
23623
23907
  media,
23908
+ ...files.length ? { files } : {},
23624
23909
  latencyMs,
23625
23910
  raw
23626
23911
  };
@@ -23689,7 +23974,9 @@ var GoogleAdapter = class {
23689
23974
  totalTokens: u.totalTokenCount ?? input + output,
23690
23975
  cachedTokens: u.cachedContentTokenCount ?? 0,
23691
23976
  cacheWriteTokens: 0,
23692
- reasoningTokens: u.thoughtsTokenCount ?? 0
23977
+ reasoningTokens: u.thoughtsTokenCount ?? 0,
23978
+ // Billed service tier (output-only `usageMetadata.serviceTier`).
23979
+ ...googleBilledTier(u.serviceTier)
23693
23980
  };
23694
23981
  }
23695
23982
  };
@@ -24564,8 +24851,48 @@ var OpenAIBatchAdapter = class {
24564
24851
  }
24565
24852
  };
24566
24853
 
24854
+ // src/llm/moderation/native.ts
24855
+ function buildNativeModeration(mod) {
24856
+ return { model: mod.model ?? MODERATION_DEFAULT_MODEL };
24857
+ }
24858
+ function parseNativeModeration(raw) {
24859
+ if (!raw || typeof raw !== "object") return void 0;
24860
+ const m = raw;
24861
+ const input = parseEntry(m.input);
24862
+ const output = parseEntry(m.output);
24863
+ if (!input && !output) return void 0;
24864
+ return {
24865
+ source: "native",
24866
+ ...input ? { input } : {},
24867
+ ...output ? { output } : {}
24868
+ };
24869
+ }
24870
+ function parseEntry(entry) {
24871
+ if (!entry || typeof entry !== "object") return void 0;
24872
+ const e = entry;
24873
+ if (e.type === "error") {
24874
+ return { error: String(e.message ?? e.code ?? "moderation error") };
24875
+ }
24876
+ if (e.type === "moderation_results" && Array.isArray(e.results)) {
24877
+ const first = e.results[0];
24878
+ return first ? toResult(first) : void 0;
24879
+ }
24880
+ if (e.type === "moderation_result" || e.categories) {
24881
+ return toResult(e);
24882
+ }
24883
+ return void 0;
24884
+ }
24885
+ function toResult(r) {
24886
+ return {
24887
+ flagged: Boolean(r.flagged),
24888
+ categories: r.categories ?? {},
24889
+ categoryScores: r.category_scores ?? {},
24890
+ categoryAppliedInputTypes: r.category_applied_input_types
24891
+ };
24892
+ }
24893
+
24567
24894
  // src/llm/providers/openai/tiers.ts
24568
- var REQUEST = {
24895
+ var REQUEST2 = {
24569
24896
  auto: "auto",
24570
24897
  standard: "default",
24571
24898
  priority: "priority",
@@ -24575,7 +24902,7 @@ var REQUEST = {
24575
24902
  var KNOWN = /* @__PURE__ */ new Set(["auto", "default", "flex", "scale", "priority"]);
24576
24903
  function openaiRequestTier(t) {
24577
24904
  if (!t) return void 0;
24578
- const mapped = REQUEST[t] ?? t;
24905
+ const mapped = REQUEST2[t] ?? t;
24579
24906
  return KNOWN.has(mapped) ? mapped : "auto";
24580
24907
  }
24581
24908
  function openaiBilledTier(raw) {
@@ -24634,6 +24961,9 @@ var OpenAIAdapter = class {
24634
24961
  if (req.stop) body.stop = req.stop;
24635
24962
  const tier = openaiRequestTier(req.serviceTier);
24636
24963
  if (tier) body.service_tier = tier;
24964
+ if (req.moderation && req.moderation.mode !== "emulate") {
24965
+ body.moderation = buildNativeModeration(req.moderation);
24966
+ }
24637
24967
  const hasAudioInput = req.messages.some(
24638
24968
  (m) => Array.isArray(m.content) && m.content.some((p) => p.type === "audio")
24639
24969
  );
@@ -24792,6 +25122,7 @@ var OpenAIAdapter = class {
24792
25122
  { tool_calls: "tool_use", length: "length", content_filter: "content_filter" }
24793
25123
  );
24794
25124
  const reasoningContent = message.reasoning_content ?? null;
25125
+ const moderation = parseNativeModeration(r.moderation);
24795
25126
  return {
24796
25127
  id: r.id,
24797
25128
  model: r.model,
@@ -24802,12 +25133,22 @@ var OpenAIAdapter = class {
24802
25133
  toolCalls,
24803
25134
  media,
24804
25135
  thinking: reasoningContent,
25136
+ ...moderation ? { moderation } : {},
24805
25137
  latencyMs,
24806
25138
  raw
24807
25139
  };
24808
25140
  }
24809
25141
  parseStreamEvent(event) {
24810
25142
  const data = JSON.parse(event.data);
25143
+ if (data.moderation) {
25144
+ const report = parseNativeModeration(data.moderation);
25145
+ const out = [];
25146
+ if (report?.input)
25147
+ out.push({ type: "moderation", phase: "input", result: report.input, source: "native" });
25148
+ if (report?.output)
25149
+ out.push({ type: "moderation", phase: "output", result: report.output, source: "native" });
25150
+ if (out.length) return out;
25151
+ }
24811
25152
  const choices = data.choices ?? [];
24812
25153
  const choice = choices[0];
24813
25154
  if (!choice) {
@@ -25408,6 +25749,9 @@ var OpenAIResponsesAdapter = class {
25408
25749
  if (req.topP !== void 0) body.top_p = req.topP;
25409
25750
  const tier = openaiRequestTier(req.serviceTier);
25410
25751
  if (tier) body.service_tier = tier;
25752
+ if (req.moderation && req.moderation.mode !== "emulate") {
25753
+ body.moderation = buildNativeModeration(req.moderation);
25754
+ }
25411
25755
  if (req.tools?.length) {
25412
25756
  body.tools = req.tools.map((t) => {
25413
25757
  if (isFunctionTool(t)) {
@@ -25446,7 +25790,9 @@ var OpenAIResponsesAdapter = class {
25446
25790
  if (req.thinking && req.thinking.mode !== "off") {
25447
25791
  body.reasoning = {
25448
25792
  effort: req.thinking.effort ?? "medium",
25449
- summary: "auto"
25793
+ summary: "auto",
25794
+ // Cross-turn reasoning persistence (gpt-5/o-series, Responses only).
25795
+ ...req.thinking.context ? { context: req.thinking.context } : {}
25450
25796
  };
25451
25797
  }
25452
25798
  return { body };
@@ -25538,6 +25884,7 @@ var OpenAIResponsesAdapter = class {
25538
25884
  const content = [];
25539
25885
  const toolCalls = [];
25540
25886
  const media = [];
25887
+ const files = [];
25541
25888
  let thinking = null;
25542
25889
  let text = "";
25543
25890
  for (const item of output) {
@@ -25549,6 +25896,22 @@ var OpenAIResponsesAdapter = class {
25549
25896
  const t = c.text;
25550
25897
  text += t;
25551
25898
  content.push({ type: "text", text: t });
25899
+ for (const a of c.annotations ?? []) {
25900
+ if (a.type === "container_file_citation" && typeof a.file_id === "string") {
25901
+ files.push({
25902
+ id: a.file_id,
25903
+ ...typeof a.filename === "string" ? { name: a.filename } : {},
25904
+ source: "code_execution"
25905
+ });
25906
+ }
25907
+ }
25908
+ }
25909
+ }
25910
+ }
25911
+ if (type === "code_interpreter_call") {
25912
+ for (const out of item.outputs ?? []) {
25913
+ if (out.type === "image" && typeof out.url === "string") {
25914
+ files.push({ url: out.url, source: "code_execution" });
25552
25915
  }
25553
25916
  }
25554
25917
  }
@@ -25590,6 +25953,7 @@ var OpenAIResponsesAdapter = class {
25590
25953
  text = r.output_text;
25591
25954
  if (text && content.length === 0) content.push({ type: "text", text });
25592
25955
  }
25956
+ const moderation = parseNativeModeration(r.moderation);
25593
25957
  return {
25594
25958
  id: r.id,
25595
25959
  model: r.model ?? "",
@@ -25600,6 +25964,8 @@ var OpenAIResponsesAdapter = class {
25600
25964
  toolCalls,
25601
25965
  thinking,
25602
25966
  media,
25967
+ ...files.length ? { files } : {},
25968
+ ...moderation ? { moderation } : {},
25603
25969
  latencyMs,
25604
25970
  raw
25605
25971
  };
@@ -25654,6 +26020,11 @@ var OpenAIResponsesAdapter = class {
25654
26020
  }
25655
26021
  if (type === "response.completed") {
25656
26022
  const response = data.response ?? data;
26023
+ const moderation = parseNativeModeration(response.moderation);
26024
+ if (moderation?.input)
26025
+ events.push({ type: "moderation", phase: "input", result: moderation.input, source: "native" });
26026
+ if (moderation?.output)
26027
+ events.push({ type: "moderation", phase: "output", result: moderation.output, source: "native" });
25657
26028
  const usage = response.usage;
25658
26029
  if (usage) events.push({ type: "usage", usage: this.parseUsage(usage) });
25659
26030
  events.push({
@@ -27320,6 +27691,7 @@ var AgentLoop = class _AgentLoop {
27320
27691
  _toolTimeout;
27321
27692
  _maxSteps;
27322
27693
  _guardrails;
27694
+ _toolInputGuardrails;
27323
27695
  _policy;
27324
27696
  _approve;
27325
27697
  _checkpoint;
@@ -27347,6 +27719,7 @@ var AgentLoop = class _AgentLoop {
27347
27719
  this._toolTimeout = config.toolTimeout ?? DEFAULT_TOOL_TIMEOUT_MS;
27348
27720
  this._maxSteps = config.maxSteps !== void 0 && config.maxSteps > 0 ? config.maxSteps : DEFAULT_MAX_STEPS;
27349
27721
  this._guardrails = config.guardrails ?? [];
27722
+ this._toolInputGuardrails = config.toolInputGuardrails ?? [];
27350
27723
  this._policy = config.policy ?? null;
27351
27724
  this._approve = config.approve ?? null;
27352
27725
  this._checkpoint = config.checkpoint ?? null;
@@ -27565,6 +27938,10 @@ var AgentLoop = class _AgentLoop {
27565
27938
  toolCalls: lastResponse?.toolCalls ?? [],
27566
27939
  thinking: lastResponse?.thinking ?? null,
27567
27940
  media: lastResponse?.media ?? [],
27941
+ // Propagate hosted-tool file outputs + inline-moderation result from the final
27942
+ // LLM response (e.g. code-execution files produced during the run).
27943
+ ...lastResponse?.files ? { files: lastResponse.files } : {},
27944
+ ...lastResponse?.moderation ? { moderation: lastResponse.moderation } : {},
27568
27945
  latencyMs: performance.now() - startPerf,
27569
27946
  raw: lastResponse?.raw ?? null
27570
27947
  };
@@ -27759,6 +28136,8 @@ var AgentLoop = class _AgentLoop {
27759
28136
  toolCalls: lastResponse?.toolCalls ?? [],
27760
28137
  thinking: lastResponse?.thinking ?? null,
27761
28138
  media: [],
28139
+ ...lastResponse?.files ? { files: lastResponse.files } : {},
28140
+ ...lastResponse?.moderation ? { moderation: lastResponse.moderation } : {},
27762
28141
  latencyMs: performance.now() - startPerf,
27763
28142
  raw: null
27764
28143
  };
@@ -27845,6 +28224,18 @@ var AgentLoop = class _AgentLoop {
27845
28224
  runTrace
27846
28225
  );
27847
28226
  if (!lookup.found) return lookup.errorResult;
28227
+ for (const g of this._toolInputGuardrails) {
28228
+ const decision = await g.check({
28229
+ toolName: tc.name,
28230
+ arguments: tc.arguments,
28231
+ callId: tc.id,
28232
+ step,
28233
+ trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id }
28234
+ });
28235
+ if (!decision.pass) {
28236
+ return this.buildDeniedResult(tc, decision.reason, reports);
28237
+ }
28238
+ }
27848
28239
  if (this._policy !== null) {
27849
28240
  const target = {
27850
28241
  kind: "tool",
@@ -27861,7 +28252,7 @@ var AgentLoop = class _AgentLoop {
27861
28252
  try {
27862
28253
  const baseCtx = { step, callId: tc.id, metrics, trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id } };
27863
28254
  const result = await executeWithTimeout(lookup.tool, tc, baseCtx, this._toolTimeout);
27864
- return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace);
28255
+ return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, lookup.tool, baseCtx);
27865
28256
  } catch (e) {
27866
28257
  return handleToolError(e, tc, this.hooks, runId, this.id, step, metrics, reports, toolStart, runTrace);
27867
28258
  }
@@ -27952,7 +28343,7 @@ var AgentLoop = class _AgentLoop {
27952
28343
  try {
27953
28344
  const baseCtx = { step, callId: tc.id, metrics, trace: { sessionId: runTrace.sessionId, requestId: runTrace.requestId, callId: tc.id } };
27954
28345
  const result = await executeWithTimeout(tool, tc, baseCtx, this._toolTimeout);
27955
- return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace);
28346
+ return await this.buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, tool, baseCtx);
27956
28347
  } catch (e) {
27957
28348
  return handleToolError(e, tc, this.hooks, runId, this.id, step, metrics, reports, toolStart, runTrace);
27958
28349
  }
@@ -27964,9 +28355,16 @@ var AgentLoop = class _AgentLoop {
27964
28355
  return this.buildDeniedResult(tc, denyMsg, reports);
27965
28356
  }
27966
28357
  /** Emit onToolCallComplete, push report, and return success content part. */
27967
- async buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace) {
28358
+ async buildSuccessResult(tc, result, runId, step, metrics, reports, toolStart, runTrace, tool, context) {
27968
28359
  const latencyMs = performance.now() - toolStart;
27969
28360
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
28361
+ let customData;
28362
+ if (tool?.customDataExtractor && context) {
28363
+ try {
28364
+ customData = await tool.customDataExtractor(result, tc.arguments, context);
28365
+ } catch {
28366
+ }
28367
+ }
27970
28368
  await this.hooks.emit("onToolCallComplete", {
27971
28369
  runId,
27972
28370
  agentId: this.id,
@@ -27988,7 +28386,8 @@ var AgentLoop = class _AgentLoop {
27988
28386
  latencyMs,
27989
28387
  skipped: false,
27990
28388
  error: null,
27991
- metrics: Object.fromEntries(metrics)
28389
+ metrics: Object.fromEntries(metrics),
28390
+ ...customData !== void 0 ? { customData } : {}
27992
28391
  });
27993
28392
  return { type: "tool_result", id: tc.id, content: resultStr };
27994
28393
  }
@@ -33533,43 +33932,6 @@ function handoff(name, description, agent, opts = {}) {
33533
33932
  });
33534
33933
  }
33535
33934
 
33536
- // src/llm/providers/openai/moderations.ts
33537
- var OPENAI_MODERATION_BASE_URL = "https://api.openai.com";
33538
- var OPENAI_MODERATION_PATH = "/v1/moderations";
33539
- var OPENAI_MODERATION_DEFAULT_MODEL = "omni-moderation-latest";
33540
- var OpenAIModerationAdapter = class {
33541
- apiKey;
33542
- baseURL;
33543
- constructor(config) {
33544
- this.apiKey = config.apiKey;
33545
- this.baseURL = config.baseURL ?? OPENAI_MODERATION_BASE_URL;
33546
- }
33547
- async moderate(input, model, fetch2) {
33548
- const res = await fetch2({
33549
- url: `${this.baseURL}${OPENAI_MODERATION_PATH}`,
33550
- method: "POST",
33551
- headers: { authorization: `Bearer ${this.apiKey}`, "content-type": "application/json" },
33552
- body: { model, input },
33553
- provider: "openai",
33554
- model,
33555
- responseType: "json"
33556
- });
33557
- if (res.status >= 400) {
33558
- throw new Error(`OpenAI moderations failed (${res.status}): ${JSON.stringify(res.body)}`);
33559
- }
33560
- const data = res.body;
33561
- return (data.results ?? []).map(parseRawResult);
33562
- }
33563
- };
33564
- function parseRawResult(r) {
33565
- return {
33566
- flagged: r.flagged,
33567
- categories: r.categories,
33568
- categoryScores: r.category_scores,
33569
- categoryAppliedInputTypes: r.category_applied_input_types
33570
- };
33571
- }
33572
-
33573
33935
  // src/helpers/moderate.ts
33574
33936
  var MODERATION_COST_NOTE = "free: moderations endpoint not billed";
33575
33937
  var MODERATION_DEFAULT_PROVIDER = "openai";