openfox 1.6.90 → 1.6.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{auto-compaction-6A34RHDV.js → auto-compaction-7AGIGW6F.js} +7 -7
  2. package/dist/{chat-handler-6ATTKNZT.js → chat-handler-UPAEF6HR.js} +10 -10
  3. package/dist/{chunk-ZORPFD4C.js → chunk-2BS6LMIU.js} +507 -474
  4. package/dist/{chunk-N7W42N5Y.js → chunk-2TKN2X2A.js} +3 -3
  5. package/dist/{chunk-7WHJUQ4Y.js → chunk-7KO2BQFQ.js} +9 -8
  6. package/dist/{chunk-6GGYIEBM.js → chunk-7QG6LY5Q.js} +3 -3
  7. package/dist/{chunk-UEZDAB5X.js → chunk-ARJIHQ3S.js} +240 -143
  8. package/dist/{chunk-TLOL4I7K.js → chunk-BHZL3N2F.js} +3 -3
  9. package/dist/{chunk-WSRF5L3S.js → chunk-CHICEUV6.js} +3 -3
  10. package/dist/{chunk-RCECKE5L.js → chunk-PQCFNTFJ.js} +15 -9
  11. package/dist/{chunk-YNIAFX24.js → chunk-RJIPM7CO.js} +4 -4
  12. package/dist/{chunk-B5AP3RSV.js → chunk-RW25DL5R.js} +3 -3
  13. package/dist/{chunk-JDDNBK6T.js → chunk-WBQNIWAQ.js} +5 -5
  14. package/dist/{chunk-DF5BZBP6.js → chunk-XGJ7HDQ7.js} +2 -2
  15. package/dist/{chunk-6P5WQP6Y.js → chunk-YTPPQK55.js} +2 -2
  16. package/dist/cli/dev.js +1 -1
  17. package/dist/cli/index.js +1 -1
  18. package/dist/{config-HMMPXQ46.js → config-7XJQNOKX.js} +5 -5
  19. package/dist/{events-AQKDXJFK.js → events-K6AYMXFT.js} +3 -3
  20. package/dist/{orchestrator-YUAFANFC.js → orchestrator-M4WINFF3.js} +8 -8
  21. package/dist/package.json +1 -1
  22. package/dist/{processor-4SOP4Q6S.js → processor-D4XQ5QMB.js} +5 -5
  23. package/dist/{protocol--8PKNwb_.d.ts → protocol-XRiJzlOK.d.ts} +1 -1
  24. package/dist/{provider-LDZFYLK6.js → provider-UO5PAZQQ.js} +7 -7
  25. package/dist/{runtime-config-CBJJJGBD.js → runtime-config-CYHDAC7W.js} +2 -2
  26. package/dist/{serve-YO7HQKUD.js → serve-Z5FNJUDT.js} +12 -12
  27. package/dist/server/index.d.ts +2 -1
  28. package/dist/server/index.js +10 -10
  29. package/dist/{service-TU4XSXYJ.js → service-3CLVG2HV.js} +20 -2
  30. package/dist/{settings-VTVOBDYG.js → settings-AYSWDZUI.js} +2 -2
  31. package/dist/shared/index.d.ts +2 -2
  32. package/dist/{tools-7MGUWXDP.js → tools-NFFQWH6M.js} +7 -7
  33. package/dist/{vision-fallback-WT66NURB.js → vision-fallback-GZZWU7CR.js} +2 -2
  34. package/dist/web/assets/{index-7tHnjaI4.css → index-CM0cLwa6.css} +2 -2
  35. package/dist/web/assets/index-SkaCnTiV.js +176 -0
  36. package/dist/web/index.html +2 -2
  37. package/dist/web/sw.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/web/assets/index-CdF408Qk.js +0 -158
  40. /package/dist/web/{workbox-8c29f6e4.js → workbox-7fc22fbe.js} +0 -0
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  SETTINGS_KEYS,
3
3
  deleteSetting,
4
- getSetting,
5
- setSetting
6
- } from "./chunk-RCECKE5L.js";
4
+ getSetting
5
+ } from "./chunk-PQCFNTFJ.js";
7
6
  import {
8
7
  getProject
9
8
  } from "./chunk-ZHBL5G5X.js";
@@ -28,11 +27,11 @@ import {
28
27
  getBackendCapabilities,
29
28
  getModelProfile,
30
29
  streamWithSegments
31
- } from "./chunk-YNIAFX24.js";
30
+ } from "./chunk-RJIPM7CO.js";
32
31
  import {
33
32
  getCurrentContextWindowId,
34
33
  getEventStore
35
- } from "./chunk-6P5WQP6Y.js";
34
+ } from "./chunk-YTPPQK55.js";
36
35
  import {
37
36
  buildContextMessagesFromEventHistory,
38
37
  foldContextState,
@@ -59,7 +58,7 @@ import {
59
58
  } from "./chunk-CQGTEGKL.js";
60
59
  import {
61
60
  getRuntimeConfig
62
- } from "./chunk-B5AP3RSV.js";
61
+ } from "./chunk-RW25DL5R.js";
63
62
  import {
64
63
  logger
65
64
  } from "./chunk-K44MW7JJ.js";
@@ -513,6 +512,7 @@ function buildStreamRequestObject(params) {
513
512
  disableThinking,
514
513
  signal,
515
514
  modelSettings,
515
+ disableXmlProtection,
516
516
  onVisionFallbackStart,
517
517
  onVisionFallbackDone
518
518
  } = params;
