@mastra/code-sdk 1.0.3-alpha.0 → 1.1.0-alpha.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 (258) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/acp/agent.js +155 -167
  3. package/dist/acp/agent.js.map +1 -1
  4. package/dist/acp/event-mapper.js +193 -168
  5. package/dist/acp/event-mapper.js.map +1 -1
  6. package/dist/acp/index.js +60 -49
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/acp/server.js +31 -30
  9. package/dist/acp/server.js.map +1 -1
  10. package/dist/acp.js +1 -4
  11. package/dist/agents/credential-resolver.js +40 -29
  12. package/dist/agents/credential-resolver.js.map +1 -1
  13. package/dist/agents/custom-provider-source.js +16 -9
  14. package/dist/agents/custom-provider-source.js.map +1 -1
  15. package/dist/agents/instructions.js +27 -36
  16. package/dist/agents/instructions.js.map +1 -1
  17. package/dist/agents/mastracode-gateway.js +355 -428
  18. package/dist/agents/mastracode-gateway.js.map +1 -1
  19. package/dist/agents/memory.js +87 -78
  20. package/dist/agents/memory.js.map +1 -1
  21. package/dist/agents/model.js +114 -119
  22. package/dist/agents/model.js.map +1 -1
  23. package/dist/agents/modes/build.js +12 -13
  24. package/dist/agents/modes/build.js.map +1 -1
  25. package/dist/agents/modes/explore.js +14 -13
  26. package/dist/agents/modes/explore.js.map +1 -1
  27. package/dist/agents/modes/plan.js +14 -15
  28. package/dist/agents/modes/plan.js.map +1 -1
  29. package/dist/agents/prompts/agent-instructions.js +78 -69
  30. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  31. package/dist/agents/prompts/build.js +19 -16
  32. package/dist/agents/prompts/build.js.map +1 -1
  33. package/dist/agents/prompts/fast.js +8 -4
  34. package/dist/agents/prompts/fast.js.map +1 -1
  35. package/dist/agents/prompts/index.js +48 -47
  36. package/dist/agents/prompts/index.js.map +1 -1
  37. package/dist/agents/prompts/model.js +6 -5
  38. package/dist/agents/prompts/model.js.map +1 -1
  39. package/dist/agents/prompts/plan.js +20 -16
  40. package/dist/agents/prompts/plan.js.map +1 -1
  41. package/dist/agents/prompts/tool-guidance.js +77 -109
  42. package/dist/agents/prompts/tool-guidance.js.map +1 -1
  43. package/dist/agents/sandbox-filesystem.js +245 -259
  44. package/dist/agents/sandbox-filesystem.js.map +1 -1
  45. package/dist/agents/sandbox-reattach.js +9 -11
  46. package/dist/agents/sandbox-reattach.js.map +1 -1
  47. package/dist/agents/thread-caveman-state.js +92 -81
  48. package/dist/agents/thread-caveman-state.js.map +1 -1
  49. package/dist/agents/tool-availability.js +62 -80
  50. package/dist/agents/tool-availability.js.map +1 -1
  51. package/dist/agents/tools.js +99 -139
  52. package/dist/agents/tools.js.map +1 -1
  53. package/dist/agents/workspace.js +232 -225
  54. package/dist/agents/workspace.js.map +1 -1
  55. package/dist/analytics.js +139 -128
  56. package/dist/analytics.js.map +1 -1
  57. package/dist/auth/authorization-input.js +39 -25
  58. package/dist/auth/authorization-input.js.map +1 -1
  59. package/dist/auth/device-code.js +112 -80
  60. package/dist/auth/device-code.js.map +1 -1
  61. package/dist/auth/index.js +2 -9
  62. package/dist/auth/pkce.js +27 -16
  63. package/dist/auth/pkce.js.map +1 -1
  64. package/dist/auth/providers/anthropic.js +107 -93
  65. package/dist/auth/providers/anthropic.js.map +1 -1
  66. package/dist/auth/providers/github-copilot.js +304 -293
  67. package/dist/auth/providers/github-copilot.js.map +1 -1
  68. package/dist/auth/providers/openai-codex.js +442 -460
  69. package/dist/auth/providers/openai-codex.js.map +1 -1
  70. package/dist/auth/providers/xai.js +179 -145
  71. package/dist/auth/providers/xai.js.map +1 -1
  72. package/dist/auth/storage.js +182 -175
  73. package/dist/auth/storage.js.map +1 -1
  74. package/dist/auth/types.js +1 -1
  75. package/dist/clipboard/index.js +206 -172
  76. package/dist/clipboard/index.js.map +1 -1
  77. package/dist/constants.js +11 -16
  78. package/dist/constants.js.map +1 -1
  79. package/dist/error-classification.js +19 -11
  80. package/dist/error-classification.js.map +1 -1
  81. package/dist/evals/context-builder.js +145 -104
  82. package/dist/evals/context-builder.js.map +1 -1
  83. package/dist/evals/index.js +4 -7
  84. package/dist/evals/scorers/classify-command.js +62 -41
  85. package/dist/evals/scorers/classify-command.js.map +1 -1
  86. package/dist/evals/scorers/efficiency.js +239 -181
  87. package/dist/evals/scorers/efficiency.js.map +1 -1
  88. package/dist/evals/scorers/extract-tools.js +51 -47
  89. package/dist/evals/scorers/extract-tools.js.map +1 -1
  90. package/dist/evals/scorers/index.js +1 -5
  91. package/dist/evals/scorers/outcome.js +321 -212
  92. package/dist/evals/scorers/outcome.js.map +1 -1
  93. package/dist/goal-manager.js +266 -261
  94. package/dist/goal-manager.js.map +1 -1
  95. package/dist/goal-signal.js +14 -13
  96. package/dist/goal-signal.js.map +1 -1
  97. package/dist/headless/cli.js +153 -154
  98. package/dist/headless/cli.js.map +1 -1
  99. package/dist/headless/flags.js +180 -178
  100. package/dist/headless/flags.js.map +1 -1
  101. package/dist/headless/format.js +72 -71
  102. package/dist/headless/format.js.map +1 -1
  103. package/dist/headless/index.js +5 -31
  104. package/dist/headless/policy.js +35 -31
  105. package/dist/headless/policy.js.map +1 -1
  106. package/dist/headless/run-mc.js +332 -334
  107. package/dist/headless/run-mc.js.map +1 -1
  108. package/dist/headless/types.js +16 -7
  109. package/dist/headless/types.js.map +1 -1
  110. package/dist/hooks/config.js +56 -61
  111. package/dist/hooks/config.js.map +1 -1
  112. package/dist/hooks/executor.js +118 -116
  113. package/dist/hooks/executor.js.map +1 -1
  114. package/dist/hooks/index.js +4 -15
  115. package/dist/hooks/manager.js +221 -280
  116. package/dist/hooks/manager.js.map +1 -1
  117. package/dist/hooks/types.js +5 -4
  118. package/dist/hooks/types.js.map +1 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +632 -712
  121. package/dist/index.js.map +1 -1
  122. package/dist/ipc/ipc-reporter.js +19 -22
  123. package/dist/ipc/ipc-reporter.js.map +1 -1
  124. package/dist/lsp/client.js +248 -306
  125. package/dist/lsp/client.js.map +1 -1
  126. package/dist/lsp/index.js +1 -6
  127. package/dist/lsp/language.js +39 -43
  128. package/dist/lsp/language.js.map +1 -1
  129. package/dist/lsp/manager.js +78 -93
  130. package/dist/lsp/manager.js.map +1 -1
  131. package/dist/lsp/server.js +151 -120
  132. package/dist/lsp/server.js.map +1 -1
  133. package/dist/lsp/workspace.js +50 -47
  134. package/dist/lsp/workspace.js.map +1 -1
  135. package/dist/mcp/config.js +210 -199
  136. package/dist/mcp/config.js.map +1 -1
  137. package/dist/mcp/index.js +2 -9
  138. package/dist/mcp/manager.js +482 -475
  139. package/dist/mcp/manager.js.map +1 -1
  140. package/dist/mcp/types.js +1 -1
  141. package/dist/onboarding/custom-packs.js +14 -17
  142. package/dist/onboarding/custom-packs.js.map +1 -1
  143. package/dist/onboarding/custom-providers.js +22 -20
  144. package/dist/onboarding/custom-providers.js.map +1 -1
  145. package/dist/onboarding/index.d.ts +1 -1
  146. package/dist/onboarding/index.d.ts.map +1 -1
  147. package/dist/onboarding/index.js +3 -21
  148. package/dist/onboarding/om-settings.d.ts +5 -0
  149. package/dist/onboarding/om-settings.d.ts.map +1 -1
  150. package/dist/onboarding/om-settings.js +41 -20
  151. package/dist/onboarding/om-settings.js.map +1 -1
  152. package/dist/onboarding/packs.d.ts +4 -6
  153. package/dist/onboarding/packs.d.ts.map +1 -1
  154. package/dist/onboarding/packs.js +154 -105
  155. package/dist/onboarding/packs.js.map +1 -1
  156. package/dist/onboarding/settings.js +575 -517
  157. package/dist/onboarding/settings.js.map +1 -1
  158. package/dist/permissions.js +112 -99
  159. package/dist/permissions.js.map +1 -1
  160. package/dist/plugin.js +21 -23
  161. package/dist/plugin.js.map +1 -1
  162. package/dist/plugins/dependencies.js +64 -87
  163. package/dist/plugins/dependencies.js.map +1 -1
  164. package/dist/plugins/install.js +159 -169
  165. package/dist/plugins/install.js.map +1 -1
  166. package/dist/plugins/loader.js +170 -185
  167. package/dist/plugins/loader.js.map +1 -1
  168. package/dist/plugins/manager.js +374 -380
  169. package/dist/plugins/manager.js.map +1 -1
  170. package/dist/plugins/manifest.js +38 -63
  171. package/dist/plugins/manifest.js.map +1 -1
  172. package/dist/plugins/package-link.js +48 -64
  173. package/dist/plugins/package-link.js.map +1 -1
  174. package/dist/plugins/paths.js +18 -19
  175. package/dist/plugins/paths.js.map +1 -1
  176. package/dist/plugins/registry.js +97 -81
  177. package/dist/plugins/registry.js.map +1 -1
  178. package/dist/plugins/scaffold.js +62 -98
  179. package/dist/plugins/scaffold.js.map +1 -1
  180. package/dist/plugins/types.js +1 -1
  181. package/dist/processors/plan-rejection-abort.js +45 -44
  182. package/dist/processors/plan-rejection-abort.js.map +1 -1
  183. package/dist/providers/amazon-bedrock-gateway.js +134 -125
  184. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  185. package/dist/providers/amazon-bedrock.js +68 -47
  186. package/dist/providers/amazon-bedrock.js.map +1 -1
  187. package/dist/providers/claude-max.js +153 -133
  188. package/dist/providers/claude-max.js.map +1 -1
  189. package/dist/providers/github-copilot.js +232 -230
  190. package/dist/providers/github-copilot.js.map +1 -1
  191. package/dist/providers/openai-codex.js +271 -260
  192. package/dist/providers/openai-codex.js.map +1 -1
  193. package/dist/providers/xai.js +57 -50
  194. package/dist/providers/xai.js.map +1 -1
  195. package/dist/schema.js +91 -89
  196. package/dist/schema.js.map +1 -1
  197. package/dist/theme-palette.js +18 -14
  198. package/dist/theme-palette.js.map +1 -1
  199. package/dist/tool-names.js +46 -37
  200. package/dist/tool-names.js.map +1 -1
  201. package/dist/tools/index.js +2 -8
  202. package/dist/tools/request-sandbox-access.js +71 -80
  203. package/dist/tools/request-sandbox-access.js.map +1 -1
  204. package/dist/tools/utils.js +26 -16
  205. package/dist/tools/utils.js.map +1 -1
  206. package/dist/tools/web-search.js +51 -55
  207. package/dist/tools/web-search.js.map +1 -1
  208. package/dist/utils/binaries.js +65 -61
  209. package/dist/utils/binaries.js.map +1 -1
  210. package/dist/utils/debug-log.js +51 -47
  211. package/dist/utils/debug-log.js.map +1 -1
  212. package/dist/utils/errors.js +157 -192
  213. package/dist/utils/errors.js.map +1 -1
  214. package/dist/utils/gateway-sync.js +49 -37
  215. package/dist/utils/gateway-sync.js.map +1 -1
  216. package/dist/utils/path-security.js +7 -6
  217. package/dist/utils/path-security.js.map +1 -1
  218. package/dist/utils/plan-diff.js +58 -39
  219. package/dist/utils/plan-diff.js.map +1 -1
  220. package/dist/utils/plans.js +92 -74
  221. package/dist/utils/plans.js.map +1 -1
  222. package/dist/utils/project.js +318 -256
  223. package/dist/utils/project.js.map +1 -1
  224. package/dist/utils/signals-pubsub.js +117 -99
  225. package/dist/utils/signals-pubsub.js.map +1 -1
  226. package/dist/utils/slash-command-loader.js +118 -154
  227. package/dist/utils/slash-command-loader.js.map +1 -1
  228. package/dist/utils/slash-command-processor.js +92 -83
  229. package/dist/utils/slash-command-processor.js.map +1 -1
  230. package/dist/utils/stdin-pipe.js +87 -63
  231. package/dist/utils/stdin-pipe.js.map +1 -1
  232. package/dist/utils/storage-factory.js +104 -84
  233. package/dist/utils/storage-factory.js.map +1 -1
  234. package/dist/utils/storage-maintenance.js +249 -211
  235. package/dist/utils/storage-maintenance.js.map +1 -1
  236. package/dist/utils/thread-lock.js +96 -92
  237. package/dist/utils/thread-lock.js.map +1 -1
  238. package/dist/utils/token-estimator.js +12 -12
  239. package/dist/utils/token-estimator.js.map +1 -1
  240. package/dist/utils/update-check.d.ts +1 -1
  241. package/dist/utils/update-check.js +341 -259
  242. package/dist/utils/update-check.js.map +1 -1
  243. package/dist/voice/stt-registry.js +131 -113
  244. package/dist/voice/stt-registry.js.map +1 -1
  245. package/package.json +11 -11
  246. package/dist/acp.js.map +0 -1
  247. package/dist/auth/index.js.map +0 -1
  248. package/dist/auth/types.js.map +0 -1
  249. package/dist/evals/index.js.map +0 -1
  250. package/dist/evals/scorers/index.js.map +0 -1
  251. package/dist/headless/index.js.map +0 -1
  252. package/dist/hooks/index.js.map +0 -1
  253. package/dist/lsp/index.js.map +0 -1
  254. package/dist/mcp/index.js.map +0 -1
  255. package/dist/mcp/types.js.map +0 -1
  256. package/dist/onboarding/index.js.map +0 -1
  257. package/dist/plugins/types.js.map +0 -1
  258. package/dist/tools/index.js.map +0 -1
