mastracode 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/README.md +9 -1
  3. package/dist/agents/instructions.d.ts.map +1 -1
  4. package/dist/agents/prompts/base.d.ts +2 -0
  5. package/dist/agents/prompts/base.d.ts.map +1 -1
  6. package/dist/agents/prompts/build.d.ts +1 -1
  7. package/dist/agents/prompts/build.d.ts.map +1 -1
  8. package/dist/agents/prompts/index.d.ts +1 -2
  9. package/dist/agents/prompts/index.d.ts.map +1 -1
  10. package/dist/agents/prompts/plan.d.ts +1 -1
  11. package/dist/agents/prompts/plan.d.ts.map +1 -1
  12. package/dist/agents/prompts/tool-guidance.d.ts +11 -0
  13. package/dist/agents/prompts/tool-guidance.d.ts.map +1 -0
  14. package/dist/agents/workspace.d.ts.map +1 -1
  15. package/dist/auth/storage.d.ts +0 -48
  16. package/dist/auth/storage.d.ts.map +1 -1
  17. package/dist/{chunk-LVGWM7ZS.cjs → chunk-7K5VFY2N.cjs} +768 -1373
  18. package/dist/chunk-7K5VFY2N.cjs.map +1 -0
  19. package/dist/chunk-7TFV3VBB.cjs +8823 -0
  20. package/dist/chunk-7TFV3VBB.cjs.map +1 -0
  21. package/dist/chunk-C6XKRHRK.cjs +853 -0
  22. package/dist/chunk-C6XKRHRK.cjs.map +1 -0
  23. package/dist/{chunk-7V6U7OKQ.js → chunk-HHX6BKLR.js} +234 -121
  24. package/dist/chunk-HHX6BKLR.js.map +1 -0
  25. package/dist/{chunk-FYTZFUHD.js → chunk-LYETHS2L.js} +6389 -5481
  26. package/dist/chunk-LYETHS2L.js.map +1 -0
  27. package/dist/{chunk-BYMDWH2E.js → chunk-V4HZ2AVV.js} +729 -1332
  28. package/dist/chunk-V4HZ2AVV.js.map +1 -0
  29. package/dist/chunk-VRZZSUQE.js +835 -0
  30. package/dist/chunk-VRZZSUQE.js.map +1 -0
  31. package/dist/{chunk-QDLLGD43.cjs → chunk-VZFPT5N7.cjs} +274 -161
  32. package/dist/chunk-VZFPT5N7.cjs.map +1 -0
  33. package/dist/cli.cjs +64 -21
  34. package/dist/cli.cjs.map +1 -1
  35. package/dist/cli.js +61 -18
  36. package/dist/cli.js.map +1 -1
  37. package/dist/index.cjs +2 -2
  38. package/dist/index.d.ts +6 -7
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/onboarding/index.d.ts +7 -0
  42. package/dist/onboarding/index.d.ts.map +1 -0
  43. package/dist/onboarding/onboarding-inline.d.ts +95 -0
  44. package/dist/onboarding/onboarding-inline.d.ts.map +1 -0
  45. package/dist/onboarding/packs.d.ts +46 -0
  46. package/dist/onboarding/packs.d.ts.map +1 -0
  47. package/dist/onboarding/settings.d.ts +110 -0
  48. package/dist/onboarding/settings.d.ts.map +1 -0
  49. package/dist/storage-52Y5MKTG.js +3 -0
  50. package/dist/storage-52Y5MKTG.js.map +1 -0
  51. package/dist/storage-JFFX7LJJ.cjs +24 -0
  52. package/dist/storage-JFFX7LJJ.cjs.map +1 -0
  53. package/dist/tools/string-replace-lsp.d.ts.map +1 -1
  54. package/dist/tui/command-dispatch.d.ts +8 -0
  55. package/dist/tui/command-dispatch.d.ts.map +1 -0
  56. package/dist/tui/commands/cost.d.ts +3 -0
  57. package/dist/tui/commands/cost.d.ts.map +1 -0
  58. package/dist/tui/commands/diff.d.ts +3 -0
  59. package/dist/tui/commands/diff.d.ts.map +1 -0
  60. package/dist/tui/commands/exit.d.ts +3 -0
  61. package/dist/tui/commands/exit.d.ts.map +1 -0
  62. package/dist/tui/commands/help.d.ts +3 -0
  63. package/dist/tui/commands/help.d.ts.map +1 -0
  64. package/dist/tui/commands/hooks.d.ts +3 -0
  65. package/dist/tui/commands/hooks.d.ts.map +1 -0
  66. package/dist/tui/commands/index.d.ts +27 -0
  67. package/dist/tui/commands/index.d.ts.map +1 -0
  68. package/dist/tui/commands/login.d.ts +3 -0
  69. package/dist/tui/commands/login.d.ts.map +1 -0
  70. package/dist/tui/commands/mcp.d.ts +3 -0
  71. package/dist/tui/commands/mcp.d.ts.map +1 -0
  72. package/dist/tui/commands/mode.d.ts +3 -0
  73. package/dist/tui/commands/mode.d.ts.map +1 -0
  74. package/dist/tui/commands/models-pack.d.ts +3 -0
  75. package/dist/tui/commands/models-pack.d.ts.map +1 -0
  76. package/dist/tui/commands/models.d.ts +3 -0
  77. package/dist/tui/commands/models.d.ts.map +1 -0
  78. package/dist/tui/commands/name.d.ts +3 -0
  79. package/dist/tui/commands/name.d.ts.map +1 -0
  80. package/dist/tui/commands/new.d.ts +3 -0
  81. package/dist/tui/commands/new.d.ts.map +1 -0
  82. package/dist/tui/commands/om.d.ts +3 -0
  83. package/dist/tui/commands/om.d.ts.map +1 -0
  84. package/dist/tui/commands/permissions.d.ts +3 -0
  85. package/dist/tui/commands/permissions.d.ts.map +1 -0
  86. package/dist/tui/commands/resource.d.ts +3 -0
  87. package/dist/tui/commands/resource.d.ts.map +1 -0
  88. package/dist/tui/commands/review.d.ts +3 -0
  89. package/dist/tui/commands/review.d.ts.map +1 -0
  90. package/dist/tui/commands/sandbox.d.ts +3 -0
  91. package/dist/tui/commands/sandbox.d.ts.map +1 -0
  92. package/dist/tui/commands/settings.d.ts +3 -0
  93. package/dist/tui/commands/settings.d.ts.map +1 -0
  94. package/dist/tui/commands/setup.d.ts +3 -0
  95. package/dist/tui/commands/setup.d.ts.map +1 -0
  96. package/dist/tui/commands/skills.d.ts +3 -0
  97. package/dist/tui/commands/skills.d.ts.map +1 -0
  98. package/dist/tui/commands/subagents.d.ts +3 -0
  99. package/dist/tui/commands/subagents.d.ts.map +1 -0
  100. package/dist/tui/commands/think.d.ts +3 -0
  101. package/dist/tui/commands/think.d.ts.map +1 -0
  102. package/dist/tui/commands/thread-tag-dir.d.ts +3 -0
  103. package/dist/tui/commands/thread-tag-dir.d.ts.map +1 -0
  104. package/dist/tui/commands/threads.d.ts +3 -0
  105. package/dist/tui/commands/threads.d.ts.map +1 -0
  106. package/dist/tui/commands/types.d.ts +28 -0
  107. package/dist/tui/commands/types.d.ts.map +1 -0
  108. package/dist/tui/commands/yolo.d.ts +3 -0
  109. package/dist/tui/commands/yolo.d.ts.map +1 -0
  110. package/dist/tui/components/banner.d.ts +9 -0
  111. package/dist/tui/components/banner.d.ts.map +1 -0
  112. package/dist/tui/components/help-overlay.d.ts +15 -0
  113. package/dist/tui/components/help-overlay.d.ts.map +1 -0
  114. package/dist/tui/components/model-selector.d.ts +3 -0
  115. package/dist/tui/components/model-selector.d.ts.map +1 -1
  116. package/dist/tui/components/om-progress.d.ts +4 -30
  117. package/dist/tui/components/om-progress.d.ts.map +1 -1
  118. package/dist/tui/components/settings.d.ts +5 -0
  119. package/dist/tui/components/settings.d.ts.map +1 -1
  120. package/dist/tui/components/tool-execution-enhanced.d.ts.map +1 -1
  121. package/dist/tui/display.d.ts +12 -0
  122. package/dist/tui/display.d.ts.map +1 -0
  123. package/dist/tui/event-dispatch.d.ts +11 -0
  124. package/dist/tui/event-dispatch.d.ts.map +1 -0
  125. package/dist/tui/handlers/agent-lifecycle.d.ts +6 -0
  126. package/dist/tui/handlers/agent-lifecycle.d.ts.map +1 -0
  127. package/dist/tui/handlers/index.d.ts +8 -0
  128. package/dist/tui/handlers/index.d.ts.map +1 -0
  129. package/dist/tui/handlers/message.d.ts +6 -0
  130. package/dist/tui/handlers/message.d.ts.map +1 -0
  131. package/dist/tui/handlers/om.d.ts +11 -0
  132. package/dist/tui/handlers/om.d.ts.map +1 -0
  133. package/dist/tui/handlers/prompts.d.ts +20 -0
  134. package/dist/tui/handlers/prompts.d.ts.map +1 -0
  135. package/dist/tui/handlers/subagent.d.ts +6 -0
  136. package/dist/tui/handlers/subagent.d.ts.map +1 -0
  137. package/dist/tui/handlers/tool.d.ts +30 -0
  138. package/dist/tui/handlers/tool.d.ts.map +1 -0
  139. package/dist/tui/handlers/types.d.ts +33 -0
  140. package/dist/tui/handlers/types.d.ts.map +1 -0
  141. package/dist/tui/mastra-tui.d.ts +9 -221
  142. package/dist/tui/mastra-tui.d.ts.map +1 -1
  143. package/dist/tui/render-messages.d.ts +22 -0
  144. package/dist/tui/render-messages.d.ts.map +1 -0
  145. package/dist/tui/setup.d.ts +17 -0
  146. package/dist/tui/setup.d.ts.map +1 -0
  147. package/dist/tui/shell.d.ts +3 -0
  148. package/dist/tui/shell.d.ts.map +1 -0
  149. package/dist/tui/state.d.ts +4 -24
  150. package/dist/tui/state.d.ts.map +1 -1
  151. package/dist/tui/status-line.d.ts +7 -0
  152. package/dist/tui/status-line.d.ts.map +1 -0
  153. package/dist/tui.cjs +17 -17
  154. package/dist/tui.js +2 -2
  155. package/dist/utils/project.d.ts +37 -9
  156. package/dist/utils/project.d.ts.map +1 -1
  157. package/dist/utils/storage-factory.d.ts +21 -0
  158. package/dist/utils/storage-factory.d.ts.map +1 -0
  159. package/package.json +7 -6
  160. package/dist/chunk-7V6U7OKQ.js.map +0 -1
  161. package/dist/chunk-BQ4ZKTYN.cjs +0 -7915
  162. package/dist/chunk-BQ4ZKTYN.cjs.map +0 -1
  163. package/dist/chunk-BYMDWH2E.js.map +0 -1
  164. package/dist/chunk-FYTZFUHD.js.map +0 -1
  165. package/dist/chunk-LVGWM7ZS.cjs.map +0 -1
  166. package/dist/chunk-QDLLGD43.cjs.map +0 -1
@@ -1,1253 +1,31 @@
1
- import { exec, spawn, execSync } from 'child_process';
2
- import { createHash } from 'crypto';
3
- import * as fs9 from 'fs';
4
- import fs9__default, { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, promises } from 'fs';
5
- import os, { homedir } from 'os';
1
+ import { AuthStorage, getAppDataDir } from './chunk-VRZZSUQE.js';
2
+ import { exec, spawn } from 'child_process';
3
+ import * as fs8 from 'fs';
4
+ import { readFileSync, writeFileSync, promises, existsSync, mkdirSync } from 'fs';
5
+ import { homedir } from 'os';
6
6
  import * as path from 'path';
7
7
  import path__default, { resolve, join, dirname } from 'path';
