@opencow-ai/opencow-agent-sdk 0.4.13 → 0.4.14

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/client.js CHANGED
@@ -35041,7 +35041,8 @@ function normalizeGithubModelsApiModel(requestedModel) {
35041
35041
  return segment;
35042
35042
  }
35043
35043
  function resolveProviderTransport(options) {
35044
- const rawBaseUrl = asEnvUrl(options?.baseUrl) ?? asEnvUrl(getQueryEnvVar("OPENAI_BASE_URL")) ?? asEnvUrl(getQueryEnvVar("OPENAI_API_BASE"));
35044
+ const optionBaseUrl = asEnvUrl(options?.baseUrl);
35045
+ const rawBaseUrl = optionBaseUrl ?? asEnvUrl(getQueryEnvVar("OPENAI_BASE_URL")) ?? asEnvUrl(getQueryEnvVar("OPENAI_API_BASE"));
35045
35046
  const envOverride = options?.transportOverride ?? getQueryEnvVar(QUERY_ENV_KEY_TRANSPORT_OVERRIDE);
35046
35047
  if (envOverride && envOverride !== "auto") {
35047
35048
  let normalized;
@@ -35051,7 +35052,8 @@ function resolveProviderTransport(options) {
35051
35052
  } else {
35052
35053
  normalized = envOverride;
35053
35054
  }
35054
- const baseUrl2 = (rawBaseUrl ?? (normalized === "openai_responses" ? DEFAULT_CODEX_BASE_URL : DEFAULT_OPENAI_BASE_URL)).replace(/\/+$/, "");
35055
+ const baseUrlSource = rawBaseUrl ?? (normalized === "openai_responses" ? DEFAULT_CODEX_BASE_URL : DEFAULT_OPENAI_BASE_URL);
35056
+ const baseUrl2 = baseUrlSource.replace(/\/+$/, "");
35055
35057
  return { transport: normalized, baseUrl: baseUrl2 };
35056
35058
  }
35057
35059
  const modelForDetection = options?.model?.trim() ?? "";
@@ -35179,7 +35181,7 @@ function resolveCodexApiCredentials(env2 = process.env) {
35179
35181
  originator: "opencow"
35180
35182
  };
35181
35183
  }
35182
- var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1", DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex", DEFAULT_GITHUB_MODELS_API_MODEL = "openai/gpt-4.1", CODEX_ALIAS_MODELS, QUERY_ENV_KEY_TRANSPORT_OVERRIDE = "__OPENCOW_TRANSPORT_OVERRIDE", QUERY_ENV_KEY_REASONING_EFFORT_OVERRIDE = "__OPENCOW_REASONING_EFFORT_OVERRIDE", QUERY_ENV_VALUE_REASONING_EFFORT_CLEAR = "__OPENCOW_CLEAR_REASONING_EFFORT__", QUERY_ENV_KEY_PROVIDER_SPECIFIC_OPENAI_RESPONSES = "__OPENCOW_PROVIDER_SPECIFIC_OPENAI_RESPONSES", LOCALHOST_HOSTNAMES, warnedCodexAliasOnce = false, MissingProviderModelError;
35184
+ var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1", DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex", DEFAULT_GITHUB_MODELS_API_MODEL = "openai/gpt-4.1", CODEX_ALIAS_MODELS, QUERY_ENV_KEY_TRANSPORT_OVERRIDE = "__OPENCOW_TRANSPORT_OVERRIDE", QUERY_ENV_KEY_REASONING_EFFORT_OVERRIDE = "__OPENCOW_REASONING_EFFORT_OVERRIDE", QUERY_ENV_VALUE_REASONING_EFFORT_CLEAR = "__OPENCOW_CLEAR_REASONING_EFFORT__", QUERY_ENV_KEY_PROVIDER_SPECIFIC_OPENAI_RESPONSES = "__OPENCOW_PROVIDER_SPECIFIC_OPENAI_RESPONSES", QUERY_ENV_KEY_EXTRA_BODY = "__OPENCOW_EXTRA_BODY", LOCALHOST_HOSTNAMES, warnedCodexAliasOnce = false, MissingProviderModelError;
35183
35185
  var init_config2 = __esm(() => {
35184
35186
  init_envUtils();
35185
35187
  init_state2();
@@ -94704,6 +94706,48 @@ var init_proxy = __esm(() => {
94704
94706
  });
94705
94707
  });
94706
94708
 
94709
+ // src/providers/shared/httpObservability.ts
94710
+ function classifyModelHttpEndpoint(url3) {
94711
+ try {
94712
+ const pathname = new URL(url3).pathname.replace(/\/+$/, "");
94713
+ if (pathname.endsWith("/messages/count_tokens"))
94714
+ return "count_tokens";
94715
+ if (pathname.endsWith("/chat/completions"))
94716
+ return "messages";
94717
+ if (pathname.endsWith("/messages"))
94718
+ return "messages";
94719
+ if (pathname.endsWith("/responses"))
94720
+ return "responses";
94721
+ return pathname || "unknown";
94722
+ } catch {
94723
+ return "unknown";
94724
+ }
94725
+ }
94726
+ function errorToFailureReason(error41) {
94727
+ if (error41 instanceof Error)
94728
+ return error41.message;
94729
+ return String(error41);
94730
+ }
94731
+ function responseBodyToFailureReason(status, body) {
94732
+ let detail = body.trim();
94733
+ try {
94734
+ const parsed = JSON.parse(body);
94735
+ const message = typeof parsed.error?.message === "string" ? parsed.error.message : typeof parsed.message === "string" ? parsed.message : undefined;
94736
+ const code = typeof parsed.error?.code === "string" ? parsed.error.code : typeof parsed.error?.type === "string" ? parsed.error.type : undefined;
94737
+ detail = [code, message].filter(Boolean).join(": ") || detail;
94738
+ } catch {}
94739
+ const compact = detail.replace(/\s+/g, " ").slice(0, 240);
94740
+ return compact ? `HTTP ${status}: ${compact}` : `HTTP ${status}`;
94741
+ }
94742
+ function emitSdkHttp(event) {
94743
+ try {
94744
+ emitHttp(event);
94745
+ } catch {}
94746
+ }
94747
+ var init_httpObservability = __esm(() => {
94748
+ init_observer();
94749
+ });
94750
+
94707
94751
  // src/providers/shared/geminiAuth.ts
