openfox 2.0.129 → 2.0.130
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 +32 -0
- package/README.md +2 -0
- package/dist/CHANGELOG.md +32 -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-4WCKNCQV.js} +32 -32
- package/dist/chunk-25DAVVTP.js +395 -0
- package/dist/{chunk-ISE55FOR.js → chunk-2AI7VYPN.js} +11 -1
- package/dist/{chunk-2COZT3YI.js → chunk-2OMXXCJJ.js} +24 -24
- package/dist/{chunk-OHTBWA7L.js → chunk-35GQEIH2.js} +13 -13
- package/dist/{chunk-N2Q3KYWM.js → chunk-3DZSHRXE.js} +7 -2
- package/dist/chunk-3OBZ55SX.js +7 -0
- package/dist/{chunk-ZIL6VKLU.js → chunk-64N4CZFT.js} +2 -2
- package/dist/{chunk-66MG44TF.js → chunk-6QZVAOSA.js} +2 -2
- package/dist/{chunk-6LX43YRA.js → chunk-7ET46SNE.js} +4 -4
- package/dist/{chunk-YJD5A375.js → chunk-AYCB32NI.js} +5 -5
- package/dist/{chunk-GIJUWPRQ.js → chunk-CGCEAKG5.js} +2 -2
- package/dist/{auth-GTGIGX5X.js → chunk-CQGJ5RWB.js} +4 -4
- package/dist/{chunk-JQMQ4DGC.js → chunk-CYQUM42E.js} +76 -32
- package/dist/{chunk-P6GTPKE5.js → chunk-DHDIKVRT.js} +278 -16
- package/dist/{chunk-ZXWJ6JTL.js → chunk-DI5GTIAU.js} +1154 -196
- package/dist/chunk-DQTN4B33.js +235 -0
- package/dist/{chunk-RV5G7QQU.js → chunk-DQTXXHD2.js} +3 -3
- package/dist/{chunk-MYNNMV75.js → chunk-DYIDDGMN.js} +54 -36
- package/dist/{chunk-DABWCAXS.js → chunk-EUOWCVU6.js} +19 -6
- package/dist/{chunk-7RIEI2TE.js → chunk-EVVWDRM5.js} +45 -16
- package/dist/{chunk-ATWOXI52.js → chunk-HCYSGLY2.js} +1 -1
- package/dist/{chunk-KBESUA3W.js → chunk-JHA2SPCU.js} +2 -2
- package/dist/{chunk-ZIYETXOW.js → chunk-JKRMMNUR.js} +4 -4
- package/dist/{chunk-H6YIVK67.js → chunk-JSONBGSY.js} +24 -4
- package/dist/{chunk-HO7A7TMF.js → chunk-KHKRHGA6.js} +15 -5
- package/dist/{chunk-K2QKX4ZJ.js → chunk-OA2FURC3.js} +8 -2
- package/dist/{chunk-ZFNBATE3.js → chunk-OXMOKQPW.js} +3 -2
- package/dist/{chunk-567L5OJ6.js → chunk-P57IBMK4.js} +2 -2
- package/dist/{session-status-6Q5B5PEN.js → chunk-T3S5MTMD.js} +2 -3
- package/dist/{chunk-7ZMOP5LS.js → chunk-TS5WKTFB.js} +18 -1
- package/dist/{chunk-PBFGJFGC.js → chunk-UV6IH6HK.js} +32 -10
- package/dist/{chunk-26WS7AH6.js → chunk-VJNA5AOB.js} +39 -7
- package/dist/{chunk-IJFKJ5D6.js → chunk-VUIXQ6X4.js} +4 -4
- package/dist/{chunk-L24A6GZ7.js → chunk-X24XN2M2.js} +84 -56
- package/dist/{chunk-AQBRI7TM.js → chunk-X3FHIDLT.js} +2 -2
- package/dist/chunk-YNMP647F.js +24 -0
- package/dist/{chunk-7YP4O4XE.js → chunk-ZRCRGPGT.js} +2 -2
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/client-26RSMUAX.js +16 -0
- package/dist/{client-pure-GU2SV3DD.js → client-pure-FLKP6PTI.js} +2 -2
- package/dist/{compactor-WKFNTUXL.js → compactor-HYBSSFKP.js} +14 -14
- package/dist/dynamic-context-2VLACYK4.js +50 -0
- package/dist/{events-GMHRX4LF.js → events-D33QXGSX.js} +7 -7
- package/dist/{folding-7K2GRPF4.js → folding-QOKBKUU6.js} +5 -5
- package/dist/{http-client-ROVNSHF6.js → http-client-MGAJTGPR.js} +3 -3
- package/dist/launch-KFENN5GS.js +53 -0
- package/dist/{manager-NOKJQTTF.js → manager-VRHX7BVI.js} +4 -4
- 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-E6GIZGEN.js → orchestrator-PLTAEOMR.js} +30 -30
- package/dist/package.json +1 -1
- package/dist/{path-security-UDZ7UVWK.js → path-security-4WQJGU3Z.js} +11 -11
- package/dist/{platform-EMKWQTZX.js → platform-MWWJ6OKV.js} +3 -3
- package/dist/plugins-registry.json +12 -0
- package/dist/{processor-7RDR675X.js → processor-K4WHZKE6.js} +31 -31
- package/dist/{profiles-HXGJIPF2.js → profiles-EBCVFYG7.js} +2 -2
- 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-CVM33XHW.js} +10 -10
- package/dist/{provider-manager-PU7RS2TK.js → provider-manager-7NL5EQLG.js} +10 -10
- package/dist/{registry-Z4GVYFTV.js → registry-KFFZFIZT.js} +3 -3
- package/dist/{serve-Z5ITDPCQ.js → serve-KQAPXO5X.js} +42 -40
- package/dist/server/index.d.ts +18 -3
- package/dist/server/index.js +39 -37
- package/dist/server-RPKVKMDD.js +56 -0
- package/dist/{service-INQWTYY2.js → service-7TLOSFAO.js} +12 -12
- package/dist/{session-overrides-4WAD3X5Q.js → session-overrides-U573CU2V.js} +5 -5
- package/dist/session-status-UFO5IFI7.js +10 -0
- package/dist/{sessions-D7VRCHQY.js → sessions-LUVWWRFO.js} +4 -4
- package/dist/{settings-VOWWO4VG.js → settings-LMAMF2X7.js} +2 -2
- 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-FA6Z52H2.js} +12 -12
- package/dist/{tools-6XUSIJN2.js → tools-SSPC3UQZ.js} +34 -34
- 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-QRGK67DB.js} +2 -2
- package/dist/web/assets/{index-CGKd7WLQ.js → index-0PVQNOfW.js} +88 -88
- package/dist/web/assets/{index-CbX8veVW.css → index-Ds94aclE.css} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- 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/dynamic-context-ZVE733UI.js +0 -28
- package/dist/launch-5ARSU7EM.js +0 -53
- package/dist/server-MUJ7EHEI.js +0 -56
|
@@ -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-DYIDDGMN.js";
|
|
20
28
|
import {
|
|
21
29
|
TEMPLATE_VARIABLES
|
|
22
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-2OMXXCJJ.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-X24XN2M2.js";
|
|
67
46
|
import {
|
|
68
47
|
checkoutBranchFromSharedSource,
|
|
69
48
|
deleteWorkspace,
|
|
@@ -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-JSONBGSY.js";
|
|
111
91
|
import {
|
|
112
92
|
getSessionDisabledServers,
|
|
113
93
|
setSessionDisabledServers
|
|
114
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-ZRCRGPGT.js";
|
|
115
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-64N4CZFT.js";
|
|
117
|
+
import {
|
|
118
|
+
ALWAYS_ALLOWED,
|
|
119
|
+
ALWAYS_ALLOWED_FOR_SUBAGENTS,
|
|
120
|
+
TOP_LEVEL_ONLY_TOOLS
|
|
121
|
+
} from "./chunk-CGCEAKG5.js";
|
|
122
|
+
import {
|
|
123
|
+
EventEmitter,
|
|
124
|
+
getPendingQuestionsForSession,
|
|
125
|
+
provideAnswer
|
|
126
|
+
} from "./chunk-JURM3RPZ.js";
|
|
127
|
+
import {
|
|
128
|
+
getPendingConfirmationsBySession,
|
|
129
|
+
providePathConfirmation
|
|
130
|
+
} from "./chunk-VJNA5AOB.js";
|
|
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-AYCB32NI.js";
|
|
149
|
+
import {
|
|
150
|
+
buildMessagesFromStoredEvents
|
|
151
|
+
} from "./chunk-JKRMMNUR.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-X3FHIDLT.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-JHA2SPCU.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-6QZVAOSA.js";
|
|
207
|
+
import {
|
|
208
|
+
createProviderManager,
|
|
209
|
+
detectModel,
|
|
210
|
+
getLlmStatus,
|
|
211
|
+
parseDefaultModelSelection
|
|
212
|
+
} from "./chunk-CYQUM42E.js";
|
|
213
|
+
import {
|
|
214
|
+
buildOpenFoxMcpBootstrap
|
|
215
|
+
} from "./chunk-YNMP647F.js";
|
|
216
|
+
import {
|
|
217
|
+
VERSION
|
|
218
|
+
} from "./chunk-3OBZ55SX.js";
|
|
219
|
+
import {
|
|
220
|
+
deleteProject,
|
|
200
221
|
getProject,
|
|
201
222
|
getProjectByWorkdir,
|
|
202
223
|
updateProject
|
|
203
224
|
} from "./chunk-3B232XG4.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-OXMOKQPW.js";
|
|
217
239
|
import {
|
|
218
240
|
deleteSetting,
|
|
219
241
|
getSetting,
|
|
220
242
|
setSetting
|
|
221
|
-
} from "./chunk-
|
|
243
|
+
} from "./chunk-3DZSHRXE.js";
|
|
222
244
|
import {
|
|
223
245
|
initDatabase
|
|
224
246
|
} from "./chunk-OYBUJIC3.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,7 @@ var SessionManager = class {
|
|
|
2761
2812
|
providerManager;
|
|
2762
2813
|
dynamicContextChangedStore = /* @__PURE__ */ new Map();
|
|
2763
2814
|
debugDumpStore = /* @__PURE__ */ new Map();
|
|
2815
|
+
announcedPromptHashStore = /* @__PURE__ */ new Map();
|
|
2764
2816
|
warmedUpSessions = /* @__PURE__ */ new Set();
|
|
2765
2817
|
switchLocks = /* @__PURE__ */ new Map();
|
|
2766
2818
|
workspaceCreationLocks = /* @__PURE__ */ new Map();
|
|
@@ -3580,7 +3632,7 @@ var SessionManager = class {
|
|
|
3580
3632
|
const state = getSessionState(sessionId, this.getCurrentModelContext(sessionId));
|
|
3581
3633
|
const maxTokens = this.getCurrentModelContext(sessionId);
|
|
3582
3634
|
const currentTokens = promptTokens + completionTokens;
|
|
3583
|
-
const compactionCount = state?.contextState.compactionCount ?? 0;
|
|
3635
|
+
const compactionCount = subAgentId ? 0 : state?.contextState.compactionCount ?? 0;
|
|
3584
3636
|
const dynamicContextChanged = this.getDynamicContextChanged(sessionId);
|
|
3585
3637
|
emitContextState(
|
|
3586
3638
|
sessionId,
|
|
@@ -3839,6 +3891,16 @@ var SessionManager = class {
|
|
|
3839
3891
|
getDynamicContextChanged(sessionId) {
|
|
3840
3892
|
return this.dynamicContextChangedStore.get(sessionId) ?? false;
|
|
3841
3893
|
}
|
|
3894
|
+
/**
|
|
3895
|
+
* The instructions/skills/model hash last announced to the model via a
|
|
3896
|
+
* system-prompt-change reminder. Drives exactly-once prompt-drift reminders.
|
|
3897
|
+
*/
|
|
3898
|
+
getAnnouncedPromptHash(sessionId) {
|
|
3899
|
+
return this.announcedPromptHashStore.get(sessionId);
|
|
3900
|
+
}
|
|
3901
|
+
setAnnouncedPromptHash(sessionId, hash) {
|
|
3902
|
+
this.announcedPromptHashStore.set(sessionId, hash);
|
|
3903
|
+
}
|
|
3842
3904
|
/**
|
|
3843
3905
|
* @deprecated Use addTokensUsed instead
|
|
3844
3906
|
*/
|
|
@@ -4748,10 +4810,766 @@ function createWorkflowRoutes(configDir, config3, projectDir) {
|
|
|
4748
4810
|
return createCrudRoutes(crudConfig, configDir, projectDir);
|
|
4749
4811
|
}
|
|
4750
4812
|
|
|
4751
|
-
// src/server/
|
|
4813
|
+
// src/server/mcp/server/endpoint.ts
|
|
4752
4814
|
import { Router as Router4 } from "express";
|
|
4753
|
-
|
|
4815
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4816
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4817
|
+
|
|
4818
|
+
// src/server/mcp/server/tools.ts
|
|
4819
|
+
import { z } from "zod";
|
|
4820
|
+
var CONTINUE_PROMPT = "Continue working on the acceptance criteria.";
|
|
4821
|
+
var MAX_DETAIL_CONTENT_LENGTH_DEFAULT = 400;
|
|
4822
|
+
var MIN_DETAIL_CONTENT_LENGTH = 50;
|
|
4823
|
+
var MAX_DETAIL_CONTENT_LENGTH_HARD_CAP = 1e4;
|
|
4824
|
+
var WAIT_TIMEOUT_SECONDS_DEFAULT = 60;
|
|
4825
|
+
var WAIT_TIMEOUT_SECONDS_MIN = 5;
|
|
4826
|
+
var WAIT_TIMEOUT_SECONDS_MAX = 600;
|
|
4827
|
+
var WAIT_POLL_INTERVAL_MS = 500;
|
|
4828
|
+
var WAIT_IDLE_GRACE_MS = 2e3;
|
|
4829
|
+
var METADATA_SCHEMAS = {
|
|
4830
|
+
criteria: {
|
|
4831
|
+
description: "Acceptance criteria that drive workflow transitions",
|
|
4832
|
+
fields: {
|
|
4833
|
+
id: "string (auto-generated)",
|
|
4834
|
+
description: "string \u2014 what needs to be done and how to verify it",
|
|
4835
|
+
status: "pending | completed | passed | failed"
|
|
4836
|
+
},
|
|
4837
|
+
defaultStatus: "pending"
|
|
4838
|
+
},
|
|
4839
|
+
todos: {
|
|
4840
|
+
description: "Task tracking items for the builder",
|
|
4841
|
+
fields: {
|
|
4842
|
+
id: "string (auto-generated)",
|
|
4843
|
+
description: "string \u2014 task description",
|
|
4844
|
+
status: "pending | in_progress | completed"
|
|
4845
|
+
},
|
|
4846
|
+
defaultStatus: "pending"
|
|
4847
|
+
},
|
|
4848
|
+
review_findings: {
|
|
4849
|
+
description: "Code review findings from the code_reviewer sub-agent",
|
|
4850
|
+
fields: {
|
|
4851
|
+
id: "string (auto-generated)",
|
|
4852
|
+
description: "string \u2014 finding description",
|
|
4853
|
+
status: "open | resolved | dismissed",
|
|
4854
|
+
severity: "minor | major | critical (optional)"
|
|
4855
|
+
},
|
|
4856
|
+
defaultStatus: "open"
|
|
4857
|
+
}
|
|
4858
|
+
};
|
|
4859
|
+
function computeSettlement(session, pendingQuestionsCount, pendingConfirmationsCount, activeExecutionStatus, completedEvidence = false) {
|
|
4860
|
+
if (session.phase === "done") return { settled: true, outcome: "completed" };
|
|
4861
|
+
if (session.phase === "blocked") return { settled: true, outcome: "blocked" };
|
|
4862
|
+
const waitingForUser = pendingQuestionsCount > 0 || pendingConfirmationsCount > 0 || activeExecutionStatus === "waiting";
|
|
4863
|
+
if (waitingForUser) return { settled: true, outcome: "waiting" };
|
|
4864
|
+
if (completedEvidence && !session.isRunning) return { settled: true, outcome: "completed" };
|
|
4865
|
+
return { settled: false, outcome: null };
|
|
4866
|
+
}
|
|
4867
|
+
function workflowPayload(execution) {
|
|
4868
|
+
if (!execution) return null;
|
|
4869
|
+
const pausedAtUserStep = execution.status === "waiting" && (execution.pendingChoices?.length ?? 0) > 0;
|
|
4870
|
+
return {
|
|
4871
|
+
id: execution.id,
|
|
4872
|
+
name: execution.workflowName,
|
|
4873
|
+
status: execution.status,
|
|
4874
|
+
...execution.currentStepId ? { currentStepId: execution.currentStepId } : {},
|
|
4875
|
+
...execution.currentStepName ? { currentStepName: execution.currentStepName } : {},
|
|
4876
|
+
stepOutput: execution.stepOutput,
|
|
4877
|
+
...execution.pendingChoices ? { pendingChoices: execution.pendingChoices } : {},
|
|
4878
|
+
...execution.subGroup ? { subGroup: execution.subGroup } : {},
|
|
4879
|
+
...pausedAtUserStep ? { resumeHint: "Call openfox_resume_workflow with choice set to one of the pendingChoices ids above." } : {}
|
|
4880
|
+
};
|
|
4881
|
+
}
|
|
4882
|
+
function normalizeMetadataId(id) {
|
|
4883
|
+
if (typeof id !== "string" && typeof id !== "number") return void 0;
|
|
4884
|
+
if (id === "") return void 0;
|
|
4885
|
+
return String(id);
|
|
4886
|
+
}
|
|
4887
|
+
function resolveWaitTimeout(value) {
|
|
4888
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return WAIT_TIMEOUT_SECONDS_DEFAULT;
|
|
4889
|
+
return Math.max(WAIT_TIMEOUT_SECONDS_MIN, Math.min(WAIT_TIMEOUT_SECONDS_MAX, Math.floor(value)));
|
|
4890
|
+
}
|
|
4891
|
+
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
4892
|
+
function ok(payload) {
|
|
4893
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
4894
|
+
}
|
|
4895
|
+
function fail(message) {
|
|
4896
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
4897
|
+
}
|
|
4898
|
+
function truncate(content, maxLength) {
|
|
4899
|
+
return content.length > maxLength ? `${content.slice(0, maxLength)}...` : content;
|
|
4900
|
+
}
|
|
4901
|
+
function resolveMaxContentLength(value) {
|
|
4902
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return MAX_DETAIL_CONTENT_LENGTH_DEFAULT;
|
|
4903
|
+
return Math.max(MIN_DETAIL_CONTENT_LENGTH, Math.min(MAX_DETAIL_CONTENT_LENGTH_HARD_CAP, Math.floor(value)));
|
|
4904
|
+
}
|
|
4905
|
+
function buildSessionStatusPayload(sessionId, deps) {
|
|
4906
|
+
const session = deps.sessionManager.getSession(sessionId);
|
|
4907
|
+
if (!session) return null;
|
|
4908
|
+
const pendingQuestions = deps.pendingQuestions(sessionId);
|
|
4909
|
+
const pendingConfirmations = deps.pendingConfirmations(sessionId);
|
|
4910
|
+
const activeExecution = deps.sessionManager.getActiveWorkflowExecution(sessionId);
|
|
4911
|
+
const status = projectSessionStatus({
|
|
4912
|
+
session,
|
|
4913
|
+
pendingQuestionsCount: pendingQuestions.length,
|
|
4914
|
+
pendingConfirmationsCount: pendingConfirmations.length,
|
|
4915
|
+
activeWorkflowStepName: activeExecution?.currentStepName ?? null
|
|
4916
|
+
});
|
|
4917
|
+
return {
|
|
4918
|
+
...status,
|
|
4919
|
+
isRunning: session.isRunning,
|
|
4920
|
+
pending: { questions: pendingQuestions, confirmations: pendingConfirmations },
|
|
4921
|
+
workflow: workflowPayload(activeExecution)
|
|
4922
|
+
};
|
|
4923
|
+
}
|
|
4924
|
+
function createOpenFoxMcpTools(deps) {
|
|
4925
|
+
const { sessionManager } = deps;
|
|
4926
|
+
const requireSession = (sessionId) => {
|
|
4927
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
4928
|
+
return { ok: false, error: "sessionId is required" };
|
|
4929
|
+
}
|
|
4930
|
+
const session = sessionManager.getSession(sessionId);
|
|
4931
|
+
if (!session) {
|
|
4932
|
+
return { ok: false, error: `Session not found: ${sessionId}` };
|
|
4933
|
+
}
|
|
4934
|
+
return { ok: true, session };
|
|
4935
|
+
};
|
|
4936
|
+
const requireSessionAndId = (args) => {
|
|
4937
|
+
const check = requireSession(args["sessionId"]);
|
|
4938
|
+
if (!check.ok) return check;
|
|
4939
|
+
return { ok: true, session: check.session, sessionId: check.session.id };
|
|
4940
|
+
};
|
|
4941
|
+
const resolvePendingCallId = (sessionId, callId, pending, noun) => {
|
|
4942
|
+
if (typeof callId !== "string" || callId.length === 0) {
|
|
4943
|
+
return { kind: "list" };
|
|
4944
|
+
}
|
|
4945
|
+
if (!pending.some((item) => item.callId === callId)) {
|
|
4946
|
+
return { kind: "error", error: `No pending ${noun} with callId "${callId}" for session ${sessionId}` };
|
|
4947
|
+
}
|
|
4948
|
+
return { kind: "found", callId };
|
|
4949
|
+
};
|
|
4950
|
+
const tools = [
|
|
4951
|
+
{
|
|
4952
|
+
name: "openfox_projects",
|
|
4953
|
+
description: "List all OpenFox projects.",
|
|
4954
|
+
inputSchema: {},
|
|
4955
|
+
handler: async () => {
|
|
4956
|
+
const projects = deps.listProjects();
|
|
4957
|
+
return ok(projects);
|
|
4958
|
+
}
|
|
4959
|
+
},
|
|
4960
|
+
{
|
|
4961
|
+
name: "openfox_create_project",
|
|
4962
|
+
description: "Create a new OpenFox project at the given directory. Creates the directory and initializes git if missing, then registers the project.",
|
|
4963
|
+
inputSchema: {
|
|
4964
|
+
name: z.string().describe("Project name (letters, numbers, hyphens, underscores, dots, spaces)"),
|
|
4965
|
+
workdir: z.string().describe("Absolute path of the project directory")
|
|
4966
|
+
},
|
|
4967
|
+
handler: async (args) => {
|
|
4968
|
+
const name = args["name"];
|
|
4969
|
+
const workdir = args["workdir"];
|
|
4970
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
4971
|
+
return fail("name is required");
|
|
4972
|
+
}
|
|
4973
|
+
if (typeof workdir !== "string" || workdir.trim().length === 0) {
|
|
4974
|
+
return fail("workdir is required");
|
|
4975
|
+
}
|
|
4976
|
+
try {
|
|
4977
|
+
const project = await deps.createProject(name.trim(), workdir.trim());
|
|
4978
|
+
return ok({ project });
|
|
4979
|
+
} catch (error) {
|
|
4980
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
},
|
|
4984
|
+
{
|
|
4985
|
+
name: "openfox_delete_project",
|
|
4986
|
+
description: "Delete an OpenFox project and all its sessions by id.",
|
|
4987
|
+
inputSchema: {
|
|
4988
|
+
projectId: z.string().describe("Project id to delete")
|
|
4989
|
+
},
|
|
4990
|
+
handler: async (args) => {
|
|
4991
|
+
const projectId = args["projectId"];
|
|
4992
|
+
if (typeof projectId !== "string" || projectId.length === 0) {
|
|
4993
|
+
return fail("projectId is required");
|
|
4994
|
+
}
|
|
4995
|
+
const deleted = deps.deleteProject(projectId);
|
|
4996
|
+
return ok({ deleted });
|
|
4997
|
+
}
|
|
4998
|
+
},
|
|
4999
|
+
{
|
|
5000
|
+
name: "openfox_create_session",
|
|
5001
|
+
description: "Create a new OpenFox session in a project, optionally selecting an agent (e.g. planner or builder) as the session mode.",
|
|
5002
|
+
inputSchema: {
|
|
5003
|
+
projectId: z.string().describe("Project to create the session in"),
|
|
5004
|
+
title: z.string().optional().describe("Optional session title"),
|
|
5005
|
+
agentId: z.string().optional().describe("Optional agent to use as the session mode (validated against available top-level agents)")
|
|
5006
|
+
},
|
|
5007
|
+
handler: async (args) => {
|
|
5008
|
+
const projectId = args["projectId"];
|
|
5009
|
+
if (typeof projectId !== "string" || projectId.length === 0) {
|
|
5010
|
+
return fail("projectId is required");
|
|
5011
|
+
}
|
|
5012
|
+
const title = typeof args["title"] === "string" ? args["title"] : void 0;
|
|
5013
|
+
const rawAgentId = args["agentId"];
|
|
5014
|
+
const agentId = typeof rawAgentId === "string" && rawAgentId.trim().length > 0 ? rawAgentId : void 0;
|
|
5015
|
+
try {
|
|
5016
|
+
const session = sessionManager.createSession(projectId, title, null, null);
|
|
5017
|
+
if (agentId) {
|
|
5018
|
+
const project = sessionManager.getProject(projectId);
|
|
5019
|
+
const topLevelIds = project ? await deps.topLevelAgentIds(project.workdir) : [];
|
|
5020
|
+
if (!topLevelIds.includes(agentId)) {
|
|
5021
|
+
return fail(
|
|
5022
|
+
`Invalid agentId "${agentId}". Must be one of: ${topLevelIds.join(", ") || "(none available)"}`
|
|
5023
|
+
);
|
|
5024
|
+
}
|
|
5025
|
+
sessionManager.setMode(session.id, agentId);
|
|
5026
|
+
}
|
|
5027
|
+
return ok({
|
|
5028
|
+
sessionId: session.id,
|
|
5029
|
+
projectId,
|
|
5030
|
+
title: session.metadata?.title ?? title ?? null,
|
|
5031
|
+
mode: agentId ?? session.mode
|
|
5032
|
+
});
|
|
5033
|
+
} catch (error) {
|
|
5034
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
},
|
|
5038
|
+
{
|
|
5039
|
+
name: "openfox_set_mode",
|
|
5040
|
+
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.",
|
|
5041
|
+
inputSchema: {
|
|
5042
|
+
sessionId: z.string().describe("Session to switch"),
|
|
5043
|
+
mode: z.string().describe("Target mode: one of the session project\u2019s top-level agents")
|
|
5044
|
+
},
|
|
5045
|
+
handler: async (args) => {
|
|
5046
|
+
const check = requireSession(args["sessionId"]);
|
|
5047
|
+
if (!check.ok) return fail(check.error);
|
|
5048
|
+
const session = check.session;
|
|
5049
|
+
const mode = args["mode"];
|
|
5050
|
+
if (typeof mode !== "string" || mode.trim().length === 0) {
|
|
5051
|
+
return fail("mode is required");
|
|
5052
|
+
}
|
|
5053
|
+
const project = sessionManager.getProject(session.projectId);
|
|
5054
|
+
const topLevelIds = project ? await deps.topLevelAgentIds(project.workdir) : [];
|
|
5055
|
+
if (!topLevelIds.includes(mode)) {
|
|
5056
|
+
return fail(`Invalid mode "${mode}". Must be one of: ${topLevelIds.join(", ") || "(none available)"}`);
|
|
5057
|
+
}
|
|
5058
|
+
const updated = sessionManager.setMode(session.id, mode);
|
|
5059
|
+
return ok({ sessionId: session.id, mode: updated?.mode ?? mode });
|
|
5060
|
+
}
|
|
5061
|
+
},
|
|
5062
|
+
{
|
|
5063
|
+
name: "openfox_sessions",
|
|
5064
|
+
description: "List sessions, optionally scoped to a project, with running state and pending interaction counts.",
|
|
5065
|
+
inputSchema: {
|
|
5066
|
+
projectId: z.string().optional().describe("Scope the listing to a project"),
|
|
5067
|
+
limit: z.number().int().positive().optional().describe("Maximum sessions to return (default 20)"),
|
|
5068
|
+
offset: z.number().int().nonnegative().optional().describe("Number of sessions to skip (default 0)")
|
|
5069
|
+
},
|
|
5070
|
+
handler: async (args) => {
|
|
5071
|
+
const limit = typeof args["limit"] === "number" ? args["limit"] : 20;
|
|
5072
|
+
const offset = typeof args["offset"] === "number" ? args["offset"] : 0;
|
|
5073
|
+
const projectId = args["projectId"];
|
|
5074
|
+
const result = typeof projectId === "string" && projectId.length > 0 ? sessionManager.listSessionsByProject(projectId, limit, offset) : sessionManager.listSessionsLimited(limit, offset);
|
|
5075
|
+
const sessions = result.sessions.map((s) => ({
|
|
5076
|
+
id: s.id,
|
|
5077
|
+
projectId: s.projectId,
|
|
5078
|
+
title: s.title ?? null,
|
|
5079
|
+
isRunning: s.isRunning,
|
|
5080
|
+
pendingQuestionCount: deps.pendingQuestions(s.id).length,
|
|
5081
|
+
pendingConfirmationCount: deps.pendingConfirmations(s.id).length,
|
|
5082
|
+
pausedWorkflowStep: sessionManager.getActiveWorkflowExecution(s.id)?.status === "waiting",
|
|
5083
|
+
updatedAt: s.updatedAt
|
|
5084
|
+
}));
|
|
5085
|
+
return ok({ sessions, hasMore: result.hasMore });
|
|
5086
|
+
}
|
|
5087
|
+
},
|
|
5088
|
+
{
|
|
5089
|
+
name: "openfox_session_status",
|
|
5090
|
+
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.",
|
|
5091
|
+
inputSchema: {
|
|
5092
|
+
sessionId: z.string().describe("Session to inspect")
|
|
5093
|
+
},
|
|
5094
|
+
handler: async (args) => {
|
|
5095
|
+
const check = requireSession(args["sessionId"]);
|
|
5096
|
+
if (!check.ok) return fail(check.error);
|
|
5097
|
+
const payload = buildSessionStatusPayload(check.session.id, deps);
|
|
5098
|
+
if (!payload) return fail(`Session not found: ${check.session.id}`);
|
|
5099
|
+
return ok(payload);
|
|
5100
|
+
}
|
|
5101
|
+
},
|
|
5102
|
+
{
|
|
5103
|
+
name: "openfox_session_detail",
|
|
5104
|
+
description: "Read the most recent messages of a session (content truncated) so progress can be followed without a live WebSocket.",
|
|
5105
|
+
inputSchema: {
|
|
5106
|
+
sessionId: z.string().describe("Session to read"),
|
|
5107
|
+
limit: z.number().int().positive().optional().describe("Maximum messages to return (default 20, capped at 50)"),
|
|
5108
|
+
maxContentLength: z.number().int().positive().optional().describe("Maximum content length per message (default 400, clamped to 50\u201310000)")
|
|
5109
|
+
},
|
|
5110
|
+
handler: async (args) => {
|
|
5111
|
+
const check = requireSession(args["sessionId"]);
|
|
5112
|
+
if (!check.ok) return fail(check.error);
|
|
5113
|
+
const limit = typeof args["limit"] === "number" ? args["limit"] : 20;
|
|
5114
|
+
const maxContentLength = resolveMaxContentLength(args["maxContentLength"]);
|
|
5115
|
+
const { messages, hiddenCount } = deps.recentMessages(check.session.id, limit);
|
|
5116
|
+
return ok({
|
|
5117
|
+
messages: messages.map((message) => ({
|
|
5118
|
+
id: message.id,
|
|
5119
|
+
role: message.role,
|
|
5120
|
+
content: truncate(message.content ?? "", maxContentLength),
|
|
5121
|
+
...message.toolCalls && message.toolCalls.length > 0 ? { toolCalls: message.toolCalls.map((toolCall) => toolCall.name) } : {}
|
|
5122
|
+
})),
|
|
5123
|
+
hiddenCount
|
|
5124
|
+
});
|
|
5125
|
+
}
|
|
5126
|
+
},
|
|
5127
|
+
{
|
|
5128
|
+
name: "openfox_session_metadata",
|
|
5129
|
+
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.",
|
|
5130
|
+
inputSchema: {
|
|
5131
|
+
sessionId: z.string().describe("Session to manage"),
|
|
5132
|
+
action: z.enum(["get", "list", "add", "update", "remove", "schema"]).describe("Operation to perform"),
|
|
5133
|
+
key: z.string().optional().describe("Metadata key (required for get/add/update/remove/schema)"),
|
|
5134
|
+
id: z.string().optional().describe("Item id (required for update and remove)"),
|
|
5135
|
+
description: z.string().optional().describe("Item description (required for add)"),
|
|
5136
|
+
status: z.string().optional().describe("Item status (for add/update)")
|
|
5137
|
+
},
|
|
5138
|
+
handler: async (args) => {
|
|
5139
|
+
const check = requireSession(args["sessionId"]);
|
|
5140
|
+
if (!check.ok) return fail(check.error);
|
|
5141
|
+
const session = check.session;
|
|
5142
|
+
const action = args["action"];
|
|
5143
|
+
const key = typeof args["key"] === "string" && args["key"].length > 0 ? args["key"] : void 0;
|
|
5144
|
+
const entries = session.metadataEntries ?? {};
|
|
5145
|
+
if (action === "list") {
|
|
5146
|
+
return ok({ keys: Object.entries(entries).map(([k, v]) => ({ key: k, count: v.length })) });
|
|
5147
|
+
}
|
|
5148
|
+
if (action === "schema") {
|
|
5149
|
+
if (!key) return fail("key is required for schema");
|
|
5150
|
+
const schema = METADATA_SCHEMAS[key];
|
|
5151
|
+
if (!schema) return ok({ key, generic: true });
|
|
5152
|
+
return ok({ key, description: schema.description, fields: schema.fields });
|
|
5153
|
+
}
|
|
5154
|
+
if (action === "get") {
|
|
5155
|
+
if (!key) return fail("key is required for get");
|
|
5156
|
+
return ok({ key, entries: entries[key] ?? [] });
|
|
5157
|
+
}
|
|
5158
|
+
if (action === "add") {
|
|
5159
|
+
if (!key) return fail("key is required for add");
|
|
5160
|
+
const description = args["description"];
|
|
5161
|
+
if (typeof description !== "string" || description.trim().length === 0) {
|
|
5162
|
+
return fail("description is required for add");
|
|
5163
|
+
}
|
|
5164
|
+
const current = entries[key] ?? [];
|
|
5165
|
+
const newEntry = {
|
|
5166
|
+
id: normalizeMetadataId(args["id"]) ?? (current.length + 1).toString(),
|
|
5167
|
+
description,
|
|
5168
|
+
status: typeof args["status"] === "string" ? args["status"] : METADATA_SCHEMAS[key]?.defaultStatus ?? "pending"
|
|
5169
|
+
};
|
|
5170
|
+
const updated = [...current, newEntry];
|
|
5171
|
+
deps.setMetadataEntries(session.id, key, updated);
|
|
5172
|
+
return ok({ key, added: newEntry, entries: updated });
|
|
5173
|
+
}
|
|
5174
|
+
if (action === "update") {
|
|
5175
|
+
if (!key) return fail("key is required for update");
|
|
5176
|
+
const id = normalizeMetadataId(args["id"]);
|
|
5177
|
+
if (!id) return fail("id is required for update");
|
|
5178
|
+
const current = entries[key];
|
|
5179
|
+
if (!current) return fail(`Key "${key}" not found`);
|
|
5180
|
+
const idx = current.findIndex((e) => e.id === id);
|
|
5181
|
+
if (idx === -1) return fail(`Item "${id}" not found in "${key}"`);
|
|
5182
|
+
const description = args["description"];
|
|
5183
|
+
const status = args["status"];
|
|
5184
|
+
if (typeof description !== "string" && typeof status !== "string") {
|
|
5185
|
+
return fail("provide at least one of description or status for update");
|
|
5186
|
+
}
|
|
5187
|
+
const updated = current.map(
|
|
5188
|
+
(e, i) => i === idx ? {
|
|
5189
|
+
...e,
|
|
5190
|
+
...typeof description === "string" ? { description } : {},
|
|
5191
|
+
...typeof status === "string" ? { status } : {}
|
|
5192
|
+
} : e
|
|
5193
|
+
);
|
|
5194
|
+
deps.setMetadataEntries(session.id, key, updated);
|
|
5195
|
+
return ok({ key, updated: id, entries: updated });
|
|
5196
|
+
}
|
|
5197
|
+
if (action === "remove") {
|
|
5198
|
+
if (!key) return fail("key is required for remove");
|
|
5199
|
+
const id = normalizeMetadataId(args["id"]);
|
|
5200
|
+
if (!id) return fail("id is required for remove");
|
|
5201
|
+
const current = entries[key];
|
|
5202
|
+
if (!current) return fail(`Key "${key}" not found`);
|
|
5203
|
+
const updated = current.filter((e) => e.id !== id);
|
|
5204
|
+
if (updated.length === current.length) return fail(`Item "${id}" not found in "${key}"`);
|
|
5205
|
+
deps.setMetadataEntries(session.id, key, updated);
|
|
5206
|
+
return ok({ key, removed: id, entries: updated });
|
|
5207
|
+
}
|
|
5208
|
+
return fail(`Unknown action "${String(action)}". Must be one of: get, list, add, update, remove, schema`);
|
|
5209
|
+
}
|
|
5210
|
+
},
|
|
5211
|
+
{
|
|
5212
|
+
name: "openfox_send_message",
|
|
5213
|
+
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.",
|
|
5214
|
+
inputSchema: {
|
|
5215
|
+
sessionId: z.string().describe("Session to message"),
|
|
5216
|
+
content: z.string().optional().describe("Message content"),
|
|
5217
|
+
attachments: z.array(z.record(z.string(), z.unknown())).optional().describe("Optional attachments"),
|
|
5218
|
+
messageKind: z.string().optional().describe("Optional message kind tag")
|
|
5219
|
+
},
|
|
5220
|
+
handler: async (args) => {
|
|
5221
|
+
const check = requireSession(args["sessionId"]);
|
|
5222
|
+
if (!check.ok) return fail(check.error);
|
|
5223
|
+
const content = typeof args["content"] === "string" ? args["content"] : void 0;
|
|
5224
|
+
const rawAttachments = args["attachments"];
|
|
5225
|
+
const attachments = Array.isArray(rawAttachments) ? rawAttachments : void 0;
|
|
5226
|
+
const messageKind = typeof args["messageKind"] === "string" ? args["messageKind"] : void 0;
|
|
5227
|
+
if (!content?.trim() && !(attachments && attachments.length > 0)) {
|
|
5228
|
+
return fail("content or attachments is required");
|
|
5229
|
+
}
|
|
5230
|
+
sessionManager.queueMessage(check.session.id, "asap", content, attachments, messageKind);
|
|
5231
|
+
return ok({ queued: true, queueState: sessionManager.getQueueState(check.session.id) });
|
|
5232
|
+
}
|
|
5233
|
+
},
|
|
5234
|
+
{
|
|
5235
|
+
name: "openfox_continue",
|
|
5236
|
+
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).",
|
|
5237
|
+
inputSchema: {
|
|
5238
|
+
sessionId: z.string().describe("Session to continue")
|
|
5239
|
+
},
|
|
5240
|
+
handler: async (args) => {
|
|
5241
|
+
const check = requireSessionAndId(args);
|
|
5242
|
+
if (!check.ok) return fail(check.error);
|
|
5243
|
+
const { session, sessionId } = check;
|
|
5244
|
+
if (session.isRunning) {
|
|
5245
|
+
return fail(`Session ${sessionId} is already running`);
|
|
5246
|
+
}
|
|
5247
|
+
if (session.phase === "blocked") {
|
|
5248
|
+
sessionManager.setPhase(sessionId, "build");
|
|
5249
|
+
sessionManager.queueMessage(sessionId, "asap", CONTINUE_PROMPT, void 0, "auto-prompt");
|
|
5250
|
+
return ok({ accepted: true });
|
|
5251
|
+
}
|
|
5252
|
+
return ok({
|
|
5253
|
+
accepted: true,
|
|
5254
|
+
note: `Session ${sessionId} is idle \u2014 use openfox_send_message to give it work.`
|
|
5255
|
+
});
|
|
5256
|
+
}
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
name: "openfox_wait",
|
|
5260
|
+
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.",
|
|
5261
|
+
inputSchema: {
|
|
5262
|
+
sessionId: z.string().describe("Session to wait on"),
|
|
5263
|
+
timeout: z.number().int().positive().optional().describe("Maximum wait in seconds (default 60, clamped 5\u2013600)")
|
|
5264
|
+
},
|
|
5265
|
+
handler: async (args) => {
|
|
5266
|
+
const check = requireSession(args["sessionId"]);
|
|
5267
|
+
if (!check.ok) return fail(check.error);
|
|
5268
|
+
const sessionId = check.session.id;
|
|
5269
|
+
const timeoutSeconds = resolveWaitTimeout(args["timeout"]);
|
|
5270
|
+
const startedAt = Date.now();
|
|
5271
|
+
const deadline = startedAt + timeoutSeconds * 1e3;
|
|
5272
|
+
const graceUntil = startedAt + WAIT_IDLE_GRACE_MS;
|
|
5273
|
+
let sawRunning = false;
|
|
5274
|
+
while (true) {
|
|
5275
|
+
const session = sessionManager.getSession(sessionId);
|
|
5276
|
+
if (!session) return fail(`Session not found: ${sessionId}`);
|
|
5277
|
+
if (session.isRunning) sawRunning = true;
|
|
5278
|
+
const pendingQuestions = deps.pendingQuestions(sessionId);
|
|
5279
|
+
const pendingConfirmations = deps.pendingConfirmations(sessionId);
|
|
5280
|
+
const activeExecution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
5281
|
+
const now = Date.now();
|
|
5282
|
+
const hasWork = (session.messages?.length ?? 0) > 0;
|
|
5283
|
+
const completedEvidence = sawRunning || hasWork && now >= graceUntil;
|
|
5284
|
+
const settlement = computeSettlement(
|
|
5285
|
+
session,
|
|
5286
|
+
pendingQuestions.length,
|
|
5287
|
+
pendingConfirmations.length,
|
|
5288
|
+
activeExecution?.status ?? null,
|
|
5289
|
+
completedEvidence
|
|
5290
|
+
);
|
|
5291
|
+
if (settlement.settled) {
|
|
5292
|
+
return ok({
|
|
5293
|
+
settled: true,
|
|
5294
|
+
outcome: settlement.outcome,
|
|
5295
|
+
waitedMs: now - startedAt,
|
|
5296
|
+
status: buildSessionStatusPayload(sessionId, deps)
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
if (now >= deadline) {
|
|
5300
|
+
return ok({
|
|
5301
|
+
settled: false,
|
|
5302
|
+
timedOut: true,
|
|
5303
|
+
waitedMs: now - startedAt,
|
|
5304
|
+
status: buildSessionStatusPayload(sessionId, deps)
|
|
5305
|
+
});
|
|
5306
|
+
}
|
|
5307
|
+
await sleep2(WAIT_POLL_INTERVAL_MS);
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
5310
|
+
},
|
|
5311
|
+
{
|
|
5312
|
+
name: "openfox_stop",
|
|
5313
|
+
description: "Stop a session completely: drains the queue, aborts the active execution, and cancels pending interactions.",
|
|
5314
|
+
inputSchema: {
|
|
5315
|
+
sessionId: z.string().describe("Session to stop")
|
|
5316
|
+
},
|
|
5317
|
+
handler: async (args) => {
|
|
5318
|
+
const check = requireSession(args["sessionId"]);
|
|
5319
|
+
if (!check.ok) return fail(check.error);
|
|
5320
|
+
deps.stopSession(check.session.id);
|
|
5321
|
+
return ok({ stopped: true });
|
|
5322
|
+
}
|
|
5323
|
+
},
|
|
5324
|
+
{
|
|
5325
|
+
name: "openfox_workflows",
|
|
5326
|
+
description: "List the workflows available to a project (builtin, user, and project scope) with their parameters.",
|
|
5327
|
+
inputSchema: {
|
|
5328
|
+
projectDir: z.string().optional().describe("Project directory for project-scoped workflows")
|
|
5329
|
+
},
|
|
5330
|
+
handler: async (args) => {
|
|
5331
|
+
const projectDir = typeof args["projectDir"] === "string" ? args["projectDir"] : void 0;
|
|
5332
|
+
return ok(await deps.listWorkflows(projectDir));
|
|
5333
|
+
}
|
|
5334
|
+
},
|
|
5335
|
+
{
|
|
5336
|
+
name: "openfox_launch_workflow",
|
|
5337
|
+
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.",
|
|
5338
|
+
inputSchema: {
|
|
5339
|
+
sessionId: z.string().describe("Session to run the workflow in"),
|
|
5340
|
+
workflowId: z.string().optional().describe("Workflow to run (default: the active workflow)"),
|
|
5341
|
+
params: z.record(z.string(), z.string()).optional().describe("Workflow parameters"),
|
|
5342
|
+
subGroup: z.string().optional().describe("Run only a sub-group slice of the workflow"),
|
|
5343
|
+
scope: z.enum(["builtin", "user", "project"]).optional().describe("Workflow scope to resolve from"),
|
|
5344
|
+
resumeFrom: z.string().optional().describe("Step id to resume a paused workflow from"),
|
|
5345
|
+
stepOutput: z.record(z.string(), z.string()).optional().describe("Step output supplied when resuming"),
|
|
5346
|
+
userChoice: z.string().optional().describe("Branch chosen by the user at a paused user step"),
|
|
5347
|
+
content: z.string().optional().describe("Optional extra instruction for the run")
|
|
5348
|
+
},
|
|
5349
|
+
handler: async (args) => {
|
|
5350
|
+
const check = requireSessionAndId(args);
|
|
5351
|
+
if (!check.ok) return fail(check.error);
|
|
5352
|
+
const { session, sessionId } = check;
|
|
5353
|
+
const payload = {
|
|
5354
|
+
...typeof args["workflowId"] === "string" ? { workflowId: args["workflowId"] } : {},
|
|
5355
|
+
...args["params"] && typeof args["params"] === "object" ? { params: args["params"] } : {},
|
|
5356
|
+
...typeof args["subGroup"] === "string" ? { subGroup: args["subGroup"] } : {},
|
|
5357
|
+
...typeof args["scope"] === "string" ? { scope: args["scope"] } : {},
|
|
5358
|
+
...typeof args["resumeFrom"] === "string" ? { resumeFrom: args["resumeFrom"] } : {},
|
|
5359
|
+
...args["stepOutput"] && typeof args["stepOutput"] === "object" ? { stepOutput: args["stepOutput"] } : {},
|
|
5360
|
+
...typeof args["userChoice"] === "string" ? { userChoice: args["userChoice"] } : {},
|
|
5361
|
+
...typeof args["content"] === "string" ? { content: args["content"] } : {}
|
|
5362
|
+
};
|
|
5363
|
+
if (payload.workflowId && !payload.resumeFrom) {
|
|
5364
|
+
const workflows = await deps.listWorkflows(session.workdir);
|
|
5365
|
+
const known = workflows.some((w) => w.id === payload.workflowId);
|
|
5366
|
+
if (!known) {
|
|
5367
|
+
return fail(`Workflow "${payload.workflowId}" not found for project ${session.workdir}`);
|
|
5368
|
+
}
|
|
5369
|
+
}
|
|
5370
|
+
if (session.isRunning) {
|
|
5371
|
+
let fullContent = payload.content ?? "";
|
|
5372
|
+
if (payload.workflowId) {
|
|
5373
|
+
const workflowInfo = `// Workflow: ${payload.workflowId}`;
|
|
5374
|
+
fullContent = fullContent ? `${workflowInfo}
|
|
5375
|
+
|
|
5376
|
+
${fullContent}` : workflowInfo;
|
|
5377
|
+
}
|
|
5378
|
+
if (!fullContent) {
|
|
5379
|
+
return fail("Session is running \u2014 give a workflowId or content to queue for the next turn boundary.");
|
|
5380
|
+
}
|
|
5381
|
+
sessionManager.queueMessage(sessionId, "asap", fullContent, void 0, "workflow-launch");
|
|
5382
|
+
return ok({ queued: true, queueState: sessionManager.getQueueState(sessionId) });
|
|
5383
|
+
}
|
|
5384
|
+
if (session.phase === "blocked") {
|
|
5385
|
+
sessionManager.setPhase(sessionId, "build");
|
|
5386
|
+
}
|
|
5387
|
+
deps.launchWorkflow(sessionId, payload);
|
|
5388
|
+
return ok({ launched: true });
|
|
5389
|
+
}
|
|
5390
|
+
},
|
|
5391
|
+
{
|
|
5392
|
+
name: "openfox_resume_workflow",
|
|
5393
|
+
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.',
|
|
5394
|
+
inputSchema: {
|
|
5395
|
+
sessionId: z.string().describe("Session paused at a workflow user step"),
|
|
5396
|
+
choice: z.string().describe("One of the pendingChoices surfaced by openfox_session_status (id or label)")
|
|
5397
|
+
},
|
|
5398
|
+
handler: async (args) => {
|
|
5399
|
+
const check = requireSession(args["sessionId"]);
|
|
5400
|
+
if (!check.ok) return fail(check.error);
|
|
5401
|
+
const sessionId = check.session.id;
|
|
5402
|
+
const choice = args["choice"];
|
|
5403
|
+
if (typeof choice !== "string" || choice.trim().length === 0) {
|
|
5404
|
+
return fail("choice is required \u2014 pick one of the pendingChoices surfaced by openfox_session_status");
|
|
5405
|
+
}
|
|
5406
|
+
const execution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
5407
|
+
if (!execution || execution.status !== "waiting" || !execution.currentStepId) {
|
|
5408
|
+
return fail(`Session ${sessionId} is not paused at a workflow user step`);
|
|
5409
|
+
}
|
|
5410
|
+
const pendingChoices = execution.pendingChoices ?? [];
|
|
5411
|
+
if (pendingChoices.length === 0) {
|
|
5412
|
+
return fail(`No choices available for paused step "${execution.currentStepName ?? execution.currentStepId}"`);
|
|
5413
|
+
}
|
|
5414
|
+
const match = pendingChoices.find((c) => c.id === choice || c.label === choice);
|
|
5415
|
+
if (!match) {
|
|
5416
|
+
return fail(`Invalid choice "${choice}". Available: ${pendingChoices.map((c) => c.id).join(", ")}`);
|
|
5417
|
+
}
|
|
5418
|
+
deps.launchWorkflow(sessionId, {
|
|
5419
|
+
workflowId: execution.workflowId,
|
|
5420
|
+
resumeFrom: execution.currentStepId,
|
|
5421
|
+
userChoice: match.id,
|
|
5422
|
+
stepOutput: execution.stepOutput
|
|
5423
|
+
});
|
|
5424
|
+
return ok({
|
|
5425
|
+
resumed: true,
|
|
5426
|
+
choice: match.id,
|
|
5427
|
+
step: execution.currentStepName ?? execution.currentStepId
|
|
5428
|
+
});
|
|
5429
|
+
}
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
name: "openfox_stop_workflow",
|
|
5433
|
+
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.",
|
|
5434
|
+
inputSchema: {
|
|
5435
|
+
sessionId: z.string().describe("Session whose workflow run should be aborted")
|
|
5436
|
+
},
|
|
5437
|
+
handler: async (args) => {
|
|
5438
|
+
const check = requireSession(args["sessionId"]);
|
|
5439
|
+
if (!check.ok) return fail(check.error);
|
|
5440
|
+
const stopped = deps.stopWorkflow(check.session.id);
|
|
5441
|
+
if (!stopped) return fail("No active workflow run to stop");
|
|
5442
|
+
return ok({ stopped: true, aborted: stopped.aborted });
|
|
5443
|
+
}
|
|
5444
|
+
},
|
|
5445
|
+
{
|
|
5446
|
+
name: "openfox_answer",
|
|
5447
|
+
description: "Answer or skip a pending ask_user question of a session by callId. Without a callId, lists the session pending questions.",
|
|
5448
|
+
inputSchema: {
|
|
5449
|
+
sessionId: z.string().describe("Session the question belongs to"),
|
|
5450
|
+
callId: z.string().optional().describe("Pending question to answer (omit to list)"),
|
|
5451
|
+
answer: z.string().optional().describe("The answer to provide"),
|
|
5452
|
+
skip: z.boolean().optional().describe("Skip the question instead of answering it")
|
|
5453
|
+
},
|
|
5454
|
+
handler: async (args) => {
|
|
5455
|
+
const check = requireSession(args["sessionId"]);
|
|
5456
|
+
if (!check.ok) return fail(check.error);
|
|
5457
|
+
const session = check.session;
|
|
5458
|
+
const pending = deps.pendingQuestions(session.id);
|
|
5459
|
+
const resolved = resolvePendingCallId(session.id, args["callId"], pending, "question");
|
|
5460
|
+
if (resolved.kind === "list") return ok({ questions: pending });
|
|
5461
|
+
if (resolved.kind === "error") return fail(resolved.error);
|
|
5462
|
+
const callId = resolved.callId;
|
|
5463
|
+
const skip = args["skip"] === true;
|
|
5464
|
+
const answer = typeof args["answer"] === "string" ? args["answer"] : "";
|
|
5465
|
+
if (!skip && answer.length === 0) {
|
|
5466
|
+
return fail("answer is required unless skip is true");
|
|
5467
|
+
}
|
|
5468
|
+
const found = deps.answerQuestion(callId, skip ? "" : answer, skip || void 0);
|
|
5469
|
+
if (!found) {
|
|
5470
|
+
return fail(`Question ${callId} is no longer pending`);
|
|
5471
|
+
}
|
|
5472
|
+
return ok({ answered: true, callId, skipped: skip || void 0 });
|
|
5473
|
+
}
|
|
5474
|
+
},
|
|
5475
|
+
{
|
|
5476
|
+
name: "openfox_confirm",
|
|
5477
|
+
description: "Resolve a pending path confirmation of a session (approve/deny, optionally always-allow the paths). Without a callId, lists the session pending confirmations.",
|
|
5478
|
+
inputSchema: {
|
|
5479
|
+
sessionId: z.string().describe("Session the confirmation belongs to"),
|
|
5480
|
+
callId: z.string().optional().describe("Pending confirmation to resolve (omit to list)"),
|
|
5481
|
+
approved: z.boolean().optional().describe("Approve or deny the requested access"),
|
|
5482
|
+
alwaysAllow: z.boolean().optional().describe("Permanently allow the paths for this session")
|
|
5483
|
+
},
|
|
5484
|
+
handler: async (args) => {
|
|
5485
|
+
const check = requireSession(args["sessionId"]);
|
|
5486
|
+
if (!check.ok) return fail(check.error);
|
|
5487
|
+
const session = check.session;
|
|
5488
|
+
const pending = deps.pendingConfirmations(session.id);
|
|
5489
|
+
const resolved = resolvePendingCallId(session.id, args["callId"], pending, "confirmation");
|
|
5490
|
+
if (resolved.kind === "list") return ok({ confirmations: pending });
|
|
5491
|
+
if (resolved.kind === "error") return fail(resolved.error);
|
|
5492
|
+
const callId = resolved.callId;
|
|
5493
|
+
const approved = args["approved"];
|
|
5494
|
+
if (typeof approved !== "boolean") {
|
|
5495
|
+
return fail("approved (boolean) is required");
|
|
5496
|
+
}
|
|
5497
|
+
const alwaysAllow = args["alwaysAllow"];
|
|
5498
|
+
const found = deps.confirmPath(callId, approved, alwaysAllow);
|
|
5499
|
+
if (!found) {
|
|
5500
|
+
return fail(`Confirmation ${callId} is no longer pending`);
|
|
5501
|
+
}
|
|
5502
|
+
return ok({ confirmed: true, callId, approved });
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
];
|
|
5506
|
+
return tools;
|
|
5507
|
+
}
|
|
5508
|
+
|
|
5509
|
+
// src/server/mcp/server/endpoint.ts
|
|
5510
|
+
function buildOpenFoxMcpServer(deps) {
|
|
5511
|
+
const server = new McpServer({
|
|
5512
|
+
name: "openfox",
|
|
5513
|
+
version: "1.0.0"
|
|
5514
|
+
});
|
|
5515
|
+
for (const tool of createOpenFoxMcpTools(deps)) {
|
|
5516
|
+
const config3 = { description: tool.description };
|
|
5517
|
+
if (Object.keys(tool.inputSchema).length > 0) {
|
|
5518
|
+
config3["inputSchema"] = tool.inputSchema;
|
|
5519
|
+
}
|
|
5520
|
+
server.registerTool(
|
|
5521
|
+
tool.name,
|
|
5522
|
+
config3,
|
|
5523
|
+
async (args) => tool.handler(args ?? {})
|
|
5524
|
+
);
|
|
5525
|
+
}
|
|
5526
|
+
return server;
|
|
5527
|
+
}
|
|
5528
|
+
function createOpenFoxMcpRouter(options) {
|
|
4754
5529
|
const router = Router4();
|
|
5530
|
+
router.all("/", async (req, res) => {
|
|
5531
|
+
try {
|
|
5532
|
+
if (options.isAuthRequired() && !await options.isAuthorized(req)) {
|
|
5533
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
5534
|
+
return;
|
|
5535
|
+
}
|
|
5536
|
+
const deps = options.resolveDeps();
|
|
5537
|
+
if (!deps) {
|
|
5538
|
+
res.status(503).json({ error: "MCP server is not ready" });
|
|
5539
|
+
return;
|
|
5540
|
+
}
|
|
5541
|
+
const server = buildOpenFoxMcpServer(deps);
|
|
5542
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
5543
|
+
await server.connect(transport);
|
|
5544
|
+
await transport.handleRequest(req, res, req.body);
|
|
5545
|
+
} catch (error) {
|
|
5546
|
+
logger.error("MCP endpoint request failed", {
|
|
5547
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5548
|
+
});
|
|
5549
|
+
if (!res.headersSent) {
|
|
5550
|
+
res.status(500).json({ error: "MCP request failed" });
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5553
|
+
});
|
|
5554
|
+
return router;
|
|
5555
|
+
}
|
|
5556
|
+
function extractSessionToken(req) {
|
|
5557
|
+
const header = req.headers["x-session-token"];
|
|
5558
|
+
if (typeof header === "string" && header.length > 0) {
|
|
5559
|
+
return header;
|
|
5560
|
+
}
|
|
5561
|
+
const authorization = req.headers["authorization"];
|
|
5562
|
+
if (typeof authorization === "string" && authorization.startsWith("Bearer ")) {
|
|
5563
|
+
const token = authorization.slice("Bearer ".length).trim();
|
|
5564
|
+
return token.length > 0 ? token : null;
|
|
5565
|
+
}
|
|
5566
|
+
return null;
|
|
5567
|
+
}
|
|
5568
|
+
|
|
5569
|
+
// src/server/routes/dev-server.ts
|
|
5570
|
+
import { Router as Router5 } from "express";
|
|
5571
|
+
function createDevServerRoutes() {
|
|
5572
|
+
const router = Router5();
|
|
4755
5573
|
router.get("/", async (req, res) => {
|
|
4756
5574
|
const workdir = req.query["workdir"];
|
|
4757
5575
|
if (!workdir) return res.status(400).json({ error: "workdir required" });
|
|
@@ -4849,7 +5667,7 @@ function createDevServerRoutes() {
|
|
|
4849
5667
|
}
|
|
4850
5668
|
|
|
4851
5669
|
// src/server/routes/workspace-config.ts
|
|
4852
|
-
import { Router as
|
|
5670
|
+
import { Router as Router6 } from "express";
|
|
4853
5671
|
import { stat as stat2, mkdir as mkdir3, readdir, access as access3 } from "fs/promises";
|
|
4854
5672
|
import { constants as constants3 } from "fs";
|
|
4855
5673
|
import { resolve as resolve4, isAbsolute as isAbsolute3, join as join5, win32 } from "path";
|
|
@@ -4946,7 +5764,7 @@ async function findOrphanedWorkspaces(dir) {
|
|
|
4946
5764
|
return results;
|
|
4947
5765
|
}
|
|
4948
5766
|
function createWorkspaceConfigRoutes(sessionManager) {
|
|
4949
|
-
const router =
|
|
5767
|
+
const router = Router6();
|
|
4950
5768
|
router.get("/config", async (req, res) => {
|
|
4951
5769
|
const workdir = req.query["workdir"];
|
|
4952
5770
|
if (!workdir) return res.status(400).json({ error: "workdir required" });
|
|
@@ -5080,9 +5898,9 @@ function createWorkspaceConfigRoutes(sessionManager) {
|
|
|
5080
5898
|
}
|
|
5081
5899
|
|
|
5082
5900
|
// src/server/routes/terminals.ts
|
|
5083
|
-
import { Router as
|
|
5901
|
+
import { Router as Router7 } from "express";
|
|
5084
5902
|
function createTerminalRoutes() {
|
|
5085
|
-
const router =
|
|
5903
|
+
const router = Router7();
|
|
5086
5904
|
router.get("/", (req, res) => {
|
|
5087
5905
|
const projectId = req.query["projectId"];
|
|
5088
5906
|
const sessions = projectId ? terminalManager.getByProject(projectId) : [];
|
|
@@ -5121,11 +5939,11 @@ function createTerminalRoutes() {
|
|
|
5121
5939
|
}
|
|
5122
5940
|
|
|
5123
5941
|
// src/server/routes/directories.ts
|
|
5124
|
-
import { Router as
|
|
5942
|
+
import { Router as Router8 } from "express";
|
|
5125
5943
|
import { readdir as readdir2 } from "fs/promises";
|
|
5126
5944
|
import { resolve as resolve5, join as join6, dirname as dirname4, basename as basename2 } from "path";
|
|
5127
5945
|
function createDirectoryRoutes() {
|
|
5128
|
-
const router =
|
|
5946
|
+
const router = Router8();
|
|
5129
5947
|
const DEFAULT_BASE_PATH = process.cwd();
|
|
5130
5948
|
router.get("/", async (req, res) => {
|
|
5131
5949
|
const path = req.query["path"] || DEFAULT_BASE_PATH;
|
|
@@ -5160,7 +5978,7 @@ function createDirectoryRoutes() {
|
|
|
5160
5978
|
}
|
|
5161
5979
|
|
|
5162
5980
|
// src/server/routes/file-search.ts
|
|
5163
|
-
import { Router as
|
|
5981
|
+
import { Router as Router9 } from "express";
|
|
5164
5982
|
import fg from "fast-glob";
|
|
5165
5983
|
var IGNORED_PATTERNS = [
|
|
5166
5984
|
"**/node_modules/**",
|
|
@@ -5173,7 +5991,7 @@ var IGNORED_PATTERNS = [
|
|
|
5173
5991
|
"**/.openfox/**"
|
|
5174
5992
|
];
|
|
5175
5993
|
function createFileSearchRoutes() {
|
|
5176
|
-
const router =
|
|
5994
|
+
const router = Router9();
|
|
5177
5995
|
router.get("/", async (req, res) => {
|
|
5178
5996
|
const query = req.query["q"] || "";
|
|
5179
5997
|
const workdir = req.query["workdir"] || process.cwd();
|
|
@@ -5265,7 +6083,7 @@ function fuzzyScore(filePath, query) {
|
|
|
5265
6083
|
}
|
|
5266
6084
|
|
|
5267
6085
|
// src/server/routes/auto-update.ts
|
|
5268
|
-
import { Router as
|
|
6086
|
+
import { Router as Router10 } from "express";
|
|
5269
6087
|
import { spawn as spawn2 } from "child_process";
|
|
5270
6088
|
var updateInProgress = false;
|
|
5271
6089
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -5362,7 +6180,7 @@ async function getLatestDevelopVersion() {
|
|
|
5362
6180
|
});
|
|
5363
6181
|
}
|
|
5364
6182
|
function createAutoUpdateRoutes(options = {}) {
|
|
5365
|
-
const router =
|
|
6183
|
+
const router = Router10();
|
|
5366
6184
|
router.get("/check", async (req, res) => {
|
|
5367
6185
|
const isService = isRunningAsService();
|
|
5368
6186
|
const isDev = isDevMode();
|
|
@@ -5479,9 +6297,9 @@ function createAutoUpdateRoutes(options = {}) {
|
|
|
5479
6297
|
}
|
|
5480
6298
|
|
|
5481
6299
|
// src/server/routes/provider-auth.ts
|
|
5482
|
-
import { Router as
|
|
6300
|
+
import { Router as Router11 } from "express";
|
|
5483
6301
|
function createProviderAuthRoutes(config3, providerManager, registry) {
|
|
5484
|
-
const router =
|
|
6302
|
+
const router = Router11();
|
|
5485
6303
|
function resolveAuthContext(providerId, res) {
|
|
5486
6304
|
const provider = providerManager.getProviders().find((item) => item.id === providerId);
|
|
5487
6305
|
if (!provider) {
|
|
@@ -5710,7 +6528,7 @@ async function loadProviderPlugins(options) {
|
|
|
5710
6528
|
}
|
|
5711
6529
|
|
|
5712
6530
|
// src/server/routes/plugins.ts
|
|
5713
|
-
import { Router as
|
|
6531
|
+
import { Router as Router12 } from "express";
|
|
5714
6532
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL2 } from "url";
|
|
5715
6533
|
import { dirname as dirname6, resolve as resolve7, join as join8 } from "path";
|
|
5716
6534
|
import { existsSync } from "fs";
|
|
@@ -5727,7 +6545,7 @@ async function openFolderRoute(dir, res) {
|
|
|
5727
6545
|
}
|
|
5728
6546
|
}
|
|
5729
6547
|
function createPluginRoutes(options) {
|
|
5730
|
-
const router =
|
|
6548
|
+
const router = Router12();
|
|
5731
6549
|
const { config: config3, providerAdapters, pluginDiagnostics, logger: logger2 } = options;
|
|
5732
6550
|
let registryCache = null;
|
|
5733
6551
|
router.get("/registry", async (_req, res) => {
|
|
@@ -5968,6 +6786,10 @@ function detectWsl() {
|
|
|
5968
6786
|
|
|
5969
6787
|
// src/server/index.ts
|
|
5970
6788
|
var __dirname3 = dirname7(fileURLToPath6(import.meta.url));
|
|
6789
|
+
async function announceContextDrift(sessionManager, sessionIds) {
|
|
6790
|
+
const { injectContextDriftRemindersForSessions } = await import("./dynamic-context-2VLACYK4.js");
|
|
6791
|
+
await injectContextDriftRemindersForSessions(sessionManager, sessionIds);
|
|
6792
|
+
}
|
|
5971
6793
|
async function createServerHandle(config3) {
|
|
5972
6794
|
setRuntimeConfig(config3);
|
|
5973
6795
|
setLogLevel(config3.logging?.level ?? void 0, config3.mode);
|
|
@@ -6072,7 +6894,7 @@ async function createServerHandle(config3) {
|
|
|
6072
6894
|
toolRegistry = createToolRegistry();
|
|
6073
6895
|
logger.info("MCP tools registered", { count: mcpTools.length });
|
|
6074
6896
|
}
|
|
6075
|
-
const { signalMcpReady } = await import("./server-
|
|
6897
|
+
const { signalMcpReady } = await import("./server-RPKVKMDD.js");
|
|
6076
6898
|
signalMcpReady();
|
|
6077
6899
|
});
|
|
6078
6900
|
const app = express();
|
|
@@ -6095,6 +6917,25 @@ async function createServerHandle(config3) {
|
|
|
6095
6917
|
};
|
|
6096
6918
|
app.use("/api", authMiddleware);
|
|
6097
6919
|
app.use(express.json({ limit: "75mb" }));
|
|
6920
|
+
let mcpToolDeps = null;
|
|
6921
|
+
const mcpAuth = {
|
|
6922
|
+
isAuthRequired: () => {
|
|
6923
|
+
const cfg = getAuthConfig();
|
|
6924
|
+
return cfg?.strategy === "network" && cfg.encryptedPassword != null;
|
|
6925
|
+
},
|
|
6926
|
+
isAuthorized: async (req) => {
|
|
6927
|
+
const token = extractSessionToken(req);
|
|
6928
|
+
return token ? isValidToken(token) : false;
|
|
6929
|
+
}
|
|
6930
|
+
};
|
|
6931
|
+
app.use(
|
|
6932
|
+
"/mcp",
|
|
6933
|
+
createOpenFoxMcpRouter({
|
|
6934
|
+
resolveDeps: () => mcpToolDeps,
|
|
6935
|
+
isAuthRequired: mcpAuth.isAuthRequired,
|
|
6936
|
+
isAuthorized: mcpAuth.isAuthorized
|
|
6937
|
+
})
|
|
6938
|
+
);
|
|
6098
6939
|
app.get("/api/health", (_req, res) => {
|
|
6099
6940
|
res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6100
6941
|
});
|
|
@@ -6245,12 +7086,12 @@ async function createServerHandle(config3) {
|
|
|
6245
7086
|
res.json({ project: updated });
|
|
6246
7087
|
});
|
|
6247
7088
|
app.delete("/api/projects/:id", async (req, res) => {
|
|
6248
|
-
const { getProject: getProject2, deleteProject } = await import("./projects-TPSLCJP2.js");
|
|
7089
|
+
const { getProject: getProject2, deleteProject: deleteProject2 } = await import("./projects-TPSLCJP2.js");
|
|
6249
7090
|
const project = getProject2(req.params.id);
|
|
6250
7091
|
if (!project) {
|
|
6251
7092
|
return res.status(404).json({ error: "Project not found" });
|
|
6252
7093
|
}
|
|
6253
|
-
|
|
7094
|
+
deleteProject2(req.params.id);
|
|
6254
7095
|
res.json({ success: true });
|
|
6255
7096
|
});
|
|
6256
7097
|
app.put("/api/projects/:id/star", async (req, res) => {
|
|
@@ -6268,9 +7109,9 @@ async function createServerHandle(config3) {
|
|
|
6268
7109
|
const sessionFavoriteRouter = express.Router();
|
|
6269
7110
|
registerSessionFavoriteRoute(sessionFavoriteRouter, sessionManager);
|
|
6270
7111
|
app.use("/api", sessionFavoriteRouter);
|
|
6271
|
-
const { createTasksService } = await import("./service-
|
|
6272
|
-
const { registerTaskRoutes } = await import("./tasks-
|
|
6273
|
-
const { setTasksService } = await import("./tools-
|
|
7112
|
+
const { createTasksService } = await import("./service-7TLOSFAO.js");
|
|
7113
|
+
const { registerTaskRoutes } = await import("./tasks-FA6Z52H2.js");
|
|
7114
|
+
const { setTasksService } = await import("./tools-SSPC3UQZ.js");
|
|
6274
7115
|
const tasksService = createTasksService({
|
|
6275
7116
|
sessionManager,
|
|
6276
7117
|
config: config3,
|
|
@@ -6302,7 +7143,7 @@ async function createServerHandle(config3) {
|
|
|
6302
7143
|
}
|
|
6303
7144
|
try {
|
|
6304
7145
|
await checkoutBranch(project.workdir, branch);
|
|
6305
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7146
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-LUVWWRFO.js");
|
|
6306
7147
|
const allSessions = sessionManager.listSessions();
|
|
6307
7148
|
for (const s of allSessions) {
|
|
6308
7149
|
if (sessionManager.getEffectiveWorkdir(s.id) === project.workdir) {
|
|
@@ -6338,7 +7179,7 @@ async function createServerHandle(config3) {
|
|
|
6338
7179
|
);
|
|
6339
7180
|
}
|
|
6340
7181
|
await createBranch(project.workdir, name, sb);
|
|
6341
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7182
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-LUVWWRFO.js");
|
|
6342
7183
|
const allSessions = sessionManager.listSessions();
|
|
6343
7184
|
for (const s of allSessions) {
|
|
6344
7185
|
if (sessionManager.getEffectiveWorkdir(s.id) === project.workdir) {
|
|
@@ -6370,7 +7211,7 @@ async function createServerHandle(config3) {
|
|
|
6370
7211
|
if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
|
|
6371
7212
|
const effectiveWorkdir = sessionManager.getEffectiveWorkdir(req.params.id);
|
|
6372
7213
|
const { checkoutBranch, validateRef: validateRef2 } = await import("./workspace-6CUE26YA.js");
|
|
6373
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7214
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-LUVWWRFO.js");
|
|
6374
7215
|
try {
|
|
6375
7216
|
await validateRef2(effectiveWorkdir, branch);
|
|
6376
7217
|
await checkoutBranch(effectiveWorkdir, branch);
|
|
@@ -6413,7 +7254,7 @@ All file and git operations should use this branch.
|
|
|
6413
7254
|
if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
|
|
6414
7255
|
const effectiveWorkdir = sessionManager.getEffectiveWorkdir(req.params.id);
|
|
6415
7256
|
const { validateRef: validateRef2 } = await import("./workspace-6CUE26YA.js");
|
|
6416
|
-
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-
|
|
7257
|
+
const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-LUVWWRFO.js");
|
|
6417
7258
|
try {
|
|
6418
7259
|
await validateRef2(effectiveWorkdir, name);
|
|
6419
7260
|
if (sourceBranch) {
|
|
@@ -6468,8 +7309,8 @@ All file and git operations should use this branch.
|
|
|
6468
7309
|
res.json({ workspaces: workspacesList });
|
|
6469
7310
|
});
|
|
6470
7311
|
app.get("/api/sessions", async (req, res) => {
|
|
6471
|
-
const { getRecentUserPromptsForSession } = await import("./events-
|
|
6472
|
-
const { getPendingConfirmationsBySession } = await import("./path-security-
|
|
7312
|
+
const { getRecentUserPromptsForSession } = await import("./events-D33QXGSX.js");
|
|
7313
|
+
const { getPendingConfirmationsBySession: getPendingConfirmationsBySession2 } = await import("./path-security-4WQJGU3Z.js");
|
|
6473
7314
|
const projectId = req.query["projectId"];
|
|
6474
7315
|
const rawLimit = req.query["limit"];
|
|
6475
7316
|
const offset = parseInt(req.query["offset"]) || 0;
|
|
@@ -6492,7 +7333,7 @@ All file and git operations should use this branch.
|
|
|
6492
7333
|
...session,
|
|
6493
7334
|
recentUserPrompts: getRecentUserPromptsForSession(session.id, 10)
|
|
6494
7335
|
}));
|
|
6495
|
-
const allPending =
|
|
7336
|
+
const allPending = getPendingConfirmationsBySession2();
|
|
6496
7337
|
const pendingConfirmationsBySession = {};
|
|
6497
7338
|
for (const s of sessions) {
|
|
6498
7339
|
const confs = allPending[s.id];
|
|
@@ -6524,7 +7365,7 @@ All file and git operations should use this branch.
|
|
|
6524
7365
|
disabledServers = mcpManager.getAllServers().filter((s) => s.config.disabled).map((s) => s.name);
|
|
6525
7366
|
}
|
|
6526
7367
|
if (disabledServers.length > 0) {
|
|
6527
|
-
const { setSessionDisabledServers: setSessionDisabledServers2 } = await import("./session-overrides-
|
|
7368
|
+
const { setSessionDisabledServers: setSessionDisabledServers2 } = await import("./session-overrides-U573CU2V.js");
|
|
6528
7369
|
setSessionDisabledServers2(session.id, disabledServers);
|
|
6529
7370
|
}
|
|
6530
7371
|
} catch {
|
|
@@ -6597,22 +7438,22 @@ All file and git operations should use this branch.
|
|
|
6597
7438
|
}
|
|
6598
7439
|
});
|
|
6599
7440
|
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-
|
|
7441
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEventsWithSnapshot2 } = await import("./events-D33QXGSX.js");
|
|
7442
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2, foldPendingConfirmations } = await import("./folding-QOKBKUU6.js");
|
|
7443
|
+
const { getPendingQuestionsForSession: getPendingQuestionsForSession2 } = await import("./tools-SSPC3UQZ.js");
|
|
7444
|
+
const { getMaxVisibleItems } = await import("./settings-LMAMF2X7.js");
|
|
6604
7445
|
const session = sessionManager.getSession(req.params.id);
|
|
6605
7446
|
if (!session) {
|
|
6606
7447
|
return res.status(404).json({ error: "Session not found" });
|
|
6607
7448
|
}
|
|
6608
7449
|
const eventStore = getEventStore2();
|
|
6609
7450
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(req.params.id);
|
|
6610
|
-
const events =
|
|
7451
|
+
const events = combineEventsWithSnapshot2(req.params.id, snapshot, eventsSinceSnapshot);
|
|
6611
7452
|
const maxVisibleItems = req.query["full"] === "true" ? void 0 : getMaxVisibleItems() || void 0;
|
|
6612
|
-
const { messages, hiddenCount } =
|
|
7453
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems);
|
|
6613
7454
|
const contextState = sessionManager.getContextState(req.params.id);
|
|
6614
7455
|
const queueState = sessionManager.getQueueState(req.params.id);
|
|
6615
|
-
const pendingQuestions =
|
|
7456
|
+
const pendingQuestions = getPendingQuestionsForSession2(req.params.id);
|
|
6616
7457
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
6617
7458
|
const activeWorkflowExecution = sessionManager.getDisplayWorkflowExecution(req.params.id);
|
|
6618
7459
|
res.json({
|
|
@@ -6627,10 +7468,10 @@ All file and git operations should use this branch.
|
|
|
6627
7468
|
});
|
|
6628
7469
|
});
|
|
6629
7470
|
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-
|
|
7471
|
+
const { projectSessionStatus: projectSessionStatus2 } = await import("./session-status-UFO5IFI7.js");
|
|
7472
|
+
const { getPendingQuestionsForSession: getPendingQuestionsForSession2 } = await import("./tools-SSPC3UQZ.js");
|
|
7473
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEventsWithSnapshot2 } = await import("./events-D33QXGSX.js");
|
|
7474
|
+
const { foldPendingConfirmations } = await import("./folding-QOKBKUU6.js");
|
|
6634
7475
|
const sessionId = req.params["id"];
|
|
6635
7476
|
if (!sessionId) {
|
|
6636
7477
|
return res.status(400).json({ error: "Session id is required" });
|
|
@@ -6641,12 +7482,12 @@ All file and git operations should use this branch.
|
|
|
6641
7482
|
}
|
|
6642
7483
|
const activeWorkflowExecution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
6643
7484
|
const activeWorkflowStepName = activeWorkflowExecution?.currentStepName ?? null;
|
|
6644
|
-
const pendingQuestions =
|
|
7485
|
+
const pendingQuestions = getPendingQuestionsForSession2(sessionId);
|
|
6645
7486
|
const eventStore = getEventStore2();
|
|
6646
7487
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6647
|
-
const events =
|
|
7488
|
+
const events = combineEventsWithSnapshot2(sessionId, snapshot, eventsSinceSnapshot);
|
|
6648
7489
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
6649
|
-
const status =
|
|
7490
|
+
const status = projectSessionStatus2({
|
|
6650
7491
|
session,
|
|
6651
7492
|
pendingQuestionsCount: pendingQuestions.length,
|
|
6652
7493
|
pendingConfirmationsCount: pendingConfirmations.length,
|
|
@@ -6660,8 +7501,8 @@ All file and git operations should use this branch.
|
|
|
6660
7501
|
if (!session) {
|
|
6661
7502
|
return res.status(404).json({ error: "Session not found" });
|
|
6662
7503
|
}
|
|
6663
|
-
const { stopSessionExecution } = await import("./chat-handler-
|
|
6664
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
7504
|
+
const { stopSessionExecution } = await import("./chat-handler-4WCKNCQV.js");
|
|
7505
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-SSPC3UQZ.js");
|
|
6665
7506
|
sessionManager.clearMessageQueue(sessionId);
|
|
6666
7507
|
stopSessionExecution(sessionId, sessionManager);
|
|
6667
7508
|
abortSession(sessionId);
|
|
@@ -6690,9 +7531,9 @@ All file and git operations should use this branch.
|
|
|
6690
7531
|
res.json({ success: true });
|
|
6691
7532
|
});
|
|
6692
7533
|
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-
|
|
7534
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-D33QXGSX.js");
|
|
7535
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QOKBKUU6.js");
|
|
7536
|
+
const { getMaxVisibleItems } = await import("./settings-LMAMF2X7.js");
|
|
6696
7537
|
const sessionId = req.params.id;
|
|
6697
7538
|
const session = sessionManager.getSession(sessionId);
|
|
6698
7539
|
if (!session) {
|
|
@@ -6709,19 +7550,20 @@ All file and git operations should use this branch.
|
|
|
6709
7550
|
const resolvedModel = model ?? provider?.models?.[0]?.id ?? "auto";
|
|
6710
7551
|
sessionManager.setSessionProvider(sessionId, providerId, resolvedModel, true, reasoningEffort);
|
|
6711
7552
|
sessionManager.setSessionProviderActive(sessionId, true);
|
|
7553
|
+
await announceContextDrift(sessionManager, [sessionId]);
|
|
6712
7554
|
const contextState = sessionManager.getContextState(sessionId);
|
|
6713
7555
|
const eventStore = getEventStore2();
|
|
6714
7556
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6715
7557
|
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
6716
7558
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6717
|
-
const { messages, hiddenCount } =
|
|
7559
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6718
7560
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
6719
7561
|
res.json({ session: toClientSession(updatedSession), messages, hiddenCount, contextState });
|
|
6720
7562
|
});
|
|
6721
7563
|
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-
|
|
7564
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-D33QXGSX.js");
|
|
7565
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QOKBKUU6.js");
|
|
7566
|
+
const { getMaxVisibleItems } = await import("./settings-LMAMF2X7.js");
|
|
6725
7567
|
const sessionId = req.params.id;
|
|
6726
7568
|
const session = sessionManager.getSession(sessionId);
|
|
6727
7569
|
if (!session) {
|
|
@@ -6729,11 +7571,12 @@ All file and git operations should use this branch.
|
|
|
6729
7571
|
}
|
|
6730
7572
|
sessionManager.setSessionProvider(sessionId, null, null, false, null);
|
|
6731
7573
|
sessionManager.setSessionProviderActive(sessionId, true);
|
|
7574
|
+
await announceContextDrift(sessionManager, [sessionId]);
|
|
6732
7575
|
const eventStore = getEventStore2();
|
|
6733
7576
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6734
7577
|
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
6735
7578
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6736
|
-
const { messages, hiddenCount } =
|
|
7579
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6737
7580
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
6738
7581
|
res.json({ session: toClientSession(updatedSession), messages, hiddenCount });
|
|
6739
7582
|
});
|
|
@@ -6850,9 +7693,9 @@ All file and git operations should use this branch.
|
|
|
6850
7693
|
res.json({ success: true });
|
|
6851
7694
|
});
|
|
6852
7695
|
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-
|
|
7696
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-D33QXGSX.js");
|
|
7697
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QOKBKUU6.js");
|
|
7698
|
+
const { getMaxVisibleItems } = await import("./settings-LMAMF2X7.js");
|
|
6856
7699
|
const sessionId = req.params.id;
|
|
6857
7700
|
const session = sessionManager.getSession(sessionId);
|
|
6858
7701
|
if (!session) {
|
|
@@ -6872,7 +7715,7 @@ All file and git operations should use this branch.
|
|
|
6872
7715
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6873
7716
|
const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
|
|
6874
7717
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6875
|
-
const { messages, hiddenCount } =
|
|
7718
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6876
7719
|
const updatedSession = sessionManager.getSession(sessionId);
|
|
6877
7720
|
res.json({ session: toClientSession(updatedSession), messages, hiddenCount });
|
|
6878
7721
|
});
|
|
@@ -6910,7 +7753,15 @@ All file and git operations should use this branch.
|
|
|
6910
7753
|
return res.status(400).json({ error: "disabledServers must be an array of strings" });
|
|
6911
7754
|
}
|
|
6912
7755
|
setSessionDisabledServers(sessionId, disabledServers);
|
|
6913
|
-
|
|
7756
|
+
const messages = session.messages ?? [];
|
|
7757
|
+
if (messages.length === 0) {
|
|
7758
|
+
const { applyDynamicContext } = await import("./dynamic-context-2VLACYK4.js");
|
|
7759
|
+
const modelName = session.providerModel ?? providerManager.getCurrentModel();
|
|
7760
|
+
await applyDynamicContext(sessionManager, sessionId, modelName);
|
|
7761
|
+
} else {
|
|
7762
|
+
sessionManager.setDynamicContextChanged(sessionId, true);
|
|
7763
|
+
}
|
|
7764
|
+
await announceContextDrift(sessionManager, [sessionId]);
|
|
6914
7765
|
const state = sessionManager.getContextState(sessionId);
|
|
6915
7766
|
wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
|
|
6916
7767
|
res.json({ disabledServers: getSessionDisabledServers(sessionId) });
|
|
@@ -6938,7 +7789,7 @@ All file and git operations should use this branch.
|
|
|
6938
7789
|
if (alwaysAllow !== void 0 && typeof alwaysAllow !== "boolean") {
|
|
6939
7790
|
return res.status(400).json({ error: "alwaysAllow must be a boolean if provided" });
|
|
6940
7791
|
}
|
|
6941
|
-
const { providePathConfirmation, getConfirmationSessionId } = await import("./tools-
|
|
7792
|
+
const { providePathConfirmation: providePathConfirmation2, getConfirmationSessionId } = await import("./tools-SSPC3UQZ.js");
|
|
6942
7793
|
const pendingSessionId = getConfirmationSessionId(callId);
|
|
6943
7794
|
if (!pendingSessionId) {
|
|
6944
7795
|
return res.status(404).json({ error: "No pending path confirmation with that ID" });
|
|
@@ -6946,22 +7797,22 @@ All file and git operations should use this branch.
|
|
|
6946
7797
|
if (pendingSessionId !== sessionId) {
|
|
6947
7798
|
return res.status(403).json({ error: "Confirmation does not belong to this session" });
|
|
6948
7799
|
}
|
|
6949
|
-
const result =
|
|
7800
|
+
const result = providePathConfirmation2(callId, approved, alwaysAllow);
|
|
6950
7801
|
if (!result.found) {
|
|
6951
7802
|
return res.status(404).json({ error: "No pending path confirmation with that ID" });
|
|
6952
7803
|
}
|
|
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-
|
|
7804
|
+
const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEvents } = await import("./events-D33QXGSX.js");
|
|
7805
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2, foldPendingConfirmations } = await import("./folding-QOKBKUU6.js");
|
|
7806
|
+
const { createSessionStateMessage } = await import("./protocol-Y7MRKRUO.js");
|
|
7807
|
+
const { getPendingQuestionsForSession: getPendingQuestionsForSession2 } = await import("./tools-SSPC3UQZ.js");
|
|
7808
|
+
const { getMaxVisibleItems } = await import("./settings-LMAMF2X7.js");
|
|
6958
7809
|
const eventStore = getEventStore2();
|
|
6959
7810
|
const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
6960
7811
|
const events = combineEvents(sessionId, snapshot, eventsSinceSnapshot);
|
|
6961
7812
|
const maxVisibleItems = getMaxVisibleItems();
|
|
6962
|
-
const { messages, hiddenCount } =
|
|
7813
|
+
const { messages, hiddenCount } = buildMessagesFromStoredEvents2(events, maxVisibleItems || void 0);
|
|
6963
7814
|
const pendingConfirmations = foldPendingConfirmations(events);
|
|
6964
|
-
const pendingQuestions =
|
|
7815
|
+
const pendingQuestions = getPendingQuestionsForSession2(sessionId);
|
|
6965
7816
|
const session = sessionManager.getSession(sessionId);
|
|
6966
7817
|
if (session) {
|
|
6967
7818
|
const stateMsg = createSessionStateMessage(
|
|
@@ -6991,8 +7842,8 @@ All file and git operations should use this branch.
|
|
|
6991
7842
|
if (!skip && typeof answer !== "string") {
|
|
6992
7843
|
return res.status(400).json({ error: "answer is required when not skipping" });
|
|
6993
7844
|
}
|
|
6994
|
-
const { provideAnswer } = await import("./tools-
|
|
6995
|
-
const found =
|
|
7845
|
+
const { provideAnswer: provideAnswer2 } = await import("./tools-SSPC3UQZ.js");
|
|
7846
|
+
const found = provideAnswer2(callId, answer ?? "", skip ?? false);
|
|
6996
7847
|
if (!found) {
|
|
6997
7848
|
return res.status(404).json({ error: "No pending question with that ID" });
|
|
6998
7849
|
}
|
|
@@ -7024,7 +7875,7 @@ All file and git operations should use this branch.
|
|
|
7024
7875
|
if (!session) {
|
|
7025
7876
|
return res.status(404).json({ error: "Session not found" });
|
|
7026
7877
|
}
|
|
7027
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
7878
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-LMAMF2X7.js");
|
|
7028
7879
|
if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
|
|
7029
7880
|
return res.json({ success: false, reason: "disabled" });
|
|
7030
7881
|
}
|
|
@@ -7057,7 +7908,7 @@ All file and git operations should use this branch.
|
|
|
7057
7908
|
backend: activeProvider?.backend ?? llmClient.getBackend(),
|
|
7058
7909
|
model: llmClient.getModel()
|
|
7059
7910
|
};
|
|
7060
|
-
const { runAgentTurn, TurnMetrics } = await import("./orchestrator-
|
|
7911
|
+
const { runAgentTurn, TurnMetrics } = await import("./orchestrator-PLTAEOMR.js");
|
|
7061
7912
|
runAgentTurn(
|
|
7062
7913
|
{
|
|
7063
7914
|
sessionManager,
|
|
@@ -7095,15 +7946,15 @@ All file and git operations should use this branch.
|
|
|
7095
7946
|
if (!session) {
|
|
7096
7947
|
return res.status(404).json({ error: "Session not found" });
|
|
7097
7948
|
}
|
|
7098
|
-
const { stopSessionExecution } = await import("./chat-handler-
|
|
7099
|
-
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-
|
|
7949
|
+
const { stopSessionExecution } = await import("./chat-handler-4WCKNCQV.js");
|
|
7950
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-SSPC3UQZ.js");
|
|
7100
7951
|
const queuedMessages = sessionManager.getQueueState(sessionId);
|
|
7101
7952
|
sessionManager.clearMessageQueue(sessionId);
|
|
7102
7953
|
stopSessionExecution(sessionId, sessionManager);
|
|
7103
7954
|
abortSession(sessionId);
|
|
7104
7955
|
cancelQuestionsForSession(sessionId, "Session stopped by user");
|
|
7105
7956
|
cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
|
|
7106
|
-
const eventStore = (await import("./events-
|
|
7957
|
+
const eventStore = (await import("./events-D33QXGSX.js")).getEventStore();
|
|
7107
7958
|
eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
|
|
7108
7959
|
res.json({ success: true, queuedMessages });
|
|
7109
7960
|
});
|
|
@@ -7117,7 +7968,7 @@ All file and git operations should use this branch.
|
|
|
7117
7968
|
if (typeof messageIndex !== "number" || messageIndex < 0) {
|
|
7118
7969
|
return res.status(400).json({ error: "messageIndex must be a non-negative number" });
|
|
7119
7970
|
}
|
|
7120
|
-
const { truncateSessionMessages } = await import("./events-
|
|
7971
|
+
const { truncateSessionMessages } = await import("./events-D33QXGSX.js");
|
|
7121
7972
|
truncateSessionMessages(sessionId, messageIndex);
|
|
7122
7973
|
res.json({ success: true });
|
|
7123
7974
|
});
|
|
@@ -7137,11 +7988,11 @@ All file and git operations should use this branch.
|
|
|
7137
7988
|
if (attachments !== void 0 && !Array.isArray(attachments)) {
|
|
7138
7989
|
return res.status(400).json({ error: "attachments must be an array if provided" });
|
|
7139
7990
|
}
|
|
7140
|
-
const { getEventStore: getEventStore2 } = await import("./events-
|
|
7141
|
-
const { buildMessagesFromStoredEvents } = await import("./folding-
|
|
7991
|
+
const { getEventStore: getEventStore2 } = await import("./events-D33QXGSX.js");
|
|
7992
|
+
const { buildMessagesFromStoredEvents: buildMessagesFromStoredEvents2 } = await import("./folding-QOKBKUU6.js");
|
|
7142
7993
|
const eventStore = getEventStore2();
|
|
7143
7994
|
const events = eventStore.getEvents(sessionId);
|
|
7144
|
-
const { messages } =
|
|
7995
|
+
const { messages } = buildMessagesFromStoredEvents2(events);
|
|
7145
7996
|
const msgIndex = messages.findIndex((m) => m.id === messageId);
|
|
7146
7997
|
if (msgIndex === -1) {
|
|
7147
7998
|
return res.status(400).json({ error: "Message not found" });
|
|
@@ -7150,7 +8001,7 @@ All file and git operations should use this branch.
|
|
|
7150
8001
|
if (msg.role !== "user" || msg.isSystemGenerated) {
|
|
7151
8002
|
return res.status(400).json({ error: "Can only replay user messages" });
|
|
7152
8003
|
}
|
|
7153
|
-
const { truncateSessionMessages } = await import("./events-
|
|
8004
|
+
const { truncateSessionMessages } = await import("./events-D33QXGSX.js");
|
|
7154
8005
|
truncateSessionMessages(sessionId, msgIndex - 1);
|
|
7155
8006
|
sessionManager.queueMessage(
|
|
7156
8007
|
sessionId,
|
|
@@ -7212,7 +8063,7 @@ All file and git operations should use this branch.
|
|
|
7212
8063
|
res.json({ accepted: true });
|
|
7213
8064
|
});
|
|
7214
8065
|
app.get("/api/settings", async (req, res) => {
|
|
7215
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
8066
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-LMAMF2X7.js");
|
|
7216
8067
|
const keysParam = req.query["keys"];
|
|
7217
8068
|
if (!keysParam) {
|
|
7218
8069
|
return res.status(400).json({ error: "keys query parameter is required" });
|
|
@@ -7225,19 +8076,19 @@ All file and git operations should use this branch.
|
|
|
7225
8076
|
res.json(result);
|
|
7226
8077
|
});
|
|
7227
8078
|
app.get("/api/settings/:key", async (req, res) => {
|
|
7228
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
8079
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-LMAMF2X7.js");
|
|
7229
8080
|
const key = req.params.key;
|
|
7230
8081
|
const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
|
|
7231
8082
|
res.json({ key, value });
|
|
7232
8083
|
});
|
|
7233
8084
|
app.get("/api/settings/:key", async (req, res) => {
|
|
7234
|
-
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-
|
|
8085
|
+
const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-LMAMF2X7.js");
|
|
7235
8086
|
const key = req.params.key;
|
|
7236
8087
|
const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
|
|
7237
8088
|
res.json({ key, value });
|
|
7238
8089
|
});
|
|
7239
8090
|
app.put("/api/settings/:key", async (req, res) => {
|
|
7240
|
-
const { setSetting: setSetting2 } = await import("./settings-
|
|
8091
|
+
const { setSetting: setSetting2 } = await import("./settings-LMAMF2X7.js");
|
|
7241
8092
|
const key = req.params.key;
|
|
7242
8093
|
const { value } = req.body;
|
|
7243
8094
|
if (value === void 0) {
|
|
@@ -7264,7 +8115,7 @@ All file and git operations should use this branch.
|
|
|
7264
8115
|
}
|
|
7265
8116
|
});
|
|
7266
8117
|
app.get("/api/tools/shells", async (_req, res) => {
|
|
7267
|
-
const { listAvailableShells } = await import("./platform-
|
|
8118
|
+
const { listAvailableShells } = await import("./platform-MWWJ6OKV.js");
|
|
7268
8119
|
res.json({ shells: listAvailableShells() });
|
|
7269
8120
|
});
|
|
7270
8121
|
app.get("/api/config", async (_req, res) => {
|
|
@@ -7338,6 +8189,7 @@ All file and git operations should use this branch.
|
|
|
7338
8189
|
...m.requestBody !== void 0 && { requestBody: m.requestBody },
|
|
7339
8190
|
...m.reasoningEfforts !== void 0 && { reasoningEfforts: m.reasoningEfforts },
|
|
7340
8191
|
...m.reasoningEffortOverride !== void 0 && { reasoningEffortOverride: m.reasoningEffortOverride },
|
|
8192
|
+
...m.modes !== void 0 && { modes: m.modes },
|
|
7341
8193
|
...m.supportsVision !== void 0 && { supportsVision: m.supportsVision },
|
|
7342
8194
|
...m.thinkingEnabled !== void 0 && { thinkingEnabled: m.thinkingEnabled },
|
|
7343
8195
|
...m.thinkingLevel !== void 0 && { thinkingLevel: m.thinkingLevel },
|
|
@@ -7356,7 +8208,7 @@ All file and git operations should use this branch.
|
|
|
7356
8208
|
}));
|
|
7357
8209
|
}
|
|
7358
8210
|
app.post("/api/proxy/test", async (_req, res) => {
|
|
7359
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
8211
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-LMAMF2X7.js");
|
|
7360
8212
|
const proxyUrl = getSetting2(SETTINGS_KEYS.PROXY_URL);
|
|
7361
8213
|
if (!proxyUrl) {
|
|
7362
8214
|
return res.status(400).json({ success: false, error: "No proxy URL configured" });
|
|
@@ -7387,7 +8239,7 @@ All file and git operations should use this branch.
|
|
|
7387
8239
|
res.json({
|
|
7388
8240
|
success: true,
|
|
7389
8241
|
url,
|
|
7390
|
-
backend: reqBackend
|
|
8242
|
+
backend: reqBackend !== "unknown" && reqBackend ? reqBackend : detectBackendFromUrl(url) ?? "unknown",
|
|
7391
8243
|
model
|
|
7392
8244
|
});
|
|
7393
8245
|
} catch (error) {
|
|
@@ -7448,7 +8300,7 @@ All file and git operations should use this branch.
|
|
|
7448
8300
|
}
|
|
7449
8301
|
});
|
|
7450
8302
|
app.post("/api/proxy/test", async (_req, res) => {
|
|
7451
|
-
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-
|
|
8303
|
+
const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-LMAMF2X7.js");
|
|
7452
8304
|
const proxyUrl = getSetting2(SETTINGS_KEYS.PROXY_URL);
|
|
7453
8305
|
if (!proxyUrl) {
|
|
7454
8306
|
return res.status(400).json({ success: false, error: "No proxy URL configured" });
|
|
@@ -7475,9 +8327,9 @@ All file and git operations should use this branch.
|
|
|
7475
8327
|
const backend = req.query["backend"];
|
|
7476
8328
|
if (!url) return res.status(400).json({ error: "url is required" });
|
|
7477
8329
|
try {
|
|
7478
|
-
const { fetchModelsWithContext } = await import("./provider-manager-
|
|
7479
|
-
const { getModelProfile: getModelProfile2 } = await import("./profiles-
|
|
7480
|
-
const { getCatalogEntry } = await import("./model-catalog-
|
|
8330
|
+
const { fetchModelsWithContext } = await import("./provider-manager-7NL5EQLG.js");
|
|
8331
|
+
const { getModelProfile: getModelProfile2 } = await import("./profiles-EBCVFYG7.js");
|
|
8332
|
+
const { getCatalogEntry } = await import("./model-catalog-2PCKTYGW.js");
|
|
7481
8333
|
const models = await fetchModelsWithContext(
|
|
7482
8334
|
url,
|
|
7483
8335
|
apiKey,
|
|
@@ -7493,6 +8345,7 @@ All file and git operations should use this branch.
|
|
|
7493
8345
|
return {
|
|
7494
8346
|
id: m.id,
|
|
7495
8347
|
contextWindow: m.contextWindow,
|
|
8348
|
+
supportsVision: m.supportsVision ?? profile.supportsVision,
|
|
7496
8349
|
defaultTemperature: profile.temperature,
|
|
7497
8350
|
defaultTopP: profile.topP,
|
|
7498
8351
|
defaultTopK: profile.topK,
|
|
@@ -7514,7 +8367,7 @@ All file and git operations should use this branch.
|
|
|
7514
8367
|
if (!url) return res.status(400).json({ error: "url is required" });
|
|
7515
8368
|
if (!models?.length) return res.status(400).json({ error: "models is required" });
|
|
7516
8369
|
try {
|
|
7517
|
-
const { autoConfig } = await import("./auto-config-
|
|
8370
|
+
const { autoConfig } = await import("./auto-config-5VSNLVNI.js");
|
|
7518
8371
|
const result = await autoConfig({
|
|
7519
8372
|
url,
|
|
7520
8373
|
...apiKey ? { apiKey } : {},
|
|
@@ -7546,10 +8399,10 @@ All file and git operations should use this branch.
|
|
|
7546
8399
|
});
|
|
7547
8400
|
return res.json({ success: true, message: { content: response2.content }, raw: response2 });
|
|
7548
8401
|
}
|
|
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-
|
|
8402
|
+
const { getModelProfile: getModelProfile2 } = await import("./profiles-EBCVFYG7.js");
|
|
8403
|
+
const { getBackendCapabilities } = await import("./backend-NDBY3DAQ.js");
|
|
8404
|
+
const { buildNonStreamingCreateParams } = await import("./client-pure-FLKP6PTI.js");
|
|
8405
|
+
const { OpenAIHttpClient } = await import("./http-client-MGAJTGPR.js");
|
|
7553
8406
|
const { ensureVersionPrefix } = await import("./url-utils-C5BM7NF7.js");
|
|
7554
8407
|
const profile = getModelProfile2(model);
|
|
7555
8408
|
const capabilities = getBackendCapabilities(backend || "unknown");
|
|
@@ -7565,7 +8418,7 @@ All file and git operations should use this branch.
|
|
|
7565
8418
|
modelSettings["queryParams"] = JSON.parse(rawQP);
|
|
7566
8419
|
} else {
|
|
7567
8420
|
const modeEnabled = mode === "thinking" ? modelConfig?.thinkingEnabled : modelConfig?.nonThinkingEnabled;
|
|
7568
|
-
if (modeEnabled) {
|
|
8421
|
+
if (modeEnabled && capabilities.supportsChatTemplateKwargs) {
|
|
7569
8422
|
modelSettings["chatTemplateKwargs"] = mode === "thinking" ? { enable_thinking: true } : { enable_thinking: false };
|
|
7570
8423
|
}
|
|
7571
8424
|
}
|
|
@@ -7622,7 +8475,7 @@ All file and git operations should use this branch.
|
|
|
7622
8475
|
try {
|
|
7623
8476
|
const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-LOK3LXFH.js");
|
|
7624
8477
|
const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
|
|
7625
|
-
const providerBackend = backend;
|
|
8478
|
+
const providerBackend = backend === "unknown" ? detectBackendFromUrl(url) ?? "unknown" : backend;
|
|
7626
8479
|
const providerModels = modelConfigs?.length ? buildModelConfigs(modelConfigs) : model ? [{ id: model, contextWindow: 2e5, source: "user" }] : [];
|
|
7627
8480
|
const configWithProvider = addProvider(globalConfig, {
|
|
7628
8481
|
name,
|
|
@@ -7679,15 +8532,15 @@ All file and git operations should use this branch.
|
|
|
7679
8532
|
}
|
|
7680
8533
|
});
|
|
7681
8534
|
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:
|
|
8535
|
+
const { z: z2 } = await import("zod");
|
|
8536
|
+
const visionFallbackUpdateSchema = z2.object({
|
|
8537
|
+
enabled: z2.boolean().optional(),
|
|
8538
|
+
url: z2.string().optional(),
|
|
8539
|
+
model: z2.string().optional(),
|
|
8540
|
+
timeout: z2.number().positive().optional(),
|
|
8541
|
+
backend: z2.enum(["ollama", "openai"]).optional(),
|
|
8542
|
+
providerModelRef: z2.string().optional(),
|
|
8543
|
+
apiKey: z2.string().optional()
|
|
7691
8544
|
});
|
|
7692
8545
|
const parsed = visionFallbackUpdateSchema.safeParse(req.body);
|
|
7693
8546
|
if (!parsed.success) {
|
|
@@ -7722,14 +8575,14 @@ All file and git operations should use this branch.
|
|
|
7722
8575
|
}
|
|
7723
8576
|
});
|
|
7724
8577
|
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:
|
|
8578
|
+
const { z: z2 } = await import("zod");
|
|
8579
|
+
const testSchema = z2.object({
|
|
8580
|
+
url: z2.string().optional(),
|
|
8581
|
+
model: z2.string().optional(),
|
|
8582
|
+
backend: z2.enum(["ollama", "openai"]).optional(),
|
|
8583
|
+
providerModelRef: z2.string().optional(),
|
|
8584
|
+
timeout: z2.number().positive().optional(),
|
|
8585
|
+
apiKey: z2.string().optional()
|
|
7733
8586
|
});
|
|
7734
8587
|
const parsed = testSchema.safeParse(req.body);
|
|
7735
8588
|
if (!parsed.success) {
|
|
@@ -7966,7 +8819,7 @@ All file and git operations should use this branch.
|
|
|
7966
8819
|
});
|
|
7967
8820
|
async function rebuildMcpTools() {
|
|
7968
8821
|
const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-FJAFLDZA.js");
|
|
7969
|
-
const { setMcpTools: setMcpTools2 } = await import("./tools-
|
|
8822
|
+
const { setMcpTools: setMcpTools2 } = await import("./tools-SSPC3UQZ.js");
|
|
7970
8823
|
const mcpTools = createMcpTools2(mcpManager);
|
|
7971
8824
|
setMcpTools2(mcpTools);
|
|
7972
8825
|
toolRegistry = createToolRegistry();
|
|
@@ -8058,6 +8911,10 @@ All file and git operations should use this branch.
|
|
|
8058
8911
|
for (const s of sessions) {
|
|
8059
8912
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8060
8913
|
}
|
|
8914
|
+
await announceContextDrift(
|
|
8915
|
+
sessionManager,
|
|
8916
|
+
sessions.map((s) => s.id)
|
|
8917
|
+
);
|
|
8061
8918
|
}
|
|
8062
8919
|
const allServers = mcpManager.getAllServers();
|
|
8063
8920
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: allServers }));
|
|
@@ -8136,6 +8993,10 @@ All file and git operations should use this branch.
|
|
|
8136
8993
|
for (const s of sessions) {
|
|
8137
8994
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8138
8995
|
}
|
|
8996
|
+
await announceContextDrift(
|
|
8997
|
+
sessionManager,
|
|
8998
|
+
sessions.map((s) => s.id)
|
|
8999
|
+
);
|
|
8139
9000
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8140
9001
|
res.json({ server: mcpManager.getServer(name) });
|
|
8141
9002
|
} catch (error) {
|
|
@@ -8168,11 +9029,15 @@ All file and git operations should use this branch.
|
|
|
8168
9029
|
for (const s of sessions) {
|
|
8169
9030
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8170
9031
|
}
|
|
9032
|
+
await announceContextDrift(
|
|
9033
|
+
sessionManager,
|
|
9034
|
+
sessions.map((s) => s.id)
|
|
9035
|
+
);
|
|
8171
9036
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8172
9037
|
res.json({ success: true });
|
|
8173
9038
|
});
|
|
8174
9039
|
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>`;
|
|
9040
|
+
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
9041
|
}
|
|
8177
9042
|
function parseOAuthResponse(value) {
|
|
8178
9043
|
let params;
|
|
@@ -8189,14 +9054,19 @@ All file and git operations should use this branch.
|
|
|
8189
9054
|
async function applyMcpOAuthResult(name) {
|
|
8190
9055
|
await mcpManager.connectServer(name);
|
|
8191
9056
|
await rebuildMcpTools();
|
|
8192
|
-
|
|
9057
|
+
const sessions = sessionManager.listSessions();
|
|
9058
|
+
for (const s of sessions) {
|
|
8193
9059
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8194
9060
|
}
|
|
9061
|
+
await announceContextDrift(
|
|
9062
|
+
sessionManager,
|
|
9063
|
+
sessions.map((s) => s.id)
|
|
9064
|
+
);
|
|
8195
9065
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8196
9066
|
}
|
|
8197
9067
|
async function completeMcpOAuth(name, serverUrl, code) {
|
|
8198
9068
|
const { auth } = await import("@modelcontextprotocol/sdk/client/auth.js");
|
|
8199
|
-
const { McpOAuthProvider: McpOAuthProvider2 } = await import("./oauth-provider-
|
|
9069
|
+
const { McpOAuthProvider: McpOAuthProvider2 } = await import("./oauth-provider-VHUS2KEG.js");
|
|
8200
9070
|
const { clearMcpOAuthAuthorizationState } = await import("./oauth-store-KECHUT54.js");
|
|
8201
9071
|
const provider = new McpOAuthProvider2(name, serverUrl);
|
|
8202
9072
|
try {
|
|
@@ -8229,8 +9099,9 @@ All file and git operations should use this branch.
|
|
|
8229
9099
|
}
|
|
8230
9100
|
try {
|
|
8231
9101
|
const { auth } = await import("@modelcontextprotocol/sdk/client/auth.js");
|
|
8232
|
-
const { McpOAuthProvider: McpOAuthProvider2 } = await import("./oauth-provider-
|
|
9102
|
+
const { McpOAuthProvider: McpOAuthProvider2, rejectStaleOAuthClient } = await import("./oauth-provider-VHUS2KEG.js");
|
|
8233
9103
|
const provider = new McpOAuthProvider2(name, server.config.url);
|
|
9104
|
+
await rejectStaleOAuthClient(provider);
|
|
8234
9105
|
const result = await auth(provider, { serverUrl: server.config.url });
|
|
8235
9106
|
if (result === "AUTHORIZED") {
|
|
8236
9107
|
await applyMcpOAuthResult(name);
|
|
@@ -8286,8 +9157,8 @@ All file and git operations should use this branch.
|
|
|
8286
9157
|
if (!parsed.code || !parsed.state) {
|
|
8287
9158
|
return res.status(400).json({ error: "Paste the whole callback URL, both code and state are needed" });
|
|
8288
9159
|
}
|
|
8289
|
-
const { readMcpOAuthEntry } = await import("./oauth-store-KECHUT54.js");
|
|
8290
|
-
const entry = await
|
|
9160
|
+
const { readMcpOAuthEntry: readMcpOAuthEntry2 } = await import("./oauth-store-KECHUT54.js");
|
|
9161
|
+
const entry = await readMcpOAuthEntry2(name, serverUrl);
|
|
8291
9162
|
if (!entry?.state || entry.state !== parsed.state) {
|
|
8292
9163
|
return res.status(400).json({ error: "This callback does not match the pending authorization" });
|
|
8293
9164
|
}
|
|
@@ -8341,6 +9212,10 @@ All file and git operations should use this branch.
|
|
|
8341
9212
|
for (const s of sessions) {
|
|
8342
9213
|
sessionManager.setDynamicContextChanged(s.id, true);
|
|
8343
9214
|
}
|
|
9215
|
+
await announceContextDrift(
|
|
9216
|
+
sessionManager,
|
|
9217
|
+
sessions.map((s) => s.id)
|
|
9218
|
+
);
|
|
8344
9219
|
wssExports.broadcastAll(createServerMessage("mcp.servers.changed", { servers: mcpManager.getAllServers() }));
|
|
8345
9220
|
res.json({ success: true });
|
|
8346
9221
|
} catch (error) {
|
|
@@ -8374,13 +9249,13 @@ All file and git operations should use this branch.
|
|
|
8374
9249
|
})
|
|
8375
9250
|
);
|
|
8376
9251
|
app.get("/api/sessions/:id/background-processes", async (req, res) => {
|
|
8377
|
-
const { getSessionProcesses } = await import("./manager-
|
|
9252
|
+
const { getSessionProcesses } = await import("./manager-VRHX7BVI.js");
|
|
8378
9253
|
const sessionId = req.params.id;
|
|
8379
9254
|
const processes = getSessionProcesses(sessionId);
|
|
8380
9255
|
res.json({ processes });
|
|
8381
9256
|
});
|
|
8382
9257
|
app.post("/api/sessions/:id/background-process/:processId/stop", async (req, res) => {
|
|
8383
|
-
const { stopProcess } = await import("./manager-
|
|
9258
|
+
const { stopProcess } = await import("./manager-VRHX7BVI.js");
|
|
8384
9259
|
const sessionId = req.params.id;
|
|
8385
9260
|
const processId = req.params.processId;
|
|
8386
9261
|
const session = sessionManager.getSession(sessionId);
|
|
@@ -8520,7 +9395,7 @@ All file and git operations should use this branch.
|
|
|
8520
9395
|
);
|
|
8521
9396
|
const wss = wssExports.wss;
|
|
8522
9397
|
deferTasksBroadcast = (projectId, payload) => wssExports.broadcastForProject(projectId, "", { type: "tasks.update", payload });
|
|
8523
|
-
const { launchWorkflowRun, abortRunnerRun } = await import("./launch-
|
|
9398
|
+
const { launchWorkflowRun, abortRunnerRun } = await import("./launch-KFENN5GS.js");
|
|
8524
9399
|
deferTasksLaunchWorkflow = (sessionId, launch) => {
|
|
8525
9400
|
const effective = sessionManager.resolveEffectiveProviderModel(sessionId);
|
|
8526
9401
|
let llmClient = getLLMClient();
|
|
@@ -8547,9 +9422,14 @@ All file and git operations should use this branch.
|
|
|
8547
9422
|
broadcastForSession: wssExports.broadcastForSession
|
|
8548
9423
|
},
|
|
8549
9424
|
{
|
|
8550
|
-
workflowId: launch.workflowId,
|
|
9425
|
+
...launch.workflowId ? { workflowId: launch.workflowId } : {},
|
|
8551
9426
|
...launch.params && Object.keys(launch.params).length > 0 ? { params: launch.params } : {},
|
|
9427
|
+
...launch.subGroup ? { subGroup: launch.subGroup } : {},
|
|
8552
9428
|
...launch.scope ? { scope: launch.scope } : {},
|
|
9429
|
+
...launch.resumeFrom ? { resumeFrom: launch.resumeFrom } : {},
|
|
9430
|
+
...launch.stepOutput ? { stepOutput: launch.stepOutput } : {},
|
|
9431
|
+
...launch.userChoice ? { userChoice: launch.userChoice } : {},
|
|
9432
|
+
...launch.content ? { content: launch.content } : {},
|
|
8553
9433
|
...launch.attachments && launch.attachments.length > 0 ? { attachments: launch.attachments } : {}
|
|
8554
9434
|
}
|
|
8555
9435
|
);
|
|
@@ -8560,7 +9440,7 @@ All file and git operations should use this branch.
|
|
|
8560
9440
|
const state = sessionManager.getContextState(sessionId);
|
|
8561
9441
|
wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
|
|
8562
9442
|
});
|
|
8563
|
-
const { QueueProcessor } = await import("./processor-
|
|
9443
|
+
const { QueueProcessor } = await import("./processor-K4WHZKE6.js");
|
|
8564
9444
|
const queueProcessor = new QueueProcessor({
|
|
8565
9445
|
sessionManager,
|
|
8566
9446
|
providerManager,
|
|
@@ -8581,6 +9461,80 @@ All file and git operations should use this branch.
|
|
|
8581
9461
|
}
|
|
8582
9462
|
return aborted;
|
|
8583
9463
|
};
|
|
9464
|
+
mcpToolDeps = {
|
|
9465
|
+
sessionManager,
|
|
9466
|
+
listProjects: () => listProjects(),
|
|
9467
|
+
createProject: async (name, workdir) => {
|
|
9468
|
+
const { createDirectoryWithGit } = await import("./project-creator-JF3YVQAH.js");
|
|
9469
|
+
return createDirectoryWithGit(name, workdir);
|
|
9470
|
+
},
|
|
9471
|
+
deleteProject: (projectId) => {
|
|
9472
|
+
const project = getProject(projectId);
|
|
9473
|
+
if (!project) return false;
|
|
9474
|
+
deleteProject(projectId);
|
|
9475
|
+
return true;
|
|
9476
|
+
},
|
|
9477
|
+
listWorkflows: (projectDir2) => listAvailableWorkflows(getGlobalConfigDir(config3.mode ?? "production"), projectDir2),
|
|
9478
|
+
topLevelAgentIds: async (workdir) => {
|
|
9479
|
+
const agents = await loadAllAgentsDefault(workdir);
|
|
9480
|
+
return getTopLevelAgents(agents).map((a) => a.metadata.id);
|
|
9481
|
+
},
|
|
9482
|
+
launchWorkflow: (sessionId, launch) => deferTasksLaunchWorkflow(sessionId, launch),
|
|
9483
|
+
stopSession: (sessionId) => {
|
|
9484
|
+
void (async () => {
|
|
9485
|
+
const { stopSessionExecution } = await import("./chat-handler-4WCKNCQV.js");
|
|
9486
|
+
const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-SSPC3UQZ.js");
|
|
9487
|
+
sessionManager.clearMessageQueue(sessionId);
|
|
9488
|
+
stopSessionExecution(sessionId, sessionManager);
|
|
9489
|
+
abortSession(sessionId);
|
|
9490
|
+
cancelQuestionsForSession(sessionId, "Session stopped by user");
|
|
9491
|
+
cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
|
|
9492
|
+
getEventStore().append(sessionId, { type: "running.changed", data: { isRunning: false } });
|
|
9493
|
+
})().catch((error) => {
|
|
9494
|
+
logger.error(`MCP stopSession failed for ${sessionId}`, {
|
|
9495
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9496
|
+
});
|
|
9497
|
+
});
|
|
9498
|
+
},
|
|
9499
|
+
stopWorkflow: (sessionId) => {
|
|
9500
|
+
const runningAborted = abortRunnerRun(sessionId);
|
|
9501
|
+
if (runningAborted) return { aborted: "running" };
|
|
9502
|
+
const execution = sessionManager.getActiveWorkflowExecution(sessionId);
|
|
9503
|
+
if (execution && execution.status === "waiting") {
|
|
9504
|
+
sessionManager.cancelWorkflow(
|
|
9505
|
+
sessionId,
|
|
9506
|
+
execution.id,
|
|
9507
|
+
execution.workflowId,
|
|
9508
|
+
execution.workflowName,
|
|
9509
|
+
execution.workflowColor
|
|
9510
|
+
);
|
|
9511
|
+
return { aborted: "paused" };
|
|
9512
|
+
}
|
|
9513
|
+
return null;
|
|
9514
|
+
},
|
|
9515
|
+
answerQuestion: (callId, answer, skip) => provideAnswer(callId, answer, skip),
|
|
9516
|
+
pendingQuestions: (sessionId) => getPendingQuestionsForSession(sessionId),
|
|
9517
|
+
confirmPath: (callId, approved, alwaysAllow) => providePathConfirmation(callId, approved, alwaysAllow).found,
|
|
9518
|
+
pendingConfirmations: (sessionId) => getPendingConfirmationsBySession()[sessionId] ?? [],
|
|
9519
|
+
setMetadataEntries: (sessionId, key, entries) => sessionManager.setMetadataEntries(sessionId, key, entries),
|
|
9520
|
+
recentMessages: (sessionId, limit) => {
|
|
9521
|
+
const eventStore = getEventStore();
|
|
9522
|
+
const { snapshot, events } = eventStore.getEventsSinceSnapshot(sessionId);
|
|
9523
|
+
const combined = combineEventsWithSnapshot(sessionId, snapshot, events);
|
|
9524
|
+
return buildMessagesFromStoredEvents(combined, Math.max(1, Math.min(limit, 50)));
|
|
9525
|
+
}
|
|
9526
|
+
};
|
|
9527
|
+
let mcpActualPort = null;
|
|
9528
|
+
setMcpBootstrapForTools(async () => {
|
|
9529
|
+
const authRequired = mcpAuth.isAuthRequired();
|
|
9530
|
+
const sessionToken = authRequired ? await currentSessionToken() : null;
|
|
9531
|
+
return buildOpenFoxMcpBootstrap({
|
|
9532
|
+
host: config3.server.host ?? "127.0.0.1",
|
|
9533
|
+
port: mcpActualPort ?? config3.server.port,
|
|
9534
|
+
authRequired,
|
|
9535
|
+
sessionToken
|
|
9536
|
+
});
|
|
9537
|
+
});
|
|
8584
9538
|
return {
|
|
8585
9539
|
httpServer,
|
|
8586
9540
|
ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
|
|
@@ -8591,9 +9545,13 @@ All file and git operations should use this branch.
|
|
|
8591
9545
|
const addr = httpServer.address();
|
|
8592
9546
|
const actualPort = typeof addr === "object" && addr ? addr.port : listenPort;
|
|
8593
9547
|
setMcpOAuthServerPort(actualPort);
|
|
9548
|
+
mcpActualPort = actualPort;
|
|
8594
9549
|
const client = getLLMClient();
|
|
8595
9550
|
logger.info(`OpenFox server running at http://${host}:${actualPort}`);
|
|
8596
9551
|
logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
|
|
9552
|
+
logger.info(
|
|
9553
|
+
`MCP endpoint available at http://${host}:${actualPort}/mcp \u2014 run 'openfox mcp' for a paste-ready client config`
|
|
9554
|
+
);
|
|
8597
9555
|
logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
|
|
8598
9556
|
resolve9({ port: actualPort });
|
|
8599
9557
|
});
|
|
@@ -8646,4 +9604,4 @@ export {
|
|
|
8646
9604
|
createServerHandle,
|
|
8647
9605
|
createServer
|
|
8648
9606
|
};
|
|
8649
|
-
//# sourceMappingURL=chunk-
|
|
9607
|
+
//# sourceMappingURL=chunk-DI5GTIAU.js.map
|