@@ -522,7 +522,8 @@ function buildStreamRequestObject(params) {
522
522
  ...toolChoice && { toolChoice },
523
523
  disableThinking: disableThinking ?? false,
524
524
  ...signal && { signal },
525
- ...modelSettings && { modelSettings }
525
+ ...modelSettings && { modelSettings },
526
+ ...disableXmlProtection !== void 0 && { disableXmlProtection }
526
527
  };
527
528
  if (onVisionFallbackStart) streamRequest.onVisionFallbackStart = onVisionFallbackStart;
528
529
  if (onVisionFallbackDone) streamRequest.onVisionFallbackDone = onVisionFallbackDone;
@@ -587,7 +588,8 @@ async function* streamLLMPure(options) {
587
588
  signal,
588
589
  disableThinking,
589
590
  onVisionFallbackStart,
590
- onVisionFallbackDone
591
+ onVisionFallbackDone,
592
+ disableXmlProtection
591
593
  } = options;
592
594
  const llmMessages = [{ role: "system", content: systemPrompt }, ...messages];
593
595
  const profile = getModelProfile(llmClient.getModel());
@@ -622,6 +624,7 @@ async function* streamLLMPure(options) {
622
624
  disableThinking: disableThinking ?? false,
623
625
  signal,
624
626
  modelSettings: options.modelSettings,
627
+ ...disableXmlProtection !== void 0 && { disableXmlProtection },
625
628
  onVisionFallbackStart,
626
629
  onVisionFallbackDone
627
630
  });
