@gajae-code/ai 0.18.5 → 0.18.6

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/src/stream.ts CHANGED
@@ -1022,8 +1022,28 @@ function mapOptionsForApi<TApi extends Api>(
1022
1022
  // For older models: use budget-based thinking
1023
1023
  if (model.thinking?.mode === "anthropic-adaptive") {
1024
1024
  const effort = mapEffortToAnthropicAdaptiveEffort(model, reasoning);
1025
+
1026
+ // Adaptive mode sends effort directly (no explicit budget_tokens).
1027
+ // However, the thinking consumes tokens from the overall max_tokens budget.
1028
+ // If max_tokens is capped at DEFAULT_REQUEST_MAX_TOKENS (32k),
1029
+ // high/xhigh/max reasoning consumes all 32k, leaving no output tokens.
1030
+ // Solution: when caller did not explicitly set maxTokens,
1031
+ // increase the cap to model.maxTokens to allow room for output.
1032
+ let adaptiveMaxTokens = base.maxTokens ?? model.maxTokens;
1033
+ const hasExplicitMaxTokens = Number.isSafeInteger(options?.maxTokens) && (options?.maxTokens as number) > 0;
1034
+ if (
1035
+ !hasExplicitMaxTokens &&
1036
+ adaptiveMaxTokens === DEFAULT_REQUEST_MAX_TOKENS &&
1037
+ Number.isSafeInteger(model.maxTokens) &&
1038
+ model.maxTokens > DEFAULT_REQUEST_MAX_TOKENS
1039
+ ) {
1040
+ // Increase to model.maxTokens to leave room for both thinking and output
1041
+ adaptiveMaxTokens = model.maxTokens;
1042
+ }
1043
+
1025
1044
  return castApi<"anthropic-messages">({
1026
1045
  ...base,
1046
+ maxTokens: adaptiveMaxTokens,
1027
1047
  thinkingEnabled: true,
1028
1048
  effort,
1029
1049
  toolChoice: mapAnthropicToolChoice(options?.toolChoice),
package/src/types.d.ts CHANGED
@@ -746,7 +746,7 @@ export type TSchema = ZodType | TJsonSchema;
746
746
  export type Static<S> = S extends ZodType ? z.infer<S> : S extends {
747
747
  static: infer T;
748
748
  } ? T : unknown;
749
- export type RawArgumentRejectionCode = "ask-deep-interview-question-body-required" | "ask-intent-review-requires-positive-round" | "ask-intent-contract-requires-non-empty-authority" | "ask-deep-interview-metadata-requires-deep-interview-gate" | "ask-round-zero-metadata-requires-full-topology-fields" | "todo-write-unknown-root-key" | "todo-write-unknown-op-entry-key" | "todo-write-unknown-op-value" | "todo-write-done-drop-requires-target" | "todo-write-unknown-init-entry-key";
749
+ export type RawArgumentRejectionCode = "ask-question-body-required" | "ask-deep-interview-metadata-required" | "ask-deep-interview-single-question-required" | "ask-deep-interview-question-body-required" | "ask-intent-review-requires-positive-round" | "ask-intent-contract-requires-non-empty-authority" | "ask-deep-interview-metadata-requires-deep-interview-gate" | "ask-round-zero-metadata-requires-full-topology-fields" | "todo-write-unknown-root-key" | "todo-write-unknown-op-entry-key" | "todo-write-unknown-op-value" | "todo-write-done-drop-requires-target" | "todo-write-unknown-init-entry-key";
750
750
  /**
751
751
  * Optional structured detail attached to a raw-argument rejection. The fixed
752
752
  * per-code guidance in `RAW_ARGUMENT_REJECTION_MESSAGES` explains the shape;
package/src/types.ts CHANGED
@@ -1019,6 +1019,9 @@ export type TSchema = ZodType | TJsonSchema;
1019
1019
  export type Static<S> = S extends ZodType ? z.infer<S> : S extends { static: infer T } ? T : unknown;
1020
1020
 
1021
1021
  export type RawArgumentRejectionCode =
1022
+ | "ask-question-body-required"
1023
+ | "ask-deep-interview-metadata-required"
1024
+ | "ask-deep-interview-single-question-required"
1022
1025
  | "ask-deep-interview-question-body-required"
1023
1026
  | "ask-intent-review-requires-positive-round"
1024
1027
  | "ask-intent-contract-requires-non-empty-authority"
@@ -23,6 +23,12 @@ export interface FallbackTrigger {
23
23
  export declare const STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE = "stream_first_event_timeout";
24
24
  /** Stable code for a nominally successful response with no content or token usage. */
25
25
  export declare const EMPTY_RESPONSE_PROVIDER_CODE = "empty_response";
26
+ /**
27
+ * Terminal error code for a successful HTTP response whose body is not the protocol the
28
+ * provider speaks (for example a JSON or HTML error returned with 200). Deterministic for
29
+ * the request, so session retry and managed fallback must surface it instead of replaying.
30
+ */
31
+ export declare const PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE = "provider_protocol_mismatch";
26
32
  /**
27
33
  * OpenAI's typed capacity-overload code. It arrives without an HTTP status —
28
34
  * inside an HTTP 200 terminal Responses envelope or a Codex error event — so the
@@ -26,6 +26,12 @@ export interface FallbackTrigger {
26
26
  export const STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE = "stream_first_event_timeout";
27
27
  /** Stable code for a nominally successful response with no content or token usage. */
28
28
  export const EMPTY_RESPONSE_PROVIDER_CODE = "empty_response";
29
+ /**
30
+ * Terminal error code for a successful HTTP response whose body is not the protocol the
31
+ * provider speaks (for example a JSON or HTML error returned with 200). Deterministic for
32
+ * the request, so session retry and managed fallback must surface it instead of replaying.
33
+ */
34
+ export const PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE = "provider_protocol_mismatch";
29
35
  /**
30
36
  * OpenAI's typed capacity-overload code. It arrives without an HTTP status —
31
37
  * inside an HTTP 200 terminal Responses envelope or a Codex error event — so the
@@ -202,6 +202,13 @@ export class FirstEventTimeoutError extends Error {
202
202
  }
203
203
  }
204
204
 
205
+ /**
206
+ * Longest delay setTimeout honors. Bun, like Node, fires any larger delay after 1 ms,
207
+ * so a very long timeout (for example an env value meant as "effectively never")
208
+ * would otherwise abort the stream immediately.
209
+ */
210
+ const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
211
+
205
212
  const dummyWatchdog = setTimeout(() => {}, 1);
206
213
  clearTimeout(dummyWatchdog);
207
214
 
@@ -211,7 +218,7 @@ clearTimeout(dummyWatchdog);
211
218
  */
212
219
  export function createWatchdog(timeoutMs: number | undefined, onTimeout: () => void): Watchdog {
213
220
  if (timeoutMs !== undefined && timeoutMs > 0) {
214
- return setTimeout(onTimeout, timeoutMs);
221
+ return setTimeout(onTimeout, Math.min(timeoutMs, MAX_TIMER_DELAY_MS));
215
222
  }
216
223
  return undefined;
217
224
  }
@@ -335,10 +342,10 @@ export async function* iterateWithIdleTimeout<T>(
335
342
  let timer: NodeJS.Timeout | undefined;
336
343
  let resolveTimeout: ((value: { kind: "timeout" }) => void) | undefined;
337
344
  const enforceTimeout = !noTimeoutEnforced && activeTimeoutMs !== undefined && activeTimeoutMs >= 0;
338
- if (enforceTimeout) {
345
+ if (enforceTimeout && activeTimeoutMs !== undefined) {
339
346
  const { promise, resolve } = Promise.withResolvers<{ kind: "timeout" }>();
340
347
  resolveTimeout = resolve;
341
- timer = setTimeout(() => resolve({ kind: "timeout" }), activeTimeoutMs);
348
+ timer = setTimeout(() => resolve({ kind: "timeout" }), Math.min(activeTimeoutMs, MAX_TIMER_DELAY_MS));
342
349
  racers.push(promise);
343
350
  }
344
351
 
@@ -57,6 +57,11 @@ export declare class GlmZcodeOAuthFlow extends OAuthCallbackFlow {
57
57
  }>;
58
58
  exchangeToken(code: string, state: string, redirectUri: string): Promise<OAuthCredentials>;
59
59
  }
60
+ /**
61
+ * Manual-input prompt for the paste fallback. Repeats the redirect-URL shape and
62
+ * the DevTools pointer so a re-prompt after a rejected paste stays self-contained.
63
+ */
64
+ export declare const GLM_ZCODE_MANUAL_INPUT_PROMPT = "Paste the full zcode://oauth/callback?code=\u2026&state=\u2026 URL (copy it from browser DevTools \u2192 Network tab; the address bar never shows it) or the authorization code:";
60
65
  export declare function loginGlmZcode(ctrl: OAuthController, options?: GlmZcodeOAuthFlowOptions): Promise<OAuthCredentials>;
61
66
  export interface GlmZcodeRefreshOptions {
62
67
  signal?: AbortSignal;
@@ -387,7 +387,7 @@ export class GlmZcodeOAuthFlow extends OAuthCallbackFlow {
387
387
  return {
388
388
  url: `${authorizeUrl}?${params.toString()}`,
389
389
  instructions:
390
- "Complete Z.AI login in your browser. This is an UNOFFICIAL ZCode-based login — use at your own risk; it may stop working or violate ZCode/Z.AI Terms of Service. Because this CLI cannot receive the zcode:// redirect, paste the final redirect URL or authorization code when prompted. If the ZCode desktop app is installed, cancel the browser's prompt to open it: the app exchanges the single-use code itself and the pasted code is then rejected (broker error 2007).",
390
+ "Complete Z.AI login in your browser. This is an UNOFFICIAL ZCode-based login — use at your own risk; it may stop working or violate ZCode/Z.AI Terms of Service. If the ZCode desktop app is installed, cancel the browser's prompt to open it: the app exchanges the single-use code itself and the pasted code is then rejected (broker error 2007). After login the browser redirects to a zcode:// address, which never appears in the address bar — copy it from DevTools instead: open the browser DevTools Network tab (filter: zcode), select the zcode://oauth/callback?code=…&state=… request, and paste that full URL (or just the code) at the prompt.",
391
391
  };
392
392
  }
393
393
 
@@ -396,6 +396,13 @@ export class GlmZcodeOAuthFlow extends OAuthCallbackFlow {
396
396
  }
397
397
  }
398
398
 
399
+ /**
400
+ * Manual-input prompt for the paste fallback. Repeats the redirect-URL shape and
401
+ * the DevTools pointer so a re-prompt after a rejected paste stays self-contained.
402
+ */
403
+ export const GLM_ZCODE_MANUAL_INPUT_PROMPT =
404
+ "Paste the full zcode://oauth/callback?code=…&state=… URL (copy it from browser DevTools → Network tab; the address bar never shows it) or the authorization code:";
405
+
399
406
  export async function loginGlmZcode(
400
407
  ctrl: OAuthController,
401
408
  options?: GlmZcodeOAuthFlowOptions,
@@ -9,6 +9,16 @@ import type {
9
9
  OAuthProviderInterface,
10
10
  } from "./types";
11
11
 
12
+ export class UnknownOAuthProviderError extends Error {
13
+ readonly provider: string;
14
+
15
+ constructor(provider: string) {
16
+ super(`Unknown OAuth provider: ${provider}`);
17
+ this.name = "UnknownOAuthProviderError";
18
+ this.provider = provider;
19
+ }
20
+ }
21
+
12
22
  const builtInOAuthProviders: OAuthProviderInfo[] = [
13
23
  {
14
24
  id: "anthropic",
@@ -442,7 +452,7 @@ export async function refreshOAuthToken(
442
452
  newCredentials = credentials;
443
453
  break;
444
454
  default:
445
- throw new Error(`Unknown OAuth provider: ${provider}`);
455
+ throw new UnknownOAuthProviderError(provider);
446
456
  }
447
457
  return newCredentials;
448
458
  }
@@ -960,6 +960,11 @@ export function validateToolCall(tools: Tool[], toolCall: ToolCall): ToolCall["a
960
960
  }
961
961
 
962
962
  const RAW_ARGUMENT_REJECTION_MESSAGES: Record<RawArgumentRejectionCode, string> = {
963
+ "ask-question-body-required": "ask question bodies must contain non-whitespace text; remove empty extra questions",
964
+ "ask-deep-interview-metadata-required":
965
+ "active deep-interview questions require structured deepInterview metadata; question prose and workflowGate do not record an answer",
966
+ "ask-deep-interview-single-question-required":
967
+ "active deep-interview asks require exactly one question; include metadata on that question, never as an extra question",
963
968
  "ask-deep-interview-question-body-required":
964
969
  "deep-interview question bodies must contain a specific question, not a placeholder",
965
970
  "ask-intent-review-requires-positive-round":