@gajae-code/ai 0.16.7 → 0.17.1

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 (58) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/dist/types/auth-storage.d.ts +23 -4
  3. package/dist/types/models.d.ts +14 -0
  4. package/dist/types/provider-models/special.d.ts +12 -0
  5. package/dist/types/providers/anthropic.d.ts +1 -1
  6. package/dist/types/providers/cursor.d.ts +33 -21
  7. package/dist/types/providers/devin-acp.d.ts +157 -0
  8. package/dist/types/providers/google-gemini-headers.d.ts +1 -1
  9. package/dist/types/providers/mock.d.ts +2 -0
  10. package/dist/types/providers/openai-responses-shared.d.ts +21 -1
  11. package/dist/types/providers/register-builtins.d.ts +1 -0
  12. package/dist/types/types.d.ts +52 -11
  13. package/dist/types/utils/block-symbols.d.ts +15 -5
  14. package/dist/types/utils/fallback-transport.d.ts +4 -1
  15. package/dist/types/utils.d.ts +13 -0
  16. package/package.json +4 -3
  17. package/src/api-registry.ts +1 -0
  18. package/src/auth-broker/redact.ts +10 -2
  19. package/src/auth-gateway/server.ts +56 -3
  20. package/src/auth-storage.ts +330 -116
  21. package/src/model-manager.ts +21 -2
  22. package/src/models.d.ts +14 -0
  23. package/src/models.json +117 -0
  24. package/src/models.ts +18 -0
  25. package/src/provider-models/descriptors.ts +7 -0
  26. package/src/provider-models/openai-compat.ts +14 -0
  27. package/src/provider-models/special.ts +39 -0
  28. package/src/providers/anthropic.d.ts +1 -1
  29. package/src/providers/anthropic.ts +1 -1
  30. package/src/providers/azure-openai-responses.ts +10 -1
  31. package/src/providers/cursor.d.ts +33 -21
  32. package/src/providers/cursor.ts +2024 -508
  33. package/src/providers/devin-acp.d.ts +157 -0
  34. package/src/providers/devin-acp.ts +1103 -0
  35. package/src/providers/google-gemini-headers.d.ts +1 -1
  36. package/src/providers/google-gemini-headers.ts +1 -1
  37. package/src/providers/mock.ts +16 -1
  38. package/src/providers/openai-chat-server.ts +3 -3
  39. package/src/providers/openai-codex-responses.ts +27 -17
  40. package/src/providers/openai-responses-server.ts +5 -5
  41. package/src/providers/openai-responses-shared.d.ts +21 -1
  42. package/src/providers/openai-responses-shared.ts +60 -6
  43. package/src/providers/openai-responses.ts +10 -1
  44. package/src/providers/register-builtins.d.ts +1 -0
  45. package/src/providers/register-builtins.ts +21 -1
  46. package/src/stream.ts +14 -0
  47. package/src/types.d.ts +52 -11
  48. package/src/types.ts +70 -8
  49. package/src/utils/block-symbols.d.ts +15 -5
  50. package/src/utils/block-symbols.ts +16 -6
  51. package/src/utils/discovery/cursor.ts +3 -2
  52. package/src/utils/fallback-transport.d.ts +4 -1
  53. package/src/utils/fallback-transport.ts +12 -5
  54. package/src/utils.d.ts +13 -0
  55. package/src/utils.ts +17 -0
  56. package/dist/types/utils/codex-entitlement.d.ts +0 -22
  57. package/src/utils/codex-entitlement.d.ts +0 -22
  58. package/src/utils/codex-entitlement.ts +0 -57