@@ -1,263 +1,265 @@
1
- import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
2
- import { applyCompatLayer, GoogleSchemaCompatLayer } from "@mastra/schema-compat";
3
- import { wrapLanguageModel } from "ai";
4
1
  import { COPILOT_HEADERS, fetchCopilotModels, getGitHubCopilotBaseUrl } from "../auth/providers/github-copilot.js";
5
2
  import { AuthStorage } from "../auth/storage.js";
3
+ import { wrapLanguageModel } from "ai";
4
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
5
+ import { GoogleSchemaCompatLayer, applyCompatLayer } from "@mastra/schema-compat";
6
+ //#region src/providers/github-copilot.ts
7
+ /**
8
+ * GitHub Copilot OAuth Provider
9
+ *
10
+ * Uses OAuth tokens from AuthStorage to authenticate with GitHub Copilot's chat API.
11
+ * The Copilot API speaks an OpenAI-compatible chat format, so we plug
12
+ * `@ai-sdk/openai-compatible` into Copilot's API URL and use a custom fetch to inject
13
+ * the bearer token and Copilot-specific headers.
14
+ *
15
+ * Inspired by:
16
+ * - opencode: https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/plugin/github-copilot/copilot.ts
17
+ * - pi-mono: https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/github-copilot.ts
18
+ */
6
19
  const COPILOT_PROVIDER_ID = "github-copilot";
