@code-yeongyu/senpi-ai 2026.9.28 → 2026.9.29-2

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 (138) hide show
  1. package/dist/api/anthropic-messages.d.ts.map +1 -1
  2. package/dist/api/anthropic-messages.js +76 -24
  3. package/dist/api/anthropic-messages.js.map +1 -1
  4. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  5. package/dist/api/bedrock-converse-stream.js +10 -3
  6. package/dist/api/bedrock-converse-stream.js.map +1 -1
  7. package/dist/api/github-copilot-endpoint.d.ts +19 -0
  8. package/dist/api/github-copilot-endpoint.d.ts.map +1 -0
  9. package/dist/api/github-copilot-endpoint.js +48 -0
  10. package/dist/api/github-copilot-endpoint.js.map +1 -0
  11. package/dist/api/github-copilot-errors.d.ts +9 -0
  12. package/dist/api/github-copilot-errors.d.ts.map +1 -0
  13. package/dist/api/github-copilot-errors.js +78 -0
  14. package/dist/api/github-copilot-errors.js.map +1 -0
  15. package/dist/api/github-copilot-headers.d.ts +1 -0
  16. package/dist/api/github-copilot-headers.d.ts.map +1 -1
  17. package/dist/api/github-copilot-headers.js +3 -0
  18. package/dist/api/github-copilot-headers.js.map +1 -1
  19. package/dist/api/openai-completions.d.ts.map +1 -1
  20. package/dist/api/openai-completions.js +31 -20
  21. package/dist/api/openai-completions.js.map +1 -1
  22. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  23. package/dist/api/openai-responses-shared.js +4 -1
  24. package/dist/api/openai-responses-shared.js.map +1 -1
  25. package/dist/api/openai-responses.d.ts.map +1 -1
  26. package/dist/api/openai-responses.js +48 -9
  27. package/dist/api/openai-responses.js.map +1 -1
  28. package/dist/auth/helpers.d.ts +1 -0
  29. package/dist/auth/helpers.d.ts.map +1 -1
  30. package/dist/auth/helpers.js +1 -0
  31. package/dist/auth/helpers.js.map +1 -1
  32. package/dist/auth/oauth/github-copilot-model-catalog.d.ts +7 -0
  33. package/dist/auth/oauth/github-copilot-model-catalog.d.ts.map +1 -0
  34. package/dist/auth/oauth/github-copilot-model-catalog.js +46 -0
  35. package/dist/auth/oauth/github-copilot-model-catalog.js.map +1 -0
  36. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  37. package/dist/auth/oauth/github-copilot.js +24 -77
  38. package/dist/auth/oauth/github-copilot.js.map +1 -1
  39. package/dist/auth/pool/slots.d.ts.map +1 -1
  40. package/dist/auth/pool/slots.js +21 -3
  41. package/dist/auth/pool/slots.js.map +1 -1
  42. package/dist/auth/resolve.d.ts +6 -0
  43. package/dist/auth/resolve.d.ts.map +1 -1
  44. package/dist/auth/resolve.js +6 -5
  45. package/dist/auth/resolve.js.map +1 -1
  46. package/dist/auth/types.d.ts +14 -0
  47. package/dist/auth/types.d.ts.map +1 -1
  48. package/dist/auth/types.js.map +1 -1
  49. package/dist/cursor/context-limit-store.d.ts.map +1 -1
  50. package/dist/cursor/context-limit-store.js +23 -18
  51. package/dist/cursor/context-limit-store.js.map +1 -1
  52. package/dist/models.d.ts.map +1 -1
  53. package/dist/models.js +3 -2
  54. package/dist/models.js.map +1 -1
  55. package/dist/openai-responses-compat.d.ts +6 -0
  56. package/dist/openai-responses-compat.d.ts.map +1 -1
  57. package/dist/openai-responses-compat.js.map +1 -1
  58. package/dist/providers/amazon-bedrock.js +13 -13
  59. package/dist/providers/amazon-bedrock.js.map +1 -1
  60. package/dist/providers/data/.manifest.json +1 -1
  61. package/dist/providers/data/amazon-bedrock.json +1 -1
  62. package/dist/providers/data/anthropic.json +1 -1
  63. package/dist/providers/data/azure-openai-responses.json +1 -1
  64. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  65. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  66. package/dist/providers/data/github-copilot.json +1 -1
  67. package/dist/providers/data/mistral.json +1 -1
  68. package/dist/providers/data/openai.json +1 -1
  69. package/dist/providers/data/opencode-go.json +1 -1
  70. package/dist/providers/data/opencode.json +1 -1
  71. package/dist/providers/data/opengateway.json +1 -1
  72. package/dist/providers/data/openrouter.json +1 -1
  73. package/dist/providers/data/together.json +1 -1
  74. package/dist/providers/data/venice.json +1 -1
  75. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  76. package/dist/providers/devin.models.d.ts +5 -3
  77. package/dist/providers/devin.models.d.ts.map +1 -1
  78. package/dist/providers/devin.models.js +6 -5
  79. package/dist/providers/devin.models.js.map +1 -1
  80. package/dist/providers/github-copilot-limits.d.ts +11 -0
  81. package/dist/providers/github-copilot-limits.d.ts.map +1 -0
  82. package/dist/providers/github-copilot-limits.js +65 -0
  83. package/dist/providers/github-copilot-limits.js.map +1 -0
  84. package/dist/providers/github-copilot.d.ts.map +1 -1
  85. package/dist/providers/github-copilot.js +24 -3
  86. package/dist/providers/github-copilot.js.map +1 -1
  87. package/dist/providers/google-vertex.d.ts.map +1 -1
  88. package/dist/providers/google-vertex.js +3 -5
  89. package/dist/providers/google-vertex.js.map +1 -1
  90. package/dist/session-resources.d.ts.map +1 -1
  91. package/dist/session-resources.js +2 -1
  92. package/dist/session-resources.js.map +1 -1
  93. package/dist/types.d.ts +6 -0
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/types.js.map +1 -1
  96. package/dist/utils/block-symbols.d.ts +4 -0
  97. package/dist/utils/block-symbols.d.ts.map +1 -1
  98. package/dist/utils/block-symbols.js +10 -6
  99. package/dist/utils/block-symbols.js.map +1 -1
  100. package/dist/utils/claude-code-version-cache.d.ts +12 -0
  101. package/dist/utils/claude-code-version-cache.d.ts.map +1 -0
  102. package/dist/utils/claude-code-version-cache.js +60 -0
  103. package/dist/utils/claude-code-version-cache.js.map +1 -0
  104. package/dist/utils/claude-code-version.d.ts +78 -0
  105. package/dist/utils/claude-code-version.d.ts.map +1 -0
  106. package/dist/utils/claude-code-version.js +203 -0
  107. package/dist/utils/claude-code-version.js.map +1 -0
  108. package/dist/utils/cursor-context-limit.d.ts.map +1 -1
  109. package/dist/utils/cursor-context-limit.js +51 -45
  110. package/dist/utils/cursor-context-limit.js.map +1 -1
  111. package/dist/utils/github-copilot-tool-limit.d.ts +10 -0
  112. package/dist/utils/github-copilot-tool-limit.d.ts.map +1 -0
  113. package/dist/utils/github-copilot-tool-limit.js +75 -0
  114. package/dist/utils/github-copilot-tool-limit.js.map +1 -0
  115. package/dist/utils/overflow.d.ts.map +1 -1
  116. package/dist/utils/overflow.js +3 -1
  117. package/dist/utils/overflow.js.map +1 -1
  118. package/dist/utils/process-singleton.d.ts +10 -0
  119. package/dist/utils/process-singleton.d.ts.map +1 -0
  120. package/dist/utils/process-singleton.js +18 -0
  121. package/dist/utils/process-singleton.js.map +1 -0
  122. package/dist/utils/prompt-cache-ttl.d.ts +3 -1
  123. package/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
  124. package/dist/utils/prompt-cache-ttl.js +4 -2
  125. package/dist/utils/prompt-cache-ttl.js.map +1 -1
  126. package/dist/utils/retry.d.ts +3 -0
  127. package/dist/utils/retry.d.ts.map +1 -1
  128. package/dist/utils/retry.js +15 -3
  129. package/dist/utils/retry.js.map +1 -1
  130. package/dist/utils/tool-choice-fallback.d.ts +29 -0
  131. package/dist/utils/tool-choice-fallback.d.ts.map +1 -1
  132. package/dist/utils/tool-choice-fallback.js +50 -2
  133. package/dist/utils/tool-choice-fallback.js.map +1 -1
  134. package/dist/utils/tool-schema-compat.d.ts +2 -0
  135. package/dist/utils/tool-schema-compat.d.ts.map +1 -1
  136. package/dist/utils/tool-schema-compat.js +28 -9
  137. package/dist/utils/tool-schema-compat.js.map +1 -1
  138. package/package.json +2 -2
