@gajae-code/ai 0.13.2 → 0.14.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.
Files changed (81) hide show
  1. package/CHANGELOG.md +61 -2
  2. package/dist/types/auth-broker/client.d.ts +9 -1
  3. package/dist/types/auth-broker/redact.d.ts +7 -0
  4. package/dist/types/auth-broker/remote-store.d.ts +50 -9
  5. package/dist/types/auth-broker/types.d.ts +14 -0
  6. package/dist/types/auth-broker/wire-schemas.d.ts +25 -0
  7. package/dist/types/auth-storage.d.ts +200 -6
  8. package/dist/types/core.d.ts +1 -0
  9. package/dist/types/model-cache.d.ts +4 -1
  10. package/dist/types/model-manager.d.ts +11 -0
  11. package/dist/types/provider-models/openai-compat.d.ts +5 -0
  12. package/dist/types/provider-models/special.d.ts +3 -0
  13. package/dist/types/providers/anthropic.d.ts +31 -0
  14. package/dist/types/providers/cursor.d.ts +9 -1
  15. package/dist/types/providers/kiro-codewhisperer.d.ts +8 -0
  16. package/dist/types/providers/mock.d.ts +8 -0
  17. package/dist/types/providers/register-builtins.d.ts +1 -0
  18. package/dist/types/providers/transform-messages.d.ts +18 -0
  19. package/dist/types/types.d.ts +34 -8
  20. package/dist/types/usage/grok-cli.d.ts +5 -0
  21. package/dist/types/usage.d.ts +6 -0
  22. package/dist/types/utils/discovery/openai-compatible.d.ts +5 -0
  23. package/dist/types/utils/event-stream.d.ts +4 -2
  24. package/dist/types/utils/fallback-transport.d.ts +10 -0
  25. package/dist/types/utils/http-inspector.d.ts +1 -0
  26. package/dist/types/utils/idle-iterator.d.ts +13 -1
  27. package/dist/types/utils/json-parse.d.ts +19 -0
  28. package/dist/types/utils/oauth/callback-server.d.ts +13 -0
  29. package/dist/types/utils/oauth/kiro.d.ts +71 -0
  30. package/dist/types/utils/oauth/types.d.ts +1 -1
  31. package/dist/types/utils/parse-bind.d.ts +8 -5
  32. package/dist/types/utils/tool-call-healing.d.ts +7 -0
  33. package/dist/types/utils/tool-choice-capability.d.ts +11 -0
  34. package/package.json +3 -2
  35. package/src/auth-broker/client.ts +30 -0
  36. package/src/auth-broker/redact.ts +15 -0
  37. package/src/auth-broker/refresher.ts +4 -2
  38. package/src/auth-broker/remote-store.ts +693 -70
  39. package/src/auth-broker/server.ts +57 -12
  40. package/src/auth-broker/types.ts +16 -0
  41. package/src/auth-broker/wire-schemas.ts +21 -0
  42. package/src/auth-gateway/server.ts +84 -19
  43. package/src/auth-storage.ts +985 -41
  44. package/src/core.ts +1 -0
  45. package/src/model-cache.ts +23 -4
  46. package/src/model-manager.ts +70 -11
  47. package/src/model-thinking.ts +45 -1
  48. package/src/models.json +9604 -1932
  49. package/src/openai-completions-compat.ts +2 -1
  50. package/src/provider-models/descriptors.ts +7 -1
  51. package/src/provider-models/openai-compat.ts +52 -28
  52. package/src/provider-models/special.ts +12 -0
  53. package/src/providers/amazon-bedrock.ts +2 -1
  54. package/src/providers/anthropic.ts +831 -27
  55. package/src/providers/cursor.ts +83 -3
  56. package/src/providers/kiro-codewhisperer.ts +572 -0
  57. package/src/providers/mock.ts +15 -2
  58. package/src/providers/ollama.ts +9 -2
  59. package/src/providers/openai-codex-responses.ts +16 -9
  60. package/src/providers/openai-completions.ts +6 -1
  61. package/src/providers/openai-responses-shared.ts +180 -18
  62. package/src/providers/register-builtins.ts +24 -2
  63. package/src/providers/transform-messages.ts +64 -1
  64. package/src/stream.ts +25 -2
  65. package/src/types.ts +36 -7
  66. package/src/usage/grok-cli.ts +86 -1
  67. package/src/usage.ts +7 -0
  68. package/src/utils/discovery/openai-compatible.ts +89 -4
  69. package/src/utils/event-stream.ts +11 -2
  70. package/src/utils/fallback-transport.ts +44 -2
  71. package/src/utils/http-inspector.ts +1 -0
  72. package/src/utils/idle-iterator.ts +29 -6
  73. package/src/utils/json-parse.ts +80 -0
  74. package/src/utils/oauth/callback-server.ts +31 -1
  75. package/src/utils/oauth/index.ts +14 -1
  76. package/src/utils/oauth/kiro.ts +448 -0
  77. package/src/utils/oauth/synthetic.ts +2 -3
  78. package/src/utils/oauth/types.ts +1 -0
  79. package/src/utils/parse-bind.ts +27 -0
  80. package/src/utils/tool-call-healing.ts +13 -2
  81. package/src/utils/tool-choice-capability.ts +386 -6
@@ -32,6 +32,8 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
32
32
  now?: () => number;
