openfox 1.6.14 → 1.6.16

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 (39) hide show
  1. package/dist/{auto-compaction-C4PMBB24.js → auto-compaction-2RKC6DC2.js} +5 -5
  2. package/dist/{chat-handler-US3QOOE4.js → chat-handler-IY2XVOO6.js} +26 -56
  3. package/dist/{chunk-7HLRI6JM.js → chunk-2KP34IDL.js} +161 -204
  4. package/dist/{chunk-N25QUEPO.js → chunk-4OI4VPZ6.js} +434 -812
  5. package/dist/{chunk-XFXOSPYH.js → chunk-55N6FAAZ.js} +1 -1
  6. package/dist/{chunk-5GE3QJSZ.js → chunk-6L3X7T4K.js} +82 -160
  7. package/dist/{chunk-QDEKU5RL.js → chunk-F54ZJN4X.js} +38 -2
  8. package/dist/{chunk-UL4JYQKK.js → chunk-IN5EP4ZB.js} +2 -2
  9. package/dist/{chunk-OOIRCXAY.js → chunk-KOUMYBYM.js} +53 -112
  10. package/dist/{chunk-3JU6H6A4.js → chunk-NN65D5SI.js} +426 -448
  11. package/dist/{chunk-3XZ23PXM.js → chunk-OVLFEBRR.js} +76 -92
  12. package/dist/chunk-SN7OBEVL.js +44 -0
  13. package/dist/{chunk-PCOG5FQD.js → chunk-T5PBG2PE.js} +6 -6
  14. package/dist/{chunk-H22VHHQ4.js → chunk-U54QVKL2.js} +4 -4
  15. package/dist/{chunk-J2DHVXRX.js → chunk-ZDNXCVW4.js} +2 -2
  16. package/dist/cli/dev.js +1 -1
  17. package/dist/cli/index.js +1 -1
  18. package/dist/{config-DEM6ASQM.js → config-67AX6CNS.js} +5 -5
  19. package/dist/{events-VDAC7E7K.js → events-2ETDOE5B.js} +3 -3
  20. package/dist/{folding-NDFQCISZ.js → folding-M7FMUBOL.js} +6 -4
  21. package/dist/{orchestrator-KZI7B5IS.js → orchestrator-U5VCLBYQ.js} +6 -6
  22. package/dist/package.json +13 -3
  23. package/dist/{processor-KBSF2HFD.js → processor-VUEJM73B.js} +24 -49
  24. package/dist/{provider-K3PHZO27.js → provider-DKGBQHUS.js} +7 -7
  25. package/dist/{serve-DERN4AB6.js → serve-GOLQMQFA.js} +12 -12
  26. package/dist/server/index.d.ts +2 -0
  27. package/dist/server/index.js +10 -10
  28. package/dist/{tools-PLVK22CF.js → tools-55YICCVH.js} +5 -5
  29. package/dist/{vision-fallback-NOC3YYIB.js → vision-fallback-ADYRFFD4.js} +2 -2
  30. package/dist/web/assets/index-CZBXRYpK.js +150 -0
  31. package/dist/web/assets/{index-MCWDS5UQ.css → index-D4V7Gtvt.css} +1 -1
  32. package/dist/web/index.html +2 -2
  33. package/dist/web/sw.js +1 -1
  34. package/package.json +13 -3
  35. package/dist/web/assets/index-Ccy2Csbl.js +0 -150
  36. /package/dist/{command-defaults → server/commands/defaults}/commit-push.command.md +0 -0
  37. /package/dist/{command-defaults → server/commands/defaults}/init.command.md +0 -0
  38. /package/dist/{command-defaults → server/commands/defaults}/test-ui.command.md +0 -0
  39. /package/dist/{skill-defaults → server/skills/defaults}/browser.skill.md +0 -0