@@ -0,0 +1,78 @@
1
+ import { normalizeProviderError } from "../utils/error-body.js";
2
+ import { formatProviderRequestId } from "../utils/retry.js";
3
+ // Codes VS Code Copilot Chat treats as quota exhaustion (extensions/copilot chatMLFetcher.ts).
4
+ const QUOTA_CODES = new Set([
5
+ "quota_exceeded",
6
+ "free_quota_exceeded",
7
+ "overage_limit_reached",
8
+ "billing_not_configured",
9
+ "additional_spend_limit_reached",
10
+ ]);
11
+ function headerValue(headers, name) {
12
+ if (headers instanceof Headers)
13
+ return headers.get(name) ?? undefined;
14
+ if (typeof headers !== "object" || headers === null)
15
+ return undefined;
16
+ for (const [key, value] of Object.entries(headers)) {
17
+ if (key.toLowerCase() === name && typeof value === "string")
18
+ return value;
19
+ }
20
+ return undefined;
21
+ }
22
+ function errorCode(body) {
23
+ if (body === undefined)
24
+ return undefined;
25
+ try {
26
+ const parsed = JSON.parse(body);
27
+ if (typeof parsed !== "object" || parsed === null)
28
+ return undefined;
29
+ const nested = Reflect.get(parsed, "error");
30
+ const code = typeof nested === "object" && nested !== null ? Reflect.get(nested, "code") : Reflect.get(parsed, "code");
31
+ return typeof code === "string" ? code : undefined;
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ function isQuotaFailure(status, body, limitKey) {
38
+ const code = errorCode(body);
39
+ if (code !== undefined && QUOTA_CODES.has(code))
40
+ return true;
41
+ if (status === 402)
42
+ return true;
43
+ return status === 429 && (limitKey?.includes("quota") === true || /quota exceeded/i.test(body ?? ""));
44
+ }
45
+ /**
46
+ * A plain-language note for a GitHub Copilot HTTP failure that the SDK message alone
47
+ * leaves opaque (`403 status code (no body)`): which refusal it is, where to look, and
48
+ * the GitHub request id support needs. `undefined` for failures the message already
49
+ * explains. The wording avoids credential words so pool classification is unchanged.
50
+ */
51
+ export function describeGitHubCopilotFailure(error) {
52
+ if (!(error instanceof Error))
53
+ return undefined;
54
+ const { status, body } = normalizeProviderError(error);
55
+ if (status === undefined)
56
+ return undefined;
57
+ const headers = Reflect.get(error, "headers");
58
+ const requestId = headerValue(headers, "x-github-request-id");
59
+ const reference = requestId === undefined ? "" : ` ${formatProviderRequestId("GitHub", requestId)}.`;
60
+ if (isQuotaFailure(status, body, headerValue(headers, "x-ratelimit-exceeded"))) {
61
+ return `GitHub Copilot quota exceeded (HTTP ${status}): the plan's included usage or its additional-usage limit is used up, so premium models are refused until it resets or the limit is raised. Usage is shown at https://github.com/settings/copilot.${reference}`;
62
+ }
63
+ if (status === 421) {
64
+ return `GitHub Copilot sent this account to a different API host (HTTP 421 Misdirected Request): Business and Enterprise accounts are served from their own host, which senpi reads from the Copilot token. Run /login github-copilot so senpi stores the account's endpoint, and report it with the GitHub request id if it persists.${reference}`;
65
+ }
66
+ if (status === 403) {
67
+ const reason = body === undefined ? " with an empty body" : "";
68
+ return `GitHub Copilot refused the request (HTTP 403${reason}). Copilot checks access per model and per client; if a fresh /login github-copilot does not help, report it with the GitHub request id.${reference}`;
69
+ }
70
+ return undefined;
71
+ }
72
+ export function withGitHubCopilotFailureNote(message, provider, error) {
73
+ if (provider !== "github-copilot")
74
+ return message;
75
+ const note = describeGitHubCopilotFailure(error);
76
+ return note === undefined ? message : `${message}\n${note}`;
77
+ }
78
+ //# sourceMappingURL=github-copilot-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-copilot-errors.js","sourceRoot":"","sources":["../../src/api/github-copilot-errors.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeProviderError } from \"../utils/error-body.ts\";\nimport { formatProviderRequestId } from \"../utils/retry.ts\";\n\n// Codes VS Code Copilot Chat treats as quota exhaustion (extensions/copilot chatMLFetcher.ts).\nconst QUOTA_CODES = new Set([\n\t\"quota_exceeded\",\n\t\"free_quota_exceeded\",\n\t\"overage_limit_reached\",\n\t\"billing_not_configured\",\n\t\"additional_spend_limit_reached\",\n]);\n\nfunction headerValue(headers: unknown, name: string): string | undefined {\n\tif (headers instanceof Headers) return headers.get(name) ?? undefined;\n\tif (typeof headers !== \"object\" || headers === null) return undefined;\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (key.toLowerCase() === name && typeof value === \"string\") return value;\n\t}\n\treturn undefined;\n}\n\nfunction errorCode(body: string | undefined): string | undefined {\n\tif (body === undefined) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(body);\n\t\tif (typeof parsed !== \"object\" || parsed === null) return undefined;\n\t\tconst nested = Reflect.get(parsed, \"error\");\n\t\tconst code =\n\t\t\ttypeof nested === \"object\" && nested !== null ? Reflect.get(nested, \"code\") : Reflect.get(parsed, \"code\");\n\t\treturn typeof code === \"string\" ? code : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isQuotaFailure(status: number, body: string | undefined, limitKey: string | undefined): boolean {\n\tconst code = errorCode(body);\n\tif (code !== undefined && QUOTA_CODES.has(code)) return true;\n\tif (status === 402) return true;\n\treturn status === 429 && (limitKey?.includes(\"quota\") === true || /quota exceeded/i.test(body ?? \"\"));\n}\n\n/**\n * A plain-language note for a GitHub Copilot HTTP failure that the SDK message alone\n * leaves opaque (`403 status code (no body)`): which refusal it is, where to look, and\n * the GitHub request id support needs. `undefined` for failures the message already\n * explains. The wording avoids credential words so pool classification is unchanged.\n */\nexport function describeGitHubCopilotFailure(error: unknown): string | undefined {\n\tif (!(error instanceof Error)) return undefined;\n\tconst { status, body } = normalizeProviderError(error);\n\tif (status === undefined) return undefined;\n\tconst headers = Reflect.get(error, \"headers\");\n\tconst requestId = headerValue(headers, \"x-github-request-id\");\n\tconst reference = requestId === undefined ? \"\" : ` ${formatProviderRequestId(\"GitHub\", requestId)}.`;\n\tif (isQuotaFailure(status, body, headerValue(headers, \"x-ratelimit-exceeded\"))) {\n\t\treturn `GitHub Copilot quota exceeded (HTTP ${status}): the plan's included usage or its additional-usage limit is used up, so premium models are refused until it resets or the limit is raised. Usage is shown at https://github.com/settings/copilot.${reference}`;\n\t}\n\tif (status === 421) {\n\t\treturn `GitHub Copilot sent this account to a different API host (HTTP 421 Misdirected Request): Business and Enterprise accounts are served from their own host, which senpi reads from the Copilot token. Run /login github-copilot so senpi stores the account's endpoint, and report it with the GitHub request id if it persists.${reference}`;\n\t}\n\tif (status === 403) {\n\t\tconst reason = body === undefined ? \" with an empty body\" : \"\";\n\t\treturn `GitHub Copilot refused the request (HTTP 403${reason}). Copilot checks access per model and per client; if a fresh /login github-copilot does not help, report it with the GitHub request id.${reference}`;\n\t}\n\treturn undefined;\n}\n\nexport function withGitHubCopilotFailureNote(message: string, provider: string, error: unknown): string {\n\tif (provider !== \"github-copilot\") return message;\n\tconst note = describeGitHubCopilotFailure(error);\n\treturn note === undefined ? message : `${message}\\n${note}`;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { Message } from "../types.ts";
2
+ export declare const GITHUB_COPILOT_REJECTED_TOKEN_STATUSES: readonly number[];
2
3
  export declare function inferCopilotInitiator(messages: Message[]): "user" | "agent";
3
4
  export declare function hasCopilotVisionInput(messages: Message[]): boolean;
4
5
  export declare function buildCopilotDynamicHeaders(params: {
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot-headers.d.ts","sourceRoot":"","sources":["../../src/api/github-copilot-headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,OAAO,CAG3E;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAUlE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACnB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWzB"}
1
+ {"version":3,"file":"github-copilot-headers.d.ts","sourceRoot":"","sources":["../../src/api/github-copilot-headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,eAAO,MAAM,sCAAsC,EAAE,SAAS,MAAM,EAAe,CAAC;AAIpF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,OAAO,CAG3E;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAUlE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACnB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWzB"}
@@ -1,3 +1,6 @@
1
+ // VS Code Copilot Chat drops its Copilot token on 401/403 and fetches a new one; a
2
+ // revoked token otherwise stays in use until its own 24h expiry.
3
+ export const GITHUB_COPILOT_REJECTED_TOKEN_STATUSES = [401, 403];
1
4
  // Copilot expects X-Initiator to indicate whether the request is user-initiated
2
5
  // or agent-initiated (e.g. follow-up after assistant/tool messages).
3
6
  export function inferCopilotInitiator(messages) {
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot-headers.js","sourceRoot":"","sources":["../../src/api/github-copilot-headers.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,QAAmB;IACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACxD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,QAAmB;IACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAG1C;IACA,MAAM,OAAO,GAA2B;QACvC,aAAa,EAAE,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC;QACrD,eAAe,EAAE,oBAAoB;KACrC,CAAC;IAEF,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,wBAAwB,CAAC,GAAG,MAAM,CAAC;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC","sourcesContent":["import type { Message } from \"../types.ts\";\n\n// Copilot expects X-Initiator to indicate whether the request is user-initiated\n// or agent-initiated (e.g. follow-up after assistant/tool messages).\nexport function inferCopilotInitiator(messages: Message[]): \"user\" | \"agent\" {\n\tconst last = messages[messages.length - 1];\n\treturn last && last.role !== \"user\" ? \"agent\" : \"user\";\n}\n\n// Copilot requires Copilot-Vision-Request header when sending images\nexport function hasCopilotVisionInput(messages: Message[]): boolean {\n\treturn messages.some((msg) => {\n\t\tif (msg.role === \"user\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\tif (msg.role === \"toolResult\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\treturn false;\n\t});\n}\n\nexport function buildCopilotDynamicHeaders(params: {\n\tmessages: Message[];\n\thasImages: boolean;\n}): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t\"X-Initiator\": inferCopilotInitiator(params.messages),\n\t\t\"Openai-Intent\": \"conversation-edits\",\n\t};\n\n\tif (params.hasImages) {\n\t\theaders[\"Copilot-Vision-Request\"] = \"true\";\n\t}\n\n\treturn headers;\n}\n"]}
1
+ {"version":3,"file":"github-copilot-headers.js","sourceRoot":"","sources":["../../src/api/github-copilot-headers.ts"],"names":[],"mappings":"AAEA,mFAAmF;AACnF,iEAAiE;AACjE,MAAM,CAAC,MAAM,sCAAsC,GAAsB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpF,gFAAgF;AAChF,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,QAAmB;IACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACxD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,qBAAqB,CAAC,QAAmB;IACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAG1C;IACA,MAAM,OAAO,GAA2B;QACvC,aAAa,EAAE,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC;QACrD,eAAe,EAAE,oBAAoB;KACrC,CAAC;IAEF,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,wBAAwB,CAAC,GAAG,MAAM,CAAC;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC","sourcesContent":["import type { Message } from \"../types.ts\";\n\n// VS Code Copilot Chat drops its Copilot token on 401/403 and fetches a new one; a\n// revoked token otherwise stays in use until its own 24h expiry.\nexport const GITHUB_COPILOT_REJECTED_TOKEN_STATUSES: readonly number[] = [401, 403];\n\n// Copilot expects X-Initiator to indicate whether the request is user-initiated\n// or agent-initiated (e.g. follow-up after assistant/tool messages).\nexport function inferCopilotInitiator(messages: Message[]): \"user\" | \"agent\" {\n\tconst last = messages[messages.length - 1];\n\treturn last && last.role !== \"user\" ? \"agent\" : \"user\";\n}\n\n// Copilot requires Copilot-Vision-Request header when sending images\nexport function hasCopilotVisionInput(messages: Message[]): boolean {\n\treturn messages.some((msg) => {\n\t\tif (msg.role === \"user\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\tif (msg.role === \"toolResult\" && Array.isArray(msg.content)) {\n\t\t\treturn msg.content.some((c) => c.type === \"image\");\n\t\t}\n\t\treturn false;\n\t});\n}\n\nexport function buildCopilotDynamicHeaders(params: {\n\tmessages: Message[];\n\thasImages: boolean;\n}): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t\"X-Initiator\": inferCopilotInitiator(params.messages),\n\t\t\"Openai-Intent\": \"conversation-edits\",\n\t};\n\n\tif (params.hasImages) {\n\t\theaders[\"Copilot-Vision-Request\"] = \"true\";\n\t}\n\n\treturn headers;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"openai-completions.d.ts","sourceRoot":"","sources":["../../src/api/openai-completions.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAOX,0BAA0B,EAI1B,MAAM,sCAAsC,CAAC;AAU9C,OAAO,KAAK,EAIX,OAAO,EAIP,KAAK,EAIL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAEb,eAAe,EAMf,MAAM,aAAa,CAAC;AAOrB,OAAO,EAEN,KAAK,+BAA+B,EACpC,MAAM,8BAA8B,CAAC;AAoCtC,OAAO,EACN,0BAA0B,IAAI,SAAS,EACvC,KAAK,+BAA+B,GACpC,MAAM,8BAA8B,CAAC;AA8NtC,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,8BAA8B,CAAC;IACpE,eAAe,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;IAC1E,2KAA2K;IAC3K,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,MAAM,WAAW,iCAAiC;IACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzD;AAwKD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,wBAAwB,CA2gBjF,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAqClF,CAAC;AAkeF,wBAAgB,eAAe,CAC9B,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,+BAA+B,EACvC,OAAO,GAAE,iCAAsC,GAC7C,0BAA0B,EAAE,CAwS9B"}
1
+ {"version":3,"file":"openai-completions.d.ts","sourceRoot":"","sources":["../../src/api/openai-completions.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAOX,0BAA0B,EAI1B,MAAM,sCAAsC,CAAC;AAU9C,OAAO,KAAK,EAIX,OAAO,EAIP,KAAK,EAIL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAEb,eAAe,EAMf,MAAM,aAAa,CAAC;AAYrB,OAAO,EAEN,KAAK,+BAA+B,EACpC,MAAM,8BAA8B,CAAC;AAqCtC,OAAO,EACN,0BAA0B,IAAI,SAAS,EACvC,KAAK,+BAA+B,GACpC,MAAM,8BAA8B,CAAC;AA8NtC,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,8BAA8B,CAAC;IACpE,eAAe,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;IAC1E,2KAA2K;IAC3K,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,MAAM,WAAW,iCAAiC;IACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzD;AAwKD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,wBAAwB,CAohBjF,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAqClF,CAAC;AAofF,wBAAgB,eAAe,CAC9B,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,+BAA+B,EACvC,OAAO,GAAE,iCAAsC,GAC7C,0BAA0B,EAAE,CAmS9B"}
@@ -3,6 +3,7 @@ import { calculateCost, clampThinkingLevel, inferOpenAIThinkingLevelMap, support
3
3
  import { readProviderDiagnostic } from "../provider-diagnostic.js";
4
4
  import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
5
5
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
6
+ import { formatGitHubCopilotToolLimitError, limitGitHubCopilotTools, recordGitHubCopilotToolLimit, } from "../utils/github-copilot-tool-limit.js";
6
7
  import { shortHash } from "../utils/hash.js";
7
8
  import { headersToRecord } from "../utils/headers.js";
8
9
  import { parseStreamingJson } from "../utils/json-parse.js";
@@ -12,9 +13,10 @@ import { awaitProviderTransport, iterateProviderTransport, openAICompatibleProvi
12
13
  import { getProviderEnvValue } from "../utils/provider-env.js";
13
14
  import { retryProviderStreamRequest } from "../utils/provider-retry.js";
14
15
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
15
- import { isForcedToolChoiceUnsupportedError, omitToolChoiceParam } from "../utils/tool-choice-fallback.js";
16
+ import { sendWithForcedToolChoiceFallback } from "../utils/tool-choice-fallback.js";
16
17
  import { normalizeToolParametersForMoonshot, normalizeToolParametersForOpenAICompat, } from "../utils/tool-schema-compat.js";
17
18
  import { appendGrammarToolInputJsonDelta, createGrammarToolInputProperties, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
19
+ import { withGitHubCopilotFailureNote } from "./github-copilot-errors.js";
18
20
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
19
21
  import { resolveOpenAIClientAuth } from "./openai-client-auth.js";
20
22
  import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
@@ -334,6 +336,11 @@ export const stream = (model, context, options) => {
334
336
  params = nextParams;
335
337
  }
336
338
  params = normalizeRequestToolSchemas(params, compat);
339
+ const limitedTools = limitGitHubCopilotTools(model.provider, params.tools, params.tool_choice);
340
+ if (limitedTools.omittedCount > 0) {
341
+ params = { ...params, tools: limitedTools.tools };
342
+ recordGitHubCopilotToolLimit(output, limitedTools.omittedCount);
343
+ }
337
344
  const requestOptions = {
338
345
  ...(options?.signal ? { signal: options.signal } : {}),
339
346
  ...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
@@ -345,16 +352,15 @@ export const stream = (model, context, options) => {
345
352
  return { data: iterateProviderTransport(data, openAICompatibleProviderDiagnosticFromError), response };
346
353
  };
347
354
  const createRequest = async () => {
348
- try {
349
- return await createStream(params);
350
- }
351
- catch (error) {
352
- if (isForcedToolChoiceUnsupportedError(error, isForcedOpenAICompletionsToolChoice(params.tool_choice))) {
353
- params = omitToolChoiceParam(params);
354
- return createStream(params);
355
- }
356
- throw error;
357
- }
355
+ const sent = await sendWithForcedToolChoiceFallback({
356
+ target: model,
357
+ params,
358
+ acceptsForcedToolChoice: compat.supportsForcedToolChoice !== false,
359
+ isForced: isForcedOpenAICompletionsToolChoice,
360
+ send: createStream,
361
+ });
362
+ params = sent.params;
363
+ return sent.result;
358
364
  };
359
365
  const { stream: openaiStream } = await retryProviderStreamRequest(async () => {
360
366
  const { data, response } = await createRequest();
@@ -747,7 +753,7 @@ export const stream = (model, context, options) => {
747
753
  const providerDiagnostic = output.stopReason === "error" ? readProviderDiagnostic(error) : undefined;
748
754
  if (providerDiagnostic !== undefined)
749
755
  output.providerDiagnostic = providerDiagnostic;
750
- output.errorMessage = formatProviderError(normalizeProviderError(error));
756
+ output.errorMessage = withGitHubCopilotFailureNote(formatGitHubCopilotToolLimitError(output, formatProviderError(normalizeProviderError(error))), model.provider, error);
751
757
  // Some providers via OpenRouter give additional information in this field.
752
758
  // normalizeProviderError already stringifies the parsed body (error.error)
753
759
  // into errorMessage, so only append the raw metadata when it is not already
@@ -1156,6 +1162,16 @@ function addCacheControlToTextContent(message, cacheControl) {
1156
1162
  }
1157
1163
  return false;
1158
1164
  }
1165
+ function appendUserMessage(params, content, mergeAdjacentUserMessages) {
1166
+ const previous = params[params.length - 1];
1167
+ if (!mergeAdjacentUserMessages || previous?.role !== "user") {
1168
+ params.push({ role: "user", content });
1169
+ return;
1170
+ }
1171
+ const previousContent = typeof previous.content === "string" ? [{ type: "text", text: previous.content }] : previous.content;
1172
+ const currentContent = typeof content === "string" ? [{ type: "text", text: content }] : content;
1173
+ previous.content = [...previousContent, ...currentContent];
1174
+ }
1159
1175
  export function convertMessages(model, context, compat, options = {}) {
1160
1176
  const params = [];
1161
1177
  const normalizeToolCallId = (id) => {
@@ -1190,6 +1206,7 @@ export function convertMessages(model, context, compat, options = {}) {
1190
1206
  preserveThinking: options.preserveThinking,
1191
1207
  normalizeSameModelToolCallIds: true,
1192
1208
  });
1209
+ const mergeAdjacentUserMessages = !model.baseUrl.includes("api.openai.com");
1193
1210
  if (context.systemPrompt) {
1194
1211
  const useDeveloperRole = model.reasoning && compat.supportsDeveloperRole;
1195
1212
  const role = useDeveloperRole ? "developer" : "system";
@@ -1208,10 +1225,7 @@ export function convertMessages(model, context, compat, options = {}) {
1208
1225
  }
1209
1226
  if (msg.role === "user") {
1210
1227
  if (typeof msg.content === "string") {
1211
- params.push({
1212
- role: "user",
1213
- content: sanitizeSurrogates(msg.content),
1214
- });
1228
+ appendUserMessage(params, sanitizeSurrogates(msg.content), mergeAdjacentUserMessages);
1215
1229
  }
1216
1230
  else {
1217
1231
  const content = msg.content.map((item) => {
@@ -1232,10 +1246,7 @@ export function convertMessages(model, context, compat, options = {}) {
1232
1246
  });
1233
1247
  if (content.length === 0)
1234
1248
  continue;
1235
- params.push({
1236
- role: "user",
1237
- content,
1238
- });
1249
+ appendUserMessage(params, content, mergeAdjacentUserMessages);
1239
1250
  }
1240
1251
  }
1241
1252
  else if (msg.role === "assistant") {