33
33
  /** Optional guard that must permit cache publication. Default: writes are permitted. */
34
34
  canPublishCache?: () => boolean;
35
+ /** Credential-and-endpoint identity required to reuse dynamic catalog IDs. */
36
+ cacheDynamicModelProvenance?: string;
35
37
  }
36
38
  /**
37
39
  * Resolution result.
@@ -44,8 +46,17 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
44
46
  export interface ModelResolutionResult<TApi extends Api = Api> {
45
47
  models: Model<TApi>[];
46
48
  stale: boolean;
49
+ /** Whether the cache row consulted for this resolution was still within its TTL. */
50
+ cacheFresh: boolean;
51
+ /** Whether the consulted cache row was authoritative. */
52
+ cacheAuthoritative: boolean;
47
53
  /** Whether this resolution successfully fetched dynamic models. */
48
54
  fetched: boolean;
55
+ /**
56
+ * IDs returned by a current authoritative dynamic provider catalog. This is
57
+ * deliberately distinct from `models`, which merges static and cached data.
58
+ */
59
+ dynamicModelIds?: readonly string[];
49
60
  }
50
61
  /**
51
62
  * Stateful facade over provider model resolution.
@@ -162,6 +162,11 @@ export interface LmStudioModelManagerConfig {
162
162
  baseUrl?: string;
163
163
  }
164
164
  export declare function lmStudioModelManagerOptions(config?: LmStudioModelManagerConfig): ModelManagerOptions<"openai-completions">;
165
+ export interface OmlxModelManagerConfig {
166
+ apiKey?: string;
167
+ baseUrl?: string;
168
+ }
169
+ export declare function omlxModelManagerOptions(config?: OmlxModelManagerConfig): ModelManagerOptions<"openai-completions">;
165
170
  export interface SyntheticModelManagerConfig {
166
171
  apiKey?: string;
167
172
  baseUrl?: string;
@@ -21,3 +21,6 @@ export declare function glmZcodeModelManagerOptions(_config?: GlmZcodeModelManag
21
21
  export interface JetBrainsJunieModelManagerConfig {
22
22
  }
23
23
  export declare function jetbrainsJunieModelManagerOptions(_config?: JetBrainsJunieModelManagerConfig): ModelManagerOptions<"anthropic-messages">;
24
+ export interface KiroModelManagerConfig {
25
+ }
26
+ export declare function kiroModelManagerOptions(_config?: KiroModelManagerConfig): ModelManagerOptions<"kiro-codewhisperer-stream">;
@@ -1,6 +1,7 @@
1
1
  import Anthropic, { type ClientOptions as AnthropicSdkClientOptions } from "@anthropic-ai/sdk";
2
2
  import type { MessageCreateParamsStreaming, MessageParam } from "@anthropic-ai/sdk/resources/messages";
3
3
  import type { FetchImpl, Message, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types";
4
+ import { type RawHttpRequestDump } from "../utils/http-inspector";
4
5
  export type AnthropicHeaderOptions = {
5
6
  apiKey: string;
6
7
  baseUrl?: string;
@@ -76,6 +77,36 @@ export declare function isAnthropicMaskedProxyRejection(error: unknown): boolean
76
77
  * loud instead of being silently retried.
77
78
  */
78
79
  export declare function isAnthropicCacheBreakpointOverflowError(error: unknown): boolean;
80
+ export type AnthropicContextManagementInjectionDiagnostic = {
81
+ strategy: string;
82
+ message: string;
83
+ captureNote: string;
84
+ };
85
+ /**
86
+ * Diagnose a context-management strategy named by an Anthropic 400 but absent
87
+ * from the body GJC sent. This mismatch is evidence of intermediary mutation,
88
+ * not permission to silently enable thinking or retry the request.
89
+ */
90
+ export declare function diagnoseAnthropicContextManagementInjection(error: unknown, dump: RawHttpRequestDump | undefined): AnthropicContextManagementInjectionDiagnostic | undefined;
91
+ export interface CpaToolAliasRestoreFailure {
92
+ /** The rejected tool-call name exactly as CPA quoted it. */
93
+ alias: string;
94
+ /**
95
+ * Base tool name parsed out of the alias (`mcp__<server>__<token>_<base>`
96
+ * → `<base>`), when the alias shape is well-formed. `undefined` for a
97
+ * malformed alias — callers must then fall back to direct discovery and
98
+ * never invent a name.
99
+ */
100
+ baseName?: string;
101
+ }
102
+ /**
103
+ * Classifies the CPA alias-restore signature and extracts the rejected alias
104
+ * plus its base tool name. Claims only statusless in-stream SSE error events
105
+ * and HTTP 5xx failures: a non-5xx status carrying this text is not the
106
+ * observed CPA delivery shape and is left to the other classifiers.
107
+ */
108
+ export declare function parseCpaToolAliasRestoreFailure(error: unknown): CpaToolAliasRestoreFailure | undefined;
109
+ export declare function isCpaToolAliasRestoreFailure(error: unknown): boolean;
79
110
  export declare const claudeCodeVersion = "2.1.219";
80
111
  export declare const claudeCodeEntrypoint = "sdk-cli";
81
112
  export declare const claudeToolPrefix: string;
@@ -1,5 +1,5 @@
1
1
  import { type JsonValue } from "@bufbuild/protobuf";
