openfox 2.0.134 → 2.0.136

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 (88) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/CHANGELOG.md +45 -0
  3. package/dist/agent-defaults/builder.agent.md +1 -0
  4. package/dist/agent-defaults/planner.agent.md +1 -0
  5. package/dist/{ask-UBIRH3SO.js → ask-46J5WYRL.js} +2 -2
  6. package/dist/{auto-config-5VSNLVNI.js → auto-config-2A7Y2WOK.js} +25 -6
  7. package/dist/{backend-NDBY3DAQ.js → backend-24FTDSHF.js} +4 -2
  8. package/dist/{chat-handler-YCZE2XEJ.js → chat-handler-SYU4HJMI.js} +30 -26
  9. package/dist/{chunk-CSYOWFPU.js → chunk-342BKOTE.js} +29 -5
  10. package/dist/{chunk-7QMWYNVF.js → chunk-4LLGI372.js} +2 -2
  11. package/dist/chunk-4SBVW4UH.js +24 -0
  12. package/dist/{chunk-FAQDLL2X.js → chunk-5YPHN5NC.js} +8 -8
  13. package/dist/{chunk-ZMK4VX7V.js → chunk-64YDOCQR.js} +53 -21
  14. package/dist/{chunk-UV6IH6HK.js → chunk-AKWJMMEJ.js} +15 -1
  15. package/dist/{chunk-ORCQ3EXQ.js → chunk-BHVR2YS5.js} +2 -2
  16. package/dist/{chunk-7HGBJFIJ.js → chunk-CPVEONNS.js} +12 -10
  17. package/dist/{chunk-QDARWVQ2.js → chunk-DG2YSWMC.js} +40 -6
  18. package/dist/{chunk-J5HOUEPE.js → chunk-EXLCFEML.js} +86 -1010
  19. package/dist/chunk-FSSUPWYO.js +336 -0
  20. package/dist/{chunk-5UQ4CKOA.js → chunk-G5GUQKLS.js} +27 -6
  21. package/dist/{chunk-M4J2L4VH.js → chunk-GP5BYQMF.js} +23 -23
  22. package/dist/{chunk-7ET46SNE.js → chunk-HEYU7FAY.js} +3 -3
  23. package/dist/{chunk-MRE4OYKI.js → chunk-MILEZY2J.js} +4 -4
  24. package/dist/{chunk-O3RCSKUH.js → chunk-MOYWDP73.js} +2 -2
  25. package/dist/{chunk-J3NMNYGM.js → chunk-N5JKVNLH.js} +2 -2
  26. package/dist/{chunk-YLVOU7T7.js → chunk-PQKLOLGC.js} +162 -6
  27. package/dist/{chunk-DWA4IE65.js → chunk-Q33DL67W.js} +2 -2
  28. package/dist/{chunk-OZWHRBDY.js → chunk-QBWJCO5K.js} +12 -29
  29. package/dist/{chunk-EUOWCVU6.js → chunk-QMU3ZY2C.js} +37 -341
  30. package/dist/{chunk-5IXCCMFU.js → chunk-QQQBNL7A.js} +350 -157
  31. package/dist/{chunk-CTWZPN7G.js → chunk-QUAEH3MP.js} +29 -1
  32. package/dist/{chunk-5UMXFLET.js → chunk-SAGDTQXD.js} +3 -3
  33. package/dist/chunk-U6FHHT2B.js +1155 -0
  34. package/dist/{chunk-YOPXAI2W.js → chunk-UL7776XB.js} +21 -21
  35. package/dist/{chunk-JURM3RPZ.js → chunk-V5AC2TV7.js} +8 -13
  36. package/dist/{chunk-BZND27IB.js → chunk-VA2TYWQR.js} +4 -4
  37. package/dist/{chunk-JE7LW7Y6.js → chunk-VIJVLLPT.js} +12 -3
  38. package/dist/{chunk-2AI7VYPN.js → chunk-VKMPR25F.js} +9 -3
  39. package/dist/chunk-VWBPQZLO.js +7 -0
  40. package/dist/{chunk-ZTBKMVV6.js → chunk-X4PVR6AW.js} +15 -2
  41. package/dist/chunk-XG3AUMPI.js +72 -0
  42. package/dist/{chunk-MEEUXRFL.js → chunk-XGG4XCKP.js} +2 -2
  43. package/dist/{chunk-QICVOQNX.js → chunk-YDPC4FLR.js} +17 -11
  44. package/dist/cli/dev.js +1 -1
  45. package/dist/cli/index.js +1 -1
  46. package/dist/client-MVI3J7SN.js +18 -0
  47. package/dist/{client-pure-FLKP6PTI.js → client-pure-YZ4IGCU4.js} +4 -2
  48. package/dist/{compactor-PD6DQ4NY.js → compactor-LBK53WAK.js} +14 -12
  49. package/dist/describe-image-4XDLSKAB.js +30 -0
  50. package/dist/{dynamic-context-WKEXQCTK.js → dynamic-context-RDBNQPPA.js} +12 -10
  51. package/dist/{events-3WSNI7XC.js → events-YTYYT3PR.js} +6 -6
  52. package/dist/export-import-GHRCOQHF.js +137 -0
  53. package/dist/{folding-H7JTX6KI.js → folding-3MMOPS4W.js} +4 -4
  54. package/dist/{http-client-A445WYZU.js → http-client-C6UYWVWZ.js} +3 -3
  55. package/dist/launch-YZ46OEFP.js +58 -0
  56. package/dist/{manager-ZACEUL5P.js → manager-FSPRJCYT.js} +4 -4
  57. package/dist/{model-catalog-2PCKTYGW.js → model-catalog-Y5MA5OZP.js} +2 -2
  58. package/dist/{orchestrator-OT66EDXO.js → orchestrator-6QGQ55NT.js} +29 -25
  59. package/dist/package.json +1 -1
  60. package/dist/{path-security-5CFMMULR.js → path-security-EC4Z2ZZG.js} +8 -8
  61. package/dist/{platform-XPMZKAUB.js → platform-KPKYBU54.js} +3 -3
  62. package/dist/{processor-CHMHMMIE.js → processor-B2RV7MVC.js} +29 -25
  63. package/dist/{provider-JWUNG2TD.js → provider-SOC6Z25R.js} +10 -8
  64. package/dist/{provider-manager-2KKZ37XJ.js → provider-manager-Y53SVVQ3.js} +10 -8
  65. package/dist/{registry-BEIR7Y5G.js → registry-FMDMGIUB.js} +3 -3
  66. package/dist/{serve-HSDVY37K.js → serve-EWA5HXC7.js} +41 -32
  67. package/dist/server/index.d.ts +16 -0
  68. package/dist/server/index.js +34 -30
  69. package/dist/server-ZAPAB3FS.js +61 -0
  70. package/dist/{service-XIUNS5XV.js → service-NKLQH647.js} +12 -9
  71. package/dist/{session-overrides-Y5B4XQPU.js → session-overrides-ANQBKV74.js} +11 -7
  72. package/dist/{sessions-IGINPPBP.js → sessions-GLYACE5R.js} +6 -4
  73. package/dist/{settings-ZJPM6PQV.js → settings-WH6BVQCP.js} +4 -2
  74. package/dist/{tasks-3JJWJJ3X.js → tasks-GPM3JAE6.js} +14 -10
  75. package/dist/tool-adapter-ZY4FC4C4.js +9 -0
  76. package/dist/{tools-WWIWD3O7.js → tools-EJOABHVO.js} +27 -23
  77. package/dist/{update-Q6SH7UP2.js → update-IPHJMRQZ.js} +2 -2
  78. package/dist/web/assets/index-CfFwerN2.js +313 -0
  79. package/dist/web/assets/{index-Y300llHY.css → index-ChJjYovg.css} +1 -1
  80. package/dist/web/index.html +2 -2
  81. package/dist/web/sw.js +1 -1
  82. package/package.json +1 -1
  83. package/dist/chunk-QTLYD2FF.js +0 -7
  84. package/dist/client-VVYOWXRR.js +0 -16
  85. package/dist/launch-FJ65HRV4.js +0 -54
  86. package/dist/server-3UVLECD5.js +0 -57
  87. package/dist/tool-adapter-FJAFLDZA.js +0 -8
  88. package/dist/web/assets/index-C48qmBAI.js +0 -313
