openfox 0.2.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/agent-defaults/builder.agent.md +3 -7
  2. package/dist/agent-defaults/code-reviewer.agent.md +1 -2
  3. package/dist/agent-defaults/debugger.agent.md +1 -2
  4. package/dist/agent-defaults/planner.agent.md +2 -9
  5. package/dist/agent-defaults/test-generator.agent.md +1 -1
  6. package/dist/agent-defaults/verifier.agent.md +4 -5
  7. package/dist/ask-3RK5YJZE.js +19 -0
  8. package/dist/auto-compaction-7LNZ5J3W.js +23 -0
  9. package/dist/{branch.api-WDHKZJS6.js → branch.api-XADFZZAA.js} +2 -2
  10. package/dist/chat-handler-T7SLWZ4E.js +201 -0
  11. package/dist/chunk-22CTURMH.js +128 -0
  12. package/dist/chunk-2KSCZSXQ.js +680 -0
  13. package/dist/{chunk-YMUIRZUI.js → chunk-2TBNG7CS.js} +12 -12
  14. package/dist/{chunk-4Z2OZ6NX.js → chunk-3EHGGBWE.js} +29 -2
  15. package/dist/{chunk-L475ND2Z.js → chunk-3UQACISJ.js} +162 -52
  16. package/dist/{chunk-AF7XS4OI.js → chunk-5YQAXUKO.js} +4 -1
  17. package/dist/{chunk-MTX5Y3V3.js → chunk-7IOZFJBW.js} +2 -2
  18. package/dist/chunk-BU2ZYOEA.js +1067 -0
  19. package/dist/chunk-M2C5GEQR.js +137 -0
  20. package/dist/{chunk-U62ZECSQ.js → chunk-PNBH3RAX.js} +2 -2
  21. package/dist/chunk-QDEKU5RL.js +75 -0
  22. package/dist/chunk-QYP5HQV4.js +438 -0
  23. package/dist/{chunk-3WOUSUW7.js → chunk-RAZQTQK2.js} +1353 -1134
  24. package/dist/chunk-S4QKTRGJ.js +343 -0
  25. package/dist/{chunk-ARFK3RZW.js → chunk-TPT6HP4H.js} +1 -1
  26. package/dist/chunk-TVQOONDR.js +75 -0
  27. package/dist/{chunk-5ZUU23XF.js → chunk-U6BUT37A.js} +286 -33
  28. package/dist/chunk-VZXXJKSQ.js +314 -0
  29. package/dist/{chunk-26NRL3HX.js → chunk-XFXOSPYH.js} +3 -2
  30. package/dist/{chunk-N2XYX4ZA.js → chunk-XKFPU2FA.js} +13 -2
  31. package/dist/{chunk-FUUYYDVG.js → chunk-YB2574C6.js} +2841 -4507
  32. package/dist/cli/dev.js +4 -3
  33. package/dist/cli/index.js +4 -3
  34. package/dist/{config-BMXSDOZB.js → config-GIFCEJDU.js} +11 -6
  35. package/dist/events-G6CKHDNZ.js +114 -0
  36. package/dist/folding-O52L65VN.js +37 -0
  37. package/dist/{init-PLRTRSDC.js → init-MKTUIGZU.js} +46 -13
  38. package/dist/orchestrator-UGXX7JRQ.js +39 -0
  39. package/dist/package.json +9 -2
  40. package/dist/{paths-4BPXWB2W.js → paths-MM76AWYB.js} +2 -2
  41. package/dist/processor-KLHKDILM.js +236 -0
  42. package/dist/{project-creator-CWPKRIKI.js → project-creator-KAQXMZCT.js} +9 -8
  43. package/dist/projects-GSMMEVSO.js +19 -0
  44. package/dist/{protocol-BFFz3WM0.d.ts → protocol-BZ6OIASP.d.ts} +71 -50
  45. package/dist/protocol-WQENDK72.js +88 -0
  46. package/dist/{provider-ETVY64A2.js → provider-4KTWU6IV.js} +41 -9
  47. package/dist/runtime-config-2XJJIMSC.js +9 -0
  48. package/dist/{serve-TL34LE77.js → serve-UK6B6G2O.js} +38 -18
  49. package/dist/server/index.d.ts +81 -27
  50. package/dist/server/index.js +19 -11
  51. package/dist/{settings-D5O3TMLD.js → settings-NCLN4JDZ.js} +4 -4
  52. package/dist/shared/index.d.ts +2 -2
  53. package/dist/shared/index.js +1 -1
  54. package/dist/tools-SGBQE736.js +48 -0
  55. package/dist/vision-fallback-63VPBSEN.js +22 -0
  56. package/dist/web/assets/{index-DHmZmhnR.css → index-BsaaC0X0.css} +32 -1
  57. package/dist/web/assets/index-CvO7QkGh.js +141 -0
  58. package/dist/web/index.html +2 -2
  59. package/dist/web/sounds/typing.mp3 +0 -0
  60. package/dist/workflow-defaults/default.workflow.json +1 -1
  61. package/package.json +9 -2
  62. package/dist/chunk-L4XDA3V7.js +0 -218
  63. package/dist/tools-QMIIDJTV.js +0 -48
  64. package/dist/web/assets/index-CAD7Iv_d.js +0 -122