7
20
  let authStorageInstance = null;
21
+ /** Get or create the shared AuthStorage instance. */
8
22
  function getAuthStorage() {
9
- if (!authStorageInstance) {
10
- authStorageInstance = new AuthStorage();
11
- }
12
- return authStorageInstance;
23
+ if (!authStorageInstance) authStorageInstance = new AuthStorage();
24
+ return authStorageInstance;
13
25
  }
26
+ /** Set a custom AuthStorage instance (useful for tests / TUI integration). */
14
27
  function setAuthStorage(storage) {
15
- authStorageInstance = storage ?? null;
28
+ authStorageInstance = storage ?? null;
16
29
  }
30
+ /**
31
+ * Heuristic: did this request come from the agent (e.g. tool result follow-ups) rather
32
+ * than a fresh user turn? Mirrors opencode's `isAgent` logic — Copilot bills these
33
+ * differently via the `x-initiator` header.
34
+ */
17
35
  function detectIsAgent(body) {
18
- if (!body || typeof body !== "object") return false;
19
- const obj = body;
20
- const messages = obj.messages;
21
- if (Array.isArray(messages) && messages.length > 0) {
22
- const last = messages[messages.length - 1];
23
- if (last?.role && last.role !== "user") return true;
24
- if (Array.isArray(last?.content)) {
25
- const hasToolResult = last.content.some(
26
- (part) => part && typeof part === "object" && part.type === "tool_result"
27
- );
28
- if (hasToolResult) return true;
29
- }
30
- }
31
- const input = obj.input;
32
- if (Array.isArray(input) && input.length > 0) {
33
- const last = input[input.length - 1];
34
- if (last?.role && last.role !== "user") return true;
35
- }
36
- return false;
36
+ if (!body || typeof body !== "object") return false;
37
+ const obj = body;
38
+ const messages = obj.messages;
39
+ if (Array.isArray(messages) && messages.length > 0) {
40
+ const last = messages[messages.length - 1];
41
+ if (last?.role && last.role !== "user") return true;
42
+ if (Array.isArray(last?.content)) {
43
+ if (last.content.some((part) => part && typeof part === "object" && part.type === "tool_result")) return true;
44
+ }
45
+ }
46
+ const input = obj.input;
47
+ if (Array.isArray(input) && input.length > 0) {
48
+ const last = input[input.length - 1];
49
+ if (last?.role && last.role !== "user") return true;
50
+ }
51
+ return false;
37
52
  }
53
+ /** Detect image/vision content in a request body. */
38
54
  function detectIsVision(body) {
39
- if (!body || typeof body !== "object") return false;
40
- const obj = body;
41
- const matchPart = (part) => {
42
- if (!part || typeof part !== "object") return false;
43
- const t = part.type;
44
- return t === "image" || t === "image_url" || t === "input_image";
45
- };
46
- const messages = obj.messages;
47
- if (Array.isArray(messages)) {
48
- return messages.some(
49
- (msg) => msg && typeof msg === "object" && Array.isArray(msg.content) && msg.content.some(matchPart)
50
- );
51
- }
52
- const input = obj.input;
53
- if (Array.isArray(input)) {
54
- return input.some(
55
- (item) => item && typeof item === "object" && Array.isArray(item.content) && item.content.some(matchPart)
56
- );
57
- }
58
- return false;
55
+ if (!body || typeof body !== "object") return false;
56
+ const obj = body;
57
+ const matchPart = (part) => {
58
+ if (!part || typeof part !== "object") return false;
59
+ const t = part.type;
60
+ return t === "image" || t === "image_url" || t === "input_image";
61
+ };
62
+ const messages = obj.messages;
63
+ if (Array.isArray(messages)) return messages.some((msg) => msg && typeof msg === "object" && Array.isArray(msg.content) && msg.content.some(matchPart));
64
+ const input = obj.input;
65
+ if (Array.isArray(input)) return input.some((item) => item && typeof item === "object" && Array.isArray(item.content) && item.content.some(matchPart));
66
+ return false;
59
67
  }
68
+ /**
69
+ * Build a fetch wrapper that authenticates with GitHub Copilot OAuth.
70
+ *
71
+ * - Injects the short-lived Copilot bearer token (auto-refreshed by AuthStorage).
72
+ * - Adds the VS Code-like Copilot headers required by the API.
73
+ * - Rewrites the request URL onto the per-token API base when `rewriteUrl` is true.
74
+ */
60
75
  function buildGitHubCopilotOAuthFetch(opts = {}) {
61
- return (async (url, init) => {
62
- const storage = opts.authStorage ?? getAuthStorage();
63
- storage.reload();
64
- const cred = storage.get(COPILOT_PROVIDER_ID);
65
- if (!cred || cred.type !== "oauth") {
66
- throw new Error("Not logged in to GitHub Copilot. Run /login first.");
67
- }
68
- const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
69
- if (!accessToken) {
70
- throw new Error("Failed to refresh GitHub Copilot token. Please /login again.");
71
- }
72
- storage.reload();
73
- const enterpriseUrl = cred.enterpriseUrl;
74
- let parsedBody;
75
- if (typeof init?.body === "string") {
76
- try {
77
- parsedBody = JSON.parse(init.body);
78
- } catch {
79
- parsedBody = void 0;
80
- }
81
- }
82
- const isAgent = detectIsAgent(parsedBody);
83
- const isVision = detectIsVision(parsedBody);
84
- const headers = new Headers();
85
- if (init?.headers) {
86
- const source = init.headers instanceof Headers ? init.headers : Array.isArray(init.headers) ? new Headers(init.headers) : new Headers(init.headers);
87
- source.forEach((value, key) => {
88
- const lower = key.toLowerCase();
89
- if (lower !== "authorization" && lower !== "x-api-key") {
90
- headers.set(key, value);
91
- }
92
- });
93
- }
94
- headers.set("Authorization", `Bearer ${accessToken}`);
95
- headers.set("x-initiator", isAgent ? "agent" : "user");
96
- headers.set("Openai-Intent", "conversation-edits");
97
- if (isVision) {
98
- headers.set("Copilot-Vision-Request", "true");
99
- }
100
- for (const [key, value] of Object.entries(COPILOT_HEADERS)) {
101
- if (!headers.has(key)) {
102
- headers.set(key, value);
103
- }
104
- }
105
- const finalUrl = opts.rewriteUrl !== false ? rewriteToCopilotBase(url, accessToken, enterpriseUrl) : url instanceof URL ? url : typeof url === "string" ? new URL(url) : new URL(url.url);
106
- try {
107
- return await fetch(finalUrl, { ...init, headers });
108
- } catch (error) {
109
- if (error && typeof error === "object") {
110
- Object.assign(error, {
111
- requestUrl: finalUrl.toString()
112
- });
113
- }
114
- throw error;
115
- }
116
- });
76
+ return (async (url, init) => {
77
+ const storage = opts.authStorage ?? getAuthStorage();
78
+ storage.reload();
79
+ const cred = storage.get(COPILOT_PROVIDER_ID);
80
+ if (!cred || cred.type !== "oauth") throw new Error("Not logged in to GitHub Copilot. Run /login first.");
81
+ const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
82
+ if (!accessToken) throw new Error("Failed to refresh GitHub Copilot token. Please /login again.");
83
+ storage.reload();
84
+ const enterpriseUrl = cred.enterpriseUrl;
85
+ let parsedBody;
86
+ if (typeof init?.body === "string") try {
87
+ parsedBody = JSON.parse(init.body);
88
+ } catch {
89
+ parsedBody = void 0;
90
+ }
91
+ const isAgent = detectIsAgent(parsedBody);
92
+ const isVision = detectIsVision(parsedBody);
93
+ const headers = new Headers();
94
+ if (init?.headers) (init.headers instanceof Headers ? init.headers : Array.isArray(init.headers) ? new Headers(init.headers) : new Headers(init.headers)).forEach((value, key) => {
95
+ const lower = key.toLowerCase();
96
+ if (lower !== "authorization" && lower !== "x-api-key") headers.set(key, value);
97
+ });
98
+ headers.set("Authorization", `Bearer ${accessToken}`);
99
+ headers.set("x-initiator", isAgent ? "agent" : "user");
100
+ headers.set("Openai-Intent", "conversation-edits");
101
+ if (isVision) headers.set("Copilot-Vision-Request", "true");
102
+ for (const [key, value] of Object.entries(COPILOT_HEADERS)) if (!headers.has(key)) headers.set(key, value);
103
+ const finalUrl = opts.rewriteUrl !== false ? rewriteToCopilotBase(url, accessToken, enterpriseUrl) : url instanceof URL ? url : typeof url === "string" ? new URL(url) : new URL(url.url);
104
+ try {
105
+ return await fetch(finalUrl, {
106
+ ...init,
107
+ headers
108
+ });
109
+ } catch (error) {
110
+ if (error && typeof error === "object") Object.assign(error, { requestUrl: finalUrl.toString() });
111
+ throw error;
112
+ }
113
+ });
117
114
  }