8
- import { createAnthropic } from '@ai-sdk/anthropic';
9
- import { wrapLanguageModel } from 'ai';
10
- import { createOpenAI } from '@ai-sdk/openai';
11
- import { ModelRouterLanguageModel } from '@mastra/core/llm';
12
- import { promisify } from 'util';
13
- import { createTool } from '@mastra/core/tools';
14
- import { z } from 'zod';
15
- import { Tiktoken } from 'js-tiktoken/lite';
16
- import o200k_base from 'js-tiktoken/ranks/o200k_base';
17
- import { execa, ExecaError } from 'execa';
18
- import stripAnsi from 'strip-ansi';
19
- import treeKill from 'tree-kill';
20
- import { StreamMessageReader, StreamMessageWriter, createMessageConnection } from 'vscode-jsonrpc/node.js';
21
- import { Position, TextDocumentIdentifier } from 'vscode-languageserver-protocol';
22
- import { createRequire } from 'module';
23
- import { pathToFileURL } from 'url';
24
- import { distance } from 'fastest-levenshtein';
25
- import { tavily } from '@tavily/core';
26
- import { parse, Lang } from '@ast-grep/napi';
27
- import chalk from 'chalk';
28
-
29
- // src/utils/project.ts
30
- function git(args, cwd) {
31
- try {
32
- return execSync(`git ${args}`, {
33
- cwd,
34
- encoding: "utf-8",
35
- stdio: ["pipe", "pipe", "pipe"]
36
- }).trim();
37
- } catch {
38
- return void 0;
39
- }
40
- }
41
- function slugify(str) {
42
- return str.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
43
- }
44
- function shortHash(str) {
45
- return createHash("sha256").update(str).digest("hex").slice(0, 12);
46
- }
47
- function normalizeGitUrl(url) {
48
- return url.replace(/\.git$/, "").replace(/^git@([^:]+):/, "https://$1/").replace(/^ssh:\/\/git@/, "https://").toLowerCase();
49
- }
50
- function detectProject(projectPath) {
51
- const absolutePath = path__default.resolve(projectPath);
52
- const gitDir = git("rev-parse --git-dir", absolutePath);
53
- const isGitRepo = gitDir !== void 0;
54
- let rootPath = absolutePath;
55
- let gitUrl;
56
- let gitBranch;
57
- let isWorktree = false;
58
- let mainRepoPath;
59
- if (isGitRepo) {
60
- rootPath = git("rev-parse --show-toplevel", absolutePath) || absolutePath;
61
- const commonDir = git("rev-parse --git-common-dir", absolutePath);
62
- if (commonDir && commonDir !== ".git" && commonDir !== gitDir) {
63
- isWorktree = true;
64
- mainRepoPath = path__default.dirname(path__default.resolve(rootPath, commonDir));
65
- }
66
- gitUrl = git("remote get-url origin", absolutePath);
67
- if (!gitUrl) {
68
- const remotes = git("remote", absolutePath);
69
- if (remotes) {
70
- const firstRemote = remotes.split("\n")[0];
71
- if (firstRemote) {
72
- gitUrl = git(`remote get-url ${firstRemote}`, absolutePath);
73
- }
74
- }
75
- }
76
- gitBranch = git("rev-parse --abbrev-ref HEAD", absolutePath);
77
- }
78
- let resourceIdSource;
79
- if (gitUrl) {
80
- resourceIdSource = normalizeGitUrl(gitUrl);
81
- } else if (mainRepoPath) {
82
- resourceIdSource = mainRepoPath;
83
- } else {
84
- resourceIdSource = rootPath;
85
- }
86
- const baseName = gitUrl ? gitUrl.split("/").pop()?.replace(/\.git$/, "") || "project" : path__default.basename(rootPath);
87
- const resourceId = `${slugify(baseName)}-${shortHash(resourceIdSource)}`;
88
- return {
89
- resourceId,
90
- name: baseName,
91
- rootPath,
92
- gitUrl,
93
- gitBranch,
94
- isWorktree,
95
- mainRepoPath
96
- };
97
- }
98
- function getAppDataDir() {
99
- const platform = os.platform();
100
- let baseDir;
101
- if (platform === "darwin") {
102
- baseDir = path__default.join(os.homedir(), "Library", "Application Support");
103
- } else if (platform === "win32") {
104
- baseDir = process.env.APPDATA || path__default.join(os.homedir(), "AppData", "Roaming");
105
- } else {
106
- baseDir = process.env.XDG_DATA_HOME || path__default.join(os.homedir(), ".local", "share");
107
- }
108
- const appDir = path__default.join(baseDir, "mastracode");
109
- if (!fs9__default.existsSync(appDir)) {
110
- fs9__default.mkdirSync(appDir, { recursive: true });
111
- }
112
- return appDir;
113
- }
114
- function getDatabasePath() {
115
- if (process.env.MASTRA_DB_PATH) {
116
- return process.env.MASTRA_DB_PATH;
117
- }
118
- return path__default.join(getAppDataDir(), "mastra.db");
119
- }
120
- function getStorageConfig(projectDir) {
121
- if (process.env.MASTRA_DB_URL) {
122
- return {
123
- url: process.env.MASTRA_DB_URL,
124
- authToken: process.env.MASTRA_DB_AUTH_TOKEN,
125
- isRemote: !process.env.MASTRA_DB_URL.startsWith("file:")
126
- };
127
- }
128
- if (projectDir) {
129
- const projectConfig = loadDatabaseConfig(path__default.join(projectDir, ".mastracode", "database.json"));
130
- if (projectConfig) return projectConfig;
131
- }
132
- const globalConfig = loadDatabaseConfig(path__default.join(os.homedir(), ".mastracode", "database.json"));
133
- if (globalConfig) return globalConfig;
134
- return {
135
- url: `file:${getDatabasePath()}`,
136
- isRemote: false
137
- };
138
- }
139
- function loadDatabaseConfig(filePath) {
140
- try {
141
- if (!fs9__default.existsSync(filePath)) return null;
142
- const raw = fs9__default.readFileSync(filePath, "utf-8");
143
- const parsed = JSON.parse(raw);
144
- if (typeof parsed?.url === "string" && parsed.url) {
145
- return {
146
- url: parsed.url,
147
- authToken: typeof parsed.authToken === "string" ? parsed.authToken : void 0,
148
- isRemote: !parsed.url.startsWith("file:")
149
- };
150
- }
151
- return null;
152
- } catch {
153
- return null;
154
- }
155
- }
156
- function getUserId(projectDir) {
157
- if (process.env.MASTRA_USER_ID) {
158
- return process.env.MASTRA_USER_ID;
159
- }
160
- const cwd = projectDir || process.cwd();
161
- const email = git("config user.email", cwd);
162
- if (email) {
163
- return email;
164
- }
165
- return os.userInfo().username || "unknown";
166
- }
167
- function getOmScope(projectDir) {
168
- const envScope = process.env.MASTRA_OM_SCOPE;
169
- if (envScope === "thread" || envScope === "resource") {
170
- return envScope;
171
- }
172
- if (projectDir) {
173
- const scope2 = loadOmScopeFromConfig(path__default.join(projectDir, ".mastracode", "database.json"));
174
- if (scope2) return scope2;
175
- }
176
- const scope = loadOmScopeFromConfig(path__default.join(os.homedir(), ".mastracode", "database.json"));
177
- if (scope) return scope;
178
- return "thread";
179
- }
180
- function loadOmScopeFromConfig(filePath) {
181
- try {
182
- if (!fs9__default.existsSync(filePath)) return null;
183
- const raw = fs9__default.readFileSync(filePath, "utf-8");
184
- const parsed = JSON.parse(raw);
185
- if (parsed?.omScope === "thread" || parsed?.omScope === "resource") {
186
- return parsed.omScope;
187
- }
188
- return null;
189
- } catch {
190
- return null;
191
- }
192
- }
193
- function getResourceIdOverride(projectDir) {
194
- if (process.env.MASTRA_RESOURCE_ID) {
195
- return process.env.MASTRA_RESOURCE_ID;
196
- }
197
- if (projectDir) {
198
- const rid2 = loadStringField(path__default.join(projectDir, ".mastracode", "database.json"), "resourceId");
199
- if (rid2) return rid2;
200
- }
201
- const rid = loadStringField(path__default.join(os.homedir(), ".mastracode", "database.json"), "resourceId");
202
- if (rid) return rid;
203
- return null;
204
- }
205
- function loadStringField(filePath, field) {
206
- try {
207
- if (!fs9__default.existsSync(filePath)) return null;
208
- const raw = fs9__default.readFileSync(filePath, "utf-8");
209
- const parsed = JSON.parse(raw);
210
- const value = parsed?.[field];
211
- if (typeof value === "string" && value) {
212
- return value;
213
- }
214
- return null;
215
- } catch {
216
- return null;
217
- }
218
- }
219
-
220
- // src/auth/pkce.ts
221
- function base64urlEncode(bytes) {
222
- let binary = "";
223
- for (const byte of bytes) {
224
- binary += String.fromCharCode(byte);
225
- }
226
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
227
- }
228
- async function generatePKCE() {
229
- const verifierBytes = new Uint8Array(32);
230
- crypto.getRandomValues(verifierBytes);
231
- const verifier = base64urlEncode(verifierBytes);
232
- const encoder = new TextEncoder();
233
- const data = encoder.encode(verifier);
234
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
235
- const challenge = base64urlEncode(new Uint8Array(hashBuffer));
236
- return { verifier, challenge };
237
- }
238
-
239
- // src/auth/providers/anthropic.ts
240
- var decode = (s) => atob(s);
241
- var CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
242
- var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
243
- var TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
244
- var REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
245
- var SCOPES = "org:create_api_key user:profile user:inference";
246
- async function loginAnthropic(onAuthUrl, onPromptCode) {
247
- const { verifier, challenge } = await generatePKCE();
248
- const authParams = new URLSearchParams({
249
- code: "true",
250
- client_id: CLIENT_ID,
251
- response_type: "code",
252
- redirect_uri: REDIRECT_URI,
253
- scope: SCOPES,
254
- code_challenge: challenge,
255
- code_challenge_method: "S256",
256
- state: verifier
257
- });
258
- const authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;
259
- onAuthUrl(authUrl);
260
- const authCode = await onPromptCode();
261
- const splits = authCode.split("#");
262
- const code = splits[0];
263
- const state = splits[1];
264
- const tokenResponse = await fetch(TOKEN_URL, {
265
- method: "POST",
266
- headers: {
267
- "Content-Type": "application/json"
268
- },
269
- body: JSON.stringify({
270
- grant_type: "authorization_code",
271
- client_id: CLIENT_ID,
272
- code,
273
- state,
274
- redirect_uri: REDIRECT_URI,
275
- code_verifier: verifier
276
- })
277
- });
278
- if (!tokenResponse.ok) {
279
- const error = await tokenResponse.text();
280
- throw new Error(`Token exchange failed: ${error}`);
281
- }
282
- const tokenData = await tokenResponse.json();
283
- const expiresAt = Date.now() + tokenData.expires_in * 1e3 - 5 * 60 * 1e3;
284
- return {
285
- refresh: tokenData.refresh_token,
286
- access: tokenData.access_token,
287
- expires: expiresAt
288
- };
289
- }
290
- async function refreshAnthropicToken(refreshToken) {
291
- const response = await fetch(TOKEN_URL, {
292
- method: "POST",
293
- headers: { "Content-Type": "application/json" },
294
- body: JSON.stringify({
295
- grant_type: "refresh_token",
296
- client_id: CLIENT_ID,
297
- refresh_token: refreshToken
298
- })
299
- });
300
- if (!response.ok) {
301
- const error = await response.text();
302
- throw new Error(`Anthropic token refresh failed: ${error}`);
303
- }
304
- const data = await response.json();
305
- return {
306
- refresh: data.refresh_token,
307
- access: data.access_token,
308
- expires: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
309
- };
310
- }
311
- var anthropicOAuthProvider = {
312
- id: "anthropic",
313
- name: "Anthropic (Claude Pro/Max)",
314
- async login(callbacks) {
315
- return loginAnthropic(
316
- (url) => callbacks.onAuth({ url }),
317
- () => callbacks.onPrompt({ message: "Paste the authorization code:" })
318
- );
319
- },
320
- async refreshToken(credentials) {
321
- return refreshAnthropicToken(credentials.refresh);
322
- },
323
- getApiKey(credentials) {
324
- return credentials.access;
325
- }
326
- };
327
-
328
- // src/auth/providers/openai-codex.ts
329
- var _randomBytes = null;
330
- var _http = null;
331
- if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
332
- import('crypto').then((m) => {
333
- _randomBytes = m.randomBytes;
334
- });
335
- import('http').then((m) => {
336
- _http = m;
337
- });
338
- }
339
- var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
340
- var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
341
- var TOKEN_URL2 = "https://auth.openai.com/oauth/token";
342
- var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
343
- var SCOPE = "openid profile email offline_access";
344
- var JWT_CLAIM_PATH = "https://api.openai.com/auth";
345
- var SUCCESS_HTML = `<!doctype html>
346
- <html lang="en">
347
- <head>
348
- <meta charset="utf-8" />
349
- <meta name="viewport" content="width=device-width, initial-scale=1" />
350
- <title>Authentication successful</title>
351
- </head>
352
- <body>
353
- <p>Authentication successful. Return to your terminal to continue.</p>
354
- </body>
355
- </html>`;
356
- function createState() {
357
- if (!_randomBytes) {
358
- throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
359
- }
360
- return _randomBytes(16).toString("hex");
361
- }
362
- function parseAuthorizationInput(input) {
363
- const value = input.trim();
364
- if (!value) return {};
365
- try {
366
- const url = new URL(value);
367
- return {
368
- code: url.searchParams.get("code") ?? void 0,
369
- state: url.searchParams.get("state") ?? void 0
370
- };
371
- } catch {
372
- }
373
- if (value.includes("#")) {
374
- const [code, state] = value.split("#", 2);
375
- return { code, state };
376
- }
377
- if (value.includes("code=")) {
378
- const params = new URLSearchParams(value);
379
- return {
380
- code: params.get("code") ?? void 0,
381
- state: params.get("state") ?? void 0
382
- };
383
- }
384
- return { code: value };
385
- }
386
- function decodeJwt(token) {
387
- try {
388
- const parts = token.split(".");
389
- if (parts.length !== 3) return null;
390
- const payload = parts[1] ?? "";
391
- const decoded = atob(payload);
392
- return JSON.parse(decoded);
393
- } catch {
394
- return null;
395
- }
396
- }
397
- async function exchangeAuthorizationCode(code, verifier, redirectUri = REDIRECT_URI2) {
398
- const response = await fetch(TOKEN_URL2, {
399
- method: "POST",
400
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
401
- body: new URLSearchParams({
402
- grant_type: "authorization_code",
403
- client_id: CLIENT_ID2,
404
- code,
405
- code_verifier: verifier,
406
- redirect_uri: redirectUri
407
- })
408
- });
409
- if (!response.ok) {
410
- const text = await response.text().catch(() => "");
411
- console.error("[openai-codex] code->token failed:", response.status, text);
412
- return { type: "failed" };
413
- }
414
- const json = await response.json();
415
- if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
416
- console.error("[openai-codex] token response missing fields:", json);
417
- return { type: "failed" };
418
- }
419
- return {
420
- type: "success",
421
- access: json.access_token,
422
- refresh: json.refresh_token,
423
- expires: Date.now() + json.expires_in * 1e3
424
- };
425
- }
426
- async function refreshAccessToken(refreshToken) {
427
- try {
428
- const response = await fetch(TOKEN_URL2, {
429
- method: "POST",
430
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
431
- body: new URLSearchParams({
432
- grant_type: "refresh_token",
433
- refresh_token: refreshToken,
434
- client_id: CLIENT_ID2
435
- })
436
- });
437
- if (!response.ok) {
438
- const text = await response.text().catch(() => "");
439
- console.error("[openai-codex] Token refresh failed:", response.status, text);
440
- return { type: "failed" };
441
- }
442
- const json = await response.json();
443
- if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
444
- console.error("[openai-codex] Token refresh response missing fields:", json);
445
- return { type: "failed" };
446
- }
447
- return {
448
- type: "success",
449
- access: json.access_token,
450
- refresh: json.refresh_token,
451
- expires: Date.now() + json.expires_in * 1e3
452
- };
453
- } catch (error) {
454
- console.error("[openai-codex] Token refresh error:", error);
455
- return { type: "failed" };
456
- }
457
- }
458
- async function createAuthorizationFlow(originator = "pi") {
459
- const { verifier, challenge } = await generatePKCE();
460
- const state = createState();
461
- const url = new URL(AUTHORIZE_URL2);
462
- url.searchParams.set("response_type", "code");
463
- url.searchParams.set("client_id", CLIENT_ID2);
464
- url.searchParams.set("redirect_uri", REDIRECT_URI2);
465
- url.searchParams.set("scope", SCOPE);
466
- url.searchParams.set("code_challenge", challenge);
467
- url.searchParams.set("code_challenge_method", "S256");
468
- url.searchParams.set("state", state);
469
- url.searchParams.set("id_token_add_organizations", "true");
470
- url.searchParams.set("codex_cli_simplified_flow", "true");
471
- url.searchParams.set("originator", originator);
472
- return { verifier, state, url: url.toString() };
473
- }
474
- function startLocalOAuthServer(state) {
475
- if (!_http) {
476
- throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
477
- }
478
- let lastCode = null;
479
- let cancelled = false;
480
- const server = _http.createServer((req, res) => {
481
- try {
482
- const url = new URL(req.url || "", "http://localhost");
483
- if (url.pathname !== "/auth/callback") {
484
- res.statusCode = 404;
485
- res.end("Not found");
486
- return;
487
- }
488
- if (url.searchParams.get("state") !== state) {
489
- res.statusCode = 400;
490
- res.end("State mismatch");
491
- return;
492
- }
493
- const code = url.searchParams.get("code");
494
- if (!code) {
495
- res.statusCode = 400;
496
- res.end("Missing authorization code");
497
- return;
498
- }
499
- res.statusCode = 200;
500
- res.setHeader("Content-Type", "text/html; charset=utf-8");
501
- res.end(SUCCESS_HTML);
502
- lastCode = code;
503
- } catch {
504
- res.statusCode = 500;
505
- res.end("Internal error");
506
- }
507
- });
508
- return new Promise((resolve10) => {
509
- server.listen(1455, "127.0.0.1", () => {
510
- resolve10({
511
- close: () => server.close(),
512
- cancelWait: () => {
513
- cancelled = true;
514
- },
515
- waitForCode: async () => {
516
- const sleep = () => new Promise((r) => setTimeout(r, 100));
517
- for (let i = 0; i < 600; i += 1) {
518
- if (lastCode) return { code: lastCode };
519
- if (cancelled) return null;
520
- await sleep();
521
- }
522
- return null;
523
- }
524
- });
525
- }).on("error", (err) => {
526
- console.error(
527
- "[openai-codex] Failed to bind http://127.0.0.1:1455 (",
528
- err.code,
529
- ") Falling back to manual paste."
530
- );
531
- resolve10({
532
- close: () => {
533
- try {
534
- server.close();
535
- } catch {
536
- }
537
- },
538
- cancelWait: () => {
539
- },
540
- waitForCode: async () => null
541
- });
542
- });
543
- });
544
- }
545
- function getAccountId(accessToken) {
546
- const payload = decodeJwt(accessToken);
547
- const auth = payload?.[JWT_CLAIM_PATH];
548
- const accountId = auth?.chatgpt_account_id;
549
- return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
550
- }
551
- async function loginOpenAICodex(options) {
552
- const { verifier, state, url } = await createAuthorizationFlow(options.originator);
553
- const server = await startLocalOAuthServer(state);
554
- options.onAuth({
555
- url,
556
- instructions: "A browser window should open. Complete login to finish."
557
- });
558
- let code;
559
- try {
560
- if (options.onManualCodeInput) {
561
- let manualCode;
562
- let manualError;
563
- const manualPromise = options.onManualCodeInput().then((input) => {
564
- manualCode = input;
565
- server.cancelWait();
566
- }).catch((err) => {
567
- manualError = err instanceof Error ? err : new Error(String(err));
568
- server.cancelWait();
569
- });
570
- const result = await server.waitForCode();
571
- if (manualError) {
572
- throw manualError;
573
- }
574
- if (result?.code) {
575
- code = result.code;
576
- } else if (manualCode) {
577
- const parsed = parseAuthorizationInput(manualCode);
578
- if (parsed.state && parsed.state !== state) {
579
- throw new Error("State mismatch");
580
- }
581
- code = parsed.code;
582
- }
583
- if (!code) {
584
- await manualPromise;
585
- if (manualError) {
586
- throw manualError;
587
- }
588
- if (manualCode) {
589
- const parsed = parseAuthorizationInput(manualCode);
590
- if (parsed.state && parsed.state !== state) {
591
- throw new Error("State mismatch");
592
- }
593
- code = parsed.code;
594
- }
595
- }
596
- } else {
597
- const result = await server.waitForCode();
598
- if (result?.code) {
599
- code = result.code;
600
- }
601
- }
602
- if (!code) {
603
- const input = await options.onPrompt({
604
- message: "Paste the authorization code (or full redirect URL):"
605
- });
606
- const parsed = parseAuthorizationInput(input);
607
- if (parsed.state && parsed.state !== state) {
608
- throw new Error("State mismatch");
609
- }
610
- code = parsed.code;
611
- }
612
- if (!code) {
613
- throw new Error("Missing authorization code");
614
- }
615
- const tokenResult = await exchangeAuthorizationCode(code, verifier);
616
- if (tokenResult.type !== "success") {
617
- throw new Error("Token exchange failed");
618
- }
619
- const accountId = getAccountId(tokenResult.access);
620
- if (!accountId) {
621
- throw new Error("Failed to extract accountId from token");
622
- }
623
- return {
624
- access: tokenResult.access,
625
- refresh: tokenResult.refresh,
626
- expires: tokenResult.expires,
627
- accountId
628
- };
629
- } finally {
630
- server.close();
631
- }
632
- }
633
- async function refreshOpenAICodexToken(refreshToken) {
634
- const result = await refreshAccessToken(refreshToken);
635
- if (result.type !== "success") {
636
- throw new Error("Failed to refresh OpenAI Codex token");
637
- }
638
- const accountId = getAccountId(result.access);
639
- if (!accountId) {
640
- throw new Error("Failed to extract accountId from token");
641
- }
642
- return {
643
- access: result.access,
644
- refresh: result.refresh,
645
- expires: result.expires,
646
- accountId
647
- };
648
- }
649
- var openaiCodexOAuthProvider = {
650
- id: "openai-codex",
651
- name: "ChatGPT Plus/Pro (Codex Subscription)",
652
- usesCallbackServer: true,
653
- async login(callbacks) {
654
- return loginOpenAICodex({
655
- onAuth: callbacks.onAuth,
656
- onPrompt: callbacks.onPrompt,
657
- onProgress: callbacks.onProgress,
658
- onManualCodeInput: callbacks.onManualCodeInput
659
- });
660
- },
661
- async refreshToken(credentials) {
662
- return refreshOpenAICodexToken(credentials.refresh);
663
- },
664
- getApiKey(credentials) {
665
- return credentials.access;
666
- }
667
- };
668
-
669
- // src/auth/storage.ts
670
- var PROVIDER_DEFAULT_MODELS = {
671
- anthropic: "anthropic/claude-opus-4-6",
672
- "openai-codex": "openai/gpt-5.3-codex"
673
- };
674
- var oauthProviderRegistry = /* @__PURE__ */ new Map([
675
- [anthropicOAuthProvider.id, anthropicOAuthProvider],
676
- [openaiCodexOAuthProvider.id, openaiCodexOAuthProvider]
677
- ]);
678
- function getOAuthProvider(id) {
679
- return oauthProviderRegistry.get(id);
680
- }
681
- function getOAuthProviders() {
682
- return Array.from(oauthProviderRegistry.values());
683
- }
684
- var AuthStorage = class {
685
- constructor(authPath = join(getAppDataDir(), "auth.json")) {
686
- this.authPath = authPath;
687
- this.reload();
688
- }
689
- data = {};
690
- /**
691
- * Reload credentials from disk.
692
- */
693
- reload() {
694
- if (!existsSync(this.authPath)) {
695
- this.data = {};
696
- return;
697
- }
698
- try {
699
- this.data = JSON.parse(readFileSync(this.authPath, "utf-8"));
700
- } catch {
701
- this.data = {};
702
- }
703
- }
704
- /**
705
- * Save credentials to disk.
706
- */
707
- save() {
708
- const dir = dirname(this.authPath);
709
- if (!existsSync(dir)) {
710
- mkdirSync(dir, { recursive: true, mode: 448 });
711
- }
712
- writeFileSync(this.authPath, JSON.stringify(this.data, null, 2), "utf-8");
713
- chmodSync(this.authPath, 384);
714
- }
715
- /**
716
- * Get credential for a provider.
717
- */
718
- get(provider) {
719
- return this.data[provider] ?? void 0;
720
- }
721
- /**
722
- * Set credential for a provider.
723
- */
724
- set(provider, credential) {
725
- this.data[provider] = credential;
726
- this.save();
727
- }
728
- /**
729
- * Remove credential for a provider.
730
- */
731
- remove(provider) {
732
- delete this.data[provider];
733
- this.save();
734
- }
735
- /**
736
- * List all providers with credentials.
737
- */
738
- list() {
739
- return Object.keys(this.data);
740
- }
741
- /**
742
- * Check if credentials exist for a provider.
743
- */
744
- has(provider) {
745
- return provider in this.data;
746
- }
747
- /**
748
- * Check if logged in via OAuth for a provider.
749
- */
750
- isLoggedIn(provider) {
751
- const cred = this.data[provider];
752
- return cred?.type === "oauth";
753
- }
754
- /**
755
- * Login to an OAuth provider.
756
- */
757
- async login(providerId, callbacks) {
758
- const provider = getOAuthProvider(providerId);
759
- if (!provider) {
760
- throw new Error(`Unknown OAuth provider: ${providerId}`);
761
- }
762
- const credentials = await provider.login(callbacks);
763
- this.set(providerId, { type: "oauth", ...credentials });
764
- }
765
- /**
766
- * Logout from a provider.
767
- */
768
- logout(provider) {
769
- this.remove(provider);
770
- }
771
- /**
772
- * Get API key for a provider, auto-refreshing OAuth tokens if needed.
773
- */
774
- async getApiKey(providerId) {
775
- const cred = this.data[providerId];
776
- if (cred?.type === "api_key") {
777
- return cred.key;
778
- }
779
- if (cred?.type === "oauth") {
780
- const provider = getOAuthProvider(providerId);
781
- if (!provider) {
782
- return void 0;
783
- }
784
- if (Date.now() >= cred.expires) {
785
- try {
786
- const newCreds = await provider.refreshToken(cred);
787
- this.set(providerId, { type: "oauth", ...newCreds });
788
- return provider.getApiKey(newCreds);
789
- } catch {
790
- return void 0;
791
- }
792
- }
793
- return provider.getApiKey(cred);
794
- }
795
- return void 0;
796
- }
797
- /**
798
- * Get the last used model ID (global preference).
799
- */
800
- getLastModelId() {
801
- return this.data._lastModelId;
802
- }
803
- /**
804
- * Set the last used model ID (global preference).
805
- */
806
- setLastModelId(modelId) {
807
- this.data._lastModelId = modelId;
808
- this.save();
809
- }
810
- /**
811
- * Get the global model ID for a specific mode.
812
- * @param modeId Mode ID (e.g., "build", "plan", "explore")
813
- */
814
- getModeModelId(modeId) {
815
- return this.data[`_modeModelId_${modeId}`];
816
- }
817
- /**
818
- * Set the global model ID for a specific mode.
819
- * @param modeId Mode ID (e.g., "build", "plan", "explore")
820
- * @param modelId Full model ID
821
- */
822
- setModeModelId(modeId, modelId) {
823
- this.data[`_modeModelId_${modeId}`] = modelId;
824
- this.save();
825
- }
826
- /**
827
- * Get the global subagent model ID for an agent type.
828
- * @param agentType Optional agent type (explore, plan, execute)
829
- */
830
- getSubagentModelId(agentType) {
831
- if (agentType) {
832
- return this.data[`_subagentModelId_${agentType}`];
833
- }
834
- return this.data._subagentModelId;
835
- }
836
- /**
837
- * Set the global subagent model ID for an agent type.
838
- * @param modelId Full model ID
839
- * @param agentType Optional agent type (explore, plan, execute)
840
- */
841
- setSubagentModelId(modelId, agentType) {
842
- if (agentType) {
843
- this.data[`_subagentModelId_${agentType}`] = modelId;
844
- } else {
845
- this.data._subagentModelId = modelId;
846
- }
847
- this.save();
848
- }
849
- /**
850
- * Get the default model for a provider after login.
851
- */
852
- getDefaultModelForProvider(providerId) {
853
- return PROVIDER_DEFAULT_MODELS[providerId];
854
- }
855
- // ===========================================================================
856
- // Model Usage Ranking
857
- // ===========================================================================
858
- getModelRanks() {
859
- return this.data._modelRanks ?? {};
860
- }
861
- /**
862
- * Get the use count for a model (0 if never used).
863
- */
864
- getModelUseCount(modelId) {
865
- return this.getModelRanks()[modelId] ?? 0;
866
- }
867
- /**
868
- * Get all model use counts.
869
- */
870
- getAllModelUseCounts() {
871
- return { ...this.getModelRanks() };
872
- }
873
- /**
874
- * Increment the use count for a model and persist.
875
- */
876
- incrementModelUseCount(modelId) {
877
- const ranks = this.getModelRanks();
878
- ranks[modelId] = (ranks[modelId] ?? 0) + 1;
879
- this.data._modelRanks = ranks;
880
- this.save();
881
- }
882
- };
883
- var claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
884
- var authStorageInstance = null;
885
- function getAuthStorage() {
886
- if (!authStorageInstance) {
887
- authStorageInstance = new AuthStorage();
888
- }
889
- return authStorageInstance;
890
- }
891
- function setAuthStorage(storage) {
892
- authStorageInstance = storage;
893
- }
894
- var claudeCodeMiddleware = {
895
- specificationVersion: "v3",
896
- transformParams: async ({ params }) => {
897
- const systemMessage = {
898
- role: "system",
899
- content: claudeCodeIdentity
900
- };
901
- if (params.temperature) {
902
- delete params.topP;
903
- }
904
- return {
905
- ...params,
906
- prompt: [systemMessage, ...params.prompt]
907
- };
908
- }
909
- };
910
- var promptCacheMiddleware = {
911
- specificationVersion: "v3",
912
- transformParams: async ({ params }) => {
913
- const prompt = [...params.prompt];
914
- const cacheControl = { type: "ephemeral", ttl: "5m" };
915
- const addCacheToMessage = (msg) => {
916
- if (typeof msg.content === "string") {
917
- return {
918
- ...msg,
919
- providerOptions: {
920
- ...msg.providerOptions,
921
- anthropic: { ...msg.providerOptions?.anthropic, cacheControl }
922
- }
923
- };
924
- }
925
- if (Array.isArray(msg.content) && msg.content.length > 0) {
926
- const content = [...msg.content];
927
- const lastPart = content[content.length - 1];
928
- content[content.length - 1] = {
929
- ...lastPart,
930
- providerOptions: {
931
- ...lastPart.providerOptions,
932
- anthropic: { ...lastPart.providerOptions?.anthropic, cacheControl }
933
- }
934
- };
935
- return { ...msg, content };
936
- }
937
- return msg;
938
- };
939
- let lastSystemIdx = -1;
940
- for (let i = prompt.length - 1; i >= 0; i--) {
941
- if (prompt[i].role === "system") {
942
- lastSystemIdx = i;
943
- break;
944
- }
945
- }
946
- if (lastSystemIdx >= 0) {
947
- prompt[lastSystemIdx] = addCacheToMessage(prompt[lastSystemIdx]);
948
- }
949
- const lastIdx = prompt.length - 1;
950
- if (lastIdx >= 0 && lastIdx !== lastSystemIdx) {
951
- prompt[lastIdx] = addCacheToMessage(prompt[lastIdx]);
952
- }
953
- return { ...params, prompt };
954
- }
955
- };
956
- function opencodeClaudeMaxProvider(modelId = "claude-sonnet-4-20250514") {
957
- if (process.env.NODE_ENV === "test" || process.env.VITEST) {
958
- const anthropic2 = createAnthropic({
959
- apiKey: process.env.ANTHROPIC_API_KEY || "test-api-key"
960
- });
961
- return wrapLanguageModel({
962
- model: anthropic2(modelId),
963
- middleware: [claudeCodeMiddleware, promptCacheMiddleware]
964
- });
965
- }
966
- const oauthFetch = async (url, init) => {
967
- const authStorage2 = getAuthStorage();
968
- authStorage2.reload();
969
- const accessToken = await authStorage2.getApiKey("anthropic");
970
- if (!accessToken) {
971
- throw new Error("Not logged in to Anthropic. Run /login first.");
972
- }
973
- return fetch(url, {
974
- ...init,
975
- headers: {
976
- Authorization: `Bearer ${accessToken}`,
977
- "anthropic-beta": "oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
978
- "anthropic-version": "2023-06-01"
979
- }
980
- });
981
- };
982
- const anthropic = createAnthropic({
983
- // Provide a dummy API key - the actual auth is handled via OAuth in oauthFetch
984
- // This prevents the SDK from throwing "API key is missing" at model creation time
985
- apiKey: "oauth-placeholder",
986
- fetch: oauthFetch
987
- });
988
- return wrapLanguageModel({
989
- model: anthropic(modelId),
990
- middleware: [claudeCodeMiddleware, promptCacheMiddleware]
991
- });
992
- }
993
- var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
994
- var authStorageInstance2 = null;
995
- function getAuthStorage2() {
996
- if (!authStorageInstance2) {
997
- authStorageInstance2 = new AuthStorage();
998
- }
999
- return authStorageInstance2;
1000
- }
1001
- function setAuthStorage2(storage) {
1002
- authStorageInstance2 = storage;
1003
- }
1004
- var CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
1005
-
1006
- IMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;
1007
- var openaiCodexMiddleware = {
1008
- specificationVersion: "v3",
1009
- transformParams: async ({ params }) => {
1010
- if (params.temperature) {
1011
- delete params.topP;
1012
- }
1013
- params.providerOptions = {
1014
- ...params.providerOptions,
1015
- openai: {
1016
- ...params.providerOptions?.openai ?? {},
1017
- // Codex API requires instructions
1018
- instructions: CODEX_INSTRUCTIONS,
1019
- // Codex API requires store to be false
1020
- store: false
1021
- }
1022
- };
1023
- return params;
1024
- }
1025
- };
1026
- function openaiCodexProvider(modelId = "codex-mini-latest") {
1027
- if (process.env.NODE_ENV === "test" || process.env.VITEST) {
1028
- const openai2 = createOpenAI({
1029
- apiKey: process.env.OPENAI_API_KEY || "test-api-key"
1030
- });
1031
- return wrapLanguageModel({
1032
- model: openai2.responses(modelId),
1033
- middleware: [openaiCodexMiddleware]
1034
- });
1035
- }
1036
- const oauthFetch = async (url, init) => {
1037
- const authStorage2 = getAuthStorage2();
1038
- authStorage2.reload();
1039
- const cred = authStorage2.get("openai-codex");
1040
- if (!cred || cred.type !== "oauth") {
1041
- throw new Error("Not logged in to OpenAI Codex. Run /login first.");
1042
- }
1043
- let accessToken = cred.access;
1044
- if (Date.now() >= cred.expires) {
1045
- const refreshedToken = await authStorage2.getApiKey("openai-codex");
1046
- if (!refreshedToken) {
1047
- throw new Error("Failed to refresh OpenAI Codex token. Please /login again.");
1048
- }
1049
- accessToken = refreshedToken;
1050
- authStorage2.reload();
1051
- }
1052
- const accountId = cred.accountId;
1053
- const headers = new Headers();
1054
- if (init?.headers) {
1055
- if (init.headers instanceof Headers) {
1056
- init.headers.forEach((value, key) => {
1057
- if (key.toLowerCase() !== "authorization") {
1058
- headers.set(key, value);
1059
- }
1060
- });
1061
- } else if (Array.isArray(init.headers)) {
1062
- for (const [key, value] of init.headers) {
1063
- if (key.toLowerCase() !== "authorization" && value !== void 0) {
1064
- headers.set(key, String(value));
1065
- }
1066
- }
1067
- } else {
1068
- for (const [key, value] of Object.entries(init.headers)) {
1069
- if (key.toLowerCase() !== "authorization" && value !== void 0) {
1070
- headers.set(key, String(value));
1071
- }
1072
- }
1073
- }
1074
- }
1075
- headers.set("Authorization", `Bearer ${accessToken}`);
1076
- if (accountId) {
1077
- headers.set("ChatGPT-Account-Id", accountId);
1078
- }
1079
- const parsed = url instanceof URL ? url : new URL(typeof url === "string" ? url : url.url);
1080
- const shouldRewrite = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions");
1081
- const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;
1082
- return fetch(finalUrl, {
1083
- ...init,
1084
- headers
1085
- });
1086
- };
1087
- const openai = createOpenAI({
1088
- // Use a dummy API key since we're using OAuth
1089
- apiKey: "oauth-dummy-key",
1090
- fetch: oauthFetch
1091
- });
1092
- return wrapLanguageModel({
1093
- model: openai.responses(modelId),
1094
- middleware: [openaiCodexMiddleware]
1095
- });
1096
- }
1097
- var authStorage = new AuthStorage();
1098
- function resolveModel(modelId) {
1099
- authStorage.reload();
1100
- const isAnthropicModel = modelId.startsWith("anthropic/");
1101
- const isOpenAIModel = modelId.startsWith("openai/");
1102
- const isMoonshotModel = modelId.startsWith("moonshotai/");
1103
- if (isMoonshotModel) {
1104
- if (!process.env.MOONSHOT_AI_API_KEY) {
1105
- throw new Error(`Need MOONSHOT_AI_API_KEY`);
1106
- }
1107
- return createAnthropic({
1108
- apiKey: process.env.MOONSHOT_AI_API_KEY,
1109
- baseURL: "https://api.moonshot.ai/anthropic/v1",
1110
- name: "moonshotai.anthropicv1"
1111
- })(modelId.substring("moonshotai/".length));
1112
- } else if (isAnthropicModel) {
1113
- return opencodeClaudeMaxProvider(modelId.substring(`anthropic/`.length));
1114
- } else if (isOpenAIModel && authStorage.isLoggedIn("openai-codex")) {
1115
- return openaiCodexProvider(modelId.substring(`openai/`.length));
1116
- } else {
1117
- return new ModelRouterLanguageModel(modelId);
1118
- }
1119
- }
1120
- function getDynamicModel({
1121
- requestContext
1122
- }) {
1123
- const harnessContext = requestContext.get("harness");
1124
- const modelId = harnessContext?.state?.currentModelId;
1125
- if (!modelId) {
1126
- throw new Error("No model selected. Use /models to select a model first.");
1127
- }
1128
- return resolveModel(modelId);
1129
- }
1130
-
1131
- // src/agents/subagents/execute.ts
1132
- var executeSubagent = {
1133
- id: "execute",
1134
- name: "Execute",
1135
- instructions: `You are a focused execution agent. Your job is to complete a specific, well-defined task by making the necessary changes to the codebase.
1136
-
1137
- ## Rules
1138
- - You have FULL ACCESS to read, write, and execute within your task scope.
1139
- - Stay focused on the specific task given. Do not make unrelated changes.
1140
- - Read files before modifying them \u2014 use view first, then string_replace_lsp or write_file.
1141
- - Verify your changes work by running relevant tests or checking for errors.
1142
-
1143
- ## Tool Strategy
1144
- - **Read first**: Always view a file before editing it
1145
- - **Edit precisely**: Use string_replace_lsp with enough context to match uniquely
1146
- - **Use specialized tools**: Prefer view/search_content/find_files over shell commands for reading
1147
- - **Parallelize**: Make independent tool calls together (e.g., view multiple files at once)
1148
-
1149
- ## Workflow
1150
- . Understand the task and explore relevant code
1151
- . For complex tasks (3+ steps): use task_write to track progress
1152
- . Make changes incrementally \u2014 verify each change before moving on
1153
- . Run tests or type-check to verify
1154
- . If you created tasks: ALWAYS call task_check before finishing
1155
-
1156
- ## Efficiency
1157
- Your output returns to the parent agent. Be concise:
1158
- - Don't repeat file contents in your response
1159
- - Summarize what changed, don't narrate each step
1160
- - Keep your final summary under 300 words
1161
-
1162
- ## Output Format
1163
- End with a structured summary:
1164
- . **Completed**: What you implemented (1-2 sentences)
1165
- . **Changes**: Files modified/created
1166
- . **Verification**: How you verified it works
1167
- . **Notes**: Follow-up needed (if any)`,
1168
- allowedTools: [
1169
- // Read tools
1170
- "view",
1171
- "search_content",
1172
- "find_files",
1173
- // Write tools
1174
- "string_replace_lsp",
1175
- "write_file",
1176
- // Execution tool
1177
- "execute_command",
1178
- // Task tracking (built-in harness tools)
1179
- "task_write",
1180
- "task_check"
1181
- ]
1182
- };
1183
-
1184
- // src/agents/subagents/explore.ts
1185
- var exploreSubagent = {
1186
- id: "explore",
1187
- name: "Explore",
1188
- instructions: `You are an expert code explorer. Your job is to investigate a codebase and answer a specific question or gather specific information.
1189
-
1190
- ## Rules
1191
- - You have READ-ONLY access. You cannot modify files or run commands.
1192
- - Be thorough \u2014 search broadly first, then drill into relevant files.
1193
- - After gathering enough information, produce a clear, concise summary of your findings.
1194
-
1195
- ## Tool Strategy
1196
- - **Start broad**: Use find_files (glob) to understand project structure
1197
- - **Search smart**: Use search_content (grep) with specific patterns \u2014 avoid overly broad searches
1198
- - **Read efficiently**: Use view with view_range for large files \u2014 don't read entire files if you only need a section
1199
- - **Parallelize**: Make multiple independent tool calls in one round when exploring different areas
1200
-
1201
- ## Efficiency
1202
- Your output returns to the parent agent. Be concise:
1203
- - Don't include raw file contents in your response \u2014 summarize what you found
1204
- - Reference files by path and line number, not by copying code
1205
- - If a search returns many results, report the count and key examples, not every match
1206
-
1207
- ## Output Format
1208
- End with a structured summary:
1209
- . **Answer**: Direct answer to the question (1-2 sentences)
1210
- . **Key Files**: Most relevant files with line numbers
1211
- . **Details**: Additional context if needed
1212
-
1213
- Keep your summary under 300 words.`,
1214
- allowedTools: ["view", "search_content", "find_files"]
1215
- };
1216
-
1217
- // src/agents/subagents/plan.ts
1218
- var planSubagent = {
1219
- id: "plan",
1220
- name: "Plan",
1221
- instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.
1222
-
1223
- ## Rules
1224
- - You have READ-ONLY access. You cannot modify files or run commands.
1225
- - First, explore the codebase to understand existing patterns, architecture, and conventions.
1226
- - Produce a concrete, actionable plan \u2014 not vague suggestions.
1227
-
1228
- ## Tool Strategy
1229
- - **Discover structure**: Use find_files (glob) to understand project layout and find relevant files
1230
- - **Find patterns**: Use search_content (grep) to locate existing implementations, imports, and conventions
1231
- - **Understand deeply**: Use view with view_range to read specific sections of key files
1232
- - **Parallelize**: Make multiple independent tool calls when exploring different areas
1233
-
1234
- ## Efficiency
1235
- Your output returns to the parent agent. Be concise:
1236
- - Don't include raw file contents \u2014 reference by path and line number
1237
- - Focus on actionable details, not general observations
1238
- - If you find many similar patterns, describe the pattern once with examples
1239
-
1240
- ## Output Format
1241
- Structure your plan as:
1242
-
1243
- . **Summary**: One-paragraph overview (2-3 sentences)
1244
- . **Files to Change**: List each file with specific changes needed
1245
- . **Implementation Order**: Numbered steps in dependency order
1246
- . **Risks**: Potential issues or edge cases (if any)
8
+ import { promisify } from 'util';
9
+ import { createTool } from '@mastra/core/tools';
10
+ import { z } from 'zod';
11
+ import { Tiktoken } from 'js-tiktoken/lite';
12
+ import o200k_base from 'js-tiktoken/ranks/o200k_base';
13
+ import { execa, ExecaError } from 'execa';
14
+ import stripAnsi from 'strip-ansi';
15
+ import treeKill from 'tree-kill';
16
+ import { StreamMessageReader, StreamMessageWriter, createMessageConnection } from 'vscode-jsonrpc/node.js';
17
+ import { Position, TextDocumentIdentifier } from 'vscode-languageserver-protocol';
18
+ import { createRequire } from 'module';
19
+ import { pathToFileURL } from 'url';
20
+ import { distance } from 'fastest-levenshtein';
21
+ import { createAnthropic } from '@ai-sdk/anthropic';
22
+ import { wrapLanguageModel } from 'ai';
23
+ import { createOpenAI } from '@ai-sdk/openai';
24
+ import { ModelRouterLanguageModel } from '@mastra/core/llm';
25
+ import { tavily } from '@tavily/core';
26
+ import { parse, Lang } from '@ast-grep/napi';
27
+ import chalk from 'chalk';
1247
28
 
1248
- Be specific about code locations (file paths, function names, line numbers). Keep the plan actionable and under 500 words.`,
1249
- allowedTools: ["view", "search_content", "find_files"]
1250
- };
1251
29
  var enc = new Tiktoken(o200k_base);
1252
30
  function sanitizeInput(text) {
1253
31
  if (!text) return "";
@@ -1456,7 +234,7 @@ Usage notes:
1456
234
  assertPathAllowed(absolutePath, root, allowedPaths);
1457
235
  await validatePath2("view", absolutePath);
1458
236
  if (await isDirectory(absolutePath)) {
1459
- const { stdout, stderr } = await execAsync(`find "${absolutePath}" -maxdepth 2 -not -path '*/\\\\.*'`);
237
+ const { stdout, stderr } = await execAsync(`find "${absolutePath}" -maxdepth 2 -not -path '*/.*'`);
1460
238
  if (stderr) {
1461
239
  throw new Error(stderr);
1462
240
  }
@@ -1964,7 +742,52 @@ ${truncateStringForTokenEstimate(cleanOutput, 1e3)}` : "[User aborted command]"
1964
742
  }