@@ -24,8 +24,8 @@ Options:
24
24
  `);
25
25
  }
26
26
  async function runConfig(mode) {
27
- const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-BMXSDOZB.js");
28
- const { getGlobalConfigPath } = await import("./paths-4BPXWB2W.js");
27
+ const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-GIFCEJDU.js");
28
+ const { getGlobalConfigPath } = await import("./paths-MM76AWYB.js");
29
29
  const config = await loadGlobalConfig(mode);
30
30
  const configPath = getGlobalConfigPath(mode);
31
31
  const activeProvider = getActiveProvider(config);
@@ -75,8 +75,8 @@ async function runCli(options) {
75
75
  const [command] = positionals;
76
76
  switch (command) {
77
77
  case "init": {
78
- const { runInitWithSelect } = await import("./init-PLRTRSDC.js");
79
- const { loadGlobalConfig, getActiveProvider } = await import("./config-BMXSDOZB.js");
78
+ const { runInitWithSelect } = await import("./init-MKTUIGZU.js");
79
+ const { loadGlobalConfig, getActiveProvider } = await import("./config-GIFCEJDU.js");
80
80
  const config = await loadGlobalConfig(mode);
81
81
  const activeProvider = getActiveProvider(config);
82
82
  if (activeProvider) {
@@ -91,17 +91,17 @@ async function runCli(options) {
91
91
  break;
92
92
  }
93
93
  case "provider": {
94
- const { runProviderCommand } = await import("./provider-ETVY64A2.js");
94
+ const { runProviderCommand } = await import("./provider-4KTWU6IV.js");
95
95
  const [, subcommand] = positionals;
96
96
  await runProviderCommand(mode, subcommand);
97
97
  break;
98
98
  }
99
99
  default: {
100
- const { configFileExists } = await import("./config-BMXSDOZB.js");
100
+ const { configFileExists } = await import("./config-GIFCEJDU.js");
101
101
  const configExists = await configFileExists(mode);
102
102
  if (!configExists) {
103
103
  console.log("Welcome to OpenFox!\n");
104
- const { trySmartDefaults, saveGlobalConfig, addProvider } = await import("./config-BMXSDOZB.js");
104
+ const { trySmartDefaults, saveGlobalConfig, addProvider } = await import("./config-GIFCEJDU.js");
105
105
  const detected = await trySmartDefaults(mode);
106
106
  if (detected) {
107
107
  console.log(`\u2713 Found ${detected.backend} (${detected.model}) at ${detected.url}`);
@@ -116,20 +116,20 @@ async function runCli(options) {
116
116
  name: "Default",
117
117
  url: detected.url,
118
118
  backend: detected.backend,
119
- maxContext: 2e5,
119
+ models: [],
120
120
  isActive: true
121
121
  });
122
- const { setDefaultModelSelection } = await import("./config-BMXSDOZB.js");
122
+ const { setDefaultModelSelection } = await import("./config-GIFCEJDU.js");
123
123
  const finalConfig = setDefaultModelSelection(configWithProvider, configWithProvider.providers[0].id, detected.model);
124
124
  await saveGlobalConfig(mode, finalConfig);
125
125
  console.log("Configuration saved!\n");
126
126
  } else {
127
127
  console.log("\u2717 No LLM server detected\n");
128
- const { runInitWithSelect } = await import("./init-PLRTRSDC.js");
128
+ const { runInitWithSelect } = await import("./init-MKTUIGZU.js");
129
129
  await runInitWithSelect(mode);
130
130
  }
131
131
  }
132
- const { runServe } = await import("./serve-TL34LE77.js");
132
+ const { runServe } = await import("./serve-UK6B6G2O.js");
133
133
  await runServe({
134
134
  mode,
135
135
  port: values.port ? parseInt(values.port) : void 0,
@@ -142,4 +142,4 @@ async function runCli(options) {
142
142
  export {
143
143
  runCli
144
144
  };
145
- //# sourceMappingURL=chunk-YMUIRZUI.js.map
145
+ //# sourceMappingURL=chunk-2TBNG7CS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-U62ZECSQ.js";
3
+ } from "./chunk-PNBH3RAX.js";
4
4
 
5
5
  // src/server/db/index.ts
6
6
  import Database from "better-sqlite3";
@@ -83,6 +83,10 @@ function runMigrations(db2) {
83
83
  logger.info("Migrating sessions table: adding workflow_phase column");
84
84
  db2.exec(`ALTER TABLE sessions ADD COLUMN workflow_phase TEXT NOT NULL DEFAULT 'plan'`);
85
85
  }
86
+ if (!columnNames.includes("danger_level")) {
87
+ logger.info("Migrating sessions table: adding danger_level column");
88
+ db2.exec(`ALTER TABLE sessions ADD COLUMN danger_level TEXT NOT NULL DEFAULT 'normal'`);
89
+ }
86
90
  db2.exec(`