2
- import type { CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, ToolResultMessage } from "../types";
2
+ import type { CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, ToolCall, ToolResultMessage } from "../types";
3
3
  import { CURSOR_CLIENT_VERSION } from "./cursor/client-version";
4
4
  export declare const CURSOR_API_URL = "https://api2.cursor.sh";
5
5
  export { CURSOR_CLIENT_VERSION };
@@ -12,11 +12,19 @@ export interface CursorOptions extends StreamOptions {
12
12
  onToolResult?: CursorToolResultHandler;
13
13
  }
14
14
  export declare const streamCursor: StreamFunction<"cursor-agent">;
15
+ type ToolCallState = ToolCall & {
16
+ index: number;
17
+ partialJson?: string;
18
+ kind: "mcp" | "todo_write" | "native";
19
+ };
15
20
  /** Exported for tests: verifies handler is invoked with correct `this` when passed as bound. */
16
21
  export declare function resolveExecHandler<TArgs, TResult>(args: TArgs, handler: ((args: TArgs) => Promise<CursorExecHandlerResult<TResult>>) | undefined, onToolResult: CursorToolResultHandler | undefined, buildFromToolResult: (toolResult: ToolResultMessage) => TResult, buildRejected: (reason: string) => TResult, buildError: (error: string) => TResult): Promise<{
17
22
  execResult: TResult;
18
23
  toolResult?: ToolResultMessage;
19
24
  }>;
25
+ /** Exported for direct regression coverage of the JSON-safety boundary. */
26
+ export declare function cursorJsonSafeValueForTest(value: unknown): unknown;
27
+ export declare function buildNativeToolCallBlock(toolCall: Record<string, unknown>, callId: string, index: number): ToolCallState | null;
20
28
  /**
21
29
  * Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
22
30
  * system prompt plus prior conversation history, as JSON blobs matching
@@ -0,0 +1,8 @@
1
+ import type { StreamFunction, StreamOptions } from "../types";
2
+ export interface KiroCodeWhispererOptions extends StreamOptions {
3
+ /** AWS region for the CodeWhisperer streaming endpoint. */
4
+ region?: string;
5
+ /** Profile ARN for enterprise IAM Identity Center accounts. */
6
+ profileArn?: string;
7
+ }
8
+ export declare const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream">;
@@ -62,6 +62,11 @@ export type MockContent = string | {
62
62
  arguments: Record<string, unknown> | string;
63
63
  /** Simulate a provider-flagged truncated call (cut off mid-arguments). */
64
64
  incompleteArguments?: boolean;
65
+ /** Typed reason matching `ToolCall.incompleteArgumentsReason`. Defaults to `"truncated"`. */
66
+ incompleteArgumentsReason?: "truncated" | "malformed" | "conflicting" | "ambiguous";
67
+ /** Simulate a provider-flagged `\uXXXX`-escaped non-ASCII argument payload. */
68
+ escapedNonAsciiArguments?: boolean;
69
+ thoughtSignature?: string;
65
70
  };
66
71
  /** One scripted response. */