@@ -1,3 +1,10 @@
1
+ import {
2
+ findSkillById,
3
+ getAllInstructions,
4
+ getEnabledSkillMetadata,
5
+ isSkillEnabled,
6
+ loadAllSkills
7
+ } from "./chunk-Q33DL67W.js";
1
8
  import {
2
9
  getGitBranch,
3
10
  isGitRepository,
@@ -6,12 +13,30 @@ import {
6
13
  import {
7
14
  getGateConfig,
8
15
  isTaskConflictError,
9
- isTaskGateError,
10
- serverT
11
- } from "./chunk-OZWHRBDY.js";
16
+ isTaskGateError
17
+ } from "./chunk-QBWJCO5K.js";
12
18
  import {
13
19
  getSessionDisabledServers
14
- } from "./chunk-QDARWVQ2.js";
20
+ } from "./chunk-DG2YSWMC.js";
21
+ import {
22
+ buildSignal,
23
+ computeFileHash,
24
+ createTool,
25
+ decodeContent,
26
+ describeImageTool,
27
+ detectEncoding,
28
+ encodeContent,
29
+ loadResolvedVisionModel,
30
+ processContextImages,
31
+ readFileTool,
32
+ requestUserConfirmation,
33
+ requireSession,
34
+ validateActionWithPermission,
35
+ validateFileForWrite
36
+ } from "./chunk-U6FHHT2B.js";
37
+ import {
38
+ serverT
39
+ } from "./chunk-4SBVW4UH.js";
15
40
  import {
16
41
  TurnMetrics,
17
42
  clearLLMFailure,
@@ -25,7 +50,12 @@ import {
25
50
  recordLLMFailure,
26
51
  sleepThroughRetryBackoff,
27
52
  streamLLMPure
28
- } from "./chunk-7ET46SNE.js";
53
+ } from "./chunk-HEYU7FAY.js";
54
+ import {
55
+ COMPACTION_PROMPT,
56
+ buildBasePrompt,
57
+ computeEffectiveTools
58
+ } from "./chunk-4LLGI372.js";
29
59
  import {
30
60
  startInspectProxy
31
61
  } from "./chunk-LW6MMSTD.js";
@@ -42,7 +72,7 @@ import {
42
72
  spawnShellProcess,
43
73
  startProcessCommand,
44
74
  stopProcess
45
- } from "./chunk-5UMXFLET.js";
75
+ } from "./chunk-SAGDTQXD.js";
46
76
  import {
47
77
  getMaxPerSession,
48
78
  getSessionProcessCount,
@@ -50,39 +80,25 @@ import {
50
80
  } from "./chunk-I72ACQ6R.js";
51
81
  import {
52
82
  createMcpTools
53
- } from "./chunk-JE7LW7Y6.js";
83
+ } from "./chunk-VIJVLLPT.js";
54
84
  import {
55
85
  applyMcpServerUpdate
56
86
  } from "./chunk-OAN4BXEW.js";
57
- import {
58
- findSkillById,
59
- getAllInstructions,
60
- getEnabledSkillMetadata,
61
- isSkillEnabled,
62
- loadAllSkills
63
- } from "./chunk-DWA4IE65.js";
64
- import {
65
- COMPACTION_PROMPT,
66
- buildBasePrompt,
67
- computeEffectiveTools
68
- } from "./chunk-7QMWYNVF.js";
69
87
  import {
70
88
  AskUserInterrupt,
71
89
  askUserTool
72
- } from "./chunk-JURM3RPZ.js";
90
+ } from "./chunk-V5AC2TV7.js";
73
91
  import {
74
92
  PathAccessDeniedError,
75
93
  extractAbsolutePathsFromCommand,
76
94
  extractSensitivePathsFromCommand,
77
- registerPathConfirmation,
78
- requestPathAccess,
79
95
  resolveRelativeTraversals
80
- } from "./chunk-YLVOU7T7.js";
96
+ } from "./chunk-PQKLOLGC.js";
81
97
  import {
82
98
  getCurrentContextWindowId,
83
99
  getCurrentWindowMessageOptions,
84
100
  getEventStore
85
- } from "./chunk-CSYOWFPU.js";
101
+ } from "./chunk-342BKOTE.js";
86
102
  import {
87
103
  buildContextMessagesFromEventHistory,
88
104
  foldContextState,
@@ -92,20 +108,19 @@ import {
92
108
  handleToolResult,
93
109
  reorderToolMessages,
94
110
  stripOrphanedToolCalls
95
- } from "./chunk-MEEUXRFL.js";
111
+ } from "./chunk-XGG4XCKP.js";
96
112
  import {
97
113
  findAgentById,
98
114
  getRuntimeConfig,
99
115
  getSubAgents,
100
116
  loadAllAgentsDefault
101
- } from "./chunk-ORCQ3EXQ.js";
117
+ } from "./chunk-BHVR2YS5.js";
102
118
  import {
103
119
  createChatDoneMessage,
104
120
  createChatLLMRetryFailedMessage,
105
121
  createChatLLMRetryMessage,
106
122
  createChatMessageMessage,
107
123
  createChatMessageUpdatedMessage,
108
- createChatPathConfirmationMessage,
109
124
  createChatStatsMessage,
110
125
  createQueueStateMessage
111
126
  } from "./chunk-AATMPL7S.js";
@@ -115,20 +130,16 @@ import {
115
130
  } from "./chunk-TS5WKTFB.js";
116
131
  import {
117
132
  OUTPUT_LIMITS,
118
- cacheSet,
119
- contentHash,
120
- decodeDataUrl,
121
- extractPdfContent,
122
133
  extractPdfText,
123
134
  formatPdfErrorMessage,
124
135
  isPdfBuffer,
125
136
  processPdfContent
126
- } from "./chunk-EUOWCVU6.js";
137
+ } from "./chunk-FSSUPWYO.js";
127
138
  import {
128
139
  SETTINGS_KEYS,
129
140
  getSetting,
130
141
  setSetting
131
- } from "./chunk-CTWZPN7G.js";
142
+ } from "./chunk-QUAEH3MP.js";
132
143
  import {
133
144
  logger
134
145
  } from "./chunk-K44MW7JJ.js";
@@ -140,630 +151,9 @@ import {
140
151
  getGlobalConfigDir
141
152
  } from "./chunk-CQGTEGKL.js";
142
153
 