87
91
  CREATE TABLE IF NOT EXISTS settings (
88
92
  key TEXT PRIMARY KEY,
@@ -122,6 +126,29 @@ function runMigrations(db2) {
122
126
  logger.info("Migrating sessions table: adding provider_model column");
123
127
  db2.exec(`ALTER TABLE sessions ADD COLUMN provider_model TEXT`);
124
128
  }
129
+ if (!columnNames.includes("message_count")) {
130
+ logger.info("Migrating sessions table: adding message_count column");
131
+ db2.exec(`ALTER TABLE sessions ADD COLUMN message_count INTEGER NOT NULL DEFAULT 0`);
132
+ logger.info("Backfilling message counts");
133
+ const backfillResult = db2.prepare(`
134
+ UPDATE sessions
135
+ SET message_count = (
136
+ SELECT COALESCE(
137
+ -- First try: get count from latest snapshot
138
+ (SELECT json_array_length(json_extract(
139
+ (SELECT payload FROM events WHERE session_id = sessions.id AND event_type = 'turn.snapshot' ORDER BY seq DESC LIMIT 1),
140
+ '$.messages'
141
+ ))),
142
+ -- Fallback: count message.start events for user/assistant roles
143
+ (SELECT COUNT(*) FROM events e
144
+ WHERE e.session_id = sessions.id
145
+ AND e.event_type = 'message.start'
146
+ AND json_extract(e.payload, '$.role') IN ('user', 'assistant'))
147
+ )
148
+ )
149
+ `).run();
150
+ logger.info("Backfilled message counts", { count: backfillResult.changes });
151
+ }
125
152
  logger.info("Database migrations completed");
126
153
  }
127
154
 
@@ -129,4 +156,4 @@ export {
129
156
  initDatabase,
130
157
  getDatabase
131
158
  };
132
- //# sourceMappingURL=chunk-4Z2OZ6NX.js.map
159
+ //# sourceMappingURL=chunk-3EHGGBWE.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  detectBackend,
3
3
  detectModel
4
- } from "./chunk-5ZUU23XF.js";
4
+ } from "./chunk-U6BUT37A.js";
5
5
  import {
6
6
  getGlobalConfigPath
7
- } from "./chunk-AF7XS4OI.js";
7
+ } from "./chunk-5YQAXUKO.js";
8
8
 
9
9
  // src/cli/config.ts
10
10
  import { z } from "zod";
@@ -45,17 +45,24 @@ async function configFileExists(mode) {
45
45
  }
46
46
  }
47
47
  var backendSchema = z.enum(["auto", "vllm", "sglang", "ollama", "llamacpp", "openai", "anthropic", "unknown"]);
