openfox 2.0.13 → 2.0.15
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/{chat-handler-6NUNVXIU.js → chat-handler-RYOG3WJP.js} +9 -7
- package/dist/{chunk-VFRWE5X4.js → chunk-2GX5EIIU.js} +481 -2104
- package/dist/{chunk-FQIT7VIA.js → chunk-3IKRNKPI.js} +6 -6
- package/dist/{chunk-A52FXWJX.js → chunk-3SGGOBCL.js} +10 -387
- package/dist/chunk-EKTGRBDC.js +2072 -0
- package/dist/{chunk-3QB2RMX2.js → chunk-F4PMNP7S.js} +2 -2
- package/dist/{chunk-TIKQWNYK.js → chunk-J7KOV4ST.js} +2 -2
- package/dist/{chunk-2VDLXCLO.js → chunk-KPKSN362.js} +288 -26
- package/dist/chunk-NWO6GRYE.js +39 -0
- package/dist/chunk-PQ56PX7L.js +397 -0
- package/dist/{chunk-HPCGVAS4.js → chunk-SNQT7LNU.js} +18 -1
- package/dist/{chunk-5BDVM6YI.js → chunk-YD6NDTKF.js} +1 -1
- package/dist/{chunk-EZJUU54W.js → chunk-Z64KW2HD.js} +23 -17
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{config-BU66P4KX.js → config-Z66BQTNX.js} +2 -2
- package/dist/{orchestrator-WXEZHUVQ.js → orchestrator-KRCVDTZ6.js} +8 -6
- package/dist/package.json +3 -3
- package/dist/{processor-GA5NG3T7.js → processor-JHR3Z42W.js} +9 -7
- package/dist/{protocol-CaLuIetw.d.ts → protocol-BC1QSQ-Y.d.ts} +16 -1
- package/dist/{protocol-YYWMFR35.js → protocol-BKNLAEPJ.js} +3 -3
- package/dist/{provider-QPECLUZ7.js → provider-YBTRC77Y.js} +7 -5
- package/dist/{provider-manager-YA2WALTF.js → provider-manager-DETXLDKW.js} +3 -2
- package/dist/{serve-TJHQ326L.js → serve-AV74ODQ6.js} +13 -9
- package/dist/server/index.d.ts +21 -2
- package/dist/server/index.js +11 -7
- package/dist/server-P7E2IPVL.js +31 -0
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/index.js +1 -1
- package/dist/tool-adapter-B7QP6NLA.js +7 -0
- package/dist/{tools-THXBQJ7A.js → tools-WEE4XDTL.js} +9 -5
- package/dist/web/assets/{index-CQw-9GC9.js → index-BMz5-yAf.js} +76 -75
- package/dist/web/assets/{index-Db3yUKBI.css → index-Ho_tVUbw.css} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildNonStreamingCreateParams,
|
|
3
|
+
buildStreamingCreateParams,
|
|
4
|
+
getBackendCapabilities,
|
|
5
|
+
getModelProfile,
|
|
6
|
+
getThinking,
|
|
7
|
+
mapFinishReason
|
|
8
|
+
} from "./chunk-Z4SWOUWC.js";
|
|
9
|
+
import {
|
|
10
|
+
logger
|
|
11
|
+
} from "./chunk-K44MW7JJ.js";
|
|
12
|
+
|
|
13
|
+
// src/server/llm/url-utils.ts
|
|
14
|
+
var VERSION_PREFIX_REGEX = /\/v\d+(\/|$)/;
|
|
15
|
+
function hasVersionPrefix(url) {
|
|
16
|
+
return VERSION_PREFIX_REGEX.test(url);
|
|
17
|
+
}
|
|
18
|
+
function ensureVersionPrefix(url, defaultVersion = "/v1") {
|
|
19
|
+
if (hasVersionPrefix(url)) return url;
|
|
20
|
+
return `${url.replace(/\/+$/, "")}${defaultVersion}`;
|
|
21
|
+
}
|
|
22
|
+
function stripVersionPrefix(url) {
|
|
23
|
+
return url.replace(/\/v\d+\/?$/, "");
|
|
24
|
+
}
|
|
25
|
+
function buildModelsUrl(baseUrl) {
|
|
26
|
+
return `${ensureVersionPrefix(baseUrl)}/models`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/server/llm/client.ts
|
|
30
|
+
import OpenAI from "openai";
|
|
31
|
+
|
|
32
|
+
// src/server/utils/errors.ts
|
|
33
|
+
var OpenFoxError = class extends Error {
|
|
34
|
+
constructor(message, code, details) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.details = details;
|
|
38
|
+
this.name = "OpenFoxError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var SessionNotFoundError = class extends OpenFoxError {
|
|
42
|
+
constructor(sessionId) {
|
|
43
|
+
super(`Session not found: ${sessionId}`, "SESSION_NOT_FOUND", { sessionId });
|
|
44
|
+
this.name = "SessionNotFoundError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var LLMError = class extends OpenFoxError {
|
|
48
|
+
constructor(message, details) {
|
|
49
|
+
super(message, "LLM_ERROR", details);
|
|
50
|
+
this.name = "LLMError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/server/llm/client.ts
|
|
55
|
+
import { Agent, setGlobalDispatcher } from "undici";
|
|
56
|
+
var agent = new Agent({ allowH2: true });
|
|
57
|
+
setGlobalDispatcher(agent);
|
|
58
|
+
function createLLMClient(config, initialBackend = "unknown") {
|
|
59
|
+
const baseURL = ensureVersionPrefix(config.llm.baseUrl);
|
|
60
|
+
const openai = new OpenAI({
|
|
61
|
+
baseURL,
|
|
62
|
+
apiKey: config.llm.apiKey ?? "not-needed"
|
|
63
|
+
});
|
|
64
|
+
let model = config.llm.model;
|
|
65
|
+
let profile = getModelProfile(model);
|
|
66
|
+
let backend = initialBackend;
|
|
67
|
+
let capabilities = getBackendCapabilities(backend);
|
|
68
|
+
const reasoningEffort = config.llm.reasoningEffort;
|
|
69
|
+
const thinkingField = config.llm.thinkingField;
|
|
70
|
+
const idleTimeout = config.llm.idleTimeout ?? 3e4;
|
|
71
|
+
return {
|
|
72
|
+
getModel() {
|
|
73
|
+
return model;
|
|
74
|
+
},
|
|
75
|
+
getProfile() {
|
|
76
|
+
return profile;
|
|
77
|
+
},
|
|
78
|
+
getBackend() {
|
|
79
|
+
return backend;
|
|
80
|
+
},
|
|
81
|
+
setBackend(newBackend) {
|
|
82
|
+
logger.debug("Setting LLM backend", { from: backend, to: newBackend });
|
|
83
|
+
backend = newBackend;
|
|
84
|
+
capabilities = getBackendCapabilities(newBackend);
|
|
85
|
+
},
|
|
86
|
+
setModel(newModel) {
|
|
87
|
+
const newProfile = getModelProfile(newModel);
|
|
88
|
+
logger.debug("Switching model", {
|
|
89
|
+
from: model,
|
|
90
|
+
to: newModel,
|
|
91
|
+
profile: newProfile.name,
|
|
92
|
+
temperature: newProfile.temperature
|
|
93
|
+
});
|
|
94
|
+
model = newModel;
|
|
95
|
+
profile = newProfile;
|
|
96
|
+
},
|
|
97
|
+
async complete(request) {
|
|
98
|
+
logger.debug("LLM complete request", {
|
|
99
|
+
messageCount: request.messages.length,
|
|
100
|
+
hasTools: !!request.tools?.length,
|
|
101
|
+
profile: profile.name,
|
|
102
|
+
reasoningEffort: request.reasoningEffort ?? reasoningEffort
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
const resolvedEffort = request.reasoningEffort ?? reasoningEffort;
|
|
106
|
+
const { params: createParams } = await buildNonStreamingCreateParams({
|
|
107
|
+
model,
|
|
108
|
+
request,
|
|
109
|
+
profile,
|
|
110
|
+
capabilities,
|
|
111
|
+
...resolvedEffort ? { reasoningEffort: resolvedEffort } : {},
|
|
112
|
+
...thinkingField ? { thinkingField } : {}
|
|
113
|
+
});
|
|
114
|
+
const response = await openai.chat.completions.create(createParams, {
|
|
115
|
+
signal: request.signal
|
|
116
|
+
});
|
|
117
|
+
const choice = response.choices[0];
|
|
118
|
+
if (!choice) {
|
|
119
|
+
throw new LLMError("No completion choice returned");
|
|
120
|
+
}
|
|
121
|
+
const message = choice.message;
|
|
122
|
+
const content = message.content ?? "";
|
|
123
|
+
const thinkingContent = getThinking(message, thinkingField) ?? "";
|
|
124
|
+
const toolCalls = message.tool_calls?.map((tc) => ({
|
|
125
|
+
id: tc.id,
|
|
126
|
+
name: tc.function.name,
|
|
127
|
+
arguments: JSON.parse(tc.function.arguments)
|
|
128
|
+
}));
|
|
129
|
+
return {
|
|
130
|
+
id: response.id,
|
|
131
|
+
content,
|
|
132
|
+
...thinkingContent ? { thinkingContent } : {},
|
|
133
|
+
...toolCalls && toolCalls.length > 0 ? { toolCalls } : {},
|
|
134
|
+
finishReason: mapFinishReason(choice.finish_reason),
|
|
135
|
+
usage: {
|
|
136
|
+
promptTokens: response.usage?.prompt_tokens ?? 0,
|
|
137
|
+
completionTokens: response.usage?.completion_tokens ?? 0,
|
|
138
|
+
totalTokens: response.usage?.total_tokens ?? 0
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.error("LLM complete error", { error: String(error) });
|
|
143
|
+
throw new LLMError(error instanceof Error ? error.message : "Unknown LLM error", {
|
|
144
|
+
originalError: error instanceof Error ? error : void 0
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
async *stream(request) {
|
|
149
|
+
const resolvedEffort = request.reasoningEffort ?? reasoningEffort;
|
|
150
|
+
logger.debug("LLM stream request", {
|
|
151
|
+
messageCount: request.messages.length,
|
|
152
|
+
hasTools: !!request.tools?.length,
|
|
153
|
+
profile: profile.name,
|
|
154
|
+
reasoningEffort: resolvedEffort,
|
|
155
|
+
idleTimeout
|
|
156
|
+
});
|
|
157
|
+
try {
|
|
158
|
+
const createParams = await buildStreamingCreateParams({
|
|
159
|
+
model,
|
|
160
|
+
request,
|
|
161
|
+
profile,
|
|
162
|
+
capabilities,
|
|
163
|
+
...resolvedEffort ? { reasoningEffort: resolvedEffort } : {},
|
|
164
|
+
...thinkingField ? { thinkingField } : {}
|
|
165
|
+
});
|
|
166
|
+
const { params: streamingParams } = createParams;
|
|
167
|
+
const stream = await openai.chat.completions.create(streamingParams, {
|
|
168
|
+
signal: request.signal
|
|
169
|
+
});
|
|
170
|
+
let fullContent = "";
|
|
171
|
+
let fullThinking = "";
|
|
172
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
173
|
+
let finishReason = "stop";
|
|
174
|
+
let usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
175
|
+
let responseId = "";
|
|
176
|
+
let lastChunkTime = Date.now();
|
|
177
|
+
const idleTimeoutController = new AbortController();
|
|
178
|
+
const idleTimer = setInterval(() => {
|
|
179
|
+
const idleDuration = Date.now() - lastChunkTime;
|
|
180
|
+
if (idleDuration > idleTimeout) {
|
|
181
|
+
logger.warn("LLM stream idle timeout triggered", { idleDuration, idleTimeout });
|
|
182
|
+
idleTimeoutController.abort();
|
|
183
|
+
}
|
|
184
|
+
}, 100);
|
|
185
|
+
const onAbort = () => clearInterval(idleTimer);
|
|
186
|
+
request.signal?.addEventListener("abort", onAbort, { once: true });
|
|
187
|
+
try {
|
|
188
|
+
for await (const chunk of stream) {
|
|
189
|
+
if (idleTimeoutController.signal.aborted) {
|
|
190
|
+
throw new Error(`LLM stream idle timeout: no chunks received for ${idleTimeout}ms`);
|
|
191
|
+
}
|
|
192
|
+
lastChunkTime = Date.now();
|
|
193
|
+
responseId = chunk.id;
|
|
194
|
+
if (chunk.usage) {
|
|
195
|
+
usage = {
|
|
196
|
+
promptTokens: chunk.usage.prompt_tokens,
|
|
197
|
+
completionTokens: chunk.usage.completion_tokens,
|
|
198
|
+
totalTokens: chunk.usage.total_tokens
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const choice = chunk.choices[0];
|
|
202
|
+
if (!choice) continue;
|
|
203
|
+
if (choice.finish_reason) {
|
|
204
|
+
finishReason = mapFinishReason(choice.finish_reason);
|
|
205
|
+
}
|
|
206
|
+
const delta = choice.delta;
|
|
207
|
+
const reasoning = getThinking(delta, thinkingField);
|
|
208
|
+
if (reasoning) {
|
|
209
|
+
fullThinking += reasoning;
|
|
210
|
+
yield { type: "thinking_delta", content: reasoning };
|
|
211
|
+
}
|
|
212
|
+
if (delta.content) {
|
|
213
|
+
fullContent += delta.content;
|
|
214
|
+
yield { type: "text_delta", content: delta.content };
|
|
215
|
+
}
|
|
216
|
+
if (delta.tool_calls) {
|
|
217
|
+
for (const tc of delta.tool_calls) {
|
|
218
|
+
const existing = toolCalls.get(tc.index);
|
|
219
|
+
if (!existing) {
|
|
220
|
+
toolCalls.set(tc.index, {
|
|
221
|
+
id: tc.id ?? "",
|
|
222
|
+
name: tc.function?.name ?? "",
|
|
223
|
+
arguments: tc.function?.arguments ?? ""
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
if (tc.id) existing.id = tc.id;
|
|
227
|
+
if (tc.function?.name) existing.name += tc.function.name;
|
|
228
|
+
if (tc.function?.arguments) existing.arguments += tc.function.arguments;
|
|
229
|
+
}
|
|
230
|
+
yield {
|
|
231
|
+
type: "tool_call_delta",
|
|
232
|
+
index: tc.index,
|
|
233
|
+
...tc.id ? { id: tc.id } : {},
|
|
234
|
+
...tc.function?.name ? { name: tc.function.name } : {},
|
|
235
|
+
...tc.function?.arguments ? { arguments: tc.function.arguments } : {}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} finally {
|
|
241
|
+
clearInterval(idleTimer);
|
|
242
|
+
request.signal?.removeEventListener("abort", onAbort);
|
|
243
|
+
}
|
|
244
|
+
const finalContent = fullContent.trim();
|
|
245
|
+
const finalThinking = fullThinking.trim();
|
|
246
|
+
const parsedToolCalls = [];
|
|
247
|
+
for (const [, tc] of toolCalls) {
|
|
248
|
+
try {
|
|
249
|
+
parsedToolCalls.push({
|
|
250
|
+
id: tc.id,
|
|
251
|
+
name: tc.name,
|
|
252
|
+
arguments: JSON.parse(tc.arguments)
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.warn("Failed to parse tool call arguments", { name: tc.name, arguments: tc.arguments });
|
|
256
|
+
parsedToolCalls.push({
|
|
257
|
+
id: tc.id,
|
|
258
|
+
name: tc.name,
|
|
259
|
+
arguments: {},
|
|
260
|
+
parseError: error instanceof Error ? error.message : "Unknown JSON parse error",
|
|
261
|
+
rawArguments: tc.arguments
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
yield {
|
|
266
|
+
type: "done",
|
|
267
|
+
response: {
|
|
268
|
+
id: responseId,
|
|
269
|
+
content: finalContent,
|
|
270
|
+
...finalThinking ? { thinkingContent: finalThinking } : {},
|
|
271
|
+
...parsedToolCalls.length > 0 ? { toolCalls: parsedToolCalls } : {},
|
|
272
|
+
finishReason,
|
|
273
|
+
usage
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
} catch (error) {
|
|
277
|
+
logger.error("LLM stream error", { error });
|
|
278
|
+
yield {
|
|
279
|
+
type: "error",
|
|
280
|
+
error: error instanceof Error ? error.message : "Unknown LLM error"
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/server/llm/models.ts
|
|
288
|
+
var modelCache = /* @__PURE__ */ new Map();
|
|
289
|
+
var llmStatus = "unknown";
|
|
290
|
+
var lastActiveUrl = null;
|
|
291
|
+
var CACHE_TTL_MS = 3e4;
|
|
292
|
+
function getCacheKey(url) {
|
|
293
|
+
return stripVersionPrefix(url);
|
|
294
|
+
}
|
|
295
|
+
async function detectModel(llmBaseUrl, retries = 3, silent = false) {
|
|
296
|
+
const cacheKey = getCacheKey(llmBaseUrl);
|
|
297
|
+
const now = Date.now();
|
|
298
|
+
const cached = modelCache.get(cacheKey);
|
|
299
|
+
if (cached && now - cached.timestamp < CACHE_TTL_MS) {
|
|
300
|
+
lastActiveUrl = cacheKey;
|
|
301
|
+
llmStatus = "connected";
|
|
302
|
+
return cached.model;
|
|
303
|
+
}
|
|
304
|
+
const url = buildModelsUrl(llmBaseUrl);
|
|
305
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
306
|
+
try {
|
|
307
|
+
if (silent) {
|
|
308
|
+
logger.debug("Fetching models from LLM server", { url, attempt });
|
|
309
|
+
}
|
|
310
|
+
const response = await fetch(url, {
|
|
311
|
+
signal: AbortSignal.timeout(1e4)
|
|
312
|
+
});
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
if (silent) {
|
|
315
|
+
logger.debug("Failed to fetch models from LLM server", { status: response.status, attempt });
|
|
316
|
+
} else {
|
|
317
|
+
logger.warn("Failed to fetch models from LLM server", { status: response.status, attempt });
|
|
318
|
+
}
|
|
319
|
+
if (attempt < retries) {
|
|
320
|
+
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
llmStatus = "disconnected";
|
|
324
|
+
return cached?.model ?? null;
|
|
325
|
+
}
|
|
326
|
+
const data = await response.json();
|
|
327
|
+
if (data.data && data.data.length > 0) {
|
|
328
|
+
const modelData = data.data[0];
|
|
329
|
+
const modelId = modelData.id;
|
|
330
|
+
modelCache.set(cacheKey, {
|
|
331
|
+
model: modelId,
|
|
332
|
+
modelInfo: modelData,
|
|
333
|
+
timestamp: now
|
|
334
|
+
});
|
|
335
|
+
lastActiveUrl = cacheKey;
|
|
336
|
+
llmStatus = "connected";
|
|
337
|
+
if (silent) {
|
|
338
|
+
logger.debug("Detected LLM model", {
|
|
339
|
+
model: modelId,
|
|
340
|
+
maxLen: modelData.max_model_len,
|
|
341
|
+
root: modelData.root
|
|
342
|
+
});
|
|
343
|
+
} else {
|
|
344
|
+
logger.info("Detected LLM model", {
|
|
345
|
+
model: modelId,
|
|
346
|
+
maxLen: modelData.max_model_len,
|
|
347
|
+
root: modelData.root
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return modelId;
|
|
351
|
+
}
|
|
352
|
+
if (silent) {
|
|
353
|
+
logger.debug("LLM server returned empty models list");
|
|
354
|
+
} else {
|
|
355
|
+
logger.warn("LLM server returned empty models list");
|
|
356
|
+
}
|
|
357
|
+
llmStatus = "disconnected";
|
|
358
|
+
return null;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
361
|
+
if (silent) {
|
|
362
|
+
logger.debug("Could not detect model from LLM server", { error: errMsg, attempt });
|
|
363
|
+
} else {
|
|
364
|
+
logger.warn("Could not detect model from LLM server", { error: errMsg, attempt });
|
|
365
|
+
}
|
|
366
|
+
if (attempt < retries) {
|
|
367
|
+
await new Promise((r) => setTimeout(r, 1e3 * attempt));
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
llmStatus = "disconnected";
|
|
373
|
+
return cached?.model ?? null;
|
|
374
|
+
}
|
|
375
|
+
function getLlmStatus() {
|
|
376
|
+
return llmStatus;
|
|
377
|
+
}
|
|
378
|
+
function clearModelCache(url) {
|
|
379
|
+
if (url) {
|
|
380
|
+
modelCache.delete(getCacheKey(url));
|
|
381
|
+
} else {
|
|
382
|
+
modelCache.clear();
|
|
383
|
+
}
|
|
384
|
+
llmStatus = "unknown";
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export {
|
|
388
|
+
SessionNotFoundError,
|
|
389
|
+
ensureVersionPrefix,
|
|
390
|
+
stripVersionPrefix,
|
|
391
|
+
buildModelsUrl,
|
|
392
|
+
createLLMClient,
|
|
393
|
+
detectModel,
|
|
394
|
+
getLlmStatus,
|
|
395
|
+
clearModelCache
|
|
396
|
+
};
|
|
397
|
+
//# sourceMappingURL=chunk-PQ56PX7L.js.map
|
|
@@ -61,9 +61,19 @@ var visionFallbackSchema = z.object({
|
|
|
61
61
|
model: z.string().default("qwen3.5:0.8b"),
|
|
62
62
|
timeout: z.number().default(120)
|
|
63
63
|
});
|
|
64
|
+
var mcpServerSchema = z.object({
|
|
65
|
+
transport: z.enum(["stdio", "http"]).default("stdio"),
|
|
66
|
+
command: z.string().optional(),
|
|
67
|
+
args: z.array(z.string()).optional(),
|
|
68
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
69
|
+
url: z.string().optional(),
|
|
70
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
71
|
+
disabledTools: z.array(z.string()).optional()
|
|
72
|
+
});
|
|
64
73
|
var defaultVisionFallback = { enabled: false, url: "http://localhost:11434", model: "qwen3.5:0.8b", timeout: 120 };
|
|
65
74
|
var configSchema = z.object({
|
|
66
75
|
providers: z.array(providerSchema).default([]),
|
|
76
|
+
mcpServers: z.record(z.string(), mcpServerSchema).optional(),
|
|
67
77
|
defaultModelSelection: z.string().optional(),
|
|
68
78
|
activeProviderId: z.string().optional(),
|
|
69
79
|
activeWorkflowId: z.string().optional(),
|
|
@@ -74,6 +84,7 @@ var configSchema = z.object({
|
|
|
74
84
|
visionFallback: visionFallbackSchema.optional()
|
|
75
85
|
}).transform((data) => ({
|
|
76
86
|
providers: data.providers ?? [],
|
|
87
|
+
mcpServers: data.mcpServers,
|
|
77
88
|
defaultModelSelection: data.defaultModelSelection,
|
|
78
89
|
activeProviderId: data.activeProviderId,
|
|
79
90
|
activeWorkflowId: data.activeWorkflowId,
|
|
@@ -225,6 +236,7 @@ async function saveGlobalConfig(mode, config) {
|
|
|
225
236
|
const configPath = getGlobalConfigPath(mode);
|
|
226
237
|
const fullConfig = {
|
|
227
238
|
providers: config.providers ?? [],
|
|
239
|
+
mcpServers: config.mcpServers,
|
|
228
240
|
defaultModelSelection: config.defaultModelSelection,
|
|
229
241
|
activeProviderId: config.activeProviderId,
|
|
230
242
|
activeWorkflowId: config.activeWorkflowId,
|
|
@@ -260,6 +272,7 @@ function setDefaultModelSelection(config, providerId, model) {
|
|
|
260
272
|
const defaultModelSelection = `${providerId}/${model}`;
|
|
261
273
|
return {
|
|
262
274
|
providers: config.providers?.map((p) => ({ ...p, isActive: p.id === providerId })) ?? [],
|
|
275
|
+
mcpServers: config.mcpServers,
|
|
263
276
|
defaultModelSelection,
|
|
264
277
|
activeProviderId: providerId,
|
|
265
278
|
activeWorkflowId: config.activeWorkflowId,
|
|
@@ -287,6 +300,7 @@ function addProvider(config, provider) {
|
|
|
287
300
|
...(config.providers ?? []).map((p) => shouldActivate ? { ...p, isActive: false } : p),
|
|
288
301
|
{ ...newProvider, isActive: shouldActivate }
|
|
289
302
|
],
|
|
303
|
+
mcpServers: config.mcpServers,
|
|
290
304
|
defaultModelSelection: shouldActivate ? `${newProvider.id}/auto` : config.defaultModelSelection,
|
|
291
305
|
activeProviderId: shouldActivate ? newProvider.id : config.activeProviderId,
|
|
292
306
|
activeWorkflowId: config.activeWorkflowId,
|
|
@@ -323,6 +337,7 @@ function removeProvider(config, providerId) {
|
|
|
323
337
|
const newActiveId = wasActive && filtered.length > 0 ? filtered[0].id : wasActive ? void 0 : config.activeProviderId;
|
|
324
338
|
return {
|
|
325
339
|
providers: filtered.map((p) => ({ ...p, isActive: p.id === newActiveId })),
|
|
340
|
+
mcpServers: config.mcpServers,
|
|
326
341
|
activeProviderId: newActiveId,
|
|
327
342
|
defaultModelSelection: newDefaultModelSelection,
|
|
328
343
|
activeWorkflowId: config.activeWorkflowId,
|
|
@@ -343,6 +358,7 @@ function activateProvider(config, providerId) {
|
|
|
343
358
|
if (!provider) {
|
|
344
359
|
return {
|
|
345
360
|
providers: config.providers ?? [],
|
|
361
|
+
mcpServers: config.mcpServers,
|
|
346
362
|
defaultModelSelection: config.defaultModelSelection,
|
|
347
363
|
activeProviderId: config.activeProviderId,
|
|
348
364
|
activeWorkflowId: config.activeWorkflowId,
|
|
@@ -360,6 +376,7 @@ function activateProvider(config, providerId) {
|
|
|
360
376
|
}
|
|
361
377
|
return {
|
|
362
378
|
providers: (config.providers ?? []).map((p) => ({ ...p, isActive: p.id === providerId })),
|
|
379
|
+
mcpServers: config.mcpServers,
|
|
363
380
|
defaultModelSelection: config.defaultModelSelection,
|
|
364
381
|
activeProviderId: providerId,
|
|
365
382
|
activeWorkflowId: config.activeWorkflowId,
|
|
@@ -419,4 +436,4 @@ export {
|
|
|
419
436
|
activateProvider,
|
|
420
437
|
mergeConfigs
|
|
421
438
|
};
|
|
422
|
-
//# sourceMappingURL=chunk-
|
|
439
|
+
//# sourceMappingURL=chunk-SNQT7LNU.js.map
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PathAccessDeniedError,
|
|
3
3
|
assembleAgentRequest,
|
|
4
|
+
computeDynamicContextHash,
|
|
4
5
|
createAssemblyResult,
|
|
6
|
+
createToolRegistry,
|
|
5
7
|
findAgentById,
|
|
6
8
|
getAllInstructions,
|
|
7
9
|
getConversationMessages,
|
|
8
10
|
getEnabledSkillMetadata,
|
|
9
11
|
getSubAgents,
|
|
12
|
+
getToolFingerprint,
|
|
10
13
|
getToolRegistryForAgent,
|
|
11
14
|
loadAllAgentsDefault,
|
|
12
15
|
processEventsForConversation,
|
|
13
16
|
runTopLevelAgentLoop
|
|
14
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-KPKSN362.js";
|
|
15
18
|
import {
|
|
16
19
|
TurnMetrics,
|
|
17
20
|
WORKFLOW_KICKOFF_PROMPT,
|
|
@@ -36,16 +39,6 @@ import {
|
|
|
36
39
|
logger
|
|
37
40
|
} from "./chunk-K44MW7JJ.js";
|
|
38
41
|
|
|
39
|
-
// src/server/chat/dynamic-context.ts
|
|
40
|
-
import { createHash } from "crypto";
|
|
41
|
-
function computeDynamicContextHash(instructionContent, skills) {
|
|
42
|
-
const dynamicInputs = JSON.stringify({
|
|
43
|
-
instructions: instructionContent,
|
|
44
|
-
skills: skills.map((s) => s.id).sort()
|
|
45
|
-
});
|
|
46
|
-
return createHash("sha256").update(dynamicInputs).digest("hex");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
42
|
// src/server/chat/orchestrator.ts
|
|
50
43
|
async function buildRetryPatterns() {
|
|
51
44
|
const { getSetting, SETTINGS_KEYS } = await import("./settings-6XX56F3F.js");
|
|
@@ -237,16 +230,30 @@ async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
|
|
|
237
230
|
onMessage: options.onMessage,
|
|
238
231
|
assembleRequest: (input) => {
|
|
239
232
|
const cached = options.sessionManager.getCachedPrompt(options.sessionId);
|
|
233
|
+
const liveTools = createToolRegistry().definitions;
|
|
234
|
+
const toolFingerprint = getToolFingerprint(liveTools);
|
|
240
235
|
if (cached) {
|
|
241
|
-
const currentHash = computeDynamicContextHash(instructionContent ?? "", skills);
|
|
236
|
+
const currentHash = computeDynamicContextHash(instructionContent ?? "", skills, toolFingerprint);
|
|
242
237
|
if (cached.hash !== currentHash) {
|
|
238
|
+
logger.debug("assembleRequest: hash mismatch", {
|
|
239
|
+
sessionId: options.sessionId,
|
|
240
|
+
cachedHash: cached.hash,
|
|
241
|
+
currentHash,
|
|
242
|
+
cachedTools: cached.tools.map((t) => t.function.name),
|
|
243
|
+
liveTools: liveTools.map((t) => t.function.name)
|
|
244
|
+
});
|
|
243
245
|
options.sessionManager.setDynamicContextChanged(options.sessionId, true);
|
|
246
|
+
options.sessionManager.setDebugDump(options.sessionId, {
|
|
247
|
+
cachedPrompt: cached.systemPrompt.slice(0, 5e3),
|
|
248
|
+
cachedTools: cached.tools.map((t) => t.function.name),
|
|
249
|
+
liveTools: liveTools.map((t) => t.function.name)
|
|
250
|
+
});
|
|
244
251
|
}
|
|
245
252
|
return createAssemblyResult({
|
|
246
253
|
systemPrompt: cached.systemPrompt,
|
|
247
254
|
messages: input.messages,
|
|
248
255
|
injectedFiles: input.injectedFiles,
|
|
249
|
-
requestTools:
|
|
256
|
+
requestTools: cached.tools.length > 0 ? cached.tools : liveTools,
|
|
250
257
|
toolChoice: input.toolChoice
|
|
251
258
|
});
|
|
252
259
|
}
|
|
@@ -256,8 +263,8 @@ async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
|
|
|
256
263
|
subAgentDefs,
|
|
257
264
|
modelName: options.llmClient.getModel()
|
|
258
265
|
});
|
|
259
|
-
const hash = computeDynamicContextHash(instructionContent ?? "", skills);
|
|
260
|
-
options.sessionManager.setCachedPrompt(options.sessionId, result.systemPrompt, hash);
|
|
266
|
+
const hash = computeDynamicContextHash(instructionContent ?? "", skills, toolFingerprint);
|
|
267
|
+
options.sessionManager.setCachedPrompt(options.sessionId, result.systemPrompt, result.tools, hash);
|
|
261
268
|
return result;
|
|
262
269
|
},
|
|
263
270
|
getToolRegistry: () => getToolRegistryForAgent(agentDef),
|
|
@@ -309,9 +316,8 @@ function buildSnapshot(sessionManager, sessionId, _lastStats) {
|
|
|
309
316
|
}
|
|
310
317
|
|
|
311
318
|
export {
|
|
312
|
-
computeDynamicContextHash,
|
|
313
319
|
runChatTurn,
|
|
314
320
|
runAgentTurn,
|
|
315
321
|
injectWorkflowKickoffIfNeeded
|
|
316
322
|
};
|
|
317
|
-
//# sourceMappingURL=chunk-
|
|
323
|
+
//# sourceMappingURL=chunk-Z64KW2HD.js.map
|
package/dist/cli/dev.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
saveGlobalConfig,
|
|
14
14
|
setDefaultModelSelection,
|
|
15
15
|
updateProvider
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-SNQT7LNU.js";
|
|
17
17
|
import "./chunk-CQGTEGKL.js";
|
|
18
18
|
export {
|
|
19
19
|
activateProvider,
|
|
@@ -31,4 +31,4 @@ export {
|
|
|
31
31
|
setDefaultModelSelection,
|
|
32
32
|
updateProvider
|
|
33
33
|
};
|
|
34
|
-
//# sourceMappingURL=config-
|
|
34
|
+
//# sourceMappingURL=config-Z66BQTNX.js.map
|
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
injectWorkflowKickoffIfNeeded,
|
|
3
3
|
runAgentTurn,
|
|
4
4
|
runChatTurn
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-Z64KW2HD.js";
|
|
6
|
+
import "./chunk-KPKSN362.js";
|
|
7
|
+
import "./chunk-XAMAYRDA.js";
|
|
7
8
|
import {
|
|
8
9
|
TurnMetrics,
|
|
9
10
|
createChatDoneEvent,
|
|
@@ -15,14 +16,15 @@ import {
|
|
|
15
16
|
import "./chunk-DL6ZILAF.js";
|
|
16
17
|
import "./chunk-PBGOZMVY.js";
|
|
17
18
|
import "./chunk-VRGRAQDG.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-NWO6GRYE.js";
|
|
19
20
|
import "./chunk-SYG2ENUQ.js";
|
|
20
21
|
import "./chunk-LX66KJPL.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-F4PMNP7S.js";
|
|
22
23
|
import "./chunk-EU3WWTFH.js";
|
|
23
24
|
import "./chunk-RFNEDBVO.js";
|
|
24
25
|
import "./chunk-FBGWG4N6.js";
|
|
25
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-YD6NDTKF.js";
|
|
27
|
+
import "./chunk-SNQT7LNU.js";
|
|
26
28
|
import "./chunk-CQGTEGKL.js";
|
|
27
29
|
import "./chunk-Z4SWOUWC.js";
|
|
28
30
|
import "./chunk-K44MW7JJ.js";
|
|
@@ -37,4 +39,4 @@ export {
|
|
|
37
39
|
runAgentTurn,
|
|
38
40
|
runChatTurn
|
|
39
41
|
};
|
|
40
|
-
//# sourceMappingURL=orchestrator-
|
|
42
|
+
//# sourceMappingURL=orchestrator-KRCVDTZ6.js.map
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openfox",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.15",
|
|
4
4
|
"description": "Local-LLM-first agentic coding assistant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@clack/prompts": "^1.1.0",
|
|
62
62
|
"@fontsource/jetbrains-mono": "^5.2.8",
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
63
64
|
"@xterm/addon-fit": "^0.11.0",
|
|
64
65
|
"@xterm/xterm": "^6.0.0",
|
|
65
66
|
"bash-language-server": "^5.6.0",
|
|
@@ -79,12 +80,11 @@
|
|
|
79
80
|
"react-syntax-highlighter": "^16.1.1",
|
|
80
81
|
"remark-gfm": "^4.0.1",
|
|
81
82
|
"shiki": "^4.2.0",
|
|
82
|
-
"sql-language-server": "^1.7.1",
|
|
83
83
|
"strip-ansi": "^7.2.0",
|
|
84
84
|
"turndown": "^7.2.2",
|
|
85
|
-
"undici": "^8.5.0",
|
|
86
85
|
"typescript": "^5.8.2",
|
|
87
86
|
"typescript-language-server": "^5.1.3",
|
|
87
|
+
"undici": "^8.5.0",
|
|
88
88
|
"vite": "^6.1.0",
|
|
89
89
|
"vscode-jsonrpc": "^8.2.1",
|
|
90
90
|
"vscode-langservers-extracted": "^4.10.0",
|