94708
94752
  import { homedir as homedir11 } from "node:os";
94709
94753
  import { join as join23 } from "node:path";
@@ -95334,6 +95378,44 @@ var init_capabilities2 = __esm(() => {
95334
95378
  ]);
95335
95379
  });
95336
95380
 
95381
+ // src/providers/shared/requestSideChannels.ts
95382
+ function isRecord(value) {
95383
+ return typeof value === "object" && value !== null && !Array.isArray(value);
95384
+ }
95385
+ function parseJsonObject(raw) {
95386
+ if (!raw)
95387
+ return;
95388
+ try {
95389
+ const parsed = JSON.parse(raw);
95390
+ return isRecord(parsed) ? parsed : undefined;
95391
+ } catch {
95392
+ return;
95393
+ }
95394
+ }
95395
+ function readExplicitExtraBodyParams() {
95396
+ return parseJsonObject(getQueryEnvVar(QUERY_ENV_KEY_EXTRA_BODY)) ?? parseJsonObject(resolveEnvVar("EXTRA_BODY"));
95397
+ }
95398
+ function mergeOpenAICompatibleExtraBodyParams(body, params) {
95399
+ const extra = {};
95400
+ const shouldMerge = (key, value) => value !== undefined && !(key in body);
95401
+ const explicitExtraBody = readExplicitExtraBodyParams();
95402
+ if (explicitExtraBody) {
95403
+ Object.assign(extra, explicitExtraBody);
95404
+ }
95405
+ const nestedExtraBody = params.extra_body;
95406
+ if (isRecord(nestedExtraBody)) {
95407
+ Object.assign(extra, nestedExtraBody);
95408
+ }
95409
+ for (const [key, value] of Object.entries(extra)) {
95410
+ if (shouldMerge(key, value))
95411
+ body[key] = value;
95412
+ }
95413
+ }
95414
+ var init_requestSideChannels = __esm(() => {
95415
+ init_state2();
95416
+ init_config2();
95417
+ });
95418
+
95337
95419
  // src/providers/codex/shim.ts