@@ -4,6 +4,7 @@ import { insertModelCacheIfAbsent, readModelCache, updateModelCacheIfUnchanged,
4
4
  import { isRetiredModel, isRetiredModelKey } from "./model-retirements";
5
5
  import { applyGeneratedModelPolicies, enrichModelThinking } from "./model-thinking";
6
6
  import { type GeneratedProvider, getBundledModels } from "./models";
7
+ import { UNK_CONTEXT_WINDOW, UNK_MAX_TOKENS } from "./provider-models/openai-compat";
7
8
  import type { Api, Model, Provider } from "./types";
8
9
  import { isSafeCatalogModelId } from "./utils/discovery/openai-compatible";
9
10
 
@@ -552,6 +553,20 @@ function fingerprintStatic<TApi extends Api>(models: readonly Model<TApi>[]): st
552
553
 
553
554
  function mergeDynamicModel<TApi extends Api>(existingModel: Model<TApi>, dynamicModel: Model<TApi>): Model<TApi> {
554
555
  const supportsImage = existingModel.input.includes("image") || dynamicModel.input.includes("image");
556
+ // Before this exact Go model was curated, ID-only discovery cached the
557
+ // non-reasoning Completions defaults. A fresh authoritative cache can skip
558
+ // discovery after an upgrade, so recover its limits from reviewed static
559
+ // Responses metadata here. Do not reinterpret individual numeric limits or
560
+ // apply this correction to reviewed discovery rows or other model IDs.
561
+ const hasPreReviewMuseLimits =
562
+ existingModel.provider === "opencode-go" &&
563
+ existingModel.id === "muse-spark-1.3-contributor" &&
564
+ existingModel.api === "openai-responses" &&
565
+ existingModel.reasoning &&
566
+ dynamicModel.api === "openai-completions" &&
567
+ !dynamicModel.reasoning &&
568
+ dynamicModel.contextWindow === UNK_CONTEXT_WINDOW &&
569
+ dynamicModel.maxTokens === UNK_MAX_TOKENS;
555
570
  // The static catalog is authoritative for transport: `api` (and its
556
571
  // api-specific `baseUrl`). Dynamic discovery enumerates ids via a single
557
572
  // hardcoded api (e.g. fetchOpenAICompatibleModels always tags
@@ -579,8 +594,12 @@ function mergeDynamicModel<TApi extends Api>(existingModel: Model<TApi>, dynamic
579
594
  cacheRead: preferDiscoveryCost(dynamicModel.cost.cacheRead, existingModel.cost.cacheRead),
580
595
  cacheWrite: preferDiscoveryCost(dynamicModel.cost.cacheWrite, existingModel.cost.cacheWrite),
581
596
  },
582
- contextWindow: preferDiscoveryLimit(dynamicModel.contextWindow, existingModel.contextWindow),
583
- maxTokens: preferDiscoveryLimit(dynamicModel.maxTokens, existingModel.maxTokens),
597
+ contextWindow: hasPreReviewMuseLimits
598
+ ? existingModel.contextWindow
599
+ : preferDiscoveryLimit(dynamicModel.contextWindow, existingModel.contextWindow),
600
+ maxTokens: hasPreReviewMuseLimits
601
+ ? existingModel.maxTokens
602
+ : preferDiscoveryLimit(dynamicModel.maxTokens, existingModel.maxTokens),
584
603
  headers: dynamicModel.headers ? { ...existingModel.headers, ...dynamicModel.headers } : existingModel.headers,
585
604
  compat: dynamicModel.compat ?? existingModel.compat,
586
605
  contextPromotionTarget: dynamicModel.contextPromotionTarget ?? existingModel.contextPromotionTarget,
package/src/models.d.ts CHANGED
@@ -18,4 +18,18 @@ export declare function calculateCost<TApi extends Api>(model: Model<TApi>, usag
18
18
  * Returns false if either model is null or undefined.
19
19
  */
20
20
  export declare function modelsAreEqual<TApi extends Api>(a: Model<TApi> | null | undefined, b: Model<TApi> | null | undefined): boolean;
21
+ /**
22
+ * APIs backed by agent-level providers — currently Devin over ACP. An
23
+ * agent-level provider runs its own agent loop and owns the conversation
24
+ * history, so it refuses GJC maintenance calls (`maintenanceCall` requests such
25
+ * as compaction summaries, handoff generation, and branch summaries) instead of
26
+ * spending billed agent quota on work it cannot answer as a text model.
27
+ */
28
+ export declare const AGENT_LEVEL_PROVIDER_APIS: ReadonlySet<Api>;
29
+ /**
30
+ * Whether a model's provider can serve GJC maintenance calls at all. When this
31
+ * is false for every model a maintenance action can reach, the call can only
32
+ * report a guaranteed refusal, so callers should skip rather than surface it.
33
+ */
34
+ export declare function modelSupportsMaintenanceCalls(model: Model<Api>): boolean;
21
35
  export {};
package/src/models.json CHANGED
@@ -64677,6 +64677,52 @@
64677
64677
  "provider": "openai",
64678
64678
  "baseUrl": ""
64679
64679
  },
64680
+ "gpt-image-2.5-flare": {
64681
+ "id": "gpt-image-2.5-flare",
64682
+ "name": "GPT Image 2.5 Flare",
64683
+ "reasoning": false,
64684
+ "input": [
64685
+ "text"
64686
+ ],
64687
+ "output": [
64688
+ "text",
64689
+ "image"
64690
+ ],
64691
+ "cost": {
64692
+ "input": 0,
64693
+ "output": 0,
64694
+ "cacheRead": 0,
64695
+ "cacheWrite": 0
64696
+ },
64697
+ "contextWindow": 128000,
64698
+ "maxTokens": 16384,
64699
+ "api": "openai-responses",
64700
+ "provider": "openai",
64701
+ "baseUrl": ""
64702
+ },
64703
+ "gpt-image-2.5-sunburst": {
64704
+ "id": "gpt-image-2.5-sunburst",
64705
+ "name": "GPT Image 2.5 Sunburst",
64706
+ "reasoning": false,
64707
+ "input": [
64708
+ "text"
64709
+ ],
64710
+ "output": [
64711
+ "text",
64712
+ "image"
64713
+ ],
64714
+ "cost": {
64715
+ "input": 0,
64716
+ "output": 0,
64717
+ "cacheRead": 0,
64718
+ "cacheWrite": 0
64719
+ },
64720
+ "contextWindow": 128000,
64721
+ "maxTokens": 16384,
64722
+ "api": "openai-responses",
64723
+ "provider": "openai",
64724
+ "baseUrl": ""
64725
+ },
64680
64726
  "gpt-realtime-2.1": {
64681
64727
  "id": "gpt-realtime-2.1",
64682
64728
  "name": "GPT-Realtime-2.1",
@@ -65547,6 +65593,52 @@
65547
65593
  "api": "openai-codex-responses",
65548
65594
  "provider": "openai-codex",
65549
65595
  "baseUrl": ""
65596
+ },
65597
+ "gpt-image-2.5-flare": {
65598
+ "id": "gpt-image-2.5-flare",
65599
+ "name": "GPT Image 2.5 Flare",
65600
+ "reasoning": false,
65601
+ "input": [
65602
+ "text"
65603
+ ],
65604
+ "output": [
65605
+ "text",
65606
+ "image"
65607
+ ],
65608
+ "cost": {
65609
+ "input": 0,
65610
+ "output": 0,
65611
+ "cacheRead": 0,
65612
+ "cacheWrite": 0
65613
+ },
65614
+ "contextWindow": 128000,
65615
+ "maxTokens": 16384,
65616
+ "api": "openai-codex-responses",
65617
+ "provider": "openai-codex",
65618
+ "baseUrl": ""
65619
+ },
65620
+ "gpt-image-2.5-sunburst": {
65621
+ "id": "gpt-image-2.5-sunburst",
65622
+ "name": "GPT Image 2.5 Sunburst",
65623
+ "reasoning": false,
65624
+ "input": [
65625
+ "text"
65626
+ ],
65627
+ "output": [
65628
+ "text",
65629
+ "image"
65630
+ ],
65631
+ "cost": {
65632
+ "input": 0,
65633
+ "output": 0,
65634
+ "cacheRead": 0,
65635
+ "cacheWrite": 0
65636
+ },
65637
+ "contextWindow": 128000,
65638
+ "maxTokens": 16384,
65639
+ "api": "openai-codex-responses",
65640
+ "provider": "openai-codex",
65641
+ "baseUrl": ""
65550
65642
  }
65551
65643
  },
65552
65644
  "opencode": {
@@ -66470,6 +66562,31 @@
66470
66562
  "maxLevel": "xhigh"
66471
66563
  }
66472
66564
  },