118
115
  function rewriteToCopilotBase(url, token, enterpriseDomain) {
119
- const original = url instanceof URL ? url : new URL(typeof url === "string" ? url : url.url);
120
- const base = new URL(getGitHubCopilotBaseUrl(token, enterpriseDomain));
121
- const pathname = original.pathname.replace(/^\/v1(\/|$)/, "/");
122
- return new URL(`${pathname}${original.search}`, base);
116
+ const original = url instanceof URL ? url : new URL(typeof url === "string" ? url : url.url);
117
+ const base = new URL(getGitHubCopilotBaseUrl(token, enterpriseDomain));
118
+ const pathname = original.pathname.replace(/^\/v1(\/|$)/, "/");
119
+ return new URL(`${pathname}${original.search}`, base);
123
120
  }
124
121
  function isGeminiModel(modelId) {
125
- return modelId.startsWith("gemini-");
122
+ return modelId.startsWith("gemini-");
126
123
  }
127
124
  function applyGeminiSchemaCompatToTools(modelId, tools) {
128
- if (!Array.isArray(tools)) {
129
- return tools;
130
- }
131
- const compatLayer = new GoogleSchemaCompatLayer({
132
- provider: COPILOT_PROVIDER_ID,
133
- modelId,
134
- supportsStructuredOutputs: false
135
- });
136
- return tools.map((tool) => {
137
- if (!tool || typeof tool !== "object" || tool.type !== "function") {
138
- return tool;
139
- }
140
- const functionTool = tool;
141
- if (!functionTool.inputSchema) {
142
- return tool;
143
- }
144
- return {
145
- ...functionTool,
146
- inputSchema: applyCompatLayer({
147
- schema: functionTool.inputSchema,
148
- compatLayers: [compatLayer],
149
- mode: "aiSdkSchema"
150
- }).jsonSchema
151
- };
152
- });
125
+ if (!Array.isArray(tools)) return tools;
126
+ const compatLayer = new GoogleSchemaCompatLayer({
127
+ provider: COPILOT_PROVIDER_ID,
128
+ modelId,
129
+ supportsStructuredOutputs: false
130
+ });
131
+ return tools.map((tool) => {
132
+ if (!tool || typeof tool !== "object" || tool.type !== "function") return tool;
133
+ const functionTool = tool;
134
+ if (!functionTool.inputSchema) return tool;
135
+ return {
136
+ ...functionTool,
137
+ inputSchema: applyCompatLayer({
138
+ schema: functionTool.inputSchema,
139
+ compatLayers: [compatLayer],
140
+ mode: "aiSdkSchema"
141
+ }).jsonSchema
142
+ };
143
+ });
153
144
  }
145
+ /** Middleware that prevents sending parameters Copilot's endpoint rejects. */
154
146
  function createCopilotMiddleware(modelId) {
155
- return {
156
- specificationVersion: "v3",
157
- transformParams: async ({ params }) => {
158
- if (params.temperature !== void 0 && params.temperature !== null) {
159
- delete params.topP;
160
- }
161
- if (isGeminiModel(modelId)) {
162
- params.tools = applyGeminiSchemaCompatToTools(
163
- modelId,
164
- params.tools
165
- );
166
- }
167
- return params;
168
- }
169
- };
147
+ return {
148
+ specificationVersion: "v3",
149
+ transformParams: async ({ params }) => {
150
+ if (params.temperature !== void 0 && params.temperature !== null) delete params.topP;
151
+ if (isGeminiModel(modelId)) params.tools = applyGeminiSchemaCompatToTools(modelId, params.tools);
152
+ return params;
153
+ }
154
+ };
170
155
  }
156
+ /**
157
+ * Creates a model that talks to GitHub Copilot using OAuth credentials.
158
+ *
159
+ * Copilot's `/chat/completions` endpoint is OpenAI-compatible, but GitHub Copilot
160
+ * is not OpenAI. Use the generic OpenAI-compatible adapter with Copilot's base URL
161
+ * instead of the OpenAI provider plus URL rewriting.
162
+ */
171
163
  function githubCopilotProvider(modelId = "gpt-4.1", options) {
172
- const headers = options?.headers;
173
- const copilot = createOpenAICompatible({
174
- name: COPILOT_PROVIDER_ID,
175
- baseURL: "https://api.githubcopilot.com",
176
- apiKey: process.env.NODE_ENV === "test" || process.env.VITEST ? "test-api-key" : "oauth-placeholder",
177
- headers,
178
- fetch: process.env.NODE_ENV === "test" || process.env.VITEST ? void 0 : buildGitHubCopilotOAuthFetch({ rewriteUrl: false, authStorage: options?.authStorage })
179
- });
180
- return wrapLanguageModel({
181
- model: copilot.chatModel(modelId),
182
- middleware: [createCopilotMiddleware(modelId)]
183
- });
164
+ const headers = options?.headers;
165
+ return wrapLanguageModel({
166
+ model: createOpenAICompatible({
167
+ name: COPILOT_PROVIDER_ID,
168
+ baseURL: "https://api.githubcopilot.com",
169
+ apiKey: process.env.NODE_ENV === "test" || process.env.VITEST ? "test-api-key" : "oauth-placeholder",
170
+ headers,
171
+ fetch: process.env.NODE_ENV === "test" || process.env.VITEST ? void 0 : buildGitHubCopilotOAuthFetch({
172
+ rewriteUrl: false,
173
+ authStorage: options?.authStorage
174
+ })
175
+ }).chatModel(modelId),
176
+ middleware: [createCopilotMiddleware(modelId)]
177
+ });
184
178
  }
185
- const COPILOT_FALLBACK_MODELS = [
186
- {
187
- id: "gpt-4.1",
188
- name: "GPT-4.1",
189
- vendor: "OpenAI",
190
- supportedEndpoints: ["/chat/completions"],
191
- isAnthropicShaped: false,
192
- supportsVision: true,
193
- supportsToolCalls: true
194
- }
195
- ];
196
- const CATALOG_TTL_MS = 10 * 60 * 1e3;
179
+ /**
180
+ * Hard-coded fallback advertised when the live `/models` request fails (network
181
+ * down, expired token, etc.). Keep this conservative because the live catalog is
182
+ * the source of truth for the user's currently-enabled Copilot models.
183
+ *
184
+ * Available across all paid Copilot tiers and free of premium-request charges.
185
+ */
186
+ const COPILOT_FALLBACK_MODELS = [{
187
+ id: "gpt-4.1",
188
+ name: "GPT-4.1",
189
+ vendor: "OpenAI",
190
+ supportedEndpoints: ["/chat/completions"],
191
+ isAnthropicShaped: false,
192
+ supportsVision: true,
193
+ supportsToolCalls: true
194
+ }];
195
+ const CATALOG_TTL_MS = 600 * 1e3;
197
196
  const CATALOG_FAILURE_TTL_MS = 60 * 1e3;