95338
95420
  function parseSseChunk(chunk) {
95339
95421
  const lines = chunk.split(`
@@ -95682,6 +95764,7 @@ async function performCodexRequest(options) {
95682
95764
  body.top_p = options.params.top_p;
95683
95765
  }
95684
95766
  }
95767
+ mergeOpenAICompatibleExtraBodyParams(body, options.params);
95685
95768
  const headers = {
95686
95769
  "Content-Type": "application/json",
95687
95770
  ...options.defaultHeaders,
@@ -96053,6 +96136,7 @@ var init_shim = __esm(() => {
96053
96136
  init_sdk();
96054
96137
  init_schema();
96055
96138
  init_capabilities2();
96139
+ init_requestSideChannels();
96056
96140
  init_canonical();
96057
96141
  });
96058
96142
 
@@ -96250,7 +96334,7 @@ function convertContentBlocks(content) {
96250
96334
  return parts[0].text ?? "";
96251
96335
  return parts;
96252
96336
  }
96253
- function convertMessages(messages, system) {
96337
+ function convertMessages(messages, system, options = {}) {
96254
96338
  const result = [];
96255
96339
  const sysText = convertSystemPrompt2(system);
96256
96340
  if (sysText) {
@@ -96313,7 +96397,7 @@ function convertMessages(messages, system) {
96313
96397
  return text || null;
96314
96398
  })()
96315
96399
  };
96316
- if (thinkingBlocks.length > 0) {
96400
+ if (options.replayReasoningContent !== false && thinkingBlocks.length > 0) {
96317
96401
  const reasoningText = thinkingBlocks.map((b) => b.thinking ?? "").filter(Boolean).join(`
96318
96402
  `);
96319
96403
  if (reasoningText) {
@@ -96446,7 +96530,8 @@ function serializeParallelToolCalls(messages, capabilities) {
96446
96530
  }
96447
96531
  function buildOpenAIRequestBody(params, ctx) {
96448
96532
  const capabilities = getOpenAIChatProviderCapabilities(ctx.resolvedModel);
96449
- const openaiMessages = serializeParallelToolCalls(convertMessages(params.messages, params.system), capabilities);
96533
+ const replayReasoningContent = !isGeminiTarget(ctx.resolvedModel);
96534
+ const openaiMessages = serializeParallelToolCalls(convertMessages(params.messages, params.system, { replayReasoningContent }), capabilities);
96450
96535
  const body = {
96451
96536
  model: ctx.resolvedModel,
96452
96537
  messages: openaiMessages,
@@ -96497,6 +96582,7 @@ function buildOpenAIRequestBody(params, ctx) {
96497
96582
  }
96498
96583
  }
96499
96584
  }
96585
+ mergeOpenAICompatibleExtraBodyParams(body, params);
96500
96586
  return body;
96501
96587
  }
96502
96588
  async function* openaiStreamToAnthropic(response, model) {
@@ -96823,10 +96909,12 @@ class OpenAIShimMessages {
96823
96909
  defaultHeaders;
96824
96910
  reasoningEffort;
96825
96911
  providerOverride;
96826
- constructor(defaultHeaders, reasoningEffort, providerOverride) {
96912
+ source;
96913
+ constructor(defaultHeaders, reasoningEffort, providerOverride, source) {
96827
96914
  this.defaultHeaders = defaultHeaders;
96828
96915
  this.reasoningEffort = reasoningEffort;
96829
96916
  this.providerOverride = providerOverride;
96917
+ this.source = source;
96830
96918
  }
96831
96919
  create(params, options) {
96832
96920
  const self2 = this;
@@ -96961,28 +97049,93 @@ class OpenAIShimMessages {
96961
97049
  signal: options?.signal
96962
97050
  };
96963
97051
  const maxAttempts = isGithub ? GITHUB_429_MAX_RETRIES : 1;
97052
+ const endpoint = classifyModelHttpEndpoint(chatCompletionsUrl);
96964
97053
  let response;
96965
97054
  for (let attempt = 0;attempt < maxAttempts; attempt++) {
97055
+ const attemptNumber = attempt + 1;
97056
+ const requestId = `req_${crypto.randomUUID().replace(/-/g, "")}`;
97057
+ const requestStartedAt = Date.now();
97058
+ emitSdkHttp({
97059
+ type: "request_start",
97060
+ requestId,
97061
+ method: fetchInit.method,
97062
+ url: chatCompletionsUrl,
97063
+ source: this.source,
97064
+ endpoint,
97065
+ attempt: attemptNumber,
97066
+ maxAttempts,
97067
+ timestamp: requestStartedAt
97068
+ });
96966
97069
  try {
96967
97070
  response = await fetch(chatCompletionsUrl, fetchInit);
96968
97071
  } catch (fetchErr) {
96969
97072
  const msg = fetchErr instanceof Error ? `${fetchErr.message}${fetchErr.cause ? ` (${fetchErr.cause?.code ?? fetchErr.cause?.message ?? ""})` : ""}` : String(fetchErr);
97073
+ emitSdkHttp({
97074
+ type: "error",
97075
+ requestId,
97076
+ error: fetchErr instanceof Error ? fetchErr : new Error(String(fetchErr)),
97077
+ durationMs: Date.now() - requestStartedAt,
97078
+ source: this.source,
97079
+ endpoint,
97080
+ attempt: attemptNumber,
97081
+ maxAttempts,
97082
+ failureReason: msg,
97083
+ timestamp: Date.now()
97084
+ });
96970
97085
  throw new APIConnectionError({
96971
97086
  message: `OpenAI-compat connection failed: ${msg}`,
96972
97087
  cause: fetchErr
96973
97088
  });
96974
97089
  }
96975
97090
  if (response.ok) {
97091
+ emitSdkHttp({
97092
+ type: "response_start",
97093
+ requestId,
97094
+ status: response.status,
97095
+ durationMs: Date.now() - requestStartedAt,
97096
+ source: this.source,
97097
+ endpoint,
97098
+ attempt: attemptNumber,
97099
+ maxAttempts,
97100
+ timestamp: Date.now()
97101
+ });
96976
97102
  return response;
96977
97103
  }
96978
97104
  if (isGithub && response.status === 429 && attempt < maxAttempts - 1) {
96979
97105
  await response.text().catch(() => {});
96980
97106
  const delaySec = Math.min(GITHUB_429_BASE_DELAY_SEC * 2 ** attempt, GITHUB_429_MAX_DELAY_SEC);
97107
+ emitSdkHttp({
97108
+ type: "response_start",
97109
+ requestId,
97110
+ status: response.status,
97111
+ durationMs: Date.now() - requestStartedAt,
97112
+ source: this.source,
97113
+ endpoint,
97114
+ attempt: attemptNumber,
97115
+ maxAttempts,
97116
+ willRetry: true,
97117
+ retryDelayMs: delaySec * 1000,
97118
+ failureReason: `HTTP ${response.status}${formatRetryAfterHint(response)}`,
97119
+ timestamp: Date.now()
97120
+ });
96981
97121
  await sleepMs(delaySec * 1000);
96982
97122
  continue;
96983
97123
  }
96984
97124
  const errorBody = await response.text().catch(() => "unknown error");
96985
97125
  const rateHint = isGithub && response.status === 429 ? formatRetryAfterHint(response) : "";
97126
+ const failureReason = `${responseBodyToFailureReason(response.status, errorBody)}${rateHint}`;
97127
+ emitSdkHttp({
97128
+ type: "response_start",
97129
+ requestId,
97130
+ status: response.status,
97131
+ durationMs: Date.now() - requestStartedAt,
97132
+ source: this.source,
97133
+ endpoint,
97134
+ attempt: attemptNumber,
97135
+ maxAttempts,
97136
+ failureReason,
97137
+ timestamp: Date.now()
97138
+ });
96986
97139
  let errorResponse;
96987
97140
  try {
96988
97141
  errorResponse = JSON.parse(errorBody);
@@ -97071,8 +97224,8 @@ function convertOpenAIResponseToAnthropic(data, model) {
97071
97224
  class OpenAIShimBeta {
97072
97225
  messages;
97073
97226
  reasoningEffort;
97074
- constructor(defaultHeaders, reasoningEffort, providerOverride) {
97075
- this.messages = new OpenAIShimMessages(defaultHeaders, reasoningEffort, providerOverride);
97227
+ constructor(defaultHeaders, reasoningEffort, providerOverride, source) {
97228
+ this.messages = new OpenAIShimMessages(defaultHeaders, reasoningEffort, providerOverride, source);
97076
97229
  this.reasoningEffort = reasoningEffort;
97077
97230
  }
97078
97231
  }
@@ -97094,7 +97247,7 @@ function createOpenAIShimClient(options) {
97094
97247
  }
97095
97248
  const beta = new OpenAIShimBeta({
97096
97249
  ...options.defaultHeaders ?? {}
97097
- }, options.reasoningEffort, options.providerOverride);
97250
+ }, options.reasoningEffort, options.providerOverride, options.source);
97098
97251
  return {
97099
97252
  beta,
97100
97253
  messages: beta.messages
@@ -97113,7 +97266,9 @@ var init_shim2 = __esm(() => {
97113
97266
  init_schemaSanitizer();
97114
97267
  init_providerProfile();
97115
97268
  init_capabilities2();
97269
+ init_httpObservability();
97116
97270
  init_canonical();
97271
+ init_requestSideChannels();
97117
97272
  OpenAIShimStream = class OpenAIShimStream {
97118
97273
  generator;
97119
97274
  controller = new AbortController;
@@ -97210,7 +97365,8 @@ async function getNormalizedClient({
97210
97365
  defaultHeaders: safeHeaders,
97211
97366
  maxRetries,
97212
97367
  timeout: parseInt(process.env.API_TIMEOUT_MS || String(600000), 10),
97213
- providerOverride
97368
+ providerOverride,
97369
+ source
97214
97370
  });
97215
97371
  }
97216
97372
  const transportOverride = getQueryEnvVar(QUERY_ENV_KEY_TRANSPORT_OVERRIDE);
@@ -97220,7 +97376,8 @@ async function getNormalizedClient({
97220
97376
  return createOpenAIShimClient2({
97221
97377
  defaultHeaders,
97222
97378
  maxRetries,
97223
- timeout: parseInt(process.env.API_TIMEOUT_MS || String(600000), 10)
97379
+ timeout: parseInt(process.env.API_TIMEOUT_MS || String(600000), 10),
97380
+ source
97224
97381
  });
97225
97382
  }
97226
97383
  const stagingOauthBaseUrl = process.env.USER_TYPE === "ant" && isEnvTruthy(process.env.USE_STAGING_OAUTH) ? getOauthConfig().BASE_API_URL : undefined;
@@ -97272,42 +97429,49 @@ function buildFetch(fetchOverride, source) {
97272
97429
  const url3 = input instanceof Request ? input.url : String(input);
97273
97430
  const method = init?.method ?? (input instanceof Request ? input.method : "GET");
97274
97431
  const requestId = headers.get(CLIENT_REQUEST_ID_HEADER) ?? randomUUID();
97432
+ const endpoint = classifyModelHttpEndpoint(url3);
97275
97433
  try {
97276
97434
  logForDebugging(`[API REQUEST] ${new URL(url3).pathname} ${CLIENT_REQUEST_ID_HEADER}=${requestId} source=${source ?? "unknown"}`);
97277
97435
  } catch {}
97278
97436
  const requestStartedAt = Date.now();
97437
+ emitSdkHttp({
97438
+ type: "request_start",
97439
+ requestId,
97440
+ method,
97441
+ url: url3,
97442
+ source,
97443
+ endpoint,
97444
+ attempt: 1,
97445
+ maxAttempts: 1,
97446
+ timestamp: requestStartedAt
97447
+ });
97279
97448
  try {
97280
- emitHttp({
97281
- type: "request_start",
97449
+ const response = await inner(input, { ...init, headers });
97450
+ emitSdkHttp({
97451
+ type: "response_start",
97282
97452
  requestId,
97283
- method,
97284
- url: url3,
97453
+ status: response.status,
97454
+ durationMs: Date.now() - requestStartedAt,
97285
97455
  source,
97286
- timestamp: requestStartedAt
97456
+ endpoint,
97457
+ attempt: 1,
97458
+ maxAttempts: 1,
97459
+ timestamp: Date.now()
97287
97460
  });
97288
- } catch {}
97289
- try {
97290
- const response = await inner(input, { ...init, headers });
97291
- try {
97292
- emitHttp({
97293
- type: "response_start",
97294
- requestId,
97295
- status: response.status,
97296
- durationMs: Date.now() - requestStartedAt,
97297
- timestamp: Date.now()
97298
- });
97299
- } catch {}
97300
97461
  return response;
97301
97462
  } catch (err) {
97302
- try {
97303
- emitHttp({
97304
- type: "error",
97305
- requestId,
97306
- error: err instanceof Error ? err : new Error(String(err)),
97307
- durationMs: Date.now() - requestStartedAt,
97308
- timestamp: Date.now()
97309
- });
97310
- } catch {}
97463
+ emitSdkHttp({
97464
+ type: "error",
97465
+ requestId,
97466
+ error: err instanceof Error ? err : new Error(String(err)),
97467
+ durationMs: Date.now() - requestStartedAt,
97468
+ source,
97469
+ endpoint,
97470
+ attempt: 1,
97471
+ maxAttempts: 1,
97472
+ failureReason: errorToFailureReason(err),
97473
+ timestamp: Date.now()
97474
+ });
97311
97475
  throw err;
97312
97476
  }
97313
97477
  };
@@ -97326,7 +97490,7 @@ var init_clientFactory = __esm(() => {
97326
97490
  init_oauth();
97327
97491
  init_debug();
97328
97492
  init_envUtils();
97329
- init_observer();
97493
+ init_httpObservability();
97330
97494
  });
97331
97495
 
97332
97496
  // src/providers/anthropic/index.ts
@@ -133214,6 +133378,17 @@ async function* withRetry(getClient, operation, options2) {
133214
133378
  status: error41.status,
133215
133379
  provider: getAPIProviderForStatsig()
133216
133380
  });
133381
+ emitSdkHttp({
133382
+ type: "retry",
133383
+ source: options2.querySource,
133384
+ endpoint: "messages",
133385
+ attempt: reportedAttempt,
133386
+ ...persistent ? {} : { maxAttempts: maxRetries + 1 },
133387
+ retryDelayMs: delayMs,
133388
+ status: error41 instanceof APIError ? error41.status : undefined,
133389
+ failureReason: errorToFailureReason(error41),
133390
+ timestamp: Date.now()
133391
+ });
133217
133392
  if (persistent) {
133218
133393
  if (delayMs > 60000) {
133219
133394
  logEvent("tengu_api_persistent_retry_wait", {
@@ -133467,6 +133642,7 @@ var init_retry = __esm(() => {
133467
133642
  init_mocking();
133468
133643
  init_errors7();
133469
133644
  init_errorUtils();
133645
+ init_httpObservability();
133470
133646
  FOREGROUND_529_RETRY_SOURCES = new Set([
133471
133647
  "repl_main_thread",
133472
133648
  "repl_main_thread:outputStyle:custom",
@@ -235103,6 +235279,11 @@ var FINGERPRINT_SALT = "59cf53e54c78";
235103
235279
  var init_fingerprint = () => {};
235104
235280
 
235105
235281
  // src/session/sideQuery.ts
235282
+ function headersForBetas(betas) {
235283
+ if (!betas || betas.length === 0)
235284
+ return;
235285
+ return { "anthropic-beta": betas.join(",") };
235286
+ }
235106
235287
  function extractFirstUserMessageText(messages) {
235107
235288
  const firstUserMessage = messages.find((m) => m.role === "user");
235108
235289
  if (!firstUserMessage)
@@ -235165,7 +235346,7 @@ async function sideQuery(opts) {
235165
235346
  }
235166
235347
  const normalizedModel = normalizeModelStringForAPI(model);
235167
235348
  const start = Date.now();
235168
- const response = await client.beta.messages.create({
235349
+ const response = await client.messages.create({
235169
235350
  model: normalizedModel,
235170
235351
  max_tokens,
235171
235352
  system: systemBlocks,
@@ -235176,9 +235357,12 @@ async function sideQuery(opts) {
235176
235357
  ...temperature !== undefined && { temperature },
235177
235358
  ...stop_sequences && { stop_sequences },
235178
235359
  ...thinkingConfig && { thinking: thinkingConfig },
235179
- ...betas.length > 0 && { betas },
235180
- metadata: getAPIMetadata()
235181
- }, { signal });
235360
+ metadata: getAPIMetadata(),
235361
+ ...getExtraBodyParams()
235362
+ }, {
235363
+ signal,
235364
+ ...betas.length > 0 ? { headers: headersForBetas(betas) } : {}
235365
+ });
235182
235366
  const requestId = response._request_id ?? undefined;
235183
235367
  const now = Date.now();
235184
235368
  const lastCompletion = getLastApiCompletionTimestamp();
@@ -250648,49 +250832,36 @@ assistant: Still waiting on the audit — that's one of the things it's checking
250648
250832
 
250649
250833
  <example>
250650
250834
  user: "Can you get a second opinion on whether this migration is safe?"
250651
- assistant: <thinking>I'll ask the code-reviewer agent it won't see my analysis, so it can give an independent read.</thinking>
250835
+ assistant: <thinking>Forking this review keeps the detailed audit out of my context while I keep steering the work.</thinking>
250652
250836
  <commentary>
250653
- A subagent_type is specified, so the agent starts fresh. It needs full context in the prompt. The briefing explains what to assess and why.
250837
+ No subagent_type is specified, so this uses the fork path. If you do specify subagent_type, copy an exact type from the available agent list; never invent one from an example.
250654
250838
  </commentary>
250655
250839
  ${AGENT_TOOL_NAME2}({
250656
250840
  name: "migration-review",
250657
250841
  description: "Independent migration review",
250658
- subagent_type: "code-reviewer",
250659
250842
  prompt: "Review migration 0042_user_schema.sql for safety. Context: we're adding a NOT NULL column to a 50M-row table. Existing rows get a backfill default. I want a second opinion on whether the backfill approach is safe under concurrent writes — I've checked locking behavior but want independent verification. Report: is this safe, and if not, what specifically breaks?"
250660
250843
  })
250661
250844
  </example>
250662
250845
  `;
250663
250846
  const currentExamples = `Example usage:
250664
250847
 
250665
- <example_agent_descriptions>
250666
- "test-runner": use this agent after you are done writing code to run tests
250667
- "greeting-responder": use this agent to respond to user greetings with a friendly joke
250668
- </example_agent_descriptions>
250669
-
250670
250848
  <example>
250671
- user: "Please write a function that checks if a number is prime"
250672
- assistant: I'm going to use the ${FILE_WRITE_TOOL_NAME2} tool to write the following code:
250673
- <code>
250674
- function isPrime(n) {
250675
- if (n <= 1) return false
250676
- for (let i = 2; i * i <= n; i++) {
250677
- if (n % i === 0) return false
250678
- }
250679
- return true
250680
- }
250681
- </code>
250849
+ user: "Hello"
250682
250850
  <commentary>
250683
- Since a significant piece of code was written and the task was completed, now use the test-runner agent to run the tests
250851
+ Do not use the ${AGENT_TOOL_NAME2} tool for a simple greeting or a short reply. Answer directly.
250684
250852
  </commentary>
250685
- assistant: Uses the ${AGENT_TOOL_NAME2} tool to launch the test-runner agent
250853
+ assistant: "Hi! Nice to see you. What shall we work on today?"
250686
250854
  </example>
250687
250855
 
250688
250856
  <example>
250689
- user: "Hello"
250857
+ user: "Can you investigate whether this migration is safe?"
250690
250858
  <commentary>
250691
- Since the user is greeting, use the greeting-responder agent to respond with a friendly joke
250859
+ This is a multi-step review, so the ${AGENT_TOOL_NAME2} tool can help. Omit subagent_type to use the default general-purpose agent, or specify a subagent_type only by copying an exact type from the available agent list.
250692
250860
  </commentary>
250693
- assistant: "I'm going to use the ${AGENT_TOOL_NAME2} tool to launch the greeting-responder agent"
250861
+ ${AGENT_TOOL_NAME2}({
250862
+ description: "Review migration safety",
250863
+ prompt: "Review migration 0042_user_schema.sql for safety. Context: we're adding a NOT NULL column to a 50M-row table. Existing rows get a backfill default. Check whether the backfill approach is safe under concurrent writes. Report: is this safe, and if not, what specifically breaks?"
250864
+ })
250694
250865
  </example>
250695
250866
  `;
250696
250867
  const listViaAttachment = shouldInjectAgentListInMessages();
@@ -250703,7 +250874,7 @@ The ${AGENT_TOOL_NAME2} tool launches specialized agents (subprocesses) that aut
250703
250874
 
250704
250875
  ${agentListSection}
250705
250876
 
250706
- ${forkEnabled ? `When using the ${AGENT_TOOL_NAME2} tool, specify a subagent_type to use a specialized agent, or omit it to fork yourself — a fork inherits your full conversation context.` : `When using the ${AGENT_TOOL_NAME2} tool, specify a subagent_type parameter to select which agent type to use. If omitted, the general-purpose agent is used.`}`;
250877
+ ${forkEnabled ? `When using the ${AGENT_TOOL_NAME2} tool, specify a subagent_type to use a specialized agent, or omit it to fork yourself — a fork inherits your full conversation context. Only use subagent_type values that appear in the available agent types; never invent an agent type from an example.` : `When using the ${AGENT_TOOL_NAME2} tool, specify a subagent_type parameter to select which agent type to use. If omitted, the general-purpose agent is used. Only use subagent_type values that appear in the available agent types; never invent an agent type from an example.`}`;
250707
250878
  if (isCoordinator) {
250708
250879
  return shared2;
250709
250880
  }
@@ -250731,7 +250902,7 @@ Usage notes:
250731
250902
  - The agent's outputs should generally be trusted
250732
250903
  - Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.)${forkEnabled ? "" : ", since it is not aware of the user's intent"}
250733
250904
  - If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
250734
- - If the user specifies that they want you to run agents "in parallel", you MUST send a single message with multiple ${AGENT_TOOL_NAME2} tool use content blocks. For example, if you need to launch both a build-validator agent and a test-runner agent in parallel, send a single message with both tool calls.
250905
+ - If the user specifies that they want you to run agents "in parallel", you MUST send a single message with multiple ${AGENT_TOOL_NAME2} tool use content blocks. Each call must either omit subagent_type or use an exact type from the available agent list.
250735
250906
  - You can optionally set \`isolation: "worktree"\` to run the agent in a temporary git worktree, giving it an isolated copy of the repository. The worktree is automatically cleaned up if the agent makes no changes; if changes are made, the worktree path and branch are returned in the result.${process.env.USER_TYPE === "ant" ? `
250736
250907
  - You can set \`isolation: "remote"\` to run the agent in a remote CCR environment. This is always a background task; you'll be notified when it completes. Use for long-running tasks that need a fresh sandbox.` : ""}${isInProcessTeammate() ? `
250737
250908
  - The run_in_background, name, team_name, and mode parameters are not available in this context. Only synchronous subagents are supported.` : isTeammate() ? `
@@ -250746,7 +250917,6 @@ var init_prompt7 = __esm(() => {
250746
250917
  init_teammate();
250747
250918
  init_teammateContext();
250748
250919
  init_prompt2();
250749
- init_prompt3();
250750
250920
  init_constants4();
250751
250921
  init_forkSubagent();
250752
250922
  init_state2();
@@ -269671,7 +269841,7 @@ async function loadPluginSettings(pluginPath, manifest) {
269671
269841
  try {
269672
269842
  const content = await getFsImplementation().readFile(settingsJsonPath, { encoding: "utf-8" });
269673
269843
  const parsed = jsonParse(content);
269674
- if (isRecord(parsed)) {
269844
+ if (isRecord2(parsed)) {
269675
269845
  const filtered = parsePluginSettings(parsed);
269676
269846
  if (filtered) {
269677
269847
  logForDebugging(`Loaded settings from settings.json for plugin ${manifest.name}`);
@@ -270350,7 +270520,7 @@ function cachePluginSettings(plugins) {
270350
270520
  logForDebugging(`Cached plugin settings with keys: ${Object.keys(settings).join(", ")}`);
270351
270521
  }
270352
270522
  }
270353
- function isRecord(value) {
270523
+ function isRecord2(value) {
270354
270524
  return typeof value === "object" && value !== null && !Array.isArray(value);
270355
270525
  }
270356
270526
  var PluginSettingsSchema, loadAllPlugins, loadAllPluginsCacheOnly;
@@ -282702,7 +282872,7 @@ function getAnthropicEnvMetadata() {
282702
282872
  function getBuildAgeMinutes() {
282703
282873
  if (false)
282704
282874
  ;
282705
- const buildTime = new Date("2026-06-25T12:29:02.938Z").getTime();
282875
+ const buildTime = new Date("2026-07-07T07:10:57.155Z").getTime();
282706
282876
  if (isNaN(buildTime))
282707
282877
  return;
282708
282878
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -285553,6 +285723,11 @@ var init_toolSearch = __esm(() => {
285553
285723
  });
285554
285724
 
285555
285725
  // src/providers/shared/tokenCount.ts
285726
+ function headersForBetas2(betas) {
285727
+ if (!betas || betas.length === 0)
285728
+ return;
285729
+ return { "anthropic-beta": betas.join(",") };
285730
+ }
285556
285731
  function hasThinkingBlocks(messages) {
285557
285732
  for (const message of messages) {
285558
285733
  if (message.role === "assistant" && Array.isArray(message.content)) {
@@ -285627,17 +285802,18 @@ async function countMessagesTokensWithAPI(messages, tools) {
285627
285802
  model,
285628
285803
  source: "count_tokens"
285629
285804
  });
285630
- const response = await anthropic.beta.messages.countTokens({
285805
+ const response = await anthropic.messages.countTokens({
285631
285806
  model: normalizeModelStringForAPI(model),
285632
285807
  messages: messages.length > 0 ? messages : [{ role: "user", content: "foo" }],
285633
285808
  tools,
285634
- ...betas.length > 0 && { betas },
285635
285809
  ...containsThinking && {
285636
285810
  thinking: {
285637
285811
  type: "enabled",
285638
285812
  budget_tokens: TOKEN_COUNT_THINKING_BUDGET
285639
285813
  }
285640
285814
  }
285815
+ }, {
285816
+ ...betas.length > 0 ? { headers: headersForBetas2(betas) } : {}
285641
285817
  });
285642
285818
  if (typeof response.input_tokens !== "number") {
285643
285819
  return null;
@@ -285676,12 +285852,11 @@ async function countTokensViaHaikuFallback(messages, tools) {
285676
285852
  const normalizedMessages = stripToolSearchFieldsFromMessages(messages);
285677
285853
  const messagesToSend = normalizedMessages.length > 0 ? normalizedMessages : [{ role: "user", content: "count" }];
285678
285854
  const betas = getModelBetas(model);
285679
- const response = await anthropic.beta.messages.create({
285855
+ const response = await anthropic.messages.create({
285680
285856
  model: normalizeModelStringForAPI(model),
285681
285857
  max_tokens: containsThinking ? TOKEN_COUNT_MAX_TOKENS : 1,
285682
285858
  messages: messagesToSend,
285683
285859
  tools: tools.length > 0 ? tools : undefined,
285684
- ...betas.length > 0 && { betas },
285685
285860
  metadata: getAPIMetadata(),
285686
285861
  ...getExtraBodyParams(),
285687
285862
  ...containsThinking && {
@@ -285690,6 +285865,8 @@ async function countTokensViaHaikuFallback(messages, tools) {
285690
285865
  budget_tokens: TOKEN_COUNT_THINKING_BUDGET
285691
285866
  }
285692
285867
  }
285868
+ }, {
285869
+ ...betas.length > 0 ? { headers: headersForBetas2(betas) } : {}
285693
285870
  });
285694
285871
  const usage = response.usage;
285695
285872
  const inputTokens = usage.input_tokens;
@@ -293420,6 +293597,11 @@ var init_apiMicrocompact = __esm(() => {
293420
293597
 
293421
293598
  // src/controller/loop.ts
293422
293599
  import { randomUUID as randomUUID17 } from "crypto";
293600
+ function headersForBetas3(betas) {
293601
+ if (!betas || betas.length === 0)
293602
+ return;
293603
+ return { "anthropic-beta": betas.join(",") };
293604
+ }
293423
293605
  function adjustParamsForNonStreaming(params, maxTokensCap) {
293424
293606
  const cappedMaxTokens = Math.min(params.max_tokens, maxTokensCap);
293425
293607
  const adjustedParams = { ...params };
@@ -293497,13 +293679,16 @@ async function* executeNonStreamingRequest(clientOptions, retryOptions, paramsFr
293497
293679
  captureRequest(retryParams);
293498
293680
  onAttempt(attempt, start, retryParams.max_tokens);
293499
293681
  const adjustedParams = adjustParamsForNonStreaming(retryParams, MAX_NON_STREAMING_TOKENS);
293682
+ const { betas: requestBetas, ...standardParams } = adjustedParams;
293683
+ const headers = headersForBetas3(requestBetas);
293500
293684
  try {
293501
- return await anthropic.beta.messages.create({
293502
- ...adjustedParams,
293503
- model: normalizeModelStringForAPI(adjustedParams.model)
293685
+ return await anthropic.messages.create({
293686
+ ...standardParams,
293687
+ model: normalizeModelStringForAPI(standardParams.model)
293504
293688
  }, {
293505
293689
  signal: retryOptions.signal,
293506
- timeout: fallbackTimeoutMs
293690
+ timeout: fallbackTimeoutMs,
293691
+ ...headers ? { headers } : {}
293507
293692
  });
293508
293693
  } catch (err2) {
293509
293694
  if (err2 instanceof APIUserAbortError)
@@ -293985,11 +294170,19 @@ ${deferredToolList}
293985
294170
  headlessProfilerCheckpoint("api_request_sent");
293986
294171
  }
293987
294172
  clientRequestId = shouldSendClientRequestId() ? randomUUID17() : undefined;
293988
- const result = await anthropic.beta.messages.create({ ...params, stream: true }, {
293989
- signal,
294173
+ const { betas: requestBetas, ...standardParams } = params;
294174
+ const headers = {
294175
+ ...headersForBetas3(requestBetas) ?? {},
293990
294176
  ...clientRequestId && {
293991
- headers: { [CLIENT_REQUEST_ID_HEADER]: clientRequestId }
294177
+ [CLIENT_REQUEST_ID_HEADER]: clientRequestId
293992
294178
  }
294179
+ };
294180
+ const result = await anthropic.messages.create({
294181
+ ...standardParams,
294182
+ stream: true
294183
+ }, {
294184
+ signal,
294185
+ ...Object.keys(headers).length > 0 ? { headers } : {}
293993
294186
  }).withResponse();
293994
294187
  queryCheckpoint("query_response_headers_received");
293995
294188
  streamRequestId = result.request_id;
@@ -297044,6 +297237,11 @@ function stripToolReferenceBlocksFromUserMessage(message) {
297044
297237
  }
297045
297238
  };
297046
297239
  }
297240
+ function omitToolSearchCaller(block2) {
297241
+ const blockWithoutCaller = { ...block2 };
297242
+ delete blockWithoutCaller.caller;
297243
+ return blockWithoutCaller;
297244
+ }
297047
297245
  function stripCallerFieldFromAssistantMessage(message) {
297048
297246
  const hasCallerField = message.message.content.some((block2) => block2.type === "tool_use" && ("caller" in block2) && block2.caller !== null);
297049
297247
  if (!hasCallerField) {
@@ -297057,12 +297255,7 @@ function stripCallerFieldFromAssistantMessage(message) {
297057
297255
  if (block2.type !== "tool_use") {
297058
297256
  return block2;
297059
297257
  }
297060
- return {
297061
- type: "tool_use",
297062
- id: block2.id,
297063
- name: block2.name,
297064
- input: block2.input
297065
- };
297258
+ return omitToolSearchCaller(block2);
297066
297259
  })
297067
297260
  }
297068
297261
  };
@@ -297339,9 +297532,9 @@ function normalizeMessagesForAPI(messages, tools = []) {
297339
297532
  input: normalizedInput
297340
297533
  };
297341
297534
  }
297535
+ const blockWithoutCaller = omitToolSearchCaller(block2);
297342
297536
  return {
297343
- type: "tool_use",
297344
- id: block2.id,
297537
+ ...blockWithoutCaller,
297345
297538
  name: canonicalName,
297346
297539
  input: normalizedInput
297347
297540
  };
@@ -297538,6 +297731,15 @@ function mergeUserContentBlocks(a2, b) {
297538
297731
  }
297539
297732
  return [...a2.slice(0, -1), smooshed, ...toolResults];
297540
297733
  }
297734
+ function normalizeToolUseIdFromAPI(contentBlock) {
297735
+ if (typeof contentBlock.id === "string" && contentBlock.id.trim().length > 0) {
297736
+ return contentBlock.id;
297737
+ }
297738
+ logEvent("tengu_tool_use_missing_id_repaired", {
297739
+ toolName: sanitizeToolNameForAnalytics(contentBlock.name)
297740
+ });
297741
+ return `toolu_${randomUUID18().replace(/-/g, "")}`;
297742
+ }
297541
297743
  function normalizeContentFromAPI(contentBlocks, tools, agentId) {
297542
297744
  if (!contentBlocks) {
297543
297745
  return [];
@@ -297576,6 +297778,7 @@ function normalizeContentFromAPI(contentBlocks, tools, agentId) {
297576
297778
  }
297577
297779
  return {
297578
297780
  ...contentBlock,
297781
+ id: normalizeToolUseIdFromAPI(contentBlock),
297579
297782
  input: normalizedInput
297580
297783
  };
297581
297784
  }
@@ -329561,7 +329764,7 @@ function extractToolUseId2(message) {
329561
329764
  return;
329562
329765
  for (let i3 = content.length - 1;i3 >= 0; i3--) {
329563
329766
  const block2 = content[i3];
329564
- if (block2?.type === "tool_use" && typeof block2.id === "string") {
329767
+ if (block2?.type === "tool_use" && typeof block2.id === "string" && block2.id.trim().length > 0) {
329565
329768
  return block2.id;
329566
329769
  }
329567
329770
  }
@@ -329651,6 +329854,7 @@ function mergeAndFilterTools(initialTools, assembled, mode) {
329651
329854
 
329652
329855
  // src/session/sdkRuntime.ts
329653
329856
  init_config2();
329857
+ init_envVars();
329654
329858
  var getAsk = () => (init_QueryEngine(), __toCommonJS(exports_QueryEngine)).ask;
329655
329859
  var getModelInvocableCommands2 = async (cwd) => {
329656
329860
  const mod = (init_runtime(), __toCommonJS(exports_runtime));
@@ -329809,11 +330013,19 @@ function normalizeInitialMessages(raw) {
329809
330013
  }
329810
330014
  return normalized;
329811
330015
  }
330016
+ function extraBodyFromOptions(options2) {
330017
+ const extraBody = options2.extra_body;
330018
+ if (!extraBody || typeof extraBody !== "object" || Array.isArray(extraBody)) {
330019
+ return;
330020
+ }
330021
+ return extraBody;
330022
+ }
329812
330023
  function optionsWithProviderRoutingEnv(options2) {
329813
330024
  const transport = options2.transport;
329814
330025
  const reasoningEffort = options2.reasoningEffort;
329815
330026
  const openaiResponses = options2.providerSpecific?.openaiResponses;
329816
- if (transport === undefined && reasoningEffort === undefined && openaiResponses === undefined) {
330027
+ const extraBody = extraBodyFromOptions(options2);
330028
+ if (transport === undefined && reasoningEffort === undefined && openaiResponses === undefined && extraBody === undefined) {
329817
330029
  return options2;
329818
330030
  }
329819
330031
  const mergedEnv = { ...options2.env ?? {} };
@@ -329828,6 +330040,11 @@ function optionsWithProviderRoutingEnv(options2) {
329828
330040
  if (openaiResponses && typeof openaiResponses === "object") {
329829
330041
  mergedEnv[QUERY_ENV_KEY_PROVIDER_SPECIFIC_OPENAI_RESPONSES] = JSON.stringify(openaiResponses);
329830
330042
  }
330043
+ if (extraBody) {
330044
+ const rawExtraBody = JSON.stringify(extraBody);
330045
+ mergedEnv[ENV_VARS.EXTRA_BODY.primary] = rawExtraBody;
330046
+ mergedEnv[QUERY_ENV_KEY_EXTRA_BODY] = rawExtraBody;
330047
+ }
329831
330048
  return { ...options2, env: mergedEnv };
329832
330049
  }
329833
330050
  function createQueryLike(generator, runtimeState, onClose) {
@@ -335712,6 +335929,11 @@ init_generators();
335712
335929
  init_clientFactory();
335713
335930
  init_retry();
335714
335931
  init_requestShaping();
335932
+ function headersForBetas4(betas) {
335933
+ if (!betas || betas.length === 0)
335934
+ return;
335935
+ return { "anthropic-beta": betas.join(",") };
335936
+ }
335715
335937
  async function verifyApiKey(apiKey, isNonInteractiveSession) {
335716
335938
  if (isNonInteractiveSession) {
335717
335939
  return true;
@@ -335726,14 +335948,15 @@ async function verifyApiKey(apiKey, isNonInteractiveSession) {
335726
335948
  source: "verify_api_key"
335727
335949
  }), async (anthropic) => {
335728
335950
  const messages = [{ role: "user", content: "test" }];
335729
- await anthropic.beta.messages.create({
335951
+ await anthropic.messages.create({
335730
335952
  model,
335731
335953
  max_tokens: 1,
335732
335954
  messages,
335733
335955
  temperature: 1,
335734
- ...betas.length > 0 && { betas },
335735
335956
  metadata: getAPIMetadata(),
335736
335957
  ...getExtraBodyParams()
335958
+ }, {
335959
+ ...betas.length > 0 ? { headers: headersForBetas4(betas) } : {}
335737
335960
  });
335738
335961
  return true;
335739
335962
  }, { maxRetries: 2, model, thinkingConfig: { type: "disabled" } }));
@@ -336132,4 +336355,4 @@ export {
336132
336355
  AbortError2 as AbortError
336133
336356
  };
336134
336357
 
336135
- //# debugId=F19FA9D2F1AEFB9664756E2164756E21
336358
+ //# debugId=1FEDB4CAC28D153364756E2164756E21