1965
743
  });
1966
744
  }
1967
- createExecuteCommandTool();
745
+ createExecuteCommandTool();
746
+
747
+ // src/lsp/language.ts
748
+ var LANGUAGE_EXTENSIONS = {
749
+ // TypeScript/JavaScript
750
+ ".ts": "typescript",
751
+ ".tsx": "typescriptreact",
752
+ ".js": "javascript",
753
+ ".jsx": "javascriptreact",
754
+ ".mjs": "javascript",
755
+ ".cjs": "javascript",
756
+ // Python
757
+ ".py": "python",
758
+ ".pyi": "python",
759
+ // Go
760
+ ".go": "go",
761
+ // Rust
762
+ ".rs": "rust",
763
+ // C/C++
764
+ ".c": "c",
765
+ ".cpp": "cpp",
766
+ ".cc": "cpp",
767
+ ".cxx": "cpp",
768
+ ".h": "c",
769
+ ".hpp": "cpp",
770
+ // Java
771
+ ".java": "java",
772
+ // JSON
773
+ ".json": "json",
774
+ ".jsonc": "jsonc",
775
+ // YAML
776
+ ".yaml": "yaml",
777
+ ".yml": "yaml",
778
+ // Markdown
779
+ ".md": "markdown",
780
+ // HTML/CSS
781
+ ".html": "html",
782
+ ".css": "css",
783
+ ".scss": "scss",
784
+ ".sass": "sass",
785
+ ".less": "less"
786
+ };
787
+ function getLanguageId(filePath) {
788
+ const ext = filePath.substring(filePath.lastIndexOf("."));
789
+ return LANGUAGE_EXTENSIONS[ext];
790
+ }
1968
791
  var LSPClient = class {
1969
792
  connection = null;
1970
793
  process = null;
@@ -2263,53 +1086,6 @@ var LSPClient = class {
2263
1086
  this.diagnostics = /* @__PURE__ */ new Map();
2264
1087
  }
2265
1088
  };