@@ -1054,28 +1057,105 @@ Error: ${toolResult.error}` : `Error: ${toolResult.error}`;
1054
1057
  }
1055
1058
 
1056
1059
  // src/server/agents/registry.ts
1057
- import { readdir, readFile as readFile2, writeFile, mkdir, access as access2, unlink } from "fs/promises";
1058
- import { join as join2, dirname as dirname2 } from "path";
1059
- import { constants as constants2 } from "fs";
1060
+ import { readdir as readdir2, readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2, access as access3, unlink as unlink2 } from "fs/promises";
1061
+ import { join as join3, dirname as dirname2 } from "path";
1062
+ import { constants as constants3 } from "fs";
1060
1063
  import { fileURLToPath } from "url";
1064
+ import matter2 from "gray-matter";
1065
+
1066
+ // src/server/shared/item-loader.ts
1067
+ import { readdir, readFile as readFile2, access as access2, writeFile, mkdir, unlink } from "fs/promises";
1068
+ import { join as join2 } from "path";
1069
+ import { constants as constants2 } from "fs";
1061
1070
  import matter from "gray-matter";
1071
+ async function pathExists(path) {
1072
+ try {
1073
+ await access2(path, constants2.R_OK);
1074
+ return true;
1075
+ } catch {
1076
+ return false;
1077
+ }
1078
+ }
1079
+ async function getDefaultIds(dir, extension) {
1080
+ try {
1081
+ const files = (await readdir(dir)).filter((f) => f.endsWith(extension));
1082
+ return files.map((f) => f.replace(extension, ""));
1083
+ } catch {
1084
+ return [];
1085
+ }
1086
+ }
1087
+ async function loadItemsFromDir(dir, options) {
1088
+ if (!await pathExists(dir)) {
1089
+ return [];
1090
+ }
1091
+ let files;
1092
+ try {
1093
+ files = (await readdir(dir)).filter((f) => f.endsWith(options.extension));
1094
+ } catch {
1095
+ return [];
1096
+ }
1097
+ const items = [];
1098
+ for (const file of files) {
1099
+ try {
1100
+ const raw = await readFile2(join2(dir, file), "utf-8");
1101
+ const { data, content } = matter(raw);
1102
+ if (data.id && content.trim()) {
1103
+ items.push({
1104
+ metadata: data,
1105
+ prompt: content.trim()
1106
+ });
1107
+ } else {
1108
+ logger.warn(`Skipping invalid ${options.logName} file`, { file });
1109
+ }
1110
+ } catch (err) {
1111
+ logger.warn(`Failed to parse ${options.logName} file`, {
1112
+ file,
1113
+ error: err instanceof Error ? err.message : String(err)
1114
+ });
1115
+ }
1116
+ }
1117
+ return items;
1118
+ }
1119
+ async function saveItemToDir(dir, item, ext, serialize) {
1120
+ if (!await pathExists(dir)) {
1121
+ await mkdir(dir, { recursive: true });
1122
+ }
1123
+ const meta = item.metadata;
1124
+ const filePath = join2(dir, `${meta.id}${ext}`);
1125
+ await writeFile(filePath, serialize(item), "utf-8");
1126
+ }
1127
+ var jsonSerializer = (item) => JSON.stringify(item, null, 2) + "\n";
1128
+ async function deleteItemFromDir(dir, id, ext) {
1129
+ const filePath = join2(dir, `${id}${ext}`);
1130
+ try {
1131
+ await unlink(filePath);
1132
+ return { success: true };
1133
+ } catch {
1134
+ return { success: false };
1135
+ }
1136
+ }
1137
+
1138
+ // src/server/agents/registry.ts
1062
1139
  var __bundleDir = dirname2(fileURLToPath(import.meta.url));
1063
- var DEFAULTS_DIR = join2(__bundleDir, "defaults");
1064
- var DEFAULTS_DIR_ALT = join2(__bundleDir, "agent-defaults");
1140
+ var DEFAULTS_DIR = join3(__bundleDir, "defaults");
1141
+ var DEFAULTS_DIR_ALT = join3(__bundleDir, "agent-defaults");
1065
1142
  var AGENT_EXTENSION = ".agent.md";
1066
1143
  function getAgentsDir(configDir) {
1067
- return join2(configDir, "agents");
1144
+ return join3(configDir, "agents");
1068
1145
  }
1069
- async function pathExists(path) {
1146
+ function getProjectAgentsDir(projectDir) {
1147
+ return join3(projectDir, ".openfox", "agents");
1148
+ }
1149
+ async function pathExists2(path) {
1070
1150
  try {
1071
- await access2(path, constants2.R_OK);
1151
+ await access3(path, constants3.R_OK);
1072
1152
  return true;
1073
1153
  } catch {
1074
1154
  return false;
1075
1155
  }
1076
1156
  }
1077
1157
  function parseAgentFile(raw, filename) {
1078
- const { data, content } = matter(raw);
1158
+ const { data, content } = matter2(raw);
1079
1159
  const meta = data;
1080
1160
  if (!meta["id"] || !content.trim()) {
1081
1161
  logger.warn("Skipping invalid agent file", { filename });
@@ -1093,19 +1173,19 @@ function parseAgentFile(raw, filename) {
1093
1173
  return { metadata, prompt: content.trim() };
1094
1174
  }
1095
1175
  async function loadAgentsFromDir(dir) {
1096
- if (!await pathExists(dir)) {
1176
+ if (!await pathExists2(dir)) {
1097
1177
  return [];
1098
1178
  }
1099
1179
  let files;
1100
1180
  try {
1101
- files = (await readdir(dir)).filter((f) => f.endsWith(AGENT_EXTENSION));
1181
+ files = (await readdir2(dir)).filter((f) => f.endsWith(AGENT_EXTENSION));
1102
1182
  } catch {
1103
1183
  return [];
1104
1184
  }
1105
1185
  const agents = [];
1106
1186
  for (const file of files) {
1107
1187
  try {
1108
- const raw = await readFile2(join2(dir, file), "utf-8");
1188
+ const raw = await readFile3(join3(dir, file), "utf-8");
1109
1189
  const agent = parseAgentFile(raw, file);
1110
1190
  if (agent) {
1111
1191
  agents.push(agent);
@@ -1124,7 +1204,10 @@ async function loadDefaultAgents() {
1124
1204
  async function loadUserAgents(configDir) {
1125
1205
  return loadAgentsFromDir(getAgentsDir(configDir));
1126
1206
  }
1127
- async function loadAllAgents(configDir) {
1207
+ async function loadProjectAgents(projectDir) {
1208
+ return loadAgentsFromDir(getProjectAgentsDir(projectDir));
1209
+ }
1210
+ async function loadAllAgents(configDir, projectDir) {
1128
1211
  const [defaultAgents, userAgents] = await Promise.all([loadDefaultAgents(), loadUserAgents(configDir)]);
1129
1212
  const agentMap = /* @__PURE__ */ new Map();
1130
1213
  for (const agent of defaultAgents) {
@@ -1133,12 +1216,40 @@ async function loadAllAgents(configDir) {
1133
1216
  for (const agent of userAgents) {
1134
1217
  agentMap.set(agent.metadata.id, agent);
1135
1218
  }
1219
+ if (projectDir) {
1220
+ const projectAgents = await loadProjectAgents(projectDir);
1221
+ for (const agent of projectAgents) {
1222
+ agentMap.set(agent.metadata.id, agent);
1223
+ }
1224
+ }
1136
1225
  return Array.from(agentMap.values());
1137
1226
  }
1227
+ async function saveAgentToProject(projectDir, agent) {
1228
+ await saveItemToDir(
1229
+ getProjectAgentsDir(projectDir),
1230
+ agent,
1231
+ AGENT_EXTENSION,
1232
+ (a) => matter2.stringify(a.prompt, a.metadata)
1233
+ );
1234
+ }
1235
+ async function deleteProjectAgent(projectDir, agentId) {
1236
+ const agentsDir = getProjectAgentsDir(projectDir);
1237
+ const paths = getAgentFilePaths(agentsDir, agentId);
1238
+ for (const filePath of paths) {
1239
+ try {
1240
+ await unlink2(filePath);
1241
+ return { success: true };
1242
+ } catch {
1243
+ continue;
1244
+ }
1245
+ }
1246
+ return { success: false };
1247
+ }
1138
1248
  async function loadAllAgentsDefault() {
1139
1249
  try {
1140
- const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? "production");
1141
- return await loadAllAgents(configDir);
1250
+ const config = getRuntimeConfig();
1251
+ const configDir = getGlobalConfigDir(config.mode ?? "production");
1252
+ return await loadAllAgents(configDir, config.workdir);
1142
1253
  } catch {
1143
1254
  return loadDefaultAgents();
1144
1255
  }
@@ -1146,24 +1257,20 @@ async function loadAllAgentsDefault() {
1146
1257
  async function getDefaultAgentIds() {
1147
1258
  for (const dir of [DEFAULTS_DIR, DEFAULTS_DIR_ALT]) {
1148
1259
  try {
1149
- const files = (await readdir(dir)).filter((f) => f.endsWith(AGENT_EXTENSION));
1260
+ const files = (await readdir2(dir)).filter((f) => f.endsWith(AGENT_EXTENSION));
1150
1261
  return files.map((f) => f.replace(AGENT_EXTENSION, ""));
1151
1262
  } catch {
1152
1263
  }
1153
1264
  }
1154
1265
  return [];
1155
1266
  }
1156
- async function getDefaultAgentContent(agentId) {
1157
- const defaults = await loadDefaultAgents();
1158
- return defaults.find((a) => a.metadata.id === agentId) ?? null;
1159
- }
1160
1267
  async function isDefaultAgent(agentId) {
1161
1268
  const defaultIds = await getDefaultAgentIds();
1162
1269
  return defaultIds.includes(agentId);
1163
1270
  }
1164
1271
  function getAgentFilePaths(agentsDir, agentId) {
1165
1272
  const hyphenated = agentId.replace(/_/g, "-");
1166
- return [join2(agentsDir, `${agentId}${AGENT_EXTENSION}`), join2(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)];
1273
+ return [join3(agentsDir, `${agentId}${AGENT_EXTENSION}`), join3(agentsDir, `${hyphenated}${AGENT_EXTENSION}`)];
1167
1274
  }
1168
1275
  function findAgentById(agentId, agents) {
1169
1276
  return agents.find((a) => a.metadata.id === agentId);
@@ -1171,24 +1278,35 @@ function findAgentById(agentId, agents) {
1171
1278
  function getSubAgents(agents) {
1172
1279
  return agents.filter((a) => a.metadata.subagent);
1173
1280
  }
1174
- async function agentExists(configDir, agentId) {
1281
+ function getTopLevelAgents(agents) {
1282
+ return agents.filter((a) => !a.metadata.subagent);
1283
+ }
1284
+ async function agentExists(configDir, agentId, projectDir) {
1175
1285
  const agentsDir = getAgentsDir(configDir);
1176
1286
  const paths = getAgentFilePaths(agentsDir, agentId);
1177
1287
  for (const filePath of paths) {
1178
- if (await pathExists(filePath)) {
1288
+ if (await pathExists2(filePath)) {
1179
1289
  return true;
1180
1290
  }
1181
1291
  }
1292
+ if (projectDir) {
1293
+ const projectPaths = getAgentFilePaths(getProjectAgentsDir(projectDir), agentId);
1294
+ for (const filePath of projectPaths) {
1295
+ if (await pathExists2(filePath)) {
1296
+ return true;
1297
+ }
1298
+ }
1299
+ }
1182
1300
  return false;
1183
1301
  }
1184
1302
  async function saveAgent(configDir, agent) {
1185
1303
  const agentsDir = getAgentsDir(configDir);
1186
- if (!await pathExists(agentsDir)) {
1187
- await mkdir(agentsDir, { recursive: true });
1304
+ if (!await pathExists2(agentsDir)) {
1305
+ await mkdir2(agentsDir, { recursive: true });
1188
1306
  }
1189
- const filePath = join2(agentsDir, `${agent.metadata.id}${AGENT_EXTENSION}`);
1190
- const content = matter.stringify(agent.prompt, agent.metadata);
1191
- await writeFile(filePath, content, "utf-8");
1307
+ const filePath = join3(agentsDir, `${agent.metadata.id}${AGENT_EXTENSION}`);
1308
+ const content = matter2.stringify(agent.prompt, agent.metadata);
1309
+ await writeFile2(filePath, content, "utf-8");
1192
1310
  }
1193
1311
  async function deleteAgent(configDir, agentId) {
1194
1312
  const isDefault = await isDefaultAgent(agentId);
@@ -1199,7 +1317,7 @@ async function deleteAgent(configDir, agentId) {
1199
1317
  const paths = getAgentFilePaths(agentsDir, agentId);
1200
1318
  for (const filePath of paths) {
1201
1319
  try {
1202
- await unlink(filePath);
1320
+ await unlink2(filePath);
1203
1321
  return { success: true };
1204
1322
  } catch {
1205
1323
  continue;
@@ -1209,7 +1327,7 @@ async function deleteAgent(configDir, agentId) {
1209
1327
  }
1210
1328
 
1211
1329
  // src/server/tools/read.ts
1212
- import { readFile as readFile4, stat } from "fs/promises";
1330
+ import { readFile as readFile5, stat } from "fs/promises";
1213
1331
  import { extname } from "path";
1214
1332
 
1215
1333
  // src/server/tools/types.ts
@@ -1240,7 +1358,7 @@ import { resolve as resolve2, isAbsolute } from "path";
1240
1358
 
1241
1359
  // src/server/tools/path-security.ts
1242
1360
  import { realpath } from "fs/promises";
1243
- import { resolve, normalize, join as join3, basename, sep } from "path";
1361
+ import { resolve, normalize, join as join4, basename, sep } from "path";
1244
1362
  import { homedir, tmpdir } from "os";
1245
1363
  var SAFE_PATHS = /* @__PURE__ */ new Set([
1246
1364
  "/dev/null",
@@ -1374,7 +1492,7 @@ function extractAbsolutePathsFromCommand(command) {
1374
1492
  }
1375
1493
  } else if (content.startsWith("~")) {
1376
1494
  const pathPart = content.slice(1);
1377
- const fullPath = join3(home, pathPart);
1495
+ const fullPath = join4(home, pathPart);
1378
1496
  const resolved = normalize(resolve(fullPath));
1379
1497
  if (!isSafePath(resolved)) {
1380
1498
  paths.push(resolved);
@@ -1684,7 +1802,7 @@ function createTool(name, definition, handler2) {
1684
1802
 
1685
1803
  // src/server/tools/file-tracker.ts
1686
1804
  import { createHash } from "crypto";
1687
- import { readFile as readFile3 } from "fs/promises";
1805
+ import { readFile as readFile4 } from "fs/promises";
1688
1806
  import { resolve as resolve3 } from "path";
1689
1807
  var FileNotReadError = class extends Error {
1690
1808
  constructor(path) {
@@ -1702,7 +1820,7 @@ var FileChangedExternallyError = class extends Error {
1702
1820
  };
1703
1821
  async function computeFileHash(filePath) {
1704
1822
  try {
1705
- const content = await readFile3(filePath);
1823
+ const content = await readFile4(filePath);
1706
1824
  const hash = createHash("sha256");
1707
1825
  hash.update(content);
1708
1826
  return hash.digest("hex");
@@ -1837,7 +1955,7 @@ var readFileTool = createTool(
1837
1955
  } catch {
1838
1956
  return helpers.error(`File not found: ${args.path}`);
1839
1957
  }
1840
- const rawBuffer = await readFile4(fullPath);
1958
+ const rawBuffer = await readFile5(fullPath);
1841
1959
  const mimeType = detectImageType(rawBuffer, args.path);
1842
1960
  if (mimeType) {
1843
1961
  const base64Data = rawBuffer.toString("base64");
@@ -1883,7 +2001,7 @@ var readFileTool = createTool(
1883
2001
  );
1884
2002
 
1885
2003
  // src/server/tools/write.ts
1886
- import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
2004
+ import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1887
2005
  import { dirname as dirname3 } from "path";
1888
2006
 
1889
2007
  // src/server/tools/diagnostics.ts
@@ -1944,8 +2062,8 @@ var writeFileTool = createTool(
1944
2062
  return helpers.error(validation.error?.message ?? "File validation failed");
1945
2063
  }
1946
2064
  const dir = dirname3(fullPath);
1947
- await mkdir2(dir, { recursive: true });
1948
- await writeFile2(fullPath, args.content, "utf-8");
2065
+ await mkdir3(dir, { recursive: true });
2066
+ await writeFile3(fullPath, args.content, "utf-8");
1949
2067
  const lineCount = args.content.split("\n").length;
1950
2068
  const byteCount = Buffer.byteLength(args.content, "utf-8");
1951
2069
  let output = `Successfully wrote ${lineCount} lines (${byteCount} bytes) to ${args.path}`;
@@ -1963,7 +2081,7 @@ var writeFileTool = createTool(
1963
2081
  );
1964
2082
 
1965
2083
  // src/server/tools/edit.ts
1966
- import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
2084
+ import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
1967
2085
 
1968
2086
  // src/server/tools/edit-context.ts
1969
2087
  var CONTEXT_LINES = 4;
@@ -2119,7 +2237,7 @@ var editFileTool = createTool(
2119
2237
  }
2120
2238
  let content;
2121
2239
  try {
2122
- content = await readFile5(fullPath, "utf-8");
2240
+ content = await readFile6(fullPath, "utf-8");
2123
2241
  } catch {
2124
2242
  return helpers.error(`File not found: ${args.path}`);
2125
2243
  }
@@ -2184,7 +2302,7 @@ Make sure whitespace and indentation match exactly.`
2184
2302
  /\n/g,
