@anuma/sdk 2.6.0 → 2.7.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.
@@ -9569,6 +9569,22 @@ function linkAbortSignals(a, b) {
9569
9569
  }
9570
9570
  };
9571
9571
  }
9572
+ function validateEndpointOverride(override) {
9573
+ const trimmed = override.trim();
9574
+ if (trimmed === "") {
9575
+ return {
9576
+ valid: false,
9577
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
9578
+ };
9579
+ }
9580
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
9581
+ return {
9582
+ valid: false,
9583
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
9584
+ };
9585
+ }
9586
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
9587
+ }
9572
9588
  async function runToolLoop(options) {
9573
9589
  const {
9574
9590
  model,
@@ -9576,6 +9592,7 @@ async function runToolLoop(options) {
9576
9592
  baseUrl = BASE_URL,
9577
9593
  headers,
9578
9594
  apiType = "auto",
9595
+ endpointOverride,
9579
9596
  temperature,
9580
9597
  maxOutputTokens,
9581
9598
  tools,
@@ -9658,6 +9675,20 @@ async function runToolLoop(options) {
9658
9675
  await fireRunError({ error: msg, stage: "model", errorObject: new Error(msg) });
9659
9676
  return { data: null, error: msg };
9660
9677
  }
9678
+ let effectiveEndpoint = strategy.endpoint;
9679
+ if (endpointOverride !== void 0) {
9680
+ const overrideValidation = validateEndpointOverride(endpointOverride);
9681
+ if (!overrideValidation.valid) {
9682
+ if (onError) onError(new Error(overrideValidation.message));
9683
+ await fireRunError({
9684
+ error: overrideValidation.message,
9685
+ stage: "model",
9686
+ errorObject: new Error(overrideValidation.message)
9687
+ });
9688
+ return { data: null, error: overrideValidation.message };
9689
+ }
9690
+ effectiveEndpoint = overrideValidation.endpoint;
9691
+ }
9661
9692
  const { signal: combinedSignal, cleanup: cleanupSignalLink } = linkAbortSignals(
9662
9693
  signal,
9663
9694
  detachSignal
@@ -9841,7 +9872,7 @@ async function runToolLoop(options) {
9841
9872
  if (onRequest) onRequest(measureRequest(0, attempt, requestBody, messages, apiTools));
9842
9873
  const sseResult = makeStreamingRequest({
9843
9874
  baseUrl,
9844
- endpoint: strategy.endpoint,
9875
+ endpoint: effectiveEndpoint,
9845
9876
  body: requestBody,
9846
9877
  token,
9847
9878
  headers: effectiveHeaders,
@@ -10313,7 +10344,7 @@ Executing tool: ${toolInfo}
10313
10344
  const continuationRound = toolIteration;
10314
10345
  const continuationResult = makeStreamingRequest({
10315
10346
  baseUrl,
10316
- endpoint: strategy.endpoint,
10347
+ endpoint: effectiveEndpoint,
10317
10348
  body: continuationRequestBody,
10318
10349
  token,
10319
10350
  headers: effectiveHeaders,
@@ -11218,6 +11249,7 @@ function useChat(options) {
11218
11249
  imageModel,
11219
11250
  apiType: requestApiType,
11220
11251
  conversationId,
11252
+ endpointOverride,
11221
11253
  piiRedaction: requestPiiRedaction,
11222
11254
  headers
11223
11255
  }) => {
@@ -11283,6 +11315,7 @@ function useChat(options) {
11283
11315
  token,
11284
11316
  baseUrl,
11285
11317
  apiType: requestApiType ?? defaultApiType,
11318
+ endpointOverride,
11286
11319
  temperature,
11287
11320
  maxOutputTokens,
11288
11321
  tools,
@@ -2240,6 +2240,12 @@ type BaseSendMessageArgs = ResponsesApiOptions & {
2240
2240
  onData?: (chunk: string) => void;
2241
2241
  /** Groups requests belonging to the same conversation for observability. Pass-through only — not forwarded to the LLM provider. */
2242
2242
  conversationId?: string;
2243
+ /**
2244
+ * Per-request override for the API path. When set, this request is sent to the
2245
+ * given path instead of the model-resolved endpoint. The request/response
2246
+ * contract is unchanged — only the path differs. Omit to use the default path.
2247
+ */
2248
+ endpointOverride?: string;
2243
2249
  /**
2244
2250
  * Per-request override for PII redaction. When set, takes precedence over the
2245
2251
  * hook-level `piiRedaction` for this call only — e.g. pass `false` to disable
@@ -2240,6 +2240,12 @@ type BaseSendMessageArgs = ResponsesApiOptions & {
2240
2240
  onData?: (chunk: string) => void;
2241
2241
  /** Groups requests belonging to the same conversation for observability. Pass-through only — not forwarded to the LLM provider. */
2242
2242
  conversationId?: string;
2243
+ /**
2244
+ * Per-request override for the API path. When set, this request is sent to the
2245
+ * given path instead of the model-resolved endpoint. The request/response
2246
+ * contract is unchanged — only the path differs. Omit to use the default path.
2247
+ */
2248
+ endpointOverride?: string;
2243
2249
  /**
2244
2250
  * Per-request override for PII redaction. When set, takes precedence over the
2245
2251
  * hook-level `piiRedaction` for this call only — e.g. pass `false` to disable
@@ -2454,6 +2454,22 @@ function linkAbortSignals(a, b) {
2454
2454
  }
2455
2455
  };
2456
2456
  }
2457
+ function validateEndpointOverride(override) {
2458
+ const trimmed = override.trim();
2459
+ if (trimmed === "") {
2460
+ return {
2461
+ valid: false,
2462
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
2463
+ };
2464
+ }
2465
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
2466
+ return {
2467
+ valid: false,
2468
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
2469
+ };
2470
+ }
2471
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
2472
+ }
2457
2473
  async function runToolLoop(options) {
2458
2474
  const {
2459
2475
  model,
@@ -2461,6 +2477,7 @@ async function runToolLoop(options) {
2461
2477
  baseUrl = BASE_URL,
2462
2478
  headers,
2463
2479
  apiType = "auto",
2480
+ endpointOverride,
2464
2481
  temperature,
2465
2482
  maxOutputTokens,
2466
2483
  tools,
@@ -2543,6 +2560,20 @@ async function runToolLoop(options) {
2543
2560
  await fireRunError({ error: msg, stage: "model", errorObject: new Error(msg) });
2544
2561
  return { data: null, error: msg };
2545
2562
  }
2563
+ let effectiveEndpoint = strategy.endpoint;
2564
+ if (endpointOverride !== void 0) {
2565
+ const overrideValidation = validateEndpointOverride(endpointOverride);
2566
+ if (!overrideValidation.valid) {
2567
+ if (onError) onError(new Error(overrideValidation.message));
2568
+ await fireRunError({
2569
+ error: overrideValidation.message,
2570
+ stage: "model",
2571
+ errorObject: new Error(overrideValidation.message)
2572
+ });
2573
+ return { data: null, error: overrideValidation.message };
2574
+ }
2575
+ effectiveEndpoint = overrideValidation.endpoint;
2576
+ }
2546
2577
  const { signal: combinedSignal, cleanup: cleanupSignalLink } = linkAbortSignals(
2547
2578
  signal,
2548
2579
  detachSignal
@@ -2726,7 +2757,7 @@ async function runToolLoop(options) {
2726
2757
  if (onRequest) onRequest(measureRequest(0, attempt, requestBody, messages, apiTools));
2727
2758
  const sseResult = makeStreamingRequest({
2728
2759
  baseUrl,
2729
- endpoint: strategy.endpoint,
2760
+ endpoint: effectiveEndpoint,
2730
2761
  body: requestBody,
2731
2762
  token,
2732
2763
  headers: effectiveHeaders,
@@ -3198,7 +3229,7 @@ Executing tool: ${toolInfo}
3198
3229
  const continuationRound = toolIteration;
3199
3230
  const continuationResult = makeStreamingRequest({
3200
3231
  baseUrl,
3201
- endpoint: strategy.endpoint,
3232
+ endpoint: effectiveEndpoint,
3202
3233
  body: continuationRequestBody,
3203
3234
  token,
3204
3235
  headers: effectiveHeaders,
@@ -4095,6 +4126,7 @@ function useChat(options) {
4095
4126
  imageModel,
4096
4127
  apiType: requestApiType,
4097
4128
  conversationId,
4129
+ endpointOverride,
4098
4130
  piiRedaction: requestPiiRedaction,
4099
4131
  headers
4100
4132
  }) => {
@@ -4160,6 +4192,7 @@ function useChat(options) {
4160
4192
  token,
4161
4193
  baseUrl,
4162
4194
  apiType: requestApiType ?? defaultApiType,
4195
+ endpointOverride,
4163
4196
  temperature,
4164
4197
  maxOutputTokens,
4165
4198
  tools,
@@ -9976,6 +9976,22 @@ function linkAbortSignals(a, b) {
9976
9976
  }
9977
9977
  };
9978
9978
  }
9979
+ function validateEndpointOverride(override) {
9980
+ const trimmed = override.trim();
9981
+ if (trimmed === "") {
9982
+ return {
9983
+ valid: false,
9984
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
9985
+ };
9986
+ }
9987
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
9988
+ return {
9989
+ valid: false,
9990
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
9991
+ };
9992
+ }
9993
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
9994
+ }
9979
9995
  async function runToolLoop(options) {
9980
9996
  const {
9981
9997
  model,
@@ -9983,6 +9999,7 @@ async function runToolLoop(options) {
9983
9999
  baseUrl = BASE_URL,
9984
10000
  headers,
9985
10001
  apiType = "auto",
10002
+ endpointOverride,
9986
10003
  temperature,
9987
10004
  maxOutputTokens,
9988
10005
  tools,
@@ -10065,6 +10082,20 @@ async function runToolLoop(options) {
10065
10082
  await fireRunError({ error: msg, stage: "model", errorObject: new Error(msg) });
10066
10083
  return { data: null, error: msg };
10067
10084
  }
10085
+ let effectiveEndpoint = strategy.endpoint;
10086
+ if (endpointOverride !== void 0) {
10087
+ const overrideValidation = validateEndpointOverride(endpointOverride);
10088
+ if (!overrideValidation.valid) {
10089
+ if (onError) onError(new Error(overrideValidation.message));
10090
+ await fireRunError({
10091
+ error: overrideValidation.message,
10092
+ stage: "model",
10093
+ errorObject: new Error(overrideValidation.message)
10094
+ });
10095
+ return { data: null, error: overrideValidation.message };
10096
+ }
10097
+ effectiveEndpoint = overrideValidation.endpoint;
10098
+ }
10068
10099
  const { signal: combinedSignal, cleanup: cleanupSignalLink } = linkAbortSignals(
10069
10100
  signal,
10070
10101
  detachSignal
@@ -10248,7 +10279,7 @@ async function runToolLoop(options) {
10248
10279
  if (onRequest) onRequest(measureRequest(0, attempt, requestBody, messages, apiTools));
10249
10280
  const sseResult = makeStreamingRequest({
10250
10281
  baseUrl,
10251
- endpoint: strategy.endpoint,
10282
+ endpoint: effectiveEndpoint,
10252
10283
  body: requestBody,
10253
10284
  token,
10254
10285
  headers: effectiveHeaders,
@@ -10720,7 +10751,7 @@ Executing tool: ${toolInfo}
10720
10751
  const continuationRound = toolIteration;
10721
10752
  const continuationResult = makeStreamingRequest({
10722
10753
  baseUrl,
10723
- endpoint: strategy.endpoint,
10754
+ endpoint: effectiveEndpoint,
10724
10755
  body: continuationRequestBody,
10725
10756
  token,
10726
10757
  headers: effectiveHeaders,
@@ -11137,6 +11168,7 @@ function useChat(options) {
11137
11168
  imageModel,
11138
11169
  apiType: requestApiType,
11139
11170
  conversationId,
11171
+ endpointOverride,
11140
11172
  piiRedaction: requestPiiRedaction
11141
11173
  }) => {
11142
11174
  if (abortControllerRef.current) {
@@ -11206,6 +11238,7 @@ function useChat(options) {
11206
11238
  baseUrl,
11207
11239
  headers,
11208
11240
  apiType: requestApiType ?? defaultApiType,
11241
+ endpointOverride,
11209
11242
  temperature,
11210
11243
  maxOutputTokens,
11211
11244
  tools,
@@ -2192,6 +2192,12 @@ type BaseSendMessageArgs = ResponsesApiOptions & {
2192
2192
  onData?: (chunk: string) => void;
2193
2193
  /** Groups requests belonging to the same conversation for observability. Pass-through only — not forwarded to the LLM provider. */
2194
2194
  conversationId?: string;
2195
+ /**
2196
+ * Per-request override for the API path. When set, this request is sent to the
2197
+ * given path instead of the model-resolved endpoint. The request/response
2198
+ * contract is unchanged — only the path differs. Omit to use the default path.
2199
+ */
2200
+ endpointOverride?: string;
2195
2201
  /**
2196
2202
  * Per-request override for PII redaction. When set, takes precedence over the
2197
2203
  * hook-level `piiRedaction` for this call only — e.g. pass `false` to disable
@@ -2192,6 +2192,12 @@ type BaseSendMessageArgs = ResponsesApiOptions & {
2192
2192
  onData?: (chunk: string) => void;
2193
2193
  /** Groups requests belonging to the same conversation for observability. Pass-through only — not forwarded to the LLM provider. */
2194
2194
  conversationId?: string;
2195
+ /**
2196
+ * Per-request override for the API path. When set, this request is sent to the
2197
+ * given path instead of the model-resolved endpoint. The request/response
2198
+ * contract is unchanged — only the path differs. Omit to use the default path.
2199
+ */
2200
+ endpointOverride?: string;
2195
2201
  /**
2196
2202
  * Per-request override for PII redaction. When set, takes precedence over the
2197
2203
  * hook-level `piiRedaction` for this call only — e.g. pass `false` to disable
@@ -2328,6 +2328,22 @@ function linkAbortSignals(a, b) {
2328
2328
  }
2329
2329
  };
2330
2330
  }
2331
+ function validateEndpointOverride(override) {
2332
+ const trimmed = override.trim();
2333
+ if (trimmed === "") {
2334
+ return {
2335
+ valid: false,
2336
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
2337
+ };
2338
+ }
2339
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
2340
+ return {
2341
+ valid: false,
2342
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
2343
+ };
2344
+ }
2345
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
2346
+ }
2331
2347
  async function runToolLoop(options) {
2332
2348
  const {
2333
2349
  model,
@@ -2335,6 +2351,7 @@ async function runToolLoop(options) {
2335
2351
  baseUrl = BASE_URL,
2336
2352
  headers,
2337
2353
  apiType = "auto",
2354
+ endpointOverride,
2338
2355
  temperature,
2339
2356
  maxOutputTokens,
2340
2357
  tools,
@@ -2417,6 +2434,20 @@ async function runToolLoop(options) {
2417
2434
  await fireRunError({ error: msg, stage: "model", errorObject: new Error(msg) });
2418
2435
  return { data: null, error: msg };
2419
2436
  }
2437
+ let effectiveEndpoint = strategy.endpoint;
2438
+ if (endpointOverride !== void 0) {
2439
+ const overrideValidation = validateEndpointOverride(endpointOverride);
2440
+ if (!overrideValidation.valid) {
2441
+ if (onError) onError(new Error(overrideValidation.message));
2442
+ await fireRunError({
2443
+ error: overrideValidation.message,
2444
+ stage: "model",
2445
+ errorObject: new Error(overrideValidation.message)
2446
+ });
2447
+ return { data: null, error: overrideValidation.message };
2448
+ }
2449
+ effectiveEndpoint = overrideValidation.endpoint;
2450
+ }
2420
2451
  const { signal: combinedSignal, cleanup: cleanupSignalLink } = linkAbortSignals(
2421
2452
  signal,
2422
2453
  detachSignal
@@ -2600,7 +2631,7 @@ async function runToolLoop(options) {
2600
2631
  if (onRequest) onRequest(measureRequest(0, attempt, requestBody, messages, apiTools));
2601
2632
  const sseResult = makeStreamingRequest({
2602
2633
  baseUrl,
2603
- endpoint: strategy.endpoint,
2634
+ endpoint: effectiveEndpoint,
2604
2635
  body: requestBody,
2605
2636
  token,
2606
2637
  headers: effectiveHeaders,
@@ -3072,7 +3103,7 @@ Executing tool: ${toolInfo}
3072
3103
  const continuationRound = toolIteration;
3073
3104
  const continuationResult = makeStreamingRequest({
3074
3105
  baseUrl,
3075
- endpoint: strategy.endpoint,
3106
+ endpoint: effectiveEndpoint,
3076
3107
  body: continuationRequestBody,
3077
3108
  token,
3078
3109
  headers: effectiveHeaders,
@@ -3486,6 +3517,7 @@ function useChat(options) {
3486
3517
  imageModel,
3487
3518
  apiType: requestApiType,
3488
3519
  conversationId,
3520
+ endpointOverride,
3489
3521
  piiRedaction: requestPiiRedaction
3490
3522
  }) => {
3491
3523
  if (abortControllerRef.current) {
@@ -3555,6 +3587,7 @@ function useChat(options) {
3555
3587
  baseUrl,
3556
3588
  headers,
3557
3589
  apiType: requestApiType ?? defaultApiType,
3590
+ endpointOverride,
3558
3591
  temperature,
3559
3592
  maxOutputTokens,
3560
3593
  tools,
@@ -31455,6 +31455,22 @@ function linkAbortSignals(a, b) {
31455
31455
  }
31456
31456
  };
31457
31457
  }
31458
+ function validateEndpointOverride(override) {
31459
+ const trimmed = override.trim();
31460
+ if (trimmed === "") {
31461
+ return {
31462
+ valid: false,
31463
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
31464
+ };
31465
+ }
31466
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
31467
+ return {
31468
+ valid: false,
31469
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
31470
+ };
31471
+ }
31472
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
31473
+ }
31458
31474
  async function runToolLoop(options) {
31459
31475
  const {
31460
31476
  model,
@@ -31462,6 +31478,7 @@ async function runToolLoop(options) {
31462
31478
  baseUrl = BASE_URL,
31463
31479
  headers,
31464
31480
  apiType = "auto",
31481
+ endpointOverride,
31465
31482
  temperature,
31466
31483
  maxOutputTokens,
31467
31484
  tools,
@@ -31544,6 +31561,20 @@ async function runToolLoop(options) {
31544
31561
  await fireRunError({ error: msg, stage: "model", errorObject: new Error(msg) });
31545
31562
  return { data: null, error: msg };
31546
31563
  }
31564
+ let effectiveEndpoint = strategy.endpoint;
31565
+ if (endpointOverride !== void 0) {
31566
+ const overrideValidation = validateEndpointOverride(endpointOverride);
31567
+ if (!overrideValidation.valid) {
31568
+ if (onError) onError(new Error(overrideValidation.message));
31569
+ await fireRunError({
31570
+ error: overrideValidation.message,
31571
+ stage: "model",
31572
+ errorObject: new Error(overrideValidation.message)
31573
+ });
31574
+ return { data: null, error: overrideValidation.message };
31575
+ }
31576
+ effectiveEndpoint = overrideValidation.endpoint;
31577
+ }
31547
31578
  const { signal: combinedSignal, cleanup: cleanupSignalLink } = linkAbortSignals(
31548
31579
  signal,
31549
31580
  detachSignal
@@ -31727,7 +31758,7 @@ async function runToolLoop(options) {
31727
31758
  if (onRequest) onRequest(measureRequest(0, attempt, requestBody, messages, apiTools));
31728
31759
  const sseResult = makeStreamingRequest({
31729
31760
  baseUrl,
31730
- endpoint: strategy.endpoint,
31761
+ endpoint: effectiveEndpoint,
31731
31762
  body: requestBody,
31732
31763
  token,
31733
31764
  headers: effectiveHeaders,
@@ -32199,7 +32230,7 @@ Executing tool: ${toolInfo}
32199
32230
  const continuationRound = toolIteration;
32200
32231
  const continuationResult = makeStreamingRequest({
32201
32232
  baseUrl,
32202
- endpoint: strategy.endpoint,
32233
+ endpoint: effectiveEndpoint,
32203
32234
  body: continuationRequestBody,
32204
32235
  token,
32205
32236
  headers: effectiveHeaders,
@@ -2211,6 +2211,23 @@ type RunToolLoopOptions = {
2211
2211
  headers?: Record<string, string>;
2212
2212
  /** Which API backend to use. @default "auto" */
2213
2213
  apiType?: ApiType;
2214
+ /**
2215
+ * Optional per-call override for the request path. When set, every streaming
2216
+ * request targets this path instead of the endpoint the strategy resolves from
2217
+ * the model. Only the URL path changes — the request body and response parsing
2218
+ * follow the resolved strategy exactly as they would without the override.
2219
+ *
2220
+ * Must be a non-empty, root-relative path (e.g. `"/api/v1/utility/responses"`);
2221
+ * a missing leading slash is normalized on. Empty/whitespace-only values and
2222
+ * protocol-relative (`"//host"`) or absolute (`"scheme://host"`) URLs are
2223
+ * rejected — the transport builds the URL as `baseUrl + endpoint`, so a bad
2224
+ * value would silently hit the portal root or send the request (and its Bearer
2225
+ * token) off-origin. An invalid override is reported like any other pre-flight
2226
+ * failure: `onRunError` fires and the run resolves to `{ data: null, error }`
2227
+ * (no throw).
2228
+ * @default undefined (path resolved from the model / `apiType`)
2229
+ */
2230
+ endpointOverride?: string;
2214
2231
  /** Controls randomness (0.0 to 2.0). */
2215
2232
  temperature?: number;
2216
2233
  /** Maximum tokens to generate. */
@@ -2483,33 +2500,6 @@ type StreamingTransportResult = {
2483
2500
  * transport both honor this contract.
2484
2501
  */
2485
2502
  type StreamingTransport = (options: StreamingTransportOptions) => StreamingTransportResult;
2486
- /**
2487
- * Framework-agnostic tool execution loop.
2488
- *
2489
- * Sends a streaming completion request, auto-executes tools that have executors,
2490
- * feeds results back to the model, and repeats until the model stops emitting
2491
- * tool calls or hits the iteration limit.
2492
- *
2493
- * This is the same loop that powers `useChat` in the React SDK, extracted so
2494
- * it can be used from Node.js servers, CLI tools, and background workers.
2495
- *
2496
- * @example
2497
- * ```ts
2498
- * import { runToolLoop } from "@anuma/sdk/server";
2499
- *
2500
- * const result = await runToolLoop({
2501
- * messages: [{ role: "user", content: [{ type: "text", text: "What's the weather?" }] }],
2502
- * model: "fireworks/accounts/fireworks/models/kimi-k2p5",
2503
- * token: "my-api-token",
2504
- * tools: [{
2505
- * type: "function",
2506
- * function: { name: "get_weather", parameters: { type: "object", properties: { city: { type: "string" } } } },
2507
- * executor: async ({ city }) => fetchWeather(city),
2508
- * }],
2509
- * onData: (chunk) => process.stdout.write(chunk),
2510
- * });
2511
- * ```
2512
- */
2513
2503
  declare function runToolLoop(options: RunToolLoopOptions): Promise<RunToolLoopResult>;
2514
2504
 
2515
2505
  /**
@@ -2211,6 +2211,23 @@ type RunToolLoopOptions = {
2211
2211
  headers?: Record<string, string>;
2212
2212
  /** Which API backend to use. @default "auto" */
2213
2213
  apiType?: ApiType;
2214
+ /**
2215
+ * Optional per-call override for the request path. When set, every streaming
2216
+ * request targets this path instead of the endpoint the strategy resolves from
2217
+ * the model. Only the URL path changes — the request body and response parsing
2218
+ * follow the resolved strategy exactly as they would without the override.
2219
+ *
2220
+ * Must be a non-empty, root-relative path (e.g. `"/api/v1/utility/responses"`);
2221
+ * a missing leading slash is normalized on. Empty/whitespace-only values and
2222
+ * protocol-relative (`"//host"`) or absolute (`"scheme://host"`) URLs are
2223
+ * rejected — the transport builds the URL as `baseUrl + endpoint`, so a bad
2224
+ * value would silently hit the portal root or send the request (and its Bearer
2225
+ * token) off-origin. An invalid override is reported like any other pre-flight
2226
+ * failure: `onRunError` fires and the run resolves to `{ data: null, error }`
2227
+ * (no throw).
2228
+ * @default undefined (path resolved from the model / `apiType`)
2229
+ */
2230
+ endpointOverride?: string;
2214
2231
  /** Controls randomness (0.0 to 2.0). */
2215
2232
  temperature?: number;
2216
2233
  /** Maximum tokens to generate. */
@@ -2483,33 +2500,6 @@ type StreamingTransportResult = {
2483
2500
  * transport both honor this contract.
2484
2501
  */
2485
2502
  type StreamingTransport = (options: StreamingTransportOptions) => StreamingTransportResult;
2486
- /**
2487
- * Framework-agnostic tool execution loop.
2488
- *
2489
- * Sends a streaming completion request, auto-executes tools that have executors,
2490
- * feeds results back to the model, and repeats until the model stops emitting
2491
- * tool calls or hits the iteration limit.
2492
- *
2493
- * This is the same loop that powers `useChat` in the React SDK, extracted so
2494
- * it can be used from Node.js servers, CLI tools, and background workers.
2495
- *
2496
- * @example
2497
- * ```ts
2498
- * import { runToolLoop } from "@anuma/sdk/server";
2499
- *
2500
- * const result = await runToolLoop({
2501
- * messages: [{ role: "user", content: [{ type: "text", text: "What's the weather?" }] }],
2502
- * model: "fireworks/accounts/fireworks/models/kimi-k2p5",
2503
- * token: "my-api-token",
2504
- * tools: [{
2505
- * type: "function",
2506
- * function: { name: "get_weather", parameters: { type: "object", properties: { city: { type: "string" } } } },
2507
- * executor: async ({ city }) => fetchWeather(city),
2508
- * }],
2509
- * onData: (chunk) => process.stdout.write(chunk),
2510
- * });
2511
- * ```
2512
- */
2513
2503
  declare function runToolLoop(options: RunToolLoopOptions): Promise<RunToolLoopResult>;
2514
2504
 
2515
2505
  /**
@@ -10026,6 +10026,22 @@ function linkAbortSignals(a, b) {
10026
10026
  }
10027
10027
  };
10028
10028
  }
10029
+ function validateEndpointOverride(override) {
10030
+ const trimmed = override.trim();
10031
+ if (trimmed === "") {
10032
+ return {
10033
+ valid: false,
10034
+ message: 'endpointOverride must be a non-empty, root-relative path (e.g. "/api/v1/utility/responses"); received an empty or whitespace-only string'
10035
+ };
10036
+ }
10037
+ if (trimmed.startsWith("//") || trimmed.includes("://")) {
10038
+ return {
10039
+ valid: false,
10040
+ message: `endpointOverride must be a root-relative path (e.g. "/api/v1/utility/responses"), not a protocol-relative or absolute URL; received "${trimmed}"`
10041
+ };
10042
+ }
10043
+ return { valid: true, endpoint: trimmed.startsWith("/") ? trimmed : `/${trimmed}` };
10044
+ }
10029
10045
  async function runToolLoop(options) {
10030
10046
  const {
10031
10047
  model,
@@ -10033,6 +10049,7 @@ async function runToolLoop(options) {
10033
10049
  baseUrl = BASE_URL,
10034
10050
  headers,
10035
10051
  apiType = "auto",
10052
+ endpointOverride,
10036
10053
  temperature,
10037
10054
  maxOutputTokens,
10038
10055
  tools,
@@ -10115,6 +10132,20 @@ async function runToolLoop(options) {
10115
10132
  await fireRunError({ error: msg, stage: "model", errorObject: new Error(msg) });
10116
10133
  return { data: null, error: msg };
10117
10134
  }
10135
+ let effectiveEndpoint = strategy.endpoint;
10136
+ if (endpointOverride !== void 0) {
10137
+ const overrideValidation = validateEndpointOverride(endpointOverride);
10138
+ if (!overrideValidation.valid) {
10139
+ if (onError) onError(new Error(overrideValidation.message));
10140
+ await fireRunError({
10141
+ error: overrideValidation.message,
10142
+ stage: "model",
10143
+ errorObject: new Error(overrideValidation.message)
10144
+ });
10145
+ return { data: null, error: overrideValidation.message };
10146
+ }
10147
+ effectiveEndpoint = overrideValidation.endpoint;
10148
+ }
10118
10149
  const { signal: combinedSignal, cleanup: cleanupSignalLink } = linkAbortSignals(
10119
10150
  signal,
10120
10151
  detachSignal
@@ -10298,7 +10329,7 @@ async function runToolLoop(options) {
10298
10329
  if (onRequest) onRequest(measureRequest(0, attempt, requestBody, messages, apiTools));
10299
10330
  const sseResult = makeStreamingRequest({
10300
10331
  baseUrl,
10301
- endpoint: strategy.endpoint,
10332
+ endpoint: effectiveEndpoint,
10302
10333
  body: requestBody,
10303
10334
  token,
10304
10335
  headers: effectiveHeaders,
@@ -10770,7 +10801,7 @@ Executing tool: ${toolInfo}
10770
10801
  const continuationRound = toolIteration;
10771
10802
  const continuationResult = makeStreamingRequest({
10772
10803
  baseUrl,
10773
- endpoint: strategy.endpoint,
10804
+ endpoint: effectiveEndpoint,
10774
10805
  body: continuationRequestBody,
10775
10806
  token,
10776
10807
  headers: effectiveHeaders,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",