@@ -114,4 +114,4 @@ async function* streamWithSegments(client, request) {
114
114
  export {
115
115
  streamWithSegments
116
116
  };
117
- //# sourceMappingURL=chunk-XFXOSPYH.js.map
117
+ //# sourceMappingURL=chunk-55N6FAAZ.js.map
@@ -2,8 +2,10 @@ import {
2
2
  buildContextMessagesFromEventHistory,
3
3
  buildMessagesFromStoredEvents,
4
4
  foldContextState,
5
- foldSessionState
6
- } from "./chunk-7HLRI6JM.js";
5
+ foldSessionState,
6
+ spreadOptionalMessageFields,
7
+ stripPromptContextMessages
8
+ } from "./chunk-2KP34IDL.js";
7
9
  import {
8
10
  getDatabase
9
11
  } from "./chunk-3EHGGBWE.js";
@@ -15,6 +17,61 @@ import {
15
17
  } from "./chunk-PNBH3RAX.js";
16
18
 
17
19
  // src/server/events/store.ts
20
+ function createEventIterator(state, subscriber) {
21
+ return {
22
+ [Symbol.asyncIterator]() {
23
+ return this;
24
+ },
25
+ async next() {
26
+ if (state.closed) {
27
+ return { value: void 0, done: true };
28
+ }
29
+ const queued = state.queue.shift();
30
+ if (queued) {
31
+ return { value: queued, done: false };
32
+ }
33
+ return new Promise((resolve) => {
34
+ state.resolveNext = resolve;
35
+ });
36
+ },
37
+ async return() {
38
+ state.closed = true;
39
+ subscriber.closed = true;
40
+ return { value: void 0, done: true };
41
+ }
42
+ };
43
+ }
44
+ function createIteratorState() {
45
+ const state = {
46
+ closed: false,
47
+ queue: [],
48
+ resolveNext: null,
49
+ closeIterator: () => {
50
+ state.closed = true;
51
+ if (state.resolveNext) {
52
+ state.resolveNext({ value: void 0, done: true });
53
+ state.resolveNext = null;
54
+ }
55
+ }
56
+ };
57
+ return state;
58
+ }
59
+ function createSubscriber(state, id) {
60
+ return {
61
+ ...id,
62
+ callback: (event) => {
63
+ if (state.closed) return;
64
+ if (state.resolveNext) {
65
+ state.resolveNext({ value: event, done: false });
66
+ state.resolveNext = null;
67
+ } else {
68
+ state.queue.push(event);
69
+ }
70
+ },
71
+ close: state.closeIterator,
72
+ closed: false
73
+ };
74
+ }
18
75
  var EventStore = class {
19
76
  db;
20
77
  subscribers = /* @__PURE__ */ new Map();
@@ -176,30 +233,8 @@ var EventStore = class {
176
233
  * Returns an async iterator that yields events and an unsubscribe function
177
234
  */
178
235
  subscribe(sessionId, fromSeq) {
179
- const queue = [];
180
- let resolveNext = null;
181
- let closed = false;
182
- const closeIterator = () => {
183
- closed = true;
184
- if (resolveNext) {
185
- resolveNext({ value: void 0, done: true });
186
- resolveNext = null;
187
- }
188
- };
189
- const subscriber = {
190
- sessionId,
191
- callback: (event) => {
192
- if (closed) return;
193
- if (resolveNext) {
194
- resolveNext({ value: event, done: false });
195
- resolveNext = null;
196
- } else {
197
- queue.push(event);
198
- }
199
- },
200
- close: closeIterator,
201
- closed: false
202
- };
236
+ const state = createIteratorState();
237
+ const subscriber = createSubscriber(state, { sessionId });
203
238
  let sessionSubs = this.subscribers.get(sessionId);
204
239
  if (!sessionSubs) {
205
240
  sessionSubs = /* @__PURE__ */ new Set();
@@ -208,36 +243,14 @@ var EventStore = class {
208
243
  sessionSubs.add(subscriber);
209
244
  if (fromSeq !== void 0) {
210
245
  const replayEvents = this.getEvents(sessionId, fromSeq);
211
- queue.push(...replayEvents);
246
+ state.queue.push(...replayEvents);
212
247
  }
213
- const iterator = {
214
- [Symbol.asyncIterator]() {
215
- return this;
216
- },
217
- async next() {
218
- if (closed) {
219
- return { value: void 0, done: true };
220
- }
221
- const queued = queue.shift();
222
- if (queued) {
223
- return { value: queued, done: false };
224
- }
225
- return new Promise((resolve) => {
226
- resolveNext = resolve;
227
- });
228
- },
229
- async return() {
230
- closed = true;
231
- subscriber.closed = true;
232
- return { value: void 0, done: true };
233
- }
234
- };
235
248
  const unsubscribe = () => {
236
249
  subscriber.closed = true;
237
- closeIterator();
250
+ state.closeIterator();
238
251
  sessionSubs?.delete(subscriber);
239
252
  };
240
- return { iterator, unsubscribe };
253
+ return { iterator: createEventIterator(state, subscriber), unsubscribe };
241
254
  }
242
255
  notifySubscribers(sessionId, event) {
243
256
  const sessionSubs = this.subscribers.get(sessionId);
@@ -262,60 +275,16 @@ var EventStore = class {
262
275
  * Returns an async iterator that yields events and an unsubscribe function
263
276
  */
264
277
  subscribeAll() {
265
- const queue = [];
266
- let resolveNext = null;
267
- let closed = false;
268
- const closeIterator = () => {
269
- closed = true;
270
- if (resolveNext) {
271
- resolveNext({ value: void 0, done: true });
272
- resolveNext = null;
273
- }
274
- };
278
+ const state = createIteratorState();
275
279
  const wsId = ++this.globalSubscriberIdCounter;
276
- const subscriber = {
277
- wsId,
278
- callback: (event) => {
279
- if (closed) return;
280
- if (resolveNext) {
281
- resolveNext({ value: event, done: false });
282
- resolveNext = null;
283
- } else {
284
- queue.push(event);
285
- }
286
- },
287
- close: closeIterator,
288
- closed: false
289
- };
280
+ const subscriber = createSubscriber(state, { wsId });
290
281
  this.globalSubscribers.set(wsId, subscriber);
291
- const iterator = {
292
- [Symbol.asyncIterator]() {
293
- return this;
294
- },
295
- async next() {
296
- if (closed) {
297
- return { value: void 0, done: true };
298
- }
299
- const queued = queue.shift();
300
- if (queued) {
301
- return { value: queued, done: false };
302
- }
303
- return new Promise((resolve) => {
304
- resolveNext = resolve;
305
- });
306
- },
307
- async return() {
308
- closed = true;
309
- subscriber.closed = true;
310
- return { value: void 0, done: true };
311
- }
312
- };
313
282
  const unsubscribe = () => {
314
283
  subscriber.closed = true;
315
- closeIterator();
284
+ state.closeIterator();
316
285
  this.globalSubscribers.delete(wsId);
317
286
  };
318
- return { iterator, unsubscribe };
287
+ return { iterator: createEventIterator(state, subscriber), unsubscribe };
319
288
  }
320
289
  // --------------------------------------------------------------------------
321
290
  // Cleanup
@@ -417,25 +386,7 @@ var EventStore = class {
417
386
  const data = JSON.parse(row.payload);
418
387
  const messages = data.messages;
419
388
  if (!messages) continue;
420
- let lastAssistantIdx = -1;
421
- for (let i = messages.length - 1; i >= 0; i--) {
422
- const msg = messages[i];
423
- if (!msg) continue;
424
- if (msg.role === "assistant" && msg.promptContext) {
425
- lastAssistantIdx = i;
426
- break;
427
- }
428
- }
429
- let changed = false;
430
- for (let i = 0; i < messages.length; i++) {
431
- const msg = messages[i];
432
- if (!msg) continue;
433
- const pc = msg.promptContext;
434
- if (pc?.messages && pc.messages.length > 0 && i !== lastAssistantIdx) {
435
- pc.messages = [];
436
- changed = true;
437
- }
438
- }
389
+ const changed = stripPromptContextMessages(messages);
439
390
  if (changed) {
440
391
  updateStmt.run(JSON.stringify(data), row.id);
441
392
  strippedSnapshots++;
@@ -646,24 +597,7 @@ function listSessions() {
646
597
  FROM sessions s
647
598
  ORDER BY s.updated_at DESC
648
599
  `).all();
649
- return rows.map((row) => ({
650
- id: row.id,
651
- projectId: row.project_id,
652
- ...row.title ? { title: row.title } : {},
653
- workdir: row.workdir,
654
- mode: row.mode ?? "planner",
655
- phase: row.workflow_phase ?? "plan",
656
- isRunning: Boolean(row.is_running),
657
- providerId: row.provider_id ?? null,
658
- providerModel: row.provider_model ?? null,
659
- createdAt: row.created_at,
660
- updatedAt: row.updated_at,
661
- criteriaCount: 0,
662
- // Derived from events
663
- criteriaCompleted: 0,
664
- // Derived from events
665
- messageCount: row.message_count
666
- }));
600
+ return rows.map(mapSessionSummaryRow);
667
601
  }
668
602
  function listSessionsByProject(projectId, limit = 20, offset = 0) {
669
603
  const db = getDatabase();
@@ -687,7 +621,15 @@ function listSessionsByProject(projectId, limit = 20, offset = 0) {
687
621
  LIMIT ? OFFSET ?
688
622
  `).all(projectId, limit + 1, offset);
689
623
  const hasMore = rows.length > limit;
690
- const sessions = rows.slice(0, limit).map((row) => ({
624
+ const sessions = rows.slice(0, limit).map(mapSessionSummaryRow);
625
+ return { sessions, hasMore };
626
+ }
627
+ function deleteSession(id) {
628
+ const db = getDatabase();
629
+ db.prepare("DELETE FROM sessions WHERE id = ?").run(id);
630
+ }
631
+ function mapSessionSummaryRow(row) {
632
+ return {
691
633
  id: row.id,
692
634
  projectId: row.project_id,
693
635
  ...row.title ? { title: row.title } : {},
@@ -702,12 +644,7 @@ function listSessionsByProject(projectId, limit = 20, offset = 0) {
702
644
  criteriaCount: 0,
703
645
  criteriaCompleted: 0,
704
646
  messageCount: row.message_count
705
- }));
706
- return { sessions, hasMore };
707
- }
708
- function deleteSession(id) {
709
- const db = getDatabase();
710
- db.prepare("DELETE FROM sessions WHERE id = ?").run(id);
647
+ };
711
648
  }
712
649
 
713
650
  // src/server/events/session.ts
@@ -717,22 +654,7 @@ function toSnapshotMessage(message) {
717
654
  role: message.role,
718
655
  content: message.content,
719
656
  timestamp: new Date(message.timestamp).getTime(),
720
- ...message.thinkingContent !== void 0 && { thinkingContent: message.thinkingContent },
721
- ...message.toolCalls !== void 0 && { toolCalls: message.toolCalls },
722
- ...message.segments !== void 0 && { segments: message.segments },
723
- ...message.stats !== void 0 && { stats: message.stats },
724
- ...message.tokenCount !== void 0 && { tokenCount: message.tokenCount },
725
- ...message.isStreaming !== void 0 && { isStreaming: message.isStreaming },
726
- ...message.partial !== void 0 && { partial: message.partial },
727
- ...message.subAgentId !== void 0 && { subAgentId: message.subAgentId },
728
- ...message.subAgentType !== void 0 && { subAgentType: message.subAgentType },
729
- ...message.isSystemGenerated !== void 0 && { isSystemGenerated: message.isSystemGenerated },
730
- ...message.messageKind !== void 0 && { messageKind: message.messageKind },
731
- ...message.contextWindowId !== void 0 && { contextWindowId: message.contextWindowId },
732
- ...message.isCompactionSummary !== void 0 && { isCompactionSummary: message.isCompactionSummary },
733
- ...message.promptContext !== void 0 && { promptContext: message.promptContext },
734
- ...message.attachments !== void 0 && { attachments: message.attachments },
735
- ...message.metadata !== void 0 && { metadata: message.metadata }
657
+ ...spreadOptionalMessageFields(message)
736
658
  };
737
659
  }
738
660
  function getSessionState(sessionId, maxTokens) {
@@ -1144,4 +1066,4 @@ export {
1144
1066
  compactContext,
1145
1067
  getRecentUserPromptsForSession
1146
1068
  };
1147
- //# sourceMappingURL=chunk-5GE3QJSZ.js.map
1069
+ //# sourceMappingURL=chunk-6L3X7T4K.js.map
@@ -1,3 +1,13 @@
1
+ import {
2
+ updateSessionMetadata
3
+ } from "./chunk-6L3X7T4K.js";
4
+ import {
5
+ buildMessagesFromStoredEvents,
6
+ foldPendingConfirmations
7
+ } from "./chunk-2KP34IDL.js";
8
+ import {
9
+ createSessionStateMessage
10
+ } from "./chunk-NJWPSSRW.js";
1
11
  import {
2
12
  logger
3
13
  } from "./chunk-PNBH3RAX.js";
@@ -67,9 +77,35 @@ function needsNameGeneration(sessionTitle, messageCount) {
67
77
  }
68
78
  return false;
69
79
  }
80
+ function needsNameGenerationCheck(sessionId, sessionTitle, messageCount) {
81
+ const needsGeneration = needsNameGeneration(sessionTitle, messageCount);
82
+ logger.debug("Session name generation check", {
83
+ sessionId,
84
+ title: sessionTitle,
85
+ messageCount,
86
+ needsGeneration
87
+ });
88
+ return needsGeneration;
89
+ }
90
+ function applyGeneratedSessionName(sessionId, name, deps) {
91
+ updateSessionMetadata(sessionId, { title: name });
92
+ deps.eventStore.append(sessionId, {
93
+ type: "session.name_generated",
94
+ data: { name }
95
+ });
96
+ const updatedSession = deps.sessionManager.getSession(sessionId);
97
+ if (updatedSession) {
98
+ const events = deps.eventStore.getEvents(sessionId);
99
+ const messages = buildMessagesFromStoredEvents(events);
100
+ const pendingConfirmations = foldPendingConfirmations(events);
101
+ deps.broadcastForSession(sessionId, createSessionStateMessage(updatedSession, messages, pendingConfirmations));
102
+ }
103
+ }
70
104
 
71
105
  export {
72
106
  generateSessionName,
73
- needsNameGeneration
107
+ needsNameGeneration,
108
+ needsNameGenerationCheck,
109
+ applyGeneratedSessionName
74
110
  };
75
- //# sourceMappingURL=chunk-QDEKU5RL.js.map
111
+ //# sourceMappingURL=chunk-F54ZJN4X.js.map
@@ -29,7 +29,7 @@ function isVisionFallbackEnabled() {
29
29
  async function ensureVisionFallbackConfigLoaded() {
30
30
  if (configLoaded) return;
31
31
  try {
32
- const { loadGlobalConfig, getVisionFallback } = await import("./config-DEM6ASQM.js");
32
+ const { loadGlobalConfig, getVisionFallback } = await import("./config-67AX6CNS.js");
33
33
  const { getRuntimeConfig } = await import("./runtime-config-2XJJIMSC.js");
34
34
  const runtimeConfig = getRuntimeConfig();
35
35
  const mode = runtimeConfig.mode ?? "production";
@@ -135,4 +135,4 @@ export {
135
135
  describeImage,
136
136
  describeImageFromDataUrl
137
137
  };
138
- //# sourceMappingURL=chunk-UL4JYQKK.js.map
138
+ //# sourceMappingURL=chunk-IN5EP4ZB.js.map
@@ -4,40 +4,61 @@ import {
4
4
  detectBackend,
5
5
  detectModel,
6
6
  setLlmStatus
7
- } from "./chunk-3XZ23PXM.js";
7
+ } from "./chunk-OVLFEBRR.js";
8
8
  import {
9
9
  logger
10
10
  } from "./chunk-PNBH3RAX.js";
11
11
 
12
12
  // src/server/provider-manager.ts
13
- async function fetchAvailableModelsFromBackend(baseUrl, apiKey) {
14
- const url = baseUrl.includes("/v1") ? `${baseUrl}/models` : `${baseUrl}/v1/models`;
13
+ function normalizeModelId(s) {
14
+ return s.toLowerCase().replace(/[-_\s:.]+/g, "");
15
+ }
16
+ async function fetchModelsFromBackend(url, apiKey) {
17
+ const headers = { "Content-Type": "application/json" };
18
+ if (apiKey) {
19
+ headers["Authorization"] = `Bearer ${apiKey}`;
20
+ }
15
21
  try {
16
- const headers = {
17
- "Content-Type": "application/json"
18
- };
19
- if (apiKey) {
20
- headers["Authorization"] = `Bearer ${apiKey}`;
21
- }
22
- const response = await fetch(url, {
23
- method: "GET",
24
- headers,
25
- signal: AbortSignal.timeout(1e4)
26
- });
22
+ const response = await fetch(url, { method: "GET", headers, signal: AbortSignal.timeout(1e4) });
27
23
  if (!response.ok) {
28
24
  logger.debug("Failed to fetch models", { url, status: response.status });
29
25
  return [];
30
26
  }
31
27
  const data = await response.json();
32
28
  if (data.data && Array.isArray(data.data)) {
33
- return data.data.map((m) => m.id).filter(Boolean);
29
+ return data.data.map((m) => ({
30
+ id: m.id,
31
+ contextWindow: m.max_model_len ?? void 0
32
+ }));
34
33
  }
35
34
  return [];
36
35
  } catch (error) {
37
- logger.debug("Error fetching models from backend", { url, error: error instanceof Error ? error.message : String(error) });
36
+ logger.debug("Error fetching models", { url, error: error instanceof Error ? error.message : String(error) });
38
37
  return [];
39
38
  }
40
39
  }
40
+ function mergeModelsWithUserOverrides(backendModels, userModels) {
41
+ const normalizedUserIdMap = new Map(userModels.map((m) => [normalizeModelId(m.id), m]));
42
+ const updatedModels = backendModels.map((backendModel) => {
43
+ const existingUserModel = normalizedUserIdMap.get(normalizeModelId(backendModel.id));
44
+ if (existingUserModel) {
45
+ return { ...existingUserModel, id: backendModel.id };
46
+ }
47
+ return backendModel;
48
+ });
49
+ const normalizedBackendIds = new Set(backendModels.map((m) => normalizeModelId(m.id)));
50
+ for (const userModel of userModels) {
51
+ if (!normalizedBackendIds.has(normalizeModelId(userModel.id))) {
52
+ updatedModels.push(userModel);
53
+ }
54
+ }
55
+ return updatedModels;
56
+ }
57
+ async function fetchAvailableModelsFromBackend(baseUrl, apiKey) {
58
+ const url = baseUrl.includes("/v1") ? `${baseUrl}/models` : `${baseUrl}/v1/models`;
59
+ const models = await fetchModelsFromBackend(url, apiKey);
60
+ return models.map((m) => m.id);
61
+ }
41
62
  async function fetchModelsWithContext(baseUrl, apiKey, backend) {
42
63
  logger.info("fetchModelsWithContext called", { baseUrl, apiKey: !!apiKey, backend });
43
64
  if (backend === "ollama") {
@@ -45,44 +66,16 @@ async function fetchModelsWithContext(baseUrl, apiKey, backend) {
45
66
  return fetchOllamaModelsWithContext(baseUrl, apiKey);
46
67
  }
47
68
  const isOpenCodeGo = baseUrl.includes("opencode.ai/zen/go");
48
- let url;
49
- if (isOpenCodeGo) {
50
- url = baseUrl.replace("/zen/go", "/zen").replace(/\/v1$/, "") + "/v1/models";
51
- logger.info("OpenCode Go detected, using alternate models endpoint", { original: baseUrl, modelsUrl: url });
52
- } else {
53
- url = baseUrl.includes("/v1") ? `${baseUrl}/models` : `${baseUrl}/v1/models`;
54
- }
69
+ const url = isOpenCodeGo ? baseUrl.replace("/zen/go", "/zen").replace(/\/v1$/, "") + "/v1/models" : baseUrl.includes("/v1") ? `${baseUrl}/models` : `${baseUrl}/v1/models`;
55
70
  logger.info("Fetching models via /v1/models", { url });
56
- try {
57
- const headers = {
58
- "Content-Type": "application/json"
59
- };
60
- if (apiKey) {
61
- headers["Authorization"] = `Bearer ${apiKey}`;
62
- }
63
- const response = await fetch(url, {
64
- method: "GET",
65
- headers,
66
- signal: AbortSignal.timeout(1e4)
67
- });
68
- if (!response.ok) {
69
- logger.debug("Failed to fetch models with context", { url, status: response.status });
70
- return [];
71
- }
72
- const data = await response.json();
73
- if (data.data && Array.isArray(data.data)) {
74
- logger.info("Fetched models from /v1/models", { count: data.data.length, models: data.data.map((m) => m.id) });
75
- return data.data.map((m) => ({
76
- id: m.id,
77
- contextWindow: m.max_model_len ?? 2e5,
78
- source: m.max_model_len ? "backend" : "default"
79
- }));
80
- }
81
- return [];
82
- } catch (error) {
83
- logger.debug("Error fetching models with context", { url, error: error instanceof Error ? error.message : String(error) });
84
- return [];
85
- }
71
+ const models = await fetchModelsFromBackend(url, apiKey);
72
+ if (models.length === 0) return [];
73
+ logger.info("Fetched models from /v1/models", { count: models.length });
74
+ return models.map((m) => ({
75
+ id: m.id,
76
+ contextWindow: m.contextWindow ?? 2e5,
77
+ source: m.contextWindow ? "backend" : "default"
78
+ }));
86
79
  }
87
80
  async function fetchOllamaModelsWithContext(baseUrl, apiKey) {
88
81
  const tagsUrl = `${baseUrl}/api/tags`;
@@ -217,34 +210,10 @@ function createProviderManager(config) {
217
210
  const backend = provider.backend;
218
211
  logger.info("activateProvider fetching models", { providerId, providerName: provider.name, url, backend });
219
212
  const modelsWithContext = await fetchModelsWithContext(url, provider.apiKey, backend);
220
- const normalize = (s) => s.toLowerCase().replace(/[-_\s:.]+/g, "");
221
213
  const userModels = provider.models.filter((m) => m.source === "user");
222
- logger.debug("activateProvider", { providerId, backendModelsCount: modelsWithContext.length, userModelsCount: userModels.length, userModels: userModels.map((m) => ({ id: m.id, contextWindow: m.contextWindow })) });
214
+ logger.debug("activateProvider", { providerId, backendModelsCount: modelsWithContext.length, userModelsCount: userModels.length });
223
215
  if (modelsWithContext.length > 0) {
224
- const updatedModels = modelsWithContext.map((m) => {
225
- let existingModel = provider.models.find((pm) => pm.id === m.id);
226
- if (!existingModel) {
227
- const normalizedId = normalize(m.id);
228
- existingModel = provider.models.find((pm) => {
229
- if (pm.source !== "user") return false;
230
- return normalize(pm.id) === normalizedId;
231
- });
232
- }
233
- if (existingModel && existingModel.source === "user") {
234
- return { ...existingModel, id: m.id };
235
- }
236
- return m;
237
- });
238
- for (const userModel of userModels) {
239
- const normalizedUserId = normalize(userModel.id);
240
- const matchedInUpdated = updatedModels.some((m) => {
241
- const normalizedBackendId = normalize(m.id);
242
- return normalizedBackendId === normalizedUserId;
243
- });
244
- if (!matchedInUpdated) {
245
- updatedModels.push(userModel);
246
- }
247
- }
216
+ const updatedModels = mergeModelsWithUserOverrides(modelsWithContext, userModels);
248
217
  providers = providers.map((p) => p.id === providerId ? { ...p, models: updatedModels } : p);
249
218
  } else if (userModels.length > 0) {
250
219
  logger.debug("Backend unavailable during provider switch, preserving user models", { providerId, userModelsCount: userModels.length });
@@ -403,52 +372,24 @@ function createProviderManager(config) {
403
372
  const backend = provider.backend;
404
373
  logger.info("refreshProviderModels fetching models", { providerId, providerName: provider.name, url, backend });
405
374
  const modelsWithContext = await fetchModelsWithContext(url, provider.apiKey, backend);
406
- const normalize = (s) => s.toLowerCase().replace(/[-_\s:.]+/g, "");
407
375
  const userModels = provider.models.filter((m) => m.source === "user");
408
- const allModelsBefore = provider.models.map((m) => ({ id: m.id, contextWindow: m.contextWindow, source: m.source }));
409
- logger.info("refreshProviderModels", { providerId, userModelsCount: userModels.length, backendModelsCount: modelsWithContext.length, userModels: userModels.map((m) => ({ id: m.id, contextWindow: m.contextWindow, source: m.source })), allModelsBefore, backendModels: modelsWithContext });
376
+ logger.info("refreshProviderModels", { providerId, userModelsCount: userModels.length, backendModelsCount: modelsWithContext.length });
410
377
  if (modelsWithContext.length === 0) {
411
378
  setLlmStatus("disconnected");
412
379
  if (userModels.length > 0) {
413
380
  logger.debug("Backend unavailable, preserving user models", { providerId, userModels: userModels.map((m) => ({ id: m.id, contextWindow: m.contextWindow })) });
414
381
  providers = providers.map((p) => p.id === providerId ? { ...p, models: userModels } : p);
415
- const preservedProvider = providers.find((p) => p.id === providerId);
416
- logger.debug("After preservation", { providerId, models: preservedProvider?.models.map((m) => ({ id: m.id, contextWindow: m.contextWindow, source: m.source })) });
417
382
  return { success: true };
418
383
  }
419
384
  return { success: false, error: "No models returned from backend" };
420
385
  }
421
386
  setLlmStatus("connected");
422
- const updatedModels = modelsWithContext.map((m) => {
423
- let existingModel = provider.models.find((pm) => pm.id === m.id);
424
- if (!existingModel) {
425
- const normalizedId = normalize(m.id);
426
- existingModel = provider.models.find((pm) => {
427
- if (pm.source !== "user") return false;
428
- return normalize(pm.id) === normalizedId;
429
- });
430
- }
431
- if (existingModel && existingModel.source === "user") {
432
- return { ...existingModel, id: m.id };
433
- }
434
- return m;
435
- });
436
- for (const userModel of userModels) {
437
- const normalizedUserId = normalize(userModel.id);
438
- const matchedInUpdated = updatedModels.some((m) => {
439
- const normalizedBackendId = normalize(m.id);
440
- return normalizedBackendId === normalizedUserId;
441
- });
442
- logger.debug("User model match check", { userModelId: userModel.id, normalizedUserId, matchedInUpdated, updatedModelsIds: updatedModels.map((m) => m.id) });
443
- if (!matchedInUpdated) {
444
- updatedModels.push(userModel);
445
- }
446
- }
387
+ const updatedModels = mergeModelsWithUserOverrides(modelsWithContext, userModels);
447
388
  providers = providers.map((p) => p.id === providerId ? { ...p, models: updatedModels } : p);
448
389
  const { providerId: currentProviderId, model: currentModel } = parseDefaultModelSelection(defaultModelSelection);
449
390
  if (currentProviderId === providerId && currentModel) {
450
- const normalizedCurrentModel = normalize(currentModel);
451
- const matchedModel = updatedModels.find((m) => normalize(m.id) === normalizedCurrentModel);
391
+ const normalizedCurrentModel = normalizeModelId(currentModel);
392
+ const matchedModel = updatedModels.find((m) => normalizeModelId(m.id) === normalizedCurrentModel);
452
393
  if (matchedModel && matchedModel.id !== currentModel) {
453
394
  defaultModelSelection = `${providerId}/${matchedModel.id}`;
454
395
  logger.debug("Updated defaultModelSelection after fuzzy match", { from: currentModel, to: matchedModel.id });
@@ -465,4 +406,4 @@ export {
465
406
  parseDefaultModelSelection,
466
407
  createProviderManager
467
408
  };
468
- //# sourceMappingURL=chunk-OOIRCXAY.js.map
409
+ //# sourceMappingURL=chunk-KOUMYBYM.js.map