@ngandu/ulicode 0.0.6
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.
- package/CHANGELOG.md +1081 -0
- package/README.md +312 -0
- package/dist/agents/definitions/ask.agent.md +53 -0
- package/dist/agents/definitions/audit-tests.agent.md +138 -0
- package/dist/agents/definitions/build.agent.md +111 -0
- package/dist/agents/definitions/execute.agent.md +99 -0
- package/dist/agents/definitions/explore.agent.md +57 -0
- package/dist/agents/definitions/fast.agent.md +48 -0
- package/dist/agents/definitions/plan-mode.agent.md +102 -0
- package/dist/agents/definitions/planner.agent.md +59 -0
- package/dist/chunk-3YYDXNUH.js +854 -0
- package/dist/chunk-3YYDXNUH.js.map +1 -0
- package/dist/chunk-IEV2IT3O.cjs +873 -0
- package/dist/chunk-IEV2IT3O.cjs.map +1 -0
- package/dist/chunk-MBWGSXBT.js +11927 -0
- package/dist/chunk-MBWGSXBT.js.map +1 -0
- package/dist/chunk-MS5RYNRK.js +137 -0
- package/dist/chunk-MS5RYNRK.js.map +1 -0
- package/dist/chunk-OXFO76JC.js +2633 -0
- package/dist/chunk-OXFO76JC.js.map +1 -0
- package/dist/chunk-PKRLG6A4.js +1756 -0
- package/dist/chunk-PKRLG6A4.js.map +1 -0
- package/dist/chunk-PUVEPQQ3.cjs +1805 -0
- package/dist/chunk-PUVEPQQ3.cjs.map +1 -0
- package/dist/chunk-R6JK3DE3.cjs +148 -0
- package/dist/chunk-R6JK3DE3.cjs.map +1 -0
- package/dist/chunk-Y3HWP75B.cjs +11974 -0
- package/dist/chunk-Y3HWP75B.cjs.map +1 -0
- package/dist/chunk-Y5PO67TG.cjs +2659 -0
- package/dist/chunk-Y5PO67TG.cjs.map +1 -0
- package/dist/cli.cjs +372 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +370 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/permissions-NRD36MYI.cjs +40 -0
- package/dist/permissions-NRD36MYI.cjs.map +1 -0
- package/dist/permissions-RC7CYR5H.js +3 -0
- package/dist/permissions-RC7CYR5H.js.map +1 -0
- package/dist/project-q9WpahUs.d.cts +329 -0
- package/dist/project-q9WpahUs.d.ts +329 -0
- package/dist/storage-6P53PQBL.cjs +24 -0
- package/dist/storage-6P53PQBL.cjs.map +1 -0
- package/dist/storage-QELMNBZ2.js +3 -0
- package/dist/storage-QELMNBZ2.js.map +1 -0
- package/dist/tui.cjs +76 -0
- package/dist/tui.cjs.map +1 -0
- package/dist/tui.d.cts +1013 -0
- package/dist/tui.d.ts +1013 -0
- package/dist/tui.js +3 -0
- package/dist/tui.js.map +1 -0
- package/package.json +107 -0
|
@@ -0,0 +1,1756 @@
|
|
|
1
|
+
import { getAppDataDir } from './chunk-3YYDXNUH.js';
|
|
2
|
+
import { MC_TOOLS } from './chunk-MS5RYNRK.js';
|
|
3
|
+
import * as fs2 from 'fs';
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import * as path4 from 'path';
|
|
7
|
+
import path4__default, { join, dirname, basename } from 'path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
9
|
+
import { taskCheckTool, taskWriteTool } from '@mastra/core/harness';
|
|
10
|
+
import * as yaml from 'js-yaml';
|
|
11
|
+
import { StreamMessageReader, StreamMessageWriter, createMessageConnection } from 'vscode-jsonrpc/node.js';
|
|
12
|
+
import { Position, TextDocumentIdentifier } from 'vscode-languageserver-protocol';
|
|
13
|
+
import { spawn } from 'child_process';
|
|
14
|
+
import { createRequire } from 'module';
|
|
15
|
+
|
|
16
|
+
var MODE_INDICATOR_FIELDS = /* @__PURE__ */ new Set([
|
|
17
|
+
"subagents",
|
|
18
|
+
"default",
|
|
19
|
+
"defaultModelId",
|
|
20
|
+
"color"
|
|
21
|
+
]);
|
|
22
|
+
function idFromFilename(filePath) {
|
|
23
|
+
return basename(filePath).replace(/\.agent\.md$/i, "");
|
|
24
|
+
}
|
|
25
|
+
function descriptionFromBody(body) {
|
|
26
|
+
const lines = body.split(/\r?\n/);
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
30
|
+
const sentence = trimmed.match(/^[^.!?]+[.!?]?/)?.[0] ?? trimmed;
|
|
31
|
+
return sentence.length > 120 ? sentence.slice(0, 117) + "..." : sentence;
|
|
32
|
+
}
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
function inferKind(frontmatter) {
|
|
36
|
+
for (const key of Object.keys(frontmatter)) {
|
|
37
|
+
if (MODE_INDICATOR_FIELDS.has(key)) return "mode";
|
|
38
|
+
}
|
|
39
|
+
return "subagent";
|
|
40
|
+
}
|
|
41
|
+
var BUILTIN_DEFINITIONS_DIR = join(dirname(fileURLToPath(import.meta.url)), "definitions");
|
|
42
|
+
var GLOBAL_AGENT_LOCATIONS = [
|
|
43
|
+
".claude/agents",
|
|
44
|
+
".ulicode/agents",
|
|
45
|
+
".uli-cli/agents",
|
|
46
|
+
".pulse/agents",
|
|
47
|
+
".mastracode/agents",
|
|
48
|
+
".config/claude/agents",
|
|
49
|
+
".config/ulicode/agents",
|
|
50
|
+
".config/uli-cli/agents",
|
|
51
|
+
".config/pulse/agents",
|
|
52
|
+
".config/mastracode/agents"
|
|
53
|
+
];
|
|
54
|
+
var PROJECT_AGENT_LOCATIONS = [
|
|
55
|
+
".claude/agents",
|
|
56
|
+
".ulicode/agents",
|
|
57
|
+
".uli-cli/agents",
|
|
58
|
+
".pulse/agents",
|
|
59
|
+
".mastracode/agents"
|
|
60
|
+
];
|
|
61
|
+
var WORKSPACE_TOOL_IDS = new Set(Object.values(MC_TOOLS));
|
|
62
|
+
var DIRECT_SUBAGENT_TOOLS = {
|
|
63
|
+
task_write: taskWriteTool,
|
|
64
|
+
task_check: taskCheckTool
|
|
65
|
+
};
|
|
66
|
+
var agentDefinitionCache = /* @__PURE__ */ new Map();
|
|
67
|
+
function pushDiagnostic(diagnostics, severity, scope, sourcePath, message) {
|
|
68
|
+
diagnostics.push({ severity, scope, sourcePath, message });
|
|
69
|
+
}
|
|
70
|
+
function titleCaseId(id) {
|
|
71
|
+
return id.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
72
|
+
}
|
|
73
|
+
function parseFrontmatter(content) {
|
|
74
|
+
const normalized = content.replace(/^\uFEFF/, "");
|
|
75
|
+
const match = normalized.match(/^---\r?\n([\s\S]*?)(?:\r?\n)?---\r?\n?([\s\S]*)$/);
|
|
76
|
+
if (!match) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const frontmatter = yaml.load(match[1]) ?? {};
|
|
80
|
+
return { frontmatter, body: match[2].trim() };
|
|
81
|
+
}
|
|
82
|
+
function scanAgentDirectory(dirPath) {
|
|
83
|
+
if (!existsSync(dirPath)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const results = [];
|
|
87
|
+
for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
|
|
88
|
+
const fullPath = join(dirPath, entry.name);
|
|
89
|
+
if (entry.isDirectory()) {
|
|
90
|
+
results.push(...scanAgentDirectory(fullPath));
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (entry.isFile() && entry.name.endsWith(".agent.md")) {
|
|
94
|
+
results.push(fullPath);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return results.sort((left, right) => left.localeCompare(right));
|
|
98
|
+
}
|
|
99
|
+
function normalizeModeDefinition(filePath, scope, frontmatter, instructions, diagnostics) {
|
|
100
|
+
const id = (frontmatter.id ?? idFromFilename(filePath)).trim();
|
|
101
|
+
if (!id) {
|
|
102
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, "could not derive `id` for mode definition");
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const name = typeof frontmatter.name === "string" && frontmatter.name.trim() ? frontmatter.name.trim() : titleCaseId(id);
|
|
106
|
+
const description = typeof frontmatter.description === "string" && frontmatter.description.trim() ? frontmatter.description.trim() : descriptionFromBody(instructions);
|
|
107
|
+
if (frontmatter.subagents !== void 0 && !Array.isArray(frontmatter.subagents)) {
|
|
108
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, `mode \`${id}\` must declare \`subagents\` as an array`);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (Array.isArray(frontmatter.subagents) && frontmatter.subagents.some((entry) => typeof entry !== "string" || entry.trim().length === 0)) {
|
|
112
|
+
pushDiagnostic(
|
|
113
|
+
diagnostics,
|
|
114
|
+
"error",
|
|
115
|
+
scope,
|
|
116
|
+
filePath,
|
|
117
|
+
`mode \`${id}\` has invalid \`subagents\` entries; expected non-empty string ids`
|
|
118
|
+
);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const subagents = Array.isArray(frontmatter.subagents) ? frontmatter.subagents.map((entry) => entry.trim()) : [];
|
|
122
|
+
return {
|
|
123
|
+
id,
|
|
124
|
+
name,
|
|
125
|
+
kind: "mode",
|
|
126
|
+
description,
|
|
127
|
+
instructions,
|
|
128
|
+
sourcePath: filePath,
|
|
129
|
+
scope,
|
|
130
|
+
hidden: frontmatter.hidden === true,
|
|
131
|
+
default: frontmatter.default === true,
|
|
132
|
+
defaultModelId: typeof frontmatter.defaultModelId === "string" ? frontmatter.defaultModelId.trim() : void 0,
|
|
133
|
+
color: typeof frontmatter.color === "string" ? frontmatter.color.trim() : void 0,
|
|
134
|
+
subagents
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function normalizeSubagentDefinition(filePath, scope, frontmatter, instructions, diagnostics) {
|
|
138
|
+
const id = (frontmatter.id ?? idFromFilename(filePath)).trim();
|
|
139
|
+
if (!id) {
|
|
140
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, "could not derive `id` for subagent definition");
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const name = typeof frontmatter.name === "string" && frontmatter.name.trim() ? frontmatter.name.trim() : titleCaseId(id);
|
|
144
|
+
const description = typeof frontmatter.description === "string" && frontmatter.description.trim() ? frontmatter.description.trim() : descriptionFromBody(instructions);
|
|
145
|
+
if (frontmatter.allowedWorkspaceTools && frontmatter.workspaceTools) {
|
|
146
|
+
pushDiagnostic(
|
|
147
|
+
diagnostics,
|
|
148
|
+
"error",
|
|
149
|
+
scope,
|
|
150
|
+
filePath,
|
|
151
|
+
`subagent \`${id}\` cannot declare both \`allowedWorkspaceTools\` and \`workspaceTools\``
|
|
152
|
+
);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
if (frontmatter.tools && typeof frontmatter.tools !== "string" && (!Array.isArray(frontmatter.tools) || frontmatter.tools.some((entry) => typeof entry !== "string"))) {
|
|
156
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, `subagent \`${id}\` must declare \`tools\` as an array of strings or "all"`);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const useAllTools = frontmatter.tools === "all" || Array.isArray(frontmatter.tools) && frontmatter.tools.length === 1 && frontmatter.tools[0] === "all";
|
|
160
|
+
const tools = useAllTools ? Object.keys(DIRECT_SUBAGENT_TOOLS) : Array.isArray(frontmatter.tools) ? frontmatter.tools.map((entry) => entry.trim()) : [];
|
|
161
|
+
const rawWorkspaceTools = frontmatter.allowedWorkspaceTools ?? frontmatter.workspaceTools;
|
|
162
|
+
const useAllWorkspaceTools = rawWorkspaceTools === "all" || Array.isArray(rawWorkspaceTools) && rawWorkspaceTools.length === 1 && rawWorkspaceTools[0] === "all";
|
|
163
|
+
let allowedWorkspaceTools;
|
|
164
|
+
if (useAllWorkspaceTools) {
|
|
165
|
+
allowedWorkspaceTools = void 0;
|
|
166
|
+
} else if (Array.isArray(rawWorkspaceTools)) {
|
|
167
|
+
allowedWorkspaceTools = rawWorkspaceTools.filter(
|
|
168
|
+
(entry) => typeof entry === "string" && entry.trim().length > 0
|
|
169
|
+
);
|
|
170
|
+
} else if (typeof rawWorkspaceTools === "string") {
|
|
171
|
+
pushDiagnostic(diagnostics, "warning", scope, filePath, `subagent \`${id}\` has unrecognized \`allowedWorkspaceTools\` value "${rawWorkspaceTools}"; expected array or "all"`);
|
|
172
|
+
allowedWorkspaceTools = void 0;
|
|
173
|
+
} else {
|
|
174
|
+
allowedWorkspaceTools = void 0;
|
|
175
|
+
}
|
|
176
|
+
const unknownDirectTools = tools.filter((toolId) => !(toolId in DIRECT_SUBAGENT_TOOLS));
|
|
177
|
+
if (unknownDirectTools.length > 0) {
|
|
178
|
+
pushDiagnostic(diagnostics, "warning", scope, filePath, `subagent \`${id}\` declares unknown direct tools (ignored at runtime): ${unknownDirectTools.join(", ")}`);
|
|
179
|
+
}
|
|
180
|
+
const unknownWorkspaceTools = (allowedWorkspaceTools ?? []).filter((toolId) => !WORKSPACE_TOOL_IDS.has(toolId));
|
|
181
|
+
if (unknownWorkspaceTools.length > 0) {
|
|
182
|
+
pushDiagnostic(diagnostics, "warning", scope, filePath, `subagent \`${id}\` declares unknown workspace tools (ignored at runtime): ${unknownWorkspaceTools.join(", ")}`);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
id,
|
|
186
|
+
name,
|
|
187
|
+
kind: "subagent",
|
|
188
|
+
description,
|
|
189
|
+
instructions,
|
|
190
|
+
sourcePath: filePath,
|
|
191
|
+
scope,
|
|
192
|
+
hidden: frontmatter.hidden === true,
|
|
193
|
+
tools,
|
|
194
|
+
allowedWorkspaceTools,
|
|
195
|
+
defaultModelFromMode: typeof frontmatter.defaultModelFromMode === "string" ? frontmatter.defaultModelFromMode.trim() : typeof frontmatter.modeId === "string" ? frontmatter.modeId.trim() : void 0
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function parseAgentDefinitionFile(filePath, scope, diagnostics) {
|
|
199
|
+
try {
|
|
200
|
+
const content = readFileSync(filePath, "utf-8");
|
|
201
|
+
const parsed = parseFrontmatter(content);
|
|
202
|
+
if (!parsed) {
|
|
203
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, "missing valid YAML frontmatter");
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
const instructions = parsed.body.trim();
|
|
207
|
+
if (!instructions) {
|
|
208
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, "missing markdown instruction body");
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const kind = parsed.frontmatter.kind ?? inferKind(parsed.frontmatter);
|
|
212
|
+
if (kind === "mode") {
|
|
213
|
+
return normalizeModeDefinition(filePath, scope, parsed.frontmatter, instructions, diagnostics);
|
|
214
|
+
}
|
|
215
|
+
if (kind === "subagent") {
|
|
216
|
+
return normalizeSubagentDefinition(filePath, scope, parsed.frontmatter, instructions, diagnostics);
|
|
217
|
+
}
|
|
218
|
+
pushDiagnostic(diagnostics, "error", scope, filePath, `unsupported \`kind\`: ${String(parsed.frontmatter.kind)}; expected \`mode\` or \`subagent\``);
|
|
219
|
+
return null;
|
|
220
|
+
} catch (error) {
|
|
221
|
+
pushDiagnostic(
|
|
222
|
+
diagnostics,
|
|
223
|
+
"error",
|
|
224
|
+
scope,
|
|
225
|
+
filePath,
|
|
226
|
+
`failed to parse agent definition: ${error instanceof Error ? error.message : String(error)}`
|
|
227
|
+
);
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function validateRegistry(registry, diagnostics) {
|
|
232
|
+
const modeIds = new Set(registry.modes.map((mode) => mode.id));
|
|
233
|
+
const subagentIds = new Set(registry.subagents.map((subagent) => subagent.id));
|
|
234
|
+
for (const mode of registry.modes) {
|
|
235
|
+
const seen = /* @__PURE__ */ new Set();
|
|
236
|
+
for (const subagentId of mode.subagents) {
|
|
237
|
+
if (seen.has(subagentId)) {
|
|
238
|
+
pushDiagnostic(diagnostics, "warning", mode.scope, mode.sourcePath, `mode \`${mode.id}\` lists duplicate subagent \`${subagentId}\``);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
seen.add(subagentId);
|
|
242
|
+
if (!subagentIds.has(subagentId)) {
|
|
243
|
+
pushDiagnostic(
|
|
244
|
+
diagnostics,
|
|
245
|
+
"error",
|
|
246
|
+
mode.scope,
|
|
247
|
+
mode.sourcePath,
|
|
248
|
+
`mode \`${mode.id}\` references unknown subagent \`${subagentId}\` in \`subagents\``
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const subagent of registry.subagents) {
|
|
254
|
+
if (subagent.defaultModelFromMode && !modeIds.has(subagent.defaultModelFromMode)) {
|
|
255
|
+
pushDiagnostic(
|
|
256
|
+
diagnostics,
|
|
257
|
+
"error",
|
|
258
|
+
subagent.scope,
|
|
259
|
+
subagent.sourcePath,
|
|
260
|
+
`subagent \`${subagent.id}\` references unknown mode \`${subagent.defaultModelFromMode}\` in \`defaultModelFromMode\``
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const ownerCount = registry.modes.filter((mode) => mode.subagents.includes(subagent.id)).length;
|
|
264
|
+
if (ownerCount === 0) {
|
|
265
|
+
pushDiagnostic(
|
|
266
|
+
diagnostics,
|
|
267
|
+
"warning",
|
|
268
|
+
subagent.scope,
|
|
269
|
+
subagent.sourcePath,
|
|
270
|
+
`subagent \`${subagent.id}\` is not referenced by any top-level mode \`subagents\` list`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const defaultModes = registry.modes.filter((mode) => mode.default);
|
|
275
|
+
if (defaultModes.length === 0) {
|
|
276
|
+
pushDiagnostic(diagnostics, "warning", "builtin", BUILTIN_DEFINITIONS_DIR, "no default top-level mode is defined");
|
|
277
|
+
} else if (defaultModes.length > 1) {
|
|
278
|
+
pushDiagnostic(
|
|
279
|
+
diagnostics,
|
|
280
|
+
"error",
|
|
281
|
+
"builtin",
|
|
282
|
+
BUILTIN_DEFINITIONS_DIR,
|
|
283
|
+
`multiple default top-level modes found: ${defaultModes.map((mode) => mode.id).join(", ")}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function mergeDefinitions(entries) {
|
|
288
|
+
const modeMap = /* @__PURE__ */ new Map();
|
|
289
|
+
const subagentMap = /* @__PURE__ */ new Map();
|
|
290
|
+
const diagnostics = [];
|
|
291
|
+
const subagentModeClaims = /* @__PURE__ */ new Map();
|
|
292
|
+
for (const entry of entries) {
|
|
293
|
+
for (const filePath of scanAgentDirectory(entry.dirPath)) {
|
|
294
|
+
const content = readFileSync(filePath, "utf-8");
|
|
295
|
+
const parsed = parseFrontmatter(content);
|
|
296
|
+
const rawModes = parsed?.frontmatter?.modes;
|
|
297
|
+
const definition = parseAgentDefinitionFile(filePath, entry.scope, diagnostics);
|
|
298
|
+
if (!definition) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (definition.kind === "mode") {
|
|
302
|
+
modeMap.set(definition.id, definition);
|
|
303
|
+
} else {
|
|
304
|
+
subagentMap.set(definition.id, definition);
|
|
305
|
+
if (Array.isArray(rawModes) && rawModes.length > 0) {
|
|
306
|
+
subagentModeClaims.set(
|
|
307
|
+
definition.id,
|
|
308
|
+
rawModes.filter((m) => typeof m === "string" && m.trim().length > 0).map((m) => m.trim())
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
for (const [subagentId, claimedModeIds] of subagentModeClaims) {
|
|
315
|
+
for (const modeId of claimedModeIds) {
|
|
316
|
+
const mode = modeMap.get(modeId);
|
|
317
|
+
if (mode && !mode.subagents.includes(subagentId)) {
|
|
318
|
+
mode.subagents.push(subagentId);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const defaultMode = Array.from(modeMap.values()).find((m) => m.default);
|
|
323
|
+
if (defaultMode) {
|
|
324
|
+
for (const [subagentId] of subagentMap) {
|
|
325
|
+
if (subagentModeClaims.has(subagentId)) continue;
|
|
326
|
+
const isReferenced = Array.from(modeMap.values()).some((m) => m.subagents.includes(subagentId));
|
|
327
|
+
if (!isReferenced) {
|
|
328
|
+
defaultMode.subagents.push(subagentId);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const registry = {
|
|
333
|
+
modes: Array.from(modeMap.values()),
|
|
334
|
+
subagents: Array.from(subagentMap.values()),
|
|
335
|
+
diagnostics
|
|
336
|
+
};
|
|
337
|
+
validateRegistry(registry, diagnostics);
|
|
338
|
+
return registry;
|
|
339
|
+
}
|
|
340
|
+
function loadAgentDefinitions(projectPath) {
|
|
341
|
+
const entries = [{ dirPath: BUILTIN_DEFINITIONS_DIR, scope: "builtin" }];
|
|
342
|
+
const home = homedir();
|
|
343
|
+
for (const location of GLOBAL_AGENT_LOCATIONS) {
|
|
344
|
+
entries.push({ dirPath: join(home, location), scope: "user" });
|
|
345
|
+
}
|
|
346
|
+
for (const location of PROJECT_AGENT_LOCATIONS) {
|
|
347
|
+
entries.push({ dirPath: join(projectPath, location), scope: "project" });
|
|
348
|
+
}
|
|
349
|
+
return mergeDefinitions(entries);
|
|
350
|
+
}
|
|
351
|
+
function loadAgentDefinitionsCached(projectPath) {
|
|
352
|
+
const cached = agentDefinitionCache.get(projectPath);
|
|
353
|
+
if (cached) {
|
|
354
|
+
return {
|
|
355
|
+
modes: cached.modes.map((mode) => ({ ...mode, subagents: [...mode.subagents] })),
|
|
356
|
+
subagents: cached.subagents.map((subagent) => ({
|
|
357
|
+
...subagent,
|
|
358
|
+
tools: [...subagent.tools],
|
|
359
|
+
allowedWorkspaceTools: subagent.allowedWorkspaceTools ? [...subagent.allowedWorkspaceTools] : void 0
|
|
360
|
+
})),
|
|
361
|
+
diagnostics: cached.diagnostics.map((diagnostic) => ({ ...diagnostic }))
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const loaded = loadAgentDefinitions(projectPath);
|
|
365
|
+
agentDefinitionCache.set(projectPath, loaded);
|
|
366
|
+
return loadAgentDefinitionsCached(projectPath);
|
|
367
|
+
}
|
|
368
|
+
function getModeDefinition(projectPath, modeId) {
|
|
369
|
+
return loadAgentDefinitionsCached(projectPath).modes.find((mode) => mode.id === modeId);
|
|
370
|
+
}
|
|
371
|
+
function formatAgentDefinitionDiagnostics(diagnostics, maxItems = 5) {
|
|
372
|
+
if (diagnostics.length === 0) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
376
|
+
const warnings = diagnostics.filter((diagnostic) => diagnostic.severity === "warning");
|
|
377
|
+
const headlineParts = [];
|
|
378
|
+
if (errors.length > 0) {
|
|
379
|
+
headlineParts.push(`${errors.length} error${errors.length === 1 ? "" : "s"}`);
|
|
380
|
+
}
|
|
381
|
+
if (warnings.length > 0) {
|
|
382
|
+
headlineParts.push(`${warnings.length} warning${warnings.length === 1 ? "" : "s"}`);
|
|
383
|
+
}
|
|
384
|
+
const visible = diagnostics.slice(0, maxItems);
|
|
385
|
+
const lines = visible.map((diagnostic) => {
|
|
386
|
+
const prefix = diagnostic.severity === "error" ? "error" : "warning";
|
|
387
|
+
return `- ${prefix}: ${diagnostic.sourcePath} \u2014 ${diagnostic.message}`;
|
|
388
|
+
});
|
|
389
|
+
if (diagnostics.length > maxItems) {
|
|
390
|
+
lines.push(`- ... ${diagnostics.length - maxItems} more`);
|
|
391
|
+
}
|
|
392
|
+
return `Ignored invalid custom agent definitions (${headlineParts.join(", ")}):
|
|
393
|
+
${lines.join("\n")}`;
|
|
394
|
+
}
|
|
395
|
+
function getSubagentOwnerModes(registry, subagentId) {
|
|
396
|
+
return registry.modes.filter((mode) => mode.subagents.includes(subagentId));
|
|
397
|
+
}
|
|
398
|
+
function getAllowedSubagentIdsForMode(registry, modeId) {
|
|
399
|
+
return registry.modes.find((mode) => mode.id === modeId)?.subagents ?? [];
|
|
400
|
+
}
|
|
401
|
+
function getSubagentModeMap(registry) {
|
|
402
|
+
const mapping = {};
|
|
403
|
+
for (const subagent of registry.subagents) {
|
|
404
|
+
if (subagent.defaultModelFromMode) {
|
|
405
|
+
mapping[subagent.id] = subagent.defaultModelFromMode;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const ownerMode = registry.modes.find((mode) => mode.subagents.includes(subagent.id));
|
|
409
|
+
if (ownerMode) {
|
|
410
|
+
mapping[subagent.id] = ownerMode.id;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return mapping;
|
|
414
|
+
}
|
|
415
|
+
function createHarnessModesFromDefinitions(registry, codeAgent) {
|
|
416
|
+
return registry.modes.map((mode) => ({
|
|
417
|
+
id: mode.id,
|
|
418
|
+
name: mode.name,
|
|
419
|
+
default: mode.default,
|
|
420
|
+
defaultModelId: mode.defaultModelId,
|
|
421
|
+
color: mode.color,
|
|
422
|
+
agent: codeAgent
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
function createHarnessSubagentsFromDefinitions(registry, disabledTools) {
|
|
426
|
+
return registry.subagents.map((subagent) => {
|
|
427
|
+
const directTools = Object.fromEntries(
|
|
428
|
+
subagent.tools.filter((toolId) => !disabledTools?.includes(toolId) && toolId in DIRECT_SUBAGENT_TOOLS).map((toolId) => [toolId, DIRECT_SUBAGENT_TOOLS[toolId]])
|
|
429
|
+
);
|
|
430
|
+
const allowedWorkspaceTools = subagent.allowedWorkspaceTools?.filter((toolId) => !disabledTools?.includes(toolId));
|
|
431
|
+
return {
|
|
432
|
+
id: subagent.id,
|
|
433
|
+
name: subagent.name,
|
|
434
|
+
description: subagent.description,
|
|
435
|
+
instructions: subagent.instructions,
|
|
436
|
+
defaultModelId: void 0,
|
|
437
|
+
allowedWorkspaceTools,
|
|
438
|
+
...Object.keys(directTools).length > 0 ? { tools: directTools } : {}
|
|
439
|
+
};
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
var MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
443
|
+
var KEEP_SIZE = 4 * 1024 * 1024;
|
|
444
|
+
function formatLogArg(a) {
|
|
445
|
+
if (typeof a === "string") return a;
|
|
446
|
+
if (a instanceof Error) return a.stack ?? `${a.name}: ${a.message}`;
|
|
447
|
+
try {
|
|
448
|
+
return JSON.stringify(a);
|
|
449
|
+
} catch {
|
|
450
|
+
return String(a);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function getDebugLogFile() {
|
|
454
|
+
return path4.join(getAppDataDir(), "debug.log");
|
|
455
|
+
}
|
|
456
|
+
function appendDebugLogLine(level, message) {
|
|
457
|
+
const logFile = getDebugLogFile();
|
|
458
|
+
const logDir = path4.dirname(logFile);
|
|
459
|
+
if (!fs2.existsSync(logDir)) {
|
|
460
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
461
|
+
}
|
|
462
|
+
truncateLogFile(logFile);
|
|
463
|
+
fs2.appendFileSync(logFile, `[${level}] ${(/* @__PURE__ */ new Date()).toISOString()} ${message}
|
|
464
|
+
`);
|
|
465
|
+
}
|
|
466
|
+
function truncateLogFile(logFile) {
|
|
467
|
+
try {
|
|
468
|
+
const stat = fs2.statSync(logFile);
|
|
469
|
+
if (stat.size > MAX_LOG_SIZE) {
|
|
470
|
+
const buf = Buffer.alloc(KEEP_SIZE);
|
|
471
|
+
const fd = fs2.openSync(logFile, "r");
|
|
472
|
+
fs2.readSync(fd, buf, 0, KEEP_SIZE, stat.size - KEEP_SIZE);
|
|
473
|
+
fs2.closeSync(fd);
|
|
474
|
+
const firstNewline = buf.indexOf(10);
|
|
475
|
+
const trimmed = firstNewline >= 0 ? buf.subarray(firstNewline + 1) : buf;
|
|
476
|
+
fs2.writeFileSync(logFile, trimmed);
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function setupDebugLogging() {
|
|
482
|
+
const debugEnabled = ["true", "1"].includes(process.env.MASTRA_DEBUG ?? "");
|
|
483
|
+
if (debugEnabled) {
|
|
484
|
+
const logFile = getDebugLogFile();
|
|
485
|
+
truncateLogFile(logFile);
|
|
486
|
+
const logStream = fs2.createWriteStream(logFile, { flags: "a" });
|
|
487
|
+
console.error = (...args) => {
|
|
488
|
+
logStream.write(`[ERROR] ${(/* @__PURE__ */ new Date()).toISOString()} ${args.map(formatLogArg).join(" ")}
|
|
489
|
+
`);
|
|
490
|
+
};
|
|
491
|
+
console.warn = (...args) => {
|
|
492
|
+
logStream.write(`[WARN] ${(/* @__PURE__ */ new Date()).toISOString()} ${args.map(formatLogArg).join(" ")}
|
|
493
|
+
`);
|
|
494
|
+
};
|
|
495
|
+
} else {
|
|
496
|
+
const noop = () => {
|
|
497
|
+
};
|
|
498
|
+
console.error = noop;
|
|
499
|
+
console.warn = noop;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function logRuntimeProfile(event, durationMs, details) {
|
|
503
|
+
const profileEnabled = ["true", "1"].includes(process.env.MASTRA_PROFILE ?? "");
|
|
504
|
+
if (!profileEnabled) return;
|
|
505
|
+
const payload = details ? `${event} durationMs=${durationMs.toFixed(2)} ${formatLogArg(details)}` : `${event} durationMs=${durationMs.toFixed(2)}`;
|
|
506
|
+
appendDebugLogLine("PROFILE", payload);
|
|
507
|
+
}
|
|
508
|
+
var STORAGE_DEFAULTS = {
|
|
509
|
+
backend: "libsql",
|
|
510
|
+
libsql: {},
|
|
511
|
+
pg: {}
|
|
512
|
+
};
|
|
513
|
+
var DEFAULTS = {
|
|
514
|
+
onboarding: {
|
|
515
|
+
completedAt: null,
|
|
516
|
+
skippedAt: null,
|
|
517
|
+
version: 0,
|
|
518
|
+
modePackId: null,
|
|
519
|
+
omPackId: null,
|
|
520
|
+
claudeMaxOAuthWarningAcknowledgedAt: null
|
|
521
|
+
},
|
|
522
|
+
models: {
|
|
523
|
+
activeModelPackId: null,
|
|
524
|
+
modeDefaults: {},
|
|
525
|
+
activeOmPackId: null,
|
|
526
|
+
omModelOverride: null,
|
|
527
|
+
subagentModels: {}
|
|
528
|
+
},
|
|
529
|
+
preferences: {
|
|
530
|
+
yolo: null,
|
|
531
|
+
theme: "auto",
|
|
532
|
+
thinkingLevel: "off",
|
|
533
|
+
quietMode: false
|
|
534
|
+
},
|
|
535
|
+
storage: { ...STORAGE_DEFAULTS },
|
|
536
|
+
customModelPacks: [],
|
|
537
|
+
customProviders: [],
|
|
538
|
+
modelUseCounts: {},
|
|
539
|
+
updateDismissedVersion: null,
|
|
540
|
+
lsp: {}
|
|
541
|
+
};
|
|
542
|
+
var THINKING_LEVEL_VALUES = ["off", "low", "medium", "high", "xhigh"];
|
|
543
|
+
var settingsCache = /* @__PURE__ */ new Map();
|
|
544
|
+
function cloneSettings(settings) {
|
|
545
|
+
return structuredClone(settings);
|
|
546
|
+
}
|
|
547
|
+
function parseThinkingLevel(value) {
|
|
548
|
+
return typeof value === "string" && THINKING_LEVEL_VALUES.includes(value) ? value : DEFAULTS.preferences.thinkingLevel;
|
|
549
|
+
}
|
|
550
|
+
function parsePreferences(rawPreferences) {
|
|
551
|
+
const raw = rawPreferences && typeof rawPreferences === "object" ? rawPreferences : {};
|
|
552
|
+
return {
|
|
553
|
+
...DEFAULTS.preferences,
|
|
554
|
+
...raw,
|
|
555
|
+
thinkingLevel: parseThinkingLevel(raw.thinkingLevel)
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function getSettingsPath() {
|
|
559
|
+
return join(getAppDataDir(), "settings.json");
|
|
560
|
+
}
|
|
561
|
+
function getCustomProviderId(name) {
|
|
562
|
+
const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
563
|
+
return slug || "provider";
|
|
564
|
+
}
|
|
565
|
+
function toCustomProviderModelId(providerName, modelName) {
|
|
566
|
+
const providerId = getCustomProviderId(providerName);
|
|
567
|
+
const trimmedModelName = modelName.trim();
|
|
568
|
+
const providerPrefix = `${providerId}/`;
|
|
569
|
+
if (trimmedModelName.startsWith(providerPrefix)) {
|
|
570
|
+
return trimmedModelName;
|
|
571
|
+
}
|
|
572
|
+
return `${providerId}/${trimmedModelName}`;
|
|
573
|
+
}
|
|
574
|
+
function parseCustomProviders(rawProviders) {
|
|
575
|
+
if (!Array.isArray(rawProviders)) return [];
|
|
576
|
+
const parsedProviders = [];
|
|
577
|
+
for (const rawProvider of rawProviders) {
|
|
578
|
+
if (!rawProvider || typeof rawProvider !== "object") continue;
|
|
579
|
+
const candidate = rawProvider;
|
|
580
|
+
const name = typeof candidate.name === "string" ? candidate.name.trim() : "";
|
|
581
|
+
const url = typeof candidate.url === "string" ? candidate.url.trim() : "";
|
|
582
|
+
if (!name || !url) continue;
|
|
583
|
+
const providerId = getCustomProviderId(name);
|
|
584
|
+
const models = Array.isArray(candidate.models) ? [
|
|
585
|
+
...new Set(
|
|
586
|
+
candidate.models.filter((model) => typeof model === "string").map((model) => model.trim()).map((model) => {
|
|
587
|
+
const providerPrefix = `${providerId}/`;
|
|
588
|
+
if (model.startsWith(providerPrefix)) {
|
|
589
|
+
return model.slice(providerPrefix.length);
|
|
590
|
+
}
|
|
591
|
+
return model;
|
|
592
|
+
})
|
|
593
|
+
)
|
|
594
|
+
].filter((model) => model.length > 0) : [];
|
|
595
|
+
const apiKey = typeof candidate.apiKey === "string" && candidate.apiKey.trim().length > 0 ? candidate.apiKey.trim() : void 0;
|
|
596
|
+
parsedProviders.push({
|
|
597
|
+
name,
|
|
598
|
+
url,
|
|
599
|
+
...apiKey ? { apiKey } : {},
|
|
600
|
+
models
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
return parsedProviders;
|
|
604
|
+
}
|
|
605
|
+
function migrateFromAuth(settingsPath) {
|
|
606
|
+
const authPath = join(getAppDataDir(), "auth.json");
|
|
607
|
+
if (!existsSync(authPath)) return false;
|
|
608
|
+
let authData;
|
|
609
|
+
try {
|
|
610
|
+
authData = JSON.parse(readFileSync(authPath, "utf-8"));
|
|
611
|
+
} catch {
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
const modelKeys = Object.keys(authData).filter((k) => k.startsWith("_"));
|
|
615
|
+
if (modelKeys.length === 0) return false;
|
|
616
|
+
let settings;
|
|
617
|
+
if (existsSync(settingsPath)) {
|
|
618
|
+
try {
|
|
619
|
+
const raw = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
620
|
+
settings = {
|
|
621
|
+
onboarding: { ...DEFAULTS.onboarding, ...raw.onboarding },
|
|
622
|
+
models: { ...DEFAULTS.models, ...raw.models },
|
|
623
|
+
preferences: parsePreferences(raw.preferences),
|
|
624
|
+
storage: {
|
|
625
|
+
...STORAGE_DEFAULTS,
|
|
626
|
+
...raw.storage,
|
|
627
|
+
libsql: { ...STORAGE_DEFAULTS.libsql, ...raw.storage?.libsql },
|
|
628
|
+
pg: { ...STORAGE_DEFAULTS.pg, ...raw.storage?.pg }
|
|
629
|
+
},
|
|
630
|
+
customModelPacks: Array.isArray(raw.customModelPacks) ? raw.customModelPacks : [],
|
|
631
|
+
customProviders: parseCustomProviders(raw.customProviders),
|
|
632
|
+
modelUseCounts: raw.modelUseCounts && typeof raw.modelUseCounts === "object" ? raw.modelUseCounts : {},
|
|
633
|
+
updateDismissedVersion: typeof raw.updateDismissedVersion === "string" ? raw.updateDismissedVersion : null,
|
|
634
|
+
lsp: raw.lsp && typeof raw.lsp === "object" ? raw.lsp : void 0
|
|
635
|
+
};
|
|
636
|
+
} catch {
|
|
637
|
+
settings = structuredClone(DEFAULTS);
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
settings = structuredClone(DEFAULTS);
|
|
641
|
+
}
|
|
642
|
+
if (authData._modelRanks && typeof authData._modelRanks === "object") {
|
|
643
|
+
settings.modelUseCounts = { ...authData._modelRanks, ...settings.modelUseCounts };
|
|
644
|
+
}
|
|
645
|
+
for (const key of modelKeys) {
|
|
646
|
+
const modeMatch = key.match(/^_modeModelId_(.+)$/);
|
|
647
|
+
if (modeMatch?.[1] && typeof authData[key] === "string" && !settings.models.modeDefaults[modeMatch[1]]) {
|
|
648
|
+
settings.models.modeDefaults[modeMatch[1]] = authData[key];
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
for (const key of modelKeys) {
|
|
652
|
+
if (key === "_subagentModelId" && typeof authData[key] === "string" && !settings.models.subagentModels["default"]) {
|
|
653
|
+
settings.models.subagentModels["default"] = authData[key];
|
|
654
|
+
}
|
|
655
|
+
const saMatch = key.match(/^_subagentModelId_(.+)$/);
|
|
656
|
+
if (saMatch?.[1] && typeof authData[key] === "string" && !settings.models.subagentModels[saMatch[1]]) {
|
|
657
|
+
settings.models.subagentModels[saMatch[1]] = authData[key];
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
saveSettings(settings, settingsPath);
|
|
661
|
+
for (const key of modelKeys) {
|
|
662
|
+
delete authData[key];
|
|
663
|
+
}
|
|
664
|
+
try {
|
|
665
|
+
writeFileSync(authPath, JSON.stringify(authData, null, 2), "utf-8");
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
var LEGACY_VARIED_MODELS = {
|
|
671
|
+
plan: "openai/gpt-5.3-codex",
|
|
672
|
+
build: "anthropic/claude-sonnet-4-5",
|
|
673
|
+
fast: "anthropic/claude-haiku-4-5"
|
|
674
|
+
};
|
|
675
|
+
function migrateLegacyVariedPack(settings) {
|
|
676
|
+
const legacyPackId = "varied";
|
|
677
|
+
const customPackId = "custom:varied";
|
|
678
|
+
const hasLegacyReference = settings.models.activeModelPackId === legacyPackId || settings.onboarding.modePackId === legacyPackId;
|
|
679
|
+
if (!hasLegacyReference) return false;
|
|
680
|
+
const existingIdx = settings.customModelPacks.findIndex((p) => p.name === "varied");
|
|
681
|
+
if (existingIdx >= 0) {
|
|
682
|
+
const existing = settings.customModelPacks[existingIdx];
|
|
683
|
+
const modelsMatch = Object.entries(LEGACY_VARIED_MODELS).every(([k, v]) => existing.models[k] === v);
|
|
684
|
+
if (!modelsMatch) {
|
|
685
|
+
existing.models = { ...LEGACY_VARIED_MODELS };
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
688
|
+
settings.customModelPacks.push({
|
|
689
|
+
name: "varied",
|
|
690
|
+
models: { ...LEGACY_VARIED_MODELS },
|
|
691
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
if (settings.models.activeModelPackId === legacyPackId) {
|
|
695
|
+
settings.models.activeModelPackId = customPackId;
|
|
696
|
+
if (Object.keys(settings.models.modeDefaults).length === 0) {
|
|
697
|
+
settings.models.modeDefaults = { ...LEGACY_VARIED_MODELS };
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (settings.onboarding.modePackId === legacyPackId) {
|
|
701
|
+
settings.onboarding.modePackId = customPackId;
|
|
702
|
+
}
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
function loadSettings(filePath = getSettingsPath()) {
|
|
706
|
+
migrateFromAuth(filePath);
|
|
707
|
+
if (!existsSync(filePath)) return structuredClone(DEFAULTS);
|
|
708
|
+
try {
|
|
709
|
+
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
710
|
+
const settings = {
|
|
711
|
+
...raw,
|
|
712
|
+
onboarding: { ...DEFAULTS.onboarding, ...raw.onboarding },
|
|
713
|
+
models: { ...DEFAULTS.models, ...raw.models },
|
|
714
|
+
preferences: parsePreferences(raw.preferences),
|
|
715
|
+
storage: {
|
|
716
|
+
...STORAGE_DEFAULTS,
|
|
717
|
+
...raw.storage,
|
|
718
|
+
libsql: { ...STORAGE_DEFAULTS.libsql, ...raw.storage?.libsql },
|
|
719
|
+
pg: { ...STORAGE_DEFAULTS.pg, ...raw.storage?.pg }
|
|
720
|
+
},
|
|
721
|
+
customModelPacks: Array.isArray(raw.customModelPacks) ? raw.customModelPacks : [],
|
|
722
|
+
customProviders: parseCustomProviders(raw.customProviders),
|
|
723
|
+
modelUseCounts: raw.modelUseCounts && typeof raw.modelUseCounts === "object" ? raw.modelUseCounts : {},
|
|
724
|
+
updateDismissedVersion: typeof raw.updateDismissedVersion === "string" ? raw.updateDismissedVersion : null,
|
|
725
|
+
lsp: raw.lsp && typeof raw.lsp === "object" ? raw.lsp : void 0
|
|
726
|
+
};
|
|
727
|
+
let settingsChanged = false;
|
|
728
|
+
if (raw.models?.omModelId && !settings.models.omModelOverride) {
|
|
729
|
+
settings.models.omModelOverride = raw.models.omModelId;
|
|
730
|
+
settingsChanged = true;
|
|
731
|
+
}
|
|
732
|
+
if (migrateLegacyVariedPack(settings)) {
|
|
733
|
+
settingsChanged = true;
|
|
734
|
+
}
|
|
735
|
+
if (settingsChanged) {
|
|
736
|
+
saveSettings(settings, filePath);
|
|
737
|
+
}
|
|
738
|
+
return settings;
|
|
739
|
+
} catch {
|
|
740
|
+
return structuredClone(DEFAULTS);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function loadSettingsCached(filePath = getSettingsPath()) {
|
|
744
|
+
const cached = settingsCache.get(filePath);
|
|
745
|
+
if (cached) {
|
|
746
|
+
return cloneSettings(cached);
|
|
747
|
+
}
|
|
748
|
+
const settings = loadSettings(filePath);
|
|
749
|
+
settingsCache.set(filePath, cloneSettings(settings));
|
|
750
|
+
return cloneSettings(settings);
|
|
751
|
+
}
|
|
752
|
+
var THREAD_ACTIVE_MODEL_PACK_ID_KEY = "activeModelPackId";
|
|
753
|
+
function parseThreadSettings(metadata) {
|
|
754
|
+
const modeModelIds = {};
|
|
755
|
+
for (const [key, value] of Object.entries(metadata ?? {})) {
|
|
756
|
+
const modeMatch = key.match(/^modeModelId_(.+)$/);
|
|
757
|
+
if (modeMatch?.[1] && typeof value === "string" && value.length > 0) {
|
|
758
|
+
modeModelIds[modeMatch[1]] = value;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const rawPackId = metadata?.[THREAD_ACTIVE_MODEL_PACK_ID_KEY];
|
|
762
|
+
const activeModelPackId = typeof rawPackId === "string" && rawPackId.length > 0 ? rawPackId : null;
|
|
763
|
+
return {
|
|
764
|
+
activeModelPackId,
|
|
765
|
+
modeModelIds
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function resolveThreadActiveModelPackId(settings, builtinPacks, metadata) {
|
|
769
|
+
const threadSettings = parseThreadSettings(metadata);
|
|
770
|
+
const isKnownPack = (packId) => {
|
|
771
|
+
if (packId.startsWith("custom:")) {
|
|
772
|
+
const name = packId.slice("custom:".length);
|
|
773
|
+
return settings.customModelPacks.some((p) => p.name === name);
|
|
774
|
+
}
|
|
775
|
+
return builtinPacks.some((p) => p.id === packId);
|
|
776
|
+
};
|
|
777
|
+
if (threadSettings.activeModelPackId && isKnownPack(threadSettings.activeModelPackId)) {
|
|
778
|
+
return threadSettings.activeModelPackId;
|
|
779
|
+
}
|
|
780
|
+
const allPacks = [
|
|
781
|
+
...builtinPacks,
|
|
782
|
+
...settings.customModelPacks.map((p) => ({ id: `custom:${p.name}`, models: p.models }))
|
|
783
|
+
];
|
|
784
|
+
for (const pack of allPacks) {
|
|
785
|
+
const packEntries = Object.entries(pack.models);
|
|
786
|
+
const threadEntries = Object.keys(threadSettings.modeModelIds);
|
|
787
|
+
const matches = packEntries.length === threadEntries.length && packEntries.every(([modeId, modelId]) => threadSettings.modeModelIds[modeId] === modelId);
|
|
788
|
+
if (matches) return pack.id;
|
|
789
|
+
}
|
|
790
|
+
if (settings.models.activeModelPackId && isKnownPack(settings.models.activeModelPackId)) {
|
|
791
|
+
return settings.models.activeModelPackId;
|
|
792
|
+
}
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
function resolveModelDefaults(settings, builtinPacks) {
|
|
796
|
+
const { activeModelPackId, modeDefaults } = settings.models;
|
|
797
|
+
if (!activeModelPackId) return modeDefaults;
|
|
798
|
+
if (activeModelPackId.startsWith("custom:")) {
|
|
799
|
+
const name = activeModelPackId.slice("custom:".length);
|
|
800
|
+
const pack = settings.customModelPacks.find((p) => p.name === name);
|
|
801
|
+
if (pack) return pack.models;
|
|
802
|
+
return modeDefaults;
|
|
803
|
+
}
|
|
804
|
+
const builtin = builtinPacks.find((p) => p.id === activeModelPackId);
|
|
805
|
+
if (builtin) return builtin.models;
|
|
806
|
+
return modeDefaults;
|
|
807
|
+
}
|
|
808
|
+
function resolveOmModel(settings, builtinOmPacks) {
|
|
809
|
+
const { activeOmPackId, omModelOverride } = settings.models;
|
|
810
|
+
if (!activeOmPackId) return omModelOverride;
|
|
811
|
+
if (activeOmPackId === "custom") return omModelOverride;
|
|
812
|
+
const pack = builtinOmPacks.find((p) => p.id === activeOmPackId);
|
|
813
|
+
if (pack) return pack.modelId;
|
|
814
|
+
return omModelOverride;
|
|
815
|
+
}
|
|
816
|
+
function saveSettings(settings, filePath = getSettingsPath()) {
|
|
817
|
+
const dir = dirname(filePath);
|
|
818
|
+
if (!existsSync(dir)) {
|
|
819
|
+
mkdirSync(dir, { recursive: true });
|
|
820
|
+
}
|
|
821
|
+
writeFileSync(filePath, JSON.stringify(settings, null, 2), "utf-8");
|
|
822
|
+
settingsCache.set(filePath, cloneSettings(settings));
|
|
823
|
+
}
|
|
824
|
+
var LSPClient = class {
|
|
825
|
+
connection = null;
|
|
826
|
+
process = null;
|
|
827
|
+
serverInfo;
|
|
828
|
+
workspaceRoot;
|
|
829
|
+
diagnostics = /* @__PURE__ */ new Map();
|
|
830
|
+
initializationOptions = null;
|
|
831
|
+
constructor(serverInfo, workspaceRoot) {
|
|
832
|
+
this.serverInfo = serverInfo;
|
|
833
|
+
this.workspaceRoot = workspaceRoot;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Initialize the LSP connection
|
|
837
|
+
*/
|
|
838
|
+
async initialize() {
|
|
839
|
+
const spawnResult = await this.serverInfo.spawn(this.workspaceRoot);
|
|
840
|
+
if (!spawnResult) {
|
|
841
|
+
throw new Error("Failed to spawn LSP server");
|
|
842
|
+
}
|
|
843
|
+
let initializationOptions = void 0;
|
|
844
|
+
if ("process" in spawnResult) {
|
|
845
|
+
this.process = spawnResult.process;
|
|
846
|
+
initializationOptions = spawnResult.initialization;
|
|
847
|
+
} else {
|
|
848
|
+
this.process = spawnResult;
|
|
849
|
+
}
|
|
850
|
+
if (!this.process.stdin || !this.process.stdout) {
|
|
851
|
+
throw new Error("Failed to create LSP process with proper stdio");
|
|
852
|
+
}
|
|
853
|
+
const reader = new StreamMessageReader(this.process.stdout);
|
|
854
|
+
const writer = new StreamMessageWriter(this.process.stdin);
|
|
855
|
+
this.connection = createMessageConnection(reader, writer);
|
|
856
|
+
this.connection.onError((error) => {
|
|
857
|
+
});
|
|
858
|
+
this.connection.onNotification("textDocument/publishDiagnostics", (params) => {
|
|
859
|
+
this.diagnostics.set(params.uri, params.diagnostics);
|
|
860
|
+
});
|
|
861
|
+
this.connection.onNotification((_method, _params) => {
|
|
862
|
+
});
|
|
863
|
+
this.connection.listen();
|
|
864
|
+
if (this.process.stderr) {
|
|
865
|
+
this.process.stderr.on("data", (_data) => {
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
this.process.on("error", (_error) => {
|
|
869
|
+
});
|
|
870
|
+
this.process.on("exit", (_code, _signal) => {
|
|
871
|
+
});
|
|
872
|
+
const initParams = {
|
|
873
|
+
processId: process.pid,
|
|
874
|
+
rootUri: `file://${this.workspaceRoot}`,
|
|
875
|
+
workspaceFolders: [
|
|
876
|
+
{
|
|
877
|
+
name: "workspace",
|
|
878
|
+
uri: `file://${this.workspaceRoot}`
|
|
879
|
+
}
|
|
880
|
+
],
|
|
881
|
+
capabilities: {
|
|
882
|
+
window: {
|
|
883
|
+
workDoneProgress: true
|
|
884
|
+
},
|
|
885
|
+
workspace: {
|
|
886
|
+
configuration: true
|
|
887
|
+
},
|
|
888
|
+
textDocument: {
|
|
889
|
+
publishDiagnostics: {
|
|
890
|
+
relatedInformation: true,
|
|
891
|
+
tagSupport: {
|
|
892
|
+
valueSet: [1, 2]
|
|
893
|
+
},
|
|
894
|
+
versionSupport: false
|
|
895
|
+
},
|
|
896
|
+
synchronization: {
|
|
897
|
+
didOpen: true,
|
|
898
|
+
didChange: true,
|
|
899
|
+
dynamicRegistration: false,
|
|
900
|
+
willSave: false,
|
|
901
|
+
willSaveWaitUntil: false,
|
|
902
|
+
didSave: false
|
|
903
|
+
},
|
|
904
|
+
completion: {
|
|
905
|
+
dynamicRegistration: false,
|
|
906
|
+
completionItem: {
|
|
907
|
+
snippetSupport: false,
|
|
908
|
+
commitCharactersSupport: false,
|
|
909
|
+
documentationFormat: ["markdown", "plaintext"],
|
|
910
|
+
deprecatedSupport: false,
|
|
911
|
+
preselectSupport: false
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
definition: {
|
|
915
|
+
dynamicRegistration: false,
|
|
916
|
+
linkSupport: true
|
|
917
|
+
},
|
|
918
|
+
typeDefinition: {
|
|
919
|
+
dynamicRegistration: false,
|
|
920
|
+
linkSupport: true
|
|
921
|
+
},
|
|
922
|
+
implementation: {
|
|
923
|
+
dynamicRegistration: false,
|
|
924
|
+
linkSupport: true
|
|
925
|
+
},
|
|
926
|
+
references: {
|
|
927
|
+
dynamicRegistration: false
|
|
928
|
+
},
|
|
929
|
+
documentHighlight: {
|
|
930
|
+
dynamicRegistration: false
|
|
931
|
+
},
|
|
932
|
+
documentSymbol: {
|
|
933
|
+
dynamicRegistration: false,
|
|
934
|
+
hierarchicalDocumentSymbolSupport: true
|
|
935
|
+
},
|
|
936
|
+
codeAction: {
|
|
937
|
+
dynamicRegistration: false,
|
|
938
|
+
codeActionLiteralSupport: {
|
|
939
|
+
codeActionKind: {
|
|
940
|
+
valueSet: [
|
|
941
|
+
"quickfix",
|
|
942
|
+
"refactor",
|
|
943
|
+
"refactor.extract",
|
|
944
|
+
"refactor.inline",
|
|
945
|
+
"refactor.rewrite",
|
|
946
|
+
"source",
|
|
947
|
+
"source.organizeImports"
|
|
948
|
+
]
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
hover: {
|
|
953
|
+
dynamicRegistration: false,
|
|
954
|
+
contentFormat: ["markdown", "plaintext"]
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
if (initializationOptions) {
|
|
960
|
+
initParams.initializationOptions = initializationOptions;
|
|
961
|
+
this.initializationOptions = initializationOptions;
|
|
962
|
+
}
|
|
963
|
+
this.connection.onRequest("workspace/configuration", (params) => {
|
|
964
|
+
return params.items?.map(() => ({})) || [];
|
|
965
|
+
});
|
|
966
|
+
this.connection.onRequest("window/workDoneProgress/create", (_params) => {
|
|
967
|
+
return null;
|
|
968
|
+
});
|
|
969
|
+
await Promise.race([
|
|
970
|
+
this.connection.sendRequest("initialize", initParams),
|
|
971
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("LSP initialize request timed out")), 1e4))
|
|
972
|
+
]);
|
|
973
|
+
try {
|
|
974
|
+
this.connection.sendNotification("initialized", {});
|
|
975
|
+
} catch {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
try {
|
|
979
|
+
if (this.initializationOptions) {
|
|
980
|
+
this.connection.sendNotification("workspace/didChangeConfiguration", {
|
|
981
|
+
settings: this.initializationOptions
|
|
982
|
+
});
|
|
983
|
+
} else {
|
|
984
|
+
this.connection.sendNotification("workspace/didChangeConfiguration", {
|
|
985
|
+
settings: {}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
} catch {
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Notify the server that a document has been opened
|
|
993
|
+
*/
|
|
994
|
+
notifyOpen(filePath, content, languageId) {
|
|
995
|
+
if (!this.connection) return;
|
|
996
|
+
const uri = `file://${filePath}`;
|
|
997
|
+
this.diagnostics.delete(uri);
|
|
998
|
+
this.connection.sendNotification("textDocument/didOpen", {
|
|
999
|
+
textDocument: {
|
|
1000
|
+
uri,
|
|
1001
|
+
languageId,
|
|
1002
|
+
version: 0,
|
|
1003
|
+
text: content
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Notify the server that a document has changed
|
|
1009
|
+
*/
|
|
1010
|
+
notifyChange(filePath, content, version) {
|
|
1011
|
+
if (!this.connection) return;
|
|
1012
|
+
this.connection.sendNotification("textDocument/didChange", {
|
|
1013
|
+
textDocument: {
|
|
1014
|
+
uri: `file://${filePath}`,
|
|
1015
|
+
version
|
|
1016
|
+
},
|
|
1017
|
+
contentChanges: [{ text: content }]
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Wait for diagnostics to be available for a specific file
|
|
1022
|
+
* @param waitForChange If true, waits for diagnostics to change from their initial state
|
|
1023
|
+
*/
|
|
1024
|
+
async waitForDiagnostics(filePath, timeoutMs = 5e3, waitForChange = false) {
|
|
1025
|
+
if (!this.connection) return [];
|
|
1026
|
+
const uri = `file://${filePath}`;
|
|
1027
|
+
const startTime = Date.now();
|
|
1028
|
+
const initialDiagnostics = this.diagnostics.get(uri);
|
|
1029
|
+
const initialCount = initialDiagnostics?.length || 0;
|
|
1030
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
1031
|
+
const currentDiagnostics = this.diagnostics.get(uri);
|
|
1032
|
+
const currentCount = currentDiagnostics?.length || 0;
|
|
1033
|
+
if (waitForChange) {
|
|
1034
|
+
if (currentDiagnostics !== void 0 && currentCount !== initialCount) {
|
|
1035
|
+
return currentDiagnostics;
|
|
1036
|
+
}
|
|
1037
|
+
} else {
|
|
1038
|
+
if (currentDiagnostics !== void 0) {
|
|
1039
|
+
return currentDiagnostics;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1043
|
+
}
|
|
1044
|
+
return waitForChange ? initialDiagnostics || [] : [];
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Get hover information for a position
|
|
1048
|
+
*/
|
|
1049
|
+
async getHover(filePath, line, character) {
|
|
1050
|
+
if (!this.connection) return null;
|
|
1051
|
+
try {
|
|
1052
|
+
return await this.connection.sendRequest("textDocument/hover", {
|
|
1053
|
+
textDocument: TextDocumentIdentifier.create(`file://${filePath}`),
|
|
1054
|
+
position: Position.create(line, character)
|
|
1055
|
+
});
|
|
1056
|
+
} catch {
|
|
1057
|
+
return null;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Get diagnostics for a specific file
|
|
1062
|
+
*/
|
|
1063
|
+
getDiagnostics(filePath) {
|
|
1064
|
+
const uri = `file://${filePath}`;
|
|
1065
|
+
return this.diagnostics.get(uri) || [];
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Get all diagnostics from all files
|
|
1069
|
+
*/
|
|
1070
|
+
getAllDiagnostics() {
|
|
1071
|
+
const allDiagnostics = [];
|
|
1072
|
+
for (const diagnostics of this.diagnostics.values()) {
|
|
1073
|
+
allDiagnostics.push(...diagnostics);
|
|
1074
|
+
}
|
|
1075
|
+
return allDiagnostics;
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Notify server that a file was closed
|
|
1079
|
+
*/
|
|
1080
|
+
notifyClose(filePath) {
|
|
1081
|
+
if (!this.connection) return;
|
|
1082
|
+
const uri = `file://${filePath}`;
|
|
1083
|
+
this.diagnostics.delete(uri);
|
|
1084
|
+
this.connection.sendNotification("textDocument/didClose", {
|
|
1085
|
+
textDocument: TextDocumentIdentifier.create(uri)
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Restart the LSP client
|
|
1090
|
+
*/
|
|
1091
|
+
async restart() {
|
|
1092
|
+
await this.shutdown();
|
|
1093
|
+
await this.initialize();
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Shutdown the connection
|
|
1097
|
+
*/
|
|
1098
|
+
async shutdown() {
|
|
1099
|
+
if (this.connection) {
|
|
1100
|
+
try {
|
|
1101
|
+
const processAlive = this.process && !this.process.killed;
|
|
1102
|
+
if (processAlive) {
|
|
1103
|
+
await Promise.race([
|
|
1104
|
+
this.connection.sendRequest("shutdown"),
|
|
1105
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Shutdown request timed out")), 1e3))
|
|
1106
|
+
]);
|
|
1107
|
+
this.connection.sendNotification("exit");
|
|
1108
|
+
}
|
|
1109
|
+
} catch {
|
|
1110
|
+
}
|
|
1111
|
+
try {
|
|
1112
|
+
this.connection.dispose();
|
|
1113
|
+
} catch {
|
|
1114
|
+
}
|
|
1115
|
+
this.connection = null;
|
|
1116
|
+
}
|
|
1117
|
+
if (this.process) {
|
|
1118
|
+
try {
|
|
1119
|
+
if (!this.process.killed) {
|
|
1120
|
+
this.process.kill();
|
|
1121
|
+
}
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1124
|
+
this.process = null;
|
|
1125
|
+
}
|
|
1126
|
+
this.diagnostics = /* @__PURE__ */ new Map();
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
// src/lsp/language.ts
|
|
1131
|
+
var LANGUAGE_EXTENSIONS = {
|
|
1132
|
+
// TypeScript/JavaScript
|
|
1133
|
+
".ts": "typescript",
|
|
1134
|
+
".tsx": "typescriptreact",
|
|
1135
|
+
".js": "javascript",
|
|
1136
|
+
".jsx": "javascriptreact",
|
|
1137
|
+
".mjs": "javascript",
|
|
1138
|
+
".cjs": "javascript",
|
|
1139
|
+
// Python
|
|
1140
|
+
".py": "python",
|
|
1141
|
+
".pyi": "python",
|
|
1142
|
+
// Go
|
|
1143
|
+
".go": "go",
|
|
1144
|
+
// Rust
|
|
1145
|
+
".rs": "rust",
|
|
1146
|
+
// C/C++
|
|
1147
|
+
".c": "c",
|
|
1148
|
+
".cpp": "cpp",
|
|
1149
|
+
".cc": "cpp",
|
|
1150
|
+
".cxx": "cpp",
|
|
1151
|
+
".h": "c",
|
|
1152
|
+
".hpp": "cpp",
|
|
1153
|
+
// Java
|
|
1154
|
+
".java": "java",
|
|
1155
|
+
// JSON
|
|
1156
|
+
".json": "json",
|
|
1157
|
+
".jsonc": "jsonc",
|
|
1158
|
+
// YAML
|
|
1159
|
+
".yaml": "yaml",
|
|
1160
|
+
".yml": "yaml",
|
|
1161
|
+
// Markdown
|
|
1162
|
+
".md": "markdown",
|
|
1163
|
+
// HTML/CSS
|
|
1164
|
+
".html": "html",
|
|
1165
|
+
".css": "css",
|
|
1166
|
+
".scss": "scss",
|
|
1167
|
+
".sass": "sass",
|
|
1168
|
+
".less": "less"
|
|
1169
|
+
};
|
|
1170
|
+
function getLanguageId(filePath) {
|
|
1171
|
+
const ext = filePath.substring(filePath.lastIndexOf("."));
|
|
1172
|
+
return LANGUAGE_EXTENSIONS[ext];
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// src/lsp/server.ts
|
|
1176
|
+
function resolveLocalBin(binName, baseDir = process.cwd()) {
|
|
1177
|
+
const binDir = join(baseDir, "node_modules", ".bin");
|
|
1178
|
+
const windowsPath = join(binDir, `${binName}.cmd`);
|
|
1179
|
+
const defaultPath = join(binDir, binName);
|
|
1180
|
+
if (process.platform === "win32" && existsSync(windowsPath)) {
|
|
1181
|
+
return windowsPath;
|
|
1182
|
+
}
|
|
1183
|
+
return defaultPath;
|
|
1184
|
+
}
|
|
1185
|
+
function findNearestRoot(cwd, markers) {
|
|
1186
|
+
let current = cwd;
|
|
1187
|
+
while (current !== "/") {
|
|
1188
|
+
for (const marker of markers) {
|
|
1189
|
+
if (existsSync(join(current, marker))) {
|
|
1190
|
+
return current;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const parent = join(current, "..");
|
|
1194
|
+
if (parent === current) break;
|
|
1195
|
+
current = parent;
|
|
1196
|
+
}
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
var BUILTIN_SERVERS = {
|
|
1200
|
+
typescript: {
|
|
1201
|
+
id: "typescript",
|
|
1202
|
+
name: "TypeScript Language Server",
|
|
1203
|
+
languageIds: ["typescript", "typescriptreact", "javascript", "javascriptreact"],
|
|
1204
|
+
installHint: "Install typescript-language-server for TypeScript LSP support",
|
|
1205
|
+
root: (cwd) => findNearestRoot(cwd, ["tsconfig.json", "package.json"]),
|
|
1206
|
+
spawn: async (root) => {
|
|
1207
|
+
const requireFromRoot = createRequire(pathToFileURL(path4__default.join(root, "package.json")));
|
|
1208
|
+
let tsserver;
|
|
1209
|
+
try {
|
|
1210
|
+
tsserver = requireFromRoot.resolve("typescript/lib/tsserver.js");
|
|
1211
|
+
} catch {
|
|
1212
|
+
tsserver = void 0;
|
|
1213
|
+
}
|
|
1214
|
+
if (!tsserver) {
|
|
1215
|
+
return void 0;
|
|
1216
|
+
}
|
|
1217
|
+
const localBin = resolveLocalBin("typescript-language-server", root);
|
|
1218
|
+
const cwdBin = resolveLocalBin("typescript-language-server");
|
|
1219
|
+
let tslsBinary;
|
|
1220
|
+
if (existsSync(localBin)) {
|
|
1221
|
+
tslsBinary = localBin;
|
|
1222
|
+
} else if (existsSync(cwdBin)) {
|
|
1223
|
+
tslsBinary = cwdBin;
|
|
1224
|
+
}
|
|
1225
|
+
if (!tslsBinary) {
|
|
1226
|
+
return void 0;
|
|
1227
|
+
}
|
|
1228
|
+
const proc = spawn(tslsBinary, ["--stdio"], {
|
|
1229
|
+
cwd: root,
|
|
1230
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1231
|
+
});
|
|
1232
|
+
return {
|
|
1233
|
+
process: proc,
|
|
1234
|
+
initialization: {
|
|
1235
|
+
tsserver: {
|
|
1236
|
+
path: tsserver,
|
|
1237
|
+
logVerbosity: "off"
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
},
|
|
1243
|
+
eslint: {
|
|
1244
|
+
id: "eslint",
|
|
1245
|
+
name: "ESLint Language Server",
|
|
1246
|
+
languageIds: ["typescript", "typescriptreact", "javascript", "javascriptreact"],
|
|
1247
|
+
installHint: "Install eslint-lsp for ESLint diagnostics",
|
|
1248
|
+
root: (cwd) => findNearestRoot(cwd, ["package.json", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml", ".eslintrc.yaml"]),
|
|
1249
|
+
spawn: (root) => {
|
|
1250
|
+
const binaryPath = resolveLocalBin("eslint-lsp");
|
|
1251
|
+
return spawn(binaryPath, ["--stdio"], {
|
|
1252
|
+
cwd: root,
|
|
1253
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
python: {
|
|
1258
|
+
id: "python",
|
|
1259
|
+
name: "Python Language Server (Pyright)",
|
|
1260
|
+
languageIds: ["python"],
|
|
1261
|
+
installHint: "Install pyright for Python LSP support",
|
|
1262
|
+
root: (cwd) => findNearestRoot(cwd, ["pyproject.toml", "setup.py", "requirements.txt", ".git"]),
|
|
1263
|
+
spawn: (root) => {
|
|
1264
|
+
const localPath = resolveLocalBin("pyright-langserver");
|
|
1265
|
+
const binaryPath = existsSync(localPath) ? localPath : "pyright-langserver";
|
|
1266
|
+
return spawn(binaryPath, ["--stdio"], {
|
|
1267
|
+
cwd: root,
|
|
1268
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
go: {
|
|
1273
|
+
id: "go",
|
|
1274
|
+
name: "Go Language Server (gopls)",
|
|
1275
|
+
languageIds: ["go"],
|
|
1276
|
+
installHint: "Install gopls for Go LSP support",
|
|
1277
|
+
root: (cwd) => findNearestRoot(cwd, ["go.mod", ".git"]),
|
|
1278
|
+
spawn: (root) => {
|
|
1279
|
+
return spawn("gopls", ["serve"], {
|
|
1280
|
+
cwd: root,
|
|
1281
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
},
|
|
1285
|
+
rust: {
|
|
1286
|
+
id: "rust",
|
|
1287
|
+
name: "Rust Language Server (rust-analyzer)",
|
|
1288
|
+
languageIds: ["rust"],
|
|
1289
|
+
installHint: "Install rust-analyzer for Rust LSP support",
|
|
1290
|
+
root: (cwd) => findNearestRoot(cwd, ["Cargo.toml", ".git"]),
|
|
1291
|
+
spawn: (root) => {
|
|
1292
|
+
return spawn("rust-analyzer", ["--stdio"], {
|
|
1293
|
+
cwd: root,
|
|
1294
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
function getServersForFile(filePath, cwd) {
|
|
1300
|
+
const languageId = getLanguageId(filePath);
|
|
1301
|
+
if (!languageId) return [];
|
|
1302
|
+
return Object.values(BUILTIN_SERVERS).filter(
|
|
1303
|
+
(server) => server.languageIds.includes(languageId) && server.root(cwd) !== null
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
var LSP_INIT_TIMEOUT_MS = 15e3;
|
|
1307
|
+
var LSP_FAILURE_COOLDOWN_MS = 3e4;
|
|
1308
|
+
var LSPManager = class {
|
|
1309
|
+
clients = /* @__PURE__ */ new Map();
|
|
1310
|
+
initializationPromises = /* @__PURE__ */ new Map();
|
|
1311
|
+
clientStatuses = /* @__PURE__ */ new Map();
|
|
1312
|
+
unavailableServers = /* @__PURE__ */ new Map();
|
|
1313
|
+
statusListeners = /* @__PURE__ */ new Set();
|
|
1314
|
+
emitStatusChange() {
|
|
1315
|
+
for (const listener of this.statusListeners) {
|
|
1316
|
+
listener();
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
setClientStatus(key, status) {
|
|
1320
|
+
this.clientStatuses.set(key, status);
|
|
1321
|
+
this.emitStatusChange();
|
|
1322
|
+
}
|
|
1323
|
+
getClientKey(filePath, workspaceRoot) {
|
|
1324
|
+
const servers = getServersForFile(filePath, workspaceRoot);
|
|
1325
|
+
if (servers.length === 0) {
|
|
1326
|
+
return { key: "" };
|
|
1327
|
+
}
|
|
1328
|
+
const serverInfo = servers.find(
|
|
1329
|
+
(s) => s.languageIds.includes("typescript") || s.languageIds.includes("javascript") || s.languageIds.includes("python") || s.languageIds.includes("go")
|
|
1330
|
+
) || servers[0];
|
|
1331
|
+
if (!serverInfo) {
|
|
1332
|
+
return { key: "" };
|
|
1333
|
+
}
|
|
1334
|
+
return {
|
|
1335
|
+
key: `${serverInfo.name}:${workspaceRoot}`,
|
|
1336
|
+
serverInfo
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
isInFailureCooldown(key) {
|
|
1340
|
+
const status = this.clientStatuses.get(key);
|
|
1341
|
+
return status?.state === "failed" && Date.now() - status.failedAt < LSP_FAILURE_COOLDOWN_MS;
|
|
1342
|
+
}
|
|
1343
|
+
async initializeClient(key, serverInfo, workspaceRoot) {
|
|
1344
|
+
const startedAt = Date.now();
|
|
1345
|
+
this.unavailableServers.delete(key);
|
|
1346
|
+
this.setClientStatus(key, { state: "initializing", startedAt });
|
|
1347
|
+
const initPromise = (async () => {
|
|
1348
|
+
const client = new LSPClient(serverInfo, workspaceRoot);
|
|
1349
|
+
await client.initialize();
|
|
1350
|
+
this.clients.set(key, client);
|
|
1351
|
+
})();
|
|
1352
|
+
this.initializationPromises.set(key, initPromise);
|
|
1353
|
+
try {
|
|
1354
|
+
await Promise.race([
|
|
1355
|
+
initPromise,
|
|
1356
|
+
new Promise(
|
|
1357
|
+
(_, reject) => setTimeout(() => reject(new Error("LSP client initialization timed out")), LSP_INIT_TIMEOUT_MS)
|
|
1358
|
+
)
|
|
1359
|
+
]);
|
|
1360
|
+
const durationMs = Date.now() - startedAt;
|
|
1361
|
+
this.setClientStatus(key, { state: "ready", readyAt: Date.now(), durationMs });
|
|
1362
|
+
logRuntimeProfile("lsp.initialize", durationMs, {
|
|
1363
|
+
key,
|
|
1364
|
+
status: "ready"
|
|
1365
|
+
});
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
const durationMs = Date.now() - startedAt;
|
|
1368
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1369
|
+
this.clients.delete(key);
|
|
1370
|
+
if (message === "Failed to spawn LSP server") {
|
|
1371
|
+
this.clientStatuses.delete(key);
|
|
1372
|
+
this.unavailableServers.set(key, {
|
|
1373
|
+
serverName: serverInfo.name,
|
|
1374
|
+
installHint: serverInfo.installHint
|
|
1375
|
+
});
|
|
1376
|
+
this.emitStatusChange();
|
|
1377
|
+
logRuntimeProfile("lsp.initialize", durationMs, {
|
|
1378
|
+
key,
|
|
1379
|
+
status: "unavailable"
|
|
1380
|
+
});
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
this.setClientStatus(key, {
|
|
1384
|
+
state: "failed",
|
|
1385
|
+
failedAt: Date.now(),
|
|
1386
|
+
durationMs,
|
|
1387
|
+
error: message
|
|
1388
|
+
});
|
|
1389
|
+
logRuntimeProfile("lsp.initialize", durationMs, {
|
|
1390
|
+
key,
|
|
1391
|
+
status: "failed",
|
|
1392
|
+
error: message
|
|
1393
|
+
});
|
|
1394
|
+
throw error;
|
|
1395
|
+
} finally {
|
|
1396
|
+
this.initializationPromises.delete(key);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Get or create an LSP client for a file
|
|
1401
|
+
* Returns null if no LSP server is available for the file
|
|
1402
|
+
*/
|
|
1403
|
+
async getClient(filePath, workspaceRoot) {
|
|
1404
|
+
const { key, serverInfo } = this.getClientKey(filePath, workspaceRoot);
|
|
1405
|
+
if (!serverInfo || !key) {
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
if (this.clients.has(key)) {
|
|
1409
|
+
return this.clients.get(key);
|
|
1410
|
+
}
|
|
1411
|
+
if (this.isInFailureCooldown(key)) {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
if (this.initializationPromises.has(key)) {
|
|
1415
|
+
await this.initializationPromises.get(key);
|
|
1416
|
+
return this.clients.get(key) || null;
|
|
1417
|
+
}
|
|
1418
|
+
try {
|
|
1419
|
+
await this.initializeClient(key, serverInfo, workspaceRoot);
|
|
1420
|
+
return this.clients.get(key) || null;
|
|
1421
|
+
} catch {
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
prewarmClient(filePath, workspaceRoot) {
|
|
1426
|
+
const { key, serverInfo } = this.getClientKey(filePath, workspaceRoot);
|
|
1427
|
+
if (!serverInfo || !key || this.clients.has(key) || this.initializationPromises.has(key) || this.isInFailureCooldown(key)) {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
void this.initializeClient(key, serverInfo, workspaceRoot).catch(() => {
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
getClientStatus(filePath, workspaceRoot) {
|
|
1434
|
+
const { key } = this.getClientKey(filePath, workspaceRoot);
|
|
1435
|
+
if (!key) return { state: "idle" };
|
|
1436
|
+
return this.clientStatuses.get(key) ?? { state: "idle" };
|
|
1437
|
+
}
|
|
1438
|
+
getInitializingClientCount() {
|
|
1439
|
+
let count = 0;
|
|
1440
|
+
for (const status of this.clientStatuses.values()) {
|
|
1441
|
+
if (status.state === "initializing") {
|
|
1442
|
+
count += 1;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return count;
|
|
1446
|
+
}
|
|
1447
|
+
getUnavailableClientCount() {
|
|
1448
|
+
return this.unavailableServers.size;
|
|
1449
|
+
}
|
|
1450
|
+
getUnavailableStatusHint() {
|
|
1451
|
+
const unavailable = Array.from(this.unavailableServers.values());
|
|
1452
|
+
if (unavailable.length === 0) {
|
|
1453
|
+
return null;
|
|
1454
|
+
}
|
|
1455
|
+
const first = unavailable[0];
|
|
1456
|
+
if (unavailable.length === 1) {
|
|
1457
|
+
return first?.installHint ?? `${first?.serverName} unavailable`;
|
|
1458
|
+
}
|
|
1459
|
+
return `${first?.installHint ?? `${first?.serverName} unavailable`} (+${unavailable.length - 1} more)`;
|
|
1460
|
+
}
|
|
1461
|
+
subscribeStatusChange(listener) {
|
|
1462
|
+
this.statusListeners.add(listener);
|
|
1463
|
+
return () => {
|
|
1464
|
+
this.statusListeners.delete(listener);
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
prewarmWorkspace(workspaceRoot) {
|
|
1468
|
+
const candidates = ["index.ts", "index.js", "main.py", "main.go", "main.rs"];
|
|
1469
|
+
for (const candidate of candidates) {
|
|
1470
|
+
const filePath = path4__default.join(workspaceRoot, candidate);
|
|
1471
|
+
const { serverInfo } = this.getClientKey(filePath, workspaceRoot);
|
|
1472
|
+
if (serverInfo) {
|
|
1473
|
+
this.prewarmClient(filePath, workspaceRoot);
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Shutdown a specific client
|
|
1480
|
+
*/
|
|
1481
|
+
async shutdownClient(workspaceRoot, serverName) {
|
|
1482
|
+
const keysToRemove = [];
|
|
1483
|
+
for (const [key, client] of this.clients.entries()) {
|
|
1484
|
+
if (key.includes(workspaceRoot) && (!serverName || key.startsWith(serverName))) {
|
|
1485
|
+
await client.shutdown();
|
|
1486
|
+
keysToRemove.push(key);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
for (const key of keysToRemove) {
|
|
1490
|
+
this.clients.delete(key);
|
|
1491
|
+
this.clientStatuses.delete(key);
|
|
1492
|
+
this.unavailableServers.delete(key);
|
|
1493
|
+
this.emitStatusChange();
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Shutdown all clients
|
|
1498
|
+
*/
|
|
1499
|
+
async shutdownAll() {
|
|
1500
|
+
const shutdownPromises = Array.from(this.clients.values()).map((client) => client.shutdown());
|
|
1501
|
+
await Promise.all(shutdownPromises);
|
|
1502
|
+
this.clients.clear();
|
|
1503
|
+
this.initializationPromises.clear();
|
|
1504
|
+
this.clientStatuses.clear();
|
|
1505
|
+
this.unavailableServers.clear();
|
|
1506
|
+
this.emitStatusChange();
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Get the number of active clients
|
|
1510
|
+
*/
|
|
1511
|
+
getActiveClientCount() {
|
|
1512
|
+
return this.clients.size;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Close a document in all active clients
|
|
1516
|
+
* This is useful for tests that reuse the same file path
|
|
1517
|
+
*/
|
|
1518
|
+
closeDocument(filePath) {
|
|
1519
|
+
for (const client of this.clients.values()) {
|
|
1520
|
+
try {
|
|
1521
|
+
client.notifyClose(filePath);
|
|
1522
|
+
} catch {
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
var lspManager = new LSPManager();
|
|
1528
|
+
|
|
1529
|
+
// src/onboarding/packs.ts
|
|
1530
|
+
var DEFAULT_PACK_MODE_IDS = ["plan", "build", "fast"];
|
|
1531
|
+
var MODE_PRIORITY = ["plan", "build", "fast", "ask"];
|
|
1532
|
+
function sortModeIds(modeIds) {
|
|
1533
|
+
return [...modeIds].sort((left, right) => {
|
|
1534
|
+
const leftPriority = MODE_PRIORITY.indexOf(left);
|
|
1535
|
+
const rightPriority = MODE_PRIORITY.indexOf(right);
|
|
1536
|
+
const leftRank = leftPriority === -1 ? Number.MAX_SAFE_INTEGER : leftPriority;
|
|
1537
|
+
const rightRank = rightPriority === -1 ? Number.MAX_SAFE_INTEGER : rightPriority;
|
|
1538
|
+
if (leftRank !== rightRank) return leftRank - rightRank;
|
|
1539
|
+
return left.localeCompare(right);
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
function getModePackOptions(modeDefinitions = []) {
|
|
1543
|
+
if (modeDefinitions.length === 0) {
|
|
1544
|
+
return DEFAULT_PACK_MODE_IDS.map((id) => ({ id, label: id }));
|
|
1545
|
+
}
|
|
1546
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1547
|
+
const modes = modeDefinitions.map((definition) => ({
|
|
1548
|
+
id: definition.id.trim(),
|
|
1549
|
+
label: definition.name?.trim() || definition.id.trim(),
|
|
1550
|
+
color: definition.color?.trim()
|
|
1551
|
+
})).filter((mode) => {
|
|
1552
|
+
if (!mode.id || seen.has(mode.id)) return false;
|
|
1553
|
+
seen.add(mode.id);
|
|
1554
|
+
return true;
|
|
1555
|
+
});
|
|
1556
|
+
return sortModeIds(modes.map((mode) => mode.id)).map((id) => modes.find((mode) => mode.id === id)).filter(Boolean);
|
|
1557
|
+
}
|
|
1558
|
+
function firstAssignedModel(models) {
|
|
1559
|
+
for (const modeId of MODE_PRIORITY) {
|
|
1560
|
+
const modelId = models[modeId];
|
|
1561
|
+
if (modelId) return modelId;
|
|
1562
|
+
}
|
|
1563
|
+
for (const modelId of Object.values(models)) {
|
|
1564
|
+
if (modelId) return modelId;
|
|
1565
|
+
}
|
|
1566
|
+
return "";
|
|
1567
|
+
}
|
|
1568
|
+
function getFallbackModelForMode(modeId, models) {
|
|
1569
|
+
const candidatesByMode = {
|
|
1570
|
+
ask: ["ask", "fast", "plan", "build"],
|
|
1571
|
+
fast: ["fast", "ask", "plan", "build"],
|
|
1572
|
+
plan: ["plan", "build", "fast", "ask"],
|
|
1573
|
+
build: ["build", "plan", "fast", "ask"]
|
|
1574
|
+
};
|
|
1575
|
+
for (const candidate of candidatesByMode[modeId] ?? [modeId, "fast", "ask", "plan", "build"]) {
|
|
1576
|
+
const modelId = models[candidate];
|
|
1577
|
+
if (modelId) return modelId;
|
|
1578
|
+
}
|
|
1579
|
+
return firstAssignedModel(models);
|
|
1580
|
+
}
|
|
1581
|
+
function expandPackModels(models, modeIds, options) {
|
|
1582
|
+
const expanded = { ...models };
|
|
1583
|
+
for (const modeId of modeIds) {
|
|
1584
|
+
if (expanded[modeId]) continue;
|
|
1585
|
+
expanded[modeId] = options?.fillMissing ? getFallbackModelForMode(modeId, expanded) : "";
|
|
1586
|
+
}
|
|
1587
|
+
return expanded;
|
|
1588
|
+
}
|
|
1589
|
+
function getAvailableModePacks(access, savedCustomPacks = [], modeDefinitions = []) {
|
|
1590
|
+
const packs = [];
|
|
1591
|
+
const modeIds = getModePackOptions(modeDefinitions).map((mode) => mode.id);
|
|
1592
|
+
const openaiCodex = "openai/gpt-5.3-codex";
|
|
1593
|
+
const anthropicBuild = access.anthropic === "oauth" ? "anthropic/claude-opus-4-6" : "anthropic/claude-sonnet-4-5";
|
|
1594
|
+
if (access.anthropic) {
|
|
1595
|
+
packs.push({
|
|
1596
|
+
id: "anthropic",
|
|
1597
|
+
name: "Anthropic",
|
|
1598
|
+
description: access.anthropic === "oauth" ? "All Anthropic models via Max subscription" : "All Anthropic models via API key",
|
|
1599
|
+
models: expandPackModels({
|
|
1600
|
+
build: anthropicBuild,
|
|
1601
|
+
plan: anthropicBuild,
|
|
1602
|
+
fast: "anthropic/claude-haiku-4-5"
|
|
1603
|
+
}, modeIds, { fillMissing: true })
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
if (access.openai) {
|
|
1607
|
+
packs.push({
|
|
1608
|
+
id: "openai",
|
|
1609
|
+
name: "OpenAI",
|
|
1610
|
+
description: access.openai === "oauth" ? "All OpenAI models via Codex subscription" : "All OpenAI models via API key",
|
|
1611
|
+
models: expandPackModels({
|
|
1612
|
+
build: openaiCodex,
|
|
1613
|
+
plan: openaiCodex,
|
|
1614
|
+
fast: "openai/gpt-5.1-codex-mini"
|
|
1615
|
+
}, modeIds, { fillMissing: true })
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
for (const cp of savedCustomPacks) {
|
|
1619
|
+
packs.push({
|
|
1620
|
+
id: `custom:${cp.name}`,
|
|
1621
|
+
name: cp.name,
|
|
1622
|
+
description: "Saved custom pack",
|
|
1623
|
+
models: expandPackModels(cp.models, modeIds, { fillMissing: true })
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
const hasCustom = savedCustomPacks.length > 0;
|
|
1627
|
+
packs.push({
|
|
1628
|
+
id: "custom",
|
|
1629
|
+
name: hasCustom ? "New Custom" : "Custom",
|
|
1630
|
+
description: "Choose a model for each mode",
|
|
1631
|
+
models: expandPackModels({}, modeIds)
|
|
1632
|
+
});
|
|
1633
|
+
return packs;
|
|
1634
|
+
}
|
|
1635
|
+
function getAvailableOmPacks(access) {
|
|
1636
|
+
const packs = [];
|
|
1637
|
+
if (access.google) {
|
|
1638
|
+
packs.push({
|
|
1639
|
+
id: "gemini",
|
|
1640
|
+
name: "Gemini Flash",
|
|
1641
|
+
description: access.google === "oauth" ? "Via Google OAuth" : "Via Google API key",
|
|
1642
|
+
modelId: "google/gemini-2.5-flash"
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
if (access.anthropic) {
|
|
1646
|
+
packs.push({
|
|
1647
|
+
id: "anthropic",
|
|
1648
|
+
name: "Claude Haiku",
|
|
1649
|
+
description: access.anthropic === "oauth" ? "Via Max subscription" : "Via Anthropic API key",
|
|
1650
|
+
modelId: "anthropic/claude-haiku-4-5"
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
if (access.openai) {
|
|
1654
|
+
packs.push({
|
|
1655
|
+
id: "openai",
|
|
1656
|
+
name: "Codex Mini",
|
|
1657
|
+
description: access.openai === "oauth" ? "Via Codex subscription" : "Via OpenAI API key",
|
|
1658
|
+
modelId: "openai/gpt-5.1-codex-mini"
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
if (access.deepseek) {
|
|
1662
|
+
packs.push({
|
|
1663
|
+
id: "deepseek",
|
|
1664
|
+
name: "DeepSeek",
|
|
1665
|
+
description: "Via DeepSeek API key",
|
|
1666
|
+
modelId: "deepseek/deepseek-chat"
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
packs.push({
|
|
1670
|
+
id: "custom",
|
|
1671
|
+
name: "Custom",
|
|
1672
|
+
description: "Choose any available model",
|
|
1673
|
+
modelId: ""
|
|
1674
|
+
});
|
|
1675
|
+
return packs;
|
|
1676
|
+
}
|
|
1677
|
+
var ONBOARDING_VERSION = 1;
|
|
1678
|
+
var ThreadLockError = class extends Error {
|
|
1679
|
+
constructor(threadId, ownerPid) {
|
|
1680
|
+
super(`Thread ${threadId} is locked by another process (PID ${ownerPid})`);
|
|
1681
|
+
this.threadId = threadId;
|
|
1682
|
+
this.ownerPid = ownerPid;
|
|
1683
|
+
this.name = "ThreadLockError";
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
function getLocksDir() {
|
|
1687
|
+
const dir = path4.join(getAppDataDir(), "locks");
|
|
1688
|
+
if (!fs2.existsSync(dir)) {
|
|
1689
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1690
|
+
}
|
|
1691
|
+
return dir;
|
|
1692
|
+
}
|
|
1693
|
+
function getLockPath(threadId) {
|
|
1694
|
+
const safeId = threadId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1695
|
+
return path4.join(getLocksDir(), `${safeId}.lock`);
|
|
1696
|
+
}
|
|
1697
|
+
function isProcessAlive(pid) {
|
|
1698
|
+
try {
|
|
1699
|
+
process.kill(pid, 0);
|
|
1700
|
+
return true;
|
|
1701
|
+
} catch {
|
|
1702
|
+
return false;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
function acquireThreadLock(threadId) {
|
|
1706
|
+
const lockPath = getLockPath(threadId);
|
|
1707
|
+
const myPid = process.pid;
|
|
1708
|
+
if (fs2.existsSync(lockPath)) {
|
|
1709
|
+
try {
|
|
1710
|
+
const content = fs2.readFileSync(lockPath, "utf-8").trim();
|
|
1711
|
+
const ownerPid = parseInt(content, 10);
|
|
1712
|
+
if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {
|
|
1713
|
+
throw new ThreadLockError(threadId, ownerPid);
|
|
1714
|
+
}
|
|
1715
|
+
} catch (error) {
|
|
1716
|
+
if (error instanceof ThreadLockError) throw error;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
fs2.writeFileSync(lockPath, String(myPid), { mode: 420 });
|
|
1720
|
+
}
|
|
1721
|
+
function releaseThreadLock(threadId) {
|
|
1722
|
+
const lockPath = getLockPath(threadId);
|
|
1723
|
+
const myPid = process.pid;
|
|
1724
|
+
try {
|
|
1725
|
+
if (!fs2.existsSync(lockPath)) return;
|
|
1726
|
+
const content = fs2.readFileSync(lockPath, "utf-8").trim();
|
|
1727
|
+
const ownerPid = parseInt(content, 10);
|
|
1728
|
+
if (ownerPid === myPid) {
|
|
1729
|
+
fs2.unlinkSync(lockPath);
|
|
1730
|
+
}
|
|
1731
|
+
} catch {
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function releaseAllThreadLocks() {
|
|
1735
|
+
try {
|
|
1736
|
+
const locksDir = getLocksDir();
|
|
1737
|
+
const files = fs2.readdirSync(locksDir);
|
|
1738
|
+
const myPid = String(process.pid);
|
|
1739
|
+
for (const file of files) {
|
|
1740
|
+
if (!file.endsWith(".lock")) continue;
|
|
1741
|
+
const lockPath = path4.join(locksDir, file);
|
|
1742
|
+
try {
|
|
1743
|
+
const content = fs2.readFileSync(lockPath, "utf-8").trim();
|
|
1744
|
+
if (content === myPid) {
|
|
1745
|
+
fs2.unlinkSync(lockPath);
|
|
1746
|
+
}
|
|
1747
|
+
} catch {
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
} catch {
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
export { ONBOARDING_VERSION, THREAD_ACTIVE_MODEL_PACK_ID_KEY, ThreadLockError, acquireThreadLock, createHarnessModesFromDefinitions, createHarnessSubagentsFromDefinitions, formatAgentDefinitionDiagnostics, getAllowedSubagentIdsForMode, getAvailableModePacks, getAvailableOmPacks, getCustomProviderId, getModeDefinition, getModePackOptions, getSubagentModeMap, getSubagentOwnerModes, loadAgentDefinitionsCached, loadSettingsCached, logRuntimeProfile, lspManager, releaseAllThreadLocks, releaseThreadLock, resolveModelDefaults, resolveOmModel, resolveThreadActiveModelPackId, saveSettings, setupDebugLogging, toCustomProviderModelId };
|
|
1755
|
+
//# sourceMappingURL=chunk-PKRLG6A4.js.map
|
|
1756
|
+
//# sourceMappingURL=chunk-PKRLG6A4.js.map
|