143
- // src/server/tools/read.ts
144
- import { readFile as readFile2, stat, readdir } from "fs/promises";
145
- import { extname, join, relative as relative2 } from "path";
146
-
147
- // src/server/tools/tool-helpers.ts
148
- import { randomUUID } from "crypto";
149
- import { resolve } from "path";
150
- var confirmationSeqCounter = 0;
151
- async function requestUserConfirmation(context, toolLabel, desc) {
152
- if (context.dangerLevel === "dangerous") return Promise.resolve(true);
153
- if (toolLabel === "workspace" || toolLabel === "command") {
154
- try {
155
- const { getSetting: getSetting2, SETTINGS_KEYS: SETTINGS_KEYS2 } = await import("./settings-ZJPM6PQV.js");
156
- if (getSetting2(SETTINGS_KEYS2.CONFIRM_ON_WORKSPACE_ACTIONS) !== "true") {
157
- return Promise.resolve(true);
158
- }
159
- } catch {
160
- }
161
- }
162
- if (context.isSubAgent) return Promise.resolve(false);
163
- if (typeof context.onEvent !== "function") return Promise.resolve(false);
164
- const seq = ++confirmationSeqCounter;
165
- const callId = context.toolCallId ? `${context.toolCallId}-${seq}` : randomUUID();
166
- try {
167
- const eventStore = getEventStore();
168
- eventStore.append(context.sessionId, {
169
- type: "path.confirmation_pending",
170
- data: { callId, tool: toolLabel, paths: [desc], workdir: context.workdir, reason: "dangerous_command" }
171
- });
172
- } catch {
173
- }
174
- const promise = registerPathConfirmation(
175
- callId,
176
- [desc],
177
- context.sessionId,
178
- toolLabel,
179
- context.workdir,
180
- "dangerous_command"
181
- );
182
- context.onEvent(
183
- createChatPathConfirmationMessage(callId, toolLabel, [desc], context.workdir, "dangerous_command")
184
- );
185
- return promise;
186
- }
187
- function buildSignal(timeoutMs, contextSignal) {
188
- const timeoutSignal = AbortSignal.timeout(timeoutMs);
189
- if (contextSignal) {
190
- return AbortSignal.any([timeoutSignal, contextSignal]);
191
- }
192
- return timeoutSignal;
193
- }
194
- function validateAction(action, allowed, startTime) {
195
- if (!action || !allowed.includes(action)) {
196
- return {
197
- success: false,
198
- error: serverT(
199
- {
200
- en: "Invalid action: {{action}}. Must be one of: {{allowed}}",
201
- fr: "Action invalide : {{action}}. Doit \xEAtre l\u2019une des actions suivantes : {{allowed}}"
202
- },
203
- { action: action ?? "", allowed: allowed.join(", ") }
204
- ),
205
- durationMs: Date.now() - startTime,
206
- truncated: false
207
- };
208
- }
209
- return void 0;
210
- }
211
- function checkActionPermission(action, permittedActions, startTime) {
212
- if (action && permittedActions && permittedActions.length > 0 && !permittedActions.includes(action)) {
213
- return {
214
- success: false,
215
- error: serverT(
216
- {
217
- en: "Action '{{action}}' not allowed. Available: {{available}}",
218
- fr: "Action \xAB {{action}} \xBB non autoris\xE9e. Disponibles : {{available}}"
219
- },
220
- { action: action ?? "", available: permittedActions.join(", ") }
221
- ),
222
- durationMs: Date.now() - startTime,
223
- truncated: false
224
- };
225
- }
226
- return void 0;
227
- }
228
- function requireSession(sessionManager, sessionId) {
229
- return sessionManager.requireSession(sessionId);
230
- }
231
- function validateActionWithPermission(action, allowedActions, toolName, permittedActions, startTime) {
232
- const actionError = validateAction(action, allowedActions, startTime ?? Date.now());
233
- if (actionError) return actionError;
234
- const permittedToolActions = permittedActions?.[toolName];
235
- const permissionError = checkActionPermission(action, permittedToolActions, startTime ?? Date.now());
236
- if (permissionError) return permissionError;
237
- return void 0;
238
- }
239
- function createTool(name, definition, handler2) {
240
- return {
241
- name,
242
- definition,
243
- async execute(args, context) {
244
- const startTime = Date.now();
245
- const helpers = {
246
- resolvePath: (path) => resolve(context.workdir, path),
247
- checkPathAccess: async (paths, command) => {
248
- if (context.onEvent) {
249
- await requestPathAccess(
250
- paths,
251
- context.workdir,
252
- context.sessionId,
253
- context.toolCallId ?? crypto.randomUUID(),
254
- name,
255
- context.onEvent,
256
- context.dangerLevel,
257
- command,
258
- context.isSubAgent
259
- );
260
- }
261
- },
262
- error: (message, truncated = false) => ({
263
- success: false,
264
- error: message,
265
- durationMs: Date.now() - startTime,
266
- truncated
267
- }),
268
- success: (output, truncated = false, extra) => ({
269
- success: true,
270
- output,
271
- durationMs: Date.now() - startTime,
272
- truncated,
273
- ...extra
274
- })
275
- };
276
- try {
277
- return await handler2(args, context, helpers);
278
- } catch (error) {
279
- if (error instanceof PathAccessDeniedError || error instanceof AskUserInterrupt) {
280
- throw error;
281
- }
282
- return {
283
- success: false,
284
- error: error instanceof Error ? error.message : serverT({
285
- en: "Unknown error in tool execution",
286
- fr: "Erreur inconnue lors de l\u2019ex\xE9cution de l\u2019outil"
287
- }),
288
- durationMs: Date.now() - startTime,
289
- truncated: false
290
- };
291
- }
292
- }
293
- };
294
- }
295
-
296
- // src/server/tools/file-tracker.ts
297
- import { createHash } from "crypto";
298
- import { readFile } from "fs/promises";
299
- import { relative, resolve as resolve2 } from "path";
300
- var FileNotReadError = class extends Error {
301
- constructor(path) {
302
- super(`File "${path}" must be read before writing. Use read_file first.`);
303
- this.name = "FileNotReadError";
304
- }
305
- };
306
- var FileChangedExternallyError = class extends Error {
307
- constructor(path) {
308
- super(
309
- `File "${path}" was modified externally and must be read before writing. Use read_file to see current contents.`
310
- );
311
- this.name = "FileChangedExternallyError";
312
- }
313
- };
314
- async function computeFileHash(filePath) {
315
- try {
316
- const content = await readFile(filePath);
317
- const hash = createHash("sha256");
318
- hash.update(content);
319
- return hash.digest("hex");
320
- } catch (error) {
321
- if (isNodeError(error) && error.code === "ENOENT") {
322
- return null;
323
- }
324
- throw error;
325
- }
326
- }
327
- async function validateFileForWrite(filePath, readFiles, baseDir) {
328
- const normalizedPath = resolve2(filePath);
329
- const currentHash = await computeFileHash(normalizedPath);
330
- if (currentHash === null) {
331
- return { valid: true };
332
- }
333
- const readEntry = readFiles[normalizedPath];
334
- if (readEntry) {
335
- if (readEntry.hash !== currentHash) {
336
- return {
337
- valid: false,
338
- error: new FileChangedExternallyError(filePath)
339
- };
340
- }
341
- return { valid: true };
342
- }
343
- if (baseDir) {
344
- const relPath = relative(baseDir, normalizedPath);
345
- if (!relPath.startsWith("..")) {
346
- const relMatches = Object.values(readFiles).filter((entry) => entry.relPath === relPath);
347
- if (relMatches.some((entry) => entry.hash === currentHash)) {
348
- return { valid: true };
349
- }
350
- }
351
- }
352
- return {
353
- valid: false,
354
- error: new FileNotReadError(filePath)
355
- };
356
- }
357
- function isNodeError(error) {
358
- return error instanceof Error && "code" in error;
359
- }
360
-
361
- // src/server/utils/encoding.ts
362
- import { detect } from "jschardet";
363
- import * as iconv from "iconv-lite";
364
- function detectEncoding(buffer) {
365
- if (buffer.length === 0) {
366
- return { encoding: "utf-8", confidence: 1, bomSize: 0 };
367
- }
368
- const bomSize = detectBOM(buffer);
369
- const bomStripped = bomSize > 0 ? buffer.subarray(bomSize) : buffer;
370
- if (bomSize > 0) {
371
- const bomEncoding = bomEncodingForSize(buffer);
372
- return { encoding: bomEncoding, confidence: 1, bomSize };
373
- }
374
- if (isValidUtf8(bomStripped)) {
375
- return { encoding: "utf-8", confidence: 0.95, bomSize: 0 };
376
- }
377
- const detected = detect(bomStripped);
378
- if (detected && detected.encoding && detected.confidence > 0.2) {
379
- return { encoding: normalizeEncoding(detected.encoding), confidence: detected.confidence, bomSize: 0 };
380
- }
381
- return { encoding: "ISO-8859-1", confidence: 0.1, bomSize: 0 };
382
- }
383
- function detectBOM(buffer) {
384
- if (buffer.length >= 4 && buffer[0] === 0 && buffer[1] === 0 && buffer[2] === 254 && buffer[3] === 255)
385
- return 4;
386
- if (buffer.length >= 4 && buffer[0] === 255 && buffer[1] === 254 && buffer[2] === 0 && buffer[3] === 0)
387
- return 4;
388
- if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) return 2;
389
- if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) return 2;
390
- if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) return 3;
391
- return 0;
392
- }
393
- function bomEncodingForSize(buffer) {
394
- if (buffer.length >= 2) {
395
- if (buffer[0] === 254 && buffer[1] === 255) return "utf-16be";
396
- if (buffer[0] === 255 && buffer[1] === 254) {
397
- if (buffer.length >= 4 && buffer[2] === 0 && buffer[3] === 0) return "utf-32";
398
- return "utf-16le";
399
- }
400
- }
401
- if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) return "utf-8";
402
- if (buffer.length >= 4 && buffer[0] === 0 && buffer[1] === 0 && buffer[2] === 254 && buffer[3] === 255)
403
- return "utf-32be";
404
- return "utf-8";
405
- }
406
- function isValidUtf8(buffer) {
407
- const decoder = new TextDecoder("utf-8", { fatal: true });
408
- try {
409
- decoder.decode(buffer);
410
- return true;
411
- } catch {
412
- return false;
413
- }
414
- }
415
- function normalizeEncoding(enc) {
416
- const lower = enc.toLowerCase().replace(/_/g, "-");
417
- const map = {
418
- "utf-8": "utf-8",
419
- utf8: "utf-8",
420
- ascii: "utf-8",
421
- "iso-8859-1": "ISO-8859-1",
422
- "iso8859-1": "ISO-8859-1",
423
- latin1: "ISO-8859-1",
424
- "windows-1252": "windows-1252",
425
- "shift-jis": "Shift_JIS",
426
- shift_jis: "Shift_JIS",
427
- "euc-jp": "EUC-JP",
428
- "euc-kr": "EUC-KR",
429
- gb2312: "GB2312",
430
- gb18030: "GB18030",
431
- big5: "Big5",
432
- "utf-16": "utf-16le",
433
- "utf-16le": "utf-16le",
434
- "utf-16be": "utf-16be",
435
- "utf-32": "utf-32le",
436
- "utf-32le": "utf-32le",
437
- "utf-32be": "utf-32be",
438
- "koi8-r": "KOI8-R",
439
- "koi8-u": "KOI8-U"
440
- };
441
- return map[lower] ?? enc;
442
- }
443
- function decodeContent(buffer, encoding) {
444
- const bomSize = detectBOM(buffer);
445
- const contentBuf = bomSize > 0 ? buffer.subarray(bomSize) : buffer;
446
- const normalized = normalizeEncoding(encoding);
447
- if (normalized === "utf-8") {
448
- return contentBuf.toString("utf-8");
449
- }
450
- if (normalized === "utf-16le") {
451
- return contentBuf.toString("utf-16le");
452
- }
453
- if (normalized === "utf-16be") {
454
- return swapUtf16Endianness(contentBuf).toString("utf-16le");
455
- }
456
- if (normalized === "utf-32le") {
457
- return utf32ToString(contentBuf, true);
458
- }
459
- if (normalized === "utf-32be") {
460
- return utf32ToString(contentBuf, false);
461
- }
462
- return iconv.decode(contentBuf, normalized);
463
- }
464
- function encodeContent(content, encoding, addBom = false) {
465
- const normalized = normalizeEncoding(encoding);
466
- let encoded;
467
- if (normalized === "utf-8") {
468
- encoded = Buffer.from(content, "utf-8");
469
- } else if (normalized === "utf-16le") {
470
- encoded = Buffer.from(content, "utf-16le");
471
- } else if (normalized === "utf-16be") {
472
- encoded = swapUtf16Endianness(Buffer.from(content, "utf-16le"));
473
- } else if (normalized === "utf-32le") {
474
- encoded = stringToUtf32(content, true);
475
- } else if (normalized === "utf-32be") {
476
- encoded = stringToUtf32(content, false);
477
- } else {
478
- encoded = iconv.encode(content, normalized);
479
- }
480
- if (addBom) {
481
- const bom = bomForEncoding(normalized);
482
- if (bom) {
483
- encoded = Buffer.concat([bom, encoded]);
484
- }
485
- }
486
- return encoded;
487
- }
488
- function bomForEncoding(encoding) {
489
- switch (encoding) {
490
- case "utf-8":
491
- return Buffer.from([239, 187, 191]);
492
- case "utf-16le":
493
- return Buffer.from([255, 254]);
494
- case "utf-16be":
495
- return Buffer.from([254, 255]);
496
- case "utf-32le":
497
- return Buffer.from([255, 254, 0, 0]);
498
- case "utf-32be":
499
- return Buffer.from([0, 0, 254, 255]);
500
- default:
501
- return null;
502
- }
503
- }
504
- function swapUtf16Endianness(buf) {
505
- const out = Buffer.alloc(buf.length);
506
- for (let i = 0; i + 1 < buf.length; i += 2) {
507
- out[i] = buf[i + 1];
508
- out[i + 1] = buf[i];
509
- }
510
- return out;
511
- }
512
- function utf32ToString(buffer, littleEndian) {
513
- const chars = [];
514
- for (let i = 0; i + 3 < buffer.length; i += 4) {
515
- const code = littleEndian ? buffer.readUInt32LE(i) : buffer.readUInt32BE(i);
516
- if (code > 1114111) continue;
517
- chars.push(String.fromCodePoint(code));
518
- }
519
- return chars.join("");
520
- }
521
- function stringToUtf32(str, littleEndian) {
522
- const codes = [];
523
- for (const ch of str) {
524
- codes.push(ch.codePointAt(0));
525
- }
526
- const buf = Buffer.alloc(codes.length * 4);
527
- for (let i = 0; i < codes.length; i++) {
528
- if (littleEndian) {
529
- buf.writeUInt32LE(codes[i], i * 4);
530
- } else {
531
- buf.writeUInt32BE(codes[i], i * 4);
532
- }
533
- }
534
- return buf;
535
- }
536
-
537
- // src/server/tools/read.ts
538
- import { fileTypeFromBuffer } from "file-type";
539
- async function detectImageType(buffer, filePath) {
540
- const fileType = await fileTypeFromBuffer(buffer);
541
- const imageMimeTypes = ["image/png", "image/jpeg", "image/gif", "image/webp", "image/bmp", "image/svg+xml"];
542
- if (fileType?.mime && imageMimeTypes.includes(fileType.mime)) {
543
- return fileType.mime;
544
- }
545
- const ext = extname(filePath).toLowerCase();
546
- if (ext === ".svg" && buffer.length > 0) {
547
- const content = buffer.toString("utf-8");
548
- const trimmedStart = content.trimStart();
549
- if (trimmedStart.startsWith("<?xml") || trimmedStart.startsWith("<svg")) {
550
- return "image/svg+xml";
551
- }
552
- }
553
- return null;
554
- }
555
- function formatSize(bytes) {
556
- if (bytes < 1024) return `${bytes} B`;
557
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
558
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
559
- }
560
- async function listDirectory(dirPath, relativePath) {
561
- const entries = await readdir(dirPath, { withFileTypes: true });
562
- const sorted = [...entries].sort((a, b) => {
563
- if (a.isDirectory() !== b.isDirectory()) {
564
- return a.isDirectory() ? -1 : 1;
565
- }
566
- return a.name.localeCompare(b.name);
567
- });
568
- const lines = [`${relativePath}/`];
569
- for (let i = 0; i < sorted.length; i++) {
570
- const entry = sorted[i];
571
- const isLast = i === sorted.length - 1;
572
- const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
573
- const suffix = entry.isDirectory() ? "/" : "";
574
- let sizeStr = "";
575
- if (!entry.isDirectory()) {
576
- try {
577
- const entryStat = await stat(join(dirPath, entry.name));
578
- sizeStr = ` ${formatSize(entryStat.size).padStart(7)}`;
579
- } catch {
580
- }
581
- }
582
- lines.push(`${prefix}${entry.name}${suffix}${sizeStr}`);
583
- }
584
- return lines.join("\n");
585
- }
586
- var readFileTool = createTool(
587
- "read_file",
588
- {
589
- type: "function",
590
- function: {
591
- name: "read_file",
592
- description: "Read the contents of a file or list directory contents. For text files: returns file content. For images (PNG, JPEG, GIF, WebP, BMP, SVG): returns base64-encoded data with MIME type metadata. For PDF files: extracts text content page by page. For directories: returns a tree-formatted listing of entries with file sizes.",
593
- parameters: {
594
- type: "object",
595
- properties: {
596
- path: {
597
- type: "string",
598
- description: "Path to the file (relative to workdir or absolute)"
599
- },
600
- offset: {
601
- type: "number",
602
- description: "Line number to start from (1-indexed). Default: 1. Only applies to text files."
603
- },
604
- limit: {
605
- type: "number",
606
- description: `Maximum number of lines to read. Default: ${OUTPUT_LIMITS.read_file.maxLines}. Only applies to text files.`
607
- }
608
- },
609
- required: ["path"]
610
- }
611
- }
612
- },
613
- async (args, context, helpers) => {
614
- const offset = args.offset ?? 1;
615
- const limit = Math.min(args.limit ?? OUTPUT_LIMITS.read_file.maxLines, OUTPUT_LIMITS.read_file.maxLines);
616
- const fullPath = helpers.resolvePath(args.path);
617
- await helpers.checkPathAccess([fullPath]);
618
- try {
619
- const stats = await stat(fullPath);
620
- if (stats.isDirectory()) {
621
- const listing = await listDirectory(fullPath, args.path);
622
- return helpers.success(listing);
623
- }
624
- if (stats.size > OUTPUT_LIMITS.read_file.maxFileBytes) {
625
- return helpers.error(
626
- serverT(
627
- {
628
- en: "File size ({{size}} bytes) exceeds maximum file size (20MB). Use shell command to process large files.",
629
- fr: "La taille du fichier ({{size}} octets) d\xE9passe la taille maximale (20 Mo). Utilisez une commande shell pour traiter les fichiers volumineux."
630
- },
631
- { size: stats.size }
632
- )
633
- );
634
- }
635
- if (stats.size > OUTPUT_LIMITS.read_file.maxImageBytes) {
636
- return helpers.error(
637
- serverT(
638
- {
639
- en: "File size ({{size}} bytes) exceeds image size limit (2MB). Use shell command to process large files.",
640
- fr: "La taille du fichier ({{size}} octets) d\xE9passe la limite pour les images (2 Mo). Utilisez une commande shell pour traiter les fichiers volumineux."
641
- },
642
- { size: stats.size }
643
- )
644
- );
645
- }
646
- } catch {
647
- return helpers.error(
648
- serverT({ en: "File not found: {{path}}", fr: "Fichier introuvable : {{path}}" }, { path: args.path })
649
- );
650
- }
651
- const rawBuffer = await readFile2(fullPath);
652
- const mimeType = await detectImageType(rawBuffer, args.path);
653
- if (mimeType) {
654
- const base64Data = rawBuffer.toString("base64");
655
- const dataUrl = `data:${mimeType};base64,${base64Data}`;
656
- const contentHash3 = await computeFileHash(fullPath);
657
- if (contentHash3) {
658
- context.sessionManager.recordFileRead(
659
- context.sessionId,
660
- fullPath,
661
- contentHash3,
662
- relative2(context.workdir, fullPath)
663
- );
664
- }
665
- return helpers.success(
666
- serverT(
667
- { en: "[Image: {{path}} ({{mime}}, {{size}} bytes)]", fr: "[Image : {{path}} ({{mime}}, {{size}} octets)]" },
668
- { path: args.path, mime: mimeType, size: rawBuffer.length }
669
- ),
670
- false,
671
- {
672
- metadata: {
673
- mimeType,
674
- size: rawBuffer.length,
675
- base64Data,
676
- dataUrl,
677
- path: fullPath
678
- }
679
- }
680
- );
681
- }
682
- if (isPdfBuffer(rawBuffer)) {
683
- try {
684
- const { text, pageCount, title, author } = await extractPdfText(rawBuffer);
685
- const { output: output2, truncated: truncated2, isScanned } = processPdfContent(text, OUTPUT_LIMITS.read_file.maxBytes);
686
- const contentHash3 = await computeFileHash(fullPath);
687
- if (contentHash3) {
688
- context.sessionManager.recordFileRead(
689
- context.sessionId,
690
- fullPath,
691
- contentHash3,
692
- relative2(context.workdir, fullPath)
693
- );
694
- }
695
- if (isScanned) {
696
- return helpers.success(
697
- `[PDF: ${args.path} \u2014 This PDF has no text layer (scanned or image-only). Use a shell command with OCR to extract text.]`,
698
- false,
699
- {
700
- metadata: {
701
- format: "pdf",
702
- pageCount,
703
- title,
704
- author,
705
- path: fullPath
706
- }
707
- }
708
- );
709
- }
710
- return helpers.success(output2, truncated2, {
711
- metadata: {
712
- format: "pdf",
713
- pageCount,
714
- title,
715
- author,
716
- path: fullPath
717
- }
718
- });
719
- } catch (err) {
720
- return helpers.error(formatPdfErrorMessage(err));
721
- }
722
- }
723
- const { encoding, confidence } = detectEncoding(rawBuffer);
724
- const content = decodeContent(rawBuffer, encoding);
725
- const lines = content.split("\n");
726
- const totalLines = lines.length;
727
- const startLine = Math.max(1, offset);
728
- const endLine = Math.min(startLine + limit - 1, totalLines);
729
- const selectedLines = lines.slice(startLine - 1, endLine);
730
- const formatted = selectedLines.join("\n");
731
- const truncated = endLine < totalLines;
732
- let output = formatted;
733
- if (truncated) {
734
- output += `
735
-
736
- [Showing lines ${startLine}-${endLine} of ${totalLines} total. Use offset to read more.]`;
737
- }
738
- if (output.length > OUTPUT_LIMITS.read_file.maxBytes) {
739
- output = output.slice(0, OUTPUT_LIMITS.read_file.maxBytes);
740
- output += "\n\n[Output truncated due to size limit]";
741
- }
742
- const contentHash2 = await computeFileHash(fullPath);
743
- if (contentHash2) {
744
- context.sessionManager.recordFileRead(
745
- context.sessionId,
746
- fullPath,
747
- contentHash2,
748
- relative2(context.workdir, fullPath)
749
- );
750
- }
751
- return helpers.success(output, truncated, {
752
- metadata: {
753
- encoding,
754
- confidence: Math.round(confidence * 100) / 100,
755
- lineCount: totalLines,
756
- startLine,
757
- endLine,
758
- path: fullPath
759
- }
760
- });
761
- }
762
- );
763
-
764
154
  // src/server/tools/write.ts