2266
-
2267
- // src/lsp/language.ts
2268
- var LANGUAGE_EXTENSIONS = {
2269
- // TypeScript/JavaScript
2270
- ".ts": "typescript",
2271
- ".tsx": "typescriptreact",
2272
- ".js": "javascript",
2273
- ".jsx": "javascriptreact",
2274
- ".mjs": "javascript",
2275
- ".cjs": "javascript",
2276
- // Python
2277
- ".py": "python",
2278
- ".pyi": "python",
2279
- // Go
2280
- ".go": "go",
2281
- // Rust
2282
- ".rs": "rust",
2283
- // C/C++
2284
- ".c": "c",
2285
- ".cpp": "cpp",
2286
- ".cc": "cpp",
2287
- ".cxx": "cpp",
2288
- ".h": "c",
2289
- ".hpp": "cpp",
2290
- // Java
2291
- ".java": "java",
2292
- // JSON
2293
- ".json": "json",
2294
- ".jsonc": "jsonc",
2295
- // YAML
2296
- ".yaml": "yaml",
2297
- ".yml": "yaml",
2298
- // Markdown
2299
- ".md": "markdown",
2300
- // HTML/CSS
2301
- ".html": "html",
2302
- ".css": "css",
2303
- ".scss": "scss",
2304
- ".sass": "sass",
2305
- ".less": "less"
2306
- };
2307
- function getLanguageId(filePath) {
2308
- const ext = filePath.substring(filePath.lastIndexOf("."));
2309
- return LANGUAGE_EXTENSIONS[ext];
2310
- }
2311
-
2312
- // src/lsp/server.ts
2313
1089
  function findNearestRoot(cwd, markers) {
2314
1090
  let current = cwd;
2315
1091
  while (current !== "/") {
@@ -2534,8 +1310,8 @@ function findWorkspaceRoot(filePath) {
2534
1310
  let closestProjectRoot = null;
2535
1311
  let searchDir = currentDir;
2536
1312
  while (searchDir !== root) {
2537
- const hasTsConfig = fs9.existsSync(path.join(searchDir, "tsconfig.json"));
2538
- const hasPackageJson = fs9.existsSync(path.join(searchDir, "package.json"));
1313
+ const hasTsConfig = fs8.existsSync(path.join(searchDir, "tsconfig.json"));
1314
+ const hasPackageJson = fs8.existsSync(path.join(searchDir, "package.json"));
2539
1315
  if (hasTsConfig || hasPackageJson) {
2540
1316
  closestProjectRoot = searchDir;
2541
1317
  break;
@@ -2553,7 +1329,7 @@ function findWorkspaceRoot(filePath) {
2553
1329
  while (currentDir !== root) {
2554
1330
  for (const marker of WORKSPACE_MARKERS) {
2555
1331
  const markerPath = path.join(currentDir, marker);
2556
- if (fs9.existsSync(markerPath)) {
1332
+ if (fs8.existsSync(markerPath)) {
2557
1333
  return currentDir;
2558
1334
  }
2559
1335
  }
@@ -2581,7 +1357,7 @@ var FileEditor = class {
2581
1357
  async view(args) {
2582
1358
  await validatePath("view", args.path);
2583
1359
  if (await this.isDirectory(args.path)) {
2584
- const { stdout, stderr } = await this.execAsync(`find "${args.path}" -maxdepth 2 -not -path '*/\\\\.*'`);
1360
+ const { stdout, stderr } = await this.execAsync(`find "${args.path}" -maxdepth 2 -not -path '*/.*'`);
2585
1361
  if (stderr) return stderr;
2586
1362
  return `Here's the files and directories up to 2 levels deep in ${args.path}, excluding hidden items:
2587
1363
  ${stdout}
@@ -2940,8 +1716,8 @@ Usage notes:
2940
1716
  const workspaceRoot = findWorkspaceRoot(absoluteFilePath);
2941
1717
  const client = await lspManager.getClient(absoluteFilePath, workspaceRoot);
2942
1718
  if (client) {
2943
- const contentNew = fs9.readFileSync(absoluteFilePath, "utf-8");
2944
- const languageId = path.extname(absoluteFilePath).slice(1);
1719
+ const contentNew = fs8.readFileSync(absoluteFilePath, "utf-8");
1720
+ const languageId = getLanguageId(absoluteFilePath) || path.extname(absoluteFilePath).slice(1);
2945
1721
  client.notifyOpen(absoluteFilePath, contentNew, languageId);
2946
1722
  client.notifyChange(absoluteFilePath, contentNew, 1);
2947
1723
  const diagnostics = await client.waitForDiagnostics(absoluteFilePath, 3e3).catch(() => []);
@@ -3250,7 +2026,7 @@ function escapeRegex(s) {
3250
2026
  }
3251
2027
  async function getModTime(filePath) {
3252
2028
  try {
3253
- const stat = await fs9.promises.stat(filePath);
2029
+ const stat = await fs8.promises.stat(filePath);
3254
2030
  return stat.mtimeMs;
3255
2031
  } catch {
3256
2032
  return 0;
@@ -3338,12 +2114,12 @@ Usage notes:
3338
2114
  const absolutePath = path.resolve(root, filePath);
3339
2115
  const allowedPaths = getAllowedPathsFromContext(toolContext);
3340
2116
  assertPathAllowed(absolutePath, root, allowedPaths);
3341
- const exists = fs9.existsSync(absolutePath);
2117
+ const exists = fs8.existsSync(absolutePath);
3342
2118
  const dir = path.dirname(absolutePath);
3343
- if (!fs9.existsSync(dir)) {
3344
- fs9.mkdirSync(dir, { recursive: true });
2119
+ if (!fs8.existsSync(dir)) {
2120
+ fs8.mkdirSync(dir, { recursive: true });
3345
2121
  }
3346
- fs9.writeFileSync(absolutePath, context.content, "utf-8");
2122
+ fs8.writeFileSync(absolutePath, context.content, "utf-8");
3347
2123
  const lineCount = context.content.split("\n").length;
3348
2124
  const relPath = path.relative(root, absolutePath);
3349
2125
  if (exists) {
@@ -3357,16 +2133,384 @@ Usage notes:
3357
2133
  isError: false
3358
2134
  };
3359
2135
  }
3360
- } catch (error) {
3361
- const msg = error instanceof Error ? error.message : "Unknown error";
3362
- return {
3363
- content: `write_file failed: ${msg}`,
3364
- isError: true
3365
- };
2136
+ } catch (error) {
2137
+ const msg = error instanceof Error ? error.message : "Unknown error";
2138
+ return {
2139
+ content: `write_file failed: ${msg}`,
2140
+ isError: true
2141
+ };
2142
+ }
2143
+ }
2144
+ });
2145
+ }
2146
+ var claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
2147
+ var authStorageInstance = null;
2148
+ function getAuthStorage() {
2149
+ if (!authStorageInstance) {
2150
+ authStorageInstance = new AuthStorage();
2151
+ }
2152
+ return authStorageInstance;
2153
+ }
2154
+ function setAuthStorage(storage) {
2155
+ authStorageInstance = storage;
2156
+ }
2157
+ var claudeCodeMiddleware = {
2158
+ specificationVersion: "v3",
2159
+ transformParams: async ({ params }) => {
2160
+ const systemMessage = {
2161
+ role: "system",
2162
+ content: claudeCodeIdentity
2163
+ };
2164
+ if (params.temperature) {
2165
+ delete params.topP;
2166
+ }
2167
+ return {
2168
+ ...params,
2169
+ prompt: [systemMessage, ...params.prompt]
2170
+ };
2171
+ }
2172
+ };
2173
+ var promptCacheMiddleware = {
2174
+ specificationVersion: "v3",
2175
+ transformParams: async ({ params }) => {
2176
+ const prompt = [...params.prompt];
2177
+ const cacheControl = { type: "ephemeral", ttl: "5m" };
2178
+ const addCacheToMessage = (msg) => {
2179
+ if (typeof msg.content === "string") {
2180
+ return {
2181
+ ...msg,
2182
+ providerOptions: {
2183
+ ...msg.providerOptions,
2184
+ anthropic: { ...msg.providerOptions?.anthropic, cacheControl }
2185
+ }
2186
+ };
2187
+ }
2188
+ if (Array.isArray(msg.content) && msg.content.length > 0) {
2189
+ const content = [...msg.content];
2190
+ const lastPart = content[content.length - 1];
2191
+ content[content.length - 1] = {
2192
+ ...lastPart,
2193
+ providerOptions: {
2194
+ ...lastPart.providerOptions,
2195
+ anthropic: { ...lastPart.providerOptions?.anthropic, cacheControl }
2196
+ }
2197
+ };
2198
+ return { ...msg, content };
2199
+ }
2200
+ return msg;
2201
+ };
2202
+ let lastSystemIdx = -1;
2203
+ for (let i = prompt.length - 1; i >= 0; i--) {
2204
+ if (prompt[i].role === "system") {
2205
+ lastSystemIdx = i;
2206
+ break;
2207
+ }
2208
+ }
2209
+ if (lastSystemIdx >= 0) {
2210
+ prompt[lastSystemIdx] = addCacheToMessage(prompt[lastSystemIdx]);
2211
+ }
2212
+ const lastIdx = prompt.length - 1;
2213
+ if (lastIdx >= 0 && lastIdx !== lastSystemIdx) {
2214
+ prompt[lastIdx] = addCacheToMessage(prompt[lastIdx]);
2215
+ }
2216
+ return { ...params, prompt };
2217
+ }
2218
+ };
2219
+ function opencodeClaudeMaxProvider(modelId = "claude-sonnet-4-20250514") {
2220
+ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
2221
+ const anthropic2 = createAnthropic({
2222
+ apiKey: process.env.ANTHROPIC_API_KEY || "test-api-key"
2223
+ });
2224
+ return wrapLanguageModel({
2225
+ model: anthropic2(modelId),
2226
+ middleware: [claudeCodeMiddleware, promptCacheMiddleware]
2227
+ });
2228
+ }
2229
+ const oauthFetch = async (url, init) => {
2230
+ const authStorage2 = getAuthStorage();
2231
+ authStorage2.reload();
2232
+ const accessToken = await authStorage2.getApiKey("anthropic");
2233
+ if (!accessToken) {
2234
+ throw new Error("Not logged in to Anthropic. Run /login first.");
2235
+ }
2236
+ return fetch(url, {
2237
+ ...init,
2238
+ headers: {
2239
+ Authorization: `Bearer ${accessToken}`,
2240
+ "anthropic-beta": "oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
2241
+ "anthropic-version": "2023-06-01"
2242
+ }
2243
+ });
2244
+ };
2245
+ const anthropic = createAnthropic({
2246
+ // Provide a dummy API key - the actual auth is handled via OAuth in oauthFetch
2247
+ // This prevents the SDK from throwing "API key is missing" at model creation time
2248
+ apiKey: "oauth-placeholder",
2249
+ fetch: oauthFetch
2250
+ });
2251
+ return wrapLanguageModel({
2252
+ model: anthropic(modelId),
2253
+ middleware: [claudeCodeMiddleware, promptCacheMiddleware]
2254
+ });
2255
+ }
2256
+ var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
2257
+ var authStorageInstance2 = null;
2258
+ function getAuthStorage2() {
2259
+ if (!authStorageInstance2) {
2260
+ authStorageInstance2 = new AuthStorage();
2261
+ }
2262
+ return authStorageInstance2;
2263
+ }
2264
+ function setAuthStorage2(storage) {
2265
+ authStorageInstance2 = storage;
2266
+ }
2267
+ var CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
2268
+
2269
+ IMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;
2270
+ var openaiCodexMiddleware = {
2271
+ specificationVersion: "v3",
2272
+ transformParams: async ({ params }) => {
2273
+ if (params.temperature) {
2274
+ delete params.topP;
2275
+ }
2276
+ params.providerOptions = {
2277
+ ...params.providerOptions,
2278
+ openai: {
2279
+ ...params.providerOptions?.openai ?? {},
2280
+ // Codex API requires instructions
2281
+ instructions: CODEX_INSTRUCTIONS,
2282
+ // Codex API requires store to be false
2283
+ store: false
2284
+ }
2285
+ };
2286
+ return params;
2287
+ }
2288
+ };
2289
+ function openaiCodexProvider(modelId = "codex-mini-latest") {
2290
+ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
2291
+ const openai2 = createOpenAI({
2292
+ apiKey: process.env.OPENAI_API_KEY || "test-api-key"
2293
+ });
2294
+ return wrapLanguageModel({
2295
+ model: openai2.responses(modelId),
2296
+ middleware: [openaiCodexMiddleware]
2297
+ });
2298
+ }
2299
+ const oauthFetch = async (url, init) => {
2300
+ const authStorage2 = getAuthStorage2();
2301
+ authStorage2.reload();
2302
+ const cred = authStorage2.get("openai-codex");
2303
+ if (!cred || cred.type !== "oauth") {
2304
+ throw new Error("Not logged in to OpenAI Codex. Run /login first.");
2305
+ }
2306
+ let accessToken = cred.access;
2307
+ if (Date.now() >= cred.expires) {
2308
+ const refreshedToken = await authStorage2.getApiKey("openai-codex");
2309
+ if (!refreshedToken) {
2310
+ throw new Error("Failed to refresh OpenAI Codex token. Please /login again.");
2311
+ }
2312
+ accessToken = refreshedToken;
2313
+ authStorage2.reload();
2314
+ }
2315
+ const accountId = cred.accountId;
2316
+ const headers = new Headers();
2317
+ if (init?.headers) {
2318
+ if (init.headers instanceof Headers) {
2319
+ init.headers.forEach((value, key) => {
2320
+ if (key.toLowerCase() !== "authorization") {
2321
+ headers.set(key, value);
2322
+ }
2323
+ });
2324
+ } else if (Array.isArray(init.headers)) {
2325
+ for (const [key, value] of init.headers) {
2326
+ if (key.toLowerCase() !== "authorization" && value !== void 0) {
2327
+ headers.set(key, String(value));
2328
+ }
2329
+ }
2330
+ } else {
2331
+ for (const [key, value] of Object.entries(init.headers)) {
2332
+ if (key.toLowerCase() !== "authorization" && value !== void 0) {
2333
+ headers.set(key, String(value));
2334
+ }
2335
+ }
3366
2336
  }
3367
2337
  }
2338
+ headers.set("Authorization", `Bearer ${accessToken}`);
2339
+ if (accountId) {
2340
+ headers.set("ChatGPT-Account-Id", accountId);
2341
+ }
2342
+ const parsed = url instanceof URL ? url : new URL(typeof url === "string" ? url : url.url);
2343
+ const shouldRewrite = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions");
2344
+ const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;
2345
+ return fetch(finalUrl, {
2346
+ ...init,
2347
+ headers
2348
+ });
2349
+ };
2350
+ const openai = createOpenAI({
2351
+ // Use a dummy API key since we're using OAuth
2352
+ apiKey: "oauth-dummy-key",
2353
+ fetch: oauthFetch
2354
+ });
2355
+ return wrapLanguageModel({
2356
+ model: openai.responses(modelId),
2357
+ middleware: [openaiCodexMiddleware]
3368
2358
  });
3369
2359
  }
2360
+ var authStorage = new AuthStorage();
2361
+ function resolveModel(modelId) {
2362
+ authStorage.reload();
2363
+ const isAnthropicModel = modelId.startsWith("anthropic/");
2364
+ const isOpenAIModel = modelId.startsWith("openai/");
2365
+ const isMoonshotModel = modelId.startsWith("moonshotai/");
2366
+ if (isMoonshotModel) {
2367
+ if (!process.env.MOONSHOT_AI_API_KEY) {
2368
+ throw new Error(`Need MOONSHOT_AI_API_KEY`);
2369
+ }
2370
+ return createAnthropic({
2371
+ apiKey: process.env.MOONSHOT_AI_API_KEY,
2372
+ baseURL: "https://api.moonshot.ai/anthropic/v1",
2373
+ name: "moonshotai.anthropicv1"
2374
+ })(modelId.substring("moonshotai/".length));
2375
+ } else if (isAnthropicModel) {
2376
+ return opencodeClaudeMaxProvider(modelId.substring(`anthropic/`.length));
2377
+ } else if (isOpenAIModel && authStorage.isLoggedIn("openai-codex")) {
2378
+ return openaiCodexProvider(modelId.substring(`openai/`.length));
2379
+ } else {
2380
+ return new ModelRouterLanguageModel(modelId);
2381
+ }
2382
+ }
2383
+ function getDynamicModel({
2384
+ requestContext
2385
+ }) {
2386
+ const harnessContext = requestContext.get("harness");
2387
+ const modelId = harnessContext?.state?.currentModelId;
2388
+ if (!modelId) {
2389
+ throw new Error("No model selected. Use /models to select a model first.");
2390
+ }
2391
+ return resolveModel(modelId);
2392
+ }
2393
+
2394
+ // src/agents/subagents/execute.ts
2395
+ var executeSubagent = {
2396
+ id: "execute",
2397
+ name: "Execute",
2398
+ instructions: `You are a focused execution agent. Your job is to complete a specific, well-defined task by making the necessary changes to the codebase.
2399
+
2400
+ ## Rules
2401
+ - You have FULL ACCESS to read, write, and execute within your task scope.
2402
+ - Stay focused on the specific task given. Do not make unrelated changes.
2403
+ - Read files before modifying them \u2014 use view first, then string_replace_lsp or write_file.
2404
+ - Verify your changes work by running relevant tests or checking for errors.
2405
+
2406
+ ## Tool Strategy
2407
+ - **Read first**: Always view a file before editing it
2408
+ - **Edit precisely**: Use string_replace_lsp with enough context to match uniquely
2409
+ - **Use specialized tools**: Prefer view/search_content/find_files over shell commands for reading
2410
+ - **Parallelize**: Make independent tool calls together (e.g., view multiple files at once)
2411
+
2412
+ ## Workflow
2413
+ . Understand the task and explore relevant code
2414
+ . For complex tasks (3+ steps): use task_write to track progress
2415
+ . Make changes incrementally \u2014 verify each change before moving on
2416
+ . Run tests or type-check to verify
2417
+ . If you created tasks: ALWAYS call task_check before finishing
2418
+
2419
+ ## Efficiency
2420
+ Your output returns to the parent agent. Be concise:
2421
+ - Don't repeat file contents in your response
2422
+ - Summarize what changed, don't narrate each step
2423
+ - Keep your final summary under 300 words
2424
+
2425
+ ## Output Format
2426
+ End with a structured summary:
2427
+ . **Completed**: What you implemented (1-2 sentences)
2428
+ . **Changes**: Files modified/created
2429
+ . **Verification**: How you verified it works
2430
+ . **Notes**: Follow-up needed (if any)`,
2431
+ allowedTools: [
2432
+ // Read tools
2433
+ "view",
2434
+ "search_content",
2435
+ "find_files",
2436
+ // Write tools
2437
+ "string_replace_lsp",
2438
+ "write_file",
2439
+ // Execution tool
2440
+ "execute_command",
2441
+ // Task tracking (built-in harness tools)
2442
+ "task_write",
2443
+ "task_check"
2444
+ ]
2445
+ };
2446
+
2447
+ // src/agents/subagents/explore.ts
2448
+ var exploreSubagent = {
2449
+ id: "explore",
2450
+ name: "Explore",
2451
+ instructions: `You are an expert code explorer. Your job is to investigate a codebase and answer a specific question or gather specific information.
2452
+
2453
+ ## Rules
2454
+ - You have READ-ONLY access. You cannot modify files or run commands.
2455
+ - Be thorough \u2014 search broadly first, then drill into relevant files.
2456
+ - After gathering enough information, produce a clear, concise summary of your findings.
2457
+
2458
+ ## Tool Strategy
2459
+ - **Start broad**: Use find_files (glob) to understand project structure
2460
+ - **Search smart**: Use search_content (grep) with specific patterns \u2014 avoid overly broad searches
2461
+ - **Read efficiently**: Use view with view_range for large files \u2014 don't read entire files if you only need a section
2462
+ - **Parallelize**: Make multiple independent tool calls in one round when exploring different areas
2463
+
2464
+ ## Efficiency
2465
+ Your output returns to the parent agent. Be concise:
2466
+ - Don't include raw file contents in your response \u2014 summarize what you found
2467
+ - Reference files by path and line number, not by copying code
2468
+ - If a search returns many results, report the count and key examples, not every match
2469
+
2470
+ ## Output Format
2471
+ End with a structured summary:
2472
+ . **Answer**: Direct answer to the question (1-2 sentences)
2473
+ . **Key Files**: Most relevant files with line numbers
2474
+ . **Details**: Additional context if needed
2475
+
2476
+ Keep your summary under 300 words.`,
2477
+ allowedTools: ["view", "search_content", "find_files"]
2478
+ };
2479
+
2480
+ // src/agents/subagents/plan.ts
2481
+ var planSubagent = {
2482
+ id: "plan",
2483
+ name: "Plan",
2484
+ instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.
2485
+
2486
+ ## Rules
2487
+ - You have READ-ONLY access. You cannot modify files or run commands.
2488
+ - First, explore the codebase to understand existing patterns, architecture, and conventions.
2489
+ - Produce a concrete, actionable plan \u2014 not vague suggestions.
2490
+
2491
+ ## Tool Strategy
2492
+ - **Discover structure**: Use find_files (glob) to understand project layout and find relevant files
2493
+ - **Find patterns**: Use search_content (grep) to locate existing implementations, imports, and conventions
2494
+ - **Understand deeply**: Use view with view_range to read specific sections of key files
2495
+ - **Parallelize**: Make multiple independent tool calls when exploring different areas
2496
+
2497
+ ## Efficiency
2498
+ Your output returns to the parent agent. Be concise:
2499
+ - Don't include raw file contents \u2014 reference by path and line number
2500
+ - Focus on actionable details, not general observations
2501
+ - If you find many similar patterns, describe the pattern once with examples
2502
+
2503
+ ## Output Format
2504
+ Structure your plan as:
2505
+
2506
+ . **Summary**: One-paragraph overview (2-3 sentences)
2507
+ . **Files to Change**: List each file with specific changes needed
2508
+ . **Implementation Order**: Numbered steps in dependency order
2509
+ . **Risks**: Potential issues or edge cases (if any)
2510
+
2511
+ Be specific about code locations (file paths, function names, line numbers). Keep the plan actionable and under 500 words.`,
2512
+ allowedTools: ["view", "search_content", "find_files"]
2513
+ };
3370
2514
  var MAX_WEB_SEARCH_TOKENS = 2e3;
3371
2515
  var MAX_WEB_EXTRACT_TOKENS = 2e3;
3372
2516
  var MIN_RELEVANCE_SCORE = 0.25;
@@ -3508,14 +2652,14 @@ Examples:
3508
2652
  - Pattern replace: { pattern: 'console.log($ARG)', replacement: 'logger.debug($ARG)' }`,
3509
2653
  // requireApproval: true,
3510
2654
  inputSchema: astSmartEditSchema,
3511
- execute: async ({ path: path13, pattern, replacement, selector, transform, targetName, newName, importSpec }, toolContext) => {
2655
+ execute: async ({ path: path12, pattern, replacement, selector, transform, targetName, newName, importSpec }, toolContext) => {
3512
2656
  try {
3513
2657
  const projectRoot = getProjectRoot();
3514
- const filePath = resolve(projectRoot, path13);
2658
+ const filePath = resolve(projectRoot, path12);
3515
2659
  const allowedPaths = getAllowedPathsFromContext(toolContext);
3516
2660
  assertPathAllowed(filePath, projectRoot, allowedPaths);
3517
2661
  const content = readFileSync(filePath, "utf-8");
3518
- const lang = getLanguageFromPath(path13);
2662
+ const lang = getLanguageFromPath(path12);
3519
2663
  const ast = parse(lang, content);
3520
2664
  const root = ast.root();
3521
2665
  let modifiedContent = content;
@@ -3590,8 +2734,8 @@ Examples:
3590
2734
  }
3591
2735
  }
3592
2736
  });
3593
- function getLanguageFromPath(path13) {
3594
- const ext = path13.split(".").pop()?.toLowerCase();
2737
+ function getLanguageFromPath(path12) {
2738
+ const ext = path12.split(".").pop()?.toLowerCase();
3595
2739
  switch (ext) {
3596
2740
  case "ts":
3597
2741
  case "tsx":
@@ -3853,6 +2997,259 @@ var requestSandboxAccessTool = createTool({
3853
2997
  }
3854
2998
  }
3855
2999
  });
3000
+
3001
+ // src/onboarding/packs.ts
3002
+ function getAvailableModePacks(access, savedCustomPacks = []) {
3003
+ const packs = [];
3004
+ const openaiCodex = "openai/gpt-5.2-codex";
3005
+ const anthropicBuild = access.anthropic === "oauth" ? "anthropic/claude-opus-4-6" : "anthropic/claude-sonnet-4-5";
3006
+ if (access.anthropic && access.openai) {
3007
+ packs.push({
3008
+ id: "varied",
3009
+ name: "Varied",
3010
+ description: "Models from multiple providers",
3011
+ models: {
3012
+ build: anthropicBuild,
3013
+ plan: openaiCodex,
3014
+ fast: access.cerebras ? "cerebras/zai-glm-4.7" : "anthropic/claude-haiku-4-5"
3015
+ }
3016
+ });
3017
+ }
3018
+ if (access.anthropic) {
3019
+ packs.push({
3020
+ id: "anthropic",
3021
+ name: "Anthropic",
3022
+ description: access.anthropic === "oauth" ? "All Anthropic models via Max subscription" : "All Anthropic models via API key",
3023
+ models: {
3024
+ build: anthropicBuild,
3025
+ plan: anthropicBuild,
3026
+ fast: "anthropic/claude-haiku-4-5"
3027
+ }
3028
+ });
3029
+ }
3030
+ if (access.openai) {
3031
+ packs.push({
3032
+ id: "openai",
3033
+ name: "OpenAI",
3034
+ description: access.openai === "oauth" ? "All OpenAI models via Codex subscription" : "All OpenAI models via API key",
3035
+ models: {
3036
+ build: openaiCodex,
3037
+ plan: openaiCodex,
3038
+ fast: "openai/gpt-5.1-codex-mini"
3039
+ }
3040
+ });
3041
+ }
3042
+ for (const cp of savedCustomPacks) {
3043
+ packs.push({
3044
+ id: `custom:${cp.name}`,
3045
+ name: cp.name,
3046
+ description: "Saved custom pack",
3047
+ models: {
3048
+ build: cp.models.build ?? "",
3049
+ plan: cp.models.plan ?? "",
3050
+ fast: cp.models.fast ?? ""
3051
+ }
3052
+ });
3053
+ }
3054
+ const hasCustom = savedCustomPacks.length > 0;
3055
+ packs.push({
3056
+ id: "custom",
3057
+ name: hasCustom ? "New Custom" : "Custom",
3058
+ description: "Choose a model for each mode",
3059
+ models: { build: "", plan: "", fast: "" }
3060
+ });
3061
+ return packs;
3062
+ }
3063
+ function getAvailableOmPacks(access) {
3064
+ const packs = [];
3065
+ if (access.google) {
3066
+ packs.push({
3067
+ id: "gemini",
3068
+ name: "Gemini Flash",
3069
+ description: access.google === "oauth" ? "Via Google OAuth" : "Via Google API key",
3070
+ modelId: "google/gemini-2.5-flash"
3071
+ });
3072
+ }
3073
+ if (access.anthropic) {
3074
+ packs.push({
3075
+ id: "anthropic",
3076
+ name: "Claude Haiku",
3077
+ description: access.anthropic === "oauth" ? "Via Max subscription" : "Via Anthropic API key",
3078
+ modelId: "anthropic/claude-haiku-4-5"
3079
+ });
3080
+ }
3081
+ if (access.openai) {
3082
+ packs.push({
3083
+ id: "openai",
3084
+ name: "Codex Mini",
3085
+ description: access.openai === "oauth" ? "Via Codex subscription" : "Via OpenAI API key",
3086
+ modelId: "openai/gpt-5.1-codex-mini"
3087
+ });
3088
+ }
3089
+ if (access.deepseek) {
3090
+ packs.push({
3091
+ id: "deepseek",
3092
+ name: "DeepSeek",
3093
+ description: "Via DeepSeek API key",
3094
+ modelId: "deepseek/deepseek-chat"
3095
+ });
3096
+ }
3097
+ packs.push({
3098
+ id: "custom",
3099
+ name: "Custom",
3100
+ description: "Choose any available model",
3101
+ modelId: ""
3102
+ });
3103
+ return packs;
3104
+ }
3105
+ var ONBOARDING_VERSION = 1;
3106
+ var STORAGE_DEFAULTS = {
3107
+ backend: "libsql",
3108
+ libsql: {},
3109
+ pg: {}
3110
+ };
3111
+ var DEFAULTS = {
3112
+ onboarding: {
3113
+ completedAt: null,
3114
+ skippedAt: null,
3115
+ version: 0,
3116
+ modePackId: null,
3117
+ omPackId: null
3118
+ },
3119
+ models: {
3120
+ activeModelPackId: null,
3121
+ modeDefaults: {},
3122
+ activeOmPackId: null,
3123
+ omModelOverride: null,
3124
+ subagentModels: {}
3125
+ },
3126
+ preferences: {
3127
+ yolo: null
3128
+ },
3129
+ storage: { ...STORAGE_DEFAULTS },
3130
+ customModelPacks: [],
3131
+ modelUseCounts: {}
3132
+ };
3133
+ function getSettingsPath() {
3134
+ return join(getAppDataDir(), "settings.json");
3135
+ }
3136
+ function migrateFromAuth(settingsPath) {
3137
+ const authPath = join(getAppDataDir(), "auth.json");
3138
+ if (!existsSync(authPath)) return false;
3139
+ let authData;
3140
+ try {
3141
+ authData = JSON.parse(readFileSync(authPath, "utf-8"));
3142
+ } catch {
3143
+ return false;
3144
+ }
3145
+ const modelKeys = Object.keys(authData).filter((k) => k.startsWith("_"));
3146
+ if (modelKeys.length === 0) return false;
3147
+ let settings;
3148
+ if (existsSync(settingsPath)) {
3149
+ try {
3150
+ const raw = JSON.parse(readFileSync(settingsPath, "utf-8"));
3151
+ settings = {
3152
+ onboarding: { ...DEFAULTS.onboarding, ...raw.onboarding },
3153
+ models: { ...DEFAULTS.models, ...raw.models },
3154
+ preferences: { ...DEFAULTS.preferences, ...raw.preferences },
3155
+ storage: {
3156
+ ...STORAGE_DEFAULTS,
3157
+ ...raw.storage,
3158
+ libsql: { ...STORAGE_DEFAULTS.libsql, ...raw.storage?.libsql },
3159
+ pg: { ...STORAGE_DEFAULTS.pg, ...raw.storage?.pg }
3160
+ },
3161
+ customModelPacks: Array.isArray(raw.customModelPacks) ? raw.customModelPacks : [],
3162
+ modelUseCounts: raw.modelUseCounts && typeof raw.modelUseCounts === "object" ? raw.modelUseCounts : {}
3163
+ };
3164
+ } catch {
3165
+ settings = structuredClone(DEFAULTS);
3166
+ }
3167
+ } else {
3168
+ settings = structuredClone(DEFAULTS);
3169
+ }
3170
+ if (authData._modelRanks && typeof authData._modelRanks === "object") {
3171
+ settings.modelUseCounts = { ...authData._modelRanks, ...settings.modelUseCounts };
3172
+ }
3173
+ for (const key of modelKeys) {
3174
+ const modeMatch = key.match(/^_modeModelId_(.+)$/);
3175
+ if (modeMatch?.[1] && typeof authData[key] === "string" && !settings.models.modeDefaults[modeMatch[1]]) {
3176
+ settings.models.modeDefaults[modeMatch[1]] = authData[key];
3177
+ }
3178
+ }
3179
+ for (const key of modelKeys) {
3180
+ if (key === "_subagentModelId" && typeof authData[key] === "string" && !settings.models.subagentModels["default"]) {
3181
+ settings.models.subagentModels["default"] = authData[key];
3182
+ }
3183
+ const saMatch = key.match(/^_subagentModelId_(.+)$/);
3184
+ if (saMatch?.[1] && typeof authData[key] === "string" && !settings.models.subagentModels[saMatch[1]]) {
3185
+ settings.models.subagentModels[saMatch[1]] = authData[key];
3186
+ }
3187
+ }
3188
+ saveSettings(settings, settingsPath);
3189
+ for (const key of modelKeys) {
3190
+ delete authData[key];
3191
+ }
3192
+ try {
3193
+ writeFileSync(authPath, JSON.stringify(authData, null, 2), "utf-8");
3194
+ } catch {
3195
+ }
3196
+ return true;
3197
+ }
3198
+ function loadSettings(filePath = getSettingsPath()) {
3199
+ migrateFromAuth(filePath);
3200
+ if (!existsSync(filePath)) return structuredClone(DEFAULTS);
3201
+ try {
3202
+ const raw = JSON.parse(readFileSync(filePath, "utf-8"));
3203
+ const settings = {
3204
+ onboarding: { ...DEFAULTS.onboarding, ...raw.onboarding },
3205
+ models: { ...DEFAULTS.models, ...raw.models },
3206
+ preferences: { ...DEFAULTS.preferences, ...raw.preferences },
3207
+ storage: {
3208
+ ...STORAGE_DEFAULTS,
3209
+ ...raw.storage,
3210
+ libsql: { ...STORAGE_DEFAULTS.libsql, ...raw.storage?.libsql },
3211
+ pg: { ...STORAGE_DEFAULTS.pg, ...raw.storage?.pg }
3212
+ },
3213
+ customModelPacks: Array.isArray(raw.customModelPacks) ? raw.customModelPacks : [],
3214
+ modelUseCounts: raw.modelUseCounts && typeof raw.modelUseCounts === "object" ? raw.modelUseCounts : {}
3215
+ };
3216
+ if (raw.models?.omModelId && !settings.models.omModelOverride) {
3217
+ settings.models.omModelOverride = raw.models.omModelId;
3218
+ saveSettings(settings, filePath);
3219
+ }
3220
+ return settings;
3221
+ } catch {
3222
+ return structuredClone(DEFAULTS);
3223
+ }
3224
+ }
3225
+ function resolveModelDefaults(settings, builtinPacks) {
3226
+ const { activeModelPackId, modeDefaults } = settings.models;
3227
+ if (!activeModelPackId) return modeDefaults;
3228
+ if (activeModelPackId.startsWith("custom:")) {
3229
+ const name = activeModelPackId.slice("custom:".length);
3230
+ const pack = settings.customModelPacks.find((p) => p.name === name);
3231
+ if (pack) return pack.models;
3232
+ return modeDefaults;
3233
+ }
3234
+ const builtin = builtinPacks.find((p) => p.id === activeModelPackId);
3235
+ if (builtin) return builtin.models;
3236
+ return modeDefaults;
3237
+ }
3238
+ function resolveOmModel(settings, builtinOmPacks) {
3239
+ const { activeOmPackId, omModelOverride } = settings.models;
3240
+ if (!activeOmPackId) return omModelOverride;
3241
+ if (activeOmPackId === "custom") return omModelOverride;
3242
+ const pack = builtinOmPacks.find((p) => p.id === activeOmPackId);
3243
+ if (pack) return pack.modelId;
3244
+ return omModelOverride;
3245
+ }
3246
+ function saveSettings(settings, filePath = getSettingsPath()) {
3247
+ const dir = dirname(filePath);
3248
+ if (!existsSync(dir)) {
3249
+ mkdirSync(dir, { recursive: true });
3250
+ }
3251
+ writeFileSync(filePath, JSON.stringify(settings, null, 2), "utf-8");
3252
+ }
3856
3253
  var mastra = {
3857
3254
  purple: "#7f45e0",
3858
3255
  // #b588fe brand is too washed out for terminal
@@ -4034,8 +3431,8 @@ var ThreadLockError = class extends Error {
4034
3431
  };
4035
3432
  function getLocksDir() {
4036
3433
  const dir = path.join(getAppDataDir(), "locks");
4037
- if (!fs9.existsSync(dir)) {
4038
- fs9.mkdirSync(dir, { recursive: true });
3434
+ if (!fs8.existsSync(dir)) {
3435
+ fs8.mkdirSync(dir, { recursive: true });
4039
3436
  }
4040
3437
  return dir;
4041
3438
  }
@@ -4054,9 +3451,9 @@ function isProcessAlive(pid) {
4054
3451
  function acquireThreadLock(threadId) {
4055
3452
  const lockPath = getLockPath(threadId);
4056
3453
  const myPid = process.pid;
4057
- if (fs9.existsSync(lockPath)) {
3454
+ if (fs8.existsSync(lockPath)) {
4058
3455
  try {
4059
- const content = fs9.readFileSync(lockPath, "utf-8").trim();
3456
+ const content = fs8.readFileSync(lockPath, "utf-8").trim();
4060
3457
  const ownerPid = parseInt(content, 10);
4061
3458
  if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {
4062
3459
  throw new ThreadLockError(threadId, ownerPid);
@@ -4065,17 +3462,17 @@ function acquireThreadLock(threadId) {
4065
3462
  if (error instanceof ThreadLockError) throw error;
4066
3463
  }
4067
3464
  }
4068
- fs9.writeFileSync(lockPath, String(myPid), { mode: 420 });
3465
+ fs8.writeFileSync(lockPath, String(myPid), { mode: 420 });
4069
3466
  }
4070
3467
  function releaseThreadLock(threadId) {
4071
3468
  const lockPath = getLockPath(threadId);
4072
3469
  const myPid = process.pid;
4073
3470
  try {
4074
- if (!fs9.existsSync(lockPath)) return;
4075
- const content = fs9.readFileSync(lockPath, "utf-8").trim();
3471
+ if (!fs8.existsSync(lockPath)) return;
3472
+ const content = fs8.readFileSync(lockPath, "utf-8").trim();
4076
3473
  const ownerPid = parseInt(content, 10);
4077
3474
  if (ownerPid === myPid) {
4078
- fs9.unlinkSync(lockPath);
3475
+ fs8.unlinkSync(lockPath);
4079
3476
  }
4080
3477
  } catch {
4081
3478
  }
@@ -4083,15 +3480,15 @@ function releaseThreadLock(threadId) {
4083
3480
  function releaseAllThreadLocks() {
4084
3481
  try {
4085
3482
  const locksDir = getLocksDir();
4086
- const files = fs9.readdirSync(locksDir);
3483
+ const files = fs8.readdirSync(locksDir);
4087
3484
  const myPid = String(process.pid);
4088
3485
  for (const file of files) {
4089
3486
  if (!file.endsWith(".lock")) continue;
4090
3487
  const lockPath = path.join(locksDir, file);
4091
3488
  try {
4092
- const content = fs9.readFileSync(lockPath, "utf-8").trim();
3489
+ const content = fs8.readFileSync(lockPath, "utf-8").trim();
4093
3490
  if (content === myPid) {
4094
- fs9.unlinkSync(lockPath);
3491
+ fs8.unlinkSync(lockPath);
4095
3492
  }
4096
3493
  } catch {
4097
3494
  }
@@ -4100,6 +3497,6 @@ function releaseAllThreadLocks() {
4100
3497
  }
4101
3498
  }
4102
3499
 
4103
- export { AuthStorage, ThreadLockError, acquireThreadLock, astSmartEditTool, bg, bold, createExecuteCommandTool, createGlobTool, createGrepTool, createViewTool, createWebExtractTool, createWebSearchTool, createWriteFileTool, detectProject, executeSubagent, exploreSubagent, fg, getAppDataDir, getDynamicModel, getEditorTheme, getMarkdownTheme, getOAuthProviders, getOmScope, getResourceIdOverride, getSelectListTheme, getSettingsListTheme, getStorageConfig, getTheme, getUserId, hasTavilyKey, mastra, parseSubagentMeta, planSubagent, releaseAllThreadLocks, releaseThreadLock, requestSandboxAccessTool, resolveModel, setAuthStorage, setAuthStorage2, setTheme, stringReplaceLspTool, theme, tintHex };
4104
- //# sourceMappingURL=chunk-BYMDWH2E.js.map
4105
- //# sourceMappingURL=chunk-BYMDWH2E.js.map
3500
+ export { ONBOARDING_VERSION, ThreadLockError, acquireThreadLock, astSmartEditTool, bg, bold, createExecuteCommandTool, createGlobTool, createGrepTool, createViewTool, createWebExtractTool, createWebSearchTool, createWriteFileTool, executeSubagent, exploreSubagent, fg, getAvailableModePacks, getAvailableOmPacks, getDynamicModel, getEditorTheme, getMarkdownTheme, getSelectListTheme, getSettingsListTheme, getTheme, hasTavilyKey, loadSettings, mastra, parseSubagentMeta, planSubagent, releaseAllThreadLocks, releaseThreadLock, requestSandboxAccessTool, resolveModel, resolveModelDefaults, resolveOmModel, saveSettings, setAuthStorage, setAuthStorage2, setTheme, stringReplaceLspTool, theme, tintHex };
3501
+ //# sourceMappingURL=chunk-V4HZ2AVV.js.map
3502
+ //# sourceMappingURL=chunk-V4HZ2AVV.js.map