openfox 2.0.135 → 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.
- package/CHANGELOG.md +27 -0
- package/dist/CHANGELOG.md +27 -0
- package/dist/agent-defaults/builder.agent.md +1 -0
- package/dist/agent-defaults/planner.agent.md +1 -0
- package/dist/{ask-UBIRH3SO.js → ask-46J5WYRL.js} +2 -2
- package/dist/{auto-config-5VSNLVNI.js → auto-config-2A7Y2WOK.js} +25 -6
- package/dist/{backend-NDBY3DAQ.js → backend-24FTDSHF.js} +4 -2
- package/dist/{chat-handler-WGDFMOOD.js → chat-handler-SYU4HJMI.js} +30 -26
- package/dist/{chunk-QZOV4OVN.js → chunk-342BKOTE.js} +5 -5
- package/dist/{chunk-7QMWYNVF.js → chunk-4LLGI372.js} +2 -2
- package/dist/chunk-4SBVW4UH.js +24 -0
- package/dist/{chunk-NFEZATSN.js → chunk-5YPHN5NC.js} +8 -8
- package/dist/{chunk-ZMK4VX7V.js → chunk-64YDOCQR.js} +53 -21
- package/dist/{chunk-UV6IH6HK.js → chunk-AKWJMMEJ.js} +15 -1
- package/dist/{chunk-ORCQ3EXQ.js → chunk-BHVR2YS5.js} +2 -2
- package/dist/{chunk-5H5NRWHA.js → chunk-CPVEONNS.js} +12 -10
- package/dist/{chunk-S67WIWUG.js → chunk-DG2YSWMC.js} +40 -6
- package/dist/{chunk-ESZQTFQP.js → chunk-EXLCFEML.js} +86 -1010
- package/dist/chunk-FSSUPWYO.js +336 -0
- package/dist/{chunk-5UQ4CKOA.js → chunk-G5GUQKLS.js} +27 -6
- package/dist/{chunk-GP3CLMPQ.js → chunk-GP5BYQMF.js} +23 -23
- package/dist/{chunk-7ET46SNE.js → chunk-HEYU7FAY.js} +3 -3
- package/dist/{chunk-IAT2LBIX.js → chunk-MILEZY2J.js} +4 -4
- package/dist/{chunk-O3RCSKUH.js → chunk-MOYWDP73.js} +2 -2
- package/dist/{chunk-J3NMNYGM.js → chunk-N5JKVNLH.js} +2 -2
- package/dist/{chunk-I5ODJKYM.js → chunk-PQKLOLGC.js} +162 -6
- package/dist/{chunk-DWA4IE65.js → chunk-Q33DL67W.js} +2 -2
- package/dist/{chunk-OZWHRBDY.js → chunk-QBWJCO5K.js} +12 -29
- package/dist/{chunk-EUOWCVU6.js → chunk-QMU3ZY2C.js} +37 -341
- package/dist/{chunk-BIZFY3QP.js → chunk-QQQBNL7A.js} +212 -148
- package/dist/{chunk-CTWZPN7G.js → chunk-QUAEH3MP.js} +29 -1
- package/dist/{chunk-5UMXFLET.js → chunk-SAGDTQXD.js} +3 -3
- package/dist/chunk-U6FHHT2B.js +1155 -0
- package/dist/{chunk-4R5GV7RW.js → chunk-UL7776XB.js} +21 -21
- package/dist/{chunk-JURM3RPZ.js → chunk-V5AC2TV7.js} +8 -13
- package/dist/{chunk-UKIUQAQZ.js → chunk-VA2TYWQR.js} +4 -4
- package/dist/{chunk-JE7LW7Y6.js → chunk-VIJVLLPT.js} +12 -3
- package/dist/{chunk-2AI7VYPN.js → chunk-VKMPR25F.js} +9 -3
- package/dist/chunk-VWBPQZLO.js +7 -0
- package/dist/{chunk-F2MOHHXB.js → chunk-X4PVR6AW.js} +2 -2
- package/dist/chunk-XG3AUMPI.js +72 -0
- package/dist/{chunk-MEEUXRFL.js → chunk-XGG4XCKP.js} +2 -2
- package/dist/{chunk-VHLD6PCZ.js → chunk-YDPC4FLR.js} +17 -11
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/client-MVI3J7SN.js +18 -0
- package/dist/{client-pure-FLKP6PTI.js → client-pure-YZ4IGCU4.js} +4 -2
- package/dist/{compactor-454UEIEX.js → compactor-LBK53WAK.js} +14 -12
- package/dist/describe-image-4XDLSKAB.js +30 -0
- package/dist/{dynamic-context-LKC6JYLQ.js → dynamic-context-RDBNQPPA.js} +12 -10
- package/dist/{events-335XAKFK.js → events-YTYYT3PR.js} +6 -6
- package/dist/{export-import-3WMRLO4A.js → export-import-GHRCOQHF.js} +7 -7
- package/dist/{folding-H7JTX6KI.js → folding-3MMOPS4W.js} +4 -4
- package/dist/{http-client-A445WYZU.js → http-client-C6UYWVWZ.js} +3 -3
- package/dist/launch-YZ46OEFP.js +58 -0
- package/dist/{manager-ZACEUL5P.js → manager-FSPRJCYT.js} +4 -4
- package/dist/{model-catalog-2PCKTYGW.js → model-catalog-Y5MA5OZP.js} +2 -2
- package/dist/{orchestrator-JGJWTRO5.js → orchestrator-6QGQ55NT.js} +29 -25
- package/dist/package.json +1 -1
- package/dist/{path-security-4LROI7F4.js → path-security-EC4Z2ZZG.js} +8 -8
- package/dist/{platform-XPMZKAUB.js → platform-KPKYBU54.js} +3 -3
- package/dist/{processor-MSQP63ML.js → processor-B2RV7MVC.js} +29 -25
- package/dist/{provider-JWUNG2TD.js → provider-SOC6Z25R.js} +10 -8
- package/dist/{provider-manager-2KKZ37XJ.js → provider-manager-Y53SVVQ3.js} +10 -8
- package/dist/{registry-BEIR7Y5G.js → registry-FMDMGIUB.js} +3 -3
- package/dist/{serve-CW5C3CBZ.js → serve-EWA5HXC7.js} +41 -32
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +34 -30
- package/dist/server-ZAPAB3FS.js +61 -0
- package/dist/{service-XIUNS5XV.js → service-NKLQH647.js} +12 -9
- package/dist/{session-overrides-LYW5PJ3R.js → session-overrides-ANQBKV74.js} +11 -7
- package/dist/{sessions-AOK3AGHY.js → sessions-GLYACE5R.js} +4 -4
- package/dist/{settings-ZJPM6PQV.js → settings-WH6BVQCP.js} +4 -2
- package/dist/{tasks-3JJWJJ3X.js → tasks-GPM3JAE6.js} +14 -10
- package/dist/tool-adapter-ZY4FC4C4.js +9 -0
- package/dist/{tools-E63MOJBO.js → tools-EJOABHVO.js} +27 -23
- package/dist/{update-XVXNSSDK.js → update-IPHJMRQZ.js} +2 -2
- package/dist/web/assets/{index-wTFCxQAw.js → index-CfFwerN2.js} +84 -84
- package/dist/web/assets/{index-Bg_Z8i3F.css → index-ChJjYovg.css} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WY3PWHWY.js +0 -7
- package/dist/client-VVYOWXRR.js +0 -16
- package/dist/launch-GJ7EHQRI.js +0 -54
- package/dist/server-ZHEB7XS2.js +0 -57
- package/dist/tool-adapter-FJAFLDZA.js +0 -8
|
@@ -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
|
-
|
|
11
|
-
} from "./chunk-OZWHRBDY.js";
|
|
16
|
+
isTaskGateError
|
|
17
|
+
} from "./chunk-QBWJCO5K.js";
|
|
12
18
|
import {
|
|
13
19
|
getSessionDisabledServers
|
|
14
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
96
|
+
} from "./chunk-PQKLOLGC.js";
|
|
81
97
|
import {
|
|
82
98
|
getCurrentContextWindowId,
|
|
83
99
|
getCurrentWindowMessageOptions,
|
|
84
100
|
getEventStore
|
|
85
|
-
} from "./chunk-
|
|
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-
|
|
111
|
+
} from "./chunk-XGG4XCKP.js";
|
|
96
112
|
import {
|
|
97
113
|
findAgentById,
|
|
98
114
|
getRuntimeConfig,
|
|
99
115
|
getSubAgents,
|
|
100
116
|
loadAllAgentsDefault
|
|
101
|
-
} from "./chunk-
|
|
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-
|
|
137
|
+
} from "./chunk-FSSUPWYO.js";
|
|
127
138
|
import {
|
|
128
139
|
SETTINGS_KEYS,
|
|
129
140
|
getSetting,
|
|
130
141
|
setSetting
|
|
131
|
-
} from "./chunk-
|
|
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
|
|
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,
|
|
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
|
|
872
|
-
import { relative as
|
|
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
|
|
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,
|
|
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
|
|
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((
|
|
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)
|
|
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 :
|
|
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((
|
|
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)
|
|
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((
|
|
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
|
-
|
|
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
|
|
1682
|
-
import { join
|
|
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-
|
|
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-
|
|
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
|
|
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 =
|
|
2158
|
+
const gitignorePath = join(currentDir, ".gitignore");
|
|
3092
2159
|
try {
|
|
3093
2160
|
await access2(gitignorePath);
|
|
3094
|
-
const content = await
|
|
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-
|
|
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
|
|
3699
|
-
import { resolve as
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
4694
|
+
//# sourceMappingURL=chunk-EXLCFEML.js.map
|