@duckmind/dm-darwin-arm64 0.13.6 → 0.13.8
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/dm +0 -0
- package/extensions/.dm-extensions.json +26 -2
- package/extensions/dm-phone/README.md +23 -0
- package/extensions/dm-phone/index.ts +12 -0
- package/extensions/dm-phone/node_modules/.package-lock.json +29 -0
- package/extensions/dm-phone/node_modules/ws/LICENSE +20 -0
- package/extensions/dm-phone/node_modules/ws/README.md +548 -0
- package/extensions/dm-phone/node_modules/ws/browser.js +8 -0
- package/extensions/dm-phone/node_modules/ws/index.js +22 -0
- package/extensions/dm-phone/node_modules/ws/lib/buffer-util.js +131 -0
- package/extensions/dm-phone/node_modules/ws/lib/constants.js +19 -0
- package/extensions/dm-phone/node_modules/ws/lib/event-target.js +292 -0
- package/extensions/dm-phone/node_modules/ws/lib/extension.js +203 -0
- package/extensions/dm-phone/node_modules/ws/lib/limiter.js +55 -0
- package/extensions/dm-phone/node_modules/ws/lib/permessage-deflate.js +528 -0
- package/extensions/dm-phone/node_modules/ws/lib/receiver.js +706 -0
- package/extensions/dm-phone/node_modules/ws/lib/sender.js +602 -0
- package/extensions/dm-phone/node_modules/ws/lib/stream.js +161 -0
- package/extensions/dm-phone/node_modules/ws/lib/subprotocol.js +62 -0
- package/extensions/dm-phone/node_modules/ws/lib/validation.js +152 -0
- package/extensions/dm-phone/node_modules/ws/lib/websocket-server.js +554 -0
- package/extensions/dm-phone/node_modules/ws/lib/websocket.js +1393 -0
- package/extensions/dm-phone/node_modules/ws/package.json +70 -0
- package/extensions/dm-phone/node_modules/ws/wrapper.mjs +21 -0
- package/extensions/dm-phone/package-lock.json +66 -0
- package/extensions/dm-phone/package.json +35 -0
- package/extensions/dm-phone/phone-session-pool.ts +8 -0
- package/extensions/dm-phone/public/app/attachments.js +233 -0
- package/extensions/dm-phone/public/app/autocomplete-controller.js +81 -0
- package/extensions/dm-phone/public/app/autocomplete.js +135 -0
- package/extensions/dm-phone/public/app/bindings.js +178 -0
- package/extensions/dm-phone/public/app/command-catalog.js +76 -0
- package/extensions/dm-phone/public/app/commands.js +370 -0
- package/extensions/dm-phone/public/app/constants.js +60 -0
- package/extensions/dm-phone/public/app/formatters.js +131 -0
- package/extensions/dm-phone/public/app/handlers.js +442 -0
- package/extensions/dm-phone/public/app/main.js +6 -0
- package/extensions/dm-phone/public/app/markdown.js +105 -0
- package/extensions/dm-phone/public/app/messages.js +418 -0
- package/extensions/dm-phone/public/app/sheet-actions.js +113 -0
- package/extensions/dm-phone/public/app/sheet-navigation.js +19 -0
- package/extensions/dm-phone/public/app/sheets-view.js +272 -0
- package/extensions/dm-phone/public/app/state.js +95 -0
- package/extensions/dm-phone/public/app/tool-rendering.js +562 -0
- package/extensions/dm-phone/public/app/transport.js +176 -0
- package/extensions/dm-phone/public/app/ui.js +409 -0
- package/extensions/dm-phone/public/app.js +1 -0
- package/extensions/dm-phone/public/icon.svg +15 -0
- package/extensions/dm-phone/public/index.html +147 -0
- package/extensions/dm-phone/public/manifest.webmanifest +17 -0
- package/extensions/dm-phone/public/styles.css +1139 -0
- package/extensions/dm-phone/public/sw.js +78 -0
- package/extensions/dm-phone/src/extension/phone-args.ts +121 -0
- package/extensions/dm-phone/src/extension/phone-paths.ts +250 -0
- package/extensions/dm-phone/src/extension/phone-quota.ts +188 -0
- package/extensions/dm-phone/src/extension/phone-runtime.ts +154 -0
- package/extensions/dm-phone/src/extension/phone-server-runtime.ts +1217 -0
- package/extensions/dm-phone/src/extension/phone-sessions.ts +139 -0
- package/extensions/dm-phone/src/extension/phone-static.ts +30 -0
- package/extensions/dm-phone/src/extension/phone-tailscale.ts +148 -0
- package/extensions/dm-phone/src/extension/phone-theme.ts +85 -0
- package/extensions/dm-phone/src/extension/register-phone-child-extension.ts +112 -0
- package/extensions/dm-phone/src/extension/register-phone-extension.ts +106 -0
- package/extensions/dm-phone/src/extension/types.ts +73 -0
- package/extensions/dm-phone/src/session-pool/parent-session-worker.ts +881 -0
- package/extensions/dm-phone/src/session-pool/session-pool.ts +470 -0
- package/extensions/dm-phone/src/session-pool/session-worker.ts +734 -0
- package/extensions/dm-phone/src/session-pool/types.ts +105 -0
- package/extensions/dm-phone/src/session-pool/utils.ts +23 -0
- package/extensions/dm-subagents/agent-management.ts +15 -6
- package/extensions/dm-subagents/agent-manager-detail.ts +12 -2
- package/extensions/dm-subagents/agent-manager-edit.ts +75 -23
- package/extensions/dm-subagents/agent-manager-list.ts +9 -2
- package/extensions/dm-subagents/agent-manager.ts +199 -11
- package/extensions/dm-subagents/agents.ts +315 -20
- package/extensions/dm-ultrathink/README.md +5 -0
- package/extensions/dm-ultrathink/src/naming.ts +75 -3
- package/package.json +1 -1
|
@@ -15,6 +15,31 @@ export type AgentScope = "user" | "project" | "both";
|
|
|
15
15
|
|
|
16
16
|
export type AgentSource = "builtin" | "user" | "project";
|
|
17
17
|
|
|
18
|
+
export interface BuiltinAgentOverrideBase {
|
|
19
|
+
model?: string;
|
|
20
|
+
fallbackModels?: string[];
|
|
21
|
+
thinking?: string;
|
|
22
|
+
systemPrompt: string;
|
|
23
|
+
skills?: string[];
|
|
24
|
+
tools?: string[];
|
|
25
|
+
mcpDirectTools?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface BuiltinAgentOverrideConfig {
|
|
29
|
+
model?: string | false;
|
|
30
|
+
fallbackModels?: string[] | false;
|
|
31
|
+
thinking?: string | false;
|
|
32
|
+
systemPrompt?: string;
|
|
33
|
+
skills?: string[] | false;
|
|
34
|
+
tools?: string[] | false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BuiltinAgentOverrideInfo {
|
|
38
|
+
scope: "user" | "project";
|
|
39
|
+
path: string;
|
|
40
|
+
base: BuiltinAgentOverrideBase;
|
|
41
|
+
}
|
|
42
|
+
|
|
18
43
|
export interface AgentConfig {
|
|
19
44
|
name: string;
|
|
20
45
|
description: string;
|
|
@@ -28,13 +53,13 @@ export interface AgentConfig {
|
|
|
28
53
|
filePath: string;
|
|
29
54
|
skills?: string[];
|
|
30
55
|
extensions?: string[];
|
|
31
|
-
// Chain behavior fields
|
|
32
56
|
output?: string;
|
|
33
57
|
defaultReads?: string[];
|
|
34
58
|
defaultProgress?: boolean;
|
|
35
59
|
interactive?: boolean;
|
|
36
60
|
maxSubagentDepth?: number;
|
|
37
61
|
extraFields?: Record<string, string>;
|
|
62
|
+
override?: BuiltinAgentOverrideInfo;
|
|
38
63
|
}
|
|
39
64
|
|
|
40
65
|
export interface ChainStepConfig {
|
|
@@ -61,6 +86,266 @@ export interface AgentDiscoveryResult {
|
|
|
61
86
|
projectAgentsDir: string | null;
|
|
62
87
|
}
|
|
63
88
|
|
|
89
|
+
function splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {
|
|
90
|
+
const mcpDirectTools: string[] = [];
|
|
91
|
+
const tools: string[] = [];
|
|
92
|
+
for (const tool of rawTools ?? []) {
|
|
93
|
+
if (tool.startsWith("mcp:")) {
|
|
94
|
+
mcpDirectTools.push(tool.slice(4));
|
|
95
|
+
} else {
|
|
96
|
+
tools.push(tool);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
...(tools.length > 0 ? { tools } : {}),
|
|
101
|
+
...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function joinToolList(config: Pick<AgentConfig, "tools" | "mcpDirectTools">): string[] | undefined {
|
|
106
|
+
const joined = [
|
|
107
|
+
...(config.tools ?? []),
|
|
108
|
+
...(config.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
|
|
109
|
+
];
|
|
110
|
+
return joined.length > 0 ? joined : undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {
|
|
114
|
+
if (!a && !b) return true;
|
|
115
|
+
if (!a || !b) return false;
|
|
116
|
+
if (a.length !== b.length) return false;
|
|
117
|
+
for (let i = 0; i < a.length; i++) {
|
|
118
|
+
if (a[i] !== b[i]) return false;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
124
|
+
return {
|
|
125
|
+
model: agent.model,
|
|
126
|
+
fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,
|
|
127
|
+
thinking: agent.thinking,
|
|
128
|
+
systemPrompt: agent.systemPrompt,
|
|
129
|
+
skills: agent.skills ? [...agent.skills] : undefined,
|
|
130
|
+
tools: agent.tools ? [...agent.tools] : undefined,
|
|
131
|
+
mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
|
|
136
|
+
return {
|
|
137
|
+
...(override.model !== undefined ? { model: override.model } : {}),
|
|
138
|
+
...(override.fallbackModels !== undefined
|
|
139
|
+
? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
|
|
140
|
+
: {}),
|
|
141
|
+
...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
|
|
142
|
+
...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
|
|
143
|
+
...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
|
|
144
|
+
...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function findNearestProjectRoot(cwd: string): string | null {
|
|
149
|
+
let currentDir = cwd;
|
|
150
|
+
while (true) {
|
|
151
|
+
if (isDirectory(path.join(currentDir, ".dm")) || isDirectory(path.join(currentDir, ".agents"))) {
|
|
152
|
+
return currentDir;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const parentDir = path.dirname(currentDir);
|
|
156
|
+
if (parentDir === currentDir) return null;
|
|
157
|
+
currentDir = parentDir;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getUserAgentSettingsPath(): string {
|
|
162
|
+
return path.join(os.homedir(), ".dm", "agent", "settings.json");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function getProjectAgentSettingsPath(cwd: string): string | null {
|
|
166
|
+
const projectRoot = findNearestProjectRoot(cwd);
|
|
167
|
+
return projectRoot ? path.join(projectRoot, ".dm", "settings.json") : null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function readSettingsFileStrict(filePath: string): Record<string, unknown> {
|
|
171
|
+
if (!fs.existsSync(filePath)) return {};
|
|
172
|
+
let parsed: unknown;
|
|
173
|
+
try {
|
|
174
|
+
parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
175
|
+
} catch (error) {
|
|
176
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
177
|
+
throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
|
|
178
|
+
}
|
|
179
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
180
|
+
throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
|
|
181
|
+
}
|
|
182
|
+
return parsed as Record<string, unknown>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {
|
|
186
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
187
|
+
fs.writeFileSync(filePath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseStringArrayOrFalse(value: unknown): string[] | false | undefined {
|
|
191
|
+
if (value === false) return false;
|
|
192
|
+
if (!Array.isArray(value)) return undefined;
|
|
193
|
+
const items = value.filter((item): item is string => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
194
|
+
return items;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function parseBuiltinOverrideEntry(value: unknown): BuiltinAgentOverrideConfig | undefined {
|
|
198
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
199
|
+
const input = value as Record<string, unknown>;
|
|
200
|
+
const override: BuiltinAgentOverrideConfig = {};
|
|
201
|
+
|
|
202
|
+
if (typeof input.model === "string" || input.model === false) override.model = input.model;
|
|
203
|
+
if (typeof input.thinking === "string" || input.thinking === false) override.thinking = input.thinking;
|
|
204
|
+
if (typeof input.systemPrompt === "string") override.systemPrompt = input.systemPrompt;
|
|
205
|
+
|
|
206
|
+
const fallbackModels = parseStringArrayOrFalse(input.fallbackModels);
|
|
207
|
+
if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;
|
|
208
|
+
|
|
209
|
+
const skills = parseStringArrayOrFalse(input.skills);
|
|
210
|
+
if (skills !== undefined) override.skills = skills;
|
|
211
|
+
|
|
212
|
+
const tools = parseStringArrayOrFalse(input.tools);
|
|
213
|
+
if (tools !== undefined) override.tools = tools;
|
|
214
|
+
|
|
215
|
+
return Object.keys(override).length > 0 ? override : undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function readBuiltinOverrides(filePath: string | null): Record<string, BuiltinAgentOverrideConfig> {
|
|
219
|
+
if (!filePath || !fs.existsSync(filePath)) return {};
|
|
220
|
+
const settings = readSettingsFileStrict(filePath);
|
|
221
|
+
const subagents = settings.subagents;
|
|
222
|
+
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return {};
|
|
223
|
+
const agentOverrides = (subagents as Record<string, unknown>).agentOverrides;
|
|
224
|
+
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return {};
|
|
225
|
+
|
|
226
|
+
const parsed: Record<string, BuiltinAgentOverrideConfig> = {};
|
|
227
|
+
for (const [name, value] of Object.entries(agentOverrides)) {
|
|
228
|
+
const override = parseBuiltinOverrideEntry(value);
|
|
229
|
+
if (override) parsed[name] = override;
|
|
230
|
+
}
|
|
231
|
+
return parsed;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function applyBuiltinOverride(
|
|
235
|
+
agent: AgentConfig,
|
|
236
|
+
override: BuiltinAgentOverrideConfig,
|
|
237
|
+
meta: { scope: "user" | "project"; path: string },
|
|
238
|
+
): AgentConfig {
|
|
239
|
+
const next: AgentConfig = {
|
|
240
|
+
...agent,
|
|
241
|
+
override: { ...meta, base: cloneOverrideBase(agent) },
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;
|
|
245
|
+
if (override.fallbackModels !== undefined) {
|
|
246
|
+
next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];
|
|
247
|
+
}
|
|
248
|
+
if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;
|
|
249
|
+
if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;
|
|
250
|
+
if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];
|
|
251
|
+
if (override.tools !== undefined) {
|
|
252
|
+
const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);
|
|
253
|
+
next.tools = tools;
|
|
254
|
+
next.mcpDirectTools = mcpDirectTools;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return next;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function applyBuiltinOverrides(
|
|
261
|
+
builtinAgents: AgentConfig[],
|
|
262
|
+
userOverrides: Record<string, BuiltinAgentOverrideConfig>,
|
|
263
|
+
projectOverrides: Record<string, BuiltinAgentOverrideConfig>,
|
|
264
|
+
userSettingsPath: string,
|
|
265
|
+
projectSettingsPath: string | null,
|
|
266
|
+
): AgentConfig[] {
|
|
267
|
+
return builtinAgents.map((agent) => {
|
|
268
|
+
const projectOverride = projectOverrides[agent.name];
|
|
269
|
+
if (projectOverride && projectSettingsPath) {
|
|
270
|
+
return applyBuiltinOverride(agent, projectOverride, { scope: "project", path: projectSettingsPath });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const userOverride = userOverrides[agent.name];
|
|
274
|
+
if (userOverride) {
|
|
275
|
+
return applyBuiltinOverride(agent, userOverride, { scope: "user", path: userSettingsPath });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return agent;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function buildBuiltinOverrideConfig(
|
|
283
|
+
base: BuiltinAgentOverrideBase,
|
|
284
|
+
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools">,
|
|
285
|
+
): BuiltinAgentOverrideConfig | undefined {
|
|
286
|
+
const override: BuiltinAgentOverrideConfig = {};
|
|
287
|
+
|
|
288
|
+
if (draft.model !== base.model) override.model = draft.model ?? false;
|
|
289
|
+
if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
|
|
290
|
+
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
291
|
+
if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;
|
|
292
|
+
if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;
|
|
293
|
+
|
|
294
|
+
const baseTools = joinToolList(base);
|
|
295
|
+
const draftTools = joinToolList(draft);
|
|
296
|
+
if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;
|
|
297
|
+
|
|
298
|
+
return Object.keys(override).length > 0 ? override : undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function saveBuiltinAgentOverride(
|
|
302
|
+
cwd: string,
|
|
303
|
+
name: string,
|
|
304
|
+
scope: "user" | "project",
|
|
305
|
+
override: BuiltinAgentOverrideConfig,
|
|
306
|
+
): string {
|
|
307
|
+
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
308
|
+
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
309
|
+
|
|
310
|
+
const settings = readSettingsFileStrict(filePath);
|
|
311
|
+
const subagents = settings.subagents && typeof settings.subagents === "object" && !Array.isArray(settings.subagents)
|
|
312
|
+
? { ...(settings.subagents as Record<string, unknown>) }
|
|
313
|
+
: {};
|
|
314
|
+
const agentOverrides = subagents.agentOverrides && typeof subagents.agentOverrides === "object" && !Array.isArray(subagents.agentOverrides)
|
|
315
|
+
? { ...(subagents.agentOverrides as Record<string, unknown>) }
|
|
316
|
+
: {};
|
|
317
|
+
|
|
318
|
+
agentOverrides[name] = cloneOverrideValue(override);
|
|
319
|
+
subagents.agentOverrides = agentOverrides;
|
|
320
|
+
settings.subagents = subagents;
|
|
321
|
+
writeSettingsFile(filePath, settings);
|
|
322
|
+
return filePath;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function removeBuiltinAgentOverride(cwd: string, name: string, scope: "user" | "project"): string {
|
|
326
|
+
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
327
|
+
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
328
|
+
if (!fs.existsSync(filePath)) return filePath;
|
|
329
|
+
|
|
330
|
+
const settings = readSettingsFileStrict(filePath);
|
|
331
|
+
const subagents = settings.subagents;
|
|
332
|
+
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return filePath;
|
|
333
|
+
const nextSubagents = { ...(subagents as Record<string, unknown>) };
|
|
334
|
+
const agentOverrides = nextSubagents.agentOverrides;
|
|
335
|
+
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return filePath;
|
|
336
|
+
|
|
337
|
+
const nextOverrides = { ...(agentOverrides as Record<string, unknown>) };
|
|
338
|
+
delete nextOverrides[name];
|
|
339
|
+
if (Object.keys(nextOverrides).length > 0) nextSubagents.agentOverrides = nextOverrides;
|
|
340
|
+
else delete nextSubagents.agentOverrides;
|
|
341
|
+
|
|
342
|
+
if (Object.keys(nextSubagents).length > 0) settings.subagents = nextSubagents;
|
|
343
|
+
else delete settings.subagents;
|
|
344
|
+
|
|
345
|
+
writeSettingsFile(filePath, settings);
|
|
346
|
+
return filePath;
|
|
347
|
+
}
|
|
348
|
+
|
|
64
349
|
function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
65
350
|
const agents: AgentConfig[] = [];
|
|
66
351
|
|
|
@@ -111,7 +396,6 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
111
396
|
}
|
|
112
397
|
}
|
|
113
398
|
|
|
114
|
-
// Parse defaultReads as comma-separated list (like tools)
|
|
115
399
|
const defaultReads = frontmatter.defaultReads
|
|
116
400
|
?.split(",")
|
|
117
401
|
.map((f) => f.trim())
|
|
@@ -155,7 +439,6 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
155
439
|
filePath,
|
|
156
440
|
skills: skills && skills.length > 0 ? skills : undefined,
|
|
157
441
|
extensions,
|
|
158
|
-
// Chain behavior fields
|
|
159
442
|
output: frontmatter.output,
|
|
160
443
|
defaultReads: defaultReads && defaultReads.length > 0 ? defaultReads : undefined,
|
|
161
444
|
defaultProgress: frontmatter.defaultProgress === "true",
|
|
@@ -216,18 +499,12 @@ function isDirectory(p: string): boolean {
|
|
|
216
499
|
}
|
|
217
500
|
|
|
218
501
|
function findNearestProjectAgentsDir(cwd: string): string | null {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (isDirectory(candidate)) return candidate;
|
|
226
|
-
|
|
227
|
-
const parentDir = path.dirname(currentDir);
|
|
228
|
-
if (parentDir === currentDir) return null;
|
|
229
|
-
currentDir = parentDir;
|
|
230
|
-
}
|
|
502
|
+
const projectRoot = findNearestProjectRoot(cwd);
|
|
503
|
+
if (!projectRoot) return null;
|
|
504
|
+
const candidateAlt = path.join(projectRoot, ".agents");
|
|
505
|
+
if (isDirectory(candidateAlt)) return candidateAlt;
|
|
506
|
+
const candidate = path.join(projectRoot, ".dm", "agents");
|
|
507
|
+
return isDirectory(candidate) ? candidate : null;
|
|
231
508
|
}
|
|
232
509
|
|
|
233
510
|
const BUILTIN_AGENTS_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "agents");
|
|
@@ -236,8 +513,16 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
236
513
|
const userDirOld = path.join(os.homedir(), ".dm", "agent", "agents");
|
|
237
514
|
const userDirNew = path.join(os.homedir(), ".agents");
|
|
238
515
|
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
|
|
239
|
-
|
|
240
|
-
const
|
|
516
|
+
const userSettingsPath = getUserAgentSettingsPath();
|
|
517
|
+
const projectSettingsPath = getProjectAgentSettingsPath(cwd);
|
|
518
|
+
|
|
519
|
+
const builtinAgents = applyBuiltinOverrides(
|
|
520
|
+
loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"),
|
|
521
|
+
scope === "project" ? {} : readBuiltinOverrides(userSettingsPath),
|
|
522
|
+
scope === "user" ? {} : readBuiltinOverrides(projectSettingsPath),
|
|
523
|
+
userSettingsPath,
|
|
524
|
+
projectSettingsPath,
|
|
525
|
+
);
|
|
241
526
|
|
|
242
527
|
const userAgentsOld = scope === "project" ? [] : loadAgentsFromDir(userDirOld, "user");
|
|
243
528
|
const userAgentsNew = scope === "project" ? [] : loadAgentsFromDir(userDirNew, "user");
|
|
@@ -256,12 +541,22 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
256
541
|
chains: ChainConfig[];
|
|
257
542
|
userDir: string;
|
|
258
543
|
projectDir: string | null;
|
|
544
|
+
userSettingsPath: string;
|
|
545
|
+
projectSettingsPath: string | null;
|
|
259
546
|
} {
|
|
260
547
|
const userDirOld = path.join(os.homedir(), ".dm", "agent", "agents");
|
|
261
548
|
const userDirNew = path.join(os.homedir(), ".agents");
|
|
262
549
|
const projectDir = findNearestProjectAgentsDir(cwd);
|
|
263
|
-
|
|
264
|
-
const
|
|
550
|
+
const userSettingsPath = getUserAgentSettingsPath();
|
|
551
|
+
const projectSettingsPath = getProjectAgentSettingsPath(cwd);
|
|
552
|
+
|
|
553
|
+
const builtin = applyBuiltinOverrides(
|
|
554
|
+
loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"),
|
|
555
|
+
readBuiltinOverrides(userSettingsPath),
|
|
556
|
+
readBuiltinOverrides(projectSettingsPath),
|
|
557
|
+
userSettingsPath,
|
|
558
|
+
projectSettingsPath,
|
|
559
|
+
);
|
|
265
560
|
const user = [
|
|
266
561
|
...loadAgentsFromDir(userDirOld, "user"),
|
|
267
562
|
...loadAgentsFromDir(userDirNew, "user"),
|
|
@@ -275,5 +570,5 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
275
570
|
|
|
276
571
|
const userDir = fs.existsSync(userDirNew) ? userDirNew : userDirOld;
|
|
277
572
|
|
|
278
|
-
return { builtin, user, project, chains, userDir, projectDir };
|
|
573
|
+
return { builtin, user, project, chains, userDir, projectDir, userSettingsPath, projectSettingsPath };
|
|
279
574
|
}
|
|
@@ -13,6 +13,11 @@ Bundled DM extension for multi-pass review loops and oracle-assisted review sess
|
|
|
13
13
|
- Global config path: `~/.dm/ultrathink.json`
|
|
14
14
|
- Config env override: `DM_ULTRATHINK_CONFIG_PATH`
|
|
15
15
|
|
|
16
|
+
## Naming-model catalogs
|
|
17
|
+
|
|
18
|
+
- DuckMind/OpenRouter sessions show `duckmind/free`, `duckmind/auto`, `duckmind/lite`, `duckmind/smart`, `duckmind/deep`
|
|
19
|
+
- OpenAI Codex / dm-multicodex sessions show `openai-codex/gpt-5.1`, `openai-codex/gpt-5.1-codex-max`, `openai-codex/gpt-5.1-codex-mini`, `openai-codex/gpt-5.2`, `openai-codex/gpt-5.2-codex`
|
|
20
|
+
|
|
16
21
|
## Bundled with dm
|
|
17
22
|
|
|
18
23
|
No separate install step is required. The extension is loaded from DM's bundled extension catalog.
|
|
@@ -61,6 +61,7 @@ type DuckMindPreset = {
|
|
|
61
61
|
|
|
62
62
|
const DUCKMIND_PROVIDER = "duckmind";
|
|
63
63
|
const OPENROUTER_PROVIDER = "openrouter";
|
|
64
|
+
const OPENAI_CODEX_PROVIDER = "openai-codex";
|
|
64
65
|
const DUCKMIND_PRESETS: readonly DuckMindPreset[] = [
|
|
65
66
|
{
|
|
66
67
|
alias: "free",
|
|
@@ -94,6 +95,34 @@ const DUCKMIND_PRESETS: readonly DuckMindPreset[] = [
|
|
|
94
95
|
},
|
|
95
96
|
];
|
|
96
97
|
|
|
98
|
+
const OPENAI_CODEX_NAMING_MODELS = [
|
|
99
|
+
{
|
|
100
|
+
modelId: "gpt-5.1",
|
|
101
|
+
fallbackModelId: "gpt-5.1-codex-mini",
|
|
102
|
+
displayName: "GPT-5.1",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
modelId: "gpt-5.1-codex-max",
|
|
106
|
+
fallbackModelId: "gpt-5.1-codex-mini",
|
|
107
|
+
displayName: "GPT-5.1 Codex Max",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
modelId: "gpt-5.1-codex-mini",
|
|
111
|
+
fallbackModelId: "gpt-5.1-codex-mini",
|
|
112
|
+
displayName: "GPT-5.1 Codex Mini",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
modelId: "gpt-5.2",
|
|
116
|
+
fallbackModelId: "gpt-5.1-codex-mini",
|
|
117
|
+
displayName: "GPT-5.2",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
modelId: "gpt-5.2-codex",
|
|
121
|
+
fallbackModelId: "gpt-5.1-codex-mini",
|
|
122
|
+
displayName: "GPT-5.2 Codex",
|
|
123
|
+
},
|
|
124
|
+
] as const;
|
|
125
|
+
|
|
97
126
|
let testOverrides: NamingTestOverrides | undefined;
|
|
98
127
|
|
|
99
128
|
export function setNamingTestOverrides(overrides?: NamingTestOverrides): void {
|
|
@@ -208,6 +237,25 @@ function buildDuckMindNamingChoices(availableModels: Model<any>[]): NamingModelC
|
|
|
208
237
|
});
|
|
209
238
|
}
|
|
210
239
|
|
|
240
|
+
function buildOpenAICodexNamingChoices(availableModels: Model<any>[]): NamingModelChoice[] {
|
|
241
|
+
const codexModels = availableModels.filter((model) => model.provider === OPENAI_CODEX_PROVIDER);
|
|
242
|
+
return OPENAI_CODEX_NAMING_MODELS.flatMap((entry) => {
|
|
243
|
+
const model = pickOpenRouterModel(codexModels, entry.modelId, entry.fallbackModelId, entry.displayName);
|
|
244
|
+
if (!model) {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
return [
|
|
248
|
+
{
|
|
249
|
+
label: `${OPENAI_CODEX_PROVIDER}/${entry.modelId}`,
|
|
250
|
+
config: {
|
|
251
|
+
provider: OPENAI_CODEX_PROVIDER,
|
|
252
|
+
modelId: entry.modelId,
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
];
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
211
259
|
function resolveDuckMindAliasModel(availableModels: Model<any>[], config: NamingModelConfig): Model<any> | undefined {
|
|
212
260
|
const normalizedProvider = config.provider.trim().toLowerCase();
|
|
213
261
|
const normalizedModelId = config.modelId.trim().toLowerCase();
|
|
@@ -231,9 +279,31 @@ function resolveDuckMindAliasModel(availableModels: Model<any>[], config: Naming
|
|
|
231
279
|
);
|
|
232
280
|
}
|
|
233
281
|
|
|
282
|
+
function resolveOpenAICodexAliasModel(availableModels: Model<any>[], config: NamingModelConfig): Model<any> | undefined {
|
|
283
|
+
const normalizedProvider = config.provider.trim().toLowerCase();
|
|
284
|
+
const normalizedModelId = config.modelId.trim().toLowerCase();
|
|
285
|
+
if (normalizedProvider !== OPENAI_CODEX_PROVIDER) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const preset = OPENAI_CODEX_NAMING_MODELS.find((entry) => normalizedModelId === entry.modelId.toLowerCase());
|
|
290
|
+
if (!preset) {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return pickOpenRouterModel(
|
|
295
|
+
availableModels.filter((model) => model.provider === OPENAI_CODEX_PROVIDER),
|
|
296
|
+
preset.modelId,
|
|
297
|
+
preset.fallbackModelId,
|
|
298
|
+
preset.displayName,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
234
302
|
async function resolveNamingModel(ctx: ExtensionContext, config: NamingModelConfig): Promise<{ model: Model<any>; apiKey: string }> {
|
|
303
|
+
const availableModels = ctx.modelRegistry.getAvailable();
|
|
235
304
|
const model =
|
|
236
|
-
resolveDuckMindAliasModel(
|
|
305
|
+
resolveDuckMindAliasModel(availableModels, config) ??
|
|
306
|
+
resolveOpenAICodexAliasModel(availableModels, config) ??
|
|
237
307
|
ctx.modelRegistry.find(config.provider, config.modelId);
|
|
238
308
|
if (!model) {
|
|
239
309
|
throw new Error(`Ultrathink naming model ${config.provider}/${config.modelId} is not available in DM.`);
|
|
@@ -320,10 +390,12 @@ export async function ensureNamingModel(
|
|
|
320
390
|
const choices =
|
|
321
391
|
ctx.model?.provider === OPENROUTER_PROVIDER || ctx.model?.provider === DUCKMIND_PROVIDER
|
|
322
392
|
? buildDuckMindNamingChoices(availableModels)
|
|
323
|
-
:
|
|
393
|
+
: ctx.model?.provider === OPENAI_CODEX_PROVIDER
|
|
394
|
+
? buildOpenAICodexNamingChoices(availableModels)
|
|
395
|
+
: availableModels.map(buildDefaultNamingChoice);
|
|
324
396
|
|
|
325
397
|
if (choices.length === 0) {
|
|
326
|
-
throw new Error("Ultrathink could not find any available
|
|
398
|
+
throw new Error("Ultrathink could not find any available naming models to use for branch and commit naming.");
|
|
327
399
|
}
|
|
328
400
|
|
|
329
401
|
const selection = await ctx.ui.select(
|