66565
+ "muse-spark-1.3-contributor": {
66566
+ "id": "muse-spark-1.3-contributor",
66567
+ "name": "Muse Spark 1.3 Contributor",
66568
+ "api": "openai-responses",
66569
+ "provider": "opencode-go",
66570
+ "baseUrl": "https://opencode.ai/zen/go/v1",
66571
+ "reasoning": true,
66572
+ "input": [
66573
+ "text",
66574
+ "image"
66575
+ ],
66576
+ "cost": {
66577
+ "input": 0.1,
66578
+ "output": 0.2,
66579
+ "cacheRead": 0.002,
66580
+ "cacheWrite": 0
66581
+ },
66582
+ "contextWindow": 1048576,
66583
+ "maxTokens": 131072,
66584
+ "thinking": {
66585
+ "mode": "effort",
66586
+ "minLevel": "minimal",
66587
+ "maxLevel": "xhigh"
66588
+ }
66589
+ },
66473
66590
  "qwen3.8-flash": {
66474
66591
  "id": "qwen3.8-flash",
66475
66592
  "name": "Qwen3.8 Flash",
package/src/models.ts CHANGED
@@ -124,3 +124,21 @@ export function modelsAreEqual<TApi extends Api>(
124
124
  if (!a || !b) return false;
125
125
  return a.id === b.id && a.provider === b.provider;
126
126
  }
127
+
128
+ /**
129
+ * APIs backed by agent-level providers — currently Devin over ACP. An
130
+ * agent-level provider runs its own agent loop and owns the conversation
131
+ * history, so it refuses GJC maintenance calls (`maintenanceCall` requests such
132
+ * as compaction summaries, handoff generation, and branch summaries) instead of
133
+ * spending billed agent quota on work it cannot answer as a text model.
134
+ */
135
+ export const AGENT_LEVEL_PROVIDER_APIS: ReadonlySet<Api> = new Set<Api>(["devin-acp"]);
136
+
137
+ /**
138
+ * Whether a model's provider can serve GJC maintenance calls at all. When this
139
+ * is false for every model a maintenance action can reach, the call can only
140
+ * report a guaranteed refusal, so callers should skip rather than surface it.
141
+ */
142
+ export function modelSupportsMaintenanceCalls(model: Model<Api>): boolean {
143
+ return !AGENT_LEVEL_PROVIDER_APIS.has(model.api);
144
+ }
@@ -53,6 +53,7 @@ import {
53
53
  } from "./openai-compat";
54
54
  import {
55
55
  cursorModelManagerOptions,
56
+ devinModelManagerOptions,
56
57
  glmZcodeModelManagerOptions,
57
58
  jetbrainsJunieModelManagerOptions,
58
59
  kiroModelManagerOptions,
@@ -364,6 +365,12 @@ export const PROVIDER_DESCRIPTORS: readonly ProviderDescriptor[] = [
364
365
  catalog("GLM ZCode (unofficial)", ["GLM_ZCODE_API_KEY"], { oauthProvider: "glm-zcode" }),
365
366
  ),
366
367
  descriptor("jetbrains-junie", "claude-sonnet-4-6", config => jetbrainsJunieModelManagerOptions(config)),
368
+ // `adaptive` is Devin's documented recommended default (docs.devin.ai/cli/adaptive).
369
+ // Enterprise organizations may disable it; the account's real model list comes from
370
+ // the ACP session's model config option.
371
+ descriptor("devin", "adaptive", () => devinModelManagerOptions(), {
372
+ allowUnauthenticated: true,
373
+ }),
367
374
  descriptor("github-copilot", "gpt-4o", config => githubCopilotModelManagerOptions(config)),
368
375
  descriptor("google", "gemini-2.5-pro", config => googleModelManagerOptions(config)),
369
376
  catalogDescriptor(
@@ -2405,6 +2405,7 @@ const OPENCODE_GO_MESSAGES_MODEL_IDS = [
2405
2405
  const OPENCODE_GO_API_OVERRIDES: Readonly<Record<string, Api>> = {
2406
2406
  ...Object.fromEntries(OPENCODE_GO_CHAT_COMPLETIONS_MODEL_IDS.map(id => [id, "openai-completions"])),
2407
2407
  ...Object.fromEntries(OPENCODE_GO_MESSAGES_MODEL_IDS.map(id => [id, "anthropic-messages"])),
2408
+ "muse-spark-1.3-contributor": "openai-responses",
2408
2409
  } as Record<string, Api>;
2409
2410
  // OpenCode Go has a provider-specific endpoint table at
2410
2411
  // https://opencode.ai/docs/go/#endpoints. Keep routing aligned with that table:
@@ -2873,6 +2874,19 @@ const OPENCODE_GO_OFFICIAL_MODELS: Readonly<Record<string, OpenCodeGoOfficialMod
2873
2874
  reasoning: true,
2874
2875
  cost: { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 },
2875
2876
  },
2877
+ // Provisional models.dev contract, not a published Meta 1.3 specification:
2878
+ // providers/opencode-go/models/muse-spark-1.3-contributor.toml
2879
+ // blob df9405a4823feeaa5705558807515bdd2d930da2 explicitly inherits 1.2 capabilities.
2880
+ // Its minimal..xhigh efforts match existing Responses inference. Advertise
2881
+ // only GJC's supported text/image inputs; pricing is from opencode.ai/docs/go.
2882
+ "muse-spark-1.3-contributor": {
2883
+ name: "Muse Spark 1.3 Contributor",
2884
+ contextWindow: 1_048_576,
2885
+ maxTokens: 131_072,
2886
+ input: ["text", "image"],
2887
+ reasoning: true,
2888
+ cost: { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 },
2889
+ },
2876
2890
  "qwen3.8-flash": {
2877
2891
  name: "Qwen3.8 Flash",
2878
2892
  contextWindow: 1_000_000,
@@ -5,6 +5,7 @@ import type { ModelManagerOptions } from "../model-manager";
5
5
  import { buildZCodeSourceHeaders, resolveGlmZcodeAnthropicBaseUrl } from "../providers/anthropic";
6
6
  import { fetchKiroApiModels, isKiroApiKey, kiroApiStaticModels } from "../providers/kiro-api-key";
7
7
  import { fetchOpenCodexModels, OPENCODEX_MODEL_CACHE_TTL_MS } from "../providers/openai-opencodex-responses";
8
+ import type { Model } from "../types";
8
9
  import { fetchCodexModels } from "../utils/discovery/codex";
9
10
  import { fetchOpenAICompatibleModels } from "../utils/discovery/openai-compatible";
10
11
  import { createBundledReferenceMap } from "./bundled-references";
@@ -16,6 +17,44 @@ export function openCodexModelManagerOptions(): ModelManagerOptions<"openai-resp
16
17
  };
17
18
  }
18
19
 
20
+ // ---------------------------------------------------------------------------
21
+ // Devin CLI (ACP)
22
+ // ---------------------------------------------------------------------------
23
+
24
+ /**
25
+ * Lazy handle to the ACP provider module. The ACP SDK must stay out of the
26
+ * catalog/startup import graph, so it is only required when Devin model
27
+ * discovery actually runs.
28
+ */
29
+ interface DevinAcpDiscoveryModule {
30
+ fetchDevinAcpModels: (config?: {
31
+ cliPath?: string;
32
+ cliArgs?: readonly string[];
33
+ cwd?: string;
34
+ }) => Promise<Model<"devin-acp">[] | null>;
35
+ }
36
+
37
+ const devinAcpDiscovery = once(() => require("../providers/devin-acp") as DevinAcpDiscoveryModule);
38
+
39
+ export interface DevinModelManagerConfig {
40
+ cliPath?: string;
41
+ cliArgs?: readonly string[];
42
+ cwd?: string;
43
+ }
44
+
45
+ /**
46
+ * Devin models come from the ACP session's own `model` config option: the
47
+ * account and enterprise allowlists are authoritative there, and ACP is the
48
+ * documented programmatic surface (`devin acp`). Discovery fails closed to "no
49
+ * models" when the CLI is missing or unauthenticated.
50
+ */
51
+ export function devinModelManagerOptions(config: DevinModelManagerConfig = {}): ModelManagerOptions<"devin-acp"> {
52
+ return {
53
+ providerId: "devin",
54
+ fetchDynamicModels: () => devinAcpDiscovery().fetchDevinAcpModels(config),
55
+ };
56
+ }
57
+
19
58
  // ---------------------------------------------------------------------------
20
59
  // OpenAI code provider
21
60
  // ---------------------------------------------------------------------------
@@ -107,7 +107,7 @@ export interface CpaToolAliasRestoreFailure {
107
107
  */
108
108
  export declare function parseCpaToolAliasRestoreFailure(error: unknown): CpaToolAliasRestoreFailure | undefined;
109
109
  export declare function isCpaToolAliasRestoreFailure(error: unknown): boolean;
110
- export declare const claudeCodeVersion = "2.1.257";
110
+ export declare const claudeCodeVersion = "2.1.273";
111
111
  export declare const claudeCodeEntrypoint = "sdk-cli";
112
112
  export declare const claudeToolPrefix: string;
113
113
  export declare const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
@@ -833,7 +833,7 @@ function getCacheControl(
833
833
  }
834
834
 
835
835
  // Stealth mode: Mimic Anthropic Code headers and tool prefixing.
836
- export const claudeCodeVersion = "2.1.257";
836
+ export const claudeCodeVersion = "2.1.273";
837
837
  export const claudeCodeEntrypoint = "sdk-cli";
838
838
  export const claudeToolPrefix: string = "proxy_";
839
839
  export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
@@ -50,6 +50,8 @@ import {
50
50
  isOpenAIResponsesProgressEvent,
51
51
  normalizeResponsesToolCallIdForTransform,
52
52
  processResponsesStream,
53
+ responsesStreamFailureCode,
54
+ unexpectedResponsesStreamEndError,
53
55
  } from "./openai-responses-shared";
54
56
  import { transformMessages } from "./transform-messages";
55
57
 
@@ -177,7 +179,7 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
177
179
  const firstEventTimeoutMs = options?.streamFirstEventTimeoutMs ?? getStreamFirstEventTimeoutMs(idleTimeoutMs);
178
180
  stream.push({ type: "start", partial: output });
179
181
 
180
- await processResponsesStream(
182
+ const sawTerminalEvent = await processResponsesStream(
181
183
  iterateWithIdleTimeout(openaiStream, {
182
184
  firstItemTimeoutMs: firstEventTimeoutMs,
183
185
  firstItemErrorMessage: AZURE_OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE,
@@ -210,6 +212,11 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
210
212
  throw new Error(output.errorMessage ?? "An unknown error occurred");
211
213
  }
212
214
 
215
+ // The Responses SSE contract always ends with a terminal event. A stream
216
+ // that closes without one is an interrupted upstream, not a completed
217
+ // turn, and must not surface as a content-free success.
218
+ if (!sawTerminalEvent) throw unexpectedResponsesStreamEndError();
219
+
213
220
  output.duration = Date.now() - startTime;
214
221
  if (firstTokenTime) output.ttft = firstTokenTime - startTime;
215
222
  stream.push({ type: "done", reason: output.stopReason, message: output });
@@ -224,6 +231,8 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
224
231
  output.stopReason = abortTracker.wasCallerAbort() ? "aborted" : "error";
225
232
  output.errorStatus = extractHttpStatusFromError(firstEventTimeoutError ?? normalizedError);
226
233
  output.transportFailure = transportFailureFacts(firstEventTimeoutError ?? normalizedError);
234
+ const streamFailureCode = responsesStreamFailureCode(firstEventTimeoutError ?? normalizedError);
235
+ if (streamFailureCode !== undefined) output.errorCode = streamFailureCode;
227
236
  output.errorMessage =
228
237
  firstEventTimeoutError?.message ?? (await finalizeErrorMessage(normalizedError, rawRequestDump));
229
238
  output.duration = Date.now() - startTime;
@@ -1,9 +1,9 @@
1
+ import http2 from "node:http2";
1
2
  import { type JsonValue } from "@bufbuild/protobuf";
2
- import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, ToolCall, ToolResultMessage, Usage } from "../types";
3
- import { kCursorExecResolved } from "../utils/block-symbols";
3
+ import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, Tool, ToolCall, ToolResultMessage, Usage } from "../types";
4
+ import { kProviderResolvedToolCall } from "../utils/block-symbols";
4
5
  import { CURSOR_CLIENT_VERSION } from "./cursor/client-version";
5
6
  import type { CursorRule, RequestedModel_ModelParameterbytes } from "./cursor/gen/agent_pb";
6
- import { type ConversationStateStructure } from "./cursor/gen/agent_pb";
7
7
  export declare const CURSOR_API_URL = "https://api2.cursor.sh";
8
8
  export { CURSOR_CLIENT_VERSION };
9
9
  /** Drop all cached state + blob bytes for a conversation (F15 bound + session-teardown hook). */
@@ -14,6 +14,24 @@ export interface CursorOptions extends StreamOptions {
14
14
  execHandlers?: CursorExecHandlers;
15
15
  onToolResult?: CursorToolResultHandler;
16
16
  }
17
+ /** Exported for deterministic validation of fragmented Connect progress. */
18
+ export declare function isPlausibleCursorConnectProgressForTest(bufferedLength: number, flags: number, messageLength?: number): boolean;
19
+ /** Exported for deterministic coverage of the Cursor exec-budget derivation. */
20
+ export declare function cursorExecDeadlineMsForTest(idleTimeoutMs: number | undefined): number;
21
+ /** Settlement proof for a started non-abortable Cursor exec. */
22
+ export interface CursorNonAbortableSettlement {
23
+ /** Resolves when the marked mutation settles; never rejects. */
24
+ settled: Promise<void>;
25
+ }
26
+ /** Exported for production-bridge coverage of non-abortable terminal ordering. */
27
+ export declare function runWithCursorExecDeadlineForTest<T>(operation: (signal: AbortSignal, markNonAbortable: () => void) => Promise<T>, signal: AbortSignal | undefined, deadlineMs: number): Promise<T>;
28
+ /** Await request-side END_STREAM under the same bounded teardown contract used by Cursor streams. */
29
+ export declare function endCursorRequestForTest(request: Pick<http2.ClientHttp2Stream, "end">, timeoutMs?: number): Promise<boolean>;
30
+ /** Exported for deterministic coverage of successful writer teardown ordering. */
31
+ export declare function waitForCursorWritesForTest(request: http2.ClientHttp2Stream | null, timeoutMs?: number): Promise<void>;
32
+ export declare function waitForCursorWriteDrainForTest(request: http2.ClientHttp2Stream, timeoutMs?: number): Promise<void>;
33
+ /** Exported for deterministic coverage of the post-fence write race. */
34
+ export declare function writeCursorFrameForTest(request: http2.ClientHttp2Stream, frame: Uint8Array): boolean;
17
35
  /** Build the ordered global USER rules Cursor expects for the current system prompt. */
18
36
  export declare function buildCursorRequestContextRules(systemPrompt: readonly string[] | undefined): CursorRule[];
19
37
  export interface CursorWireModelResolution {
@@ -30,42 +48,34 @@ type ToolCallState = ToolCall & {
30
48
  index: number;
31
49
  partialJson?: string;
32
50
  kind: "mcp" | "todo_write" | "native" | "cursor-exec";
33
- [kCursorExecResolved]?: true;
51
+ [kProviderResolvedToolCall]?: true;
34
52
  };
35
53
  interface UsageState {
36
54
  sawTokenDelta: boolean;
37
- /**
38
- * Latest `ConversationTokenDetails.used_tokens`: the whole conversation's
39
- * token consumption as counted by Cursor, not this turn's output.
40
- */
41
55
  conversationUsedTokens: number;
42
- /** Output tokens already included in the latest checkpoint snapshot. */
43
56
  checkpointOutputTokens: number;
44
- /** Whether the current stream received a checkpoint, including an explicit zero. */
45
57
  hasConversationCheckpoint: boolean;
46
- pendingCheckpoint?: ConversationStateStructure;
47
58
  }
59
+ export declare function storeCursorBlobForTest(blobStore: Map<string, Uint8Array>, blobId: Uint8Array, blobData: Uint8Array, limits: {
60
+ maxBytes: number;
61
+ }): boolean;
48
62
  /** Exported for tests: verifies handler is invoked with correct `this` when passed as bound. */
49
- 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<{
63
+ export declare function resolveExecHandler<TArgs, TResult>(args: TArgs, handler: ((args: TArgs, signal?: AbortSignal, markNonAbortable?: () => void) => Promise<CursorExecHandlerResult<TResult>>) | undefined, onToolResult: CursorToolResultHandler | undefined, buildFromToolResult: (toolResult: ToolResultMessage) => TResult, buildRejected: (reason: string) => TResult, buildError: (error: string) => TResult, signal?: AbortSignal, markNonAbortable?: () => void): Promise<{
50
64
  execResult: TResult;
51
65
  toolResult?: ToolResultMessage;
52
66
  }>;
53
67
  /** Exported for deterministic coverage of ordered server-message handling. */
54
- export declare function createCursorMessageQueueForTest(onError?: (error: unknown) => void): {
55
- enqueue(handler: () => void | Promise<void>): Promise<void>;
68
+ export declare function createCursorMessageQueueForTest(onError?: (error: unknown) => void, maxPendingBytes?: number): {
69
+ enqueue(handler: () => void | Promise<void>, byteSize?: number): Promise<void>;
56
70
  drain(): Promise<void>;
71
+ pending(): number;
72
+ pendingBytes(): number;
57
73
  };
58
74
  /** Exported for direct regression coverage of the JSON-safety boundary. */
59
75
  export declare function cursorJsonSafeValueForTest(value: unknown): unknown;
60
76
  export declare function buildNativeToolCallBlock(toolCall: Record<string, unknown>, callId: string, index: number): ToolCallState | null;
61
- /**
62
- * Cursor streams output tokens as deltas and reports whole-conversation
63
- * consumption separately as `ConversationTokenDetails.used_tokens`. Derive
64
- * prompt tokens from the difference so context accounting and compaction see a
65
- * real prompt size instead of zero.
66
- */
77
+ /** Derive prompt usage from Cursor's whole-conversation checkpoint total. */
67
78
  export declare function finalizeCursorUsage(output: AssistantMessage, usageState: UsageState): void;
68
- /** Exposes {@link finalizeCursorUsage} for tests without a live HTTP/2 stream. */
69
79
  export declare function finalizeCursorUsageForTest(usedTokens: number, outputTokens: number, options?: {
70
80
  checkpointOutputTokens?: number;
71
81
  hasConversationCheckpoint?: boolean;
@@ -91,6 +101,8 @@ export declare function finalizeCursorUsageForTest(usedTokens: number, outputTok
91
101
  * an empty `rootPromptMessagesJson` head.
92
102
  */
93
103
  export declare function buildCursorSystemPromptJsons(systemPrompt: readonly string[] | undefined, modelId?: string): string[];
104
+ /** Exported for regression coverage of the tool usage-cache identity boundary. */
105
+ export declare function buildCursorUsageToolsKeyForTest(tools: Tool[]): string;
94
106
  /** Exported for tests: decodes Cursor history blobs built from conversation messages. */
95
107
  export declare function buildCursorHistoryForTest(messages: Message[]): {
96
108
  rootPromptMessagesJson: unknown[];