openfox 2.0.129 → 2.0.131
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 +44 -0
- package/README.md +2 -0
- package/dist/CHANGELOG.md +44 -0
- package/dist/auth-D3WLIBTG.js +19 -0
- package/dist/{auto-config-454YE6KC.js → auto-config-5VSNLVNI.js} +6 -5
- package/dist/{backend-4YPEC3J2.js → backend-NDBY3DAQ.js} +4 -2
- package/dist/{chat-handler-TH6JBO6G.js → chat-handler-RAIMQU7F.js} +35 -35
- package/dist/{chunk-ISE55FOR.js → chunk-2AI7VYPN.js} +11 -1
- package/dist/{chunk-HO7A7TMF.js → chunk-55DGZIZ6.js} +15 -5
- package/dist/{chunk-L24A6GZ7.js → chunk-5AI3LHRQ.js} +85 -57
- package/dist/{chunk-ZFNBATE3.js → chunk-6EZDW4JS.js} +3 -2
- package/dist/{chunk-ZXWJ6JTL.js → chunk-6MUSEPV7.js} +1197 -225
- package/dist/{chunk-OHTBWA7L.js → chunk-6SUBBT2M.js} +13 -13
- package/dist/{chunk-6LX43YRA.js → chunk-7ET46SNE.js} +4 -4
- package/dist/{chunk-GIJUWPRQ.js → chunk-7JVBDBXE.js} +2 -2
- package/dist/{chunk-MYNNMV75.js → chunk-7LGMQ5KS.js} +54 -36
- package/dist/{chunk-ZIL6VKLU.js → chunk-BKMWZTNO.js} +3 -3
- package/dist/chunk-BMBOSVRM.js +7 -0
- package/dist/{auth-GTGIGX5X.js → chunk-CQGJ5RWB.js} +4 -4
- package/dist/{chunk-3B232XG4.js → chunk-DPFET73G.js} +2 -2
- package/dist/chunk-DQTN4B33.js +235 -0
- package/dist/{chunk-KBESUA3W.js → chunk-ELYHHEYL.js} +3 -3
- package/dist/{chunk-DABWCAXS.js → chunk-EUOWCVU6.js} +19 -6
- package/dist/{chunk-YJD5A375.js → chunk-FGNJMKDV.js} +5 -5
- package/dist/{chunk-ATWOXI52.js → chunk-HCYSGLY2.js} +1 -1
- package/dist/{chunk-OYBUJIC3.js → chunk-JU757HVI.js} +5 -1
- package/dist/{chunk-AQBRI7TM.js → chunk-KWAHD2JA.js} +13 -8
- package/dist/{chunk-N2Q3KYWM.js → chunk-LQKXJPKP.js} +8 -3
- package/dist/{chunk-EHFIUXWZ.js → chunk-LW6MMSTD.js} +2 -2
- package/dist/{chunk-ZIYETXOW.js → chunk-M5T4RDPJ.js} +4 -4
- package/dist/{chunk-66MG44TF.js → chunk-MGEYAYGU.js} +2 -2
- package/dist/{chunk-RV5G7QQU.js → chunk-MQMPIXON.js} +3 -3
- package/dist/{chunk-K2QKX4ZJ.js → chunk-OA2FURC3.js} +8 -2
- package/dist/{chunk-567L5OJ6.js → chunk-P57IBMK4.js} +2 -2
- package/dist/{chunk-P6GTPKE5.js → chunk-QJG5K3X2.js} +278 -16
- package/dist/{chunk-2COZT3YI.js → chunk-RQPHGBWI.js} +24 -24
- package/dist/chunk-SVALN5KE.js +394 -0
- package/dist/{session-status-6Q5B5PEN.js → chunk-T3S5MTMD.js} +2 -3
- package/dist/{chunk-7YP4O4XE.js → chunk-TACWRDDC.js} +3 -3
- package/dist/{chunk-7ZMOP5LS.js → chunk-TS5WKTFB.js} +18 -1
- package/dist/{chunk-PBFGJFGC.js → chunk-UV6IH6HK.js} +32 -10
- package/dist/{chunk-7RIEI2TE.js → chunk-WCM5ZW5N.js} +52 -17
- package/dist/{chunk-Y3BSBKHY.js → chunk-XE6UC5Z3.js} +2 -2
- package/dist/{chunk-26WS7AH6.js → chunk-Y6ZGHOKM.js} +39 -7
- package/dist/chunk-YNMP647F.js +24 -0
- package/dist/{chunk-H6YIVK67.js → chunk-ZCFQOC3C.js} +26 -6
- package/dist/{chunk-IJFKJ5D6.js → chunk-ZL4IAFF3.js} +4 -4
- package/dist/{chunk-JQMQ4DGC.js → chunk-ZVHUISW7.js} +76 -32
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/client-E6QLHPHD.js +16 -0
- package/dist/{client-pure-GU2SV3DD.js → client-pure-FLKP6PTI.js} +2 -2
- package/dist/compactor-OEJAA2YP.js +32 -0
- package/dist/dynamic-context-Y555VDLA.js +52 -0
- package/dist/{events-GMHRX4LF.js → events-ZJPRGVL2.js} +9 -9
- package/dist/{folding-7K2GRPF4.js → folding-QRZOHY2I.js} +7 -7
- package/dist/http-client-P4XTKAZA.js +11 -0
- package/dist/{inspect-proxy-UKPU3Z64.js → inspect-proxy-A3OROWPG.js} +4 -4
- package/dist/launch-Y4H7FDU2.js +53 -0
- package/dist/{manager-NOKJQTTF.js → manager-TMBFBCQL.js} +5 -5
- package/dist/mcp-YU7AM3UV.js +120 -0
- package/dist/{model-catalog-ZLOWTQA7.js → model-catalog-2PCKTYGW.js} +2 -2
- package/dist/{oauth-provider-DGBC2YM6.js → oauth-provider-VHUS2KEG.js} +8 -2
- package/dist/orchestrator-SQN2SJ2A.js +64 -0
- package/dist/package.json +1 -1
- package/dist/{path-security-UDZ7UVWK.js → path-security-CBACXAMS.js} +13 -13
- package/dist/{platform-EMKWQTZX.js → platform-5LQXOZO3.js} +4 -4
- package/dist/plugins-registry.json +12 -0
- package/dist/{processor-7RDR675X.js → processor-MEHODYNE.js} +34 -34
- package/dist/{profiles-HXGJIPF2.js → profiles-EBCVFYG7.js} +2 -2
- package/dist/{project-creator-JF3YVQAH.js → project-creator-6AGQTNXB.js} +3 -3
- package/dist/{projects-TPSLCJP2.js → projects-5U37RUTL.js} +3 -3
- package/dist/{protocol-DMneA8mx.d.ts → protocol-BMUoqwYH.d.ts} +2 -1
- package/dist/{protocol-TS3GVTT3.js → protocol-Y7MRKRUO.js} +3 -3
- package/dist/provider/index.d.ts +4 -4
- package/dist/{provider-DXEICEIE.js → provider-6YRII4IV.js} +11 -11
- package/dist/{provider-manager-PU7RS2TK.js → provider-manager-URNJBK5Q.js} +11 -11
- package/dist/{registry-Z4GVYFTV.js → registry-CMSSZHDC.js} +5 -5
- package/dist/{serve-Z5ITDPCQ.js → serve-NPADSJG7.js} +45 -43
- package/dist/server/index.d.ts +28 -4
- package/dist/server/index.js +42 -40
- package/dist/server-5TY22HDN.js +56 -0
- package/dist/service-VPXGREXF.js +27 -0
- package/dist/{session-overrides-4WAD3X5Q.js → session-overrides-OECQYRDK.js} +7 -7
- package/dist/session-status-UFO5IFI7.js +10 -0
- package/dist/{sessions-D7VRCHQY.js → sessions-NMPX5ICI.js} +6 -6
- package/dist/{settings-VOWWO4VG.js → settings-MDCRNQBY.js} +3 -3
- package/dist/shared/index.d.ts +3 -3
- package/dist/shared/index.js +1 -1
- package/dist/skill-defaults/browser/SKILL.md +20 -1
- package/dist/{tasks-SE3DOPVP.js → tasks-MNPXK2S5.js} +14 -14
- package/dist/{tools-6XUSIJN2.js → tools-6BDT5YMT.js} +37 -37
- package/dist/{types-PAKlAzp9.d.ts → types-BIkpxjaj.d.ts} +11 -2
- package/dist/{types-BWuVnaBw.d.ts → types-CoY8Ka1C.d.ts} +1 -1
- package/dist/{update-EJUI6PSV.js → update-XKCOBE47.js} +2 -2
- package/dist/web/assets/{index-CGKd7WLQ.js → index-Bx5r3aJx.js} +88 -88
- package/dist/web/assets/{index-CbX8veVW.css → index-Y300llHY.css} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/dist/{workspace-6CUE26YA.js → workspace-LM646OHC.js} +4 -4
- package/package.json +1 -1
- package/plugins-registry.json +12 -0
- package/dist/chunk-HIRDDCPD.js +0 -7
- package/dist/chunk-PHP7PLFX.js +0 -151
- package/dist/chunk-RSB4AR2A.js +0 -108
- package/dist/client-RZGGDGUA.js +0 -16
- package/dist/compactor-WKFNTUXL.js +0 -32
- package/dist/dynamic-context-ZVE733UI.js +0 -28
- package/dist/http-client-ROVNSHF6.js +0 -11
- package/dist/launch-5ARSU7EM.js +0 -53
- package/dist/orchestrator-E6GIZGEN.js +0 -64
- package/dist/server-MUJ7EHEI.js +0 -56
- package/dist/service-INQWTYY2.js +0 -27
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
projectSessionStatus
|
|
3
|
+
} from "./chunk-T3S5MTMD.js";
|
|
4
|
+
import {
|
|
5
|
+
openFolder
|
|
6
|
+
} from "./chunk-WLJJRX22.js";
|
|
1
7
|
import {
|
|
2
8
|
McpOAuthProvider,
|
|
3
9
|
setMcpOAuthServerPort
|
|
4
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DQTN4B33.js";
|
|
5
11
|
import {
|
|
12
|
+
readMcpOAuthEntry,
|
|
6
13
|
setMcpOAuthStoreMode,
|
|
7
14
|
setMcpOAuthStorePath
|
|
8
15
|
} from "./chunk-IJ53Y77N.js";
|
|
9
16
|
import {
|
|
10
17
|
createWebSocketServer,
|
|
18
|
+
currentSessionToken,
|
|
11
19
|
getAuthConfig,
|
|
12
20
|
hasPassword,
|
|
13
21
|
isValidToken,
|
|
@@ -16,10 +24,10 @@ import {
|
|
|
16
24
|
terminalManager,
|
|
17
25
|
tokenFromPassword,
|
|
18
26
|
verifyPassword
|
|
19
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-7LGMQ5KS.js";
|
|
20
28
|
import {
|
|
21
29
|
TEMPLATE_VARIABLES
|
|
22
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-RQPHGBWI.js";
|
|
23
31
|
import {
|
|
24
32
|
createToolRegistry,
|
|
25
33
|
devServerManager,
|
|
@@ -28,42 +36,13 @@ import {
|
|
|
28
36
|
getBuiltInToolNames,
|
|
29
37
|
resolveLLMClientForAgent,
|
|
30
38
|
setAgentModelOverride,
|
|
39
|
+
setMcpBootstrapForTools,
|
|
31
40
|
setMcpConfigMode,
|
|
32
41
|
setMcpConfigPath,
|
|
33
42
|
setMcpManagerForTools,
|
|
34
43
|
setMcpTools,
|
|
35
44
|
setNotifyMcpServersChanged
|
|
36
|
-
} from "./chunk-
|
|
37
|
-
import {
|
|
38
|
-
deleteOwnedSkill,
|
|
39
|
-
deleteProjectSkill,
|
|
40
|
-
deleteSkill,
|
|
41
|
-
findSkillById,
|
|
42
|
-
getDefaultSkillIds,
|
|
43
|
-
isDefaultSkill,
|
|
44
|
-
isSkillEnabled,
|
|
45
|
-
loadAllSkills,
|
|
46
|
-
loadAllSkillsWithDiagnostics,
|
|
47
|
-
loadDefaultSkills,
|
|
48
|
-
loadProjectSkills,
|
|
49
|
-
loadUserSkills,
|
|
50
|
-
saveSkill,
|
|
51
|
-
saveSkillToProject,
|
|
52
|
-
setSkillEnabled,
|
|
53
|
-
skillExists,
|
|
54
|
-
updateOwnedSkill
|
|
55
|
-
} from "./chunk-ZIL6VKLU.js";
|
|
56
|
-
import {
|
|
57
|
-
openFolder
|
|
58
|
-
} from "./chunk-WLJJRX22.js";
|
|
59
|
-
import {
|
|
60
|
-
ALWAYS_ALLOWED,
|
|
61
|
-
ALWAYS_ALLOWED_FOR_SUBAGENTS,
|
|
62
|
-
TOP_LEVEL_ONLY_TOOLS
|
|
63
|
-
} from "./chunk-GIJUWPRQ.js";
|
|
64
|
-
import {
|
|
65
|
-
createMcpTools
|
|
66
|
-
} from "./chunk-JE7LW7Y6.js";
|
|
45
|
+
} from "./chunk-5AI3LHRQ.js";
|
|
67
46
|
import {
|
|
68
47
|
checkoutBranchFromSharedSource,
|
|
69
48
|
deleteWorkspace,
|
|
@@ -80,7 +59,7 @@ import {
|
|
|
80
59
|
validateRef,
|
|
81
60
|
validateWorkspaceName,
|
|
82
61
|
workspaceExists
|
|
83
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-XE6UC5Z3.js";
|
|
84
63
|
import {
|
|
85
64
|
commandExists,
|
|
86
65
|
deleteCommand,
|
|
@@ -93,6 +72,7 @@ import {
|
|
|
93
72
|
getDefaultWorkflowIds,
|
|
94
73
|
isDefaultCommand,
|
|
95
74
|
isDefaultWorkflow,
|
|
75
|
+
listAvailableWorkflows,
|
|
96
76
|
loadAllCommands,
|
|
97
77
|
loadAllWorkflows,
|
|
98
78
|
loadDefaultCommands,
|
|
@@ -107,12 +87,49 @@ import {
|
|
|
107
87
|
saveWorkflow,
|
|
108
88
|
saveWorkflowToProject,
|
|
109
89
|
workflowExists
|
|
110
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-ZCFQOC3C.js";
|
|
111
91
|
import {
|
|
112
92
|
getSessionDisabledServers,
|
|
113
93
|
setSessionDisabledServers
|
|
114
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-TACWRDDC.js";
|
|
95
|
+
import {
|
|
96
|
+
createMcpTools
|
|
97
|
+
} from "./chunk-JE7LW7Y6.js";
|
|
98
|
+
import {
|
|
99
|
+
deleteOwnedSkill,
|
|
100
|
+
deleteProjectSkill,
|
|
101
|
+
deleteSkill,
|
|
102
|
+
findSkillById,
|
|
103
|
+
getDefaultSkillIds,
|
|
104
|
+
isDefaultSkill,
|
|
105
|
+
isSkillEnabled,
|
|
106
|
+
loadAllSkills,
|
|
107
|
+
loadAllSkillsWithDiagnostics,
|
|
108
|
+
loadDefaultSkills,
|
|
109
|
+
loadProjectSkills,
|
|
110
|
+
loadUserSkills,
|
|
111
|
+
saveSkill,
|
|
112
|
+
saveSkillToProject,
|
|
113
|
+
setSkillEnabled,
|
|
114
|
+
skillExists,
|
|
115
|
+
updateOwnedSkill
|
|
116
|
+
} from "./chunk-BKMWZTNO.js";
|
|
117
|
+
import {
|
|
118
|
+
ALWAYS_ALLOWED,
|
|
119
|
+
ALWAYS_ALLOWED_FOR_SUBAGENTS,
|
|
120
|
+
TOP_LEVEL_ONLY_TOOLS
|
|
121
|
+
} from "./chunk-7JVBDBXE.js";
|
|
122
|
+
import {
|
|
123
|
+
EventEmitter,
|
|
124
|
+
getPendingQuestionsForSession,
|
|
125
|
+
provideAnswer
|
|
126
|
+
} from "./chunk-JURM3RPZ.js";
|
|
127
|
+
import {
|
|
128
|
+
getPendingConfirmationsBySession,
|
|
129
|
+
providePathConfirmation
|
|
130
|
+
} from "./chunk-Y6ZGHOKM.js";
|
|
115
131
|
import {
|
|
132
|
+
combineEventsWithSnapshot,
|
|
116
133
|
emitAssistantMessageStart,
|
|
117
134
|
emitContextState,
|
|
118
135
|
emitCriteriaSet,
|
|
@@ -128,7 +145,10 @@ import {
|
|
|
128
145
|
getEventStore,
|
|
129
146
|
getSessionState,
|
|
130
147
|
initEventStore
|
|
131
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-FGNJMKDV.js";
|
|
149
|
+
import {
|
|
150
|
+
buildMessagesFromStoredEvents
|
|
151
|
+
} from "./chunk-M5T4RDPJ.js";
|
|
132
152
|
import {
|
|
133
153
|
clearWorkflowExecution,
|
|
134
154
|
createSession,
|
|
@@ -154,27 +174,7 @@ import {
|
|
|
154
174
|
updateSessionRunning,
|
|
155
175
|
updateSessionWorkdir,
|
|
156
176
|
updateWorkflowExecutionStatus
|
|
157
|
-
} from "./chunk-
|
|
158
|
-
import {
|
|
159
|
-
createContextStateMessage,
|
|
160
|
-
toClientSession
|
|
161
|
-
} from "./chunk-567L5OJ6.js";
|
|
162
|
-
import {
|
|
163
|
-
getPathSeparator,
|
|
164
|
-
isAbsolutePath
|
|
165
|
-
} from "./chunk-66MG44TF.js";
|
|
166
|
-
import {
|
|
167
|
-
EventEmitter
|
|
168
|
-
} from "./chunk-JURM3RPZ.js";
|
|
169
|
-
import {
|
|
170
|
-
createProviderManager,
|
|
171
|
-
detectModel,
|
|
172
|
-
getLlmStatus,
|
|
173
|
-
parseDefaultModelSelection
|
|
174
|
-
} from "./chunk-JQMQ4DGC.js";
|
|
175
|
-
import {
|
|
176
|
-
VERSION
|
|
177
|
-
} from "./chunk-HIRDDCPD.js";
|
|
177
|
+
} from "./chunk-KWAHD2JA.js";
|
|
178
178
|
import {
|
|
179
179
|
agentExists,
|
|
180
180
|
deleteAgent,
|
|
@@ -191,47 +191,69 @@ import {
|
|
|
191
191
|
saveAgent,
|
|
192
192
|
saveAgentToProject,
|
|
193
193
|
setRuntimeConfig
|
|
194
|
-
} from "./chunk-
|
|
194
|
+
} from "./chunk-ELYHHEYL.js";
|
|
195
195
|
import {
|
|
196
196
|
findFileByInternalId,
|
|
197
197
|
pathExists
|
|
198
198
|
} from "./chunk-OSXPQSLW.js";
|
|
199
199
|
import {
|
|
200
|
+
createContextStateMessage,
|
|
201
|
+
toClientSession
|
|
202
|
+
} from "./chunk-P57IBMK4.js";
|
|
203
|
+
import {
|
|
204
|
+
getPathSeparator,
|
|
205
|
+
isAbsolutePath
|
|
206
|
+
} from "./chunk-MGEYAYGU.js";
|
|
207
|
+
import {
|
|
208
|
+
createProviderManager,
|
|
209
|
+
detectModel,
|
|
210
|
+
getLlmStatus,
|
|
211
|
+
parseDefaultModelSelection
|
|
212
|
+
} from "./chunk-ZVHUISW7.js";
|
|
213
|
+
import {
|
|
214
|
+
buildOpenFoxMcpBootstrap
|
|
215
|
+
} from "./chunk-YNMP647F.js";
|
|
216
|
+
import {
|
|
217
|
+
VERSION
|
|
218
|
+
} from "./chunk-BMBOSVRM.js";
|
|
219
|
+
import {
|
|
220
|
+
deleteProject,
|
|
200
221
|
getProject,
|
|
201
222
|
getProjectByWorkdir,
|
|
202
223
|
updateProject
|
|
203
|
-
} from "./chunk-
|
|
224
|
+
} from "./chunk-DPFET73G.js";
|
|
204
225
|
import {
|
|
205
226
|
getModelProfile
|
|
206
|
-
} from "./chunk-
|
|
227
|
+
} from "./chunk-TS5WKTFB.js";
|
|
207
228
|
import {
|
|
229
|
+
detectBackendFromUrl,
|
|
208
230
|
getBackendDisplayName
|
|
209
|
-
} from "./chunk-
|
|
231
|
+
} from "./chunk-UV6IH6HK.js";
|
|
210
232
|
import {
|
|
211
233
|
buildModelsUrl
|
|
212
234
|
} from "./chunk-HNCM3D7Y.js";
|
|
213
235
|
import {
|
|
214
236
|
SessionNotFoundError,
|
|
215
237
|
WorkspaceInUseError
|
|
216
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-6EZDW4JS.js";
|
|
217
239
|
import {
|
|
218
240
|
deleteSetting,
|
|
219
241
|
getSetting,
|
|
220
242
|
setSetting
|
|
221
|
-
} from "./chunk-
|
|
243
|
+
} from "./chunk-LQKXJPKP.js";
|
|
222
244
|
import {
|
|
223
245
|
initDatabase
|
|
224
|
-
} from "./chunk-
|
|
246
|
+
} from "./chunk-JU757HVI.js";
|
|
225
247
|
import {
|
|
226
248
|
isReasoningEffortValue
|
|
227
|
-
} from "./chunk-
|
|
249
|
+
} from "./chunk-2AI7VYPN.js";
|
|
228
250
|
import {
|
|
229
251
|
logger,
|
|
230
252
|
setLogLevel
|
|
231
253
|
} from "./chunk-K44MW7JJ.js";
|
|
232
254
|
import {
|
|
233
255
|
createServerMessage
|
|
234
|
-
} from "./chunk-
|
|
256
|
+
} from "./chunk-HCYSGLY2.js";
|
|
235
257
|
import {
|
|
236
258
|
getGlobalConfigDir
|
|
237
259
|
} from "./chunk-CQGTEGKL.js";
|
|
@@ -776,6 +798,30 @@ var RULES = [
|
|
|
776
798
|
tools: [],
|
|
777
799
|
response: "TypeScript is a strongly typed programming language that builds on JavaScript."
|
|
778
800
|
},
|
|
801
|
+
// Default-workflow completion: the mock must be able to drive the built-in
|
|
802
|
+
// workflow to $done (finalize/summarize agent steps + sub-agent return_value).
|
|
803
|
+
{
|
|
804
|
+
match: /You must call return_value with a summary of your findings/i,
|
|
805
|
+
tools: [
|
|
806
|
+
{ name: "return_value", arguments: { content: "No issues found in the reviewed changes.", result: "success" } }
|
|
807
|
+
],
|
|
808
|
+
response: "Returning findings summary."
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
match: /## Code Review Findings/i,
|
|
812
|
+
tools: [{ name: "step_done", arguments: {} }],
|
|
813
|
+
response: "All review findings resolved."
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
match: /The step cannot complete because not all review findings/i,
|
|
817
|
+
tools: [{ name: "step_done", arguments: {} }],
|
|
818
|
+
response: "Review findings handled."
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
match: /Produce a concise summary of what was accomplished/i,
|
|
822
|
+
tools: [{ name: "step_done", arguments: {} }],
|
|
823
|
+
response: "Summary produced."
|
|
824
|
+
},
|
|
779
825
|
{
|
|
780
826
|
match: /.*/,
|
|
781
827
|
tools: [],
|
|
@@ -1561,7 +1607,8 @@ var McpManager = class {
|
|
|
1561
1607
|
httpOpts.requestInit = { headers };
|
|
1562
1608
|
}
|
|
1563
1609
|
if (entry.config.oauth) {
|
|
1564
|
-
|
|
1610
|
+
const stored = await readMcpOAuthEntry(name, entry.config.url);
|
|
1611
|
+
httpOpts.authProvider = stored?.tokens ? new McpOAuthProvider(name, entry.config.url) : McpOAuthProvider.forBackgroundProbe(name, entry.config.url);
|
|
1565
1612
|
}
|
|
1566
1613
|
transport = new StreamableHTTPClientTransport(new URL(entry.config.url), httpOpts);
|
|
1567
1614
|
} else {
|
|
@@ -1715,7 +1762,11 @@ var McpManager = class {
|
|
|
1715
1762
|
}
|
|
1716
1763
|
const content = result.content;
|
|
1717
1764
|
const textParts = content.filter((c) => c.type === "text").map((c) => c.text);
|
|
1718
|
-
|
|
1765
|
+
const text = textParts.join("\n");
|
|
1766
|
+
if (result.isError) {
|
|
1767
|
+
return { success: false, error: text || "MCP tool call failed" };
|
|
1768
|
+
}
|
|
1769
|
+
return { success: true, output: text };
|
|
1719
1770
|
} catch (error) {
|
|
1720
1771
|
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1721
1772
|
}
|
|
@@ -2761,6 +2812,8 @@ var SessionManager = class {
|
|
|
2761
2812
|
providerManager;
|
|
2762
2813
|
dynamicContextChangedStore = /* @__PURE__ */ new Map();
|
|
2763
2814
|
debugDumpStore = /* @__PURE__ */ new Map();
|
|
2815
|
+
announcedPromptHashStore = /* @__PURE__ */ new Map();
|
|
2816
|
+
announcedToolFingerprintStore = /* @__PURE__ */ new Map();
|
|
2764
2817
|
warmedUpSessions = /* @__PURE__ */ new Set();
|
|
2765
2818
|
switchLocks = /* @__PURE__ */ new Map();
|
|
2766
2819
|
workspaceCreationLocks = /* @__PURE__ */ new Map();
|
|
@@ -3027,7 +3080,7 @@ var SessionManager = class {
|
|
|
3027
3080
|
updateSessionMessageCount(newSession.id, messages.length);
|
|
3028
3081
|
const cached2 = getSessionCachedPrompt(originalSessionId);
|
|
3029
3082
|
if (cached2) {
|
|
3030
|
-
updateSessionCachedPrompt(newSession.id, cached2.systemPrompt, cached2.tools, cached2.hash);
|
|
3083
|
+
updateSessionCachedPrompt(newSession.id, cached2.systemPrompt, cached2.tools, cached2.hash, cached2.promptHash);
|
|
3031
3084
|
this.markWarmedUp(newSession.id);
|
|
3032
3085
|
}
|
|
3033
3086
|
this.emit({ type: "session_updated", session: this.requireSession(newSession.id) });
|
|
@@ -3097,6 +3150,8 @@ var SessionManager = class {
|
|
|
3097
3150
|
eventStore.deleteSession(id);
|
|
3098
3151
|
this.messageQueues.delete(id);
|
|
3099
3152
|
this.warmedUpSessions.delete(id);
|
|
3153
|
+
this.announcedPromptHashStore.delete(id);
|
|
3154
|
+
this.announcedToolFingerprintStore.delete(id);
|
|
3100
3155
|
deleteSession(id);
|
|
3101
3156
|
if (this.activeSessionId === id) {
|
|
3102
3157
|
this.activeSessionId = null;
|
|
@@ -3580,7 +3635,7 @@ var SessionManager = class {
|
|
|
3580
3635
|
const state = getSessionState(sessionId, this.getCurrentModelContext(sessionId));
|
|
3581
3636
|
const maxTokens = this.getCurrentModelContext(sessionId);
|
|
3582
3637
|
const currentTokens = promptTokens + completionTokens;
|
|
3583
|
-
const compactionCount = state?.contextState.compactionCount ?? 0;
|
|
3638
|
+
const compactionCount = subAgentId ? 0 : state?.contextState.compactionCount ?? 0;
|
|
3584
3639
|
const dynamicContextChanged = this.getDynamicContextChanged(sessionId);
|
|
3585
3640
|
emitContextState(
|
|
3586
3641
|
sessionId,
|
|
@@ -3819,8 +3874,8 @@ var SessionManager = class {
|
|
|
3819
3874
|
resetWarmup(sessionId) {
|
|
3820
3875
|
this.warmedUpSessions.delete(sessionId);
|
|
3821
3876
|
}
|
|
3822
|
-
setCachedPrompt(sessionId, systemPrompt, tools, hash) {
|
|
3823
|
-
updateSessionCachedPrompt(sessionId, systemPrompt, tools, hash);
|
|
3877
|
+
setCachedPrompt(sessionId, systemPrompt, tools, hash, promptHash) {
|
|
3878
|
+
updateSessionCachedPrompt(sessionId, systemPrompt, tools, hash, promptHash);
|
|
3824
3879
|
this.resetWarmup(sessionId);
|
|
3825
3880
|
}
|
|
3826
3881
|
getCachedPrompt(sessionId) {
|
|
@@ -3839,6 +3894,27 @@ var SessionManager = class {
|
|
|
3839
3894
|
getDynamicContextChanged(sessionId) {
|
|
3840
3895
|
return this.dynamicContextChangedStore.get(sessionId) ?? false;
|
|
3841
3896
|
}
|
|
3897
|
+
/**
|
|
3898
|
+
* The instructions/skills/model hash last announced to the model via a
|
|
3899
|
+
* system-prompt-change reminder. Drives exactly-once prompt-drift reminders.
|
|
3900
|
+
*/
|
|
3901
|
+
getAnnouncedPromptHash(sessionId) {
|
|
3902
|
+
return this.announcedPromptHashStore.get(sessionId);
|
|
3903
|
+
}
|
|
3904
|
+
setAnnouncedPromptHash(sessionId, hash) {
|
|
3905
|
+
this.announcedPromptHashStore.set(sessionId, hash);
|
|
3906
|
+
}
|
|
3907
|
+
/**
|
|
3908
|
+
* The tool fingerprint last announced to the model via a tool-change
|
|
3909
|
+
* reminder. Drives exactly-once tool-drift reminders WITHOUT mutating the
|
|
3910
|
+
* cached prefix (the cached tools stay frozen until a manual rebase).
|
|
3911
|
+
*/
|
|
3912
|
+
getAnnouncedToolFingerprint(sessionId) {
|
|
3913
|
+
return this.announcedToolFingerprintStore.get(sessionId);
|
|
3914
|
+
}
|
|
3915
|
+
setAnnouncedToolFingerprint(sessionId, fingerprint) {
|
|
3916
|
+
this.announcedToolFingerprintStore.set(sessionId, fingerprint);
|
|
3917
|
+
}
|
|
3842
3918
|
/**
|
|
3843
3919
|
* @deprecated Use addTokensUsed instead
|
|
3844
3920
|
*/
|
|
@@ -4748,10 +4824,766 @@ function createWorkflowRoutes(configDir, config3, projectDir) {
|
|
|
4748
4824
|
return createCrudRoutes(crudConfig, configDir, projectDir);
|
|
4749
4825
|
}
|
|
4750
4826
|
|
|
4751
|
-
// src/server/
|
|
4827
|
+
// src/server/mcp/server/endpoint.ts
|
|
4752
4828
|
import { Router as Router4 } from "express";
|
|
4753
|
-
|
|
4829
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4830
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4831
|
+
|
|
4832
|
+
// src/server/mcp/server/tools.ts
|
|
4833
|
+
import { z } from "zod";
|
|
4834
|
+
var CONTINUE_PROMPT = "Continue working on the acceptance criteria.";
|
|
4835
|
+
var MAX_DETAIL_CONTENT_LENGTH_DEFAULT = 400;
|
|
4836
|
+
var MIN_DETAIL_CONTENT_LENGTH = 50;
|
|
4837
|
+
var MAX_DETAIL_CONTENT_LENGTH_HARD_CAP = 1e4;
|
|
4838
|
+
var WAIT_TIMEOUT_SECONDS_DEFAULT = 60;
|
|
4839
|
+
var WAIT_TIMEOUT_SECONDS_MIN = 5;
|
|
4840
|
+
var WAIT_TIMEOUT_SECONDS_MAX = 600;
|
|
4841
|
+
var WAIT_POLL_INTERVAL_MS = 500;
|
|
4842
|
+
var WAIT_IDLE_GRACE_MS = 2e3;
|
|
4843
|
+
var METADATA_SCHEMAS = {
|
|
4844
|
+
criteria: {
|
|
4845
|
+
description: "Acceptance criteria that drive workflow transitions",
|
|
4846
|
+
fields: {
|
|
4847
|
+
id: "string (auto-generated)",
|
|
4848
|
+
description: "string \u2014 what needs to be done and how to verify it",
|
|
4849
|
+
status: "pending | completed | passed | failed"
|
|
4850
|
+
},
|
|
4851
|
+
defaultStatus: "pending"
|
|
4852
|
+
},
|
|
4853
|
+
todos: {
|
|
4854
|
+
description: "Task tracking items for the builder",
|
|
4855
|
+
fields: {
|
|
4856
|
+
id: "string (auto-generated)",
|
|
4857
|
+
description: "string \u2014 task description",
|
|
4858
|
+
status: "pending | in_progress | completed"
|
|
4859
|
+
},
|
|
4860
|
+
defaultStatus: "pending"
|
|
4861
|
+
},
|
|
4862
|
+
review_findings: {
|
|
4863
|
+
description: "Code review findings from the code_reviewer sub-agent",
|
|
4864
|
+
fields: {
|
|
4865
|
+
id: "string (auto-generated)",
|
|
4866
|
+
description: "string \u2014 finding description",
|
|
4867
|
+
status: "open | resolved | dismissed",
|
|
4868
|
+
severity: "minor | major | critical (optional)"
|
|
4869
|
+
},
|
|
4870
|
+
defaultStatus: "open"
|
|
4871
|
+
}
|
|
4872
|
+
};
|
|
4873
|
+
function computeSettlement(session, pendingQuestionsCount, pendingConfirmationsCount, activeExecutionStatus, completedEvidence = false) {
|
|
4874
|
+
if (session.phase === "done") return { settled: true, outcome: "completed" };
|
|
4875
|
+
if (session.phase === "blocked") return { settled: true, outcome: "blocked" };
|
|
4876
|
+
const waitingForUser = pendingQuestionsCount > 0 || pendingConfirmationsCount > 0 || activeExecutionStatus === "waiting";
|
|
4877
|
+
if (waitingForUser) return { settled: true, outcome: "waiting" };
|
|
4878
|
+
if (completedEvidence && !session.isRunning) return { settled: true, outcome: "completed" };
|
|
4879
|
+
return { settled: false, outcome: null };
|
|
4880
|
+
}
|
|
4881
|
+
function workflowPayload(execution) {
|
|
4882
|
+
if (!execution) return null;
|
|
4883
|
+
const pausedAtUserStep = execution.status === "waiting" && (execution.pendingChoices?.length ?? 0) > 0;
|
|
4884
|
+
return {
|
|
4885
|
+
id: execution.id,
|
|
4886
|
+
name: execution.workflowName,
|
|
4887
|
+
status: execution.status,
|
|
4888
|
+
...execution.currentStepId ? { currentStepId: execution.currentStepId } : {},
|
|
4889
|
+
...execution.currentStepName ? { currentStepName: execution.currentStepName } : {},
|
|
4890
|
+
stepOutput: execution.stepOutput,
|
|
4891
|
+
...execution.pendingChoices ? { pendingChoices: execution.pendingChoices } : {},
|
|
4892
|
+
...execution.subGroup ? { subGroup: execution.subGroup } : {},
|
|
4893
|
+
...pausedAtUserStep ? { resumeHint: "Call openfox_resume_workflow with choice set to one of the pendingChoices ids above." } : {}
|
|
4894
|
+
};
|
|
4895
|
+
}
|
|
4896
|
+
function normalizeMetadataId(id) {
|
|
4897
|
+
if (typeof id !== "string" && typeof id !== "number") return void 0;
|
|
4898
|
+
if (id === "") return void 0;
|
|
4899
|
+
return String(id);
|
|
4900
|
+
}
|
|
4901
|
+
function resolveWaitTimeout(value) {
|
|
4902
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return WAIT_TIMEOUT_SECONDS_DEFAULT;
|
|
4903
|
+
return Math.max(WAIT_TIMEOUT_SECONDS_MIN, Math.min(WAIT_TIMEOUT_SECONDS_MAX, Math.floor(value)));
|
|
4904
|
+
}
|
|
4905
|
+
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
4906
|
+
function ok(payload) {
|
|
4907
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
4908
|
+
}
|
|
4909
|
+
function fail(message) {
|
|
4910
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
4911
|
+
}
|
|
4912
|
+
function truncate(content, maxLength) {
|
|
4913
|
+
return content.length > maxLength ? `${content.slice(0, maxLength)}...` : content;
|
|
4914
|
+
}
|
|
4915
|
+
function resolveMaxContentLength(value) {
|
|
4916
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return MAX_DETAIL_CONTENT_LENGTH_DEFAULT;
|
|
4917
|
+
return Math.max(MIN_DETAIL_CONTENT_LENGTH, Math.min(MAX_DETAIL_CONTENT_LENGTH_HARD_CAP, Math.floor(value)));
|
|
4918
|
+
}
|
|
4919
|
+
function buildSessionStatusPayload(sessionId, deps) {
|
|
4920
|
+
const session = deps.sessionManager.getSession(sessionId);
|
|
4921
|
+
if (!session) return null;
|
|
4922
|
+
const pendingQuestions = deps.pendingQuestions(sessionId);
|
|
4923
|
+
const pendingConfirmations = deps.pendingConfirmations(sessionId);
|
|
4924
|
+
const activeExecution = deps.sessionManager.getActiveWorkflowExecution(sessionId);
|
|
4925
|
+
const status = projectSessionStatus({
|
|
4926
|
+
session,
|
|
4927
|
+
pendingQuestionsCount: pendingQuestions.length,
|
|
4928
|
+
pendingConfirmationsCount: pendingConfirmations.length,
|
|
4929
|
+
activeWorkflowStepName: activeExecution?.currentStepName ?? null
|
|
4930
|
+
});
|
|
4931
|
+
return {
|
|
4932
|
+
...status,
|
|
4933
|
+
isRunning: session.isRunning,
|
|
4934
|
+
pending: { questions: pendingQuestions, confirmations: pendingConfirmations },
|
|
4935
|
+
workflow: workflowPayload(activeExecution)
|
|
4936
|
+
};
|
|
4937
|
+
}
|
|
4938
|
+
function createOpenFoxMcpTools(deps) {
|
|
4939
|
+
const { sessionManager } = deps;
|
|
4940
|
+
const requireSession = (sessionId) => {
|
|
4941
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
4942
|
+
return { ok: false, error: "sessionId is required" };
|
|
4943
|
+
}
|
|
4944
|
+
const session = sessionManager.getSession(sessionId);
|
|
4945
|
+
if (!session) {
|
|
4946
|
+
return { ok: false, error: `Session not found: ${sessionId}` };
|
|
4947
|
+
}
|
|
4948
|
+
return { ok: true, session };
|
|
4949
|
+
};
|
|
4950
|
+
const requireSessionAndId = (args) => {
|
|
4951
|
+
const check = requireSession(args["sessionId"]);
|
|
4952
|
+
if (!check.ok) return check;
|
|
4953
|
+
return { ok: true, session: check.session, sessionId: check.session.id };
|
|
4954
|
+
};
|
|
4955
|
+
const resolvePendingCallId = (sessionId, callId, pending, noun) => {
|
|
4956
|
+
if (typeof callId !== "string" || callId.length === 0) {
|
|
4957
|
+
return { kind: "list" };
|
|
4958
|
+
}
|
|
4959
|
+
if (!pending.some((item) => item.callId === callId)) {
|
|
4960
|
+
return { kind: "error", error: `No pending ${noun} with callId "${callId}" for session ${sessionId}` };
|
|
4961
|
+
}
|
|
4962
|
+
return { kind: "found", callId };
|
|
4963
|
+
};
|
|
4964
|
+
const tools = [
|
|
4965
|
+
{
|
|
4966
|
+
name: "openfox_projects",
|
|
4967
|
+
description: "List all OpenFox projects.",
|
|
4968
|
+
inputSchema: {},
|
|
4969
|
+
handler: async () => {
|
|
4970
|
+
const projects = deps.listProjects();
|
|
4971
|
+
return ok(projects);
|
|
4972
|
+
}
|
|
4973
|
+
},
|
|
4974
|
+
{
|
|
4975
|
+
name: "openfox_create_project",
|
|
4976
|
+
description: "Create a new OpenFox project at the given directory. Creates the directory and initializes git if missing, then registers the project.",
|
|
4977
|
+
inputSchema: {
|
|
4978
|
+
name: z.string().describe("Project name (letters, numbers, hyphens, underscores, dots, spaces)"),
|
|
4979
|
+
workdir: z.string().describe("Absolute path of the project directory")
|
|
4980
|
+
},
|
|
4981
|
+
handler: async (args) => {
|
|
4982
|
+
const name = args["name"];
|
|
4983
|
+
const workdir = args["workdir"];
|
|
4984
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
4985
|
+
return fail("name is required");
|
|
4986
|
+
}
|
|
4987
|
+
if (typeof workdir !== "string" || workdir.trim().length === 0) {
|
|
4988
|
+
return fail("workdir is required");
|
|
4989
|
+
}
|
|
4990
|
+
try {
|
|
4991
|
+
const project = await deps.createProject(name.trim(), workdir.trim());
|
|
4992
|
+
return ok({ project });
|
|
4993
|
+
} catch (error) {
|
|
4994
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
},
|
|
4998
|
+
{
|
|
4999
|
+
name: "openfox_delete_project",
|
|
5000
|
+
description: "Delete an OpenFox project and all its sessions by id.",
|
|
5001
|
+
inputSchema: {
|
|
5002
|
+
projectId: z.string().describe("Project id to delete")
|
|
5003
|
+
},
|
|
5004
|
+
handler: async (args) => {
|
|
5005
|
+
const projectId = args["projectId"];
|
|
5006
|
+
if (typeof projectId !== "string" || projectId.length === 0) {
|
|
5007
|
+
return fail("projectId is required");
|
|
5008
|
+
}
|
|
5009
|
+
const deleted = deps.deleteProject(projectId);
|
|
5010
|
+
return ok({ deleted });
|
|
5011
|
+
}
|
|
5012
|
+
},
|
|
5013
|
+
{
|
|
5014
|
+
name: "openfox_create_session",
|
|
5015
|
+
description: "Create a new OpenFox session in a project, optionally selecting an agent (e.g. planner or builder) as the session mode.",
|
|
5016
|
+
inputSchema: {
|
|
5017
|
+
projectId: z.string().describe("Project to create the session in"),
|
|
5018
|
+
title: z.string().optional().describe("Optional session title"),
|
|
5019
|
+
agentId: z.string().optional().describe("Optional agent to use as the session mode (validated against available top-level agents)")
|
|
5020
|
+
},
|
|
5021
|
+
handler: async (args) => {
|
|
5022
|
+
const projectId = args["projectId"];
|
|
5023
|
+
if (typeof projectId !== "string" || projectId.length === 0) {
|
|
5024
|
+
return fail("projectId is required");
|
|
5025
|
+
}
|
|
5026
|
+
const title = typeof args["title"] === "string" ? args["title"] : void 0;
|
|
5027
|
+
const rawAgentId = args["agentId"];
|
|
5028
|
+
const agentId = typeof rawAgentId === "string" && rawAgentId.trim().length > 0 ? rawAgentId : void 0;
|
|
5029
|
+
try {
|
|
5030
|
+
const session = sessionManager.createSession(projectId, title, null, null);
|
|
5031
|
+
if (agentId) {
|
|
5032
|
+
const project = sessionManager.getProject(projectId);
|
|
5033
|
+
const topLevelIds = project ? await deps.topLevelAgentIds(project.workdir) : [];
|
|
5034
|
+
if (!topLevelIds.includes(agentId)) {
|
|
5035
|
+
return fail(
|
|
5036
|
+
`Invalid agentId "${agentId}". Must be one of: ${topLevelIds.join(", ") || "(none available)"}`
|
|
5037
|
+
);
|
|
5038
|
+
}
|
|
5039
|
+
sessionManager.setMode(session.id, agentId);
|
|
5040
|
+
}
|
|
5041
|
+
return ok({
|
|
5042
|
+
sessionId: session.id,
|
|
5043
|
+
projectId,
|
|
5044
|
+
title: session.metadata?.title ?? title ?? null,
|
|
5045
|
+
mode: agentId ?? session.mode
|
|
5046
|
+
});
|
|
5047
|
+
} catch (error) {
|
|
5048
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
},
|
|
5052
|
+
{
|
|
5053
|
+
name: "openfox_set_mode",
|
|
5054
|
+
description: "Switch an existing session to a different mode (agent, e.g. planner or builder) at any time. The mode is validated against the session project\u2019s top-level agents.",
|
|
5055
|
+
inputSchema: {
|
|
5056
|
+
sessionId: z.string().describe("Session to switch"),
|
|
5057
|
+
mode: z.string().describe("Target mode: one of the session project\u2019s top-level agents")
|
|
5058
|
+
},
|
|
5059
|
+
handler: async (args) => {
|
|
5060
|
+
const check = requireSession(args["sessionId"]);
|
|
5061
|
+
if (!check.ok) return fail(check.error);
|
|
5062
|
+
const session = check.session;
|
|
5063
|
+
const mode = args["mode"];
|
|
5064
|
+
if (typeof mode !== "string" || mode.trim().length === 0) {
|
|
5065
|
+
return fail("mode is required");
|
|
5066
|
+
}
|
|
5067
|
+
const project = sessionManager.getProject(session.projectId);
|
|
5068
|
+
const topLevelIds = project ? await deps.topLevelAgentIds(project.workdir) : [];
|
|
5069
|
+
if (!topLevelIds.includes(mode)) {
|
|
5070
|
+
return fail(`Invalid mode "${mode}". Must be one of: ${topLevelIds.join(", ") || "(none available)"}`);
|
|
5071
|
+
}
|
|
5072
|
+
const updated = sessionManager.setMode(session.id, mode);
|
|
5073
|
+
return ok({ sessionId: session.id, mode: updated?.mode ?? mode });
|
|
5074
|
+
}
|
|
5075
|
+
},
|
|
5076
|
+
{
|
|
5077
|
+
name: "openfox_sessions",
|
|
5078
|
+
description: "List sessions, optionally scoped to a project, with running state and pending interaction counts.",
|
|
5079
|
+
inputSchema: {
|
|
5080
|
+
projectId: z.string().optional().describe("Scope the listing to a project"),
|
|
5081
|
+
limit: z.number().int().positive().optional().describe("Maximum sessions to return (default 20)"),
|
|
5082
|
+
offset: z.number().int().nonnegative().optional().describe("Number of sessions to skip (default 0)")
|
|
5083
|
+
},
|
|
5084
|
+
handler: async (args) => {
|
|
5085
|
+
const limit = typeof args["limit"] === "number" ? args["limit"] : 20;
|
|
5086
|
+
const offset = typeof args["offset"] === "number" ? args["offset"] : 0;
|
|
5087
|
+
const projectId = args["projectId"];
|
|
5088
|
+
const result = typeof projectId === "string" && projectId.length > 0 ? sessionManager.listSessionsByProject(projectId, limit, offset) : sessionManager.listSessionsLimited(limit, offset);
|
|
5089
|
+
const sessions = result.sessions.map((s) => ({
|
|
5090
|
+
id: s.id,
|
|
5091
|
+
projectId: s.projectId,
|
|
5092
|
+
title: s.title ?? null,
|
|
5093
|
+
isRunning: s.isRunning,
|
|
5094
|
+
pendingQuestionCount: deps.pendingQuestions(s.id).length,
|
|
5095
|
+
pendingConfirmationCount: deps.pendingConfirmations(s.id).length,
|
|
5096
|
+
pausedWorkflowStep: sessionManager.getActiveWorkflowExecution(s.id)?.status === "waiting",
|
|
5097
|
+
updatedAt: s.updatedAt
|
|
5098
|
+
}));
|
|
5099
|
+
return ok({ sessions, hasMore: result.hasMore });
|
|
5100
|
+
}
|
|
5101
|
+
},
|
|
5102
|
+
{
|
|
5103
|
+
name: "openfox_session_status",
|
|
5104
|
+
description: "Read a session state snapshot: projected status, running flag, active workflow execution (including a paused user step with its choices), pending questions and confirmations, last activity, and a UI link.",
|
|
5105
|
+
inputSchema: {
|
|
5106
|
+
sessionId: z.string().describe("Session to inspect")
|
|
5107
|
+
},
|
|
5108
|
+
handler: async (args) => {
|
|
5109
|
+
const check = requireSession(args["sessionId"]);
|
|
5110
|
+
if (!check.ok) return fail(check.error);
|
|
5111
|
+
const payload = buildSessionStatusPayload(check.session.id, deps);
|
|
5112
|
+
if (!payload) return fail(`Session not found: ${check.session.id}`);
|
|
5113
|
+
return ok(payload);
|
|
5114
|
+
}
|
|
5115
|
+
},
|
|
5116
|
+
{
|
|
5117
|
+
name: "openfox_session_detail",
|
|
5118
|
+
description: "Read the most recent messages of a session (content truncated) so progress can be followed without a live WebSocket.",
|
|
5119
|
+
inputSchema: {
|
|
5120
|
+
sessionId: z.string().describe("Session to read"),
|
|
5121
|
+
limit: z.number().int().positive().optional().describe("Maximum messages to return (default 20, capped at 50)"),
|
|
5122
|
+
maxContentLength: z.number().int().positive().optional().describe("Maximum content length per message (default 400, clamped to 50\u201310000)")
|
|
5123
|
+
},
|
|
5124
|
+
handler: async (args) => {
|
|
5125
|
+
const check = requireSession(args["sessionId"]);
|
|
5126
|
+
if (!check.ok) return fail(check.error);
|
|
5127
|
+
const limit = typeof args["limit"] === "number" ? args["limit"] : 20;
|
|
5128
|
+
const maxContentLength = resolveMaxContentLength(args["maxContentLength"]);
|
|
5129
|
+
const { messages, hiddenCount } = deps.recentMessages(check.session.id, limit);
|
|
5130
|
+
return ok({
|
|
5131
|
+
messages: messages.map((message) => ({
|
|
5132
|
+
id: message.id,
|
|
5133
|
+
role: message.role,
|
|
5134
|
+
content: truncate(message.content ?? "", maxContentLength),
|
|
5135
|
+
...message.toolCalls && message.toolCalls.length > 0 ? { toolCalls: message.toolCalls.map((toolCall) => toolCall.name) } : {}
|
|
5136
|
+
})),
|
|
5137
|
+
hiddenCount
|
|
5138
|
+
});
|
|
5139
|
+
}
|
|
5140
|
+
},
|
|
5141
|
+
{
|
|
5142
|
+
name: "openfox_session_metadata",
|
|
5143
|
+
description: "Manage structured session data by key (criteria, todos, review_findings, ...). Actions: get (list entries for a key), list (keys), add, update, remove, schema. Writes land in the same store as the agent\u2019s session_metadata tool, so they drive workflow transitions.",
|
|
5144
|
+
inputSchema: {
|
|
5145
|
+
sessionId: z.string().describe("Session to manage"),
|
|
5146
|
+
action: z.enum(["get", "list", "add", "update", "remove", "schema"]).describe("Operation to perform"),
|
|
5147
|
+
key: z.string().optional().describe("Metadata key (required for get/add/update/remove/schema)"),
|
|
5148
|
+
id: z.string().optional().describe("Item id (required for update and remove)"),
|
|
5149
|
+
description: z.string().optional().describe("Item description (required for add)"),
|
|
5150
|
+
status: z.string().optional().describe("Item status (for add/update)")
|
|
5151
|
+
},
|
|
5152
|
+
handler: async (args) => {
|
|
5153
|
+
const check = requireSession(args["sessionId"]);
|
|
5154
|
+
if (!check.ok) return fail(check.error);
|
|
5155
|
+
const session = check.session;
|
|
5156
|
+
const action = args["action"];
|
|
5157
|
+
const key = typeof args["key"] === "string" && args["key"].length > 0 ? args["key"] : void 0;
|
|
5158
|
+
const entries = session.metadataEntries ?? {};
|
|
5159
|
+
if (action === "list") {
|
|
5160
|
+
return ok({ keys: Object.entries(entries).map(([k, v]) => ({ key: k, count: v.length })) });
|
|
5161
|
+
}
|
|
5162
|
+
if (action === "schema") {
|
|
5163
|
+
if (!key) return fail("key is required for schema");
|
|
5164
|
+
const schema = METADATA_SCHEMAS[key];
|
|
5165
|
+
if (!schema) return ok({ key, generic: true });
|
|
5166
|
+
return ok({ key, description: schema.description, fields: schema.fields });
|
|
5167
|
+
}
|
|
5168
|
+
if (action === "get") {
|
|
5169
|
+
if (!key) return fail("key is required for get");
|
|
5170
|
+
return ok({ key, entries: entries[key] ?? [] });
|
|
5171
|
+
}
|
|
5172
|
+
if (action === "add") {
|
|
5173
|
+
if (!key) return fail("key is required for add");
|
|
5174
|
+
const description = args["description"];
|
|
5175
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
5176
|
+
return fail("description is required for add");
|
|
5177
|
+
}
|
|
5178
|
+
const current = entries[key] ?? [];
|
|
5179
|
+
const newEntry = {
|
|
5180
|
+
id: normalizeMetadataId(args["id"]) ?? (current.length + 1).toString(),
|
|
5181
|
+
description,
|
|
5182
|
+
status: typeof args["status"] === "string" ? args["status"] : METADATA_SCHEMAS[key]?.defaultStatus ?? "pending"
|
|
5183
|
+
};
|
|
5184
|
+
const updated = [...current, newEntry];
|
|
5185
|
+
deps.setMetadataEntries(session.id, key, updated);
|
|
5186
|
+
return ok({ key, added: newEntry, entries: updated });
|
|
5187
|
+
}
|
|
5188
|
+
if (action === "update") {
|
|
5189
|
+
if (!key) return fail("key is required for update");
|
|
5190
|
+
const id = normalizeMetadataId(args["id"]);
|
|
5191
|
+
if (!id) return fail("id is required for update");
|
|
5192
|
+
const current = entries[key];
|
|
5193
|
+
if (!current) return fail(`Key "${key}" not found`);
|
|
5194
|
+
const idx = current.findIndex((e) => e.id === id);
|
|
5195
|
+
if (idx === -1) return fail(`Item "${id}" not found in "${key}"`);
|
|
5196
|
+
const description = args["description"];
|
|
5197
|
+
const status = args["status"];
|
|
5198
|
+
if (typeof description !== "string" && typeof status !== "string") {
|
|
5199
|
+
return fail("provide at least one of description or status for update");
|
|
5200
|
+
}
|
|
5201
|
+
const updated = current.map(
|
|
5202
|
+
(e, i) => i === idx ? {
|
|
5203
|
+
...e,
|
|
5204
|
+
...typeof description === "string" ? { description } : {},
|
|
5205
|
+
...typeof status === "string" ? { status } : {}
|
|
5206
|
+
} : e
|
|
5207
|
+
);
|
|
5208
|
+
deps.setMetadataEntries(session.id, key, updated);
|
|
5209
|
+
return ok({ key, updated: id, entries: updated });
|
|
5210
|
+
}
|
|
5211
|
+
if (action === "remove") {
|
|
5212
|
+
if (!key) return fail("key is required for remove");
|
|
5213
|
+
const id = normalizeMetadataId(args["id"]);
|
|
5214
|
+
if (!id) return fail("id is required for remove");
|
|
5215
|
+
const current = entries[key];
|
|
5216
|
+
if (!current) return fail(`Key "${key}" not found`);
|
|
5217
|
+
const updated = current.filter((e) => e.id !== id);
|
|
5218
|
+
if (updated.length === current.length) return fail(`Item "${id}" not found in "${key}"`);
|
|
5219
|
+
deps.setMetadataEntries(session.id, key, updated);
|
|
5220
|
+
return ok({ key, removed: id, entries: updated });
|
|
5221
|
+
}
|
|
5222
|
+
return fail(`Unknown action "${String(action)}". Must be one of: get, list, add, update, remove, schema`);
|
|
5223
|
+
}
|
|
5224
|
+
},
|
|
5225
|
+
{
|
|
5226
|
+
name: "openfox_send_message",
|
|
5227
|
+
description: "Queue a message for a session. Safe on running sessions (queued, processed at the next turn boundary) and starts a new turn on idle sessions. Returns the queue state.",
|
|
5228
|
+
inputSchema: {
|
|
5229
|
+
sessionId: z.string().describe("Session to message"),
|
|
5230
|
+
content: z.string().optional().describe("Message content"),
|
|
5231
|
+
attachments: z.array(z.record(z.string(), z.unknown())).optional().describe("Optional attachments"),
|
|
5232
|
+
messageKind: z.string().optional().describe("Optional message kind tag")
|
|
5233
|
+
},
|
|
5234
|
+
handler: async (args) => {
|
|
5235
|
+
const check = requireSession(args["sessionId"]);
|
|
5236
|
+
if (!check.ok) return fail(check.error);
|
|
5237
|
+
const content = typeof args["content"] === "string" ? args["content"] : void 0;
|
|
5238
|
+
const rawAttachments = args["attachments"];
|
|
5239
|
+
const attachments = Array.isArray(rawAttachments) ? rawAttachments : void 0;
|
|
5240
|
+
const messageKind = typeof args["messageKind"] === "string" ? args["messageKind"] : void 0;
|
|
5241
|
+
if (!content?.trim() && !(attachments && attachments.length > 0)) {
|
|
5242
|
+
return fail("content or attachments is required");
|
|
5243
|
+
}
|
|
5244
|
+
sessionManager.queueMessage(check.session.id, "asap", content, attachments, messageKind);
|
|
5245
|
+
return ok({ queued: true, queueState: sessionManager.getQueueState(check.session.id) });
|
|
5246
|
+
}
|
|
5247
|
+
},
|
|
5248
|
+
{
|
|
5249
|
+
name: "openfox_continue",
|
|
5250
|
+
description: "Continue an idle or blocked session: a blocked session is reset to the build phase and an auto-prompt is queued; an idle session is acknowledged (use openfox_send_message to drive it with content).",
|
|
5251
|
+
inputSchema: {
|
|
5252
|
+
sessionId: z.string().describe("Session to continue")
|
|
5253
|
+
},
|
|
5254
|
+
handler: async (args) => {
|
|
5255
|
+
const check = requireSessionAndId(args);
|
|
5256
|
+
if (!check.ok) return fail(check.error);
|
|
5257
|
+
const { session, sessionId } = check;
|
|
5258
|
+
if (session.isRunning) {
|
|
5259
|
+
return fail(`Session ${sessionId} is already running`);
|
|
5260
|
+
}
|
|
5261
|
+
if (session.phase === "blocked") {
|
|
5262
|
+
sessionManager.setPhase(sessionId, "build");
|
|
5263
|
+
sessionManager.queueMessage(sessionId, "asap", CONTINUE_PROMPT, void 0, "auto-prompt");
|
|
5264
|
+
return ok({ accepted: true });
|
|
5265
|
+
}
|
|
5266
|
+
return ok({
|
|
5267
|
+
accepted: true,
|
|
5268
|
+
note: `Session ${sessionId} is idle \u2014 use openfox_send_message to give it work.`
|
|
5269
|
+
});
|
|
5270
|
+
}
|
|
5271
|
+
},
|
|
5272
|
+
{
|
|
5273
|
+
name: "openfox_wait",
|
|
5274
|
+
description: "Block until a session settles: it finishes (completed), blocks (blocked), or pauses for the user (waiting \u2014 a pending question, path confirmation, or a paused workflow user step). Returns the outcome and a full status snapshot, or times out.",
|
|
5275
|
+
inputSchema: {
|
|
5276
|
+
sessionId: z.string().describe("Session to wait on"),
|
|
5277
|
+
timeout: z.number().int().positive().optional().describe("Maximum wait in seconds (default 60, clamped 5\u2013600)")
|
|
5278
|
+
},
|
|
5279
|
+
handler: async (args) => {
|
|
5280
|
+
const check = requireSession(args["sessionId"]);
|
|
5281
|
+
if (!check.ok) return fail(check.error);
|
|
5282
|
+
const sessionId = check.session.id;
|
|
5283
|
+
const timeoutSeconds = resolveWaitTimeout(args["timeout"]);
|
|
5284
|
+
const startedAt = Date.now();
|
|
5285
|
+
const deadline = startedAt + timeoutSeconds * 1e3;
|
|
5286
|
+
const graceUntil = startedAt + WAIT_IDLE_GRACE_MS;
|
|
5287
|
+
let sawRunning = false;
|
|
5288
|
+
while (true) {
|
|
5289
|
+
const session = sessionManager.getSession(sessionId);
|
|
5290
|
+
if (!session) return fail(`Session not found: ${sessionId}`);
|
|
5291
|
+
if (session.isRunning) sawRunning = true;
|
|
5292
|
+
const pendingQuestions = deps.pendingQuestions(sessionId);
|
|
5293
|
+
const pendingConfirmations = deps.pendingConfirmations(sessionId);
|
|
5294
|
+
const activeExecution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
5295
|
+
const now = Date.now();
|
|
5296
|
+
const hasWork = (session.messages?.length ?? 0) > 0;
|
|
5297
|
+
const completedEvidence = sawRunning || hasWork && now >= graceUntil;
|
|
5298
|
+
const settlement = computeSettlement(
|
|
5299
|
+
session,
|
|
5300
|
+
pendingQuestions.length,
|
|
5301
|
+
pendingConfirmations.length,
|
|
5302
|
+
activeExecution?.status ?? null,
|
|
5303
|
+
completedEvidence
|
|
5304
|
+
);
|
|
5305
|
+
if (settlement.settled) {
|
|
5306
|
+
return ok({
|
|
5307
|
+
settled: true,
|
|
5308
|
+
outcome: settlement.outcome,
|
|
5309
|
+
waitedMs: now - startedAt,
|
|
5310
|
+
status: buildSessionStatusPayload(sessionId, deps)
|
|
5311
|
+
});
|
|
5312
|
+
}
|
|
5313
|
+
if (now >= deadline) {
|
|
5314
|
+
return ok({
|
|
5315
|
+
settled: false,
|
|
5316
|
+
timedOut: true,
|
|
5317
|
+
waitedMs: now - startedAt,
|
|
5318
|
+
status: buildSessionStatusPayload(sessionId, deps)
|
|
5319
|
+
});
|
|
5320
|
+
}
|
|
5321
|
+
await sleep2(WAIT_POLL_INTERVAL_MS);
|
|
5322
|
+
}
|
|
5323
|
+
}
|
|
5324
|
+
},
|
|
5325
|
+
{
|
|
5326
|
+
name: "openfox_stop",
|
|
5327
|
+
description: "Stop a session completely: drains the queue, aborts the active execution, and cancels pending interactions.",
|
|
5328
|
+
inputSchema: {
|
|
5329
|
+
sessionId: z.string().describe("Session to stop")
|
|
5330
|
+
},
|
|
5331
|
+
handler: async (args) => {
|
|
5332
|
+
const check = requireSession(args["sessionId"]);
|
|
5333
|
+
if (!check.ok) return fail(check.error);
|
|
5334
|
+
deps.stopSession(check.session.id);
|
|
5335
|
+
return ok({ stopped: true });
|
|
5336
|
+
}
|
|
5337
|
+
},
|
|
5338
|
+
{
|
|
5339
|
+
name: "openfox_workflows",
|
|
5340
|
+
description: "List the workflows available to a project (builtin, user, and project scope) with their parameters.",
|
|
5341
|
+
inputSchema: {
|
|
5342
|
+
projectDir: z.string().optional().describe("Project directory for project-scoped workflows")
|
|
5343
|
+
},
|
|
5344
|
+
handler: async (args) => {
|
|
5345
|
+
const projectDir = typeof args["projectDir"] === "string" ? args["projectDir"] : void 0;
|
|
5346
|
+
return ok(await deps.listWorkflows(projectDir));
|
|
5347
|
+
}
|
|
5348
|
+
},
|
|
5349
|
+
{
|
|
5350
|
+
name: "openfox_launch_workflow",
|
|
5351
|
+
description: "Launch (or resume) a workflow run in a session. On idle sessions the run starts immediately; on running sessions it is queued as a workflow-launch marker. Supports resuming paused user steps via resumeFrom/userChoice.",
|
|
5352
|
+
inputSchema: {
|
|
5353
|
+
sessionId: z.string().describe("Session to run the workflow in"),
|
|
5354
|
+
workflowId: z.string().optional().describe("Workflow to run (default: the active workflow)"),
|
|
5355
|
+
params: z.record(z.string(), z.string()).optional().describe("Workflow parameters"),
|
|
5356
|
+
subGroup: z.string().optional().describe("Run only a sub-group slice of the workflow"),
|
|
5357
|
+
scope: z.enum(["builtin", "user", "project"]).optional().describe("Workflow scope to resolve from"),
|
|
5358
|
+
resumeFrom: z.string().optional().describe("Step id to resume a paused workflow from"),
|
|
5359
|
+
stepOutput: z.record(z.string(), z.string()).optional().describe("Step output supplied when resuming"),
|
|
5360
|
+
userChoice: z.string().optional().describe("Branch chosen by the user at a paused user step"),
|
|
5361
|
+
content: z.string().optional().describe("Optional extra instruction for the run")
|
|
5362
|
+
},
|
|
5363
|
+
handler: async (args) => {
|
|
5364
|
+
const check = requireSessionAndId(args);
|
|
5365
|
+
if (!check.ok) return fail(check.error);
|
|
5366
|
+
const { session, sessionId } = check;
|
|
5367
|
+
const payload = {
|
|
5368
|
+
...typeof args["workflowId"] === "string" ? { workflowId: args["workflowId"] } : {},
|
|
5369
|
+
...args["params"] && typeof args["params"] === "object" ? { params: args["params"] } : {},
|
|
5370
|
+
...typeof args["subGroup"] === "string" ? { subGroup: args["subGroup"] } : {},
|
|
5371
|
+
...typeof args["scope"] === "string" ? { scope: args["scope"] } : {},
|
|
5372
|
+
...typeof args["resumeFrom"] === "string" ? { resumeFrom: args["resumeFrom"] } : {},
|
|
5373
|
+
...args["stepOutput"] && typeof args["stepOutput"] === "object" ? { stepOutput: args["stepOutput"] } : {},
|
|
5374
|
+
...typeof args["userChoice"] === "string" ? { userChoice: args["userChoice"] } : {},
|
|
5375
|
+
...typeof args["content"] === "string" ? { content: args["content"] } : {}
|
|
5376
|
+
};
|
|
5377
|
+
if (payload.workflowId && !payload.resumeFrom) {
|
|
5378
|
+
const workflows = await deps.listWorkflows(session.workdir);
|
|
5379
|
+
const known = workflows.some((w) => w.id === payload.workflowId);
|
|
5380
|
+
if (!known) {
|
|
5381
|
+
return fail(`Workflow "${payload.workflowId}" not found for project ${session.workdir}`);
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
if (session.isRunning) {
|
|
5385
|
+
let fullContent = payload.content ?? "";
|
|
5386
|
+
if (payload.workflowId) {
|
|
5387
|
+
const workflowInfo = `// Workflow: ${payload.workflowId}`;
|
|
5388
|
+
fullContent = fullContent ? `${workflowInfo}
|
|
5389
|
+
|
|
5390
|
+
${fullContent}` : workflowInfo;
|
|
5391
|
+
}
|
|
5392
|
+
if (!fullContent) {
|
|
5393
|
+
return fail("Session is running \u2014 give a workflowId or content to queue for the next turn boundary.");
|
|
5394
|
+
}
|
|
5395
|
+
sessionManager.queueMessage(sessionId, "asap", fullContent, void 0, "workflow-launch");
|
|
5396
|
+
return ok({ queued: true, queueState: sessionManager.getQueueState(sessionId) });
|
|
5397
|
+
}
|
|
5398
|
+
if (session.phase === "blocked") {
|
|
5399
|
+
sessionManager.setPhase(sessionId, "build");
|
|
5400
|
+
}
|
|
5401
|
+
deps.launchWorkflow(sessionId, payload);
|
|
5402
|
+
return ok({ launched: true });
|
|
5403
|
+
}
|
|
5404
|
+
},
|
|
5405
|
+
{
|
|
5406
|
+
name: "openfox_resume_workflow",
|
|
5407
|
+
description: 'Resolve a paused workflow user step by picking one of its choices (e.g. "Work in current workspace"). The session must be paused at a user step \u2014 see openfox_session_status \u2192 workflow.pendingChoices (and the resumeHint there). Accepts the choice id or its label.',
|
|
5408
|
+
inputSchema: {
|
|
5409
|
+
sessionId: z.string().describe("Session paused at a workflow user step"),
|
|
5410
|
+
choice: z.string().describe("One of the pendingChoices surfaced by openfox_session_status (id or label)")
|
|
5411
|
+
},
|
|
5412
|
+
handler: async (args) => {
|
|
5413
|
+
const check = requireSession(args["sessionId"]);
|
|
5414
|
+
if (!check.ok) return fail(check.error);
|
|
5415
|
+
const sessionId = check.session.id;
|
|
5416
|
+
const choice = args["choice"];
|
|
5417
|
+
if (typeof choice !== "string" || choice.trim().length === 0) {
|
|
5418
|
+
return fail("choice is required \u2014 pick one of the pendingChoices surfaced by openfox_session_status");
|
|
5419
|
+
}
|
|
5420
|
+
const execution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
5421
|
+
if (!execution || execution.status !== "waiting" || !execution.currentStepId) {
|
|
5422
|
+
return fail(`Session ${sessionId} is not paused at a workflow user step`);
|
|
5423
|
+
}
|
|
5424
|
+
const pendingChoices = execution.pendingChoices ?? [];
|
|
5425
|
+
if (pendingChoices.length === 0) {
|
|
5426
|
+
return fail(`No choices available for paused step "${execution.currentStepName ?? execution.currentStepId}"`);
|
|
5427
|
+
}
|
|
5428
|
+
const match = pendingChoices.find((c) => c.id === choice || c.label === choice);
|
|
5429
|
+
if (!match) {
|
|
5430
|
+
return fail(`Invalid choice "${choice}". Available: ${pendingChoices.map((c) => c.id).join(", ")}`);
|
|
5431
|
+
}
|
|
5432
|
+
deps.launchWorkflow(sessionId, {
|
|
5433
|
+
workflowId: execution.workflowId,
|
|
5434
|
+
resumeFrom: execution.currentStepId,
|
|
5435
|
+
userChoice: match.id,
|
|
5436
|
+
stepOutput: execution.stepOutput
|
|
5437
|
+
});
|
|
5438
|
+
return ok({
|
|
5439
|
+
resumed: true,
|
|
5440
|
+
choice: match.id,
|
|
5441
|
+
step: execution.currentStepName ?? execution.currentStepId
|
|
5442
|
+
});
|
|
5443
|
+
}
|
|
5444
|
+
},
|
|
5445
|
+
{
|
|
5446
|
+
name: "openfox_stop_workflow",
|
|
5447
|
+
description: "Abort the active workflow run of a session (task/MCP-launched runs): aborts a live run or cancels a paused user-step execution, and errors when nothing is active. Does not stop plain chat turns.",
|
|
5448
|
+
inputSchema: {
|
|
5449
|
+
sessionId: z.string().describe("Session whose workflow run should be aborted")
|
|
5450
|
+
},
|
|
5451
|
+
handler: async (args) => {
|
|
5452
|
+
const check = requireSession(args["sessionId"]);
|
|
5453
|
+
if (!check.ok) return fail(check.error);
|
|
5454
|
+
const stopped = deps.stopWorkflow(check.session.id);
|
|
5455
|
+
if (!stopped) return fail("No active workflow run to stop");
|
|
5456
|
+
return ok({ stopped: true, aborted: stopped.aborted });
|
|
5457
|
+
}
|
|
5458
|
+
},
|
|
5459
|
+
{
|
|
5460
|
+
name: "openfox_answer",
|
|
5461
|
+
description: "Answer or skip a pending ask_user question of a session by callId. Without a callId, lists the session pending questions.",
|
|
5462
|
+
inputSchema: {
|
|
5463
|
+
sessionId: z.string().describe("Session the question belongs to"),
|
|
5464
|
+
callId: z.string().optional().describe("Pending question to answer (omit to list)"),
|
|
5465
|
+
answer: z.string().optional().describe("The answer to provide"),
|
|
5466
|
+
skip: z.boolean().optional().describe("Skip the question instead of answering it")
|
|
5467
|
+
},
|
|
5468
|
+
handler: async (args) => {
|
|
5469
|
+
const check = requireSession(args["sessionId"]);
|
|
5470
|
+
if (!check.ok) return fail(check.error);
|
|
5471
|
+
const session = check.session;
|
|
5472
|
+
const pending = deps.pendingQuestions(session.id);
|
|
5473
|
+
const resolved = resolvePendingCallId(session.id, args["callId"], pending, "question");
|
|
5474
|
+
if (resolved.kind === "list") return ok({ questions: pending });
|
|
5475
|
+
if (resolved.kind === "error") return fail(resolved.error);
|
|
5476
|
+
const callId = resolved.callId;
|
|
5477
|
+
const skip = args["skip"] === true;
|
|
5478
|
+
const answer = typeof args["answer"] === "string" ? args["answer"] : "";
|
|
5479
|
+
if (!skip && answer.length === 0) {
|
|
5480
|
+
return fail("answer is required unless skip is true");
|
|
5481
|
+
}
|
|
5482
|
+
const found = deps.answerQuestion(callId, skip ? "" : answer, skip || void 0);
|
|
5483
|
+
if (!found) {
|
|
5484
|
+
return fail(`Question ${callId} is no longer pending`);
|
|
5485
|
+
}
|
|
5486
|
+
return ok({ answered: true, callId, skipped: skip || void 0 });
|
|
5487
|
+
}
|
|
5488
|
+
},
|
|
5489
|
+
{
|
|
5490
|
+
name: "openfox_confirm",
|
|
5491
|
+
description: "Resolve a pending path confirmation of a session (approve/deny, optionally always-allow the paths). Without a callId, lists the session pending confirmations.",
|
|
5492
|
+
inputSchema: {
|
|
5493
|
+
sessionId: z.string().describe("Session the confirmation belongs to"),
|
|
5494
|
+
callId: z.string().optional().describe("Pending confirmation to resolve (omit to list)"),
|
|
5495
|
+
approved: z.boolean().optional().describe("Approve or deny the requested access"),
|
|
5496
|
+
alwaysAllow: z.boolean().optional().describe("Permanently allow the paths for this session")
|
|
5497
|
+
},
|
|
5498
|
+
handler: async (args) => {
|
|
5499
|
+
const check = requireSession(args["sessionId"]);
|
|
5500
|
+
if (!check.ok) return fail(check.error);
|
|
5501
|
+
const session = check.session;
|
|
5502
|
+
const pending = deps.pendingConfirmations(session.id);
|
|
5503
|
+
const resolved = resolvePendingCallId(session.id, args["callId"], pending, "confirmation");
|
|
5504
|
+
if (resolved.kind === "list") return ok({ confirmations: pending });
|
|
5505
|
+
if (resolved.kind === "error") return fail(resolved.error);
|
|
5506
|
+
const callId = resolved.callId;
|
|
5507
|
+
const approved = args["approved"];
|
|
5508
|
+
if (typeof approved !== "boolean") {
|
|
5509
|
+
return fail("approved (boolean) is required");
|
|
5510
|
+
}
|
|
5511
|
+
const alwaysAllow = args["alwaysAllow"];
|
|
5512
|
+
const found = deps.confirmPath(callId, approved, alwaysAllow);
|
|
5513
|
+
if (!found) {
|
|
5514
|
+
return fail(`Confirmation ${callId} is no longer pending`);
|
|
5515
|
+
}
|
|
5516
|
+
return ok({ confirmed: true, callId, approved });
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
];
|
|
5520
|
+
return tools;
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5523
|
+
// src/server/mcp/server/endpoint.ts
|
|
5524
|
+
function buildOpenFoxMcpServer(deps) {
|
|
5525
|
+
const server = new McpServer({
|
|
5526
|
+
name: "openfox",
|
|
5527
|
+
version: "1.0.0"
|
|
5528
|
+
});
|
|
5529
|
+
for (const tool of createOpenFoxMcpTools(deps)) {
|
|
5530
|
+
const config3 = { description: tool.description };
|
|
5531
|
+
if (Object.keys(tool.inputSchema).length > 0) {
|
|
5532
|
+
config3["inputSchema"] = tool.inputSchema;
|
|
5533
|
+
}
|
|
5534
|
+
server.registerTool(
|
|
5535
|
+
tool.name,
|
|
5536
|
+
config3,
|
|
5537
|
+
async (args) => tool.handler(args ?? {})
|
|
5538
|
+
);
|
|
5539
|
+
}
|
|
5540
|
+
return server;
|
|
5541
|
+
}
|
|
5542
|
+
function createOpenFoxMcpRouter(options) {
|
|
4754
5543
|
const router = Router4();
|
|
5544
|
+
router.all("/", async (req, res) => {
|
|
5545
|
+
try {
|
|
5546
|
+
if (options.isAuthRequired() && !await options.isAuthorized(req)) {
|
|
5547
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
5548
|
+
return;
|
|
5549
|
+
}
|
|
5550
|
+
const deps = options.resolveDeps();
|
|
5551
|
+
if (!deps) {
|
|
5552
|
+
res.status(503).json({ error: "MCP server is not ready" });
|
|
5553
|
+
return;
|
|
5554
|
+
}
|
|
5555
|
+
const server = buildOpenFoxMcpServer(deps);
|
|
5556
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
5557
|
+
await server.connect(transport);
|
|
5558
|
+
await transport.handleRequest(req, res, req.body);
|
|
5559
|
+
} catch (error) {
|
|
5560
|
+
logger.error("MCP endpoint request failed", {
|
|
5561
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5562
|
+
});
|
|
5563
|
+
if (!res.headersSent) {
|
|
5564
|
+
res.status(500).json({ error: "MCP request failed" });
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5567
|
+
});
|
|
5568
|
+
return router;
|
|
5569
|
+
}
|
|
5570
|
+
function extractSessionToken(req) {
|
|
5571
|
+
const header = req.headers["x-session-token"];
|
|
5572
|
+
if (typeof header === "string" && header.length > 0) {
|
|
5573
|
+
return header;
|
|
5574
|
+
}
|
|
5575
|
+
const authorization = req.headers["authorization"];
|
|
5576
|
+
if (typeof authorization === "string" && authorization.startsWith("Bearer ")) {
|
|
5577
|
+
const token = authorization.slice("Bearer ".length).trim();
|
|
5578
|
+
return token.length > 0 ? token : null;
|
|
5579
|
+
}
|
|
5580
|
+
return null;
|
|
5581
|
+
}
|
|
5582
|
+
|
|
5583
|
+
// src/server/routes/dev-server.ts
|
|
5584
|
+
import { Router as Router5 } from "express";
|
|
5585
|
+
function createDevServerRoutes() {
|
|
5586
|
+
const router = Router5();
|
|
4755
5587
|
router.get("/", async (req, res) => {
|
|
4756
5588
|
const workdir = req.query["workdir"];
|
|
4757
5589
|
if (!workdir) return res.status(400).json({ error: "workdir required" });
|
|
@@ -4849,7 +5681,7 @@ function createDevServerRoutes() {
|
|
|
4849
5681
|
}
|
|
4850
5682
|
|
|
4851
5683
|
// src/server/routes/workspace-config.ts
|
|
4852
|
-
import { Router as
|
|
5684
|
+
import { Router as Router6 } from "express";
|
|
4853
5685
|
import { stat as stat2, mkdir as mkdir3, readdir, access as access3 } from "fs/promises";
|
|
4854
5686
|
import { constants as constants3 } from "fs";
|
|
4855
5687
|
import { resolve as resolve4, isAbsolute as isAbsolute3, join as join5, win32 } from "path";
|
|
@@ -4946,7 +5778,7 @@ async function findOrphanedWorkspaces(dir) {
|
|
|
4946
5778
|
return results;
|
|
4947
5779
|
}
|
|
4948
5780
|
function createWorkspaceConfigRoutes(sessionManager) {
|
|
4949
|
-
const router =
|
|
5781
|
+
const router = Router6();
|
|
4950
5782
|
router.get("/config", async (req, res) => {
|
|
4951
5783
|
const workdir = req.query["workdir"];
|
|
4952
5784
|
if (!workdir) return res.status(400).json({ error: "workdir required" });
|
|
@@ -5080,9 +5912,9 @@ function createWorkspaceConfigRoutes(sessionManager) {
|
|
|
5080
5912
|
}
|
|
5081
5913
|
|
|
5082
5914
|
// src/server/routes/terminals.ts
|
|
5083
|
-
import { Router as
|
|
5915
|
+
import { Router as Router7 } from "express";
|
|
5084
5916
|
function createTerminalRoutes() {
|
|
5085
|
-
const router =
|
|
5917
|
+
const router = Router7();
|
|
5086
5918
|
router.get("/", (req, res) => {
|
|
5087
5919
|
const projectId = req.query["projectId"];
|
|
5088
5920
|
const sessions = projectId ? terminalManager.getByProject(projectId) : [];
|
|
@@ -5121,11 +5953,11 @@ function createTerminalRoutes() {
|
|
|
5121
5953
|
}
|
|
5122
5954
|
|
|
5123
5955
|
// src/server/routes/directories.ts
|
|
5124
|
-
import { Router as
|
|
5956
|
+
import { Router as Router8 } from "express";
|
|
5125
5957
|
import { readdir as readdir2 } from "fs/promises";
|
|
5126
5958
|
import { resolve as resolve5, join as join6, dirname as dirname4, basename as basename2 } from "path";
|
|
5127
5959
|
function createDirectoryRoutes() {
|
|
5128
|
-
const router =
|
|
5960
|
+
const router = Router8();
|
|
5129
5961
|
const DEFAULT_BASE_PATH = process.cwd();
|
|
5130
5962
|
router.get("/", async (req, res) => {
|
|
5131
5963
|
const path = req.query["path"] || DEFAULT_BASE_PATH;
|
|
@@ -5160,7 +5992,7 @@ function createDirectoryRoutes() {
|
|
|
5160
5992
|
}
|
|
5161
5993
|
|
|
5162
5994
|
// src/server/routes/file-search.ts
|
|
5163
|
-
import { Router as
|
|
5995
|
+
import { Router as Router9 } from "express";
|
|
5164
5996
|
import fg from "fast-glob";
|
|
5165
5997
|
var IGNORED_PATTERNS = [
|
|
5166
5998
|
"**/node_modules/**",
|
|
@@ -5173,7 +6005,7 @@ var IGNORED_PATTERNS = [
|
|
|
5173
6005
|
"**/.openfox/**"
|
|
5174
6006
|
];
|
|
5175
6007
|
function createFileSearchRoutes() {
|
|
5176
|
-
const router =
|
|
6008
|
+
const router = Router9();
|
|
5177
6009
|
router.get("/", async (req, res) => {
|
|
5178
6010
|
const query = req.query["q"] || "";
|
|
5179
6011
|
const workdir = req.query["workdir"] || process.cwd();
|
|
@@ -5265,7 +6097,7 @@ function fuzzyScore(filePath, query) {
|
|
|
5265
6097
|
}
|
|
5266
6098
|
|
|
5267
6099
|
// src/server/routes/auto-update.ts
|
|
5268
|
-
import { Router as
|
|
6100
|
+
import { Router as Router10 } from "express";
|
|
5269
6101
|
import { spawn as spawn2 } from "child_process";
|
|
5270
6102
|
var updateInProgress = false;
|
|
5271
6103
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -5362,7 +6194,7 @@ async function getLatestDevelopVersion() {
|
|
|
5362
6194
|
});
|
|
5363
6195
|
}
|
|
5364
6196
|
function createAutoUpdateRoutes(options = {}) {
|
|
5365
|
-
const router =
|
|
6197
|
+
const router = Router10();
|
|
5366
6198
|
router.get("/check", async (req, res) => {
|
|
5367
6199
|
const isService = isRunningAsService();
|
|
5368
6200
|
const isDev = isDevMode();
|
|
@@ -5479,9 +6311,9 @@ function createAutoUpdateRoutes(options = {}) {
|
|
|
5479
6311
|
}
|
|
5480
6312
|
|
|
5481
6313
|
// src/server/routes/provider-auth.ts
|
|
5482
|
-
import { Router as
|
|
6314
|
+
import { Router as Router11 } from "express";
|
|
5483
6315
|
function createProviderAuthRoutes(config3, providerManager, registry) {
|
|
5484
|
-
const router =
|
|
6316
|
+
const router = Router11();
|
|
5485
6317
|
function resolveAuthContext(providerId, res) {
|
|
5486
6318
|
const provider = providerManager.getProviders().find((item) => item.id === providerId);
|
|
5487
6319
|
if (!provider) {
|
|
@@ -5710,7 +6542,7 @@ async function loadProviderPlugins(options) {
|
|
|
5710
6542
|
}
|
|
5711
6543
|
|
|
5712
6544
|
// src/server/routes/plugins.ts
|
|
5713
|
-
import { Router as
|
|
6545
|
+
import { Router as Router12 } from "express";
|
|
5714
6546
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL2 } from "url";
|
|
5715
6547
|
import { dirname as dirname6, resolve as resolve7, join as join8 } from "path";
|
|
5716
6548
|
import { existsSync } from "fs";
|
|
@@ -5727,7 +6559,7 @@ async function openFolderRoute(dir, res) {
|
|
|
5727
6559
|
}
|
|
5728
6560
|
}
|
|
5729
6561
|
function createPluginRoutes(options) {
|
|
5730
|
-
const router =
|
|
6562
|
+
const router = Router12();
|
|
5731
6563
|
const { config: config3, providerAdapters, pluginDiagnostics, logger: logger2 } = options;
|
|
5732
6564
|
let registryCache = null;
|
|
5733
6565
|
router.get("/registry", async (_req, res) => {
|
|
@@ -5968,6 +6800,10 @@ function detectWsl() {
|
|
|
5968
6800
|
|
|
5969
6801
|
// src/server/index.ts
|
|
5970
6802
|
var __dirname3 = dirname7(fileURLToPath6(import.meta.url));
|
|
6803
|
+
async function announceContextDrift(sessionManager, sessionIds) {
|
|
6804
|
+
const { injectContextDriftRemindersForSessions } = await import("./dynamic-context-Y555VDLA.js");
|
|
6805
|
+
await injectContextDriftRemindersForSessions(sessionManager, sessionIds);
|
|
6806
|
+
}
|
|
5971
6807
|
async function createServerHandle(config3) {
|
|
5972
6808
|
setRuntimeConfig(config3);
|
|
5973
6809
|
setLogLevel(config3.logging?.level ?? void 0, config3.mode);
|
|
@@ -6072,7 +6908,7 @@ async function createServerHandle(config3) {
|
|
|
6072
6908
|
toolRegistry = createToolRegistry();
|
|
6073
6909
|
logger.info("MCP tools registered", { count: mcpTools.length });
|
|
6074
6910
|
}
|
|
6075
|
-
const { signalMcpReady } = await import("./server-
|
|
6911
|
+
const { signalMcpReady } = await import("./server-5TY22HDN.js");
|
|
6076
6912
|
signalMcpReady();
|
|
6077
6913
|
});
|
|
6078
6914
|
const app = express();
|
|
@@ -6095,6 +6931,25 @@ async function createServerHandle(config3) {
|
|
|
6095
6931
|
};
|
|
6096
6932
|
app.use("/api", authMiddleware);
|
|
6097
6933
|
app.use(express.json({ limit: "75mb" }));
|
|
6934
|
+
let mcpToolDeps = null;
|
|
6935
|
+
const mcpAuth = {
|
|
6936
|
+
isAuthRequired: () => {
|
|
6937
|
+
const cfg = getAuthConfig();
|
|
6938
|
+
return cfg?.strategy === "network" && cfg.encryptedPassword != null;
|
|
6939
|
+
},
|
|
6940
|
+
isAuthorized: async (req) => {
|
|
6941
|
+
const token = extractSessionToken(req);
|
|
6942
|
+
return token ? isValidToken(token) : false;
|
|
6943
|
+
}
|
|
6944
|
+
};
|
|
6945
|
+
app.use(
|
|
6946
|
+
"/mcp",
|
|
6947
|
+
createOpenFoxMcpRouter({
|
|
6948
|
+
resolveDeps: () => mcpToolDeps,
|
|
6949
|
+
isAuthRequired: mcpAuth.isAuthRequired,
|
|
6950
|
+
isAuthorized: mcpAuth.isAuthorized
|
|
6951
|
+
})
|
|
6952
|
+
);
|
|
6098
6953
|
app.get("/api/health", (_req, res) => {
|
|
6099
6954
|
res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6100
6955
|
});
|
|
@@ -6155,7 +7010,7 @@ async function createServerHandle(config3) {
|
|
|
6155
7010
|
res.json({ tools });
|
|
6156
7011
|
});
|
|
6157
7012
|
app.get("/api/projects", async (_req, res) => {
|
|
6158
|
-
const { listProjects: listProjects2 } = await import("./projects-
|
|
7013
|
+
const { listProjects: listProjects2 } = await import("./projects-5U37RUTL.js");
|
|
6159
7014
|
const projects = listProjects2();
|
|
6160
7015
|
res.json({ projects });
|
|
6161
7016
|
});
|
|
@@ -6164,7 +7019,7 @@ async function createServerHandle(config3) {
|
|
|
6164
7019
|
if (!name || !workdir) {
|
|
6165
7020
|
return res.status(400).json({ error: "name and workdir are required" });
|
|
6166
7021
|
}
|
|
6167
|
-
const { createDirectoryWithGit } = await import("./project-creator-
|
|
7022
|
+
const { createDirectoryWithGit } = await import("./project-creator-6AGQTNXB.js");
|
|
6168
7023
|
try {
|
|
6169
7024
|
const project = await createDirectoryWithGit(name, workdir);
|
|
6170
7025
|
res.status(201).json({ project });
|
|
@@ -6209,7 +7064,7 @@ async function createServerHandle(config3) {
|
|
|
6209
7064
|
}
|
|
6210
7065
|
});
|
|
6211
7066
|
app.get("/api/projects/:id", async (req, res) => {
|
|
6212
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
7067
|
+
const { getProject: getProject2 } = await import("./projects-5U37RUTL.js");
|
|
6213
7068
|
const project = getProject2(req.params.id);
|
|
6214
7069
|
if (!project) {
|
|
6215
7070
|
return res.status(404).json({ error: "Project not found" });
|
|
@@ -6217,7 +7072,7 @@ async function createServerHandle(config3) {
|
|
|
6217
7072
|
res.json({ project });
|
|
6218
7073
|
});
|
|
6219
7074
|
app.get("/api/projects/:id/open-folder", async (req, res) => {
|
|
6220
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
7075
|
+
const { getProject: getProject2 } = await import("./projects-5U37RUTL.js");
|
|
6221
7076
|
const project = getProject2(req.params.id);
|
|
6222
7077
|
if (!project) {
|
|
6223
7078
|
return res.status(404).json({ error: "Project not found" });
|
|
@@ -6231,7 +7086,7 @@ async function createServerHandle(config3) {
|
|
|
6231
7086
|
}
|
|
6232
7087
|
});
|
|
6233
7088
|
app.put("/api/projects/:id", async (req, res) => {
|
|
6234
|
-
const { updateProject: updateProject2 } = await import("./projects-
|
|
7089
|
+
const { updateProject: updateProject2 } = await import("./projects-5U37RUTL.js");
|
|
6235
7090
|
const { name, customInstructions, dangerLevel, defaultAgent } = req.body;
|
|
6236
7091
|
const updates = {};
|
|
6237
7092
|
if (name !== void 0) updates.name = name;
|
|
@@ -6245,16 +7100,16 @@ async function createServerHandle(config3) {
|
|
|
6245
7100
|
res.json({ project: updated });
|
|
6246
7101
|
});
|
|
6247
7102
|
app.delete("/api/projects/:id", async (req, res) => {
|
|
6248
|
-
const { getProject: getProject2, deleteProject } = await import("./projects-
|
|
7103
|
+
const { getProject: getProject2, deleteProject: deleteProject2 } = await import("./projects-5U37RUTL.js");
|
|
6249
7104
|
const project = getProject2(req.params.id);
|
|
6250
7105
|
if (!project) {
|
|
6251
7106
|
return res.status(404).json({ error: "Project not found" });
|
|
6252
7107
|
}
|
|
6253
|
-
|
|
7108
|
+
deleteProject2(req.params.id);
|
|
6254
7109
|
res.json({ success: true });
|
|
6255
7110
|
});
|
|
6256
7111
|
app.put("/api/projects/:id/star", async (req, res) => {
|
|
6257
|
-
const { toggleStar } = await import("./projects-
|
|
7112
|
+
const { toggleStar } = await import("./projects-5U37RUTL.js");
|
|
6258
7113
|
const { isStarred } = req.body;
|
|
6259
7114
|
if (typeof isStarred !== "boolean") {
|
|
6260
7115
|
return res.status(400).json({ error: "isStarred is required and must be a boolean" });
|
|
@@ -6268,9 +7123,9 @@ async function createServerHandle(config3) {
|
|
|
6268
7123
|
const sessionFavoriteRouter = express.Router();
|
|
6269
7124
|
registerSessionFavoriteRoute(sessionFavoriteRouter, sessionManager);
|
|
6270
7125
|
app.use("/api", sessionFavoriteRouter);
|
|
6271
|
-
const { createTasksService } = await import("./service-
|
|
6272
|
-
const { registerTaskRoutes } = await import("./tasks-
|
|
6273
|
-
const { setTasksService } = await import("./tools-
|
|
7126
|
+
const { createTasksService } = await import("./service-VPXGREXF.js");
|
|
7127
|
+
const { registerTaskRoutes } = await import("./tasks-MNPXK2S5.js");
|
|
7128
|
+
const { setTasksService } = await import("./tools-6BDT5YMT.js");
|
|
6274
7129
|
const tasksService = createTasksService({
|
|
6275
7130
|
sessionManager,
|
|
6276
7131
|
config: config3,
|
|
@@ -6283,26 +7138,26 @@ async function createServerHandle(config3) {
|
|
|
6283
7138
|
registerTaskRoutes(tasksRouter, tasksService);
|
|
6284
7139
|
app.use("/api", tasksRouter);
|
|
6285
7140
|
app.get("/api/projects/:id/branches", async (req, res) => {
|
|
6286
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
7141
|
+
const { getProject: getProject2 } = await import("./projects-5U37RUTL.js");
|
|
6287
7142
|
const project = getProject2(req.params.id);
|
|
6288
7143
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
6289
|
-
const { listBranches } = await import("./workspace-
|
|
7144
|
+
const { listBranches } = await import("./workspace-LM646OHC.js");
|
|
6290
7145
|
const branches = await listBranches(project.workdir);
|
|
6291
7146
|
res.json({ branches });
|
|
6292
7147
|
});
|
|
6293
7148
|
app.post("/api/projects/:id/checkout", async (req, res) => {
|
|
6294
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
7149
|
+
const { getProject: getProject2 } = await import("./projects-5U37RUTL.js");
|
|
6295
7150
|
const project = getProject2(req.params.id);
|
|
6296
7151
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
6297
7152
|
const { branch } = req.body;
|
|
6298
7153
|
if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
|
|
6299
|
-
const { checkoutBranch, isGitRepository } = await import("./workspace-
|
|
7154
|
+
const { checkoutBranch, isGitRepository } = await import("./workspace-LM646OHC.js");
|
|
6300
7155
|
if (!await isGitRepository(project.workdir)) {
|
|
6301
7156
|
return res.status(400).json({ error: "Project is not a git repository" });
|
|
6302
7157
|
}
|
|
6303
7158
|
try {
|
|
6304
7159
|
await checkoutBranch(project.workdir, branch);
|
|
6305
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7160
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-NMPX5ICI.js");
|
|
6306
7161
|
const allSessions = sessionManager.listSessions();
|
|
6307
7162
|
for (const s of allSessions) {
|
|
6308
7163
|
if (sessionManager.getEffectiveWorkdir(s.id) === project.workdir) {
|
|
@@ -6316,12 +7171,12 @@ async function createServerHandle(config3) {
|
|
|
6316
7171
|
}
|
|
6317
7172
|
});
|
|
6318
7173
|
app.post("/api/projects/:id/checkout-new", async (req, res) => {
|
|
6319
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
7174
|
+
const { getProject: getProject2 } = await import("./projects-5U37RUTL.js");
|
|
6320
7175
|
const project = getProject2(req.params.id);
|
|
6321
7176
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
6322
7177
|
const { name, sourceBranch } = req.body;
|
|
6323
7178
|
if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
|
|
6324
|
-
const { createBranch, resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, validateRef: validateRef2, getDefaultBranch, isGitRepository } = await import("./workspace-
|
|
7179
|
+
const { createBranch, resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, validateRef: validateRef2, getDefaultBranch, isGitRepository } = await import("./workspace-LM646OHC.js");
|
|
6325
7180
|
if (!await isGitRepository(project.workdir)) {
|
|
6326
7181
|
return res.status(400).json({ error: "Project is not a git repository" });
|
|
6327
7182
|
}
|
|
@@ -6338,7 +7193,7 @@ async function createServerHandle(config3) {
|
|
|
6338
7193
|
);
|
|
6339
7194
|
}
|
|
6340
7195
|
await createBranch(project.workdir, name, sb);
|
|
6341
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7196
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-NMPX5ICI.js");
|
|
6342
7197
|
const allSessions = sessionManager.listSessions();
|
|
6343
7198
|
for (const s of allSessions) {
|
|
6344
7199
|
if (sessionManager.getEffectiveWorkdir(s.id) === project.workdir) {
|
|
@@ -6355,7 +7210,7 @@ async function createServerHandle(config3) {
|
|
|
6355
7210
|
const session = sessionManager.getSession(req.params.id);
|
|
6356
7211
|
if (!session) return res.status(404).json({ error: "Session not found" });
|
|
6357
7212
|
const effectiveWorkdir = sessionManager.getEffectiveWorkdir(req.params.id);
|
|
6358
|
-
const { listBranches, getDefaultBranch } = await import("./workspace-
|
|
7213
|
+
const { listBranches, getDefaultBranch } = await import("./workspace-LM646OHC.js");
|
|
6359
7214
|
const [branches, defaultBranch] = await Promise.all([
|
|
6360
7215
|
listBranches(effectiveWorkdir),
|
|
6361
7216
|
getDefaultBranch(session.workdir)
|
|
@@ -6369,8 +7224,8 @@ async function createServerHandle(config3) {
|
|
|
6369
7224
|
const { branch } = req.body;
|
|
6370
7225
|
if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
|
|
6371
7226
|
const effectiveWorkdir = sessionManager.getEffectiveWorkdir(req.params.id);
|
|
6372
|
-
const { checkoutBranch, validateRef: validateRef2 } = await import("./workspace-
|
|
6373
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7227
|
+
const { checkoutBranch, validateRef: validateRef2 } = await import("./workspace-LM646OHC.js");
|
|
7228
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-NMPX5ICI.js");
|
|
6374
7229
|
try {
|
|
6375
7230
|
await validateRef2(effectiveWorkdir, branch);
|
|
6376
7231
|
await checkoutBranch(effectiveWorkdir, branch);
|
|
@@ -6412,16 +7267,16 @@ All file and git operations should use this branch.
|
|
|
6412
7267
|
const { name, sourceBranch } = req.body;
|
|
6413
7268
|
if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
|
|
6414
7269
|
const effectiveWorkdir = sessionManager.getEffectiveWorkdir(req.params.id);
|
|
6415
|
-
const { validateRef: validateRef2 } = await import("./workspace-
|
|
6416
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7270
|
+
const { validateRef: validateRef2 } = await import("./workspace-LM646OHC.js");
|
|
7271
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-NMPX5ICI.js");
|
|
6417
7272
|
try {
|
|
6418
7273
|
await validateRef2(effectiveWorkdir, name);
|
|
6419
7274
|
if (sourceBranch) {
|
|
6420
|
-
const { resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, createBranch } = await import("./workspace-
|
|
7275
|
+
const { resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, createBranch } = await import("./workspace-LM646OHC.js");
|
|
6421
7276
|
const sb = await resolveAndValidateSourceBranch2(effectiveWorkdir, sourceBranch, session.workdir);
|
|
6422
7277
|
await createBranch(effectiveWorkdir, name, sb);
|
|
6423
7278
|
} else {
|
|
6424
|
-
const { createBranch, getDefaultBranch, resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2 } = await import("./workspace-
|
|
7279
|
+
const { createBranch, getDefaultBranch, resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2 } = await import("./workspace-LM646OHC.js");
|
|
6425
7280
|
const defaultBranch = await getDefaultBranch(session.workdir);
|
|
6426
7281
|
const sb = await resolveAndValidateSourceBranch2(effectiveWorkdir, defaultBranch, session.workdir);
|
|
6427
7282
|
await createBranch(effectiveWorkdir, name, sb);
|
|
@@ -6459,17 +7314,17 @@ All file and git operations should use this branch.
|
|
|
6459
7314
|
}
|
|
6460
7315
|
});
|
|
6461
7316
|
app.get("/api/projects/:id/workspaces", async (req, res) => {
|
|
6462
|
-
const { getProject: getProject2 } = await import("./projects-
|
|
7317
|
+
const { getProject: getProject2 } = await import("./projects-5U37RUTL.js");
|
|
6463
7318
|
const project = getProject2(req.params.id);
|
|
6464
7319
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
6465
|
-
const { listWorkspaces } = await import("./workspace-
|
|
7320
|
+
const { listWorkspaces } = await import("./workspace-LM646OHC.js");
|
|
6466
7321
|
const all = await listWorkspaces(project.name, project.workdir);
|
|
6467
7322
|
const workspacesList = all.filter((ws) => ws.path !== project.workdir);
|
|
6468
7323
|
res.json({ workspaces: workspacesList });
|
|
6469
7324
|
});
|
|
6470
7325
|
app.get("/api/sessions", async (req, res) => {
|
|
6471
|
-
const { getRecentUserPromptsForSession } = await import("./events-
|
|
6472
|
-
const { getPendingConfirmationsBySession } = await import("./path-security-
|
|
7326
|
+
const { getRecentUserPromptsForSession } = await import("./events-ZJPRGVL2.js");
|
|
7327
|
+
const { getPendingConfirmationsBySession: getPendingConfirmationsBySession2 } = await import("./path-security-CBACXAMS.js");
|
|
6473
7328
|
const projectId = req.query["projectId"];
|
|
6474
7329
|
const rawLimit = req.query["limit"];
|
|
6475
7330
|
const offset = parseInt(req.query["offset"]) || 0;
|
|
@@ -6492,7 +7347,7 @@ All file and git operations should use this branch.
|
|
|
6492
7347
|
...session,
|
|
6493
7348
|
recentUserPrompts: getRecentUserPromptsForSession(session.id, 10)
|
|
6494
7349
|
}));
|
|
6495
|
-
const allPending =
|
|
7350
|
+
const allPending = getPendingConfirmationsBySession2();
|
|
6496
7351
|
const pendingConfirmationsBySession = {};
|
|
6497
7352
|
for (const s of sessions) {
|
|
6498
7353
|
const confs = allPending[s.id];
|
|
@@ -6524,7 +7379,7 @@ All file and git operations should use this branch.
|
|
|
6524
7379
|
disabledServers = mcpManager.getAllServers().filter((s) => s.config.disabled).map((s) => s.name);
|
|
6525
7380
|
}
|
|
6526
7381
|
if (disabledServers.length > 0) {
|
|
6527
|
-
const { setSessionDisabledServers: setSessionDisabledServers2 } = await import("./session-overrides-
|
|
7382
|
+
const { setSessionDisabledServers: setSessionDisabledServers2 } = await import("./session-overrides-OECQYRDK.js");
|
|
6528
7383
|
setSessionDisabledServers2(session.id, disabledServers);
|
|
6529
7384
|
}
|
|
6530
7385
|
} catch {
|
|
@@ -6561,7 +7416,7 @@ All file and git operations should use this branch.
|
|
|
6561
7416
|
if (!target || typeof target !== "string") return res.status(400).json({ error: "target is required" });
|
|
6562
7417
|
const project = sessionManager.getProject(session.projectId);
|
|
6563
7418
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
6564
|
-
const { isGitRepository } = await import("./workspace-
|
|
7419
|
+
const { isGitRepository } = await import("./workspace-LM646OHC.js");
|
|
6565
7420
|
if (!await isGitRepository(project.workdir)) {
|
|
6566
7421
|
return res.status(400).json({ error: "Project is not a git repository" });
|
|
6567
7422
|
}
|
|
@@ -6579,7 +7434,7 @@ All file and git operations should use this branch.
|
|
|
6579
7434
|
if (!target || typeof target !== "string") return res.status(400).json({ error: "target is required" });
|
|
6580
7435
|
const project = sessionManager.getProject(session.projectId);
|
|
6581
7436
|
if (!project) return res.status(404).json({ error: "Project not found" });
|
|
6582
|
-
const { isGitRepository } = await import("./workspace-
|
|
7437
|
+
const { isGitRepository } = await import("./workspace-LM646OHC.js");
|
|
6583
7438
|
if (!await isGitRepository(project.workdir)) {
|
|
6584
7439
|
return res.status(400).json({ error: "Project is not a git repository" });
|
|
6585
7440
|
}
|
|
@@ -6597,22 +7452,22 @@ All file and git operations should use this branch.
|
|
|
6597
7452
|
}
|
|
6598
7453
|
});
|
|
6599
7454
|
app.get("/api/sessions/:id", async (req, res) => {
|
|
6600
|
-
const { getEventStore: getEventStore2, combineEventsWithSnapshot } = await import("./events-
|
|
6601
|
-
const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-
|
|
6602
|
-
const { getPendingQuestionsForSession } = await import("./tools-
|
|
6603
|
-
const { getMaxVisibleItems } = await import("./settings-
|
|
7455
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEventsWithSnapshot2 } = await import("./events-ZJPRGVL2.js");
|
|
7456
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2, foldPendingConfirmations } = await import("./folding-QRZOHY2I.js");
|
|
7457
|
+
const { getPendingQuestionsForSession: getPendingQuestionsForSession2 } = await import("./tools-6BDT5YMT.js");
|
|
7458
|
+
const { getMaxVisibleItems } = await import("./settings-MDCRNQBY.js");
|
|
6604
7459
|
const session = sessionManager.getSession(req.params.id);
|
|
6605
7460
|
if (!session) {
|
|
6606
7461
|
return res.status(404).json({ error: "Session not found" });
|
|
6607
7462
|
}
|
|
6608
7463
|
const eventStore = getEventStore2();
|
|
6609
7464
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(req.params.id);
|
|
6610
|
-
const events =
|
|
7465
|
+
const events = combineEventsWithSnapshot2(req.params.id, snapshot, eventsSinceSnapshot);
|
|
6611
7466
|
const maxVisibleItems = req.query["full"] === "true" ? void 0 : getMaxVisibleItems() || void 0;
|
|
6612
|
-
const { messages, hiddenCount } =
|
|
7467
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems);
|
|
6613
7468
|
const contextState = sessionManager.getContextState(req.params.id);
|
|
6614
7469
|
const queueState = sessionManager.getQueueState(req.params.id);
|
|
6615
|
-
const pendingQuestions =
|
|
7470
|
+
const pendingQuestions = getPendingQuestionsForSession2(req.params.id);
|
|
6616
7471
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
6617
7472
|
const activeWorkflowExecution = sessionManager.getDisplayWorkflowExecution(req.params.id);
|
|
6618
7473
|
res.json({
|
|
@@ -6627,10 +7482,10 @@ All file and git operations should use this branch.
|
|
|
6627
7482
|
});
|
|
6628
7483
|
});
|
|
6629
7484
|
app.get("/api/sessions/:id/status", async (req, res) => {
|
|
6630
|
-
const { projectSessionStatus } = await import("./session-status-
|
|
6631
|
-
const { getPendingQuestionsForSession } = await import("./tools-
|
|
6632
|
-
const { getEventStore: getEventStore2, combineEventsWithSnapshot } = await import("./events-
|
|
6633
|
-
const { foldPendingConfirmations } = await import("./folding-
|
|
7485
|
+
const { projectSessionStatus: projectSessionStatus2 } = await import("./session-status-UFO5IFI7.js");
|
|
7486
|
+
const { getPendingQuestionsForSession: getPendingQuestionsForSession2 } = await import("./tools-6BDT5YMT.js");
|
|
7487
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEventsWithSnapshot2 } = await import("./events-ZJPRGVL2.js");
|
|
7488
|
+
const { foldPendingConfirmations } = await import("./folding-QRZOHY2I.js");
|
|
6634
7489
|
const sessionId = req.params["id"];
|
|
6635
7490
|
if (!sessionId) {
|
|
6636
7491
|
return res.status(400).json({ error: "Session id is required" });
|
|
@@ -6641,12 +7496,12 @@ All file and git operations should use this branch.
|
|
|
6641
7496
|
}
|
|
6642
7497
|
const activeWorkflowExecution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
6643
7498
|
const activeWorkflowStepName = activeWorkflowExecution?.currentStepName ?? null;
|
|
6644
|
-
const pendingQuestions =
|
|
7499
|
+
const pendingQuestions = getPendingQuestionsForSession2(sessionId);
|
|
6645
7500
|
const eventStore = getEventStore2();
|
|
6646
7501
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6647
|
-
const events =
|
|
7502
|
+
const events = combineEventsWithSnapshot2(sessionId, snapshot, eventsSinceSnapshot);
|
|
6648
7503
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
6649
|
-
const status =
|
|
7504
|
+
const status = projectSessionStatus2({
|
|
6650
7505
|
session,
|
|
6651
7506
|
pendingQuestionsCount: pendingQuestions.length,
|
|
6652
7507
|
pendingConfirmationsCount: pendingConfirmations.length,
|
|
@@ -6660,8 +7515,8 @@ All file and git operations should use this branch.
|
|
|
6660
7515
|
if (!session) {
|
|
6661
7516
|
return res.status(404).json({ error: "Session not found" });
|
|
6662
7517
|
}
|
|
6663
|
-
const { stopSessionExecution } = await import("./chat-handler-
|
|
6664
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
7518
|
+
const { stopSessionExecution } = await import("./chat-handler-RAIMQU7F.js");
|
|
7519
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-6BDT5YMT.js");
|
|
6665
7520
|
sessionManager.clearMessageQueue(sessionId);
|
|
6666
7521
|
stopSessionExecution(sessionId, sessionManager);
|
|
6667
7522
|
abortSession(sessionId);
|
|
@@ -6690,9 +7545,9 @@ All file and git operations should use this branch.
|
|
|
6690
7545
|
res.json({ success: true });
|
|
6691
7546
|
});
|
|
6692
7547
|
app.post("/api/sessions/:id/provider", async (req, res) => {
|
|
6693
|
-
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-
|
|
6694
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
6695
|
-
const { getMaxVisibleItems } = await import("./settings-
|
|
7548
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-ZJPRGVL2.js");
|
|
7549
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QRZOHY2I.js");
|
|
7550
|
+
const { getMaxVisibleItems } = await import("./settings-MDCRNQBY.js");
|
|
6696
7551
|
const sessionId = req.params.id;
|
|
6697
7552
|
const session = sessionManager.getSession(sessionId);
|
|
6698
7553
|
if (!session) {
|
|
@@ -6709,19 +7564,20 @@ All file and git operations should use this branch.
|
|
|
6709
7564
|
const resolvedModel = model ?? provider?.models?.[0]?.id ?? "auto";
|
|
6710
7565
|
sessionManager.setSessionProvider(sessionId, providerId, resolvedModel, true, reasoningEffort);
|
|
6711
7566
|
sessionManager.setSessionProviderActive(sessionId, true);
|
|
7567
|
+
await announceContextDrift(sessionManager, [sessionId]);
|
|
6712
7568
|
const contextState = sessionManager.getContextState(sessionId);
|
|
6713
7569
|
const eventStore = getEventStore2();
|
|
6714
7570
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6715
7571
|
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
6716
7572
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6717
|
-
const { messages, hiddenCount } =
|
|
7573
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6718
7574
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
6719
7575
|
res.json({ session: toClientSession(updatedSession), messages, hiddenCount, contextState });
|
|
6720
7576
|
});
|
|
6721
7577
|
app.delete("/api/sessions/:id/provider", async (req, res) => {
|
|
6722
|
-
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-
|
|
6723
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
6724
|
-
const { getMaxVisibleItems } = await import("./settings-
|
|
7578
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-ZJPRGVL2.js");
|
|
7579
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QRZOHY2I.js");
|
|
7580
|
+
const { getMaxVisibleItems } = await import("./settings-MDCRNQBY.js");
|
|
6725
7581
|
const sessionId = req.params.id;
|
|
6726
7582
|
const session = sessionManager.getSession(sessionId);
|
|
6727
7583
|
if (!session) {
|
|
@@ -6729,11 +7585,12 @@ All file and git operations should use this branch.
|
|
|
6729
7585
|
}
|
|
6730
7586
|
sessionManager.setSessionProvider(sessionId, null, null, false, null);
|
|
6731
7587
|
sessionManager.setSessionProviderActive(sessionId, true);
|
|
7588
|
+
await announceContextDrift(sessionManager, [sessionId]);
|
|
6732
7589
|
const eventStore = getEventStore2();
|
|
6733
7590
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6734
7591
|
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
6735
7592
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6736
|
-
const { messages, hiddenCount } =
|
|
7593
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6737
7594
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
6738
7595
|
res.json({ session: toClientSession(updatedSession), messages, hiddenCount });
|
|
6739
7596
|
});
|
|
@@ -6850,9 +7707,9 @@ All file and git operations should use this branch.
|
|
|
6850
7707
|
res.json({ success: true });
|
|
6851
7708
|
});
|
|
6852
7709
|
app.put("/api/sessions/:id/mode", async (req, res) => {
|
|
6853
|
-
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-
|
|
6854
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
6855
|
-
const { getMaxVisibleItems } = await import("./settings-
|
|
7710
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-ZJPRGVL2.js");
|
|
7711
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QRZOHY2I.js");
|
|
7712
|
+
const { getMaxVisibleItems } = await import("./settings-MDCRNQBY.js");
|
|
6856
7713
|
const sessionId = req.params.id;
|
|
6857
7714
|
const session = sessionManager.getSession(sessionId);
|
|
6858
7715
|
if (!session) {
|
|
@@ -6872,7 +7729,7 @@ All file and git operations should use this branch.
|
|
|
6872
7729
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6873
7730
|
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
6874
7731
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6875
|
-
const { messages, hiddenCount } =
|
|
7732
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6876
7733
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
6877
7734
|
res.json({ session: toClientSession(updatedSession), messages, hiddenCount });
|
|
6878
7735
|
});
|
|
@@ -6910,7 +7767,15 @@ All file and git operations should use this branch.
|
|
|
6910
7767
|
return res.status(400).json({ error: "disabledServers must be an array of strings" });
|
|
6911
7768
|
}
|
|
6912
7769
|
setSessionDisabledServers(sessionId, disabledServers);
|
|
6913
|
-
|
|
7770
|
+
const messages = session.messages ?? [];
|
|
7771
|
+
if (messages.length === 0) {
|
|
7772
|
+
const { applyDynamicContext } = await import("./dynamic-context-Y555VDLA.js");
|
|
7773
|
+
const modelName = session.providerModel ?? providerManager.getCurrentModel();
|
|
7774
|
+
await applyDynamicContext(sessionManager, sessionId, modelName);
|
|
7775
|
+
} else {
|
|
7776
|
+
sessionManager.setDynamicContextChanged(sessionId, true);
|
|
7777
|
+
}
|
|
7778
|
+
await announceContextDrift(sessionManager, [sessionId]);
|
|
6914
7779
|
const state = sessionManager.getContextState(sessionId);
|
|
6915
7780
|
wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
|
|
6916
7781
|
res.json({ disabledServers: getSessionDisabledServers(sessionId) });
|
|
@@ -6938,7 +7803,7 @@ All file and git operations should use this branch.
|
|
|
6938
7803
|
if (alwaysAllow !== void 0 && typeof alwaysAllow !== "boolean") {
|
|
6939
7804
|
return res.status(400).json({ error: "alwaysAllow must be a boolean if provided" });
|
|
6940
7805
|
}
|
|
6941
|
-
const { providePathConfirmation, getConfirmationSessionId } = await import("./tools-
|
|
7806
|
+
const { providePathConfirmation: providePathConfirmation2, getConfirmationSessionId } = await import("./tools-6BDT5YMT.js");
|
|
6942
7807
|
const pendingSessionId = getConfirmationSessionId(callId);
|
|
6943
7808
|
if (!pendingSessionId) {
|
|
6944
7809
|
return res.status(404).json({ error: "No pending path confirmation with that ID" });
|
|
@@ -6946,22 +7811,22 @@ All file and git operations should use this branch.
|
|
|
6946
7811
|
if (pendingSessionId !== sessionId) {
|
|
6947
7812
|
return res.status(403).json({ error: "Confirmation does not belong to this session" });
|
|
6948
7813
|
}
|
|
6949
|
-
const result =
|
|
7814
|
+
const result = providePathConfirmation2(callId, approved, alwaysAllow);
|
|
6950
7815
|
if (!result.found) {
|
|
6951
7816
|
return res.status(404).json({ error: "No pending path confirmation with that ID" });
|
|
6952
7817
|
}
|
|
6953
|
-
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEvents } = await import("./events-
|
|
6954
|
-
const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-
|
|
6955
|
-
const { createSessionStateMessage } = await import("./protocol-
|
|
6956
|
-
const { getPendingQuestionsForSession } = await import("./tools-
|
|
6957
|
-
const { getMaxVisibleItems } = await import("./settings-
|
|
7818
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEvents } = await import("./events-ZJPRGVL2.js");
|
|
7819
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2, foldPendingConfirmations } = await import("./folding-QRZOHY2I.js");
|
|
7820
|
+
const { createSessionStateMessage } = await import("./protocol-Y7MRKRUO.js");
|
|
7821
|
+
const { getPendingQuestionsForSession: getPendingQuestionsForSession2 } = await import("./tools-6BDT5YMT.js");
|
|
7822
|
+
const { getMaxVisibleItems } = await import("./settings-MDCRNQBY.js");
|
|
6958
7823
|
const eventStore = getEventStore2();
|
|
6959
7824
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6960
7825
|
const events = combineEvents(sessionId, snapshot, eventsSinceSnapshot);
|
|
6961
7826
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6962
|
-
const { messages, hiddenCount } =
|
|
7827
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6963
7828
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
6964
|
-
const pendingQuestions =
|
|
7829
|
+
const pendingQuestions = getPendingQuestionsForSession2(sessionId);
|
|
6965
7830
|
const session = sessionManager.getSession(sessionId);
|
|
6966
7831
|
if (session) {
|
|
6967
7832
|
const stateMsg = createSessionStateMessage(
|
|
@@ -6991,8 +7856,8 @@ All file and git operations should use this branch.
|
|
|
6991
7856
|
if (!skip && typeof answer !== "string") {
|
|
6992
7857
|
return res.status(400).json({ error: "answer is required when not skipping" });
|
|
6993
7858
|
}
|
|
6994
|
-
const { provideAnswer } = await import("./tools-
|
|
6995
|
-
const found =
|
|
7859
|
+
const { provideAnswer: provideAnswer2 } = await import("./tools-6BDT5YMT.js");
|
|
7860
|
+
const found = provideAnswer2(callId, answer ?? "", skip ?? false);
|
|
6996
7861
|
if (!found) {
|
|
6997
7862
|
return res.status(404).json({ error: "No pending question with that ID" });
|
|
6998
7863
|
}
|
|
@@ -7024,7 +7889,7 @@ All file and git operations should use this branch.
|
|
|
7024
7889
|
if (!session) {
|
|
7025
7890
|
return res.status(404).json({ error: "Session not found" });
|
|
7026
7891
|
}
|
|
7027
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
7892
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-MDCRNQBY.js");
|
|
7028
7893
|
if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
|
|
7029
7894
|
return res.json({ success: false, reason: "disabled" });
|
|
7030
7895
|
}
|
|
@@ -7057,7 +7922,7 @@ All file and git operations should use this branch.
|
|
|
7057
7922
|
backend: activeProvider?.backend ?? llmClient.getBackend(),
|
|
7058
7923
|
model: llmClient.getModel()
|
|
7059
7924
|
};
|
|
7060
|
-
const { runAgentTurn, TurnMetrics } = await import("./orchestrator-
|
|
7925
|
+
const { runAgentTurn, TurnMetrics } = await import("./orchestrator-SQN2SJ2A.js");
|
|
7061
7926
|
runAgentTurn(
|
|
7062
7927
|
{
|
|
7063
7928
|
sessionManager,
|
|
@@ -7095,15 +7960,15 @@ All file and git operations should use this branch.
|
|
|
7095
7960
|
if (!session) {
|
|
7096
7961
|
return res.status(404).json({ error: "Session not found" });
|
|
7097
7962
|
}
|
|
7098
|
-
const { stopSessionExecution } = await import("./chat-handler-
|
|
7099
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
7963
|
+
const { stopSessionExecution } = await import("./chat-handler-RAIMQU7F.js");
|
|
7964
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-6BDT5YMT.js");
|
|
7100
7965
|
const queuedMessages = sessionManager.getQueueState(sessionId);
|
|
7101
7966
|
sessionManager.clearMessageQueue(sessionId);
|
|
7102
7967
|
stopSessionExecution(sessionId, sessionManager);
|
|
7103
7968
|
abortSession(sessionId);
|
|
7104
7969
|
cancelQuestionsForSession(sessionId, "Session stopped by user");
|
|
7105
7970
|
cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
|
|
7106
|
-
const eventStore = (await import("./events-
|
|
7971
|
+
const eventStore = (await import("./events-ZJPRGVL2.js")).getEventStore();
|
|
7107
7972
|
eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
|
|
7108
7973
|
res.json({ success: true, queuedMessages });
|
|
7109
7974
|
});
|
|
@@ -7117,7 +7982,7 @@ All file and git operations should use this branch.
|
|
|
7117
7982
|
if (typeof messageIndex !== "number" || messageIndex < 0) {
|
|
7118
7983
|
return res.status(400).json({ error: "messageIndex must be a non-negative number" });
|
|
7119
7984
|
}
|
|
7120
|
-
const { truncateSessionMessages } = await import("./events-
|
|
7985
|
+
const { truncateSessionMessages } = await import("./events-ZJPRGVL2.js");
|
|
7121
7986
|
truncateSessionMessages(sessionId, messageIndex);
|
|
7122
7987
|
res.json({ success: true });
|
|
7123
7988
|
});
|
|
@@ -7137,11 +8002,11 @@ All file and git operations should use this branch.
|
|
|
7137
8002
|
if (attachments !== void 0 && !Array.isArray(attachments)) {
|
|
7138
8003
|
return res.status(400).json({ error: "attachments must be an array if provided" });
|
|
7139
8004
|
}
|
|
7140
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
7141
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
8005
|
+
const { getEventStore: getEventStore2 } = await import("./events-ZJPRGVL2.js");
|
|
8006
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QRZOHY2I.js");
|
|
7142
8007
|
const eventStore = getEventStore2();
|
|
7143
8008
|
const events = eventStore.getEvents(sessionId);
|
|
7144
|
-
const { messages } =
|
|
8009
|
+
const { messages } = buildMessagesFromStoredEvents2(events);
|
|
7145
8010
|
const msgIndex = messages.findIndex((m) => m.id === messageId);
|
|
7146
8011
|
if (msgIndex === -1) {
|
|
7147
8012
|
return res.status(400).json({ error: "Message not found" });
|
|
@@ -7150,7 +8015,7 @@ All file and git operations should use this branch.
|
|
|
7150
8015
|
if (msg.role !== "user" || msg.isSystemGenerated) {
|
|
7151
8016
|
return res.status(400).json({ error: "Can only replay user messages" });
|
|
7152
8017
|
}
|
|
7153
|
-
const { truncateSessionMessages } = await import("./events-
|
|
8018
|
+
const { truncateSessionMessages } = await import("./events-ZJPRGVL2.js");
|
|
7154
8019
|
truncateSessionMessages(sessionId, msgIndex - 1);
|
|
7155
8020
|
sessionManager.queueMessage(
|
|
7156
8021
|
sessionId,
|
|
@@ -7212,7 +8077,7 @@ All file and git operations should use this branch.
|
|
|
7212
8077
|
res.json({ accepted: true });
|
|
7213
8078
|
});
|
|
7214
8079
|
app.get("/api/settings", async (req, res) => {
|
|
7215
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
8080
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-MDCRNQBY.js");
|
|
7216
8081
|
const keysParam = req.query["keys"];
|
|
7217
8082
|
if (!keysParam) {
|
|
7218
8083
|
return res.status(400).json({ error: "keys query parameter is required" });
|
|
@@ -7225,19 +8090,19 @@ All file and git operations should use this branch.
|
|
|
7225
8090
|
res.json(result);
|
|
7226
8091
|
});
|
|
7227
8092
|
app.get("/api/settings/:key", async (req, res) => {
|
|
7228
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
8093
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-MDCRNQBY.js");
|
|
7229
8094
|
const key = req.params.key;
|
|
7230
8095
|
const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
|
|
7231
8096
|
res.json({ key, value });
|
|
7232
8097
|
});
|
|
7233
8098
|
app.get("/api/settings/:key", async (req, res) => {
|
|
7234
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
8099
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-MDCRNQBY.js");
|
|
7235
8100
|
const key = req.params.key;
|
|
7236
8101
|
const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
|
|
7237
8102
|
res.json({ key, value });
|
|
7238
8103
|
});
|
|
7239
8104
|
app.put("/api/settings/:key", async (req, res) => {
|
|
7240
|
-
const { setSetting: setSetting2 } = await import("./settings-
|
|
8105
|
+
const { setSetting: setSetting2 } = await import("./settings-MDCRNQBY.js");
|
|
7241
8106
|
const key = req.params.key;
|
|
7242
8107
|
const { value } = req.body;
|
|
7243
8108
|
if (value === void 0) {
|
|
@@ -7264,7 +8129,7 @@ All file and git operations should use this branch.
|
|
|
7264
8129
|
}
|
|
7265
8130
|
});
|
|
7266
8131
|
app.get("/api/tools/shells", async (_req, res) => {
|
|
7267
|
-
const { listAvailableShells } = await import("./platform-
|
|
8132
|
+
const { listAvailableShells } = await import("./platform-5LQXOZO3.js");
|
|
7268
8133
|
res.json({ shells: listAvailableShells() });
|
|
7269
8134
|
});
|
|
7270
8135
|
app.get("/api/config", async (_req, res) => {
|
|
@@ -7338,6 +8203,7 @@ All file and git operations should use this branch.
|
|
|
7338
8203
|
...m.requestBody !== void 0 && { requestBody: m.requestBody },
|
|
7339
8204
|
...m.reasoningEfforts !== void 0 && { reasoningEfforts: m.reasoningEfforts },
|
|
7340
8205
|
...m.reasoningEffortOverride !== void 0 && { reasoningEffortOverride: m.reasoningEffortOverride },
|
|
8206
|
+
...m.modes !== void 0 && { modes: m.modes },
|
|
7341
8207
|
...m.supportsVision !== void 0 && { supportsVision: m.supportsVision },
|
|
7342
8208
|
...m.thinkingEnabled !== void 0 && { thinkingEnabled: m.thinkingEnabled },
|
|
7343
8209
|
...m.thinkingLevel !== void 0 && { thinkingLevel: m.thinkingLevel },
|
|
@@ -7356,7 +8222,7 @@ All file and git operations should use this branch.
|
|
|
7356
8222
|
}));
|
|
7357
8223
|
}
|
|
7358
8224
|
app.post("/api/proxy/test", async (_req, res) => {
|
|
7359
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
8225
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-MDCRNQBY.js");
|
|
7360
8226
|
const proxyUrl = getSetting2(SETTINGS_KEYS.PROXY_URL);
|
|
7361
8227
|
if (!proxyUrl) {
|
|
7362
8228
|
return res.status(400).json({ success: false, error: "No proxy URL configured" });
|
|
@@ -7387,7 +8253,7 @@ All file and git operations should use this branch.
|
|
|
7387
8253
|
res.json({
|
|
7388
8254
|
success: true,
|
|
7389
8255
|
url,
|
|
7390
|
-
backend: reqBackend
|
|
8256
|
+
backend: reqBackend !== "unknown" && reqBackend ? reqBackend : detectBackendFromUrl(url) ?? "unknown",
|
|
7391
8257
|
model
|
|
7392
8258
|
});
|
|
7393
8259
|
} catch (error) {
|
|
@@ -7448,7 +8314,7 @@ All file and git operations should use this branch.
|
|
|
7448
8314
|
}
|
|
7449
8315
|
});
|
|
7450
8316
|
app.post("/api/proxy/test", async (_req, res) => {
|
|
7451
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
8317
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-MDCRNQBY.js");
|
|
7452
8318
|
const proxyUrl = getSetting2(SETTINGS_KEYS.PROXY_URL);
|
|
7453
8319
|
if (!proxyUrl) {
|
|
7454
8320
|
return res.status(400).json({ success: false, error: "No proxy URL configured" });
|
|
@@ -7475,9 +8341,9 @@ All file and git operations should use this branch.
|
|
|
7475
8341
|
const backend = req.query["backend"];
|
|
7476
8342
|
if (!url) return res.status(400).json({ error: "url is required" });
|
|
7477
8343
|
try {
|
|
7478
|
-
const { fetchModelsWithContext } = await import("./provider-manager-
|
|
7479
|
-
const { getModelProfile: getModelProfile2 } = await import("./profiles-
|
|
7480
|
-
const { getCatalogEntry } = await import("./model-catalog-
|
|
8344
|
+
const { fetchModelsWithContext } = await import("./provider-manager-URNJBK5Q.js");
|
|
8345
|
+
const { getModelProfile: getModelProfile2 } = await import("./profiles-EBCVFYG7.js");
|
|
8346
|
+
const { getCatalogEntry } = await import("./model-catalog-2PCKTYGW.js");
|
|
7481
8347
|
const models = await fetchModelsWithContext(
|
|
7482
8348
|
url,
|
|
7483
8349
|
apiKey,
|
|
@@ -7493,6 +8359,7 @@ All file and git operations should use this branch.
|
|
|
7493
8359
|
return {
|
|
7494
8360
|
id: m.id,
|
|
7495
8361
|
contextWindow: m.contextWindow,
|
|
8362
|
+
supportsVision: m.supportsVision ?? profile.supportsVision,
|
|
7496
8363
|
defaultTemperature: profile.temperature,
|
|
7497
8364
|
defaultTopP: profile.topP,
|
|
7498
8365
|
defaultTopK: profile.topK,
|
|
@@ -7514,7 +8381,7 @@ All file and git operations should use this branch.
|
|
|
7514
8381
|
if (!url) return res.status(400).json({ error: "url is required" });
|
|
7515
8382
|
if (!models?.length) return res.status(400).json({ error: "models is required" });
|
|
7516
8383
|
try {
|
|
7517
|
-
const { autoConfig } = await import("./auto-config-
|
|
8384
|
+
const { autoConfig } = await import("./auto-config-5VSNLVNI.js");
|
|
7518
8385
|
const result = await autoConfig({
|
|
7519
8386
|
url,
|
|
7520
8387
|
...apiKey ? { apiKey } : {},
|
|
@@ -7546,10 +8413,10 @@ All file and git operations should use this branch.
|
|
|
7546
8413
|
});
|
|
7547
8414
|
return res.json({ success: true, message: { content: response2.content }, raw: response2 });
|
|
7548
8415
|
}
|
|
7549
|
-
const { getModelProfile: getModelProfile2 } = await import("./profiles-
|
|
7550
|
-
const { getBackendCapabilities } = await import("./backend-
|
|
7551
|
-
const { buildNonStreamingCreateParams } = await import("./client-pure-
|
|
7552
|
-
const { OpenAIHttpClient } = await import("./http-client-
|
|
8416
|
+
const { getModelProfile: getModelProfile2 } = await import("./profiles-EBCVFYG7.js");
|
|
8417
|
+
const { getBackendCapabilities } = await import("./backend-NDBY3DAQ.js");
|
|
8418
|
+
const { buildNonStreamingCreateParams } = await import("./client-pure-FLKP6PTI.js");
|
|
8419
|
+
const { OpenAIHttpClient } = await import("./http-client-P4XTKAZA.js");
|
|
7553
8420
|
const { ensureVersionPrefix } = await import("./url-utils-C5BM7NF7.js");
|
|
7554
8421
|
const profile = getModelProfile2(model);
|
|
7555
8422
|
const capabilities = getBackendCapabilities(backend || "unknown");
|
|
@@ -7565,7 +8432,7 @@ All file and git operations should use this branch.
|
|
|
7565
8432
|
modelSettings["queryParams"] = JSON.parse(rawQP);
|
|
7566
8433
|
} else {
|
|
7567
8434
|
const modeEnabled = mode === "thinking" ? modelConfig?.thinkingEnabled : modelConfig?.nonThinkingEnabled;
|
|
7568
|
-
if (modeEnabled) {
|
|
8435
|
+
if (modeEnabled && capabilities.supportsChatTemplateKwargs) {
|
|
7569
8436
|
modelSettings["chatTemplateKwargs"] = mode === "thinking" ? { enable_thinking: true } : { enable_thinking: false };
|
|
7570
8437
|
}
|
|
7571
8438
|
}
|
|
@@ -7622,7 +8489,7 @@ All file and git operations should use this branch.
|
|
|
7622
8489
|
try {
|
|
7623
8490
|
const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-LOK3LXFH.js");
|
|
7624
8491
|
const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
|
|
7625
|
-
const providerBackend = backend;
|
|
8492
|
+
const providerBackend = backend === "unknown" ? detectBackendFromUrl(url) ?? "unknown" : backend;
|
|
7626
8493
|
const providerModels = modelConfigs?.length ? buildModelConfigs(modelConfigs) : model ? [{ id: model, contextWindow: 2e5, source: "user" }] : [];
|
|
7627
8494
|
const configWithProvider = addProvider(globalConfig, {
|
|
7628
8495
|
name,
|
|
@@ -7679,15 +8546,15 @@ All file and git operations should use this branch.
|
|
|
7679
8546
|
}
|
|
7680
8547
|
});
|
|
7681
8548
|
app.put("/api/config/vision-fallback", async (req, res) => {
|
|
7682
|
-
const { z } = await import("zod");
|
|
7683
|
-
const visionFallbackUpdateSchema =
|
|
7684
|
-
enabled:
|
|
7685
|
-
url:
|
|
7686
|
-
model:
|
|
7687
|
-
timeout:
|
|
7688
|
-
backend:
|
|
7689
|
-
providerModelRef:
|
|
7690
|
-
apiKey:
|
|
8549
|
+
const { z: z2 } = await import("zod");
|
|
8550
|
+
const visionFallbackUpdateSchema = z2.object({
|
|
8551
|
+
enabled: z2.boolean().optional(),
|
|
8552
|
+
url: z2.string().optional(),
|
|
8553
|
+
model: z2.string().optional(),
|
|
8554
|
+
timeout: z2.number().positive().optional(),
|
|
8555
|
+
backend: z2.enum(["ollama", "openai"]).optional(),
|
|
8556
|
+
providerModelRef: z2.string().optional(),
|
|
8557
|
+
apiKey: z2.string().optional()
|
|
7691
8558
|
});
|
|
7692
8559
|
const parsed = visionFallbackUpdateSchema.safeParse(req.body);
|
|
7693
8560
|
if (!parsed.success) {
|
|
@@ -7722,14 +8589,14 @@ All file and git operations should use this branch.
|
|
|
7722
8589
|
}
|
|
7723
8590
|
});
|
|
7724
8591
|
app.post("/api/config/vision-fallback/test", async (req, res) => {
|
|
7725
|
-
const { z } = await import("zod");
|
|
7726
|
-
const testSchema =
|
|
7727
|
-
url:
|
|
7728
|
-
model:
|
|
7729
|
-
backend:
|
|
7730
|
-
providerModelRef:
|
|
7731
|
-
timeout:
|
|
7732
|
-
apiKey:
|
|
8592
|
+
const { z: z2 } = await import("zod");
|
|
8593
|
+
const testSchema = z2.object({
|
|
8594
|
+
url: z2.string().optional(),
|
|
8595
|
+
model: z2.string().optional(),
|
|
8596
|
+
backend: z2.enum(["ollama", "openai"]).optional(),
|
|
8597
|
+
providerModelRef: z2.string().optional(),
|
|
8598
|
+
timeout: z2.number().positive().optional(),
|
|
8599
|
+
apiKey: z2.string().optional()
|
|
7733
8600
|
});
|
|
7734
8601
|
const parsed = testSchema.safeParse(req.body);
|
|
7735
8602
|
if (!parsed.success) {
|
|
@@ -7966,7 +8833,7 @@ All file and git operations should use this branch.
|
|
|
7966
8833
|
});
|
|
7967
8834
|
async function rebuildMcpTools() {
|
|
7968
8835
|
const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-FJAFLDZA.js");
|
|
7969
|
-
const { setMcpTools: setMcpTools2 } = await import("./tools-
|
|
8836
|
+
const { setMcpTools: setMcpTools2 } = await import("./tools-6BDT5YMT.js");
|
|
7970
8837
|
const mcpTools = createMcpTools2(mcpManager);
|
|
7971
8838
|
setMcpTools2(mcpTools);
|
|
7972
8839
|
toolRegistry = createToolRegistry();
|
|
@@ -8058,6 +8925,10 @@ All file and git operations should use this branch.
|
|
|
8058
8925
|
for (const s of sessions) {
|
|
8059
8926
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8060
8927
|
}
|
|
8928
|
+
await announceContextDrift(
|
|
8929
|
+
sessionManager,
|
|
8930
|
+
sessions.map((s) => s.id)
|
|
8931
|
+
);
|
|
8061
8932
|
}
|
|
8062
8933
|
const allServers = mcpManager.getAllServers();
|
|
8063
8934
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: allServers }));
|
|
@@ -8136,6 +9007,10 @@ All file and git operations should use this branch.
|
|
|
8136
9007
|
for (const s of sessions) {
|
|
8137
9008
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8138
9009
|
}
|
|
9010
|
+
await announceContextDrift(
|
|
9011
|
+
sessionManager,
|
|
9012
|
+
sessions.map((s) => s.id)
|
|
9013
|
+
);
|
|
8139
9014
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8140
9015
|
res.json({ server: mcpManager.getServer(name) });
|
|
8141
9016
|
} catch (error) {
|
|
@@ -8168,11 +9043,15 @@ All file and git operations should use this branch.
|
|
|
8168
9043
|
for (const s of sessions) {
|
|
8169
9044
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8170
9045
|
}
|
|
9046
|
+
await announceContextDrift(
|
|
9047
|
+
sessionManager,
|
|
9048
|
+
sessions.map((s) => s.id)
|
|
9049
|
+
);
|
|
8171
9050
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8172
9051
|
res.json({ success: true });
|
|
8173
9052
|
});
|
|
8174
9053
|
function oauthCallbackPage(message) {
|
|
8175
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>OpenFox</title></head><body style="font-family:system-ui;padding:2rem"><p>${message}</p></body></html>`;
|
|
9054
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>OpenFox</title></head><body style="font-family:system-ui;padding:2rem"><p>${message}</p><script>try{new BroadcastChannel('openfox-oauth').postMessage({type:'oauth-callback-complete'})}catch{}</script></body></html>`;
|
|
8176
9055
|
}
|
|
8177
9056
|
function parseOAuthResponse(value) {
|
|
8178
9057
|
let params;
|
|
@@ -8189,14 +9068,19 @@ All file and git operations should use this branch.
|
|
|
8189
9068
|
async function applyMcpOAuthResult(name) {
|
|
8190
9069
|
await mcpManager.connectServer(name);
|
|
8191
9070
|
await rebuildMcpTools();
|
|
8192
|
-
|
|
9071
|
+
const sessions = sessionManager.listSessions();
|
|
9072
|
+
for (const s of sessions) {
|
|
8193
9073
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8194
9074
|
}
|
|
9075
|
+
await announceContextDrift(
|
|
9076
|
+
sessionManager,
|
|
9077
|
+
sessions.map((s) => s.id)
|
|
9078
|
+
);
|
|
8195
9079
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8196
9080
|
}
|
|
8197
9081
|
async function completeMcpOAuth(name, serverUrl, code) {
|
|
8198
9082
|
const { auth } = await import("@modelcontextprotocol/sdk/client/auth.js");
|
|
8199
|
-
const { McpOAuthProvider: McpOAuthProvider2 } = await import("./oauth-provider-
|
|
9083
|
+
const { McpOAuthProvider: McpOAuthProvider2 } = await import("./oauth-provider-VHUS2KEG.js");
|
|
8200
9084
|
const { clearMcpOAuthAuthorizationState } = await import("./oauth-store-KECHUT54.js");
|
|
8201
9085
|
const provider = new McpOAuthProvider2(name, serverUrl);
|
|
8202
9086
|
try {
|
|
@@ -8229,8 +9113,9 @@ All file and git operations should use this branch.
|
|
|
8229
9113
|
}
|
|
8230
9114
|
try {
|
|
8231
9115
|
const { auth } = await import("@modelcontextprotocol/sdk/client/auth.js");
|
|
8232
|
-
const { McpOAuthProvider: McpOAuthProvider2 } = await import("./oauth-provider-
|
|
9116
|
+
const { McpOAuthProvider: McpOAuthProvider2, rejectStaleOAuthClient } = await import("./oauth-provider-VHUS2KEG.js");
|
|
8233
9117
|
const provider = new McpOAuthProvider2(name, server.config.url);
|
|
9118
|
+
await rejectStaleOAuthClient(provider);
|
|
8234
9119
|
const result = await auth(provider, { serverUrl: server.config.url });
|
|
8235
9120
|
if (result === "AUTHORIZED") {
|
|
8236
9121
|
await applyMcpOAuthResult(name);
|
|
@@ -8286,8 +9171,8 @@ All file and git operations should use this branch.
|
|
|
8286
9171
|
if (!parsed.code || !parsed.state) {
|
|
8287
9172
|
return res.status(400).json({ error: "Paste the whole callback URL, both code and state are needed" });
|
|
8288
9173
|
}
|
|
8289
|
-
const { readMcpOAuthEntry } = await import("./oauth-store-KECHUT54.js");
|
|
8290
|
-
const entry = await
|
|
9174
|
+
const { readMcpOAuthEntry: readMcpOAuthEntry2 } = await import("./oauth-store-KECHUT54.js");
|
|
9175
|
+
const entry = await readMcpOAuthEntry2(name, serverUrl);
|
|
8291
9176
|
if (!entry?.state || entry.state !== parsed.state) {
|
|
8292
9177
|
return res.status(400).json({ error: "This callback does not match the pending authorization" });
|
|
8293
9178
|
}
|
|
@@ -8341,13 +9226,17 @@ All file and git operations should use this branch.
|
|
|
8341
9226
|
for (const s of sessions) {
|
|
8342
9227
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8343
9228
|
}
|
|
9229
|
+
await announceContextDrift(
|
|
9230
|
+
sessionManager,
|
|
9231
|
+
sessions.map((s) => s.id)
|
|
9232
|
+
);
|
|
8344
9233
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8345
9234
|
res.json({ success: true });
|
|
8346
9235
|
} catch (error) {
|
|
8347
9236
|
res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
|
|
8348
9237
|
}
|
|
8349
9238
|
});
|
|
8350
|
-
const { listProjects } = await import("./projects-
|
|
9239
|
+
const { listProjects } = await import("./projects-5U37RUTL.js");
|
|
8351
9240
|
const allProjects = listProjects();
|
|
8352
9241
|
const projectDir = allProjects.find(
|
|
8353
9242
|
(p) => p.workdir === config3.workdir || config3.workdir && p.workdir.startsWith(config3.workdir + "/")
|
|
@@ -8374,13 +9263,13 @@ All file and git operations should use this branch.
|
|
|
8374
9263
|
})
|
|
8375
9264
|
);
|
|
8376
9265
|
app.get("/api/sessions/:id/background-processes", async (req, res) => {
|
|
8377
|
-
const { getSessionProcesses } = await import("./manager-
|
|
9266
|
+
const { getSessionProcesses } = await import("./manager-TMBFBCQL.js");
|
|
8378
9267
|
const sessionId = req.params.id;
|
|
8379
9268
|
const processes = getSessionProcesses(sessionId);
|
|
8380
9269
|
res.json({ processes });
|
|
8381
9270
|
});
|
|
8382
9271
|
app.post("/api/sessions/:id/background-process/:processId/stop", async (req, res) => {
|
|
8383
|
-
const { stopProcess } = await import("./manager-
|
|
9272
|
+
const { stopProcess } = await import("./manager-TMBFBCQL.js");
|
|
8384
9273
|
const sessionId = req.params.id;
|
|
8385
9274
|
const processId = req.params.processId;
|
|
8386
9275
|
const session = sessionManager.getSession(sessionId);
|
|
@@ -8520,7 +9409,7 @@ All file and git operations should use this branch.
|
|
|
8520
9409
|
);
|
|
8521
9410
|
const wss = wssExports.wss;
|
|
8522
9411
|
deferTasksBroadcast = (projectId, payload) => wssExports.broadcastForProject(projectId, "", { type: "tasks.update", payload });
|
|
8523
|
-
const { launchWorkflowRun, abortRunnerRun } = await import("./launch-
|
|
9412
|
+
const { launchWorkflowRun, abortRunnerRun } = await import("./launch-Y4H7FDU2.js");
|
|
8524
9413
|
deferTasksLaunchWorkflow = (sessionId, launch) => {
|
|
8525
9414
|
const effective = sessionManager.resolveEffectiveProviderModel(sessionId);
|
|
8526
9415
|
let llmClient = getLLMClient();
|
|
@@ -8547,9 +9436,14 @@ All file and git operations should use this branch.
|
|
|
8547
9436
|
broadcastForSession: wssExports.broadcastForSession
|
|
8548
9437
|
},
|
|
8549
9438
|
{
|
|
8550
|
-
workflowId: launch.workflowId,
|
|
9439
|
+
...launch.workflowId ? { workflowId: launch.workflowId } : {},
|
|
8551
9440
|
...launch.params && Object.keys(launch.params).length > 0 ? { params: launch.params } : {},
|
|
9441
|
+
...launch.subGroup ? { subGroup: launch.subGroup } : {},
|
|
8552
9442
|
...launch.scope ? { scope: launch.scope } : {},
|
|
9443
|
+
...launch.resumeFrom ? { resumeFrom: launch.resumeFrom } : {},
|
|
9444
|
+
...launch.stepOutput ? { stepOutput: launch.stepOutput } : {},
|
|
9445
|
+
...launch.userChoice ? { userChoice: launch.userChoice } : {},
|
|
9446
|
+
...launch.content ? { content: launch.content } : {},
|
|
8553
9447
|
...launch.attachments && launch.attachments.length > 0 ? { attachments: launch.attachments } : {}
|
|
8554
9448
|
}
|
|
8555
9449
|
);
|
|
@@ -8560,7 +9454,7 @@ All file and git operations should use this branch.
|
|
|
8560
9454
|
const state = sessionManager.getContextState(sessionId);
|
|
8561
9455
|
wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
|
|
8562
9456
|
});
|
|
8563
|
-
const { QueueProcessor } = await import("./processor-
|
|
9457
|
+
const { QueueProcessor } = await import("./processor-MEHODYNE.js");
|
|
8564
9458
|
const queueProcessor = new QueueProcessor({
|
|
8565
9459
|
sessionManager,
|
|
8566
9460
|
providerManager,
|
|
@@ -8581,6 +9475,80 @@ All file and git operations should use this branch.
|
|
|
8581
9475
|
}
|
|
8582
9476
|
return aborted;
|
|
8583
9477
|
};
|
|
9478
|
+
mcpToolDeps = {
|
|
9479
|
+
sessionManager,
|
|
9480
|
+
listProjects: () => listProjects(),
|
|
9481
|
+
createProject: async (name, workdir) => {
|
|
9482
|
+
const { createDirectoryWithGit } = await import("./project-creator-6AGQTNXB.js");
|
|
9483
|
+
return createDirectoryWithGit(name, workdir);
|
|
9484
|
+
},
|
|
9485
|
+
deleteProject: (projectId) => {
|
|
9486
|
+
const project = getProject(projectId);
|
|
9487
|
+
if (!project) return false;
|
|
9488
|
+
deleteProject(projectId);
|
|
9489
|
+
return true;
|
|
9490
|
+
},
|
|
9491
|
+
listWorkflows: (projectDir2) => listAvailableWorkflows(getGlobalConfigDir(config3.mode ?? "production"), projectDir2),
|
|
9492
|
+
topLevelAgentIds: async (workdir) => {
|
|
9493
|
+
const agents = await loadAllAgentsDefault(workdir);
|
|
9494
|
+
return getTopLevelAgents(agents).map((a) => a.metadata.id);
|
|
9495
|
+
},
|
|
9496
|
+
launchWorkflow: (sessionId, launch) => deferTasksLaunchWorkflow(sessionId, launch),
|
|
9497
|
+
stopSession: (sessionId) => {
|
|
9498
|
+
void (async () => {
|
|
9499
|
+
const { stopSessionExecution } = await import("./chat-handler-RAIMQU7F.js");
|
|
9500
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-6BDT5YMT.js");
|
|
9501
|
+
sessionManager.clearMessageQueue(sessionId);
|
|
9502
|
+
stopSessionExecution(sessionId, sessionManager);
|
|
9503
|
+
abortSession(sessionId);
|
|
9504
|
+
cancelQuestionsForSession(sessionId, "Session stopped by user");
|
|
9505
|
+
cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
|
|
9506
|
+
getEventStore().append(sessionId, { type: "running.changed", data: { isRunning: false } });
|
|
9507
|
+
})().catch((error) => {
|
|
9508
|
+
logger.error(`MCP stopSession failed for ${sessionId}`, {
|
|
9509
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9510
|
+
});
|
|
9511
|
+
});
|
|
9512
|
+
},
|
|
9513
|
+
stopWorkflow: (sessionId) => {
|
|
9514
|
+
const runningAborted = abortRunnerRun(sessionId);
|
|
9515
|
+
if (runningAborted) return { aborted: "running" };
|
|
9516
|
+
const execution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
9517
|
+
if (execution && execution.status === "waiting") {
|
|
9518
|
+
sessionManager.cancelWorkflow(
|
|
9519
|
+
sessionId,
|
|
9520
|
+
execution.id,
|
|
9521
|
+
execution.workflowId,
|
|
9522
|
+
execution.workflowName,
|
|
9523
|
+
execution.workflowColor
|
|
9524
|
+
);
|
|
9525
|
+
return { aborted: "paused" };
|
|
9526
|
+
}
|
|
9527
|
+
return null;
|
|
9528
|
+
},
|
|
9529
|
+
answerQuestion: (callId, answer, skip) => provideAnswer(callId, answer, skip),
|
|
9530
|
+
pendingQuestions: (sessionId) => getPendingQuestionsForSession(sessionId),
|
|
9531
|
+
confirmPath: (callId, approved, alwaysAllow) => providePathConfirmation(callId, approved, alwaysAllow).found,
|
|
9532
|
+
pendingConfirmations: (sessionId) => getPendingConfirmationsBySession()[sessionId] ?? [],
|
|
9533
|
+
setMetadataEntries: (sessionId, key, entries) => sessionManager.setMetadataEntries(sessionId, key, entries),
|
|
9534
|
+
recentMessages: (sessionId, limit) => {
|
|
9535
|
+
const eventStore = getEventStore();
|
|
9536
|
+
const { snapshot, events } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
9537
|
+
const combined = combineEventsWithSnapshot(sessionId, snapshot, events);
|
|
9538
|
+
return buildMessagesFromStoredEvents(combined, Math.max(1, Math.min(limit, 50)));
|
|
9539
|
+
}
|
|
9540
|
+
};
|
|
9541
|
+
let mcpActualPort = null;
|
|
9542
|
+
setMcpBootstrapForTools(async () => {
|
|
9543
|
+
const authRequired = mcpAuth.isAuthRequired();
|
|
9544
|
+
const sessionToken = authRequired ? await currentSessionToken() : null;
|
|
9545
|
+
return buildOpenFoxMcpBootstrap({
|
|
9546
|
+
host: config3.server.host ?? "127.0.0.1",
|
|
9547
|
+
port: mcpActualPort ?? config3.server.port,
|
|
9548
|
+
authRequired,
|
|
9549
|
+
sessionToken
|
|
9550
|
+
});
|
|
9551
|
+
});
|
|
8584
9552
|
return {
|
|
8585
9553
|
httpServer,
|
|
8586
9554
|
ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
|
|
@@ -8591,9 +9559,13 @@ All file and git operations should use this branch.
|
|
|
8591
9559
|
const addr = httpServer.address();
|
|
8592
9560
|
const actualPort = typeof addr === "object" && addr ? addr.port : listenPort;
|
|
8593
9561
|
setMcpOAuthServerPort(actualPort);
|
|
9562
|
+
mcpActualPort = actualPort;
|
|
8594
9563
|
const client = getLLMClient();
|
|
8595
9564
|
logger.info(`OpenFox server running at http://${host}:${actualPort}`);
|
|
8596
9565
|
logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
|
|
9566
|
+
logger.info(
|
|
9567
|
+
`MCP endpoint available at http://${host}:${actualPort}/mcp \u2014 run 'openfox mcp' for a paste-ready client config`
|
|
9568
|
+
);
|
|
8597
9569
|
logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
|
|
8598
9570
|
resolve9({ port: actualPort });
|
|
8599
9571
|
});
|
|
@@ -8604,7 +9576,7 @@ All file and git operations should use this branch.
|
|
|
8604
9576
|
void (async () => {
|
|
8605
9577
|
await devServerManager.stopAll();
|
|
8606
9578
|
await mcpManager.disconnectAll();
|
|
8607
|
-
const { stopAllInspectProxies } = await import("./inspect-proxy-
|
|
9579
|
+
const { stopAllInspectProxies } = await import("./inspect-proxy-A3OROWPG.js");
|
|
8608
9580
|
stopAllInspectProxies();
|
|
8609
9581
|
const { cleanupAllProcesses } = await import("./store-JYPZ2RXB.js");
|
|
8610
9582
|
cleanupAllProcesses();
|
|
@@ -8646,4 +9618,4 @@ export {
|
|
|
8646
9618
|
createServerHandle,
|
|
8647
9619
|
createServer
|
|
8648
9620
|
};
|
|
8649
|
-
//# sourceMappingURL=chunk-
|
|
9621
|
+
//# sourceMappingURL=chunk-6MUSEPV7.js.map
|