2185
2303
  fileLineEnding === "crlf" ? "\r\n" : fileLineEnding === "cr" ? "\r" : "\n"
2186
2304
  );
2187
- await writeFile3(fullPath, newContent, "utf-8");
2305
+ await writeFile4(fullPath, newContent, "utf-8");
2188
2306
  let output = `Successfully replaced ${replaceAll ? occurrences : 1} occurrence(s) in ${args.path}`;
2189
2307
  let diagnostics = [];
2190
2308
  if (context.lspManager) {
@@ -2685,66 +2803,10 @@ function createToolProgressHandler(eventStore, messageId, callId, sessionId) {
2685
2803
  }
2686
2804
 
2687
2805
  // src/server/skills/registry.ts
2688
- import { writeFile as writeFile4, mkdir as mkdir3, unlink as unlink2 } from "fs/promises";
2806
+ import { writeFile as writeFile5, mkdir as mkdir4, unlink as unlink3 } from "fs/promises";
2689
2807
  import { join as join5, dirname as dirname4 } from "path";
2690
2808
  import { fileURLToPath as fileURLToPath2 } from "url";
2691
2809
  import matter3 from "gray-matter";
2692
-
2693
- // src/server/shared/item-loader.ts
2694
- import { readdir as readdir2, readFile as readFile6, access as access3 } from "fs/promises";
2695
- import { join as join4 } from "path";
2696
- import { constants as constants3 } from "fs";
2697
- import matter2 from "gray-matter";
2698
- async function pathExists2(path) {
2699
- try {
2700
- await access3(path, constants3.R_OK);
2701
- return true;
2702
- } catch {
2703
- return false;
2704
- }
2705
- }
2706
- async function getDefaultIds(dir, extension) {
2707
- try {
2708
- const files = (await readdir2(dir)).filter((f) => f.endsWith(extension));
2709
- return files.map((f) => f.replace(extension, ""));
2710
- } catch {
2711
- return [];
2712
- }
2713
- }
2714
- async function loadItemsFromDir(dir, options) {
2715
- if (!await pathExists2(dir)) {
2716
- return [];
2717
- }
2718
- let files;
2719
- try {
2720
- files = (await readdir2(dir)).filter((f) => f.endsWith(options.extension));
2721
- } catch {
2722
- return [];
2723
- }
2724
- const items = [];
2725
- for (const file of files) {
2726
- try {
2727
- const raw = await readFile6(join4(dir, file), "utf-8");
2728
- const { data, content } = matter2(raw);
2729
- if (data.id && content.trim()) {
2730
- items.push({
2731
- metadata: data,
2732
- prompt: content.trim()
2733
- });
2734
- } else {
2735
- logger.warn(`Skipping invalid ${options.logName} file`, { file });
2736
- }
2737
- } catch (err) {
2738
- logger.warn(`Failed to parse ${options.logName} file`, {
2739
- file,
2740
- error: err instanceof Error ? err.message : String(err)
2741
- });
2742
- }
2743
- }
2744
- return items;
2745
- }
2746
-
2747
- // src/server/skills/registry.ts
2748
2810
  var __bundleDir2 = dirname4(fileURLToPath2(import.meta.url));
2749
2811
  var DEFAULTS_DIR2 = join5(__bundleDir2, "defaults");
2750
2812
  var DEFAULTS_DIR_ALT2 = join5(__bundleDir2, "skill-defaults");
@@ -2753,6 +2815,9 @@ var SKILL_SETTING_PREFIX = "skill.enabled.";
2753
2815
  function getSkillsDir(configDir) {
2754
2816
  return join5(configDir, "skills");
2755
2817
  }
2818
+ function getProjectSkillsDir(projectDir) {
2819
+ return join5(projectDir, ".openfox", "skills");
2820
+ }
2756
2821
  async function loadDefaultSkills() {
2757
2822
  let defaults = await loadItemsFromDir(DEFAULTS_DIR2, {
2758
2823
  extension: SKILL_EXTENSION,
@@ -2772,7 +2837,13 @@ async function loadUserSkills(configDir) {
2772
2837
  logName: "skill"
2773
2838
  });
2774
2839
  }
2775
- async function loadAllSkills(configDir) {
2840
+ async function loadProjectSkills(projectDir) {
2841
+ return loadItemsFromDir(getProjectSkillsDir(projectDir), {
2842
+ extension: SKILL_EXTENSION,
2843
+ logName: "skill"
2844
+ });
2845
+ }
2846
+ async function loadAllSkills(configDir, projectDir) {
2776
2847
  const [defaultSkills, userSkills] = await Promise.all([loadDefaultSkills(), loadUserSkills(configDir)]);
2777
2848
  const skillMap = /* @__PURE__ */ new Map();
2778
2849
  for (const skill of defaultSkills) {
@@ -2781,14 +2852,20 @@ async function loadAllSkills(configDir) {
2781
2852
  for (const skill of userSkills) {
2782
2853
  skillMap.set(skill.metadata.id, skill);
2783
2854
  }
2855
+ if (projectDir) {
2856
+ const projectSkills = await loadProjectSkills(projectDir);
2857
+ for (const skill of projectSkills) {
2858
+ skillMap.set(skill.metadata.id, skill);
2859
+ }
2860
+ }
2784
2861
  return Array.from(skillMap.values());
2785
2862
  }
2786
- async function getEnabledSkills(configDir) {
2787
- const all = await loadAllSkills(configDir);
2863
+ async function getEnabledSkills(configDir, projectDir) {
2864
+ const all = await loadAllSkills(configDir, projectDir);
2788
2865
  return all.filter((s) => isSkillEnabled(s.metadata.id));
2789
2866
  }
2790
- async function getEnabledSkillMetadata(configDir) {
2791
- const enabled = await getEnabledSkills(configDir);
2867
+ async function getEnabledSkillMetadata(configDir, projectDir) {
2868
+ const enabled = await getEnabledSkills(configDir, projectDir);
2792
2869
  return enabled.map((s) => s.metadata);
2793
2870
  }
2794
2871
  function isSkillEnabled(skillId) {
@@ -2796,18 +2873,11 @@ function isSkillEnabled(skillId) {
2796
2873
  if (value === null) return true;
2797
2874
  return value === "true";
2798
2875
  }
2799
- function setSkillEnabled(skillId, enabled) {
2800
- setSetting(`${SKILL_SETTING_PREFIX}${skillId}`, String(enabled));
2801
- }
2802
2876
  async function getDefaultSkillIds() {
2803
2877
  const ids = await getDefaultIds(DEFAULTS_DIR2, SKILL_EXTENSION);
2804
2878
  if (ids.length) return ids;
2805
2879
  return getDefaultIds(DEFAULTS_DIR_ALT2, SKILL_EXTENSION);
2806
2880
  }
2807
- async function getDefaultSkillContent(skillId) {
2808
- const defaults = await loadDefaultSkills();
2809
- return defaults.find((s) => s.metadata.id === skillId) ?? null;
2810
- }
2811
2881
  async function isDefaultSkill(skillId) {
2812
2882
  const defaultIds = await getDefaultSkillIds();
2813
2883
  return defaultIds.includes(skillId);
@@ -2815,17 +2885,28 @@ async function isDefaultSkill(skillId) {
2815
2885
  function findSkillById(skillId, skills) {
2816
2886
  return skills.find((s) => s.metadata.id === skillId);
2817
2887
  }
2818
- async function skillExists(configDir, skillId) {
2819
- return pathExists2(join5(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`));
2888
+ async function skillExists(configDir, skillId, projectDir) {
2889
+ if (await pathExists(join5(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`))) return true;
2890
+ if (projectDir && await pathExists(join5(getProjectSkillsDir(projectDir), `${skillId}${SKILL_EXTENSION}`)))
2891
+ return true;
2892
+ return false;
2820
2893
  }
2821
2894
  async function saveSkill(configDir, skill) {
2822
2895
  const skillsDir = getSkillsDir(configDir);
2823
- if (!await pathExists2(skillsDir)) {
2824
- await mkdir3(skillsDir, { recursive: true });
2896
+ if (!await pathExists(skillsDir)) {
2897
+ await mkdir4(skillsDir, { recursive: true });
2825
2898
  }
2826
2899
  const filePath = join5(skillsDir, `${skill.metadata.id}${SKILL_EXTENSION}`);
2827
2900
  const content = matter3.stringify(skill.prompt, skill.metadata);
2828
- await writeFile4(filePath, content, "utf-8");
2901
+ await writeFile5(filePath, content, "utf-8");
2902
+ }
2903
+ async function saveSkillToProject(projectDir, skill) {
2904
+ await saveItemToDir(
2905
+ getProjectSkillsDir(projectDir),
2906
+ skill,
2907
+ SKILL_EXTENSION,
2908
+ (s) => matter3.stringify(s.prompt, s.metadata)
2909
+ );
2829
2910
  }
2830
2911
  async function deleteSkill(configDir, skillId) {
2831
2912
  const isDefault = await isDefaultSkill(skillId);
@@ -2834,13 +2915,16 @@ async function deleteSkill(configDir, skillId) {
2834
2915
  }
2835
2916
  const filePath = join5(getSkillsDir(configDir), `${skillId}${SKILL_EXTENSION}`);
2836
2917
  try {
2837
- await unlink2(filePath);
2918
+ await unlink3(filePath);
2838
2919
  deleteSetting(`${SKILL_SETTING_PREFIX}${skillId}`);
2839
2920
  return { success: true };
2840
2921
  } catch {
2841
2922
  return { success: false };
2842
2923
  }
2843
2924
  }
2925
+ async function deleteProjectSkill(projectDir, skillId) {
2926
+ return deleteItemFromDir(getProjectSkillsDir(projectDir), skillId, SKILL_EXTENSION);
2927
+ }
2844
2928
 
2845
2929
  // src/server/chat/agent-loop.ts
2846
2930
  import stripAnsi from "strip-ansi";
@@ -3134,8 +3218,9 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
3134
3218
  eventStore.append(sessionId, createFormatRetryEvent(formatRetryCount, MAX_FORMAT_RETRIES));
3135
3219
  requestMessages.push({ role: "user", content: FORMAT_CORRECTION_PROMPT2, source: "history" });
3136
3220
  }
3137
- const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? "production");
3138
- const skills = await getEnabledSkillMetadata(configDir);
3221
+ const runtimeConfig = getRuntimeConfig();
3222
+ const configDir = getGlobalConfigDir(runtimeConfig.mode ?? "production");
3223
+ const skills = await getEnabledSkillMetadata(configDir, runtimeConfig.workdir);
3139
3224
  if (signal?.aborted) throw new Error("Aborted");
3140
3225
  const assembledRequest = config.assembleRequest({
3141
3226
  workdir: session.workdir,
@@ -3187,6 +3272,7 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
3187
3272
  };
3188
3273
  const previousContextTokens = sessionManager.getContextState(sessionId).currentTokens;
3189
3274
  const modelSettings = currentMaxTokensOverride !== void 0 ? { ...sessionManager.getCurrentModelSettings(), maxTokens: currentMaxTokensOverride } : sessionManager.getCurrentModelSettings();
3275
+ const disableXmlProtection = getSetting("llm.disableXmlProtection") === "true";
3190
3276
  const streamGen = streamLLMPure({
3191
3277
  messageId: assistantMsgId,
3192
3278
  systemPrompt: assembledRequest.systemPrompt,
@@ -3195,6 +3281,7 @@ async function runTopLevelAgentLoop(config, turnMetrics) {
3195
3281
  tools: toolRegistry.definitions,
3196
3282
  toolChoice: "auto",
3197
3283
  signal,
3284
+ disableXmlProtection,
3198
3285
  onVisionFallbackStart,
3199
3286
  onVisionFallbackDone,
3200
3287
  ...modelSettings && { modelSettings }
@@ -3523,8 +3610,9 @@ async function executeSubAgent(options) {
3523
3610
  }
3524
3611
  const { content: instructionContent, files } = await getAllInstructions(session.workdir, session.projectId);
3525
3612
  const injectedFiles = toInjectedFiles(files);
3526
- const configDir = getGlobalConfigDir(getRuntimeConfig().mode ?? "production");
3527
- const skills = await getEnabledSkillMetadata(configDir);
3613
+ const config = getRuntimeConfig();
3614
+ const configDir = getGlobalConfigDir(config.mode ?? "production");
3615
+ const skills = await getEnabledSkillMetadata(configDir, config.workdir);
3528
3616
  const systemPrompt = buildSubAgentSystemPrompt(
3529
3617
  session.workdir,
3530
3618
  agentDef,
@@ -3793,7 +3881,7 @@ var callSubAgentTool = {
3793
3881
  };
3794
3882
  }
3795
3883
  try {
3796
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-7MGUWXDP.js");
3884
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-NFFQWH6M.js");
3797
3885
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3798
3886
  const turnMetrics = new TurnMetrics();
3799
3887
  const result = await executeSubAgent({
@@ -3841,7 +3929,7 @@ var handler = async (args, _context, helpers) => {
3841
3929
  }
3842
3930
  const config = getRuntimeConfig();
3843
3931
  const configDir = getGlobalConfigDir(config.mode ?? "production");
3844
- const allSkills = await loadAllSkills(configDir);
3932
+ const allSkills = await loadAllSkills(configDir, config.workdir);
3845
3933
  const skill = findSkillById(skillId, allSkills);
3846
3934
  if (!skill) {
3847
3935
  const available = allSkills.map((s) => s.metadata.id).join(", ");
@@ -4045,7 +4133,7 @@ var webFetchTool = createTool(
4045
4133
 
4046
4134
  // src/server/dev-server/manager.ts
4047
4135
  import { spawn as spawn3 } from "child_process";
4048
- import { readFile as readFile7, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
4136
+ import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
4049
4137
  import { resolve as resolve5, join as join6 } from "path";
4050
4138
  var MAX_LOG_LINES = 2e3;
4051
4139
  var MAX_LOG_BYTES = 1e5;
@@ -4128,9 +4216,9 @@ var DevServerManager = class {
4128
4216
  async saveConfig(workdir, config) {
4129
4217
  const resolved = this.resolveWorkdir(workdir);
4130
4218
  const dirPath = join6(resolved, ".openfox");
4131
- await mkdir4(dirPath, { recursive: true });
4219
+ await mkdir5(dirPath, { recursive: true });
4132
4220
  const configPath = getDevServerConfigPath(workdir);
4133
- await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
4221
+ await writeFile6(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
4134
4222
  }
4135
4223
  async start(workdir) {
4136
4224
  const instance = this.getInstance(workdir);
@@ -4290,7 +4378,7 @@ var devServerTool = createTool(
4290
4378
  type: "function",
4291
4379
  function: {
4292
4380
  name: "dev_server",
4293
- description: "Control the project dev server. Start, stop, restart, check status, or fetch logs with optional pagination. The dev server command and URL are configured in .openfox/dev-dev.json (dev mode) or .openfox/dev.json (prod).",
4381
+ description: "Control the project dev server. Start, stop, restart, check status, or fetch logs with optional pagination. The dev server is configured via .openfox/dev.json in the project root with fields: command (string, required), url (string, required), hotReload (boolean, optional, default false), disableInspect (boolean, optional, default false).",
4294
4382
  parameters: {
4295
4383
  type: "object",
4296
4384
  properties: {
@@ -4358,7 +4446,7 @@ var devServerTool = createTool(
4358
4446
  }
4359
4447
  if (!status.config) {
4360
4448
  return helpers.error(
4361
- 'No .openfox/dev-dev.json (dev mode) or .openfox/dev.json (prod) config found. Create one with:\n\n{\n "command": "npm run dev",\n "url": "http://localhost:3000",\n "hotReload": true\n}'
4449
+ 'No .openfox/dev.json config found in the project root. Create one with:\n\n{\n "command": "npm run dev",\n "url": "http://localhost:3000",\n "hotReload": true,\n "disableInspect": false\n}'
4362
4450
  );
4363
4451
  }
4364
4452
  return helpers.success(
@@ -4830,7 +4918,7 @@ async function performContextCompaction(options) {
4830
4918
  const subAgentDefs = getSubAgents(allAgents);
4831
4919
  const toolRegistry = getToolRegistryForAgent(plannerDef);
4832
4920
  const configDir = getGlobalConfigDir(config.mode ?? "production");
4833
- const skills = await getEnabledSkillMetadata(configDir);
4921
+ const skills = await getEnabledSkillMetadata(configDir, config.workdir);
4834
4922
  const assembledRequest = assembleAgentRequest({
4835
4923
  agentDef: plannerDef,
4836
4924
  subAgentDefs,
@@ -4933,14 +5021,24 @@ export {
4933
5021
  cancelPathConfirmationsForSession,
4934
5022
  checkAborted,
4935
5023
  spawnShellProcess,
5024
+ pathExists,
5025
+ getDefaultIds,
5026
+ loadItemsFromDir,
5027
+ saveItemToDir,
5028
+ jsonSerializer,
5029
+ deleteItemFromDir,
4936
5030
  loadDefaultAgents,
4937
5031
  loadUserAgents,
5032
+ loadProjectAgents,
4938
5033
  loadAllAgents,
5034
+ saveAgentToProject,
5035
+ deleteProjectAgent,
4939
5036
  loadAllAgentsDefault,
4940
5037
  getDefaultAgentIds,
4941
- getDefaultAgentContent,
5038
+ isDefaultAgent,
4942
5039
  findAgentById,
4943
5040
  getSubAgents,
5041
+ getTopLevelAgents,
4944
5042
  agentExists,
4945
5043
  saveAgent,
4946
5044
  deleteAgent,
@@ -4954,20 +5052,19 @@ export {
4954
5052
  createToolResultEvent,
4955
5053
  createChatDoneEvent,
4956
5054
  assembleAgentRequest,
4957
- pathExists2 as pathExists,
4958
- getDefaultIds,
4959
- loadItemsFromDir,
4960
5055
  loadDefaultSkills,
4961
5056
  loadUserSkills,
5057
+ loadProjectSkills,
4962
5058
  loadAllSkills,
4963
5059
  isSkillEnabled,
4964
- setSkillEnabled,
4965
5060
  getDefaultSkillIds,
4966
- getDefaultSkillContent,
5061
+ isDefaultSkill,
4967
5062
  findSkillById,
4968
5063
  skillExists,
4969
5064
  saveSkill,
5065
+ saveSkillToProject,
4970
5066
  deleteSkill,
5067
+ deleteProjectSkill,
4971
5068
  maybeAutoCompactContext,
4972
5069
  performManualContextCompaction,
4973
5070
  resolveCompactionStatsIdentity,
@@ -4983,4 +5080,4 @@ export {
4983
5080
  getToolRegistryForAgent,
4984
5081
  createToolRegistry
4985
5082
  };
4986
- //# sourceMappingURL=chunk-UEZDAB5X.js.map
5083
+ //# sourceMappingURL=chunk-ARJIHQ3S.js.map