198
197
  const CATALOG_FETCH_TIMEOUT_MS = 5e3;
199
198
  let catalogCache = null;
200
199
  let inflightFetch = null;
200
+ /** Reset the in-process Copilot catalog cache (test seam, also useful after logout). */
201
201
  function clearCopilotCatalogCache() {
202
- catalogCache = null;
203
- inflightFetch = null;
202
+ catalogCache = null;
203
+ inflightFetch = null;
204
204
  }
205
+ /**
206
+ * Return the user's currently-available Copilot models.
207
+ *
208
+ * - Returns `[]` when the user is not logged in to GitHub Copilot.
209
+ * - Returns the cached list when a recent fetch succeeded.
210
+ * - On the cache-miss / expired path, fetches `/models` with a 5s timeout, filters
211
+ * to picker-enabled and non-policy-disabled models, then caches for 10 minutes.
212
+ * - On fetch failure, returns a small hard-coded fallback (so packs still work
213
+ * offline) and caches that for 1 minute to avoid hammering the API.
214
+ *
215
+ * Concurrent calls during a fetch share the inflight promise.
216
+ */
205
217
  async function getCopilotModelCatalog(opts = {}) {
206
- const storage = opts.authStorage ?? getAuthStorage();
207
- storage.reload();
208
- const cred = storage.get(COPILOT_PROVIDER_ID);
209
- if (!cred || cred.type !== "oauth") {
210
- return [];
211
- }
212
- const now = Date.now();
213
- if (catalogCache && now - catalogCache.fetchedAt < catalogCache.ttl) {
214
- return catalogCache.models;
215
- }
216
- if (inflightFetch) return inflightFetch;
217
- inflightFetch = (async () => {
218
- try {
219
- const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
220
- if (!accessToken) throw new Error("No Copilot bearer token");
221
- storage.reload();
222
- const refreshed = storage.get(COPILOT_PROVIDER_ID);
223
- const enterpriseUrl = refreshed?.enterpriseUrl;
224
- const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);
225
- const controller = new AbortController();
226
- const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);
227
- try {
228
- const models = await fetchCopilotModels({
229
- baseUrl,
230
- bearerToken: accessToken,
231
- signal: controller.signal
232
- });
233
- catalogCache = { fetchedAt: Date.now(), ttl: CATALOG_TTL_MS, models };
234
- return models;
235
- } finally {
236
- clearTimeout(timer);
237
- }
238
- } catch (error) {
239
- catalogCache = {
240
- fetchedAt: Date.now(),
241
- ttl: CATALOG_FAILURE_TTL_MS,
242
- models: COPILOT_FALLBACK_MODELS
243
- };
244
- console.warn(
245
- "Failed to fetch live GitHub Copilot models, using fallback list:",
246
- error instanceof Error ? error.message : error
247
- );
248
- return COPILOT_FALLBACK_MODELS;
249
- } finally {
250
- inflightFetch = null;
251
- }
252
- })();
253
- return inflightFetch;
218
+ const storage = opts.authStorage ?? getAuthStorage();
219
+ storage.reload();
220
+ const cred = storage.get(COPILOT_PROVIDER_ID);
221
+ if (!cred || cred.type !== "oauth") return [];
222
+ if (catalogCache && Date.now() - catalogCache.fetchedAt < catalogCache.ttl) return catalogCache.models;
223
+ if (inflightFetch) return inflightFetch;
224
+ inflightFetch = (async () => {
225
+ try {
226
+ const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
227
+ if (!accessToken) throw new Error("No Copilot bearer token");
228
+ storage.reload();
229
+ const enterpriseUrl = storage.get(COPILOT_PROVIDER_ID)?.enterpriseUrl;
230
+ const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);
231
+ const controller = new AbortController();
232
+ const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);
233
+ try {
234
+ const models = await fetchCopilotModels({
235
+ baseUrl,
236
+ bearerToken: accessToken,
237
+ signal: controller.signal
238
+ });
239
+ catalogCache = {
240
+ fetchedAt: Date.now(),
241
+ ttl: CATALOG_TTL_MS,
242
+ models
243
+ };
244
+ return models;
245
+ } finally {
246
+ clearTimeout(timer);
247
+ }
248
+ } catch (error) {
249
+ catalogCache = {
250
+ fetchedAt: Date.now(),
251
+ ttl: CATALOG_FAILURE_TTL_MS,
252
+ models: COPILOT_FALLBACK_MODELS
253
+ };
254
+ console.warn("Failed to fetch live GitHub Copilot models, using fallback list:", error instanceof Error ? error.message : error);
255
+ return COPILOT_FALLBACK_MODELS;
256
+ } finally {
257
+ inflightFetch = null;
258
+ }
259
+ })();
260
+ return inflightFetch;
254
261
  }
