openfox 2.0.59 → 2.0.61
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/dist/agent-defaults/builder.agent.md +1 -0
- package/dist/agent-defaults/planner.agent.md +1 -0
- package/dist/{chat-handler-AS44EMQE.js → chat-handler-6DRGMDXZ.js} +4 -4
- package/dist/{chunk-5MDIKIMG.js → chunk-AA4G4DGK.js} +3 -3
- package/dist/{chunk-FRU2X3IZ.js → chunk-KX4C5Q5J.js} +408 -69
- package/dist/{chunk-LQFCQ6PY.js → chunk-LHTVTDQQ.js} +519 -253
- package/dist/{chunk-P7C6KF2H.js → chunk-S5DWUGKZ.js} +2 -2
- package/dist/{chunk-CDRQJDSJ.js → chunk-SVSKQYFH.js} +7 -2
- package/dist/{chunk-EKI5SZC7.js → chunk-ZHSCXD75.js} +3 -3
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{orchestrator-MH7J2YVP.js → orchestrator-AHCSLBB3.js} +4 -4
- package/dist/package.json +4 -2
- package/dist/{processor-34MVJZTD.js → processor-45VWLZIV.js} +4 -4
- package/dist/{serve-NHF6B5RQ.js → serve-V44O2QE6.js} +6 -6
- package/dist/server/index.js +5 -5
- package/dist/{server-KDGQZNYQ.js → server-JDB2QVKP.js} +5 -5
- package/dist/{settings-X43DCM55.js → settings-ASLTSUE2.js} +2 -2
- package/dist/skill-defaults/{browser.skill.md → browser/SKILL.md} +5 -3
- package/dist/{tools-FQ54IAYG.js → tools-7QUMO5DJ.js} +3 -3
- package/dist/web/assets/{index-DjfRXOO7.css → index-BGkyTfuQ.css} +1 -1
- package/dist/web/assets/{index-Dqqp_j-f.js → index-CETZPQXr.js} +73 -73
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +4 -2
|
@@ -32,8 +32,9 @@ import {
|
|
|
32
32
|
import {
|
|
33
33
|
SETTINGS_KEYS,
|
|
34
34
|
deleteSetting,
|
|
35
|
-
getSetting
|
|
36
|
-
|
|
35
|
+
getSetting,
|
|
36
|
+
setSetting
|
|
37
|
+
} from "./chunk-SVSKQYFH.js";
|
|
37
38
|
import {
|
|
38
39
|
getProject
|
|
39
40
|
} from "./chunk-YQ3SOPBI.js";
|
|
@@ -488,6 +489,13 @@ function cancelPathConfirmationsForSession(sessionId, reason) {
|
|
|
488
489
|
}
|
|
489
490
|
|
|
490
491
|
// src/server/tools/tool-helpers.ts
|
|
492
|
+
function buildSignal(timeoutMs, contextSignal) {
|
|
493
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
494
|
+
if (contextSignal) {
|
|
495
|
+
return AbortSignal.any([timeoutSignal, contextSignal]);
|
|
496
|
+
}
|
|
497
|
+
return timeoutSignal;
|
|
498
|
+
}
|
|
491
499
|
function validateAction(action, allowed, startTime) {
|
|
492
500
|
if (!action || !allowed.includes(action)) {
|
|
493
501
|
return {
|
|
@@ -1669,7 +1677,7 @@ ${Object.entries(schema.fields).map(([f, t]) => ` ${f}: ${t}`).join("\n")}`
|
|
|
1669
1677
|
);
|
|
1670
1678
|
|
|
1671
1679
|
// src/server/sub-agents/manager.ts
|
|
1672
|
-
import { readFile as
|
|
1680
|
+
import { readFile as readFile8, access as access4 } from "fs/promises";
|
|
1673
1681
|
import { join as join7, dirname as dirname5, isAbsolute as isAbsolute3 } from "path";
|
|
1674
1682
|
|
|
1675
1683
|
// src/server/agents/registry.ts
|
|
@@ -2034,62 +2042,173 @@ async function fileExists(path) {
|
|
|
2034
2042
|
}
|
|
2035
2043
|
|
|
2036
2044
|
// src/server/skills/registry.ts
|
|
2037
|
-
import { writeFile as writeFile5, mkdir as mkdir4, unlink as unlink3 } from "fs/promises";
|
|
2045
|
+
import { writeFile as writeFile5, mkdir as mkdir4, unlink as unlink3, readdir as readdir4, readFile as readFile7, realpath as realpath2, rm } from "fs/promises";
|
|
2038
2046
|
import { join as join6, dirname as dirname4 } from "path";
|
|
2039
2047
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2048
|
+
import { homedir as homedir2 } from "os";
|
|
2040
2049
|
import matter3 from "gray-matter";
|
|
2041
2050
|
var __bundleDir2 = dirname4(fileURLToPath2(import.meta.url));
|
|
2042
2051
|
var DEFAULTS_DIR2 = join6(__bundleDir2, "defaults");
|
|
2043
2052
|
var DEFAULTS_DIR_ALT2 = join6(__bundleDir2, "skill-defaults");
|
|
2044
2053
|
var SKILL_EXTENSION = ".skill.md";
|
|
2045
2054
|
var SKILL_SETTING_PREFIX = "skill.enabled.";
|
|
2055
|
+
var PORTABLE_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2046
2056
|
function getSkillsDir(configDir) {
|
|
2047
2057
|
return join6(configDir, "skills");
|
|
2048
2058
|
}
|
|
2049
2059
|
function getProjectSkillsDir(projectDir) {
|
|
2050
2060
|
return join6(projectDir, ".openfox", "skills");
|
|
2051
2061
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
+
function portableDisplayName(data, id) {
|
|
2063
|
+
const metadata = data["metadata"];
|
|
2064
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return id;
|
|
2065
|
+
const openfox = metadata["openfox"];
|
|
2066
|
+
if (!openfox || typeof openfox !== "object" || Array.isArray(openfox)) return id;
|
|
2067
|
+
const displayName = openfox["displayName"];
|
|
2068
|
+
return typeof displayName === "string" && displayName.trim() ? displayName : id;
|
|
2069
|
+
}
|
|
2070
|
+
function portableVersion(data) {
|
|
2071
|
+
const metadata = data["metadata"];
|
|
2072
|
+
if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
|
|
2073
|
+
const version = metadata["version"];
|
|
2074
|
+
if (version !== void 0) return String(version);
|
|
2075
|
+
}
|
|
2076
|
+
return data["version"] === void 0 ? "" : String(data["version"]);
|
|
2077
|
+
}
|
|
2078
|
+
async function loadPortableSkills(dir, source) {
|
|
2079
|
+
let entries;
|
|
2080
|
+
try {
|
|
2081
|
+
entries = await readdir4(dir, { withFileTypes: true });
|
|
2082
|
+
} catch {
|
|
2083
|
+
return [];
|
|
2062
2084
|
}
|
|
2063
|
-
|
|
2085
|
+
const skills = [];
|
|
2086
|
+
for (const entry of entries) {
|
|
2087
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
2088
|
+
const packageDir = join6(dir, entry.name);
|
|
2089
|
+
const entrypoint = join6(packageDir, "SKILL.md");
|
|
2090
|
+
try {
|
|
2091
|
+
const content = await readFile7(entrypoint, "utf-8");
|
|
2092
|
+
const parsed = matter3(content);
|
|
2093
|
+
const data = parsed.data;
|
|
2094
|
+
const id = typeof data["name"] === "string" ? data["name"].trim() : "";
|
|
2095
|
+
const description = typeof data["description"] === "string" ? data["description"].trim() : "";
|
|
2096
|
+
const prompt = parsed.content.trim();
|
|
2097
|
+
if (!id || !description || !prompt) continue;
|
|
2098
|
+
const resolvedDirectory = await realpath2(packageDir);
|
|
2099
|
+
const warnings = [];
|
|
2100
|
+
if (id.length > 64 || !PORTABLE_NAME_REGEX.test(id)) {
|
|
2101
|
+
warnings.push("Skill name must use 1-64 lowercase letters, numbers, and single hyphens");
|
|
2102
|
+
}
|
|
2103
|
+
if (id !== entry.name) {
|
|
2104
|
+
warnings.push(`Skill name "${id}" does not match package directory "${entry.name}"`);
|
|
2105
|
+
}
|
|
2106
|
+
skills.push({
|
|
2107
|
+
metadata: {
|
|
2108
|
+
id,
|
|
2109
|
+
name: portableDisplayName(data, id),
|
|
2110
|
+
description,
|
|
2111
|
+
version: portableVersion(data)
|
|
2112
|
+
},
|
|
2113
|
+
prompt,
|
|
2114
|
+
rawMetadata: data,
|
|
2115
|
+
entrypoint,
|
|
2116
|
+
directory: resolvedDirectory,
|
|
2117
|
+
source,
|
|
2118
|
+
legacy: false,
|
|
2119
|
+
warnings
|
|
2120
|
+
});
|
|
2121
|
+
} catch {
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
return skills;
|
|
2125
|
+
}
|
|
2126
|
+
async function annotateLegacySkills(skills, dir, source) {
|
|
2127
|
+
return Promise.all(
|
|
2128
|
+
skills.map(async (skill) => {
|
|
2129
|
+
const entrypoint = join6(dir, `${skill.metadata.id}${SKILL_EXTENSION}`);
|
|
2130
|
+
let resolvedEntrypoint = entrypoint;
|
|
2131
|
+
try {
|
|
2132
|
+
resolvedEntrypoint = await realpath2(entrypoint);
|
|
2133
|
+
} catch {
|
|
2134
|
+
}
|
|
2135
|
+
return {
|
|
2136
|
+
...skill,
|
|
2137
|
+
rawMetadata: { ...skill.metadata },
|
|
2138
|
+
entrypoint: resolvedEntrypoint,
|
|
2139
|
+
directory: dirname4(resolvedEntrypoint),
|
|
2140
|
+
source,
|
|
2141
|
+
legacy: true,
|
|
2142
|
+
warnings: ["Legacy .skill.md format"]
|
|
2143
|
+
};
|
|
2144
|
+
})
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
async function loadSkillsDirectory(dir, source) {
|
|
2148
|
+
const [legacyRaw, portable] = await Promise.all([
|
|
2149
|
+
loadItemsFromDir(dir, { extension: SKILL_EXTENSION, logName: "skill" }),
|
|
2150
|
+
loadPortableSkills(dir, source)
|
|
2151
|
+
]);
|
|
2152
|
+
const legacy = await annotateLegacySkills(legacyRaw, dir, source);
|
|
2153
|
+
const merged = new Map(legacy.map((skill) => [skill.metadata.id, skill]));
|
|
2154
|
+
for (const skill of portable) merged.set(skill.metadata.id, skill);
|
|
2155
|
+
return [...merged.values()];
|
|
2156
|
+
}
|
|
2157
|
+
function getSelectedSkillDirectories() {
|
|
2158
|
+
const raw = getSetting("skills.directories");
|
|
2159
|
+
if (!raw) return [];
|
|
2160
|
+
try {
|
|
2161
|
+
const parsed = JSON.parse(raw);
|
|
2162
|
+
return Array.isArray(parsed) ? parsed.filter((value) => typeof value === "string") : [];
|
|
2163
|
+
} catch {
|
|
2164
|
+
return [];
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
async function loadDefaultSkills() {
|
|
2168
|
+
const defaults = await loadSkillsDirectory(DEFAULTS_DIR2, "bundled");
|
|
2169
|
+
if (defaults.length) return defaults;
|
|
2170
|
+
return loadSkillsDirectory(DEFAULTS_DIR_ALT2, "bundled");
|
|
2064
2171
|
}
|
|
2065
2172
|
async function loadUserSkills(configDir) {
|
|
2066
|
-
return
|
|
2067
|
-
extension: SKILL_EXTENSION,
|
|
2068
|
-
logName: "skill"
|
|
2069
|
-
});
|
|
2173
|
+
return loadSkillsDirectory(getSkillsDir(configDir), "global-openfox");
|
|
2070
2174
|
}
|
|
2071
2175
|
async function loadProjectSkills(projectDir) {
|
|
2072
|
-
return
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2176
|
+
return loadSkillsDirectory(getProjectSkillsDir(projectDir), "project-openfox");
|
|
2177
|
+
}
|
|
2178
|
+
async function loadAllSkills(configDir, projectDir, options = {}) {
|
|
2179
|
+
return (await loadAllSkillsWithDiagnostics(configDir, projectDir, options)).skills;
|
|
2076
2180
|
}
|
|
2077
|
-
async function
|
|
2078
|
-
const
|
|
2181
|
+
async function loadAllSkillsWithDiagnostics(configDir, projectDir, options = {}) {
|
|
2182
|
+
const home = options.homeDir ?? homedir2();
|
|
2183
|
+
const selected = (options.selectedDirectories ?? getSelectedSkillDirectories()).map(
|
|
2184
|
+
(dir) => dir === "~" ? home : dir.startsWith("~/") ? join6(home, dir.slice(2)) : dir
|
|
2185
|
+
);
|
|
2186
|
+
const locations = [
|
|
2187
|
+
loadDefaultSkills(),
|
|
2188
|
+
loadSkillsDirectory(join6(home, ".agents", "skills"), "global-shared"),
|
|
2189
|
+
loadUserSkills(configDir),
|
|
2190
|
+
...selected.map((dir) => loadSkillsDirectory(dir, "selected")),
|
|
2191
|
+
...projectDir ? [loadSkillsDirectory(join6(projectDir, ".agents", "skills"), "project-shared"), loadProjectSkills(projectDir)] : []
|
|
2192
|
+
];
|
|
2193
|
+
const groups = await Promise.all(locations);
|
|
2079
2194
|
const skillMap = /* @__PURE__ */ new Map();
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2195
|
+
const diagnostics = [];
|
|
2196
|
+
for (const group of groups) {
|
|
2197
|
+
for (const skill of group) {
|
|
2198
|
+
const previous = skillMap.get(skill.metadata.id);
|
|
2199
|
+
if (previous) {
|
|
2200
|
+
if (previous.directory === skill.directory && previous.legacy === skill.legacy) {
|
|
2201
|
+
diagnostics.push(`Skill "${skill.metadata.id}" reached through multiple paths`);
|
|
2202
|
+
} else {
|
|
2203
|
+
diagnostics.push(
|
|
2204
|
+
`Skill "${skill.metadata.id}" from ${skill.source ?? "unknown"} overrides ${previous.source ?? "unknown"}`
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2089
2208
|
skillMap.set(skill.metadata.id, skill);
|
|
2090
2209
|
}
|
|
2091
2210
|
}
|
|
2092
|
-
return Array.from(skillMap.values());
|
|
2211
|
+
return { skills: Array.from(skillMap.values()), diagnostics };
|
|
2093
2212
|
}
|
|
2094
2213
|
async function getEnabledSkills(configDir, projectDir) {
|
|
2095
2214
|
const all = await loadAllSkills(configDir, projectDir);
|
|
@@ -2104,10 +2223,12 @@ function isSkillEnabled(skillId) {
|
|
|
2104
2223
|
if (value === null) return true;
|
|
2105
2224
|
return value === "true";
|
|
2106
2225
|
}
|
|
2226
|
+
function setSkillEnabled(skillId, enabled) {
|
|
2227
|
+
setSetting(`${SKILL_SETTING_PREFIX}${skillId}`, String(enabled));
|
|
2228
|
+
}
|
|
2107
2229
|
async function getDefaultSkillIds() {
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
return getDefaultIds(DEFAULTS_DIR_ALT2, SKILL_EXTENSION);
|
|
2230
|
+
const defaults = await loadDefaultSkills();
|
|
2231
|
+
return defaults.map((s) => s.metadata.id);
|
|
2111
2232
|
}
|
|
2112
2233
|
async function isDefaultSkill(skillId) {
|
|
2113
2234
|
const defaultIds = await getDefaultSkillIds();
|
|
@@ -2118,26 +2239,104 @@ function findSkillById(skillId, skills) {
|
|
|
2118
2239
|
}
|
|
2119
2240
|
async function skillExists(configDir, skillId, projectDir) {
|
|
2120
2241
|
if (await pathExists(join6(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`))) return true;
|
|
2242
|
+
if (await pathExists(join6(getSkillsDir(configDir), skillId, "SKILL.md"))) return true;
|
|
2121
2243
|
if (projectDir && await pathExists(join6(getProjectSkillsDir(projectDir), `${skillId}${SKILL_EXTENSION}`)))
|
|
2122
2244
|
return true;
|
|
2245
|
+
if (projectDir && await pathExists(join6(getProjectSkillsDir(projectDir), skillId, "SKILL.md"))) return true;
|
|
2123
2246
|
return false;
|
|
2124
2247
|
}
|
|
2125
|
-
|
|
2126
|
-
const
|
|
2127
|
-
if (
|
|
2128
|
-
|
|
2248
|
+
function portableFrontmatter(skill) {
|
|
2249
|
+
const metadata = {};
|
|
2250
|
+
if (skill.metadata.version) metadata["version"] = skill.metadata.version;
|
|
2251
|
+
if (skill.metadata.name !== skill.metadata.id) {
|
|
2252
|
+
metadata["openfox"] = { displayName: skill.metadata.name };
|
|
2129
2253
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2254
|
+
return {
|
|
2255
|
+
name: skill.metadata.id,
|
|
2256
|
+
description: skill.metadata.description,
|
|
2257
|
+
...Object.keys(metadata).length ? { metadata } : {}
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
async function savePortableSkill(dir, skill) {
|
|
2261
|
+
const packageDir = join6(dir, skill.metadata.id);
|
|
2262
|
+
await mkdir4(packageDir, { recursive: true });
|
|
2263
|
+
await writeFile5(join6(packageDir, "SKILL.md"), matter3.stringify(skill.prompt, portableFrontmatter(skill)), "utf-8");
|
|
2264
|
+
}
|
|
2265
|
+
async function saveSkill(configDir, skill) {
|
|
2266
|
+
await savePortableSkill(getSkillsDir(configDir), skill);
|
|
2133
2267
|
}
|
|
2134
2268
|
async function saveSkillToProject(projectDir, skill) {
|
|
2135
|
-
await
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2269
|
+
await savePortableSkill(getProjectSkillsDir(projectDir), skill);
|
|
2270
|
+
}
|
|
2271
|
+
function asRecord(value) {
|
|
2272
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2273
|
+
}
|
|
2274
|
+
function updatedPortableFrontmatter(existing, metadata) {
|
|
2275
|
+
const frontmatter = { ...existing.rawMetadata ?? {} };
|
|
2276
|
+
const portableMetadata = { ...asRecord(frontmatter["metadata"]) };
|
|
2277
|
+
const openfox = { ...asRecord(portableMetadata["openfox"]) };
|
|
2278
|
+
frontmatter["name"] = existing.metadata.id;
|
|
2279
|
+
frontmatter["description"] = metadata.description;
|
|
2280
|
+
if (metadata.version) portableMetadata["version"] = metadata.version;
|
|
2281
|
+
else delete portableMetadata["version"];
|
|
2282
|
+
if (metadata.name !== existing.metadata.id) openfox["displayName"] = metadata.name;
|
|
2283
|
+
else delete openfox["displayName"];
|
|
2284
|
+
if (Object.keys(openfox).length) portableMetadata["openfox"] = openfox;
|
|
2285
|
+
else delete portableMetadata["openfox"];
|
|
2286
|
+
if (Object.keys(portableMetadata).length) frontmatter["metadata"] = portableMetadata;
|
|
2287
|
+
else delete frontmatter["metadata"];
|
|
2288
|
+
return frontmatter;
|
|
2289
|
+
}
|
|
2290
|
+
function canModifySkill(existing) {
|
|
2291
|
+
if (existing.source === "global-openfox" || existing.source === "project-openfox") return true;
|
|
2292
|
+
return !existing.legacy && (existing.source === "global-shared" || existing.source === "selected" || existing.source === "project-shared");
|
|
2293
|
+
}
|
|
2294
|
+
async function updateOwnedSkill(existing, changes) {
|
|
2295
|
+
if (!existing.entrypoint || !canModifySkill(existing)) return null;
|
|
2296
|
+
const normalizedMetadata = {
|
|
2297
|
+
...existing.metadata,
|
|
2298
|
+
...changes.metadata ?? {},
|
|
2299
|
+
id: existing.metadata.id
|
|
2300
|
+
};
|
|
2301
|
+
if (!existing.legacy) {
|
|
2302
|
+
const frontmatter = updatedPortableFrontmatter(existing, normalizedMetadata);
|
|
2303
|
+
const updated2 = {
|
|
2304
|
+
...existing,
|
|
2305
|
+
...changes,
|
|
2306
|
+
metadata: normalizedMetadata,
|
|
2307
|
+
rawMetadata: frontmatter,
|
|
2308
|
+
prompt: changes.prompt ?? existing.prompt
|
|
2309
|
+
};
|
|
2310
|
+
await writeFile5(existing.entrypoint, matter3.stringify(updated2.prompt, frontmatter), "utf-8");
|
|
2311
|
+
return updated2;
|
|
2312
|
+
}
|
|
2313
|
+
const metadata = {
|
|
2314
|
+
...existing.rawMetadata ?? existing.metadata,
|
|
2315
|
+
...changes.metadata ?? {},
|
|
2316
|
+
id: existing.metadata.id
|
|
2317
|
+
};
|
|
2318
|
+
const updated = {
|
|
2319
|
+
...existing,
|
|
2320
|
+
...changes,
|
|
2321
|
+
metadata,
|
|
2322
|
+
rawMetadata: metadata,
|
|
2323
|
+
prompt: changes.prompt ?? existing.prompt
|
|
2324
|
+
};
|
|
2325
|
+
await writeFile5(existing.entrypoint, matter3.stringify(updated.prompt, metadata), "utf-8");
|
|
2326
|
+
return updated;
|
|
2327
|
+
}
|
|
2328
|
+
async function deleteOwnedSkill(existing) {
|
|
2329
|
+
if (!existing.entrypoint || !canModifySkill(existing)) {
|
|
2330
|
+
return { success: false, reason: "This skill is read-only" };
|
|
2331
|
+
}
|
|
2332
|
+
try {
|
|
2333
|
+
if (existing.legacy) await unlink3(existing.entrypoint);
|
|
2334
|
+
else await rm(dirname4(existing.entrypoint), { recursive: true });
|
|
2335
|
+
deleteSetting(`${SKILL_SETTING_PREFIX}${existing.metadata.id}`);
|
|
2336
|
+
return { success: true };
|
|
2337
|
+
} catch {
|
|
2338
|
+
return { success: false, reason: "Cannot delete this skill" };
|
|
2339
|
+
}
|
|
2141
2340
|
}
|
|
2142
2341
|
async function deleteSkill(configDir, skillId) {
|
|
2143
2342
|
const isDefault = await isDefaultSkill(skillId);
|
|
@@ -3258,7 +3457,7 @@ async function loadGitIgnoreRules(workdir) {
|
|
|
3258
3457
|
const gitignorePath = join7(currentDir, ".gitignore");
|
|
3259
3458
|
try {
|
|
3260
3459
|
await access4(gitignorePath);
|
|
3261
|
-
const content = await
|
|
3460
|
+
const content = await readFile8(gitignorePath, "utf-8");
|
|
3262
3461
|
const trimmed = content.trim();
|
|
3263
3462
|
if (!trimmed) return "";
|
|
3264
3463
|
const lines = trimmed.split("\n");
|
|
@@ -3459,7 +3658,7 @@ var callSubAgentTool = {
|
|
|
3459
3658
|
};
|
|
3460
3659
|
}
|
|
3461
3660
|
try {
|
|
3462
|
-
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-
|
|
3661
|
+
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7QUMO5DJ.js");
|
|
3463
3662
|
const toolRegistry = getToolRegistryForAgent2(agentDef);
|
|
3464
3663
|
const turnMetrics = new TurnMetrics();
|
|
3465
3664
|
const result = await executeSubAgent({
|
|
@@ -3497,6 +3696,13 @@ var callSubAgentTool = {
|
|
|
3497
3696
|
};
|
|
3498
3697
|
|
|
3499
3698
|
// src/server/tools/load-skill.ts
|
|
3699
|
+
function formatSkillPrompt(skill) {
|
|
3700
|
+
if (skill.legacy !== false || !skill.directory) return skill.prompt;
|
|
3701
|
+
return `Skill package directory: ${skill.directory}
|
|
3702
|
+
Resolve relative paths in these instructions from that directory.
|
|
3703
|
+
|
|
3704
|
+
${skill.prompt}`;
|
|
3705
|
+
}
|
|
3500
3706
|
var handler = async (args, _context, helpers) => {
|
|
3501
3707
|
const { skillId } = args;
|
|
3502
3708
|
if (!skillId) {
|
|
@@ -3513,7 +3719,7 @@ var handler = async (args, _context, helpers) => {
|
|
|
3513
3719
|
const available = allSkills.map((s) => s.metadata.id).join(", ");
|
|
3514
3720
|
return helpers.error(`Skill "${skillId}" not found. Available skills: ${available}`);
|
|
3515
3721
|
}
|
|
3516
|
-
return helpers.success(skill
|
|
3722
|
+
return helpers.success(formatSkillPrompt(skill));
|
|
3517
3723
|
};
|
|
3518
3724
|
var loadSkillTool = createTool(
|
|
3519
3725
|
"load_skill",
|
|
@@ -3591,13 +3797,6 @@ function buildAcceptHeader(format) {
|
|
|
3591
3797
|
return "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
|
|
3592
3798
|
}
|
|
3593
3799
|
}
|
|
3594
|
-
function buildSignal(timeoutMs, contextSignal) {
|
|
3595
|
-
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
3596
|
-
if (contextSignal) {
|
|
3597
|
-
return AbortSignal.any([timeoutSignal, contextSignal]);
|
|
3598
|
-
}
|
|
3599
|
-
return timeoutSignal;
|
|
3600
|
-
}
|
|
3601
3800
|
function convertHTMLToMarkdown(html) {
|
|
3602
3801
|
const turndown = new TurndownService({
|
|
3603
3802
|
headingStyle: "atx",
|
|
@@ -3710,7 +3909,7 @@ var webFetchTool = createTool(
|
|
|
3710
3909
|
|
|
3711
3910
|
// src/server/dev-server/manager.ts
|
|
3712
3911
|
import { spawn as spawn2 } from "child_process";
|
|
3713
|
-
import { readFile as
|
|
3912
|
+
import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
3714
3913
|
import { resolve as resolve5, join as join8 } from "path";
|
|
3715
3914
|
var MAX_LOG_LINES = 2e3;
|
|
3716
3915
|
var MAX_LOG_BYTES = 1e5;
|
|
@@ -3777,7 +3976,7 @@ var DevServerManager = class {
|
|
|
3777
3976
|
async loadConfig(workdir) {
|
|
3778
3977
|
try {
|
|
3779
3978
|
const configPath = getDevServerConfigPath(workdir);
|
|
3780
|
-
const raw = await
|
|
3979
|
+
const raw = await readFile9(configPath, "utf-8");
|
|
3781
3980
|
const parsed = JSON.parse(raw);
|
|
3782
3981
|
if (!parsed.command || !parsed.url) return null;
|
|
3783
3982
|
return {
|
|
@@ -4276,7 +4475,7 @@ function resolveAgentDef2(sessionManager, sessionId) {
|
|
|
4276
4475
|
}
|
|
4277
4476
|
async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
|
|
4278
4477
|
const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
|
|
4279
|
-
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-
|
|
4478
|
+
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7QUMO5DJ.js");
|
|
4280
4479
|
const tools = getToolRegistryForAgent2(agentDef).definitions;
|
|
4281
4480
|
const toolFingerprint = getToolFingerprint(tools);
|
|
4282
4481
|
const allAgents = await loadAllAgentsDefault();
|
|
@@ -4289,7 +4488,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
|
|
|
4289
4488
|
async function computeSessionHash(sessionManager, sessionId) {
|
|
4290
4489
|
const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
|
|
4291
4490
|
const agentDef = await resolveAgentDef2(sessionManager, sessionId);
|
|
4292
|
-
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-
|
|
4491
|
+
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7QUMO5DJ.js");
|
|
4293
4492
|
const tools = getToolRegistryForAgent2(agentDef).definitions;
|
|
4294
4493
|
const toolFingerprint = getToolFingerprint(tools);
|
|
4295
4494
|
return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
|
|
@@ -4400,7 +4599,7 @@ var mcpConfigTool = createTool(
|
|
|
4400
4599
|
await saveGlobalConfig(mcpConfigMode, { ...globalConfig, mcpServers: updated }, mcpConfigPath);
|
|
4401
4600
|
}
|
|
4402
4601
|
async function rebuildTools() {
|
|
4403
|
-
const { setMcpTools: setMcpTools2 } = await import("./tools-
|
|
4602
|
+
const { setMcpTools: setMcpTools2 } = await import("./tools-7QUMO5DJ.js");
|
|
4404
4603
|
const mcpTools = createMcpTools(mcpManagerForTools);
|
|
4405
4604
|
setMcpTools2(mcpTools);
|
|
4406
4605
|
}
|
|
@@ -4502,7 +4701,7 @@ var mcpConfigTool = createTool(
|
|
|
4502
4701
|
);
|
|
4503
4702
|
|
|
4504
4703
|
// src/server/tools/trace-code.ts
|
|
4505
|
-
import { stat as stat2, readFile as
|
|
4704
|
+
import { stat as stat2, readFile as readFile10 } from "fs/promises";
|
|
4506
4705
|
function graphNode(location, depth, relation, symbolName, symbolKind) {
|
|
4507
4706
|
const node = { location, depth, relation, symbolName };
|
|
4508
4707
|
if (symbolKind) node.symbolKind = symbolKind;
|
|
@@ -4534,7 +4733,7 @@ async function getCachedLines(path, approvedPaths) {
|
|
|
4534
4733
|
}
|
|
4535
4734
|
try {
|
|
4536
4735
|
const stats = await stat2(path);
|
|
4537
|
-
const content = await
|
|
4736
|
+
const content = await readFile10(path, "utf-8");
|
|
4538
4737
|
const lines = content.split("\n");
|
|
4539
4738
|
fileCache.set(path, { mtimeMs: stats.mtimeMs, lines });
|
|
4540
4739
|
if (fileCache.size > CACHE_MAX_SIZE) {
|
|
@@ -4765,6 +4964,141 @@ var traceCodeTool = createTool(
|
|
|
4765
4964
|
}
|
|
4766
4965
|
);
|
|
4767
4966
|
|
|
4967
|
+
// src/server/tools/web-search.ts
|
|
4968
|
+
var DEFAULT_TIMEOUT_MS2 = 15e3;
|
|
4969
|
+
var DEFAULT_MAX_RESULTS = 5;
|
|
4970
|
+
function getTavilyApiKey() {
|
|
4971
|
+
return process.env["TAVILY_API_KEY"] ?? getSetting(SETTINGS_KEYS.SEARCH_TAVILY_API_KEY);
|
|
4972
|
+
}
|
|
4973
|
+
function getSearxngUrl() {
|
|
4974
|
+
return process.env["SEARXNG_URL"] ?? getSetting(SETTINGS_KEYS.SEARCH_SEARXNG_URL);
|
|
4975
|
+
}
|
|
4976
|
+
function getSearxngApiKey() {
|
|
4977
|
+
return process.env["SEARXNG_API_KEY"] ?? getSetting(SETTINGS_KEYS.SEARCH_SEARXNG_API_KEY);
|
|
4978
|
+
}
|
|
4979
|
+
function getActiveEngine() {
|
|
4980
|
+
const preferredEngine = getSetting(SETTINGS_KEYS.SEARCH_ENGINE);
|
|
4981
|
+
if (preferredEngine === "") {
|
|
4982
|
+
return null;
|
|
4983
|
+
}
|
|
4984
|
+
if (preferredEngine === "tavily") {
|
|
4985
|
+
const apiKey = getTavilyApiKey();
|
|
4986
|
+
if (!apiKey) return null;
|
|
4987
|
+
return { engine: "tavily", apiKey };
|
|
4988
|
+
}
|
|
4989
|
+
if (preferredEngine === "searxng") {
|
|
4990
|
+
const url = getSearxngUrl();
|
|
4991
|
+
if (!url) return null;
|
|
4992
|
+
const searxngApiKey = getSearxngApiKey();
|
|
4993
|
+
return searxngApiKey ? { engine: "searxng", url, apiKey: searxngApiKey } : { engine: "searxng", url };
|
|
4994
|
+
}
|
|
4995
|
+
const tavilyKey = getTavilyApiKey();
|
|
4996
|
+
if (tavilyKey) return { engine: "tavily", apiKey: tavilyKey };
|
|
4997
|
+
const searxngUrl = getSearxngUrl();
|
|
4998
|
+
if (searxngUrl) {
|
|
4999
|
+
const searxngApiKey = getSearxngApiKey();
|
|
5000
|
+
return searxngApiKey ? { engine: "searxng", url: searxngUrl, apiKey: searxngApiKey } : { engine: "searxng", url: searxngUrl };
|
|
5001
|
+
}
|
|
5002
|
+
return null;
|
|
5003
|
+
}
|
|
5004
|
+
async function searchTavily(query, maxResults, apiKey, signal) {
|
|
5005
|
+
const response = await fetch("https://api.tavily.com/search", {
|
|
5006
|
+
method: "POST",
|
|
5007
|
+
headers: { "Content-Type": "application/json" },
|
|
5008
|
+
body: JSON.stringify({ api_key: apiKey, query, max_results: maxResults }),
|
|
5009
|
+
signal
|
|
5010
|
+
});
|
|
5011
|
+
if (!response.ok) {
|
|
5012
|
+
const body = await response.text().catch(() => "");
|
|
5013
|
+
throw new Error(`Tavily search failed (${response.status}): ${body}`);
|
|
5014
|
+
}
|
|
5015
|
+
const data = await response.json();
|
|
5016
|
+
return (data.results ?? []).map((r) => ({ title: r.title, url: r.url, content: r.content }));
|
|
5017
|
+
}
|
|
5018
|
+
async function searchSearxng(query, maxResults, config, signal) {
|
|
5019
|
+
const baseUrl = config.url.replace(/\/+$/, "");
|
|
5020
|
+
const url = new URL(`${baseUrl}/search`);
|
|
5021
|
+
url.searchParams.set("format", "json");
|
|
5022
|
+
url.searchParams.set("q", query);
|
|
5023
|
+
const headers = {};
|
|
5024
|
+
if (config.apiKey) {
|
|
5025
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
5026
|
+
}
|
|
5027
|
+
const response = await fetch(url.toString(), { headers, signal });
|
|
5028
|
+
if (!response.ok) {
|
|
5029
|
+
const body = await response.text().catch(() => "");
|
|
5030
|
+
throw new Error(`SearXNG search failed (${response.status}): ${body}`);
|
|
5031
|
+
}
|
|
5032
|
+
const data = await response.json();
|
|
5033
|
+
return (data.results ?? []).slice(0, maxResults).map((r) => ({
|
|
5034
|
+
title: r.title,
|
|
5035
|
+
url: r.url,
|
|
5036
|
+
content: r.content
|
|
5037
|
+
}));
|
|
5038
|
+
}
|
|
5039
|
+
function formatResults(results) {
|
|
5040
|
+
return results.map((r, i) => `[${i + 1}] ${r.title}
|
|
5041
|
+
URL: ${r.url}
|
|
5042
|
+
${r.content}`).join("\n\n");
|
|
5043
|
+
}
|
|
5044
|
+
function clampMaxResults(maxResults) {
|
|
5045
|
+
return maxResults > 0 ? maxResults : DEFAULT_MAX_RESULTS;
|
|
5046
|
+
}
|
|
5047
|
+
var webSearchTool = createTool(
|
|
5048
|
+
"web_search",
|
|
5049
|
+
{
|
|
5050
|
+
type: "function",
|
|
5051
|
+
function: {
|
|
5052
|
+
name: "web_search",
|
|
5053
|
+
description: "Search the web using a configured search engine (Tavily or SearXNG). Returns a list of results with title, URL, and content snippet for each. Use this to find relevant web pages, then use web_fetch to retrieve full content.",
|
|
5054
|
+
parameters: {
|
|
5055
|
+
type: "object",
|
|
5056
|
+
properties: {
|
|
5057
|
+
query: {
|
|
5058
|
+
type: "string",
|
|
5059
|
+
description: "The search query"
|
|
5060
|
+
},
|
|
5061
|
+
max_results: {
|
|
5062
|
+
type: "number",
|
|
5063
|
+
description: "Maximum number of search results to return (default: 5)"
|
|
5064
|
+
}
|
|
5065
|
+
},
|
|
5066
|
+
required: ["query"]
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
},
|
|
5070
|
+
async (args, context, helpers) => {
|
|
5071
|
+
const query = args.query;
|
|
5072
|
+
const maxResults = clampMaxResults(args.max_results ?? DEFAULT_MAX_RESULTS);
|
|
5073
|
+
const active = getActiveEngine();
|
|
5074
|
+
if (!active) {
|
|
5075
|
+
return helpers.error(
|
|
5076
|
+
"web_search requires at least one search engine configured. Set the TAVILY_API_KEY environment variable, or configure SearXNG via SEARXNG_URL (and optionally SEARXNG_API_KEY). You can also configure these in Settings > Advanced > Search Engine."
|
|
5077
|
+
);
|
|
5078
|
+
}
|
|
5079
|
+
const signal = buildSignal(DEFAULT_TIMEOUT_MS2, context.signal);
|
|
5080
|
+
try {
|
|
5081
|
+
let results;
|
|
5082
|
+
if (active.engine === "tavily") {
|
|
5083
|
+
results = await searchTavily(query, maxResults, active.apiKey, signal);
|
|
5084
|
+
} else {
|
|
5085
|
+
const searxngConfig = { url: active.url };
|
|
5086
|
+
if (active.apiKey) searxngConfig.apiKey = active.apiKey;
|
|
5087
|
+
results = await searchSearxng(query, maxResults, searxngConfig, signal);
|
|
5088
|
+
}
|
|
5089
|
+
if (results.length === 0) {
|
|
5090
|
+
return helpers.success("No search results found.", false);
|
|
5091
|
+
}
|
|
5092
|
+
return helpers.success(formatResults(results), false);
|
|
5093
|
+
} catch (error) {
|
|
5094
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
5095
|
+
return helpers.error("Search request timed out (15s). Try a more specific query.");
|
|
5096
|
+
}
|
|
5097
|
+
return helpers.error(error instanceof Error ? error.message : "Search request failed");
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
);
|
|
5101
|
+
|
|
4768
5102
|
// src/server/tools/index.ts
|
|
4769
5103
|
var _builtInTools;
|
|
4770
5104
|
function getBuiltInTools() {
|
|
@@ -4780,6 +5114,7 @@ function getBuiltInTools() {
|
|
|
4780
5114
|
loadSkillTool,
|
|
4781
5115
|
returnValueTool,
|
|
4782
5116
|
webFetchTool,
|
|
5117
|
+
webSearchTool,
|
|
4783
5118
|
devServerTool,
|
|
4784
5119
|
stepDoneTool,
|
|
4785
5120
|
backgroundProcessTool,
|
|
@@ -5036,14 +5371,18 @@ export {
|
|
|
5036
5371
|
loadUserSkills,
|
|
5037
5372
|
loadProjectSkills,
|
|
5038
5373
|
loadAllSkills,
|
|
5374
|
+
loadAllSkillsWithDiagnostics,
|
|
5039
5375
|
getEnabledSkillMetadata,
|
|
5040
5376
|
isSkillEnabled,
|
|
5377
|
+
setSkillEnabled,
|
|
5041
5378
|
getDefaultSkillIds,
|
|
5042
5379
|
isDefaultSkill,
|
|
5043
5380
|
findSkillById,
|
|
5044
5381
|
skillExists,
|
|
5045
5382
|
saveSkill,
|
|
5046
5383
|
saveSkillToProject,
|
|
5384
|
+
updateOwnedSkill,
|
|
5385
|
+
deleteOwnedSkill,
|
|
5047
5386
|
deleteSkill,
|
|
5048
5387
|
deleteProjectSkill,
|
|
5049
5388
|
runTopLevelAgentLoop,
|
|
@@ -5071,4 +5410,4 @@ export {
|
|
|
5071
5410
|
getToolRegistryForAgent,
|
|
5072
5411
|
createToolRegistry
|
|
5073
5412
|
};
|
|
5074
|
-
//# sourceMappingURL=chunk-
|
|
5413
|
+
//# sourceMappingURL=chunk-KX4C5Q5J.js.map
|