48
+ var modelConfigSchema = z.object({
49
+ id: z.string(),
50
+ contextWindow: z.number(),
51
+ source: z.enum(["backend", "user", "default"])
52
+ });
48
53
  var providerSchema = z.object({
49
54
  id: z.string(),
50
55
  name: z.string(),
51
56
  url: z.string(),
52
57
  backend: backendSchema,
53
58
  apiKey: z.string().optional(),
54
- maxContext: z.number().optional(),
59
+ models: z.array(modelConfigSchema).default([]),
55
60
  isActive: z.boolean(),
56
61
  createdAt: z.string(),
57
62
  // Deprecated: model field kept for migration, will be removed after migration
58
- model: z.string().optional()
63
+ model: z.string().optional(),
64
+ // Deprecated: maxContext kept for migration
65
+ maxContext: z.number().optional()
59
66
  });
60
67
  var serverSchema = z.object({
61
68
  port: z.number().default(10369),
@@ -63,7 +70,7 @@ var serverSchema = z.object({
63
70
  openBrowser: z.boolean().default(true)
64
71
  });
65
72
  var loggingSchema = z.object({
66
- level: z.enum(["debug", "info", "warn", "error"]).default("info")
73
+ level: z.enum(["debug", "info", "warn", "error"]).default("error")
67
74
  });
68
75
  var databaseSchema = z.object({
69
76
  path: z.string().default("")
@@ -71,16 +78,34 @@ var databaseSchema = z.object({
71
78
  var workspaceSchema = z.object({
72
79
  workdir: z.string().default(process.cwd())
73
80
  });
81
+ var visionFallbackSchema = z.object({
82
+ enabled: z.boolean().default(false),
83
+ url: z.string().default("http://localhost:11434"),
84
+ model: z.string().default("qwen3-vl:2b"),
85
+ timeout: z.number().default(120)
86
+ });
87
+ var defaultVisionFallback = { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 };
74
88
  var configSchema = z.object({
75
89
  providers: z.array(providerSchema).default([]),
76
90
  defaultModelSelection: z.string().optional(),
77
91
  activeProviderId: z.string().optional(),
78
92
  activeWorkflowId: z.string().optional(),
79
93
  server: serverSchema.default({ port: 10369, host: "127.0.0.1", openBrowser: true }),
80
- logging: loggingSchema.default({ level: "info" }),
94
+ logging: loggingSchema.default({ level: "error" }),
81
95
  database: databaseSchema.default({ path: "" }),
82
- workspace: workspaceSchema.default(() => ({ workdir: process.cwd() }))
83
- });
96
+ workspace: workspaceSchema.default(() => ({ workdir: process.cwd() })),
97
+ visionFallback: visionFallbackSchema.optional()
98
+ }).transform((data) => ({
99
+ providers: data.providers ?? [],
100
+ defaultModelSelection: data.defaultModelSelection,
101
+ activeProviderId: data.activeProviderId,
102
+ activeWorkflowId: data.activeWorkflowId,
103
+ server: data.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
104
+ logging: data.logging ?? { level: "error" },
105
+ database: data.database ?? { path: "" },
106
+ workspace: data.workspace ?? { workdir: process.cwd() },
107
+ visionFallback: data.visionFallback ?? defaultVisionFallback
108
+ }));
84
109
  var oldLlmSchema = z.object({
85
110
  url: z.string().url().default("http://localhost:8000/v1"),
86
111
  model: z.string().default("auto"),
@@ -92,26 +117,44 @@ var oldLlmSchema = z.object({
92
117
  var oldConfigSchema = z.object({
93
118
  llm: oldLlmSchema,
94
119
  server: serverSchema.default({ port: 10369, host: "127.0.0.1", openBrowser: true }),
95
- logging: loggingSchema.default({ level: "info" }),
120
+ logging: loggingSchema.default({ level: "error" }),
96
121
  database: databaseSchema.default({ path: "" })
97
122
  });
123
+ function getVisionFallback(config) {
124
+ return config.visionFallback ?? getDefaultVisionFallback();
125
+ }
98
126
  function migrateConfig(raw) {
99
127
  if (typeof raw === "object" && raw !== null && "providers" in raw) {
100
128
  const obj = raw;
101
- if (obj.defaultModelSelection) {
102
- const providers2 = obj.providers.map((p) => {
103
- const { model, ...rest } = p;
104
- return rest;
105
- });
106
- return configSchema.parse({
107
- ...obj,
108
- providers: providers2
109
- });
110
- }
129
+ let migrationOccurred = false;
111
130
  const providers = obj.providers.map((p) => {
112
- const { model, ...rest } = p;
113
- return rest;
131
+ const { model, maxContext, models: existingModels, ...rest } = p;
132
+ let models = existingModels ?? [];
133
+ if (maxContext !== void 0 && (existingModels === void 0 || existingModels.length === 0)) {
134
+ migrationOccurred = true;
135
+ models = [{
136
+ id: model ?? "auto",
137
+ contextWindow: maxContext,
138
+ source: "user"
139
+ }];
140
+ }
141
+ return {
142
+ ...rest,
143
+ models
144
+ };
114
145
  });
146
+ if (migrationOccurred) {
147
+ console.warn("Migrating legacy maxContext to model-specific config");
148
+ }
149
+ if (obj.defaultModelSelection) {
150
+ return {
151
+ config: configSchema.parse({
152
+ ...obj,
153
+ providers
154
+ }),
155
+ migrated: migrationOccurred
156
+ };
157
+ }
115
158
  let defaultModelSelection;
116
159
  if (obj.activeProviderId) {
117
160
  const activeProvider = obj.providers.find((p) => p.id === obj.activeProviderId);
@@ -121,55 +164,88 @@ function migrateConfig(raw) {
121
164
  defaultModelSelection = `${obj.activeProviderId}/auto`;
122
165
  }
123
166
  }
124
- return configSchema.parse({
125
- ...obj,
126
- providers,
127
- defaultModelSelection
128
- });
167
+ return {
168
+ config: configSchema.parse({
169
+ ...obj,
170
+ providers,
171
+ defaultModelSelection
172
+ }),
173
+ migrated: migrationOccurred
174
+ };
129
175
  }
130
176
  if (typeof raw === "object" && raw !== null && "llm" in raw) {
131
177
  const oldConfig = oldConfigSchema.parse(raw);
132
178
  const providerId = randomUUID();
179
+ const models = [{
180
+ id: oldConfig.llm.model || "auto",
181
+ contextWindow: oldConfig.llm.maxContext,
182
+ source: "user"
183
+ }];
133
184
  const provider = {
134
185
  id: providerId,
135
186
  name: "Default",
136
187
  url: oldConfig.llm.url,
137
188
  backend: oldConfig.llm.backend,
138
189
  apiKey: oldConfig.llm.apiKey,
139
- maxContext: oldConfig.llm.maxContext,
190
+ models,
140
191
  isActive: true,
141
192
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
142
193
  };
143
194
  const model = oldConfig.llm.model || "auto";
144
195
  return {
145
- providers: [provider],
146
- defaultModelSelection: `${providerId}/${model}`,
147
- server: oldConfig.server,
148
- logging: oldConfig.logging,
149
- database: oldConfig.database,
150
- workspace: { workdir: process.cwd() }
196
+ config: configSchema.parse({
197
+ providers: [provider],
198
+ defaultModelSelection: `${providerId}/${model}`,
199
+ server: oldConfig.server,
200
+ logging: oldConfig.logging,
201
+ database: oldConfig.database,
202
+ workspace: { workdir: process.cwd() }
203
+ }),
204
+ migrated: true
151
205
  };
152
206
  }
153
- return configSchema.parse(raw);
207
+ return {
208
+ config: configSchema.parse(raw),
209
+ migrated: false
210
+ };
154
211
  }
155
212
  async function loadGlobalConfig(mode) {
156
213
  const configPath = getGlobalConfigPath(mode);
157
214
  try {
158
215
  const content = await readFile(configPath, "utf-8");
159
216
  const parsed = JSON.parse(content);
160
- return migrateConfig(parsed);
217
+ const { config, migrated } = migrateConfig(parsed);
218
+ if (migrated) {
219
+ await saveGlobalConfig(mode, config);
220
+ }
221
+ return config;
161
222
  } catch {
162
223
  return configSchema.parse({});
163
224
  }
164
225
  }
226
+ function getDefaultVisionFallback() {
227
+ return { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 };
228
+ }
165
229
  async function saveGlobalConfig(mode, config) {
166
230
  const configPath = getGlobalConfigPath(mode);
231
+ const fullConfig = {
232
+ providers: config.providers ?? [],
233
+ defaultModelSelection: config.defaultModelSelection,
234
+ activeProviderId: config.activeProviderId,
235
+ activeWorkflowId: config.activeWorkflowId,
236
+ server: config.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
237
+ logging: config.logging ?? { level: "error" },
238
+ database: config.database ?? { path: "" },
239
+ workspace: config.workspace ?? { workdir: process.cwd() },
240
+ visionFallback: config.visionFallback ?? { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
241
+ };
167
242
  await mkdir(dirname(configPath), { recursive: true });
168
- await writeFile(configPath, JSON.stringify(config, null, 2));
243
+ await writeFile(configPath, JSON.stringify(fullConfig, null, 2));
169
244
  }
170
245
  function getActiveProvider(config) {
171
246
  if (config.defaultModelSelection) {
172
- const [providerId] = config.defaultModelSelection.split("/");
247
+ const slashIndex = config.defaultModelSelection.indexOf("/");
248
+ const providerId = slashIndex === -1 ? config.defaultModelSelection : config.defaultModelSelection.substring(0, slashIndex);
173
249
  return config.providers?.find((p) => p.id === providerId);
174
250
  }
175
251
  if (!config.activeProviderId) return void 0;
@@ -177,17 +253,21 @@ function getActiveProvider(config) {
177
253
  }
178
254
  function getDefaultModel(config) {
179
255
  if (!config.defaultModelSelection) return void 0;
180
- const parts = config.defaultModelSelection.split("/");
181
- return parts[1] ?? "auto";
256
+ const slashIndex = config.defaultModelSelection.indexOf("/");
257
+ return slashIndex === -1 ? "auto" : config.defaultModelSelection.substring(slashIndex + 1);
182
258
  }
183
259
  function setDefaultModelSelection(config, providerId, model) {
184
260
  const defaultModelSelection = `${providerId}/${model}`;
185
261
  return {
186
- ...config,
262
+ providers: config.providers?.map((p) => ({ ...p, isActive: p.id === providerId })) ?? [],
187
263
  defaultModelSelection,
188
264
  activeProviderId: providerId,
189
- // Keep for backwards compatibility
190
- providers: config.providers?.map((p) => ({ ...p, isActive: p.id === providerId })) ?? []
265
+ activeWorkflowId: config.activeWorkflowId,
266
+ server: config.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
267
+ logging: config.logging ?? { level: "error" },
268
+ database: config.database ?? { path: "" },
269
+ workspace: config.workspace ?? { workdir: process.cwd() },
270
+ visionFallback: config.visionFallback ?? { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
191
271
  };
192
272
  }
193
273
  function addProvider(config, provider) {
@@ -196,16 +276,20 @@ function addProvider(config, provider) {
196
276
  id: randomUUID(),
197
277
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
198
278
  };
199
- const shouldActivate = provider.isActive || config.providers?.length === 0;
279
+ const shouldActivate = provider.isActive || (config.providers?.length ?? 0) === 0;
200
280
  return {
201
- ...config,
202
281
  providers: [
203
282
  ...(config.providers ?? []).map((p) => shouldActivate ? { ...p, isActive: false } : p),
204
283
  { ...newProvider, isActive: shouldActivate }
205
284
  ],
206
285
  defaultModelSelection: shouldActivate ? `${newProvider.id}/auto` : config.defaultModelSelection,
207
286
  activeProviderId: shouldActivate ? newProvider.id : config.activeProviderId,
208
- workspace: config.workspace ?? { workdir: process.cwd() }
287
+ activeWorkflowId: config.activeWorkflowId,
288
+ server: config.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
289
+ logging: config.logging ?? { level: "error" },
290
+ database: config.database ?? { path: "" },
291
+ workspace: config.workspace ?? { workdir: process.cwd() },
292
+ visionFallback: config.visionFallback ?? { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
209
293
  };
210
294
  }
211
295
  function removeProvider(config, providerId) {
@@ -213,7 +297,8 @@ function removeProvider(config, providerId) {
213
297
  const filtered = currentProviders.filter((p) => p.id !== providerId);
214
298
  let newDefaultModelSelection = config.defaultModelSelection;
215
299
  if (config.defaultModelSelection) {
216
- const [selectedProviderId] = config.defaultModelSelection.split("/");
300
+ const slashIndex = config.defaultModelSelection.indexOf("/");
301
+ const selectedProviderId = slashIndex === -1 ? config.defaultModelSelection : config.defaultModelSelection.substring(0, slashIndex);
217
302
  if (selectedProviderId === providerId) {
218
303
  newDefaultModelSelection = filtered.length > 0 ? `${filtered[0].id}/auto` : void 0;
219
304
  }
@@ -221,19 +306,42 @@ function removeProvider(config, providerId) {
221
306
  const wasActive = config.activeProviderId === providerId;
222
307
  const newActiveId = wasActive && filtered.length > 0 ? filtered[0].id : wasActive ? void 0 : config.activeProviderId;
223
308
  return {
224
- ...config,
225
309
  providers: filtered.map((p) => ({ ...p, isActive: p.id === newActiveId })),
226
310
  activeProviderId: newActiveId,
227
- defaultModelSelection: newDefaultModelSelection
311
+ defaultModelSelection: newDefaultModelSelection,
312
+ activeWorkflowId: config.activeWorkflowId,
313
+ server: config.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
314
+ logging: config.logging ?? { level: "error" },
315
+ database: config.database ?? { path: "" },
316
+ workspace: config.workspace ?? { workdir: process.cwd() },
317
+ visionFallback: config.visionFallback ?? { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
228
318
  };
229
319
  }
230
320
  function activateProvider(config, providerId) {
231
321
  const provider = config.providers?.find((p) => p.id === providerId);
232
- if (!provider) return config;
322
+ if (!provider) {
323
+ return {
324
+ providers: config.providers ?? [],
325
+ defaultModelSelection: config.defaultModelSelection,
326
+ activeProviderId: config.activeProviderId,
327
+ activeWorkflowId: config.activeWorkflowId,
328
+ server: config.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
329
+ logging: config.logging ?? { level: "error" },
330
+ database: config.database ?? { path: "" },
331
+ workspace: config.workspace ?? { workdir: process.cwd() },
332
+ visionFallback: config.visionFallback ?? { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
333
+ };
334
+ }
233
335
  return {
234
- ...config,
235
336
  providers: (config.providers ?? []).map((p) => ({ ...p, isActive: p.id === providerId })),
236
- activeProviderId: providerId
337
+ defaultModelSelection: config.defaultModelSelection,
338
+ activeProviderId: providerId,
339
+ activeWorkflowId: config.activeWorkflowId,
340
+ server: config.server ?? { port: 10369, host: "127.0.0.1", openBrowser: true },
341
+ logging: config.logging ?? { level: "error" },
342
+ database: config.database ?? { path: "" },
343
+ workspace: config.workspace ?? { workdir: process.cwd() },
344
+ visionFallback: config.visionFallback ?? { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
237
345
  };
238
346
  }
239
347
  function mergeConfigs(...configs) {
@@ -259,8 +367,10 @@ function mergeConfigs(...configs) {
259
367
  export {
260
368
  trySmartDefaults,
261
369
  configFileExists,
370
+ getVisionFallback,
262
371
  migrateConfig,
263
372
  loadGlobalConfig,
373
+ getDefaultVisionFallback,
264
374
  saveGlobalConfig,
265
375
  getActiveProvider,
266
376
  getDefaultModel,
@@ -270,4 +380,4 @@ export {
270
380
  activateProvider,
271
381
  mergeConfigs
272
382
  };
273
- //# sourceMappingURL=chunk-L475ND2Z.js.map
383
+ //# sourceMappingURL=chunk-3UQACISJ.js.map
@@ -3,6 +3,9 @@ import { homedir, platform } from "os";
3
3
  import { join } from "path";
4
4
  import { mkdir, access } from "fs/promises";
5
5
  function getGlobalConfigDir(mode) {
6
+ if (mode === "test") {
7
+ return join(process.cwd(), "e2e", ".openfox-test");
8
+ }
6
9
  const suffix = mode === "development" ? "-dev" : "";
7
10
  const home = homedir();
8
11
  switch (platform()) {
@@ -52,4 +55,4 @@ export {
52
55
  getSkillsDir,
53
56
  ensureDataDirExists
54
57
  };
55
- //# sourceMappingURL=chunk-AF7XS4OI.js.map
58
+ //# sourceMappingURL=chunk-5YQAXUKO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getDatabase
3
- } from "./chunk-4Z2OZ6NX.js";
3
+ } from "./chunk-3EHGGBWE.js";
4
4
 
5
5
  // src/server/db/settings.ts
6
6
  var SETTINGS_KEYS = {
@@ -43,4 +43,4 @@ export {
43
43
  deleteSetting,
44
44
  getAllSettings
45
45
  };
46
- //# sourceMappingURL=chunk-MTX5Y3V3.js.map
46
+ //# sourceMappingURL=chunk-7IOZFJBW.js.map