67
72
  export interface MockResponse {
@@ -75,6 +80,9 @@ export interface MockResponse {
75
80
  };
76
81
  /** Pre-set responseId. */
77
82
  responseId?: string;
83
+ /** Optional provider metadata copied onto the final assistant message. */
84
+ disabledFeatures?: string[];
85
+ providerPayload?: AssistantMessage["providerPayload"];
78
86
  /** Optional typed provider failure metadata for retry/fallback tests. */
79
87
  transportFailure?: AssistantMessage["transportFailure"];
80
88
  /** If set, the stream emits a terminal error event instead of completing. */
@@ -55,4 +55,5 @@ export declare const streamOpenAIResponses: (model: Model<"openai-responses">, c
55
55
  export declare const streamCursor: (model: Model<"cursor-agent">, context: Context, options: OptionsForApi<"cursor-agent">) => EventStreamImpl;
56
56
  export declare const streamOllama: (model: Model<"ollama-chat">, context: Context, options: OptionsForApi<"ollama-chat">) => EventStreamImpl;
57
57
  export declare const streamBedrock: (model: Model<"bedrock-converse-stream">, context: Context, options: OptionsForApi<"bedrock-converse-stream">) => EventStreamImpl;
58
+ export declare const streamKiroCodeWhisperer: (model: Model<"kiro-codewhisperer-stream">, context: Context, options: OptionsForApi<"kiro-codewhisperer-stream">) => EventStreamImpl;
58
59
  export {};
@@ -9,6 +9,24 @@ import type { Api, AssistantMessage, Message, Model } from "../types";
9
9
  * - Injects synthetic "aborted" tool results
10
10
  * - Adds a <turn-aborted> guidance marker for the model
11
11
  */
12
+ /**
13
+ * Detect directly adjacent private thinking blocks inside one assistant message's
14
+ * content. `thinking` and `redacted_thinking` are one adjacency class: the
15
+ * Anthropic wire contract rejects a replayed assistant turn where two such blocks
16
+ * sit next to each other with no intervening `tool_use`/`text` block (#4416).
17
+ *
18
+ * This is a pure, allocation-free predicate used by defense-in-depth diagnostics
19
+ * (issue #4443): the write-time transcript assertion (coding-agent persistence)
20
+ * and the stream-assembler SSE diagnostic (anthropic stream completion). It never
21
+ * inspects block payloads — only the block-type sequence — so it cannot leak
22
+ * thinking text, signatures, or credentials.
23
+ *
24
+ * Blocks separated by any non-private block (`tool_use`, `text`, …) are ordinary
25
+ * interleaved-thinking shape and return `false`.
26
+ */
27
+ export declare function hasAdjacentPrivateThinkingBlocks(content: {
28
+ type: string;
29
+ }[]): boolean;
12
30
  export declare function transformMessages<TApi extends Api>(messages: Message[], model: Model<TApi>, normalizeToolCallId?: (id: string, model: Model<TApi>, source: AssistantMessage) => string, options?: {
13
31
  repairLatestAssistantThinking?: boolean;
14
32
  repairAllAssistantThinking?: boolean;
@@ -7,6 +7,7 @@ import type { DeleteArgs, DeleteResult, DiagnosticsArgs, DiagnosticsResult, Grep
7
7
  import type { GoogleOptions } from "./providers/google";
8
8
  import type { GoogleGeminiCliOptions } from "./providers/google-gemini-cli";
9
9
  import type { GoogleVertexOptions } from "./providers/google-vertex";
10
+ import type { KiroCodeWhispererOptions } from "./providers/kiro-codewhisperer";
10
11
  import type { OllamaChatOptions } from "./providers/ollama";
11
12
  import type { OpenAICodexResponsesOptions } from "./providers/openai-codex-responses";
12
13
  import type { OpenAICompletionsOptions } from "./providers/openai-completions";
@@ -14,7 +15,7 @@ import type { OpenAIResponsesOptions } from "./providers/openai-responses";
14
15
  import type { AssistantMessageEventStream } from "./utils/event-stream";
15
16
  import type { FallbackAttemptToken, TransportFailureFacts } from "./utils/fallback-transport";
16
17
  export type { AssistantMessageEventStream } from "./utils/event-stream";
17
- export type KnownApi = "openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent";
18
+ export type KnownApi = "openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "kiro-codewhisperer-stream";
18
19
  export type Api = KnownApi | (string & {});
19
20
  export interface ApiOptionsMap {
20
21
  "anthropic-messages": AnthropicOptions;
@@ -28,6 +29,7 @@ export interface ApiOptionsMap {
28
29
  "google-vertex": GoogleVertexOptions;
29
30
  "ollama-chat": OllamaChatOptions;
30
31
  "cursor-agent": CursorOptions;
32
+ "kiro-codewhisperer-stream": KiroCodeWhispererOptions;
31
33
  }
32
34
  export type OptionsForApi<TApi extends Api> = StreamOptions | (TApi extends keyof ApiOptionsMap ? ApiOptionsMap[TApi] : never);
33
35
  /** Canonical thinking transport used by a model. */
@@ -51,7 +53,7 @@ export interface ThinkingConfig {
51
53
  /** Provider-specific transport used to encode the selected effort. */
52
54
  mode: ThinkingControlMode;
53
55
  }
54
- export declare const KNOWN_PROVIDERS: readonly ["alibaba-token-plan", "amazon-bedrock", "azure-openai", "anthropic", "google", "google-gemini-cli", "google-antigravity", "google-vertex", "openai", "openai-codex", "opencodex", "kimi-code", "minimax-code", "minimax-code-cn", "github-copilot", "fireworks", "firepass", "fugu", "gitlab-duo", "cursor", "jetbrains-junie", "deepseek", "deepinfra", "xai", "groq", "cerebras", "openrouter", "kilo", "vercel-ai-gateway", "zai", "glm-zcode", "mistral", "minimax", "opencode-go", "opencode-zen", "opengateway", "bizrouter", "mara", "synthetic", "cloudflare-ai-gateway", "huggingface", "litellm", "moonshot", "nvidia", "nanogpt", "ollama", "ollama-cloud", "qianfan", "qwen-portal", "together", "venice", "vllm", "xiaomi", "xiaomi-token-plan-sgp", "xiaomi-token-plan-ams", "xiaomi-token-plan-cn", "zenmux", "lm-studio"];
56
+ export declare const KNOWN_PROVIDERS: readonly ["alibaba-token-plan", "amazon-bedrock", "kiro", "azure-openai", "anthropic", "google", "google-gemini-cli", "google-antigravity", "google-vertex", "openai", "openai-codex", "opencodex", "kimi-code", "minimax-code", "minimax-code-cn", "github-copilot", "fireworks", "firepass", "fugu", "gitlab-duo", "cursor", "jetbrains-junie", "deepseek", "deepinfra", "xai", "groq", "cerebras", "openrouter", "kilo", "vercel-ai-gateway", "zai", "glm-zcode", "mistral", "minimax", "opencode-go", "opencode-zen", "opengateway", "bizrouter", "mara", "synthetic", "cloudflare-ai-gateway", "huggingface", "litellm", "moonshot", "nvidia", "nanogpt", "ollama", "ollama-cloud", "qianfan", "qwen-portal", "together", "venice", "vllm", "xiaomi", "xiaomi-token-plan-sgp", "xiaomi-token-plan-ams", "xiaomi-token-plan-cn", "zenmux", "lm-studio", "omlx"];
55
57
  export type KnownProvider = (typeof KNOWN_PROVIDERS)[number];
56
58
  export declare function isKnownProvider(provider: string): provider is KnownProvider;
57
59
  export type Provider = KnownProvider | string;
@@ -374,13 +376,37 @@ export interface ToolCall {
374
376
  */
375
377
  customWireName?: string;
376
378
  /**
377
- * Set when the provider detected the argument JSON was truncated — the model
378
- * hit its output-token limit (or the response was otherwise cut short) before
379
- * emitting a complete arguments object. The `arguments` field then holds a
380
- * best-effort partial parse and must not be executed as-is; the agent loop
381
- * rejects the call with a retryable error instead.
379
+ * Set when the provider detected the argument JSON was not safely executable —
380
+ * the model hit its output-token limit (or the response was otherwise cut short)
381
+ * before emitting a complete arguments object, the terminal payload was malformed,
382
+ * the streamed and terminal payloads conflicted, or the tool-call identity was
383
+ * ambiguous on the wire. The `arguments` field then holds a best-effort partial
384
+ * parse and must not be executed as-is; the agent loop rejects the call with a
385
+ * retryable, reason-specific error instead.
382
386
  */
383
387
  incompleteArguments?: boolean;
388
+ /**
389
+ * When `incompleteArguments` is set, the typed cause so the agent loop can give
390
+ * reason-specific recovery guidance:
391
+ * - `"truncated"`: the response was cut short mid-arguments (output-token limit).
392
+ * - `"malformed"`: the terminal arguments did not decode to a valid JSON object.
393
+ * - `"conflicting"`: the streamed and terminal argument payloads disagree.
394
+ * - `"ambiguous"`: the tool-call identity could not be unambiguously resolved
395
+ * (duplicate `call_id`, id/call_id collision, etc.), so attribution is unsafe.
396
+ * Absent when `incompleteArguments` is not set. Existing callers that read only
397
+ * `incompleteArguments` continue to work.
398
+ */
399
+ incompleteArgumentsReason?: "truncated" | "malformed" | "conflicting" | "ambiguous";
400
+ /**
401
+ * Set when the raw argument JSON spelled a printable non-ASCII character as a
402
+ * `\uXXXX` escape instead of literal UTF-8. Such a payload parses cleanly but
403
+ * is unverifiable: one mistyped hex digit decodes to a different, equally
404
+ * valid character, so the text can be silently wrong with no in-band evidence.
405
+ * The agent loop rejects the call with a retryable error instead of executing
406
+ * it. Escapes that are required (control characters) or unavoidable (lone
407
+ * surrogates) never set this.
408
+ */
409
+ escapedNonAsciiArguments?: boolean;
384
410
  }
385
411
  export interface Usage {
386
412
  /** Non-cached input tokens (matches the bucket the provider bills as new input). */
@@ -429,7 +455,7 @@ export interface Usage {
429
455
  };
430
456
  }
431
457
  export type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";
432
- export type AssistantErrorKind = "provider_safety_stop";
458
+ export type AssistantErrorKind = "provider_safety_stop" | "local_snapshot_failure" | "local_buffer_overflow";
433
459
  export interface OpenAIResponsesHistoryPayload {
434
460
  type: "openaiResponsesHistory";
435
461
  provider?: string;
@@ -4,7 +4,12 @@ interface BillingUsage {
4
4
  used: number;
5
5
  billingPeriodEnd: string;
6
6
  }
7
+ interface WeeklyBillingUsage {
8
+ creditUsagePercent: number;
9
+ billingPeriodEnd: string;
10
+ }
7
11
  export declare function parseGrokCliBillingUsage(payload: unknown): BillingUsage;
12
+ export declare function parseGrokCliWeeklyBillingUsage(payload: unknown): WeeklyBillingUsage | undefined;
8
13
  /** Test seam: the usage access token as resolved from a credential plus trusted env. */
9
14
  export declare function resolveGrokAccessTokenForTest(params: UsageFetchParams): string | undefined;
10
15
  export declare const grokCliUsageProvider: UsageProvider;
@@ -210,6 +210,11 @@ export interface UsageLogger {
210
210
  warn(message: string, meta?: Record<string, unknown>): void;
211
211
  }
212
212
  /** Credential bundle for usage endpoints. */
213
+ /** MCP OAuth authority carried through usage-triggered refresh. */
214
+ export interface UsageMCPOAuthBinding {
215
+ resourceOrigin: string;
216
+ tokenEndpoint: string;
217
+ }
213
218
  export interface UsageCredential {
214
219
  type: "api_key" | "oauth";
215
220
  apiKey?: string;
@@ -220,6 +225,7 @@ export interface UsageCredential {
220
225
  projectId?: string;
221
226
  email?: string;
222
227
  enterpriseUrl?: string;
228
+ mcpBinding?: UsageMCPOAuthBinding;
223
229
  metadata?: Record<string, unknown>;
224
230
  }
225
231
  /** Parameters provided to a usage fetcher. */
@@ -65,6 +65,11 @@ export interface FetchOpenAICompatibleModelsOptions<TApi extends Api> {
65
65
  */
66
66
  mapModel?: (entry: OpenAICompatibleModelRecord, defaults: Model<TApi>, context: OpenAICompatibleModelMapperContext<TApi>) => Model<TApi> | null;
67
67
  }
68
+ /**
69
+ * Resolves an endpoint for an implicit local provider without allowing an
70
+ * environment override to turn its keyless discovery into a remote request.
71
+ */
72
+ export declare function resolveLoopbackOpenAIBaseUrl(value: string | undefined, fallback: string): string;
68
73
  /**
69
74
  * Fetches and normalizes an OpenAI-compatible `/models` catalog.
70
75
  *
@@ -11,7 +11,7 @@ export declare class EventStream<T, R = T> implements AsyncIterable<T> {
11
11
  rejectFinalResult: (err: unknown) => void;
12
12
  isComplete: (event: T) => boolean;
13
13
  extractResult: (event: T) => R;
14
- constructor(isComplete: (event: T) => boolean, extractResult: (event: T) => R);
14
+ constructor(isComplete: (event: T) => boolean, extractResult: (event: T) => R, onConsumerClose?: () => void);
15
15
  /**
16
16
  * Read-only snapshot of the not-yet-consumed events. Always a fresh copy:
17
17
  * external code can never mutate internal queue state or observe head-index
@@ -20,6 +20,8 @@ export declare class EventStream<T, R = T> implements AsyncIterable<T> {
20
20
  get queue(): T[];
21
21
  /** Read-only test seam for outstanding consumer-drain waiters. */
22
22
  get pendingConsumerDrainCountForTests(): number;
23
+ /** Whether an async iterator is currently consuming this stream. */
24
+ get hasActiveConsumer(): boolean;
23
25
  push(event: T): void;
24
26
  deliver(event: T): void;
25
27
  /**
@@ -35,5 +37,5 @@ export declare class EventStream<T, R = T> implements AsyncIterable<T> {
35
37
  result(): Promise<R>;
36
38
  }
37
39
  export declare class AssistantMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
38
- constructor();
40
+ constructor(onConsumerClose?: () => void);
39
41
  }
@@ -44,6 +44,16 @@ export interface TransportFailureFacts {
44
44
  /** OpenAI's typed `error.code`, preserved separately at the transport boundary. */
45
45
  openaiErrorCode?: string;
46
46
  headers?: Record<string, string>;
47
+ /** Safe request-size observation for retry amplification policy. Never contains body content. */
48
+ requestBytes?: number;
49
+ /** Time spent waiting for the first semantic stream event on the failed request. */
50
+ firstEventElapsedMs?: number;
51
+ /** Configured first-event window before any bounded endpoint grace. */
52
+ firstEventTimeoutMs?: number;
53
+ /** Coarse endpoint class; deliberately excludes host, path, credentials, and query parameters. */
54
+ endpointClass?: "canonical" | "custom";
55
+ /** Provider-supplied ceiling for total attempts, including the initial request. */
56
+ retryMaxAttempts?: number;
47
57
  }
48
58
  /** Opaque per-invocation marker required by managed fallback transport calls. */
49
59
  export interface FallbackAttemptToken {
@@ -6,6 +6,7 @@ export type RawHttpRequestDump = {
6
6
  url?: string;
7
7
  headers?: Record<string, string>;
8
8
  body?: unknown;
9
+ diagnostics?: Record<string, unknown>;
9
10
  };
10
11
  export type CapturedHttpErrorResponse = {
11
12
  status: number;
@@ -48,9 +48,21 @@ export declare function getStreamFirstEventTimeoutMs(idleTimeoutMs?: number, fal
48
48
  */
49
49
  export declare function resolveOpenAISdkRequestTimeoutMs(provider: string, streamFirstEventTimeoutOverride?: number): number | undefined;
50
50
  export type Watchdog = NodeJS.Timeout | undefined;
51
+ export interface FirstEventTimeoutFacts {
52
+ requestBytes?: number;
53
+ firstEventElapsedMs?: number;
54
+ firstEventTimeoutMs?: number;
55
+ endpointClass?: "canonical" | "custom";
56
+ retryMaxAttempts?: number;
57
+ }
51
58
  export declare class FirstEventTimeoutError extends Error {
52
59
  readonly providerCode = "stream_first_event_timeout";
53
- constructor(message: string);
60
+ readonly requestBytes?: number;
61
+ readonly firstEventElapsedMs?: number;
62
+ readonly firstEventTimeoutMs?: number;
63
+ readonly endpointClass?: "canonical" | "custom";
64
+ readonly retryMaxAttempts?: number;
65
+ constructor(message: string, facts?: FirstEventTimeoutFacts);
54
66
  }
55
67
  /**
56
68
  * Starts a watchdog that aborts a request if no first stream event arrives in time.
@@ -1,4 +1,23 @@
1
1
  export declare function repairJson(json: string): string;
2
+ /**
3
+ * First unnecessary `\uXXXX` escape in a JSON document, or `undefined` when the
4
+ * document contains none.
5
+ *
6
+ * "Unnecessary" means the escape encodes a character JSON can carry literally:
7
+ * any non-ASCII printable character. Control characters (< U+0020) MUST be
8
+ * escaped, and an unpaired surrogate CANNOT be written literally, so neither
9
+ * counts. A `\\uXXXX` sequence is a literal backslash followed by `u` — the
10
+ * intended source syntax when the model is writing code or a nested JSON
11
+ * document — and is skipped, which is why this scans the raw text with the same
12
+ * string/escape state machine as {@link repairJson} instead of using a regex.
13
+ *
14
+ * Models that spell non-ASCII text as hand-written hex instead of literal UTF-8
15
+ * mistype the digits, and every mistyped nibble silently decodes to a different
16
+ * but perfectly valid character (`\uc7a5` vs `\uc7a4`). The resulting arguments
17
+ * parse cleanly and cannot be repaired after the fact, so the escape itself is
18
+ * the only observable evidence that the payload is untrustworthy.
19
+ */
20
+ export declare function findUnnecessaryUnicodeEscape(json: string): string | undefined;
2
21
  export declare function parseJsonWithRepair<T>(json: string): T;
3
22
  /**
4
23
  * Attempts to parse potentially incomplete JSON during streaming.
@@ -18,6 +18,17 @@ export interface OAuthCallbackFlowOptions {
18
18
  * `onManualCodeInput` handler on the controller.
19
19
  */
20
20
  skipCallbackServer?: boolean;
21
+ /**
22
+ * Expected authorization-server issuer recorded from validated metadata
23
+ * (RFC 9207 / MCP 2026-07-28). When set, a present `iss` that differs
24
+ * rejects the response before any other parameter is acted on.
25
+ */
26
+ expectedIssuer?: string;
27
+ /**
28
+ * `authorization_response_iss_parameter_supported` from the same metadata.
29
+ * When true, a response WITHOUT `iss` is rejected.
30
+ */
31
+ issuerResponseIssSupported?: boolean;
21
32
  }
22
33
  /**
23
34
  * Abstract base class for OAuth flows with local callback servers.
@@ -30,6 +41,8 @@ export declare abstract class OAuthCallbackFlow {
30
41
  callbackHostname: string;
31
42
  callbackBindHostname: string;
32
43
  redirectUri?: string;
44
+ expectedIssuer?: string;
45
+ issuerResponseIssSupported?: boolean;
33
46
  constructor(ctrl: OAuthController, preferredPortOrOptions: number | OAuthCallbackFlowOptions, callbackPath?: string);
34
47
  /**
35
48
  * Generate provider-specific authorization URL.
@@ -0,0 +1,71 @@
1
+ import type { OAuthCredentials } from "./types";
2
+ interface StartDeviceAuthorizationResponse {
3
+ deviceCode: string;
4
+ userCode: string;
5
+ verificationUri: string;
6
+ verificationUriComplete?: string;
7
+ interval: number;
8
+ expiresIn: number;
9
+ }
10
+ interface CreateTokenSuccess {
11
+ accessToken: string;
12
+ tokenType: string;
13
+ expiresIn: number;
14
+ refreshToken?: string;
15
+ }
16
+ interface ClientRegistration {
17
+ clientId: string;
18
+ clientSecret: string;
19
+ expiresAt: number;
20
+ }
21
+ /**
22
+ * Register a public SSO OIDC client. Registration responses include an expiry
23
+ * timestamp (`clientSecretExpiresAt`); we cache until then to avoid re-registering
24
+ * on every login attempt.
25
+ *
26
+ * The SSO OIDC `RegisterClient` endpoint is public (no authentication required).
27
+ */
28
+ export declare function registerClient(region: string, startUrl: string, signal?: AbortSignal): Promise<ClientRegistration>;
29
+ /** Drop cached client registration — used by tests. */
30
+ export declare function clearClientRegistrationCache(): void;
31
+ /**
32
+ * Start device authorization. The SSO OIDC `StartDeviceAuthorization` endpoint
33
+ * is public (requires registered clientId/clientSecret, not SigV4).
34
+ */
35
+ export declare function startDeviceAuthorization(region: string, startUrl: string, registration: ClientRegistration, signal?: AbortSignal): Promise<StartDeviceAuthorizationResponse>;
36
+ /**
37
+ * Poll `CreateToken` until the user completes authorization or the device code
38
+ * expires. Handles `authorization_pending` (continue polling) and `slow_down`
39
+ * (increase interval) per the published SSO OIDC model.
40
+ */
41
+ export declare function pollForToken(region: string, registration: ClientRegistration, deviceCode: string, intervalSeconds: number, expiresInSeconds: number, signal?: AbortSignal): Promise<CreateTokenSuccess>;
42
+ /**
43
+ * Refresh an expired access token using the stored refresh token via
44
+ * `CreateToken` with `grantType: "refresh_token"`.
45
+ *
46
+ * Rotation is published behavior: the response includes a new `refreshToken`.
47
+ * If the server does not return a new one, the old refresh token is retained.
48
+ */
49
+ export declare function refreshKiroToken(credentials: OAuthCredentials): Promise<OAuthCredentials>;
50
+ export interface KiroLoginOptions {
51
+ onAuth: (url: string, instructions?: string) => void;
52
+ onPrompt: (prompt: {
53
+ message: string;
54
+ placeholder?: string;
55
+ allowEmpty?: boolean;
56
+ }) => Promise<string>;
57
+ onProgress?: (message: string) => void;
58
+ signal?: AbortSignal;
59
+ /** Override for tests. */
60
+ fetchImpl?: typeof globalThis.fetch;
61
+ }
62
+ export declare function loginKiro(options: KiroLoginOptions): Promise<OAuthCredentials>;
63
+ /**
64
+ * Attempt to import a cached SSO access token from `~/.aws/sso/cache/`.
65
+ * Returns the token if a valid (non-expired) one exists, otherwise undefined.
66
+ *
67
+ * This reuses the documented AWS CLI SSO cache location, not any third-party
68
+ * credential store.
69
+ */
70
+ export declare function importSsoCacheToken(): OAuthCredentials | undefined;
71
+ export {};
@@ -7,7 +7,7 @@ export type OAuthCredentials = {
7
7
  email?: string;
8
8
  accountId?: string;
9
9
  };
10
- export type OAuthProvider = "alibaba-token-plan" | "anthropic" | "bizrouter" | "mara" | "cerebras" | "cloudflare-ai-gateway" | "cursor" | "deepseek" | "deepinfra" | "fireworks" | "firepass" | "fugu" | "github-copilot" | "google-gemini-cli" | "google-antigravity" | "gitlab-duo" | "huggingface" | "kimi-code" | "kilo" | "kagi" | "litellm" | "lm-studio" | "minimax-code" | "minimax-code-cn" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "opengateway" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "synthetic" | "tavily" | "together" | "venice" | "vercel-ai-gateway" | "vllm" | "xai" | "glm-zcode" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "opencodex" | "zai";
10
+ export type OAuthProvider = "kiro" | "alibaba-token-plan" | "anthropic" | "bizrouter" | "mara" | "cerebras" | "cloudflare-ai-gateway" | "cursor" | "deepseek" | "deepinfra" | "fireworks" | "firepass" | "fugu" | "github-copilot" | "google-gemini-cli" | "google-antigravity" | "gitlab-duo" | "huggingface" | "kimi-code" | "kilo" | "kagi" | "litellm" | "lm-studio" | "minimax-code" | "minimax-code-cn" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "opengateway" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "synthetic" | "tavily" | "together" | "venice" | "vercel-ai-gateway" | "vllm" | "xai" | "glm-zcode" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "opencodex" | "zai";
11
11
  export type OAuthProviderId = OAuthProvider | (string & {});
12
12
  export type OAuthPrompt = {
13
13
  message: string;
@@ -1,8 +1,3 @@
1
- /**
2
- * Shared `host:port` parser used by the auth-broker and auth-gateway boot
3
- * paths. Centralized so the two servers can't drift on what they accept (the
4
- * gateway used to silently allow empty hostnames; this fixes it).
5
- */
6
1
  export interface ParsedBind {
7
2
  hostname: string;
8
3
  port: number;
@@ -21,3 +16,11 @@ export interface ParsedBind {
21
16
  * - non-integer / out-of-range port
22
17
  */
23
18
  export declare function parseBind(raw: string): ParsedBind;
19
+ /** True for loopback-only hostnames the auth servers may bind without credentials. */
20
+ export declare function isLoopbackHostname(hostname: string): boolean;
21
+ /**
22
+ * Fail closed when an unauthenticated auth server (empty bearer token set)
23
+ * would bind a non-loopback address: that exposes credential operations to the
24
+ * network with no proof of possession.
25
+ */
26
+ export declare function assertAuthenticatedOrLoopback(bind: ParsedBind, bearerTokenCount: number, serverName: string): void;
@@ -20,6 +20,13 @@ export interface HealedToolCall {
20
20
  readonly id: string;
21
21
  readonly name: string;
22
22
  readonly arguments: string;
23
+ /**
24
+ * Whether the raw leaked payload spelled a printable non-ASCII character as a
25
+ * `\uXXXX` escape. Captured BEFORE the normalizing round-trip below, which
26
+ * decodes escapes into literal characters and would otherwise erase the only
27
+ * evidence that the text is unverifiable.
28
+ */
29
+ readonly escapedNonAsciiArguments: boolean;
23
30
  }
24
31
  /**
25
32
  * State machine that consumes streamed text, emits visible text with all
@@ -16,6 +16,17 @@ export declare function toolChoiceRegistryKey(model: Model<Api>): string;
16
16
  export declare function getToolChoiceCapabilityOverride(model: Model<Api>): ToolChoiceSupport | undefined;
17
17
  /** Clears runtime tool-choice capability overrides for tests. */
18
18
  export declare function clearToolChoiceIncapabilityRegistryForTests(): void;
19
+ /** Overrides durable-cache dependencies for isolated tests. */
20
+ export declare function configureToolChoiceCapabilityCacheForTests(options?: {
21
+ path?: string;
22
+ now?: () => number;
23
+ beforeExpiredDelete?: () => void;
24
+ beforeMalformedDelete?: () => void;
25
+ onCacheOpen?: () => void;
26
+ simulateOperationError?: () => Error | undefined;
27
+ beforeCorruptRetire?: () => void;
28
+ beforeLockExactUnlink?: (lockPath: string) => void;
29
+ }): void;
19
30
  /** Records a discovered maximum supported tool-choice level for a model. */
20
31
  export declare function markToolChoiceIncapability(model: Model<Api>, maxSupport: ToolChoiceSupport, reason?: string): void;
21
32
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/ai",
4
- "version": "0.13.2",
4
+ "version": "0.14.0",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -40,7 +40,8 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/sdk": "^0.94.0",
42
42
  "@bufbuild/protobuf": "^2.12.0",
43
- "@gajae-code/utils": "0.13.2",
43
+ "@gajae-code/natives": "0.14.0",
44
+ "@gajae-code/utils": "0.14.0",
44
45
  "openai": "^6.36.0",
45
46
  "partial-json": "^0.1.7",
46
47
  "zod": "4.4.3"