255
- export {
256
- buildGitHubCopilotOAuthFetch,
257
- clearCopilotCatalogCache,
258
- getAuthStorage,
259
- getCopilotModelCatalog,
260
- githubCopilotProvider,
261
- setAuthStorage
262
- };
262
+ //#endregion
263
+ export { buildGitHubCopilotOAuthFetch, clearCopilotCatalogCache, getAuthStorage, getCopilotModelCatalog, githubCopilotProvider, setAuthStorage };
264
+
263
265
  //# sourceMappingURL=github-copilot.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/providers/github-copilot.ts"],"sourcesContent":["/**\n * GitHub Copilot OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with GitHub Copilot's chat API.\n * The Copilot API speaks an OpenAI-compatible chat format, so we plug\n * `@ai-sdk/openai-compatible` into Copilot's API URL and use a custom fetch to inject\n * the bearer token and Copilot-specific headers.\n *\n * Inspired by:\n * - opencode: https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/plugin/github-copilot/copilot.ts\n * - pi-mono: https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/github-copilot.ts\n */\n\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport type { JSONSchema7 } from '@mastra/schema-compat';\nimport { applyCompatLayer, GoogleSchemaCompatLayer } from '@mastra/schema-compat';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { COPILOT_HEADERS, fetchCopilotModels, getGitHubCopilotBaseUrl } from '../auth/providers/github-copilot.js';\nimport type { CopilotModelEntry, GitHubCopilotCredentials } from '../auth/providers/github-copilot.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\n\nconst COPILOT_PROVIDER_ID = 'github-copilot';\n\n// Singleton auth storage instance (shared with claude-max.ts / openai-codex.ts when not overridden).\nlet authStorageInstance: AuthStorage | null = null;\n\n/** Get or create the shared AuthStorage instance. */\nexport function getAuthStorage(): AuthStorage {\n if (!authStorageInstance) {\n authStorageInstance = new AuthStorage();\n }\n return authStorageInstance;\n}\n\n/** Set a custom AuthStorage instance (useful for tests / TUI integration). */\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\n/**\n * Heuristic: did this request come from the agent (e.g. tool result follow-ups) rather\n * than a fresh user turn? Mirrors opencode's `isAgent` logic — Copilot bills these\n * differently via the `x-initiator` header.\n */\nfunction detectIsAgent(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const messages = obj.messages;\n if (Array.isArray(messages) && messages.length > 0) {\n const last = messages[messages.length - 1] as { role?: string; content?: unknown };\n if (last?.role && last.role !== 'user') return true;\n if (Array.isArray(last?.content)) {\n // If the last user turn carries any tool_result parts, treat it as an agent turn.\n const hasToolResult = last.content.some(\n (part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'tool_result',\n );\n if (hasToolResult) return true;\n }\n }\n\n const input = obj.input;\n if (Array.isArray(input) && input.length > 0) {\n const last = input[input.length - 1] as { role?: string };\n if (last?.role && last.role !== 'user') return true;\n }\n\n return false;\n}\n\n/** Detect image/vision content in a request body. */\nfunction detectIsVision(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const matchPart = (part: unknown): boolean => {\n if (!part || typeof part !== 'object') return false;\n const t = (part as { type?: string }).type;\n return t === 'image' || t === 'image_url' || t === 'input_image';\n };\n\n const messages = obj.messages;\n if (Array.isArray(messages)) {\n return messages.some(\n (msg: unknown) =>\n msg &&\n typeof msg === 'object' &&\n Array.isArray((msg as { content?: unknown }).content) &&\n ((msg as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n const input = obj.input;\n if (Array.isArray(input)) {\n return input.some(\n (item: unknown) =>\n item &&\n typeof item === 'object' &&\n Array.isArray((item as { content?: unknown }).content) &&\n ((item as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n return false;\n}\n\n/**\n * Build a fetch wrapper that authenticates with GitHub Copilot OAuth.\n *\n * - Injects the short-lived Copilot bearer token (auto-refreshed by AuthStorage).\n * - Adds the VS Code-like Copilot headers required by the API.\n * - Rewrites the request URL onto the per-token API base when `rewriteUrl` is true.\n */\nexport function buildGitHubCopilotOAuthFetch(\n opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n throw new Error('Not logged in to GitHub Copilot. Run /login first.');\n }\n\n // getApiKey() refreshes the Copilot bearer if it has expired.\n const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) {\n throw new Error('Failed to refresh GitHub Copilot token. Please /login again.');\n }\n storage.reload();\n\n const enterpriseUrl = (cred as GitHubCopilotCredentials).enterpriseUrl;\n\n let parsedBody: unknown;\n if (typeof init?.body === 'string') {\n try {\n parsedBody = JSON.parse(init.body);\n } catch {\n parsedBody = undefined;\n }\n }\n const isAgent = detectIsAgent(parsedBody);\n const isVision = detectIsVision(parsedBody);\n\n // Preserve non-auth headers from caller.\n const headers = new Headers();\n if (init?.headers) {\n const source =\n init.headers instanceof Headers\n ? init.headers\n : Array.isArray(init.headers)\n ? new Headers(init.headers as Array<[string, string]>)\n : new Headers(init.headers as Record<string, string>);\n source.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower !== 'authorization' && lower !== 'x-api-key') {\n headers.set(key, value);\n }\n });\n }\n\n headers.set('Authorization', `Bearer ${accessToken}`);\n headers.set('x-initiator', isAgent ? 'agent' : 'user');\n headers.set('Openai-Intent', 'conversation-edits');\n if (isVision) {\n headers.set('Copilot-Vision-Request', 'true');\n }\n for (const [key, value] of Object.entries(COPILOT_HEADERS)) {\n // Only set if caller didn't already provide it (allow overrides for tests).\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n\n const finalUrl =\n opts.rewriteUrl !== false\n ? rewriteToCopilotBase(url, accessToken, enterpriseUrl)\n : url instanceof URL\n ? url\n : typeof url === 'string'\n ? new URL(url)\n : new URL((url as Request).url);\n\n try {\n return await fetch(finalUrl, { ...init, headers });\n } catch (error) {\n if (error && typeof error === 'object') {\n Object.assign(error as Record<string, unknown>, {\n requestUrl: finalUrl.toString(),\n });\n }\n throw error;\n }\n }) as typeof fetch;\n}\n\nfunction rewriteToCopilotBase(url: string | URL | Request, token: string, enterpriseDomain?: string): URL {\n const original = url instanceof URL ? url : new URL(typeof url === 'string' ? url : (url as Request).url);\n const base = new URL(getGitHubCopilotBaseUrl(token, enterpriseDomain));\n // Copilot's OpenAI-compatible API serves endpoints at the root of the base host\n // (`/chat/completions`, `/responses`, `/models`, ...) — not under a `/v1/` prefix\n // like api.openai.com does. The @ai-sdk/openai default baseURL is\n // `https://api.openai.com/v1`, so the SDK builds requests like\n // `https://api.openai.com/v1/chat/completions`. Strip the leading `/v1` segment\n // when rewriting onto the Copilot base or Copilot will return 404 Not Found.\n const pathname = original.pathname.replace(/^\\/v1(\\/|$)/, '/');\n return new URL(`${pathname}${original.search}`, base);\n}\n\nfunction isGeminiModel(modelId: string): boolean {\n return modelId.startsWith('gemini-');\n}\n\nfunction applyGeminiSchemaCompatToTools(modelId: string, tools: unknown): unknown {\n if (!Array.isArray(tools)) {\n return tools;\n }\n\n const compatLayer = new GoogleSchemaCompatLayer({\n provider: COPILOT_PROVIDER_ID,\n modelId,\n supportsStructuredOutputs: false,\n });\n\n return tools.map(tool => {\n if (!tool || typeof tool !== 'object' || (tool as { type?: unknown }).type !== 'function') {\n return tool;\n }\n\n const functionTool = tool as { inputSchema?: JSONSchema7 };\n if (!functionTool.inputSchema) {\n return tool;\n }\n\n return {\n ...functionTool,\n inputSchema: applyCompatLayer({\n schema: functionTool.inputSchema,\n compatLayers: [compatLayer],\n mode: 'aiSdkSchema',\n }).jsonSchema as JSONSchema7,\n };\n });\n}\n\n/** Middleware that prevents sending parameters Copilot's endpoint rejects. */\nfunction createCopilotMiddleware(modelId: string): LanguageModelMiddleware {\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n if (params.temperature !== undefined && params.temperature !== null) {\n delete params.topP;\n }\n\n if (isGeminiModel(modelId)) {\n (params as { tools?: unknown }).tools = applyGeminiSchemaCompatToTools(\n modelId,\n (params as { tools?: unknown }).tools,\n );\n }\n\n return params;\n },\n };\n}\n\n/**\n * Creates a model that talks to GitHub Copilot using OAuth credentials.\n *\n * Copilot's `/chat/completions` endpoint is OpenAI-compatible, but GitHub Copilot\n * is not OpenAI. Use the generic OpenAI-compatible adapter with Copilot's base URL\n * instead of the OpenAI provider plus URL rewriting.\n */\nexport function githubCopilotProvider(\n modelId: string = 'gpt-4.1',\n options?: { headers?: Record<string, string>; authStorage?: CredentialStore },\n): MastraModelConfig {\n const headers = options?.headers;\n const copilot = createOpenAICompatible({\n name: COPILOT_PROVIDER_ID,\n baseURL: 'https://api.githubcopilot.com',\n apiKey: process.env.NODE_ENV === 'test' || process.env.VITEST ? 'test-api-key' : 'oauth-placeholder',\n headers,\n fetch:\n process.env.NODE_ENV === 'test' || process.env.VITEST\n ? undefined\n : (buildGitHubCopilotOAuthFetch({ rewriteUrl: false, authStorage: options?.authStorage }) as any),\n });\n\n return wrapLanguageModel({\n model: copilot.chatModel(modelId),\n middleware: [createCopilotMiddleware(modelId)],\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live model catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Hard-coded fallback advertised when the live `/models` request fails (network\n * down, expired token, etc.). Keep this conservative because the live catalog is\n * the source of truth for the user's currently-enabled Copilot models.\n *\n * Available across all paid Copilot tiers and free of premium-request charges.\n */\nconst COPILOT_FALLBACK_MODELS: CopilotModelEntry[] = [\n {\n id: 'gpt-4.1',\n name: 'GPT-4.1',\n vendor: 'OpenAI',\n supportedEndpoints: ['/chat/completions'],\n isAnthropicShaped: false,\n supportsVision: true,\n supportsToolCalls: true,\n },\n];\n\nconst CATALOG_TTL_MS = 10 * 60 * 1000;\nconst CATALOG_FAILURE_TTL_MS = 60 * 1000;\nconst CATALOG_FETCH_TIMEOUT_MS = 5_000;\n\ninterface CatalogCacheEntry {\n fetchedAt: number;\n ttl: number;\n models: CopilotModelEntry[];\n}\n\nlet catalogCache: CatalogCacheEntry | null = null;\nlet inflightFetch: Promise<CopilotModelEntry[]> | null = null;\n\n/** Reset the in-process Copilot catalog cache (test seam, also useful after logout). */\nexport function clearCopilotCatalogCache(): void {\n catalogCache = null;\n inflightFetch = null;\n}\n\n/**\n * Return the user's currently-available Copilot models.\n *\n * - Returns `[]` when the user is not logged in to GitHub Copilot.\n * - Returns the cached list when a recent fetch succeeded.\n * - On the cache-miss / expired path, fetches `/models` with a 5s timeout, filters\n * to picker-enabled and non-policy-disabled models, then caches for 10 minutes.\n * - On fetch failure, returns a small hard-coded fallback (so packs still work\n * offline) and caches that for 1 minute to avoid hammering the API.\n *\n * Concurrent calls during a fetch share the inflight promise.\n */\nexport async function getCopilotModelCatalog(opts: { authStorage?: AuthStorage } = {}): Promise<CopilotModelEntry[]> {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n return [];\n }\n\n const now = Date.now();\n if (catalogCache && now - catalogCache.fetchedAt < catalogCache.ttl) {\n return catalogCache.models;\n }\n\n if (inflightFetch) return inflightFetch;\n\n inflightFetch = (async (): Promise<CopilotModelEntry[]> => {\n try {\n // getApiKey() refreshes the Copilot bearer if it has expired.\n const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) throw new Error('No Copilot bearer token');\n storage.reload();\n\n const refreshed = storage.get(COPILOT_PROVIDER_ID);\n const enterpriseUrl = (refreshed as GitHubCopilotCredentials | undefined)?.enterpriseUrl;\n const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);\n try {\n const models = await fetchCopilotModels({\n baseUrl,\n bearerToken: accessToken,\n signal: controller.signal,\n });\n catalogCache = { fetchedAt: Date.now(), ttl: CATALOG_TTL_MS, models };\n return models;\n } finally {\n clearTimeout(timer);\n }\n } catch (error) {\n catalogCache = {\n fetchedAt: Date.now(),\n ttl: CATALOG_FAILURE_TTL_MS,\n models: COPILOT_FALLBACK_MODELS,\n };\n console.warn(\n 'Failed to fetch live GitHub Copilot models, using fallback list:',\n error instanceof Error ? error.message : error,\n );\n return COPILOT_FALLBACK_MODELS;\n } finally {\n inflightFetch = null;\n }\n })();\n\n return inflightFetch;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"github-copilot.js","names":[],"sources":["../../src/providers/github-copilot.ts"],"sourcesContent":["/**\n * GitHub Copilot OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with GitHub Copilot's chat API.\n * The Copilot API speaks an OpenAI-compatible chat format, so we plug\n * `@ai-sdk/openai-compatible` into Copilot's API URL and use a custom fetch to inject\n * the bearer token and Copilot-specific headers.\n *\n * Inspired by:\n * - opencode: https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/plugin/github-copilot/copilot.ts\n * - pi-mono: https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/github-copilot.ts\n */\n\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport type { JSONSchema7 } from '@mastra/schema-compat';\nimport { applyCompatLayer, GoogleSchemaCompatLayer } from '@mastra/schema-compat';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { COPILOT_HEADERS, fetchCopilotModels, getGitHubCopilotBaseUrl } from '../auth/providers/github-copilot.js';\nimport type { CopilotModelEntry, GitHubCopilotCredentials } from '../auth/providers/github-copilot.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\n\nconst COPILOT_PROVIDER_ID = 'github-copilot';\n\n// Singleton auth storage instance (shared with claude-max.ts / openai-codex.ts when not overridden).\nlet authStorageInstance: AuthStorage | null = null;\n\n/** Get or create the shared AuthStorage instance. */\nexport function getAuthStorage(): AuthStorage {\n if (!authStorageInstance) {\n authStorageInstance = new AuthStorage();\n }\n return authStorageInstance;\n}\n\n/** Set a custom AuthStorage instance (useful for tests / TUI integration). */\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\n/**\n * Heuristic: did this request come from the agent (e.g. tool result follow-ups) rather\n * than a fresh user turn? Mirrors opencode's `isAgent` logic — Copilot bills these\n * differently via the `x-initiator` header.\n */\nfunction detectIsAgent(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const messages = obj.messages;\n if (Array.isArray(messages) && messages.length > 0) {\n const last = messages[messages.length - 1] as { role?: string; content?: unknown };\n if (last?.role && last.role !== 'user') return true;\n if (Array.isArray(last?.content)) {\n // If the last user turn carries any tool_result parts, treat it as an agent turn.\n const hasToolResult = last.content.some(\n (part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'tool_result',\n );\n if (hasToolResult) return true;\n }\n }\n\n const input = obj.input;\n if (Array.isArray(input) && input.length > 0) {\n const last = input[input.length - 1] as { role?: string };\n if (last?.role && last.role !== 'user') return true;\n }\n\n return false;\n}\n\n/** Detect image/vision content in a request body. */\nfunction detectIsVision(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const matchPart = (part: unknown): boolean => {\n if (!part || typeof part !== 'object') return false;\n const t = (part as { type?: string }).type;\n return t === 'image' || t === 'image_url' || t === 'input_image';\n };\n\n const messages = obj.messages;\n if (Array.isArray(messages)) {\n return messages.some(\n (msg: unknown) =>\n msg &&\n typeof msg === 'object' &&\n Array.isArray((msg as { content?: unknown }).content) &&\n ((msg as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n const input = obj.input;\n if (Array.isArray(input)) {\n return input.some(\n (item: unknown) =>\n item &&\n typeof item === 'object' &&\n Array.isArray((item as { content?: unknown }).content) &&\n ((item as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n return false;\n}\n\n/**\n * Build a fetch wrapper that authenticates with GitHub Copilot OAuth.\n *\n * - Injects the short-lived Copilot bearer token (auto-refreshed by AuthStorage).\n * - Adds the VS Code-like Copilot headers required by the API.\n * - Rewrites the request URL onto the per-token API base when `rewriteUrl` is true.\n */\nexport function buildGitHubCopilotOAuthFetch(\n opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n throw new Error('Not logged in to GitHub Copilot. Run /login first.');\n }\n\n // getApiKey() refreshes the Copilot bearer if it has expired.\n const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) {\n throw new Error('Failed to refresh GitHub Copilot token. Please /login again.');\n }\n storage.reload();\n\n const enterpriseUrl = (cred as GitHubCopilotCredentials).enterpriseUrl;\n\n let parsedBody: unknown;\n if (typeof init?.body === 'string') {\n try {\n parsedBody = JSON.parse(init.body);\n } catch {\n parsedBody = undefined;\n }\n }\n const isAgent = detectIsAgent(parsedBody);\n const isVision = detectIsVision(parsedBody);\n\n // Preserve non-auth headers from caller.\n const headers = new Headers();\n if (init?.headers) {\n const source =\n init.headers instanceof Headers\n ? init.headers\n : Array.isArray(init.headers)\n ? new Headers(init.headers as Array<[string, string]>)\n : new Headers(init.headers as Record<string, string>);\n source.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower !== 'authorization' && lower !== 'x-api-key') {\n headers.set(key, value);\n }\n });\n }\n\n headers.set('Authorization', `Bearer ${accessToken}`);\n headers.set('x-initiator', isAgent ? 'agent' : 'user');\n headers.set('Openai-Intent', 'conversation-edits');\n if (isVision) {\n headers.set('Copilot-Vision-Request', 'true');\n }\n for (const [key, value] of Object.entries(COPILOT_HEADERS)) {\n // Only set if caller didn't already provide it (allow overrides for tests).\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n\n const finalUrl =\n opts.rewriteUrl !== false\n ? rewriteToCopilotBase(url, accessToken, enterpriseUrl)\n : url instanceof URL\n ? url\n : typeof url === 'string'\n ? new URL(url)\n : new URL((url as Request).url);\n\n try {\n return await fetch(finalUrl, { ...init, headers });\n } catch (error) {\n if (error && typeof error === 'object') {\n Object.assign(error as Record<string, unknown>, {\n requestUrl: finalUrl.toString(),\n });\n }\n throw error;\n }\n }) as typeof fetch;\n}\n\nfunction rewriteToCopilotBase(url: string | URL | Request, token: string, enterpriseDomain?: string): URL {\n const original = url instanceof URL ? url : new URL(typeof url === 'string' ? url : (url as Request).url);\n const base = new URL(getGitHubCopilotBaseUrl(token, enterpriseDomain));\n // Copilot's OpenAI-compatible API serves endpoints at the root of the base host\n // (`/chat/completions`, `/responses`, `/models`, ...) — not under a `/v1/` prefix\n // like api.openai.com does. The @ai-sdk/openai default baseURL is\n // `https://api.openai.com/v1`, so the SDK builds requests like\n // `https://api.openai.com/v1/chat/completions`. Strip the leading `/v1` segment\n // when rewriting onto the Copilot base or Copilot will return 404 Not Found.\n const pathname = original.pathname.replace(/^\\/v1(\\/|$)/, '/');\n return new URL(`${pathname}${original.search}`, base);\n}\n\nfunction isGeminiModel(modelId: string): boolean {\n return modelId.startsWith('gemini-');\n}\n\nfunction applyGeminiSchemaCompatToTools(modelId: string, tools: unknown): unknown {\n if (!Array.isArray(tools)) {\n return tools;\n }\n\n const compatLayer = new GoogleSchemaCompatLayer({\n provider: COPILOT_PROVIDER_ID,\n modelId,\n supportsStructuredOutputs: false,\n });\n\n return tools.map(tool => {\n if (!tool || typeof tool !== 'object' || (tool as { type?: unknown }).type !== 'function') {\n return tool;\n }\n\n const functionTool = tool as { inputSchema?: JSONSchema7 };\n if (!functionTool.inputSchema) {\n return tool;\n }\n\n return {\n ...functionTool,\n inputSchema: applyCompatLayer({\n schema: functionTool.inputSchema,\n compatLayers: [compatLayer],\n mode: 'aiSdkSchema',\n }).jsonSchema as JSONSchema7,\n };\n });\n}\n\n/** Middleware that prevents sending parameters Copilot's endpoint rejects. */\nfunction createCopilotMiddleware(modelId: string): LanguageModelMiddleware {\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n if (params.temperature !== undefined && params.temperature !== null) {\n delete params.topP;\n }\n\n if (isGeminiModel(modelId)) {\n (params as { tools?: unknown }).tools = applyGeminiSchemaCompatToTools(\n modelId,\n (params as { tools?: unknown }).tools,\n );\n }\n\n return params;\n },\n };\n}\n\n/**\n * Creates a model that talks to GitHub Copilot using OAuth credentials.\n *\n * Copilot's `/chat/completions` endpoint is OpenAI-compatible, but GitHub Copilot\n * is not OpenAI. Use the generic OpenAI-compatible adapter with Copilot's base URL\n * instead of the OpenAI provider plus URL rewriting.\n */\nexport function githubCopilotProvider(\n modelId: string = 'gpt-4.1',\n options?: { headers?: Record<string, string>; authStorage?: CredentialStore },\n): MastraModelConfig {\n const headers = options?.headers;\n const copilot = createOpenAICompatible({\n name: COPILOT_PROVIDER_ID,\n baseURL: 'https://api.githubcopilot.com',\n apiKey: process.env.NODE_ENV === 'test' || process.env.VITEST ? 'test-api-key' : 'oauth-placeholder',\n headers,\n fetch:\n process.env.NODE_ENV === 'test' || process.env.VITEST\n ? undefined\n : (buildGitHubCopilotOAuthFetch({ rewriteUrl: false, authStorage: options?.authStorage }) as any),\n });\n\n return wrapLanguageModel({\n model: copilot.chatModel(modelId),\n middleware: [createCopilotMiddleware(modelId)],\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live model catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Hard-coded fallback advertised when the live `/models` request fails (network\n * down, expired token, etc.). Keep this conservative because the live catalog is\n * the source of truth for the user's currently-enabled Copilot models.\n *\n * Available across all paid Copilot tiers and free of premium-request charges.\n */\nconst COPILOT_FALLBACK_MODELS: CopilotModelEntry[] = [\n {\n id: 'gpt-4.1',\n name: 'GPT-4.1',\n vendor: 'OpenAI',\n supportedEndpoints: ['/chat/completions'],\n isAnthropicShaped: false,\n supportsVision: true,\n supportsToolCalls: true,\n },\n];\n\nconst CATALOG_TTL_MS = 10 * 60 * 1000;\nconst CATALOG_FAILURE_TTL_MS = 60 * 1000;\nconst CATALOG_FETCH_TIMEOUT_MS = 5_000;\n\ninterface CatalogCacheEntry {\n fetchedAt: number;\n ttl: number;\n models: CopilotModelEntry[];\n}\n\nlet catalogCache: CatalogCacheEntry | null = null;\nlet inflightFetch: Promise<CopilotModelEntry[]> | null = null;\n\n/** Reset the in-process Copilot catalog cache (test seam, also useful after logout). */\nexport function clearCopilotCatalogCache(): void {\n catalogCache = null;\n inflightFetch = null;\n}\n\n/**\n * Return the user's currently-available Copilot models.\n *\n * - Returns `[]` when the user is not logged in to GitHub Copilot.\n * - Returns the cached list when a recent fetch succeeded.\n * - On the cache-miss / expired path, fetches `/models` with a 5s timeout, filters\n * to picker-enabled and non-policy-disabled models, then caches for 10 minutes.\n * - On fetch failure, returns a small hard-coded fallback (so packs still work\n * offline) and caches that for 1 minute to avoid hammering the API.\n *\n * Concurrent calls during a fetch share the inflight promise.\n */\nexport async function getCopilotModelCatalog(opts: { authStorage?: AuthStorage } = {}): Promise<CopilotModelEntry[]> {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n return [];\n }\n\n const now = Date.now();\n if (catalogCache && now - catalogCache.fetchedAt < catalogCache.ttl) {\n return catalogCache.models;\n }\n\n if (inflightFetch) return inflightFetch;\n\n inflightFetch = (async (): Promise<CopilotModelEntry[]> => {\n try {\n // getApiKey() refreshes the Copilot bearer if it has expired.\n const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) throw new Error('No Copilot bearer token');\n storage.reload();\n\n const refreshed = storage.get(COPILOT_PROVIDER_ID);\n const enterpriseUrl = (refreshed as GitHubCopilotCredentials | undefined)?.enterpriseUrl;\n const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);\n try {\n const models = await fetchCopilotModels({\n baseUrl,\n bearerToken: accessToken,\n signal: controller.signal,\n });\n catalogCache = { fetchedAt: Date.now(), ttl: CATALOG_TTL_MS, models };\n return models;\n } finally {\n clearTimeout(timer);\n }\n } catch (error) {\n catalogCache = {\n fetchedAt: Date.now(),\n ttl: CATALOG_FAILURE_TTL_MS,\n models: COPILOT_FALLBACK_MODELS,\n };\n console.warn(\n 'Failed to fetch live GitHub Copilot models, using fallback list:',\n error instanceof Error ? error.message : error,\n );\n return COPILOT_FALLBACK_MODELS;\n } finally {\n inflightFetch = null;\n }\n })();\n\n return inflightFetch;\n}\n"],"mappings":"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"}