765
155
  import { writeFile, mkdir } from "fs/promises";
766
- import { dirname, relative as relative3 } from "path";
156
+ import { dirname, relative } from "path";
767
157
 
768
158
  // src/server/tools/diagnostics.ts
769
159
  function formatDiagnosticsForLLM(diagnostics) {
@@ -858,7 +248,7 @@ var writeFileTool = createTool(
858
248
  }
859
249
  const newHash = await computeFileHash(fullPath);
860
250
  if (newHash) {
861
- context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash, relative3(context.workdir, fullPath));
251
+ context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash, relative(context.workdir, fullPath));
862
252
  }
863
253
  return helpers.success(output, false, {
864
254
  ...diagnostics.length > 0 && { diagnostics },
@@ -868,8 +258,8 @@ var writeFileTool = createTool(
868
258
  );
869
259
 
870
260
  // src/server/tools/edit.ts
871
- import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
872
- import { relative as relative4 } from "path";
261
+ import { readFile, writeFile as writeFile2 } from "fs/promises";
262
+ import { relative as relative2 } from "path";
873
263
 
874
264
  // src/server/tools/edit-context.ts
875
265
  var CONTEXT_LINES = 4;
@@ -1043,7 +433,7 @@ var editFileTool = createTool(
1043
433
  }
1044
434
  let rawBuffer;
1045
435
  try {
1046
- rawBuffer = await readFile3(fullPath);
436
+ rawBuffer = await readFile(fullPath);
1047
437
  } catch {
1048
438
  return helpers.error(
1049
439
  serverT({ en: "File not found: {{path}}", fr: "Fichier introuvable : {{path}}" }, { path: args.path })
@@ -1141,7 +531,7 @@ var editFileTool = createTool(
1141
531
  }
1142
532
  const newHash = await computeFileHash(fullPath);
1143
533
  if (newHash) {
1144
- context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash, relative4(context.workdir, fullPath));
534
+ context.sessionManager.updateFileHash(context.sessionId, fullPath, newHash, relative2(context.workdir, fullPath));
1145
535
  }
1146
536
  return helpers.success(output, false, {
1147
537
  ...diagnostics.length > 0 && { diagnostics },
@@ -1154,7 +544,7 @@ var editFileTool = createTool(
1154
544
 
1155
545
  // src/server/tools/shell.ts
1156
546
  import { spawn } from "child_process";
1157
- import { resolve as resolve3, isAbsolute } from "path";
547
+ import { resolve, isAbsolute } from "path";
1158
548
  import { access } from "fs/promises";
1159
549
  import stripAnsi from "strip-ansi";
1160
550
 
@@ -1211,7 +601,7 @@ async function checkRtkAvailability() {
1211
601
  rtkAvailable = true;
1212
602
  } catch {
1213
603
  try {
1214
- const out = await new Promise((resolve5, reject) => {
604
+ const out = await new Promise((resolve3, reject) => {
1215
605
  const proc = spawn("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
1216
606
  let output = "";
1217
607
  proc.stdout?.on("data", (d) => {
@@ -1219,7 +609,7 @@ async function checkRtkAvailability() {
1219
609
  });
1220
610
  proc.on("error", reject);
1221
611
  proc.on("close", (code) => {
1222
- if (code === 0) resolve5(output.trim());
612
+ if (code === 0) resolve3(output.trim());
1223
613
  else reject(new Error(`exit ${code}`));
1224
614
  });
1225
615
  });
@@ -1310,7 +700,7 @@ var runCommandTool = createTool(
1310
700
  pathsToCheck.push(...resolveRelativeTraversals(args.command, workingDir));
1311
701
  const sensitivePaths = extractSensitivePathsFromCommand(args.command);
1312
702
  for (const sensitivePath of sensitivePaths) {
1313
- const resolved = isAbsolute(sensitivePath) ? sensitivePath : resolve3(workingDir, sensitivePath);
703
+ const resolved = isAbsolute(sensitivePath) ? sensitivePath : resolve(workingDir, sensitivePath);
1314
704
  pathsToCheck.push(resolved);
1315
705
  }
1316
706
  await helpers.checkPathAccess(pathsToCheck, args.command);
@@ -1365,7 +755,7 @@ ${result.stderr}`;
1365
755
  async function tryRtkRewrite(command) {
1366
756
  if (!await checkRtkAvailability()) return command;
1367
757
  try {
1368
- const result = await new Promise((resolve5, reject) => {
758
+ const result = await new Promise((resolve3, reject) => {
1369
759
  const proc = spawn("rtk", ["rewrite", command], {
1370
760
  stdio: ["ignore", "pipe", "pipe"],
1371
761
  timeout: 2e3
@@ -1376,7 +766,7 @@ async function tryRtkRewrite(command) {
1376
766
  });
1377
767
  proc.on("error", reject);
1378
768
  proc.on("close", (code) => {
1379
- if (code === 0 || code === 3) resolve5(decodeUtf8(chunks).trim());
769
+ if (code === 0 || code === 3) resolve3(decodeUtf8(chunks).trim());
1380
770
  else reject(new Error(`exit ${code}`));
1381
771
  });
1382
772
  });
@@ -1386,7 +776,7 @@ async function tryRtkRewrite(command) {
1386
776
  return command;
1387
777
  }
1388
778
  function executeCommand(command, cwd, timeout, signal, onProgress) {
1389
- return new Promise((resolve5, reject) => {
779
+ return new Promise((resolve3, reject) => {
1390
780
  if (checkAborted(signal)) {
1391
781
  reject(new Error("Command aborted before execution"));
1392
782
  return;
@@ -1422,7 +812,7 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
1422
812
  });
1423
813
  const settle = (exitCode, appendix) => {
1424
814
  const out = (stdout + stdoutDecoder.end()).trim();
1425
- resolve5({
815
+ resolve3({
1426
816
  stdout: appendix ? out ? `${out}
1427
817
 
1428
818
  ${appendix}` : appendix : out,
@@ -1678,8 +1068,8 @@ ${Object.entries(schema.fields).map(([f, t]) => ` ${f}: ${t}`).join("\n")}`
1678
1068
  );
1679
1069
 
1680
1070
  // src/server/sub-agents/manager.ts
1681
- import { readFile as readFile4, access as access2 } from "fs/promises";
1682
- import { join as join2, dirname as dirname2, isAbsolute as isAbsolute2 } from "path";
1071
+ import { readFile as readFile2, access as access2 } from "fs/promises";
1072
+ import { join, dirname as dirname2, isAbsolute as isAbsolute2 } from "path";
1683
1073
 
1684
1074
  // src/server/agents/model-overrides.ts
1685
1075
  import { z } from "zod";
@@ -1827,7 +1217,7 @@ async function executeTools(assistantMsgId, toolCalls, ctx, append) {
1827
1217
  data: { callId: error.callId, question: error.question, type: error.type, options: error.options }
1828
1218
  });
1829
1219
  append(createChatDoneEvent(assistantMsgId, "waiting_for_user"));
1830
- const { awaitAnswer } = await import("./ask-UBIRH3SO.js");
1220
+ const { awaitAnswer } = await import("./ask-46J5WYRL.js");
1831
1221
  const answerPromise = awaitAnswer(error.callId);
1832
1222
  if (!answerPromise) {
1833
1223
  throw new Error(
@@ -2335,7 +1725,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
2335
1725
  currentMaxTokensOverride = void 0;
2336
1726
  if (!compacting) {
2337
1727
  const contextState = sessionManager.getContextState(sessionId);
2338
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-PD6DQ4NY.js");
1728
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-LBK53WAK.js");
2339
1729
  if (shouldCompact(
2340
1730
  contextState.currentTokens,
2341
1731
  contextState.maxTokens,
@@ -2620,323 +2010,6 @@ function createAssemblyResult(input) {
2620
2010
  };
2621
2011
  }
2622
2012
 
2623
- // src/server/llm/vision-fallback.ts
2624
- var IMAGE_PROMPT = `Describe this image thoroughly and accurately. Include:
2625
- - What the image shows (UI, diagram, photo, illustration, etc.)
2626
- - All visible text, transcribed verbatim \u2014 every label, heading, number, and value
2627
- - Spatial layout: where elements are positioned and how they relate
2628
- - Colors, visual hierarchy, and any distinctive visual features
2629
-
2630
- Be exhaustive rather than concise. Prioritize precision \u2014 if there are numbers, lists, or structured data, capture them completely. If the image contains a table or grid, reproduce it as a markdown table with exact values.`;
2631
- function buildPrompt(context) {
2632
- return context ? `${IMAGE_PROMPT}
2633
-
2634
- Context: ${context}` : IMAGE_PROMPT;
2635
- }
2636
- function buildOllamaRequest(base64Data, model, context) {
2637
- return {
2638
- model,
2639
- messages: [
2640
- {
2641
- role: "user",
2642
- content: buildPrompt(context),
2643
- images: [base64Data]
2644
- }
2645
- ],
2646
- stream: false,
2647
- think: false
2648
- };
2649
- }
2650
- function buildOpenAIRequest(base64Data, model, context) {
2651
- return {
2652
- model,
2653
- messages: [
2654
- {
2655
- role: "user",
2656
- content: [
2657
- { type: "text", text: buildPrompt(context) },
2658
- { type: "image_url", image_url: { url: `data:image/png;base64,${base64Data}` } }
2659
- ]
2660
- }
2661
- ]
2662
- };
2663
- }
2664
- function parseOllamaResponse(data) {
2665
- const resp = data;
2666
- return resp.message?.content?.trim() ?? null;
2667
- }
2668
- function parseOpenAIResponse(data) {
2669
- const resp = data;
2670
- const choice = resp.choices?.[0];
2671
- if (!choice) return null;
2672
- return choice.message?.content?.trim() ?? null;
2673
- }
2674
- async function describeImage(base64Data, visionModel, options) {
2675
- const timeout = visionModel.timeout;
2676
- try {
2677
- const isOpenAI = visionModel.backend === "openai";
2678
- const url = isOpenAI ? `${visionModel.baseUrl.replace(/\/+$/, "")}/chat/completions` : `${visionModel.baseUrl.replace(/\/+$/, "")}/api/chat`;
2679
- const requestBody = isOpenAI ? buildOpenAIRequest(base64Data, visionModel.model, options?.context) : buildOllamaRequest(base64Data, visionModel.model, options?.context);
2680
- const timeoutController = new AbortController();
2681
- const timeoutId = setTimeout(() => timeoutController.abort(), timeout);
2682
- const signal = options?.signal ? AbortSignal.any([timeoutController.signal, options.signal]) : timeoutController.signal;
2683
- const headers = {
2684
- "Content-Type": "application/json"
2685
- };
2686
- if (isOpenAI && visionModel.apiKey) {
2687
- headers["Authorization"] = `Bearer ${visionModel.apiKey}`;
2688
- }
2689
- const response = await fetch(url, {
2690
- method: "POST",
2691
- headers,
2692
- body: JSON.stringify(requestBody),
2693
- signal
2694
- });
2695
- clearTimeout(timeoutId);
2696
- if (!response.ok) {
2697
- const errorText = await response.text().catch(() => "Unknown error");
2698
- logger.error("Vision fallback API error", { status: response.status, error: errorText });
2699
- return `[Image description failed: HTTP ${response.status}]`;
2700
- }
2701
- const data = await response.json();
2702
- const description = isOpenAI ? parseOpenAIResponse(data) : parseOllamaResponse(data);
2703
- if (!description) {
2704
- logger.warn("Vision fallback returned empty description");
2705
- return "[Image - could not describe]";
2706
- }
2707
- return description;
2708
- } catch (error) {
2709
- const message = error instanceof Error ? error.message : "Unknown error";
2710
- logger.error("Vision fallback error", { error: message });
2711
- if (message.includes("abort")) {
2712
- return "[Image description timed out]";
2713
- }
2714
- return `[Image description failed: ${message}]`;
2715
- }
2716
- }
2717
- async function describeImageFromDataUrl(dataUrl, visionModel, options) {
2718
- const base64Match = dataUrl.match(/^data:image\/[^;]+;base64,(.+)$/);
2719
- if (!base64Match || !base64Match[1]) {
2720
- return "[Invalid image data URL]";
2721
- }
2722
- return describeImage(base64Match[1], visionModel, options);
2723
- }
2724
-
2725
- // src/server/context/image-processor.ts
2726
- async function loadVisionModelFromGlobalConfig() {
2727
- try {
2728
- const { loadGlobalConfig: loadGlobalConfig2, resolveVisionFallback } = await import("./config-LOK3LXFH.js");
2729
- const runtimeConfig = getRuntimeConfig();
2730
- const mode = runtimeConfig.mode ?? "production";
2731
- const globalConfig = await loadGlobalConfig2(mode);
2732
- return resolveVisionFallback(globalConfig);
2733
- } catch {
2734
- }
2735
- return void 0;
2736
- }
2737
- var descriptionCache = /* @__PURE__ */ new Map();
2738
- function isImageAttachment(att) {
2739
- return att.mimeType.startsWith("image/");
2740
- }
2741
- function isPdfAttachment(att) {
2742
- return att.mimeType === "application/pdf";
2743
- }
2744
- function hasImageMetadata(result) {
2745
- const meta = result.metadata;
2746
- if (!meta) return false;
2747
- const dataUrl = meta["dataUrl"];
2748
- const mimeType = meta["mimeType"];
2749
- return typeof dataUrl === "string" && typeof mimeType === "string" && mimeType.startsWith("image/");
2750
- }
2751
- async function describeImageDataUrl(dataUrl, attachmentId, messageId, options, descriptions, filename) {
2752
- const cacheKey = contentHash(dataUrl);
2753
- if (descriptionCache.has(cacheKey)) {
2754
- const cached = descriptionCache.get(cacheKey);
2755
- descriptions.set(attachmentId, cached);
2756
- return cached;
2757
- }
2758
- if (options.visionModel) {
2759
- const startData = {
2760
- messageId,
2761
- attachmentId
2762
- };
2763
- if (filename !== void 0) {
2764
- startData.filename = filename;
2765
- }
2766
- options.onEvent?.({ type: "vision_fallback.start", data: startData });
2767
- const description = await describeImageFromDataUrl(dataUrl, options.visionModel, {
2768
- context: filename ? `File: ${filename}` : void 0,
2769
- signal: options.signal
2770
- });
2771
- cacheSet(descriptionCache, cacheKey, description);
2772
- descriptions.set(attachmentId, description);
2773
- options.onEvent?.({ type: "vision_fallback.done", data: { messageId, attachmentId, description } });
2774
- return description;
2775
- }
2776
- const placeholder = filename ? `[Image: ${filename}]` : "[Image]";
2777
- descriptions.set(attachmentId, placeholder);
2778
- return placeholder;
2779
- }
2780
- async function describeAttachment(att, messageId, options, descriptions) {
2781
- return describeImageDataUrl(att.data, att.id, messageId, options, descriptions, att.filename);
2782
- }
2783
- async function describePdfAttachment(att, messageId, options, descriptions) {
2784
- const hasVision = !!options.visionModel;
2785
- const cacheKey = `pdf:${hasVision}:${contentHash(att.data)}`;
2786
- const filenameLabel = att.filename || "document.pdf";
2787
- if (descriptionCache.has(cacheKey)) {
2788
- const cached = descriptionCache.get(cacheKey);
2789
- const output2 = `[PDF: ${filenameLabel}]${cached}`;
2790
- descriptions.set(att.id, output2);
2791
- return output2;
2792
- }
2793
- const buffer = decodeDataUrl(att.data);
2794
- if (!buffer) {
2795
- const fallback = `[PDF: ${filenameLabel}] (could not decode)`;
2796
- cacheSet(descriptionCache, cacheKey, fallback);
2797
- descriptions.set(att.id, fallback);
2798
- return fallback;
2799
- }
2800
- let blocks;
2801
- try {
2802
- const result = await extractPdfContent(buffer);
2803
- blocks = result.blocks;
2804
- } catch {
2805
- const errorText = `[PDF: ${filenameLabel}] (could not extract content)`;
2806
- descriptions.set(att.id, errorText);
2807
- return errorText;
2808
- }
2809
- let body = "";
2810
- let imageIndex = 0;
2811
- for (const block of blocks) {
2812
- if (block.type === "text" && block.content) {
2813
- body += "\n\n" + block.content;
2814
- } else if (block.type === "image" && block.dataUrl) {
2815
- const imgAttachmentId = `${att.id}/image-${imageIndex}`;
2816
- const description = await describeImageDataUrl(block.dataUrl, imgAttachmentId, messageId, options, descriptions);
2817
- body += "\n\n" + description;
2818
- imageIndex++;
2819
- }
2820
- }
2821
- const output = `[PDF: ${filenameLabel}]${body}`;
2822
- cacheSet(descriptionCache, cacheKey, body);
2823
- descriptions.set(att.id, output);
2824
- return output;
2825
- }
2826
- async function processContextImages(events, options) {
2827
- if (options.modelSupportsVision) {
2828
- return { events, descriptions: /* @__PURE__ */ new Map() };
2829
- }
2830
- const descriptions = /* @__PURE__ */ new Map();
2831
- const modifiedEvents = events.map((event) => structuredClone(event));
2832
- for (const event of modifiedEvents) {
2833
- if (event.type === "message.start") {
2834
- const data = event.data;
2835
- if (!data.attachments || data.attachments.length === 0) continue;
2836
- let enriched = false;
2837
- const imageAtts = data.attachments.filter(isImageAttachment);
2838
- for (const att of imageAtts) {
2839
- if (att.description) {
2840
- descriptions.set(att.id, att.description);
2841
- continue;
2842
- }
2843
- const description = await describeAttachment(att, data.messageId, options, descriptions);
2844
- att.description = description;
2845
- enriched = true;
2846
- }
2847
- const pdfAtts = data.attachments.filter(isPdfAttachment);
2848
- for (const att of pdfAtts) {
2849
- if (att.pdfContent) {
2850
- descriptions.set(att.id, att.pdfContent);
2851
- continue;
2852
- }
2853
- const pdfContent = await describePdfAttachment(att, data.messageId, options, descriptions);
2854
- att.pdfContent = pdfContent;
2855
- enriched = true;
2856
- }
2857
- if (enriched && options.persistEvent) {
2858
- options.persistEvent(event.sessionId, event.seq, data);
2859
- }
2860
- }
2861
- if (event.type === "tool.result") {
2862
- const data = event.data;
2863
- if (!data.result.metadata || !hasImageMetadata(data.result)) continue;
2864
- const meta = data.result.metadata;
2865
- if (meta["description"]) {
2866
- descriptions.set(data.toolCallId, meta["description"]);
2867
- continue;
2868
- }
2869
- const dataUrl = meta["dataUrl"];
2870
- const path = meta["path"];
2871
- const description = await describeImageDataUrl(
2872
- dataUrl,
2873
- data.toolCallId,
2874
- data.messageId,
2875
- options,
2876
- descriptions,
2877
- path
2878
- );
2879
- meta["description"] = description;
2880
- if (options.persistEvent) {
2881
- options.persistEvent(event.sessionId, event.seq, data);
2882
- }
2883
- }
2884
- if (event.type === "turn.snapshot") {
2885
- const snapshot = event.data;
2886
- let enriched = false;
2887
- for (const message of snapshot.messages) {
2888
- if (message.role === "user" && message.attachments && message.attachments.length > 0) {
2889
- for (const att of message.attachments) {
2890
- if (isImageAttachment(att)) {
2891
- if (att.description) {
2892
- descriptions.set(att.id, att.description);
2893
- continue;
2894
- }
2895
- const description = await describeAttachment(att, message.id, options, descriptions);
2896
- att.description = description;
2897
- enriched = true;
2898
- } else if (isPdfAttachment(att)) {
2899
- if (att.pdfContent) {
2900
- descriptions.set(att.id, att.pdfContent);
2901
- continue;
2902
- }
2903
- const pdfContent = await describePdfAttachment(att, message.id, options, descriptions);
2904
- att.pdfContent = pdfContent;
2905
- enriched = true;
2906
- }
2907
- }
2908
- }
2909
- if (message.role === "assistant" && message.toolCalls) {
2910
- for (const toolCall of message.toolCalls) {
2911
- if (!toolCall.result || !toolCall.result.metadata || !hasImageMetadata(toolCall.result)) continue;
2912
- const meta = toolCall.result.metadata;
2913
- if (meta["description"]) {
2914
- descriptions.set(toolCall.id, meta["description"]);
2915
- continue;
2916
- }
2917
- const dataUrl = meta["dataUrl"];
2918
- const path = meta["path"];
2919
- const description = await describeImageDataUrl(
2920
- dataUrl,
2921
- toolCall.id,
2922
- message.id,
2923
- options,
2924
- descriptions,
2925
- path
2926
- );
2927
- meta["description"] = description;
2928
- enriched = true;
2929
- }
2930
- }
2931
- }
2932
- if (enriched && options.persistEvent) {
2933
- options.persistEvent(event.sessionId, event.seq, snapshot);
2934
- }
2935
- }
2936
- }
2937
- return { events: modifiedEvents, descriptions };
2938
- }
2939
-
2940
2013
  // src/server/chat/conversation-history.ts
2941
2014
  function buildContextMessages(events, scope) {
2942
2015
  if (scope.type === "toplevel") {
@@ -3044,13 +2117,7 @@ async function processEventsForConversation(sessionId, llmClient, onEvent) {
3044
2117
  const eventStore = getEventStore();
3045
2118
  const rawEvents = eventStore.getEvents(sessionId);
3046
2119
  const modelVision = modelSupportsVision(llmClient.getModel());
3047
- const runtimeConfig = getRuntimeConfig();
3048
- const visionModel = runtimeConfig.llm?.visionModel ? {
3049
- baseUrl: runtimeConfig.llm.baseUrl,
3050
- model: runtimeConfig.llm.visionModel,
3051
- timeout: runtimeConfig.llm.timeout,
3052
- backend: runtimeConfig.llm.backend === "ollama" ? "ollama" : "openai"
3053
- } : await loadVisionModelFromGlobalConfig();
2120
+ const visionModel = await loadResolvedVisionModel();
3054
2121
  const { events: processedEvents } = await processContextImages(rawEvents, {
3055
2122
  modelSupportsVision: modelVision,
3056
2123
  ...visionModel ? { visionModel } : {},
@@ -3088,10 +2155,10 @@ async function loadGitIgnoreRules(workdir) {
3088
2155
  if (!isAbsolute2(workdir)) return "";
3089
2156
  let currentDir = workdir;
3090
2157
  while (true) {
3091
- const gitignorePath = join2(currentDir, ".gitignore");
2158
+ const gitignorePath = join(currentDir, ".gitignore");
3092
2159
  try {
3093
2160
  await access2(gitignorePath);
3094
- const content = await readFile4(gitignorePath, "utf-8");
2161
+ const content = await readFile2(gitignorePath, "utf-8");
3095
2162
  const trimmed = content.trim();
3096
2163
  if (!trimmed) return "";
3097
2164
  const lines = trimmed.split("\n");
@@ -3374,7 +2441,7 @@ var callSubAgentTool = {
3374
2441
  };
3375
2442
  }
3376
2443
  try {
3377
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-WWIWD3O7.js");
2444
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-EJOABHVO.js");
3378
2445
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3379
2446
  const turnMetrics = new TurnMetrics();
3380
2447
  const result = await executeSubAgent({
@@ -3695,8 +2762,8 @@ var webFetchTool = createTool(
3695
2762
 
3696
2763
  // src/server/dev-server/manager.ts
3697
2764
  import "child_process";
3698
- import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
3699
- import { resolve as resolve4, join as join3 } from "path";
2765
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
2766
+ import { resolve as resolve2, join as join2 } from "path";
3700
2767
  import net from "net";
3701
2768
  var MAX_LOG_LINES = 2e3;
3702
2769
  var MAX_LOG_BYTES = 1e5;
@@ -3704,7 +2771,7 @@ var DEFAULT_PROBE_PORT = 10469;
3704
2771
  var MAX_PORT_SCAN = 200;
3705
2772
  var PROBE_TIMEOUT_MS = 300;
3706
2773
  var getDevServerConfigPath = (workdir) => {
3707
- return join3(resolve4(workdir), ".openfox", "dev.json");
2774
+ return join2(resolve2(workdir), ".openfox", "dev.json");
3708
2775
  };
3709
2776
  var ERROR_PATTERNS = [
3710
2777
  /EADDRINUSE/,
@@ -3753,7 +2820,7 @@ var DevServerManager = class {
3753
2820
  this._sessionManager = sm;
3754
2821
  }
3755
2822
  resolveWorkdir(workdir) {
3756
- return resolve4(workdir);
2823
+ return resolve2(workdir);
3757
2824
  }
3758
2825
  getInstance(workdir) {
3759
2826
  const key = this.resolveWorkdir(workdir);
@@ -3828,7 +2895,7 @@ var DevServerManager = class {
3828
2895
  const tryLoad = async (dir) => {
3829
2896
  try {
3830
2897
  const configPath = getDevServerConfigPath(dir);
3831
- const raw = await readFile5(configPath, "utf-8");
2898
+ const raw = await readFile3(configPath, "utf-8");
3832
2899
  const parsed = JSON.parse(raw);
3833
2900
  if (!parsed.command || !parsed.url) return null;
3834
2901
  return {
@@ -3854,7 +2921,7 @@ var DevServerManager = class {
3854
2921
  }
3855
2922
  async saveConfig(workdir, config) {
3856
2923
  const resolved = this.resolveWorkdir(workdir);
3857
- const dirPath = join3(resolved, ".openfox");
2924
+ const dirPath = join2(resolved, ".openfox");
3858
2925
  await mkdir2(dirPath, { recursive: true });
3859
2926
  const configPath = getDevServerConfigPath(workdir);
3860
2927
  await writeFile3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
@@ -4478,11 +3545,11 @@ var mcpConfigTool = createTool(
4478
3545
  async function notifyContextChanged(sessionId) {
4479
3546
  context.sessionManager.setDynamicContextChanged(sessionId, true);
4480
3547
  mcpNotifyChanged?.(sessionId);
4481
- const { injectContextDriftReminders } = await import("./dynamic-context-WKEXQCTK.js");
3548
+ const { injectContextDriftReminders } = await import("./dynamic-context-RDBNQPPA.js");
4482
3549
  await injectContextDriftReminders(context.sessionManager, sessionId);
4483
3550
  }
4484
3551
  async function rebuildTools() {
4485
- const { setMcpTools: setMcpTools2 } = await import("./tools-WWIWD3O7.js");
3552
+ const { setMcpTools: setMcpTools2 } = await import("./tools-EJOABHVO.js");
4486
3553
  const mcpTools = createMcpTools(mcpManagerForTools);
4487
3554
  setMcpTools2(mcpTools);
4488
3555
  }
@@ -4491,7 +3558,7 @@ var mcpConfigTool = createTool(
4491
3558
  if (servers.length === 0) {
4492
3559
  return helpers.success(serverT({ en: "No MCP servers configured.", fr: "Aucun serveur MCP configur\xE9." }));
4493
3560
  }
4494
- const { getSessionDisabledServers: getSessionDisabledServers2 } = await import("./session-overrides-Y5B4XQPU.js");
3561
+ const { getSessionDisabledServers: getSessionDisabledServers2 } = await import("./session-overrides-ANQBKV74.js");
4495
3562
  const disabledForSession = new Set(context.sessionId ? getSessionDisabledServers2(context.sessionId) : []);
4496
3563
  const visibleServers = servers.filter((server) => !disabledForSession.has(server.name));
4497
3564
  if (visibleServers.length === 0) {
@@ -5066,7 +4133,15 @@ var projectTasksTool = createTool(
5066
4133
  attachments: {
5067
4134
  type: "array",
5068
4135
  description: "Optional attachments (same shape as chat attachments)",
5069
- items: {}
4136
+ items: {
4137
+ type: "object",
4138
+ properties: {
4139
+ id: { type: "string", description: "Attachment ID" },
4140
+ filename: { type: "string", description: "File name" },
4141
+ mimeType: { type: "string", description: "MIME type" },
4142
+ size: { type: "number", description: "File size in bytes" }
4143
+ }
4144
+ }
5070
4145
  },
5071
4146
  agentId: { type: "string", description: "Selected agent id" },
5072
4147
  providerId: { type: "string", description: "Provider id used when a session is spawned" },
@@ -5335,6 +4410,7 @@ function getBuiltInTools() {
5335
4410
  if (!_builtInTools) {
5336
4411
  _builtInTools = [
5337
4412
  readFileTool,
4413
+ describeImageTool,
5338
4414
  writeFileTool,
5339
4415
  editFileTool,
5340
4416
  runCommandTool,
@@ -5615,4 +4691,4 @@ export {
5615
4691
  getToolRegistryForAgent,
5616
4692
  createToolRegistry
5617
4693
  };
5618
- //# sourceMappingURL=chunk-J5HOUEPE.js.map
4694
+ //# sourceMappingURL=chunk-EXLCFEML.js.map