nolo-cli 0.7.0 → 0.8.0-alpha.11
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/_executeModel-GPU2276G.js +198 -0
- package/action-PAXRPJJF.js +27 -0
- package/addContentAction-CZVWF3QZ.js +25 -0
- package/addDialogAgentAction-CCIDTPHC.js +67 -0
- package/agentDeleteCommand-4ZZFM6AT.js +323 -0
- package/agentEmailCommands-C4FIVQYV.js +621 -0
- package/agentListCommands-VQAKVNEX.js +219 -0
- package/agentMachineCommands-ONFDXZUQ.js +421 -0
- package/agentPullCommand-O2MTUAFR.js +122 -0
- package/agentRecordCommands-QANUGHLK.js +89 -0
- package/agentRunCommand-A3OSRB5Q.js +102 -0
- package/agentRunControl-K4VKNEHF.js +57 -0
- package/agentRuntimeLocal-62ZB2ODP.js +99 -0
- package/agentSlice-GCGLVR2N.js +41 -0
- package/authCommand-WXMD3IPR.js +574 -0
- package/authCommands-X74AFDTC.js +26 -0
- package/chunk-2EISRLRC.js +238 -0
- package/chunk-2FTTDCD4.js +675 -0
- package/chunk-2MALU52L.js +51 -0
- package/chunk-2UZXR7WO.js +3631 -0
- package/chunk-3ASDTTEI.js +1774 -0
- package/chunk-3EUW3WEO.js +8 -0
- package/chunk-3LY4F7SV.js +9293 -0
- package/chunk-4CQLVGRV.js +50 -0
- package/chunk-4IJYA27C.js +342 -0
- package/chunk-4SN6J65J.js +38 -0
- package/chunk-4VNS5WPM.js +42 -0
- package/chunk-53DNV5AT.js +421 -0
- package/chunk-572EKYLF.js +792 -0
- package/chunk-5MIDPSSK.js +206 -0
- package/chunk-5MMAK75Q.js +15 -0
- package/chunk-63W5N6P3.js +150 -0
- package/chunk-6B7G54GE.js +1078 -0
- package/chunk-6HYVJV7B.js +102 -0
- package/chunk-6WD3OOJS.js +20 -0
- package/chunk-7OC5VCLW.js +929 -0
- package/chunk-7YLKNMOM.js +562 -0
- package/chunk-ATNTHIZI.js +13 -0
- package/chunk-AUGHVUFE.js +69 -0
- package/chunk-AVMKZHBC.js +85 -0
- package/chunk-AZPCASRE.js +36 -0
- package/chunk-BR2WP3DF.js +548 -0
- package/chunk-BSTNFPHD.js +231 -0
- package/chunk-CF2WZF4N.js +134 -0
- package/chunk-CF7PE763.js +10 -0
- package/chunk-CJH2VTEU.js +360 -0
- package/chunk-CKOJQDKR.js +198 -0
- package/chunk-CM54RSSA.js +206 -0
- package/chunk-CQAHS7YO.js +4300 -0
- package/chunk-CWMSB67P.js +34 -0
- package/chunk-DFFIZ25G.js +30 -0
- package/chunk-DWLEOOGR.js +8 -0
- package/chunk-DZ3XJVET.js +231 -0
- package/chunk-EHLHSX7R.js +1433 -0
- package/chunk-ELRRLDWI.js +478 -0
- package/chunk-EYEEHCU4.js +3066 -0
- package/chunk-F6QQI5YI.js +19 -0
- package/chunk-G3SUCT4W.js +8 -0
- package/chunk-G5H7RNFL.js +807 -0
- package/chunk-GAKEBQD6.js +30 -0
- package/chunk-GBRG75BQ.js +86 -0
- package/chunk-GCMRDRCQ.js +190 -0
- package/chunk-GGUO4FKF.js +27158 -0
- package/chunk-GGXABIPP.js +628 -0
- package/chunk-H2RMRZJE.js +113 -0
- package/chunk-H33743XH.js +25 -0
- package/chunk-HEIIIZBX.js +104 -0
- package/chunk-HNMXYYJR.js +12 -0
- package/chunk-I3D64LRJ.js +1447 -0
- package/chunk-IAXBCTTE.js +175 -0
- package/chunk-IDZTNRP2.js +85 -0
- package/chunk-ISNCLGUZ.js +146 -0
- package/chunk-IYQV26RA.js +138 -0
- package/chunk-J4576WMJ.js +91 -0
- package/chunk-J5KV47ZW.js +222 -0
- package/chunk-JBTJAPUF.js +35 -0
- package/chunk-JZPBVBIY.js +94 -0
- package/chunk-KB7YFGL6.js +34 -0
- package/chunk-KKY4B2Q2.js +21 -0
- package/chunk-KQHGZWSV.js +63 -0
- package/chunk-KUG52IAP.js +8 -0
- package/chunk-KX6YBIHG.js +8 -0
- package/chunk-LAWBMYBC.js +171 -0
- package/chunk-LCBAMY2M.js +60 -0
- package/chunk-LM25IZBJ.js +902 -0
- package/chunk-LM6IYQ3F.js +16 -0
- package/chunk-M76ECQRD.js +13658 -0
- package/chunk-MRC4Q34M.js +188 -0
- package/chunk-NXZGF7F7.js +2878 -0
- package/chunk-OF5H3QWS.js +300 -0
- package/chunk-OLZBQWZU.js +6 -0
- package/chunk-OWOHNGCN.js +50 -0
- package/chunk-PN7AFLOK.js +3401 -0
- package/chunk-QR5IPGT3.js +24674 -0
- package/chunk-QSJ3HFEF.js +8 -0
- package/chunk-QYXLLLGL.js +4519 -0
- package/chunk-R2HXWML7.js +1949 -0
- package/chunk-ROWQEJRP.js +139 -0
- package/chunk-S7Z2HMCN.js +25 -0
- package/chunk-SYOF5TAS.js +962 -0
- package/chunk-TDIQFT2F.js +301 -0
- package/chunk-TILFE6LV.js +381 -0
- package/chunk-TZNCFAUP.js +2157 -0
- package/chunk-U4TRT5V4.js +246 -0
- package/chunk-U6VC4MU4.js +8 -0
- package/chunk-UAD2WT54.js +64 -0
- package/chunk-USZMK5UT.js +105 -0
- package/chunk-VEJLAVTC.js +2052 -0
- package/chunk-VF4K2QE6.js +21 -0
- package/chunk-VHBWHNVT.js +59 -0
- package/chunk-VIDPVFP3.js +20 -0
- package/chunk-VP4WJWKW.js +8882 -0
- package/chunk-VQ2AJRGD.js +148 -0
- package/chunk-W4NVOSFF.js +37 -0
- package/chunk-WWC7I6TA.js +8 -0
- package/chunk-X7W4TNB4.js +1550 -0
- package/chunk-XBIUN5CR.js +191 -0
- package/chunk-XDVAYP26.js +537 -0
- package/chunk-XKIMRZO2.js +221 -0
- package/chunk-Y5GCAMGP.js +9 -0
- package/chunk-YOUZDXU7.js +9013 -0
- package/chunk-Z4NR32XR.js +50 -0
- package/chunk-ZNFJWEN4.js +56 -0
- package/cliExecutor-DNE7TCC4.js +36 -0
- package/clipboardy-BVE3LVEH.js +12001 -0
- package/createAgentAutomationAction-537X433B.js +864 -0
- package/createDialogAction-YUD4RKBS.js +52 -0
- package/createPageAction-KJMFHL4C.js +169 -0
- package/db-HCJ5PRB5.js +271 -0
- package/dbSlice-C4UJLRZP.js +59 -0
- package/deleteFile-4SKGPHOZ.js +41 -0
- package/dialogCommands-H74KJKHC.js +59 -0
- package/dialogSlice-ETSD4YWS.js +146 -0
- package/docCreateCommands-ADUSU34K.js +215 -0
- package/docDeleteCommands-ZXV3S5OS.js +176 -0
- package/docReadCommands-YYYI6LT7.js +129 -0
- package/docStore-ZBF5DDKR.js +96 -0
- package/docUpdateCommands-7MGJ3BYA.js +313 -0
- package/fetchMessages-JOCYIFKL.js +16 -0
- package/getPricing-PWPJ6MNV.js +30 -0
- package/handleSendMessageAction-MGKI62Y6.js +183 -0
- package/index.js +184 -112288
- package/lib-FYYBQIXS.js +19839 -0
- package/localDialogRead-D3BELSTF.js +26 -0
- package/localFirstLog-DHNOY2NA.js +19 -0
- package/localRuntimeAuthority-TEGCOWX4.js +471 -0
- package/localRuntimeDb-LGDF6FQP.js +12 -0
- package/machineCommands-RMTMN7VQ.js +774 -0
- package/machineInfo-M6KB6HZN.js +10 -0
- package/machineWsRunDispatch-UIP355FN.js +105 -0
- package/memoryCommands-O4MIPIWQ.js +476 -0
- package/models-XLEOET3E.js +9 -0
- package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
- package/package.json +3 -25
- package/read-RIJCIVKX.js +17 -0
- package/readDocTool-GZD2N3LO.js +16 -0
- package/readlineWorkspace-CRNQRZ6D.js +5374 -0
- package/removeDialogAgentAction-CXLST3N6.js +72 -0
- package/runAgentClientLoop-2S334SGA.js +288 -0
- package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
- package/setDialogExtraReferencesAction-LP6VYGU7.js +59 -0
- package/setPrimaryDialogAgentAction-D3GGH23T.js +79 -0
- package/settingSlice-J4I3Z5H7.js +198 -0
- package/skillSummaryMarker-42HKSXYY.js +9 -0
- package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
- package/slateUtils-2M7MUTWX.js +19 -0
- package/spaceCommands-NDEWMFZJ.js +450 -0
- package/spaceSlice-RLB6RTHO.js +145 -0
- package/streamAgentChatTurn-Y2TTYPBD.js +4503 -0
- package/tableCommands-GOFP7QKA.js +1286 -0
- package/toolApiClient-QI3YBNWB.js +26 -0
- package/tools-YZYLY2ZQ.js +83 -0
- package/understanding-HCSFPE7R.js +318 -0
- package/updateCommands-VOSPKXFF.js +31 -0
- package/updateDialogTitleAction-LQV6QLBW.js +185 -0
|
@@ -0,0 +1,4503 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyModelLayerOverride,
|
|
3
|
+
buildLinkedSpacesSection,
|
|
4
|
+
buildSpaceContextLayer
|
|
5
|
+
} from "./chunk-CM54RSSA.js";
|
|
6
|
+
import {
|
|
7
|
+
buildStaticUserPolicyContext,
|
|
8
|
+
resolveSpaceContextPreloadPlan
|
|
9
|
+
} from "./chunk-BSTNFPHD.js";
|
|
10
|
+
import {
|
|
11
|
+
getFinalPrice,
|
|
12
|
+
getModelPricing,
|
|
13
|
+
getPrices,
|
|
14
|
+
hasExplicitAgentPricing
|
|
15
|
+
} from "./chunk-VQ2AJRGD.js";
|
|
16
|
+
import {
|
|
17
|
+
buildCliPrompt
|
|
18
|
+
} from "./chunk-ATNTHIZI.js";
|
|
19
|
+
import {
|
|
20
|
+
INVALID_TOOL_ARGS_REPLACEMENT,
|
|
21
|
+
buildInvalidToolCallSelfHealResult,
|
|
22
|
+
filterAndCleanMessages,
|
|
23
|
+
handleToolCalls,
|
|
24
|
+
parseApiError,
|
|
25
|
+
parseToolCallArguments,
|
|
26
|
+
persistToolMessages,
|
|
27
|
+
readStreamChunk,
|
|
28
|
+
sanitizeOutboundResponsesInput,
|
|
29
|
+
sendOpenAICompletionsRequest,
|
|
30
|
+
updateTotalUsage
|
|
31
|
+
} from "./chunk-R2HXWML7.js";
|
|
32
|
+
import {
|
|
33
|
+
prepareTools
|
|
34
|
+
} from "./chunk-LAWBMYBC.js";
|
|
35
|
+
import {
|
|
36
|
+
getPublicImageAgentDefaultProfile,
|
|
37
|
+
getPublicImageAgentMode
|
|
38
|
+
} from "./chunk-VIDPVFP3.js";
|
|
39
|
+
import {
|
|
40
|
+
fetchReferenceContents,
|
|
41
|
+
generateRequestBody,
|
|
42
|
+
mergeReferences,
|
|
43
|
+
performFetchRequest,
|
|
44
|
+
performServerProxyFetchWithRetry,
|
|
45
|
+
resolveAgentCallPlan,
|
|
46
|
+
resolveClientWire,
|
|
47
|
+
resolveReferenceAssets,
|
|
48
|
+
resolveToolsFromKeys
|
|
49
|
+
} from "./chunk-TZNCFAUP.js";
|
|
50
|
+
import "./chunk-KKY4B2Q2.js";
|
|
51
|
+
import "./chunk-2MALU52L.js";
|
|
52
|
+
import {
|
|
53
|
+
TOOL_PACKS,
|
|
54
|
+
applyDefaultWebToolPacks,
|
|
55
|
+
applyDisabledTools,
|
|
56
|
+
createSSEParser,
|
|
57
|
+
expandEnabledPacks,
|
|
58
|
+
getAllToolRuns,
|
|
59
|
+
shouldBlockForGptPro
|
|
60
|
+
} from "./chunk-GGUO4FKF.js";
|
|
61
|
+
import "./chunk-GAKEBQD6.js";
|
|
62
|
+
import "./chunk-KQHGZWSV.js";
|
|
63
|
+
import "./chunk-XKIMRZO2.js";
|
|
64
|
+
import "./chunk-3LY4F7SV.js";
|
|
65
|
+
import {
|
|
66
|
+
extractCategorizedMentions
|
|
67
|
+
} from "./chunk-VP4WJWKW.js";
|
|
68
|
+
import "./chunk-63W5N6P3.js";
|
|
69
|
+
import {
|
|
70
|
+
getDocState
|
|
71
|
+
} from "./chunk-53DNV5AT.js";
|
|
72
|
+
import "./chunk-7OC5VCLW.js";
|
|
73
|
+
import "./chunk-NXZGF7F7.js";
|
|
74
|
+
import "./chunk-HEIIIZBX.js";
|
|
75
|
+
import {
|
|
76
|
+
PERSONALIZATION_DIALOG_CATEGORY,
|
|
77
|
+
addToolMessage,
|
|
78
|
+
addUserMessage,
|
|
79
|
+
buildPersonalizationDialogPolicyContext,
|
|
80
|
+
estimateTokenCount,
|
|
81
|
+
extractReferenceKeysFromMessage,
|
|
82
|
+
finalizeTransientMessageOnError,
|
|
83
|
+
getModelContextWindow,
|
|
84
|
+
getModelInfo,
|
|
85
|
+
messageStreamEnd,
|
|
86
|
+
messageStreaming,
|
|
87
|
+
planContextUsage,
|
|
88
|
+
prepareAndPersistUserMessage,
|
|
89
|
+
removeTransientMessage,
|
|
90
|
+
selectAllMsgs,
|
|
91
|
+
selectCurrentDialogConfig,
|
|
92
|
+
selectCurrentSpaceId,
|
|
93
|
+
selectCurrentTable,
|
|
94
|
+
selectCurrentUserBalance,
|
|
95
|
+
selectDialogConfigByKey,
|
|
96
|
+
selectTableRows,
|
|
97
|
+
selectViewMode
|
|
98
|
+
} from "./chunk-QR5IPGT3.js";
|
|
99
|
+
import "./chunk-MRC4Q34M.js";
|
|
100
|
+
import "./chunk-J4576WMJ.js";
|
|
101
|
+
import "./chunk-CWMSB67P.js";
|
|
102
|
+
import "./chunk-7YLKNMOM.js";
|
|
103
|
+
import {
|
|
104
|
+
createCliChatTurnStream,
|
|
105
|
+
getCliChatSession,
|
|
106
|
+
startCliChatSession
|
|
107
|
+
} from "./chunk-IYQV26RA.js";
|
|
108
|
+
import "./chunk-Z4NR32XR.js";
|
|
109
|
+
import "./chunk-M76ECQRD.js";
|
|
110
|
+
import "./chunk-AUGHVUFE.js";
|
|
111
|
+
import "./chunk-H2RMRZJE.js";
|
|
112
|
+
import "./chunk-UAD2WT54.js";
|
|
113
|
+
import "./chunk-USZMK5UT.js";
|
|
114
|
+
import {
|
|
115
|
+
assertDesktopAgentRuntimeTurnBodyHasNoRawSecrets,
|
|
116
|
+
buildDesktopAgentRuntimeAgentConfigSnapshot,
|
|
117
|
+
buildDesktopAgentRuntimeDialogHistorySnapshot
|
|
118
|
+
} from "./chunk-TILFE6LV.js";
|
|
119
|
+
import {
|
|
120
|
+
resolveAgentImageInputSupport
|
|
121
|
+
} from "./chunk-SYOF5TAS.js";
|
|
122
|
+
import {
|
|
123
|
+
extractImagePartsFromResponseOutput,
|
|
124
|
+
extractTextFromResponseOutput,
|
|
125
|
+
toResponsesTools
|
|
126
|
+
} from "./chunk-XDVAYP26.js";
|
|
127
|
+
import {
|
|
128
|
+
canonicalizeToolNames,
|
|
129
|
+
prioritizeToolNames,
|
|
130
|
+
resolveRuntimeToolSurfaceForAgent
|
|
131
|
+
} from "./chunk-LM25IZBJ.js";
|
|
132
|
+
import "./chunk-5MMAK75Q.js";
|
|
133
|
+
import {
|
|
134
|
+
getApiEndpoint,
|
|
135
|
+
getModelConfig,
|
|
136
|
+
getProviderByModelName
|
|
137
|
+
} from "./chunk-572EKYLF.js";
|
|
138
|
+
import "./chunk-TDIQFT2F.js";
|
|
139
|
+
import "./chunk-JZPBVBIY.js";
|
|
140
|
+
import "./chunk-BR2WP3DF.js";
|
|
141
|
+
import {
|
|
142
|
+
resolveToolBaseUrl
|
|
143
|
+
} from "./chunk-5MIDPSSK.js";
|
|
144
|
+
import {
|
|
145
|
+
extractCustomId
|
|
146
|
+
} from "./chunk-DFFIZ25G.js";
|
|
147
|
+
import {
|
|
148
|
+
addActiveController,
|
|
149
|
+
clearPendingUserInputQueue,
|
|
150
|
+
dequeueUserInput,
|
|
151
|
+
removeActiveController,
|
|
152
|
+
selectActiveControllers,
|
|
153
|
+
selectPendingFiles,
|
|
154
|
+
selectPendingUserInputQueue,
|
|
155
|
+
tokenUsageLiveUpdate
|
|
156
|
+
} from "./chunk-3ASDTTEI.js";
|
|
157
|
+
import "./chunk-DWLEOOGR.js";
|
|
158
|
+
import {
|
|
159
|
+
toErrorMessage
|
|
160
|
+
} from "./chunk-HNMXYYJR.js";
|
|
161
|
+
import "./chunk-IAXBCTTE.js";
|
|
162
|
+
import {
|
|
163
|
+
selectAiRecentContentLimit,
|
|
164
|
+
selectCurrentServer,
|
|
165
|
+
selectGlobalPrompt,
|
|
166
|
+
selectKnowledgeCaptureLevel,
|
|
167
|
+
selectMaxExecutionTime,
|
|
168
|
+
selectSpaceContextLevel,
|
|
169
|
+
selectUserTonePreset
|
|
170
|
+
} from "./chunk-VEJLAVTC.js";
|
|
171
|
+
import "./chunk-Y5GCAMGP.js";
|
|
172
|
+
import {
|
|
173
|
+
patch,
|
|
174
|
+
read,
|
|
175
|
+
selectById,
|
|
176
|
+
write
|
|
177
|
+
} from "./chunk-G5H7RNFL.js";
|
|
178
|
+
import "./chunk-AZPCASRE.js";
|
|
179
|
+
import "./chunk-2EISRLRC.js";
|
|
180
|
+
import {
|
|
181
|
+
asRecordOrEmpty
|
|
182
|
+
} from "./chunk-KX6YBIHG.js";
|
|
183
|
+
import {
|
|
184
|
+
asNonEmptyStringArray,
|
|
185
|
+
createDialogMessageKeyAndId,
|
|
186
|
+
dialogMessageKey
|
|
187
|
+
} from "./chunk-PN7AFLOK.js";
|
|
188
|
+
import {
|
|
189
|
+
asOptionalFiniteNumber
|
|
190
|
+
} from "./chunk-WWC7I6TA.js";
|
|
191
|
+
import "./chunk-QSJ3HFEF.js";
|
|
192
|
+
import "./chunk-ELRRLDWI.js";
|
|
193
|
+
import {
|
|
194
|
+
selectIdentityToken,
|
|
195
|
+
selectIdentityUser,
|
|
196
|
+
selectIdentityUserId,
|
|
197
|
+
selectRuntimeCurrentServer
|
|
198
|
+
} from "./chunk-EYEEHCU4.js";
|
|
199
|
+
import {
|
|
200
|
+
getIsDesktopApp,
|
|
201
|
+
isAbortError
|
|
202
|
+
} from "./chunk-QYXLLLGL.js";
|
|
203
|
+
import "./chunk-GBRG75BQ.js";
|
|
204
|
+
import "./chunk-4SN6J65J.js";
|
|
205
|
+
import "./chunk-LCBAMY2M.js";
|
|
206
|
+
import {
|
|
207
|
+
asTrimmedString
|
|
208
|
+
} from "./chunk-3EUW3WEO.js";
|
|
209
|
+
import "./chunk-U6VC4MU4.js";
|
|
210
|
+
import "./chunk-CF7PE763.js";
|
|
211
|
+
import {
|
|
212
|
+
asOptionalTrimmedString
|
|
213
|
+
} from "./chunk-G3SUCT4W.js";
|
|
214
|
+
import {
|
|
215
|
+
isRecord
|
|
216
|
+
} from "./chunk-KUG52IAP.js";
|
|
217
|
+
import "./chunk-4VNS5WPM.js";
|
|
218
|
+
|
|
219
|
+
// packages/ai/agent/isVoiceModel.ts
|
|
220
|
+
function isVoiceModel(model, provider) {
|
|
221
|
+
const providerLower = String(provider || "").toLowerCase();
|
|
222
|
+
const modelLower = String(model || "").toLowerCase();
|
|
223
|
+
if (providerLower === "google" && modelLower.includes("live")) return true;
|
|
224
|
+
if (modelLower.includes("live-preview")) return true;
|
|
225
|
+
if (modelLower.includes("live-001")) return true;
|
|
226
|
+
if (modelLower.includes("native-audio")) return true;
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// packages/ai/agent/isLiveAudioOnlyAgent.ts
|
|
231
|
+
function isLiveAudioOnlyAgent(agent) {
|
|
232
|
+
if (agent.defaultInteractionMode !== "live_audio") return false;
|
|
233
|
+
return isVoiceModel(agent.model, agent.provider);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// packages/ai/agent/projectDesktopToolUiContent.ts
|
|
237
|
+
var DESKTOP_TOOL_UI_CONTENT_MAX_CHARS = 48e3;
|
|
238
|
+
function clipUiText(value, max = DESKTOP_TOOL_UI_CONTENT_MAX_CHARS) {
|
|
239
|
+
if (value.length <= max) return value;
|
|
240
|
+
return `${value.slice(0, Math.max(0, max - 20))}
|
|
241
|
+
\u2026[truncated for UI]`;
|
|
242
|
+
}
|
|
243
|
+
function tryParseJsonRecord(text) {
|
|
244
|
+
const trimmed = text.trim();
|
|
245
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
246
|
+
try {
|
|
247
|
+
const parsed = JSON.parse(trimmed);
|
|
248
|
+
return isRecord(parsed) ? parsed : null;
|
|
249
|
+
} catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function parseShellToolTextContent(content) {
|
|
254
|
+
const text = content ?? "";
|
|
255
|
+
if (!text.trim()) return { stdout: "", stderr: "" };
|
|
256
|
+
const exitMatch = text.match(/(?:^|\n)exitCode:\s*(-?\d+)\s*$/);
|
|
257
|
+
const exitCode = exitMatch ? Number(exitMatch[1]) : void 0;
|
|
258
|
+
const body = (exitMatch ? text.slice(0, exitMatch.index) : text).replace(
|
|
259
|
+
/\s+$/,
|
|
260
|
+
""
|
|
261
|
+
);
|
|
262
|
+
let stdout = "";
|
|
263
|
+
let stderr = "";
|
|
264
|
+
if (body.startsWith("stdout:\n") || body.includes("\nstdout:\n")) {
|
|
265
|
+
const afterStdout = body.replace(/^[\s\S]*?stdout:\n/, "");
|
|
266
|
+
if (afterStdout.includes("\n\nstderr:\n")) {
|
|
267
|
+
const [out, err] = afterStdout.split("\n\nstderr:\n");
|
|
268
|
+
stdout = out ?? "";
|
|
269
|
+
stderr = err ?? "";
|
|
270
|
+
} else if (afterStdout.startsWith("stderr:\n")) {
|
|
271
|
+
stderr = afterStdout.slice("stderr:\n".length);
|
|
272
|
+
} else {
|
|
273
|
+
stdout = afterStdout;
|
|
274
|
+
}
|
|
275
|
+
} else if (body.startsWith("stderr:\n") || body.includes("\nstderr:\n")) {
|
|
276
|
+
stderr = body.replace(/^[\s\S]*?stderr:\n/, "");
|
|
277
|
+
} else {
|
|
278
|
+
stdout = body;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
stdout,
|
|
282
|
+
stderr,
|
|
283
|
+
...typeof exitCode === "number" && Number.isFinite(exitCode) ? { exitCode } : {}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function resolveShellCommand(metadata, argumentsPreview) {
|
|
287
|
+
return asOptionalTrimmedString(metadata.command) || asOptionalTrimmedString(metadata.cmd) || asOptionalTrimmedString(argumentsPreview) || "";
|
|
288
|
+
}
|
|
289
|
+
function projectDesktopToolUiContent(args) {
|
|
290
|
+
const toolName = asOptionalTrimmedString(args.toolName) || "tool";
|
|
291
|
+
const metadata = isRecord(args.metadata) ? args.metadata : {};
|
|
292
|
+
const rawContent = typeof args.content === "string" ? args.content : typeof args.summary === "string" ? args.summary : typeof args.message === "string" ? args.message : "";
|
|
293
|
+
if (toolName === "execShell") {
|
|
294
|
+
const existing = tryParseJsonRecord(rawContent);
|
|
295
|
+
if (existing && (typeof existing.stdout === "string" || typeof existing.command === "string" || typeof existing.stderr === "string")) {
|
|
296
|
+
const command2 = asOptionalTrimmedString(existing.command) || resolveShellCommand(metadata, args.argumentsPreview);
|
|
297
|
+
return JSON.stringify({
|
|
298
|
+
...existing,
|
|
299
|
+
command: command2,
|
|
300
|
+
cwd: asOptionalTrimmedString(existing.cwd) || asOptionalTrimmedString(metadata.cwd) || null,
|
|
301
|
+
exitCode: asOptionalFiniteNumber(existing.exitCode) ?? asOptionalFiniteNumber(metadata.exitCode)
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
const parsed = parseShellToolTextContent(rawContent);
|
|
305
|
+
const command = resolveShellCommand(metadata, args.argumentsPreview);
|
|
306
|
+
return JSON.stringify({
|
|
307
|
+
command,
|
|
308
|
+
cwd: asOptionalTrimmedString(metadata.cwd) || null,
|
|
309
|
+
stdout: clipUiText(parsed.stdout),
|
|
310
|
+
stderr: clipUiText(parsed.stderr),
|
|
311
|
+
exitCode: asOptionalFiniteNumber(metadata.exitCode) ?? parsed.exitCode,
|
|
312
|
+
timedOut: metadata.timedOut === true,
|
|
313
|
+
blocked: metadata.blocked === true
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (toolName === "readFile" || toolName === "read_file") {
|
|
317
|
+
const filePath = asOptionalTrimmedString(metadata.path) || asOptionalTrimmedString(metadata.filePath) || asOptionalTrimmedString(metadata.file_path) || asOptionalTrimmedString(args.argumentsPreview) || "";
|
|
318
|
+
const body = typeof args.content === "string" && args.content || typeof args.summary === "string" && args.summary || typeof args.message === "string" && args.message || "";
|
|
319
|
+
return JSON.stringify({
|
|
320
|
+
filePath,
|
|
321
|
+
content: clipUiText(body),
|
|
322
|
+
...asOptionalFiniteNumber(metadata.startLine) != null ? { startLine: asOptionalFiniteNumber(metadata.startLine) } : {},
|
|
323
|
+
...asOptionalFiniteNumber(metadata.endLine) != null ? { endLine: asOptionalFiniteNumber(metadata.endLine) } : {},
|
|
324
|
+
...asOptionalFiniteNumber(metadata.totalLines) != null ? { totalLines: asOptionalFiniteNumber(metadata.totalLines) } : {},
|
|
325
|
+
...metadata.truncated === true ? { truncated: true } : {}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
const preferred = typeof args.content === "string" && args.content || typeof args.summary === "string" && args.summary || typeof args.message === "string" && args.message || "";
|
|
329
|
+
return clipUiText(preferred);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// packages/ai/agent/desktopTurnSegments.ts
|
|
333
|
+
function attachToolCallIdToSegment(segments, callId) {
|
|
334
|
+
if (!segments || segments.length === 0) return;
|
|
335
|
+
const current = segments[segments.length - 1];
|
|
336
|
+
if (!current.toolCallIds) {
|
|
337
|
+
current.toolCallIds = [];
|
|
338
|
+
}
|
|
339
|
+
current.toolCallIds.push(callId);
|
|
340
|
+
}
|
|
341
|
+
function resolveSegmentToolCalls(toolCallIds, turnMessages) {
|
|
342
|
+
if (!toolCallIds || toolCallIds.length === 0 || !Array.isArray(turnMessages) || turnMessages.length === 0) {
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
const callMap = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const msg of turnMessages) {
|
|
347
|
+
if (Array.isArray(msg?.tool_calls)) {
|
|
348
|
+
for (const tc of msg.tool_calls) {
|
|
349
|
+
if (tc && typeof tc.id === "string") {
|
|
350
|
+
callMap.set(tc.id, tc);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const resolved = [];
|
|
356
|
+
for (const id of toolCallIds) {
|
|
357
|
+
const tc = callMap.get(id);
|
|
358
|
+
if (tc) {
|
|
359
|
+
resolved.push(tc);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return resolved;
|
|
363
|
+
}
|
|
364
|
+
function buildMinimalToolCallsFromIds(toolCallIds, toolNameById) {
|
|
365
|
+
if (!toolCallIds || toolCallIds.length === 0) return [];
|
|
366
|
+
const result = [];
|
|
367
|
+
for (const id of toolCallIds) {
|
|
368
|
+
if (!toolNameById.has(id)) continue;
|
|
369
|
+
const name = toolNameById.get(id) || "tool";
|
|
370
|
+
result.push({ id, type: "function", function: { name, arguments: "{}" } });
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
function selectPersistableFinalizedSegments(segments) {
|
|
375
|
+
if (!Array.isArray(segments)) return [];
|
|
376
|
+
return segments.filter(
|
|
377
|
+
(segment) => segment.finalized && (segment.content.length > 0 || segment.toolCallIds && segment.toolCallIds.length > 0)
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// packages/chat/queue/chatQueueLifecycleActions.ts
|
|
382
|
+
function runChatQueueTurnEnd(payload) {
|
|
383
|
+
return (_dispatch, _getState, extra) => {
|
|
384
|
+
const adapter = extra?.chatQueueAdapter;
|
|
385
|
+
if (!adapter) return;
|
|
386
|
+
void adapter.notifyTurnEnd(payload.dialogKey, {
|
|
387
|
+
ok: payload.ok,
|
|
388
|
+
aborted: payload.aborted
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// packages/ai/agent/getFullChatContextKeys.ts
|
|
394
|
+
var difference = (arrA, arrB) => {
|
|
395
|
+
if (!arrA.length) return [];
|
|
396
|
+
if (!arrB.length) return [...arrA];
|
|
397
|
+
const exclude = new Set(arrB);
|
|
398
|
+
return arrA.filter((item) => !exclude.has(item));
|
|
399
|
+
};
|
|
400
|
+
var getFullChatContextKeys = async (state, dispatch, agentConfig, userInput, dialogConfig) => {
|
|
401
|
+
const msgs = selectAllMsgs(
|
|
402
|
+
state,
|
|
403
|
+
dialogConfig?.dbKey ? extractCustomId(dialogConfig.dbKey) : dialogConfig?.id
|
|
404
|
+
);
|
|
405
|
+
const botInstructionKeys = /* @__PURE__ */ new Set();
|
|
406
|
+
const botKnowledgeKeys = /* @__PURE__ */ new Set();
|
|
407
|
+
if (Array.isArray(agentConfig.references)) {
|
|
408
|
+
for (const ref of agentConfig.references) {
|
|
409
|
+
if (!ref?.dbKey) continue;
|
|
410
|
+
if (ref.type === "instruction") {
|
|
411
|
+
botInstructionKeys.add(ref.dbKey);
|
|
412
|
+
} else {
|
|
413
|
+
botKnowledgeKeys.add(ref.dbKey);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
const currentInputKeys = /* @__PURE__ */ new Set();
|
|
418
|
+
if (Array.isArray(userInput)) {
|
|
419
|
+
for (const part of userInput) {
|
|
420
|
+
if (part?.pageKey) currentInputKeys.add(part.pageKey);
|
|
421
|
+
if (part?.dialogKey) currentInputKeys.add(part.dialogKey);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const historyKeys = /* @__PURE__ */ new Set();
|
|
425
|
+
const savedKeys = dialogConfig?.referenceKeys;
|
|
426
|
+
if (Array.isArray(savedKeys)) {
|
|
427
|
+
savedKeys.forEach((k) => historyKeys.add(k));
|
|
428
|
+
}
|
|
429
|
+
const scanContentParts = (msg) => {
|
|
430
|
+
if (!msg) return;
|
|
431
|
+
for (const key of extractReferenceKeysFromMessage(msg)) {
|
|
432
|
+
historyKeys.add(key);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
if (msgs && msgs.length > 0) {
|
|
436
|
+
const summarizedBeforeId = dialogConfig?.summarizedBeforeId;
|
|
437
|
+
if (!summarizedBeforeId) {
|
|
438
|
+
for (const msg of msgs) scanContentParts(msg);
|
|
439
|
+
} else {
|
|
440
|
+
let foundMarker = false;
|
|
441
|
+
let startScan = false;
|
|
442
|
+
for (const msg of msgs) {
|
|
443
|
+
if (!startScan) {
|
|
444
|
+
if (msg.id === summarizedBeforeId) {
|
|
445
|
+
foundMarker = true;
|
|
446
|
+
startScan = true;
|
|
447
|
+
}
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
scanContentParts(msg);
|
|
451
|
+
}
|
|
452
|
+
if (!foundMarker) {
|
|
453
|
+
console.warn(`[getFullChatContextKeys] marker ${summarizedBeforeId} not found, scanning all`);
|
|
454
|
+
for (const msg of msgs) scanContentParts(msg);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
botInstructionKeys,
|
|
460
|
+
currentInputKeys,
|
|
461
|
+
historyKeys,
|
|
462
|
+
botKnowledgeKeys
|
|
463
|
+
};
|
|
464
|
+
};
|
|
465
|
+
var deduplicateContextKeys = (keys) => {
|
|
466
|
+
const {
|
|
467
|
+
botInstructionKeys,
|
|
468
|
+
currentInputKeys,
|
|
469
|
+
historyKeys,
|
|
470
|
+
botKnowledgeKeys
|
|
471
|
+
} = keys;
|
|
472
|
+
const finalBotInstructionKeys = Array.from(botInstructionKeys);
|
|
473
|
+
const finalCurrentInputKeys = difference(
|
|
474
|
+
Array.from(currentInputKeys),
|
|
475
|
+
finalBotInstructionKeys
|
|
476
|
+
);
|
|
477
|
+
const finalHistoryKeys = difference(Array.from(historyKeys), [
|
|
478
|
+
...finalBotInstructionKeys,
|
|
479
|
+
...finalCurrentInputKeys
|
|
480
|
+
]);
|
|
481
|
+
const finalBotKnowledgeKeys = difference(Array.from(botKnowledgeKeys), [
|
|
482
|
+
...finalBotInstructionKeys,
|
|
483
|
+
...finalCurrentInputKeys,
|
|
484
|
+
...finalHistoryKeys
|
|
485
|
+
]);
|
|
486
|
+
return {
|
|
487
|
+
botInstructionsContext: finalBotInstructionKeys,
|
|
488
|
+
currentInputContext: finalCurrentInputKeys,
|
|
489
|
+
historyContext: finalHistoryKeys,
|
|
490
|
+
botKnowledgeContext: finalBotKnowledgeKeys
|
|
491
|
+
};
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// packages/ai/chat/streamRetry.ts
|
|
495
|
+
var MAX_INITIAL_STREAM_RETRIES = 1;
|
|
496
|
+
var DEFAULT_INITIAL_STREAM_RETRY_AFTER_MS = 1500;
|
|
497
|
+
var normalizeInitialStreamRetryAfterMs = (value) => {
|
|
498
|
+
const parsed = Number(value);
|
|
499
|
+
return Number.isFinite(parsed) && parsed >= 0 ? Math.round(parsed) : DEFAULT_INITIAL_STREAM_RETRY_AFTER_MS;
|
|
500
|
+
};
|
|
501
|
+
var isRetryableInitialStreamError = (error) => {
|
|
502
|
+
if (!error || isAbortError(error)) return false;
|
|
503
|
+
const message = typeof error?.message === "string" ? error.message : String(error);
|
|
504
|
+
return /ECONNRESET|ECONNREFUSED|EPIPE|socket hang up|network error|failed to fetch|fetch failed|load failed|connection closed|stream ended before first visible delta|response stream ended before first visible delta|模型响应流 .* 秒内没有返回新内容/i.test(
|
|
505
|
+
message
|
|
506
|
+
);
|
|
507
|
+
};
|
|
508
|
+
var waitForInitialStreamRetry = async (retryAfterMs, signal) => {
|
|
509
|
+
if (signal?.aborted) {
|
|
510
|
+
const abortError = new Error("Aborted");
|
|
511
|
+
abortError.name = "AbortError";
|
|
512
|
+
throw abortError;
|
|
513
|
+
}
|
|
514
|
+
await new Promise((resolve, reject) => {
|
|
515
|
+
const onAbort = () => {
|
|
516
|
+
clearTimeout(timeoutId);
|
|
517
|
+
signal?.removeEventListener("abort", onAbort);
|
|
518
|
+
const abortError = new Error("Aborted");
|
|
519
|
+
abortError.name = "AbortError";
|
|
520
|
+
reject(abortError);
|
|
521
|
+
};
|
|
522
|
+
const timeoutId = setTimeout(() => {
|
|
523
|
+
signal?.removeEventListener("abort", onAbort);
|
|
524
|
+
resolve();
|
|
525
|
+
}, normalizeInitialStreamRetryAfterMs(retryAfterMs));
|
|
526
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
527
|
+
});
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
// packages/ai/chat/sendOpenAIResponseRequest.ts
|
|
531
|
+
var seg = (txt) => [{ type: "text", text: txt ?? "" }];
|
|
532
|
+
var shouldEnableBuiltInImageGeneration = (agentConfig) => String(agentConfig?.provider || "").toLowerCase() === "openai" && !getModelInfo(String(agentConfig?.model || ""))?.hasImageOutput && !!agentConfig?.imageConfig?.enabled;
|
|
533
|
+
var withImageGenerationStage = (messageMetadata, stage) => {
|
|
534
|
+
const previousState = messageMetadata?.imageGenerationState;
|
|
535
|
+
if (previousState?.kind !== "image_generation") {
|
|
536
|
+
return messageMetadata;
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
...messageMetadata ?? {},
|
|
540
|
+
imageGenerationState: {
|
|
541
|
+
...previousState,
|
|
542
|
+
stage
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
};
|
|
546
|
+
var shouldShowImageSavingState = (contentBuffer) => contentBuffer.some((part) => part?.type === "image_url") && !contentBuffer.some(
|
|
547
|
+
(part) => part?.type === "text" && typeof part.text === "string" && part.text.trim()
|
|
548
|
+
);
|
|
549
|
+
var logQuickChatPerfStage = (startedAt, stage, details) => {
|
|
550
|
+
if (!startedAt) return;
|
|
551
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
552
|
+
console.info("[QuickChatPerf]", {
|
|
553
|
+
stage,
|
|
554
|
+
elapsedMs: now - startedAt,
|
|
555
|
+
...typeof performance !== "undefined" ? { atMs: now } : {},
|
|
556
|
+
...details ?? {}
|
|
557
|
+
});
|
|
558
|
+
};
|
|
559
|
+
var safeCancel = async (reader) => {
|
|
560
|
+
if (!reader) return;
|
|
561
|
+
try {
|
|
562
|
+
await reader.cancel();
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
var ensureToolCall = (state, key, partial) => {
|
|
567
|
+
let toolCall = state.assistantToolCalls.find((call) => call.id === key);
|
|
568
|
+
if (!toolCall) {
|
|
569
|
+
toolCall = {
|
|
570
|
+
id: key,
|
|
571
|
+
type: "function",
|
|
572
|
+
function: { name: "", arguments: "" }
|
|
573
|
+
};
|
|
574
|
+
state.assistantToolCalls.push(toolCall);
|
|
575
|
+
}
|
|
576
|
+
if (partial.id) toolCall.id = partial.id;
|
|
577
|
+
if (partial.function?.name) toolCall.function.name = partial.function.name;
|
|
578
|
+
if (typeof partial.function?.arguments === "string") {
|
|
579
|
+
toolCall.function.arguments = partial.function.arguments;
|
|
580
|
+
}
|
|
581
|
+
return toolCall;
|
|
582
|
+
};
|
|
583
|
+
var extractTextFromOutputItem = (item) => {
|
|
584
|
+
if (item?.type !== "message" || !Array.isArray(item.content)) return "";
|
|
585
|
+
return item.content.filter(
|
|
586
|
+
(content) => content?.type === "output_text" && typeof content.text === "string"
|
|
587
|
+
).map((content) => content.text).join("");
|
|
588
|
+
};
|
|
589
|
+
var getStreamErrorMessage = (event) => {
|
|
590
|
+
const directMessage = asOptionalTrimmedString(event?.message);
|
|
591
|
+
if (directMessage) return directMessage;
|
|
592
|
+
const nestedMessage = asOptionalTrimmedString(event?.error?.message) ?? asOptionalTrimmedString(event?.error?.msg);
|
|
593
|
+
if (nestedMessage) return nestedMessage;
|
|
594
|
+
const nestedCode = asOptionalTrimmedString(event?.error?.code) ?? asOptionalTrimmedString(event?.code);
|
|
595
|
+
if (nestedCode) return nestedCode;
|
|
596
|
+
const nestedType = asOptionalTrimmedString(event?.error?.type);
|
|
597
|
+
if (nestedType) return nestedType;
|
|
598
|
+
const eventType = asOptionalTrimmedString(event?.type);
|
|
599
|
+
if (eventType && eventType !== "error") return eventType;
|
|
600
|
+
return "Unknown error";
|
|
601
|
+
};
|
|
602
|
+
var sendOpenAIResponseRequest = async ({
|
|
603
|
+
bodyData,
|
|
604
|
+
agentConfig,
|
|
605
|
+
thunkApi,
|
|
606
|
+
dialogKey,
|
|
607
|
+
parentMessageId,
|
|
608
|
+
messageMetadata,
|
|
609
|
+
quickChatPerfStartedAt
|
|
610
|
+
}) => {
|
|
611
|
+
const { dispatch, getState, signal: thunkSignal } = thunkApi;
|
|
612
|
+
const dialogId = extractCustomId(dialogKey);
|
|
613
|
+
const controller = new AbortController();
|
|
614
|
+
thunkSignal.addEventListener("abort", () => controller.abort());
|
|
615
|
+
const signal = controller.signal;
|
|
616
|
+
const streamSpaceId = selectCurrentSpaceId(getState()) || void 0;
|
|
617
|
+
let messageId;
|
|
618
|
+
let msgKey;
|
|
619
|
+
if (parentMessageId) {
|
|
620
|
+
messageId = parentMessageId;
|
|
621
|
+
msgKey = `msg:${dialogId}:${messageId}`;
|
|
622
|
+
} else {
|
|
623
|
+
const newIds = createDialogMessageKeyAndId(dialogId);
|
|
624
|
+
messageId = newIds.messageId;
|
|
625
|
+
msgKey = newIds.key;
|
|
626
|
+
}
|
|
627
|
+
dispatch(addActiveController({ messageId, controller, dialogKey }));
|
|
628
|
+
const state = {
|
|
629
|
+
content: "",
|
|
630
|
+
contentBuffer: [],
|
|
631
|
+
reasoning: "",
|
|
632
|
+
usage: null,
|
|
633
|
+
assistantToolCalls: [],
|
|
634
|
+
completedResponse: null
|
|
635
|
+
};
|
|
636
|
+
let activeMessageMetadata = messageMetadata;
|
|
637
|
+
const buildMeta = (hasPendingInteraction = false, hasHandedOff = false, finishReason = null) => ({
|
|
638
|
+
hasToolCalls: state.assistantToolCalls.length > 0,
|
|
639
|
+
hasPendingInteraction,
|
|
640
|
+
hasHandedOff,
|
|
641
|
+
finishReason,
|
|
642
|
+
messageId,
|
|
643
|
+
usage: state.usage ?? void 0
|
|
644
|
+
});
|
|
645
|
+
const resetStateForRetry = () => {
|
|
646
|
+
state.content = "";
|
|
647
|
+
state.contentBuffer = [];
|
|
648
|
+
state.reasoning = "";
|
|
649
|
+
state.usage = null;
|
|
650
|
+
state.assistantToolCalls = [];
|
|
651
|
+
state.completedResponse = null;
|
|
652
|
+
activeMessageMetadata = messageMetadata;
|
|
653
|
+
};
|
|
654
|
+
const canRetryInitialResponseAttempt = (attempt, loggedFirstVisibleDelta, finishReason) => attempt < MAX_INITIAL_STREAM_RETRIES && !loggedFirstVisibleDelta && !state.assistantToolCalls.length && !finishReason && !state.completedResponse;
|
|
655
|
+
const flush = () => dispatch(
|
|
656
|
+
messageStreaming({
|
|
657
|
+
id: messageId,
|
|
658
|
+
dialogId,
|
|
659
|
+
dbKey: msgKey,
|
|
660
|
+
content: state.contentBuffer,
|
|
661
|
+
thinkContent: state.reasoning,
|
|
662
|
+
role: "assistant",
|
|
663
|
+
agentKey: agentConfig.dbKey,
|
|
664
|
+
cybotKey: agentConfig.dbKey,
|
|
665
|
+
...asOptionalTrimmedString(agentConfig?.name) ? { agentName: asOptionalTrimmedString(agentConfig?.name) } : {},
|
|
666
|
+
...activeMessageMetadata ?? {}
|
|
667
|
+
})
|
|
668
|
+
);
|
|
669
|
+
const finalize = async () => {
|
|
670
|
+
if (!state.content) {
|
|
671
|
+
const completedText = extractTextFromResponseOutput(state.completedResponse);
|
|
672
|
+
if (completedText) {
|
|
673
|
+
state.content = completedText;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const completedImages = extractImagePartsFromResponseOutput(state.completedResponse);
|
|
677
|
+
if (state.contentBuffer.length === 0) {
|
|
678
|
+
state.contentBuffer = [
|
|
679
|
+
...state.content ? seg(state.content) : [],
|
|
680
|
+
...completedImages
|
|
681
|
+
];
|
|
682
|
+
} else if (completedImages.length > 0 && !state.contentBuffer.some((part) => part.type === "image_url")) {
|
|
683
|
+
state.contentBuffer = [...state.contentBuffer, ...completedImages];
|
|
684
|
+
}
|
|
685
|
+
if (state.contentBuffer.length === 0 && state.content) {
|
|
686
|
+
state.contentBuffer = seg(state.content);
|
|
687
|
+
}
|
|
688
|
+
activeMessageMetadata = withImageGenerationStage(
|
|
689
|
+
activeMessageMetadata,
|
|
690
|
+
"saving"
|
|
691
|
+
);
|
|
692
|
+
if (activeMessageMetadata?.imageGenerationState && shouldShowImageSavingState(state.contentBuffer)) {
|
|
693
|
+
dispatch(
|
|
694
|
+
messageStreaming({
|
|
695
|
+
id: messageId,
|
|
696
|
+
dialogId,
|
|
697
|
+
dbKey: msgKey,
|
|
698
|
+
content: "",
|
|
699
|
+
thinkContent: state.reasoning,
|
|
700
|
+
role: "assistant",
|
|
701
|
+
agentKey: agentConfig.dbKey,
|
|
702
|
+
cybotKey: agentConfig.dbKey,
|
|
703
|
+
...asOptionalTrimmedString(agentConfig?.name) ? { agentName: asOptionalTrimmedString(agentConfig?.name) } : {},
|
|
704
|
+
...activeMessageMetadata ?? {}
|
|
705
|
+
})
|
|
706
|
+
);
|
|
707
|
+
} else {
|
|
708
|
+
flush();
|
|
709
|
+
}
|
|
710
|
+
await dispatch(
|
|
711
|
+
messageStreamEnd({
|
|
712
|
+
finalContentBuffer: state.contentBuffer,
|
|
713
|
+
totalUsage: state.usage,
|
|
714
|
+
msgKey,
|
|
715
|
+
agentConfig,
|
|
716
|
+
dialogId,
|
|
717
|
+
dialogKey,
|
|
718
|
+
messageId,
|
|
719
|
+
reasoningBuffer: state.reasoning,
|
|
720
|
+
messageMetadata: activeMessageMetadata,
|
|
721
|
+
toolCalls: state.assistantToolCalls,
|
|
722
|
+
spaceId: streamSpaceId
|
|
723
|
+
})
|
|
724
|
+
);
|
|
725
|
+
};
|
|
726
|
+
const processToolCalls = async () => {
|
|
727
|
+
if (!state.assistantToolCalls.length) {
|
|
728
|
+
await finalize();
|
|
729
|
+
return buildMeta(false, false, null);
|
|
730
|
+
}
|
|
731
|
+
const validCalls = [];
|
|
732
|
+
const invalidCalls = [];
|
|
733
|
+
for (const call of state.assistantToolCalls) {
|
|
734
|
+
if (!call) continue;
|
|
735
|
+
if (parseToolCallArguments(call?.function?.arguments).valid) {
|
|
736
|
+
validCalls.push(call);
|
|
737
|
+
} else {
|
|
738
|
+
invalidCalls.push({
|
|
739
|
+
...call,
|
|
740
|
+
type: "function",
|
|
741
|
+
function: {
|
|
742
|
+
name: call?.function?.name ?? "",
|
|
743
|
+
arguments: INVALID_TOOL_ARGS_REPLACEMENT
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (invalidCalls.length > 0) {
|
|
749
|
+
const invalidById = /* @__PURE__ */ new Map();
|
|
750
|
+
for (const call of invalidCalls) {
|
|
751
|
+
if (call?.id) invalidById.set(call.id, call);
|
|
752
|
+
}
|
|
753
|
+
state.assistantToolCalls = state.assistantToolCalls.map((call) => {
|
|
754
|
+
const invalid = call?.id ? invalidById.get(call.id) : void 0;
|
|
755
|
+
if (!invalid) return call;
|
|
756
|
+
return {
|
|
757
|
+
...call,
|
|
758
|
+
type: "function",
|
|
759
|
+
function: {
|
|
760
|
+
name: invalid.function?.name ?? call.function?.name ?? "",
|
|
761
|
+
arguments: INVALID_TOOL_ARGS_REPLACEMENT
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
});
|
|
765
|
+
for (let i = 0; i < invalidCalls.length; i++) {
|
|
766
|
+
const call = invalidCalls[i];
|
|
767
|
+
const callId = call?.id || `call_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
768
|
+
const toolIndex = validCalls.length + i;
|
|
769
|
+
const runningToolMessageId = `${messageId}-t${String(toolIndex).padStart(3, "0")}`;
|
|
770
|
+
const runningToolDbKey = dialogMessageKey(dialogId, runningToolMessageId);
|
|
771
|
+
const toolName = call?.function?.name || "unknown";
|
|
772
|
+
const selfHealContent = buildInvalidToolCallSelfHealResult(callId, toolName);
|
|
773
|
+
const toolMessage = {
|
|
774
|
+
id: runningToolMessageId,
|
|
775
|
+
dbKey: runningToolDbKey,
|
|
776
|
+
role: "tool",
|
|
777
|
+
content: selfHealContent,
|
|
778
|
+
toolCallId: callId,
|
|
779
|
+
thinkContent: "",
|
|
780
|
+
cybotKey: agentConfig.dbKey,
|
|
781
|
+
isStreaming: false,
|
|
782
|
+
toolName,
|
|
783
|
+
parentMessageId: messageId,
|
|
784
|
+
toolPayload: {
|
|
785
|
+
toolName,
|
|
786
|
+
status: "failed",
|
|
787
|
+
input: {},
|
|
788
|
+
rawToolCall: call,
|
|
789
|
+
error: {
|
|
790
|
+
type: "InvalidToolCallArguments",
|
|
791
|
+
message: "\u5DE5\u5177\u53C2\u6570 JSON \u88AB\u622A\u65AD\u6216\u975E\u6CD5\uFF0C\u5DF2\u8DF3\u8FC7\u6267\u884C\u5E76\u66FF\u6362\u4E3A\u5360\u4F4D arguments\u3002",
|
|
792
|
+
retryable: true
|
|
793
|
+
},
|
|
794
|
+
summary: `\u274C ${toolName} \u53C2\u6570\u88AB\u622A\u65AD\uFF0C\u5DF2\u8DF3\u8FC7`
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
dispatch(addToolMessage(toolMessage));
|
|
798
|
+
const { controller: _c, ...messageToWrite } = toolMessage;
|
|
799
|
+
await dispatch(
|
|
800
|
+
write({
|
|
801
|
+
data: { ...messageToWrite, type: "msg" /* MSG */ },
|
|
802
|
+
customKey: runningToolDbKey
|
|
803
|
+
})
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (!validCalls.length) {
|
|
808
|
+
await finalize();
|
|
809
|
+
return buildMeta(false, false, null);
|
|
810
|
+
}
|
|
811
|
+
if (state.usage) {
|
|
812
|
+
dispatch(
|
|
813
|
+
tokenUsageLiveUpdate({
|
|
814
|
+
input_tokens: state.usage.prompt_tokens ?? state.usage.input_tokens,
|
|
815
|
+
output_tokens: state.usage.completion_tokens ?? state.usage.output_tokens,
|
|
816
|
+
cost: state.usage.cost,
|
|
817
|
+
dialogKey
|
|
818
|
+
})
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
const result = await dispatch(
|
|
822
|
+
handleToolCalls({
|
|
823
|
+
accumulatedCalls: validCalls,
|
|
824
|
+
currentContentBuffer: state.contentBuffer,
|
|
825
|
+
agentConfig,
|
|
826
|
+
messageId,
|
|
827
|
+
dialogId,
|
|
828
|
+
dialogKey
|
|
829
|
+
})
|
|
830
|
+
).unwrap();
|
|
831
|
+
state.content = Array.isArray(result.finalContentBuffer) ? result.finalContentBuffer.filter((part) => part?.type === "text").map((part) => part.text ?? "").join("") : state.content;
|
|
832
|
+
state.contentBuffer = Array.isArray(result.finalContentBuffer) ? result.finalContentBuffer : state.contentBuffer;
|
|
833
|
+
await finalize();
|
|
834
|
+
return buildMeta(result.hasPendingInteraction, result.hasHandedOff, null);
|
|
835
|
+
};
|
|
836
|
+
let reader;
|
|
837
|
+
try {
|
|
838
|
+
if (!parentMessageId) {
|
|
839
|
+
dispatch(
|
|
840
|
+
messageStreaming({
|
|
841
|
+
id: messageId,
|
|
842
|
+
dialogId,
|
|
843
|
+
dbKey: msgKey,
|
|
844
|
+
content: "",
|
|
845
|
+
role: "assistant",
|
|
846
|
+
agentKey: agentConfig.dbKey,
|
|
847
|
+
cybotKey: agentConfig.dbKey,
|
|
848
|
+
...asOptionalTrimmedString(agentConfig?.name) ? { agentName: asOptionalTrimmedString(agentConfig?.name) } : {},
|
|
849
|
+
...activeMessageMetadata ?? {},
|
|
850
|
+
isStreaming: true
|
|
851
|
+
})
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
const functionTools = getModelInfo(agentConfig.model)?.hasImageOutput ? [] : prepareTools(agentConfig.tools ?? [], { provider: agentConfig.provider });
|
|
855
|
+
const imageMode = getPublicImageAgentMode(agentConfig);
|
|
856
|
+
const imageProfile = imageMode === "continuous" ? getPublicImageAgentDefaultProfile("continuous") : null;
|
|
857
|
+
const tools = [
|
|
858
|
+
...toResponsesTools(functionTools) ?? [],
|
|
859
|
+
...shouldEnableBuiltInImageGeneration(agentConfig) ? [{ type: "image_generation" }] : [],
|
|
860
|
+
...imageMode === "continuous" ? [
|
|
861
|
+
{
|
|
862
|
+
type: "image_generation",
|
|
863
|
+
action: "auto",
|
|
864
|
+
quality: imageProfile?.quality,
|
|
865
|
+
output_format: imageProfile?.outputFormat
|
|
866
|
+
}
|
|
867
|
+
] : []
|
|
868
|
+
];
|
|
869
|
+
const sanitizedInput = Array.isArray(bodyData?.input) ? sanitizeOutboundResponsesInput(bodyData.input) : bodyData?.input;
|
|
870
|
+
const baseBodyData = sanitizedInput !== bodyData?.input ? { ...bodyData, input: sanitizedInput } : bodyData;
|
|
871
|
+
const requestBody = {
|
|
872
|
+
...baseBodyData,
|
|
873
|
+
...tools.length ? { tools, tool_choice: baseBodyData.tool_choice ?? "auto" } : {},
|
|
874
|
+
stream: true
|
|
875
|
+
};
|
|
876
|
+
const api = getApiEndpoint(agentConfig);
|
|
877
|
+
const token = selectIdentityToken(getState());
|
|
878
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-response-fetch-starting", {
|
|
879
|
+
api,
|
|
880
|
+
dialogKey
|
|
881
|
+
});
|
|
882
|
+
attemptLoop:
|
|
883
|
+
for (let attempt = 0; attempt <= MAX_INITIAL_STREAM_RETRIES; attempt += 1) {
|
|
884
|
+
const response = await performFetchRequest({
|
|
885
|
+
agentConfig,
|
|
886
|
+
api,
|
|
887
|
+
bodyData: requestBody,
|
|
888
|
+
currentServer: selectRuntimeCurrentServer(getState()),
|
|
889
|
+
signal,
|
|
890
|
+
token: token ?? "",
|
|
891
|
+
dialogId
|
|
892
|
+
});
|
|
893
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-response-fetch-response", {
|
|
894
|
+
ok: response.ok,
|
|
895
|
+
status: response.status,
|
|
896
|
+
dialogKey
|
|
897
|
+
});
|
|
898
|
+
activeMessageMetadata = withImageGenerationStage(
|
|
899
|
+
activeMessageMetadata,
|
|
900
|
+
"generating"
|
|
901
|
+
);
|
|
902
|
+
if (!parentMessageId && activeMessageMetadata?.imageGenerationState) {
|
|
903
|
+
dispatch(
|
|
904
|
+
messageStreaming({
|
|
905
|
+
id: messageId,
|
|
906
|
+
dialogId,
|
|
907
|
+
dbKey: msgKey,
|
|
908
|
+
content: "",
|
|
909
|
+
role: "assistant",
|
|
910
|
+
agentKey: agentConfig.dbKey,
|
|
911
|
+
cybotKey: agentConfig.dbKey,
|
|
912
|
+
...asOptionalTrimmedString(agentConfig?.name) ? { agentName: asOptionalTrimmedString(agentConfig?.name) } : {},
|
|
913
|
+
...activeMessageMetadata ?? {},
|
|
914
|
+
isStreaming: true
|
|
915
|
+
})
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
if (!response.ok) {
|
|
919
|
+
const errorMessage = await parseApiError(response);
|
|
920
|
+
state.content = `[\u9519\u8BEF: ${errorMessage}]`;
|
|
921
|
+
await finalize();
|
|
922
|
+
return buildMeta();
|
|
923
|
+
}
|
|
924
|
+
reader = response.body?.getReader();
|
|
925
|
+
if (!reader) {
|
|
926
|
+
if (canRetryInitialResponseAttempt(attempt, false, null)) {
|
|
927
|
+
resetStateForRetry();
|
|
928
|
+
await waitForInitialStreamRetry(1500, signal);
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
await finalize();
|
|
932
|
+
return buildMeta();
|
|
933
|
+
}
|
|
934
|
+
const parseSSE = createSSEParser();
|
|
935
|
+
const decoder = new TextDecoder();
|
|
936
|
+
let finishReason = null;
|
|
937
|
+
let loggedFirstStreamChunk = false;
|
|
938
|
+
let loggedFirstParsedEvent = false;
|
|
939
|
+
let loggedFirstVisibleDelta = false;
|
|
940
|
+
try {
|
|
941
|
+
while (true) {
|
|
942
|
+
const { done, value } = await readStreamChunk(reader, { signal });
|
|
943
|
+
if (done) {
|
|
944
|
+
if (canRetryInitialResponseAttempt(attempt, loggedFirstVisibleDelta, finishReason)) {
|
|
945
|
+
await safeCancel(reader);
|
|
946
|
+
reader = void 0;
|
|
947
|
+
resetStateForRetry();
|
|
948
|
+
await waitForInitialStreamRetry(1500, signal);
|
|
949
|
+
continue attemptLoop;
|
|
950
|
+
}
|
|
951
|
+
const meta = await processToolCalls();
|
|
952
|
+
return {
|
|
953
|
+
...meta,
|
|
954
|
+
finishReason: meta.finishReason ?? finishReason
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
if (!loggedFirstStreamChunk) {
|
|
958
|
+
loggedFirstStreamChunk = true;
|
|
959
|
+
logQuickChatPerfStage(
|
|
960
|
+
quickChatPerfStartedAt,
|
|
961
|
+
"openai-response-first-stream-chunk",
|
|
962
|
+
{ dialogKey, byteLength: value.byteLength }
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
966
|
+
const events = parseSSE(chunk);
|
|
967
|
+
if (events.length > 0 && !loggedFirstParsedEvent) {
|
|
968
|
+
loggedFirstParsedEvent = true;
|
|
969
|
+
logQuickChatPerfStage(
|
|
970
|
+
quickChatPerfStartedAt,
|
|
971
|
+
"openai-response-first-sse-event",
|
|
972
|
+
{ dialogKey, eventCount: events.length }
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
const eventList = Array.isArray(events) ? events : [events];
|
|
976
|
+
for (const event of eventList) {
|
|
977
|
+
if (event?.usage) {
|
|
978
|
+
state.usage = updateTotalUsage(state.usage, event.usage);
|
|
979
|
+
}
|
|
980
|
+
if (event?.type === "error" || event?.error) {
|
|
981
|
+
const streamError = new Error(getStreamErrorMessage(event));
|
|
982
|
+
if (canRetryInitialResponseAttempt(attempt, loggedFirstVisibleDelta, finishReason) && isRetryableInitialStreamError(streamError)) {
|
|
983
|
+
await safeCancel(reader);
|
|
984
|
+
reader = void 0;
|
|
985
|
+
resetStateForRetry();
|
|
986
|
+
await waitForInitialStreamRetry(1500, signal);
|
|
987
|
+
continue attemptLoop;
|
|
988
|
+
}
|
|
989
|
+
state.content += `
|
|
990
|
+
[Error: ${getStreamErrorMessage(event)}]`;
|
|
991
|
+
await finalize();
|
|
992
|
+
return buildMeta();
|
|
993
|
+
}
|
|
994
|
+
switch (event?.type) {
|
|
995
|
+
case "response.output_text.delta":
|
|
996
|
+
if (event.delta) {
|
|
997
|
+
state.content += event.delta;
|
|
998
|
+
state.contentBuffer = seg(state.content);
|
|
999
|
+
if (!loggedFirstVisibleDelta) {
|
|
1000
|
+
loggedFirstVisibleDelta = true;
|
|
1001
|
+
logQuickChatPerfStage(
|
|
1002
|
+
quickChatPerfStartedAt,
|
|
1003
|
+
"openai-response-first-visible-delta",
|
|
1004
|
+
{ dialogKey }
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
flush();
|
|
1008
|
+
}
|
|
1009
|
+
break;
|
|
1010
|
+
case "response.reasoning.delta":
|
|
1011
|
+
if (event.delta?.text) {
|
|
1012
|
+
state.reasoning += event.delta.text;
|
|
1013
|
+
flush();
|
|
1014
|
+
}
|
|
1015
|
+
break;
|
|
1016
|
+
case "response.reasoning.done":
|
|
1017
|
+
if (event.text) {
|
|
1018
|
+
state.reasoning += event.text;
|
|
1019
|
+
flush();
|
|
1020
|
+
}
|
|
1021
|
+
break;
|
|
1022
|
+
case "response.output_item.added":
|
|
1023
|
+
case "response.output_item.done": {
|
|
1024
|
+
const item = event.item;
|
|
1025
|
+
if (!state.content) {
|
|
1026
|
+
const itemText = extractTextFromOutputItem(item);
|
|
1027
|
+
if (itemText) {
|
|
1028
|
+
state.content = itemText;
|
|
1029
|
+
state.contentBuffer = seg(state.content);
|
|
1030
|
+
if (!loggedFirstVisibleDelta) {
|
|
1031
|
+
loggedFirstVisibleDelta = true;
|
|
1032
|
+
logQuickChatPerfStage(
|
|
1033
|
+
quickChatPerfStartedAt,
|
|
1034
|
+
"openai-response-first-visible-delta",
|
|
1035
|
+
{ dialogKey }
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
flush();
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
if (item?.type === "function_call") {
|
|
1042
|
+
ensureToolCall(state, item.call_id || item.id, {
|
|
1043
|
+
id: item.call_id || item.id,
|
|
1044
|
+
function: {
|
|
1045
|
+
name: item.name || "",
|
|
1046
|
+
arguments: typeof item.arguments === "string" ? item.arguments : ""
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
case "response.function_call_arguments.delta": {
|
|
1053
|
+
const key = event.call_id || event.item_id || `${event.output_index ?? 0}`;
|
|
1054
|
+
const toolCall = ensureToolCall(state, key, {
|
|
1055
|
+
id: event.call_id || key,
|
|
1056
|
+
function: { name: event.name || "", arguments: "" }
|
|
1057
|
+
});
|
|
1058
|
+
toolCall.function.arguments += event.delta ?? "";
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
case "response.function_call_arguments.done": {
|
|
1062
|
+
const key = event.call_id || event.item_id || `${event.output_index ?? 0}`;
|
|
1063
|
+
ensureToolCall(state, key, {
|
|
1064
|
+
id: event.call_id || key,
|
|
1065
|
+
function: {
|
|
1066
|
+
name: event.name || "",
|
|
1067
|
+
arguments: typeof event.arguments === "string" ? event.arguments : typeof event.output?.arguments === "string" ? event.output.arguments : ""
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
break;
|
|
1071
|
+
}
|
|
1072
|
+
case "response.completed":
|
|
1073
|
+
state.completedResponse = event.response ?? null;
|
|
1074
|
+
finishReason = event.response?.status === "completed" ? "stop" : event.response?.status ?? null;
|
|
1075
|
+
if (event.response?.usage) {
|
|
1076
|
+
state.usage = updateTotalUsage(state.usage, event.response.usage);
|
|
1077
|
+
}
|
|
1078
|
+
break;
|
|
1079
|
+
case "response.failed":
|
|
1080
|
+
state.content += `
|
|
1081
|
+
[API Failed: ${event.response?.error?.message || "unknown"}]`;
|
|
1082
|
+
finishReason = "error";
|
|
1083
|
+
await finalize();
|
|
1084
|
+
return buildMeta(false, false, finishReason);
|
|
1085
|
+
case "response.incomplete":
|
|
1086
|
+
state.content += `
|
|
1087
|
+
[Incomplete: ${event.response?.incomplete_details?.reason || "unknown"}]`;
|
|
1088
|
+
finishReason = "incomplete";
|
|
1089
|
+
await finalize();
|
|
1090
|
+
return buildMeta(false, false, finishReason);
|
|
1091
|
+
default:
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
if (canRetryInitialResponseAttempt(attempt, loggedFirstVisibleDelta, finishReason) && isRetryableInitialStreamError(
|
|
1098
|
+
error ?? new Error("response stream ended before first visible delta")
|
|
1099
|
+
)) {
|
|
1100
|
+
await safeCancel(reader);
|
|
1101
|
+
reader = void 0;
|
|
1102
|
+
resetStateForRetry();
|
|
1103
|
+
await waitForInitialStreamRetry(1500, signal);
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
throw error;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return buildMeta(false, false, null);
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
state.content += isAbortError(error) ? "[\u7528\u6237\u4E2D\u65AD]" : `[\u5F02\u5E38: ${error?.message || "unknown"}]`;
|
|
1112
|
+
await finalize();
|
|
1113
|
+
return buildMeta(false, false, "error");
|
|
1114
|
+
} finally {
|
|
1115
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-response-stream-finished", {
|
|
1116
|
+
dialogKey
|
|
1117
|
+
});
|
|
1118
|
+
dispatch(removeActiveController({ messageId, dialogKey }));
|
|
1119
|
+
await safeCancel(reader);
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
// packages/ai/agent/cleanAgentMessages.ts
|
|
1124
|
+
var isEmptyContent = (content) => {
|
|
1125
|
+
if (content == null) return true;
|
|
1126
|
+
if (typeof content === "string") {
|
|
1127
|
+
return content.trim().length === 0;
|
|
1128
|
+
}
|
|
1129
|
+
if (Array.isArray(content)) {
|
|
1130
|
+
return content.length === 0;
|
|
1131
|
+
}
|
|
1132
|
+
return false;
|
|
1133
|
+
};
|
|
1134
|
+
var buildAgentViewMessages = (allMessages, currentAgentKey) => {
|
|
1135
|
+
const result = [];
|
|
1136
|
+
for (const msg of allMessages) {
|
|
1137
|
+
if (!msg) continue;
|
|
1138
|
+
const role = msg.role;
|
|
1139
|
+
if (!role) continue;
|
|
1140
|
+
const hasToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 || Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0;
|
|
1141
|
+
const empty = isEmptyContent(msg.content);
|
|
1142
|
+
if (role === "assistant" && empty && !hasToolCalls) {
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
const isCurrentAgent = msg.cybotKey === currentAgentKey;
|
|
1146
|
+
if (role === "user") {
|
|
1147
|
+
result.push(msg);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (role === "assistant") {
|
|
1151
|
+
if (isCurrentAgent) {
|
|
1152
|
+
result.push(msg);
|
|
1153
|
+
} else {
|
|
1154
|
+
const cloned = { ...msg };
|
|
1155
|
+
if (cloned.tool_calls) {
|
|
1156
|
+
delete cloned.tool_calls;
|
|
1157
|
+
}
|
|
1158
|
+
if (cloned.toolCalls) {
|
|
1159
|
+
delete cloned.toolCalls;
|
|
1160
|
+
}
|
|
1161
|
+
const rawAgentName = cloned.agentName;
|
|
1162
|
+
const agentName = rawAgentName && String(rawAgentName).trim() ? String(rawAgentName).trim() : "\u5176\u4ED6 Agent";
|
|
1163
|
+
if (typeof cloned.content === "string") {
|
|
1164
|
+
const trimmed = cloned.content.trim();
|
|
1165
|
+
if (trimmed) {
|
|
1166
|
+
cloned.content = `\u3010${agentName}\u3011 ${trimmed}`;
|
|
1167
|
+
} else {
|
|
1168
|
+
cloned.content = `\u3010${agentName}\u3011\uFF08\u4F7F\u7528\u4E86\u5DE5\u5177\u751F\u6210\u56DE\u590D\uFF09`;
|
|
1169
|
+
}
|
|
1170
|
+
} else {
|
|
1171
|
+
cloned.content = `\u3010${agentName}\u3011\uFF08\u4F7F\u7528\u4E86\u5DE5\u5177\u751F\u6210\u56DE\u590D\uFF09`;
|
|
1172
|
+
}
|
|
1173
|
+
result.push(cloned);
|
|
1174
|
+
}
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
if (role === "tool") {
|
|
1178
|
+
if (isCurrentAgent) {
|
|
1179
|
+
result.push(msg);
|
|
1180
|
+
} else {
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
result.push(msg);
|
|
1186
|
+
}
|
|
1187
|
+
return result;
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// packages/ai/agent/quickChatModelOverride.ts
|
|
1191
|
+
function applyQuickChatModelOverride(base, override) {
|
|
1192
|
+
return applyModelLayerOverride(
|
|
1193
|
+
base,
|
|
1194
|
+
override
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// packages/ai/agent/canvasEditingContext.ts
|
|
1199
|
+
function buildCanvasNodeEditingContextSummary(runtimeOptions) {
|
|
1200
|
+
if (runtimeOptions?.editingTarget?.kind !== "canvas_node") return null;
|
|
1201
|
+
const editingTarget = runtimeOptions.editingTarget;
|
|
1202
|
+
const metadata = editingTarget.metadata ?? {};
|
|
1203
|
+
const selectedNodeId = typeof metadata.selectedNodeId === "string" ? metadata.selectedNodeId : editingTarget.key ?? "(\u672A\u77E5\u8282\u70B9)";
|
|
1204
|
+
const part = typeof metadata.part === "string" ? metadata.part : editingTarget.title ?? selectedNodeId;
|
|
1205
|
+
const nodeType = typeof metadata.type === "string" ? metadata.type : "(\u672A\u77E5\u7C7B\u578B)";
|
|
1206
|
+
const path = Array.isArray(metadata.path) ? metadata.path.filter((item) => typeof item === "string") : [];
|
|
1207
|
+
const props = asRecordOrEmpty(metadata.props);
|
|
1208
|
+
const style = asRecordOrEmpty(metadata.style);
|
|
1209
|
+
return [
|
|
1210
|
+
"\u5F53\u524D\u7F16\u8F91\u76EE\u6807\uFF1ACanvas Tree \u4E2D\u7684\u4E00\u4E2A\u9009\u4E2D\u8282\u70B9\u3002",
|
|
1211
|
+
`- \u8282\u70B9 ID: ${selectedNodeId}`,
|
|
1212
|
+
`- part: ${part}`,
|
|
1213
|
+
`- \u8282\u70B9\u7C7B\u578B: ${nodeType}`,
|
|
1214
|
+
...path.length ? [`- \u8282\u70B9\u8DEF\u5F84: ${path.join(" > ")}`] : [],
|
|
1215
|
+
"",
|
|
1216
|
+
"\u5F53\u524D\u8282\u70B9 props:",
|
|
1217
|
+
JSON.stringify(props, null, 2),
|
|
1218
|
+
"",
|
|
1219
|
+
"\u5F53\u524D\u8282\u70B9 style:",
|
|
1220
|
+
JSON.stringify(style, null, 2),
|
|
1221
|
+
"",
|
|
1222
|
+
"\u3010\u7ED9 AI \u7684\u64CD\u4F5C\u6307\u5357 / \u975E\u7528\u6237\u539F\u8BDD\u3011",
|
|
1223
|
+
`1. \u5982\u679C\u7528\u6237\u8981\u6C42\u4FEE\u6539\u5F53\u524D\u6A21\u5757\uFF0C\u53EA\u8F93\u51FA updateNode\uFF0C\u76EE\u6807 id \u5FC5\u987B\u662F ${selectedNodeId}\u3002`,
|
|
1224
|
+
"2. \u4E0D\u8981\u91CD\u5EFA root/shell\uFF0C\u4E0D\u8981\u91CD\u65B0 append \u5DF2\u5B58\u5728\u7684\u5927\u5757\u5185\u5BB9\u3002",
|
|
1225
|
+
"3. \u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u65B0\u589E\u5B50\u6A21\u5757\uFF0C\u5426\u5219\u4E0D\u8981 appendNode\u3002",
|
|
1226
|
+
"4. \u56DE\u590D\u4ECD\u5FC5\u987B\u9075\u5B88 Canvas Tree MVP \u534F\u8BAE\uFF1A\u53EA\u8F93\u51FA canvas_snapshot NDJSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u89E3\u91CA\u6216\u6E90\u7801\u3002"
|
|
1227
|
+
].join("\n");
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// packages/ai/agent/buildEditingContext.ts
|
|
1231
|
+
var buildAppConstraintPacks = (args) => {
|
|
1232
|
+
const imports = new Set(args.externalImports);
|
|
1233
|
+
const fileSet = new Set(args.fileNames);
|
|
1234
|
+
const packs = [
|
|
1235
|
+
{
|
|
1236
|
+
id: "repair-loop",
|
|
1237
|
+
title: "\u9884\u68C0\u4E0E\u5B9A\u70B9\u4FEE\u590D",
|
|
1238
|
+
rules: [
|
|
1239
|
+
"\u6BCF\u6B21\u6539\u5B8C\u4EE3\u7801\u540E\uFF0C\u5148\u8C03\u7528 appPreflight\uFF0C\u53EA\u6709\u9884\u68C0\u901A\u8FC7\u540E\u518D\u8C03\u7528 appDeploy\u3002",
|
|
1240
|
+
"\u5982\u679C preflight / deploy \u5931\u8D25\uFF0C\u4F18\u5148\u6839\u636E\u8FD4\u56DE\u7684 issues \u505A\u5B9A\u70B9\u4FEE\u590D\uFF0C\u7136\u540E\u91CD\u65B0 preflight\uFF1B\u4E0D\u8981\u65E0\u5173\u91CD\u5199\u6574\u9875\u5E94\u7528\u3002",
|
|
1241
|
+
"\u5982\u679C\u5DE5\u5177\u7ED3\u679C\u91CC\u5E26\u6709 repairPlan\uFF0C\u9ED8\u8BA4\u7ACB\u5373\u6267\u884C repairPlan \u4E2D\u7684\u5C40\u90E8\u4FEE\u590D\u6B65\u9AA4\uFF0C\u4E0D\u8981\u5148\u505C\u4E0B\u6765\u95EE\u7528\u6237\u3002",
|
|
1242
|
+
"\u5982\u679C\u5DE5\u5177\u7ED3\u679C\u660E\u786E\u8BF4\u660E\u662F HTML / \u975E JSON \u54CD\u5E94\u3001transport failure \u6216 retryable=false\uFF0C\u505C\u6B62\u81EA\u52A8 deploy \u91CD\u8BD5\uFF1B\u8FD9\u8BF4\u660E\u5F53\u524D\u662F\u5E73\u53F0\u901A\u9053\u5F02\u5E38\uFF0C\u4E0D\u662F\u4EE3\u7801\u95EE\u9898\u3002"
|
|
1243
|
+
]
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
id: "design-system",
|
|
1247
|
+
title: "\u8BBE\u8BA1\u7CFB\u7EDF\u4E0E\u5C0F\u6539\u52A8\u7EA6\u675F",
|
|
1248
|
+
rules: [
|
|
1249
|
+
"\u5148\u68C0\u67E5\u5F53\u524D\u5E94\u7528\u662F\u5426\u5DF2\u7ECF\u6709 theme / tokens / designSystem / \u5171\u4EAB\u6837\u5F0F\u5E38\u91CF\uFF1B\u5982\u679C\u5DF2\u6709\uFF0C\u4F18\u5148\u6CBF\u7528\u8FD9\u5957\u8BBE\u8BA1\u7CFB\u7EDF\uFF0C\u4E0D\u8981\u5E73\u884C\u518D\u9020\u4E00\u5957\u3002",
|
|
1250
|
+
"\u5982\u679C\u5F53\u524D\u5E94\u7528\u8FD8\u6CA1\u6709\u8BBE\u8BA1\u7CFB\u7EDF\uFF0C\u800C\u7528\u6237\u9700\u6C42\u6D89\u53CA UI / \u6837\u5F0F / \u65B0\u9875\u9762\uFF0C\u4F18\u5148\u8865\u4E00\u5C42\u6700\u5C0F\u5171\u4EAB token\uFF08colors\u3001typography\u3001spacing\u3001radius\u3001shadow\uFF09\uFF0C\u518D\u8BA9\u7EC4\u4EF6\u6D88\u8D39\u8FD9\u4E9B token\u3002",
|
|
1251
|
+
"\u5982\u679C\u5F53\u524D\u5E94\u7528\u662F\u65E7\u5199\u6CD5\uFF1A\u89C6\u89C9\u503C\u6563\u843D\u5728\u591A\u4E2A\u7EC4\u4EF6\u7684\u786C\u7F16\u7801 style / \u5E38\u91CF\u91CC\uFF0C\u800C\u672C\u6B21\u9700\u6C42\u53EA\u662F\u8C03\u5B57\u4F53\u3001\u989C\u8272\u3001\u95F4\u8DDD\u3001\u5706\u89D2\u3001\u9634\u5F71\uFF0C\u9ED8\u8BA4\u6267\u884C\u4E00\u6B21\u6700\u5C0F token \u8FC1\u79FB\uFF0C\u518D\u5728 token \u5C42\u5B8C\u6210\u8C03\u6574\uFF1B\u4E0D\u8981\u7EE7\u7EED\u628A\u65B0\u6570\u5B57\u6563\u843D\u5199\u56DE\u591A\u4E2A\u4F4D\u7F6E\u3002",
|
|
1252
|
+
"\u5F53\u7528\u6237\u53EA\u662F\u60F3\u8C03\u5B57\u4F53\u5927\u5C0F\u3001\u5B57\u91CD\u3001\u989C\u8272\u3001\u5706\u89D2\u3001\u9634\u5F71\u3001\u95F4\u8DDD\u7B49\u89C6\u89C9\u53C2\u6570\u65F6\uFF0C\u4F18\u5148\u4FEE\u6539 token \u6216\u547D\u4E2D\u7684\u5C40\u90E8\u7EC4\u4EF6\uFF0C\u4E0D\u8981\u987A\u624B\u91CD\u5199\u6574\u4E2A\u9875\u9762\u7ED3\u6784\u3002",
|
|
1253
|
+
"\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u6574\u4F53\u6539\u7248\u6216\u91CD\u505A\u98CE\u683C\uFF0C\u5426\u5219\u4E0D\u8981\u8FDE\u5E26\u4FEE\u6539\u5E03\u5C40\u3001\u7EC4\u4EF6\u6811\u3001\u6587\u6848\u3001\u6570\u636E\u6D41\u3001\u8DEF\u7531\u6216\u672A\u547D\u4E2D\u7684\u9875\u9762\u3002",
|
|
1254
|
+
"\u5982\u679C\u53EA\u662F\u5C0F\u6539\u4E00\u5904\uFF0C\u5C3D\u91CF\u628A\u53D8\u66F4\u6536\u655B\u5728\u5C11\u6570\u6587\u4EF6\uFF0C\u5E76\u4FDD\u6301\u672A\u547D\u4E2D\u6587\u4EF6\u7684\u7ED3\u6784\u4E0E\u547D\u540D\u7A33\u5B9A\u3002"
|
|
1255
|
+
]
|
|
1256
|
+
}
|
|
1257
|
+
];
|
|
1258
|
+
if (args.framework === "react-spa" || fileSet.has("main.tsx") || fileSet.has("App.tsx")) {
|
|
1259
|
+
packs.push({
|
|
1260
|
+
id: "react-spa-core",
|
|
1261
|
+
title: "React SPA \u5F62\u6001\u4FDD\u6301",
|
|
1262
|
+
rules: [
|
|
1263
|
+
'\u7EE7\u7EED\u6CBF\u7528 framework: "react-spa" + files\uFF0C\u4E0D\u8981\u9000\u56DE\u5355\u6587\u4EF6 Worker\u3002',
|
|
1264
|
+
"\u4FDD\u7559\u7A33\u5B9A\u5165\u53E3\u7ED3\u6784\uFF0C\u4F18\u5148\u7EF4\u62A4 main.tsx + App.tsx\uFF0C\u518D\u5C40\u90E8\u4FEE\u6539\u7EC4\u4EF6\u6587\u4EF6\u3002"
|
|
1265
|
+
]
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
if (imports.has("react-icons/lu")) {
|
|
1269
|
+
packs.push({
|
|
1270
|
+
id: "react-icons-lu",
|
|
1271
|
+
title: "Lucide \u56FE\u6807\u5B89\u5168\u89C4\u5219",
|
|
1272
|
+
rules: [
|
|
1273
|
+
"\u53EA\u80FD\u4F7F\u7528 react-icons/lu \u4E2D\u771F\u5B9E\u5B58\u5728\u7684\u56FE\u6807\u540D\uFF0C\u4E0D\u8981\u731C\u6D4B\u53D8\u4F53\u540D\u3002",
|
|
1274
|
+
"\u5982\u679C\u4E0D\u786E\u5B9A\u56FE\u6807\u540D\uFF0C\u4F18\u5148\u4F7F\u7528 LuCircle\u3001LuCheck\u3001LuX\u3001LuInfo\u3001LuArrowRight \u8FD9\u7C7B\u57FA\u7840\u56FE\u6807\u3002"
|
|
1275
|
+
]
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
if (imports.has("leaflet") || imports.has("react-leaflet")) {
|
|
1279
|
+
packs.push({
|
|
1280
|
+
id: "leaflet",
|
|
1281
|
+
title: "Leaflet \u5730\u56FE\u7EA6\u675F",
|
|
1282
|
+
rules: [
|
|
1283
|
+
"\u4E0D\u8981\u624B\u52A8 import leaflet.css\uFF1B\u5E73\u53F0\u4F1A\u81EA\u52A8\u6CE8\u5165 Leaflet \u6837\u5F0F\u3002",
|
|
1284
|
+
"\u4FEE\u6539\u5730\u56FE\u65F6\u4F18\u5148\u4FDD\u7559\u73B0\u6709\u5750\u6807\u3001\u7F29\u653E\u548C\u56FE\u5C42\u7ED3\u6784\uFF0C\u53EA\u505A\u5FC5\u8981\u7684\u4EA4\u4E92\u6216\u89C6\u89C9\u8C03\u6574\u3002"
|
|
1285
|
+
]
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
if (imports.has("@xyflow/react")) {
|
|
1289
|
+
packs.push({
|
|
1290
|
+
id: "xyflow",
|
|
1291
|
+
title: "Flow \u56FE\u7F16\u8F91\u7EA6\u675F",
|
|
1292
|
+
rules: [
|
|
1293
|
+
"\u4E0D\u8981\u65B0\u589E\u5916\u90E8 CSS import\uFF1B\u9700\u8981\u6837\u5F0F\u65F6\u76F4\u63A5\u8865\u6700\u5C0F\u5FC5\u8981\u5185\u8054\u6837\u5F0F\u6216\u7EC4\u4EF6\u5185 style \u6807\u7B7E\u3002",
|
|
1294
|
+
"\u4F18\u5148\u590D\u7528\u73B0\u6709\u8282\u70B9/\u8FB9\u6570\u636E\u7ED3\u6784\uFF0C\u907F\u514D\u91CD\u5199\u6574\u5957 flow \u753B\u5E03\u3002"
|
|
1295
|
+
]
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
if (imports.has("echarts") || imports.has("echarts-for-react")) {
|
|
1299
|
+
packs.push({
|
|
1300
|
+
id: "echarts",
|
|
1301
|
+
title: "\u56FE\u8868\u6539\u52A8\u7EA6\u675F",
|
|
1302
|
+
rules: [
|
|
1303
|
+
"\u4F18\u5148\u5C40\u90E8\u4FEE\u6539 option \u914D\u7F6E\u3001\u6570\u636E\u6620\u5C04\u548C\u7EC4\u4EF6 props\uFF0C\u4E0D\u8981\u91CD\u5199\u6574\u4E2A\u56FE\u8868\u9875\u9762\u3002",
|
|
1304
|
+
"\u54CD\u5E94\u5F0F\u5E03\u5C40\u4F18\u5148\u9760\u5BB9\u5668\u5C3A\u5BF8\u548C\u73B0\u6709\u7EC4\u4EF6\u7ED3\u6784\u8C03\u6574\uFF0C\u4E0D\u8981\u989D\u5916\u5F15\u5165 CSS \u6587\u4EF6\u3002"
|
|
1305
|
+
]
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
if (imports.has("docx") || imports.has("xlsx")) {
|
|
1309
|
+
packs.push({
|
|
1310
|
+
id: "file-processing",
|
|
1311
|
+
title: "\u6587\u6863/\u8868\u683C\u5904\u7406\u7EA6\u675F",
|
|
1312
|
+
rules: [
|
|
1313
|
+
"\u4E0D\u8981\u628A\u5927\u578B\u6570\u636E\u6216\u6A21\u677F\u5185\u5BB9\u76F4\u63A5\u5185\u5D4C\u8FDB\u4EE3\u7801\u5E38\u91CF\uFF1B\u4F18\u5148\u4FDD\u7559\u8FD0\u884C\u65F6\u52A0\u8F7D\u65B9\u5F0F\u3002",
|
|
1314
|
+
"\u4FEE\u6539\u5BFC\u5165\u5BFC\u51FA\u903B\u8F91\u65F6\uFF0C\u4F18\u5148\u6CBF\u7528\u73B0\u6709\u6587\u4EF6\u5904\u7406\u94FE\u8DEF\u548C\u6570\u636E\u7ED3\u6784\u3002"
|
|
1315
|
+
]
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
return packs;
|
|
1319
|
+
};
|
|
1320
|
+
var formatAppConstraintPacks = (packs) => {
|
|
1321
|
+
if (packs.length === 0) return [];
|
|
1322
|
+
return [
|
|
1323
|
+
"",
|
|
1324
|
+
"\u5F53\u524D\u6FC0\u6D3B\u7EA6\u675F\u5305\uFF08\u6309\u5F53\u524D\u5E94\u7528\u4F9D\u8D56/\u5F62\u6001\u52A8\u6001\u6CE8\u5165\uFF09\uFF1A",
|
|
1325
|
+
...packs.flatMap((pack) => [
|
|
1326
|
+
`- ${pack.title}\uFF08${pack.id}\uFF09`,
|
|
1327
|
+
...pack.rules.map((rule) => ` - ${rule}`)
|
|
1328
|
+
])
|
|
1329
|
+
];
|
|
1330
|
+
};
|
|
1331
|
+
var buildEditingContextSummary = (state, runtimeOptions) => {
|
|
1332
|
+
const targetKind = runtimeOptions?.editingTarget?.kind;
|
|
1333
|
+
if (targetKind === "table") {
|
|
1334
|
+
const table = selectCurrentTable(state);
|
|
1335
|
+
const rows = selectTableRows(state);
|
|
1336
|
+
const metadata = runtimeOptions?.editingTarget?.metadata;
|
|
1337
|
+
const focusContext = metadata && typeof metadata === "object" ? metadata.focusContext : null;
|
|
1338
|
+
if (!table) return null;
|
|
1339
|
+
const columns = Array.isArray(table.columns) ? table.columns : [];
|
|
1340
|
+
const columnSummaries = columns.length ? columns.map((c) => {
|
|
1341
|
+
const displayName = c.label || c.name;
|
|
1342
|
+
const type = c.type || "text";
|
|
1343
|
+
const requiredFlag = c.required ? "\u5FC5\u586B" : "\u53EF\u9009";
|
|
1344
|
+
const primaryFlag = c.isPrimary ? "\uFF0C\u4E3B\u5B57\u6BB5" : "";
|
|
1345
|
+
const optionsStr = Array.isArray(c.options) && c.options.length ? `\uFF0C\u53EF\u9009\u503C\uFF1A${c.options.join(" | ")}` : "";
|
|
1346
|
+
const descStr = c.description ? `\u3002\u8BF4\u660E\uFF1A${c.description}` : "";
|
|
1347
|
+
return `- ${c.name}\uFF08\u663E\u793A\u540D\uFF1A${displayName}\uFF0C\u7C7B\u578B\uFF1A${type}\uFF0C${requiredFlag}${primaryFlag}${optionsStr}${descStr}\uFF09`;
|
|
1348
|
+
}) : ["- (\u5F53\u524D\u8868\u5C1A\u672A\u5B9A\u4E49\u5B57\u6BB5)"];
|
|
1349
|
+
const sampleRows = Array.isArray(rows) ? rows.slice(0, 20) : [];
|
|
1350
|
+
const rowsPreview = sampleRows.length ? JSON.stringify(sampleRows, null, 2) : "(\u5F53\u524D\u8868\u6682\u65E0\u884C\u6570\u636E)";
|
|
1351
|
+
const tableTitle = table.displayName ?? table.tableId ?? "(\u672A\u547D\u540D\u8868)";
|
|
1352
|
+
const tableDesc = table.description ? `\u7528\u9014\u8BF4\u660E\uFF1A${table.description}` : "(\u6682\u65E0\u7528\u9014\u8BF4\u660E)";
|
|
1353
|
+
const tableTagsLine = Array.isArray(table.tags) && table.tags.length ? `- \u5173\u952E\u8BCD\u6807\u7B7E: ${table.tags.join(", ")}` : null;
|
|
1354
|
+
return [
|
|
1355
|
+
"\u5F53\u524D\u7F16\u8F91\u76EE\u6807\uFF1A\u4E00\u5F20\u6570\u636E\u8868\uFF08Editing Table\uFF09\u3002",
|
|
1356
|
+
`- \u8868 ID: ${table.tableId}`,
|
|
1357
|
+
`- \u663E\u793A\u540D\u79F0: ${tableTitle}`,
|
|
1358
|
+
`- ${tableDesc}`,
|
|
1359
|
+
...tableTagsLine ? [tableTagsLine] : [],
|
|
1360
|
+
...focusContext && typeof focusContext === "object" && "columnName" in focusContext ? [
|
|
1361
|
+
"",
|
|
1362
|
+
"\u5F53\u524D\u7126\u70B9\uFF08Focus Context\uFF09\uFF1A",
|
|
1363
|
+
`- \u5F53\u524D\u5355\u5143\u683C\u5217: ${String(focusContext.columnName ?? "(\u672A\u77E5\u5217)")}`,
|
|
1364
|
+
...focusContext.rowTitle ? [`- \u5F53\u524D\u884C\u6807\u9898: ${String(focusContext.rowTitle)}`] : [],
|
|
1365
|
+
...focusContext.rowIndex !== null && focusContext.rowIndex !== void 0 ? [`- \u5F53\u524D\u884C\u53F7: ${Number(focusContext.rowIndex) + 1}`] : [],
|
|
1366
|
+
...focusContext.cellPreview ? [`- \u5F53\u524D\u5355\u5143\u683C\u5185\u5BB9\u9884\u89C8: ${String(focusContext.cellPreview)}`] : []
|
|
1367
|
+
] : [],
|
|
1368
|
+
"",
|
|
1369
|
+
"\u5B57\u6BB5\u5B9A\u4E49\uFF08Field Schema\uFF09\uFF1A",
|
|
1370
|
+
...columnSummaries,
|
|
1371
|
+
"",
|
|
1372
|
+
"\u3010\u7ED9 AI \u7684\u64CD\u4F5C\u6307\u5357 / \u975E\u7528\u6237\u539F\u8BDD\u3011",
|
|
1373
|
+
"\u5F53\u7528\u6237\u5E0C\u671B\u5728\u5F53\u524D\u8868\u4E2D\u201C\u65B0\u589E\u4E00\u884C / \u65B0\u589E\u8BB0\u5F55 / \u63D2\u5165\u4E00\u6761 / \u5E2E\u6211\u8BB0\u4E00\u6761 xxx\u201D\u65F6\uFF1A",
|
|
1374
|
+
"1. \u5FC5\u987B\u8C03\u7528\u5DE5\u5177 addTableRow\uFF08\u800C\u4E0D\u662F\u53EA\u5728\u56DE\u7B54\u4E2D\u53E3\u5934\u63CF\u8FF0\u8981\u65B0\u589E\u7684\u6570\u636E\uFF09\u3002",
|
|
1375
|
+
"2. \u8C03\u7528 addTableRow \u65F6\uFF1A",
|
|
1376
|
+
" - \u4F7F\u7528\u53C2\u6570 values\uFF08\u4E00\u4E2A\u5BF9\u8C61\uFF09\uFF0C\u5176\u4E2D\u6BCF\u4E2A key \u5FC5\u987B\u662F\u4E0A\u9762\u5B57\u6BB5\u540D\u4E4B\u4E00\uFF08name\uFF0C\u800C\u4E0D\u662F label\uFF09\u3002",
|
|
1377
|
+
' - \u4F8B\u5982\uFF1A{"values":{"title":"\u4FEE Bug #123","status":"todo","note":"\u9AD8\u4F18\u5148\u7EA7"}}\u3002',
|
|
1378
|
+
" - \u5C3D\u91CF\u4ECE\u7528\u6237\u7684\u81EA\u7136\u8BED\u8A00\u4E2D\u63A8\u65AD\u5E76\u586B\u6EE1\u6240\u6709\u76F8\u5173\u5B57\u6BB5\uFF1B\u5BF9\u4E8E\u5FC5\u586B\u5B57\u6BB5\uFF08required=true\uFF09\u5C24\u5176\u8981\u6CE8\u610F\u3002",
|
|
1379
|
+
' - \u7528\u6237\u6CA1\u6709\u63D0\u5230\u7684\u5B57\u6BB5\uFF0C\u53EF\u4EE5\u4F7F\u7528\u7A7A\u5B57\u7B26\u4E32 "" \u6216 null \u4F5C\u4E3A\u5360\u4F4D\u3002',
|
|
1380
|
+
" - \u7EDD\u4E0D\u8981\u4F20\u5165\u7A7A\u5BF9\u8C61 {} \u4F5C\u4E3A values\u3002",
|
|
1381
|
+
"",
|
|
1382
|
+
"\u4EE5\u4E0B\u662F\u90E8\u5206\u793A\u4F8B\u884C\uFF08\u6700\u591A 20 \u884C\uFF0C\u7528\u4E8E\u5E2E\u52A9\u4F60\u7406\u89E3\u5217\u542B\u4E49\uFF0C\u8BF7\u907F\u514D\u5728\u56DE\u7B54\u4E2D\u5B8C\u6574\u7C98\u8D34\u6574\u8868\uFF09\uFF1A",
|
|
1383
|
+
rowsPreview
|
|
1384
|
+
].join("\n");
|
|
1385
|
+
}
|
|
1386
|
+
if (targetKind === "page" || targetKind === "article") {
|
|
1387
|
+
const page = getDocState();
|
|
1388
|
+
const metadata = runtimeOptions?.editingTarget?.metadata;
|
|
1389
|
+
const focusContext = metadata && typeof metadata === "object" ? metadata.focusContext : null;
|
|
1390
|
+
if (!page) return null;
|
|
1391
|
+
const title = page.title ?? "(\u672A\u547D\u540D\u9875\u9762)";
|
|
1392
|
+
return [
|
|
1393
|
+
"\u5F53\u524D\u7F16\u8F91\u76EE\u6807\uFF1A\u4E00\u4E2A\u9875\u9762 / \u6587\u7AE0\uFF08Editing Document\uFF09\u3002",
|
|
1394
|
+
`- \u6807\u9898: ${title}`,
|
|
1395
|
+
...focusContext && typeof focusContext === "object" && "anchorPath" in focusContext ? [
|
|
1396
|
+
"",
|
|
1397
|
+
"\u5F53\u524D\u7126\u70B9\uFF08Focus Context\uFF09\uFF1A",
|
|
1398
|
+
`- \u5149\u6807\u662F\u5426\u6298\u53E0: ${Boolean(focusContext.isCollapsed) ? "\u662F" : "\u5426"}`,
|
|
1399
|
+
...focusContext.blockType ? [`- \u5F53\u524D\u5757\u7C7B\u578B: ${String(focusContext.blockType)}`] : [],
|
|
1400
|
+
...focusContext.selectedText ? [`- \u5F53\u524D\u9009\u4E2D\u6587\u672C: ${String(focusContext.selectedText)}`] : [],
|
|
1401
|
+
...focusContext.anchorPath?.length ? [`- \u5F53\u524D\u951A\u70B9\u8DEF\u5F84: ${focusContext.anchorPath.join(" > ")}`] : []
|
|
1402
|
+
] : [],
|
|
1403
|
+
"",
|
|
1404
|
+
"\u5982\u679C\u7528\u6237\u8981\u6C42\u4F60\u5BF9\u8FD9\u7BC7\u6587\u7AE0\u8FDB\u884C\u4FEE\u6539\u6216\u4F18\u5316\uFF0C\u8BF7\u5728\u56DE\u7B54\u4E2D\u660E\u786E\u6307\u51FA\u4FEE\u6539\u65B9\u5411\u3002",
|
|
1405
|
+
"\u5982\u679C\u5B58\u5728\u5F53\u524D\u9009\u533A\u6216\u5149\u6807\u4E0A\u4E0B\u6587\uFF0C\u4F18\u5148\u56F4\u7ED5\u8BE5\u5C40\u90E8\u4F4D\u7F6E\u505A\u5B9A\u70B9\u6539\u5199\uFF0C\u800C\u4E0D\u662F\u6CDB\u6CDB\u91CD\u5199\u6574\u7BC7\u6587\u6863\u3002"
|
|
1406
|
+
].join("\n");
|
|
1407
|
+
}
|
|
1408
|
+
if (targetKind === "app") {
|
|
1409
|
+
const editingTarget = runtimeOptions?.editingTarget;
|
|
1410
|
+
const metadata = editingTarget?.metadata;
|
|
1411
|
+
const appId = editingTarget?.key ?? "(\u672A\u77E5 appId)";
|
|
1412
|
+
const title = editingTarget?.title ?? "(\u672A\u547D\u540D\u5E94\u7528)";
|
|
1413
|
+
const framework = typeof metadata?.framework === "string" ? metadata.framework : "worker";
|
|
1414
|
+
const appUrl = typeof metadata?.appUrl === "string" ? metadata.appUrl : null;
|
|
1415
|
+
const fileNames = Array.isArray(metadata?.fileNames) ? metadata.fileNames.filter((name) => typeof name === "string") : [];
|
|
1416
|
+
const externalImports = Array.isArray(metadata?.externalImports) ? metadata.externalImports.filter(
|
|
1417
|
+
(name) => typeof name === "string"
|
|
1418
|
+
) : [];
|
|
1419
|
+
const sourceSummary = asOptionalTrimmedString(editingTarget?.summary) ?? null;
|
|
1420
|
+
const selectedNode = metadata && typeof metadata === "object" && metadata.selectedNode && typeof metadata.selectedNode === "object" ? metadata.selectedNode : null;
|
|
1421
|
+
const constraintPacks = buildAppConstraintPacks({
|
|
1422
|
+
framework,
|
|
1423
|
+
fileNames,
|
|
1424
|
+
externalImports
|
|
1425
|
+
});
|
|
1426
|
+
if (selectedNode && typeof selectedNode.cssPath === "string") {
|
|
1427
|
+
const tagName = typeof selectedNode.tagName === "string" && selectedNode.tagName ? selectedNode.tagName : "\u5143\u7D20";
|
|
1428
|
+
const cssPath = String(selectedNode.cssPath);
|
|
1429
|
+
const outerHTMLSnippet = typeof selectedNode.outerHTMLSnippet === "string" ? selectedNode.outerHTMLSnippet : "";
|
|
1430
|
+
const textSnippet = typeof selectedNode.textSnippet === "string" ? selectedNode.textSnippet : "";
|
|
1431
|
+
const noloLoc = typeof selectedNode.noloLoc === "string" ? selectedNode.noloLoc : "";
|
|
1432
|
+
constraintPacks.push({
|
|
1433
|
+
id: "selected-node",
|
|
1434
|
+
title: "\u7528\u6237\u9009\u4E2D\u8282\u70B9\uFF08\u5B9A\u70B9\u4FEE\u6539\uFF09",
|
|
1435
|
+
rules: [
|
|
1436
|
+
`\u7528\u6237\u5DF2\u5728\u9884\u89C8\u4E2D\u7CBE\u51C6\u9009\u4E2D\u4E00\u4E2A\u5143\u7D20\uFF1A<${tagName}> \uFF0CCSS \u8DEF\u5F84\uFF1A${cssPath}${noloLoc ? `\uFF0C\u6E90\u7801\u4F4D\u7F6E\uFF1A${noloLoc}` : ""}\u3002\u672C\u8F6E\u4FEE\u6539\u5FC5\u987B\u4EE5\u8FD9\u4E2A\u5143\u7D20\u4E3A\u4E2D\u5FC3\u3002`,
|
|
1437
|
+
`\u9009\u4E2D\u5143\u7D20\u7684 HTML \u7247\u6BB5\uFF08\u53EF\u80FD\u622A\u65AD\uFF09\uFF1A${outerHTMLSnippet}${textSnippet ? `\uFF1B\u53EF\u89C1\u6587\u672C\uFF1A${textSnippet}` : ""}\u3002\u5148\u5728\u6E90\u7801\u6587\u4EF6\u4E2D\u5B9A\u4F4D\u5230\u751F\u6210\u8FD9\u6BB5 HTML \u7684\u4EE3\u7801\uFF0C\u518D\u52A8\u624B\u3002`,
|
|
1438
|
+
"\u4FEE\u6539\u6536\u655B\u5728\u8BE5\u5143\u7D20\u53CA\u5176\u76F4\u63A5\u6837\u5F0F\u6765\u6E90\uFF08\u547D\u4E2D\u7684\u7EC4\u4EF6/\u7C7B/design token\uFF09\uFF1B\u8C03\u89C6\u89C9\u53C2\u6570\u4F18\u5148\u6539 token \u6216\u5C40\u90E8\u7EC4\u4EF6\u3002",
|
|
1439
|
+
"\u7981\u6B62\u91CD\u5199\u672A\u547D\u4E2D\u7684\u9875\u9762\u7ED3\u6784\u3001\u5E03\u5C40\u3001\u6587\u6848\u3001\u6570\u636E\u6D41\u6216\u5176\u4ED6\u7EC4\u4EF6\uFF1B\u4FDD\u6301\u672A\u547D\u4E2D\u6587\u4EF6\u4E0D\u53D8\u3002",
|
|
1440
|
+
"\u5982\u679C\u6309 CSS \u8DEF\u5F84\u548C HTML \u7247\u6BB5\u5728\u6E90\u7801\u4E2D\u627E\u4E0D\u5230\u5BF9\u5E94\u4F4D\u7F6E\uFF0C\u5148\u5411\u7528\u6237\u8BF4\u660E\u5E76\u8BF7\u7528\u6237\u91CD\u65B0\u9009\u62E9\uFF0C\u4E0D\u8981\u51ED\u731C\u6D4B\u5927\u8303\u56F4\u6539\u52A8\u3002"
|
|
1441
|
+
]
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
return [
|
|
1445
|
+
"\u5F53\u524D\u7F16\u8F91\u76EE\u6807\uFF1A\u4E00\u4E2A Web \u5E94\u7528\uFF08Editing App\uFF09\u3002",
|
|
1446
|
+
`- \u5E94\u7528 ID: ${appId}`,
|
|
1447
|
+
`- \u540D\u79F0: ${title}`,
|
|
1448
|
+
`- \u6280\u672F\u5F62\u6001: ${framework}`,
|
|
1449
|
+
...appUrl ? [`- \u5F53\u524D\u8BBF\u95EE\u5730\u5740: ${appUrl}`] : [],
|
|
1450
|
+
...fileNames.length ? [`- \u5F53\u524D\u6E90\u7801\u6587\u4EF6: ${fileNames.join(", ")}`] : ["- \u5F53\u524D\u6E90\u7801\u6587\u4EF6: (\u672A\u63D0\u4F9B\u591A\u6587\u4EF6\u6E05\u5355\uFF1B\u53EF\u80FD\u662F\u5355\u6587\u4EF6\u6E90\u7801\uFF0C\u4E5F\u53EF\u80FD\u53EA\u5269\u90E8\u7F72\u4EA7\u7269)"],
|
|
1451
|
+
...externalImports.length ? [`- \u5F53\u524D\u4F9D\u8D56\u767D\u540D\u5355\u547D\u4E2D: ${externalImports.join(", ")}`] : [],
|
|
1452
|
+
...sourceSummary ? ["", sourceSummary] : [],
|
|
1453
|
+
"",
|
|
1454
|
+
"\u3010\u7ED9 AI \u7684\u64CD\u4F5C\u6307\u5357 / \u975E\u7528\u6237\u539F\u8BDD\u3011",
|
|
1455
|
+
"1. \u7528\u6237\u8981\u6C42\u4FEE\u6539\u5F53\u524D\u5E94\u7528\u65F6\uFF0C\u5148\u8C03\u7528 appRead \u83B7\u53D6\u5F53\u524D\u4EE3\u7801/\u6587\u4EF6\uFF0C\u518D\u57FA\u4E8E\u73B0\u6709\u5B9E\u73B0\u4FEE\u6539\u3002",
|
|
1456
|
+
"2. \u5982\u679C appRead \u8FD4\u56DE workspaceRef/sourceFiles/sourceOmitted\uFF0C\u4E0D\u8981\u6574\u7AD9\u91CD\u5199\uFF1B\u4F7F\u7528 App Builder \u7684\u53D7\u9650 workspace \u6587\u4EF6\u5DE5\u5177\uFF1AappFileList=listFiles\u3001appFileSearch=searchFiles\u3001appFileRead=readFile\u3001appFileReplace=editFile\u3001appFileWrite=writeFile\u3002\u5148\u5B9A\u4F4D\u6587\u4EF6\u4E0E\u547D\u4E2D\u884C\uFF0C\u5927\u6587\u4EF6\u53EA\u8BFB\u53D6\u5FC5\u8981\u884C\u8303\u56F4\uFF1B\u6587\u5B57\u3001\u6837\u5F0F\u3001token\u3001\u5C40\u90E8\u903B\u8F91\u7B49\u5C0F\u6539\u52A8\u5FC5\u987B\u4F18\u5148\u7528 appFileReplace \u7CBE\u786E\u66FF\u6362\u552F\u4E00\u7247\u6BB5\uFF0C\u53EA\u6709\u65B0\u5EFA\u6587\u4EF6\u6216\u786E\u5B9E\u9700\u8981\u6574\u6587\u4EF6\u91CD\u5199\u65F6\u624D\u7528 appFileWrite\u3002",
|
|
1457
|
+
"3. \u5148\u8BC6\u522B\u5F53\u524D\u5E94\u7528\u662F\u5426\u5DF2\u6709 theme / tokens / design system\uFF1B\u5DF2\u6709\u5C31\u4F18\u5148\u6539\u8FD9\u5C42\uFF0C\u6CA1\u6709\u518D\u8865\u4E00\u5C42\u6700\u5C0F\u5171\u4EAB token\uFF0C\u518D\u57FA\u4E8E token \u8C03\u6574\u7EC4\u4EF6\u3002",
|
|
1458
|
+
"4. \u5982\u679C\u5F53\u524D\u5E94\u7528\u8FD8\u662F\u65E7\u5199\u6CD5\uFF1A\u89C6\u89C9\u503C\u6563\u843D\u5728\u7EC4\u4EF6\u786C\u7F16\u7801 style \u91CC\uFF0C\u800C\u7528\u6237\u53EA\u662F\u505A\u5B57\u4F53/\u989C\u8272/\u95F4\u8DDD\u7B49\u89C6\u89C9\u5FAE\u8C03\uFF0C\u9ED8\u8BA4\u5148\u628A\u547D\u4E2D\u7684\u89C6\u89C9\u503C\u62BD\u5230\u6700\u5C0F token \u5C42\uFF0C\u518D\u5B8C\u6210\u672C\u6B21\u4FEE\u6539\uFF1B\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4E0D\u8981\u91CD\u6784\u3002",
|
|
1459
|
+
`5. \u91CD\u65B0\u90E8\u7F72\u5F53\u524D\u5E94\u7528\u65F6\uFF0CappDeploy \u5FC5\u987B\u7EE7\u7EED\u4F20\u540C\u4E00\u4E2A appId\uFF08${appId}\uFF09\uFF0C\u907F\u514D\u521B\u5EFA\u65B0\u5E94\u7528\u3002`,
|
|
1460
|
+
framework === "nolo-react" ? "6. \u5F53\u524D\u5E94\u7528\u662F Nolo React SSR\uFF0C\u4FEE\u6539\u65F6\u4F18\u5148\u4F7F\u7528\u53D7\u9650 app workspace \u6587\u4EF6\u5DE5\u5177\u64CD\u4F5C\u6E90\u7801\u5DE5\u4F5C\u533A\uFF0C\u7136\u540E appPreflight/appDeploy\uFF1B\u4E0D\u8981\u9000\u56DE react-spa \u6216\u5355\u6587\u4EF6 Worker\u3002" : framework === "react-spa" ? '6. \u5F53\u524D\u5E94\u7528\u662F React SPA\uFF0C\u4FEE\u6539\u65F6\u7EE7\u7EED\u6CBF\u7528 framework: "react-spa" + files\uFF0C\u4E0D\u8981\u9000\u56DE\u5355\u6587\u4EF6 Worker\u3002' : "6. \u5F53\u524D\u5E94\u7528\u76EE\u524D\u662F Worker \u5F62\u6001\uFF1B\u5982\u679C\u9700\u6C42\u53D8\u6210\u590D\u6742\u4EA4\u4E92\u6216\u56FE\u8868\uFF0C\u53EF\u4EE5\u8BC4\u4F30\u5347\u7EA7\u4E3A React SPA\u3002",
|
|
1461
|
+
...fileNames.length ? [] : [
|
|
1462
|
+
"7. \u5F53\u524D\u6CA1\u6709\u6E90\u7801\u6587\u4EF6\u6E05\u5355\u65F6\uFF0C\u5FC5\u987B\u5148\u7528 appRead \u5224\u65AD\u8BFB\u5230\u7684\u662F\u53EF\u7EF4\u62A4\u6E90\u7801\u8FD8\u662F\u90E8\u7F72\u4EA7\u7269 / \u6253\u5305 bundle\u3002",
|
|
1463
|
+
"8. \u5982\u679C appRead \u8FD4\u56DE\u7684\u662F HTML \u58F3\u3001importmap\u3001\u538B\u7F29 bundle \u6216\u660E\u663E\u4E0D\u662F\u539F\u59CB\u6E90\u7801\u6587\u4EF6\uFF0C\u7981\u6B62\u5728\u672A\u544A\u77E5\u7528\u6237\u98CE\u9669\u7684\u60C5\u51B5\u4E0B\u6574\u7AD9\u91CD\u5199\uFF1B\u5E94\u5148\u8BF4\u660E\u201C\u5F53\u524D\u7F3A\u5C11\u539F\u59CB\u6E90\u7801\u5FEB\u7167\uFF0C\u7EE7\u7EED\u4FEE\u6539\u66F4\u50CF\u6574\u4F53\u91CD\u5EFA\u201D\uFF0C\u7B49\u7528\u6237\u786E\u8BA4\u540E\u518D\u7EE7\u7EED\u3002"
|
|
1464
|
+
],
|
|
1465
|
+
"9. \u5982\u679C\u7528\u6237\u53EA\u662F\u8981\u8C03\u5B57\u4F53\u5927\u5C0F\u3001\u914D\u8272\u3001\u5706\u89D2\u3001\u9634\u5F71\u3001\u7559\u767D\u7B49\u89C6\u89C9\u7EC6\u8282\uFF0C\u9ED8\u8BA4\u4F18\u5148\u6539\u8BBE\u8BA1 token \u6216\u547D\u4E2D\u7684\u5C40\u90E8\u7EC4\u4EF6\uFF1B\u5BF9\u65E7\u5199\u6CD5\u5E94\u7528\u5219\u4F18\u5148\u505A\u6700\u5C0F token \u8FC1\u79FB\uFF0C\u4E0D\u8981\u53EA\u6539\u6563\u843D\u786C\u7F16\u7801\u3002",
|
|
1466
|
+
"10. \u6BCF\u6B21\u4FEE\u6539\u5B8C\u6210\u540E\uFF0C\u5148 appPreflight\uFF0C\u518D appDeploy\uFF1B\u5982\u679C\u5931\u8D25\uFF0C\u6309\u8FD4\u56DE issues \u5B9A\u70B9\u4FEE\u590D\u3002",
|
|
1467
|
+
"11. \u56DE\u590D\u7528\u6237\u65F6\u4F18\u5148\u8BF4\u660E\u505A\u4E86\u4EC0\u4E48\u53D8\u5316\u3001\u73B0\u5728\u5E94\u7528\u53EF\u4EE5\u600E\u4E48\u7528\uFF0C\u800C\u4E0D\u662F\u76F4\u63A5\u5806\u4EE3\u7801\u3002",
|
|
1468
|
+
...formatAppConstraintPacks(constraintPacks)
|
|
1469
|
+
].join("\n");
|
|
1470
|
+
}
|
|
1471
|
+
if (targetKind === "image" || targetKind === "file") {
|
|
1472
|
+
const editingTarget = runtimeOptions?.editingTarget;
|
|
1473
|
+
const metadata = editingTarget?.metadata;
|
|
1474
|
+
const title = editingTarget?.title ?? "(\u672A\u547D\u540D\u5BF9\u8C61)";
|
|
1475
|
+
const objectKey = editingTarget?.key ?? "(\u672A\u77E5 key)";
|
|
1476
|
+
const fileId = typeof metadata?.fileId === "string" ? metadata.fileId : null;
|
|
1477
|
+
const url = typeof metadata?.url === "string" ? metadata.url : null;
|
|
1478
|
+
const size = typeof metadata?.size === "number" ? metadata.size : null;
|
|
1479
|
+
return [
|
|
1480
|
+
`\u5F53\u524D\u7F16\u8F91\u76EE\u6807\uFF1A\u4E00\u4E2A${targetKind === "image" ? "\u56FE\u7247" : "\u6587\u4EF6"}\u5BF9\u8C61\uFF08Editing ${targetKind === "image" ? "Image" : "File"}\uFF09\u3002`,
|
|
1481
|
+
`- \u5BF9\u8C61 key: ${objectKey}`,
|
|
1482
|
+
`- \u6807\u9898: ${title}`,
|
|
1483
|
+
...fileId ? [`- fileId: ${fileId}`] : [],
|
|
1484
|
+
...url ? [`- \u8D44\u6E90\u5730\u5740: ${url}`] : [],
|
|
1485
|
+
...size !== null ? [`- \u6587\u4EF6\u5927\u5C0F: ${size} bytes`] : [],
|
|
1486
|
+
"",
|
|
1487
|
+
"\u3010\u7ED9 AI \u7684\u64CD\u4F5C\u6307\u5357 / \u975E\u7528\u6237\u539F\u8BDD\u3011",
|
|
1488
|
+
targetKind === "image" ? "\u5F53\u524D\u9636\u6BB5\u4F18\u5148\u5E2E\u52A9\u7528\u6237\u7406\u89E3\u56FE\u7247\u5185\u5BB9\u3001\u63D0\u70BC\u91CD\u70B9\u3001\u547D\u540D\u5F52\u7C7B\u548C\u4E0B\u4E00\u6B65\u5904\u7406\u5EFA\u8BAE\u3002\u4E0D\u8981\u5047\u88C5\u5DF2\u7ECF\u5B8C\u6210\u590D\u6742\u56FE\u7247\u7F16\u8F91\u3002" : "\u5F53\u524D\u9636\u6BB5\u4F18\u5148\u5E2E\u52A9\u7528\u6237\u7406\u89E3\u6587\u4EF6\u7528\u9014\u3001\u63D0\u53D6\u5904\u7406\u601D\u8DEF\u3001\u7ED9\u51FA\u6574\u7406\u5EFA\u8BAE\u548C\u4E0B\u4E00\u6B65\u64CD\u4F5C\u5EFA\u8BAE\u3002\u4E0D\u8981\u5047\u88C5\u5DF2\u7ECF\u5B8C\u6574\u89E3\u6790\u6587\u4EF6\u5185\u5BB9\u3002"
|
|
1489
|
+
].join("\n");
|
|
1490
|
+
}
|
|
1491
|
+
if (targetKind === "canvas_node") {
|
|
1492
|
+
return buildCanvasNodeEditingContextSummary(runtimeOptions);
|
|
1493
|
+
}
|
|
1494
|
+
return null;
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
// packages/ai/agent/appWorkingMemory.ts
|
|
1498
|
+
var APP_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1499
|
+
"appList",
|
|
1500
|
+
"appRead",
|
|
1501
|
+
"appDeploy",
|
|
1502
|
+
"appPreflight",
|
|
1503
|
+
"appDelete"
|
|
1504
|
+
]);
|
|
1505
|
+
var contentToText = (content) => {
|
|
1506
|
+
if (!content) return "";
|
|
1507
|
+
if (typeof content === "string") return content;
|
|
1508
|
+
if (Array.isArray(content)) {
|
|
1509
|
+
return content.map((part) => "text" in part ? part.text ?? "" : "").join("\n");
|
|
1510
|
+
}
|
|
1511
|
+
return "";
|
|
1512
|
+
};
|
|
1513
|
+
var parseAppListEntries = (text) => text.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("- **") && line.includes("(appId:"));
|
|
1514
|
+
var buildRecentAppToolMemory = (messages, toolRuns) => {
|
|
1515
|
+
const messageById = new Map(messages.map((msg) => [msg.id, msg]));
|
|
1516
|
+
const recentRuns = [...toolRuns].filter((run) => APP_TOOL_NAMES.has(run.toolName)).sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0)).slice(0, 8);
|
|
1517
|
+
if (recentRuns.length === 0) return null;
|
|
1518
|
+
const lines = [
|
|
1519
|
+
"\u8FD9\u4E9B\u4FE1\u606F\u6765\u81EA\u5F53\u524D\u5BF9\u8BDD\u91CC\u6700\u8FD1\u7684 app \u5DE5\u5177\u8C03\u7528\uFF0C\u4E0D\u4F9D\u8D56\u53F3\u4FA7\u7F16\u8F91\u6001\u3002"
|
|
1520
|
+
];
|
|
1521
|
+
const latestDeployLike = recentRuns.find(
|
|
1522
|
+
(run) => ["appDeploy", "appRead", "appPreflight"].includes(run.toolName)
|
|
1523
|
+
);
|
|
1524
|
+
if (latestDeployLike) {
|
|
1525
|
+
const input = latestDeployLike.input ?? {};
|
|
1526
|
+
const parts = [
|
|
1527
|
+
`- \u6700\u8FD1\u4E00\u6B21\u5173\u952E app \u64CD\u4F5C: ${latestDeployLike.toolName}`,
|
|
1528
|
+
typeof input.appId === "string" ? `appId=${input.appId}` : null,
|
|
1529
|
+
typeof input.name === "string" ? `name=${input.name}` : null,
|
|
1530
|
+
typeof input.framework === "string" ? `framework=${input.framework}` : null
|
|
1531
|
+
].filter(Boolean);
|
|
1532
|
+
lines.push(parts.join("\uFF0C"));
|
|
1533
|
+
}
|
|
1534
|
+
const relatedMessages = recentRuns.map((run) => messageById.get(run.messageId)).filter((msg) => !!msg);
|
|
1535
|
+
const appListMessage = relatedMessages.find((msg) => msg.toolName === "appList");
|
|
1536
|
+
if (appListMessage) {
|
|
1537
|
+
const entries = parseAppListEntries(contentToText(appListMessage.content)).slice(0, 5);
|
|
1538
|
+
if (entries.length > 0) {
|
|
1539
|
+
lines.push("- \u6700\u8FD1\u4E00\u6B21 appList \u7ED3\u679C\uFF1A");
|
|
1540
|
+
lines.push(...entries.map((entry) => ` ${entry}`));
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
const latestReadMessage = relatedMessages.find((msg) => msg.toolName === "appRead");
|
|
1544
|
+
if (latestReadMessage) {
|
|
1545
|
+
const text = contentToText(latestReadMessage.content);
|
|
1546
|
+
const appId = text.match(/- appId:\s*(.+)/)?.[1]?.trim();
|
|
1547
|
+
const url = text.match(/- 访问地址:\s*(.+)/)?.[1]?.trim();
|
|
1548
|
+
if (appId || url) {
|
|
1549
|
+
lines.push(
|
|
1550
|
+
[
|
|
1551
|
+
"- \u6700\u8FD1\u4E00\u6B21 appRead \u771F\u503C:",
|
|
1552
|
+
appId ? `appId=${appId}` : null,
|
|
1553
|
+
url ? `url=${url}` : null
|
|
1554
|
+
].filter(Boolean).join("\uFF0C")
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
const latestPreflightRun = recentRuns.find((run) => run.toolName === "appPreflight");
|
|
1559
|
+
if (latestPreflightRun) {
|
|
1560
|
+
lines.push(
|
|
1561
|
+
[
|
|
1562
|
+
"- \u6700\u8FD1\u4E00\u6B21 appPreflight:",
|
|
1563
|
+
latestPreflightRun.status === "failed" ? "\u5931\u8D25" : "\u5DF2\u6267\u884C",
|
|
1564
|
+
latestPreflightRun.outputSummary ? `\u6458\u8981=${latestPreflightRun.outputSummary}` : null
|
|
1565
|
+
].filter(Boolean).join("\uFF0C")
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
const latestDeployMessage = relatedMessages.find((msg) => msg.toolName === "appDeploy");
|
|
1569
|
+
if (latestDeployMessage) {
|
|
1570
|
+
const text = contentToText(latestDeployMessage.content);
|
|
1571
|
+
const appId = text.match(/- appId:\s*(.+)/)?.[1]?.trim();
|
|
1572
|
+
const url = text.match(/- 访问地址:\s*(.+)/)?.[1]?.trim();
|
|
1573
|
+
if (appId || url) {
|
|
1574
|
+
lines.push(
|
|
1575
|
+
[
|
|
1576
|
+
"- \u6700\u8FD1\u4E00\u6B21 appDeploy \u7ED3\u679C:",
|
|
1577
|
+
appId ? `appId=${appId}` : null,
|
|
1578
|
+
url ? `url=${url}` : null
|
|
1579
|
+
].filter(Boolean).join("\uFF0C")
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
lines.push(
|
|
1584
|
+
"- \u5982\u679C\u7528\u6237\u8BF4\u201C\u521A\u624D\u90A3\u4E2A app / \u90A3\u4E2A\u7F51\u7AD9\u201D\uFF0C\u4F18\u5148\u628A\u5B83\u7406\u89E3\u4E3A\u4E0A\u9762\u6700\u8FD1\u4E00\u6B21\u88AB\u8BFB\u53D6\u3001\u9884\u68C0\u6216\u90E8\u7F72\u7684\u5E94\u7528\uFF1B\u82E5\u4ECD\u6709\u6B67\u4E49\uFF0C\u518D\u7528 appList \u6216 appRead \u786E\u8BA4\u3002"
|
|
1585
|
+
);
|
|
1586
|
+
return lines.join("\n");
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
// packages/ai/agent/streamAgentChatTurnUtils.ts
|
|
1590
|
+
var BROWSER_UNAVAILABLE_CORE_TOOLS = {
|
|
1591
|
+
queryModelUsage: true,
|
|
1592
|
+
createAgentAutomation: true,
|
|
1593
|
+
notifyUser: true
|
|
1594
|
+
};
|
|
1595
|
+
var getRuntimeCoreTools = () => {
|
|
1596
|
+
if (typeof window === "undefined") {
|
|
1597
|
+
return [...TOOL_PACKS.CORE];
|
|
1598
|
+
}
|
|
1599
|
+
return TOOL_PACKS.CORE.filter(
|
|
1600
|
+
(toolName) => !BROWSER_UNAVAILABLE_CORE_TOOLS[toolName]
|
|
1601
|
+
);
|
|
1602
|
+
};
|
|
1603
|
+
var isInlineVisualArtifactAgent = (agentConfig) => {
|
|
1604
|
+
const tags = Array.isArray(agentConfig.tags) ? agentConfig.tags : [];
|
|
1605
|
+
return tags.some(
|
|
1606
|
+
(tag) => typeof tag === "string" && ["inline-artifact", "streaming-ui"].includes(tag)
|
|
1607
|
+
);
|
|
1608
|
+
};
|
|
1609
|
+
var estimateTokensOfMessage = (msg) => {
|
|
1610
|
+
let content = "";
|
|
1611
|
+
if (typeof msg.content === "string") {
|
|
1612
|
+
content = msg.content;
|
|
1613
|
+
} else if (Array.isArray(msg.content)) {
|
|
1614
|
+
content = msg.content.map((p) => {
|
|
1615
|
+
if (p.type === "text") return p.text || "";
|
|
1616
|
+
if (p.type === "image_url") return "[image]";
|
|
1617
|
+
return "[non-text]";
|
|
1618
|
+
}).join("");
|
|
1619
|
+
} else if (msg.content && typeof msg.content === "object") {
|
|
1620
|
+
content = JSON.stringify(msg.content);
|
|
1621
|
+
}
|
|
1622
|
+
let extraTokens = 0;
|
|
1623
|
+
if (Array.isArray(msg.tool_calls)) {
|
|
1624
|
+
const toolsStr = JSON.stringify(msg.tool_calls);
|
|
1625
|
+
extraTokens = estimateTokenCount(toolsStr);
|
|
1626
|
+
}
|
|
1627
|
+
return estimateTokenCount(content) + extraTokens;
|
|
1628
|
+
};
|
|
1629
|
+
var classifyConversationLoad = (messages) => {
|
|
1630
|
+
const N = 20;
|
|
1631
|
+
if (!Array.isArray(messages) || messages.length === 0) return "light";
|
|
1632
|
+
const tail = messages.slice(-N);
|
|
1633
|
+
const tokenSamples = tail.map(estimateTokensOfMessage);
|
|
1634
|
+
if (tokenSamples.length === 0) return "light";
|
|
1635
|
+
const sum = tokenSamples.reduce((acc, v) => acc + v, 0);
|
|
1636
|
+
const avg = sum / tokenSamples.length;
|
|
1637
|
+
const sorted = [...tokenSamples].sort((a, b) => a - b);
|
|
1638
|
+
const p95 = sorted[Math.floor((sorted.length - 1) * 0.95)];
|
|
1639
|
+
if (p95 < 200 && avg < 120) {
|
|
1640
|
+
return "light";
|
|
1641
|
+
}
|
|
1642
|
+
if (p95 > 2e3 || avg > 1200) {
|
|
1643
|
+
return "heavy";
|
|
1644
|
+
}
|
|
1645
|
+
return "medium";
|
|
1646
|
+
};
|
|
1647
|
+
var compressOldToolResults = (messages, maxChars = 800) => {
|
|
1648
|
+
let lastToolCallAssistantIdx = -1;
|
|
1649
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1650
|
+
if (messages[i].role === "assistant" && Array.isArray(messages[i].tool_calls) && messages[i].tool_calls.length > 0) {
|
|
1651
|
+
lastToolCallAssistantIdx = i;
|
|
1652
|
+
break;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return messages.map((msg, idx) => {
|
|
1656
|
+
if (msg.role !== "tool") return msg;
|
|
1657
|
+
if (idx > lastToolCallAssistantIdx) return msg;
|
|
1658
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1659
|
+
if (content.length <= maxChars) return msg;
|
|
1660
|
+
return {
|
|
1661
|
+
...msg,
|
|
1662
|
+
content: content.slice(0, maxChars) + `
|
|
1663
|
+
\u2026[\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${content.length} \u5B57\u7B26]`
|
|
1664
|
+
};
|
|
1665
|
+
});
|
|
1666
|
+
};
|
|
1667
|
+
var trimMessagesWithSummary = (messages, contextWindow, summaryTokenCount) => {
|
|
1668
|
+
const recentLoad = classifyConversationLoad(messages);
|
|
1669
|
+
const { rawMessageBudget, minTailTokens } = planContextUsage({
|
|
1670
|
+
contextWindow,
|
|
1671
|
+
summaryTokens: summaryTokenCount,
|
|
1672
|
+
recentLoad
|
|
1673
|
+
});
|
|
1674
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
1675
|
+
let totalTokens = 0;
|
|
1676
|
+
let keepCount = 0;
|
|
1677
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1678
|
+
const t = estimateTokensOfMessage(messages[i]);
|
|
1679
|
+
if (totalTokens + t > rawMessageBudget) break;
|
|
1680
|
+
totalTokens += t;
|
|
1681
|
+
keepCount++;
|
|
1682
|
+
}
|
|
1683
|
+
if (keepCount >= messages.length) {
|
|
1684
|
+
return messages;
|
|
1685
|
+
}
|
|
1686
|
+
if (totalTokens < minTailTokens) {
|
|
1687
|
+
const maxBudgetWithSlack = rawMessageBudget * 1.2;
|
|
1688
|
+
for (let i = messages.length - 1 - keepCount; i >= 0; i--) {
|
|
1689
|
+
const t = estimateTokensOfMessage(messages[i]);
|
|
1690
|
+
if (totalTokens + t > maxBudgetWithSlack) break;
|
|
1691
|
+
totalTokens += t;
|
|
1692
|
+
keepCount++;
|
|
1693
|
+
if (totalTokens >= minTailTokens) break;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
if (keepCount === 0 && messages.length > 0) {
|
|
1697
|
+
keepCount = Math.min(2, messages.length);
|
|
1698
|
+
}
|
|
1699
|
+
let cutIndex = messages.length - keepCount;
|
|
1700
|
+
if (cutIndex < 0) cutIndex = 0;
|
|
1701
|
+
while (cutIndex < messages.length && messages[cutIndex].role === "tool") {
|
|
1702
|
+
cutIndex++;
|
|
1703
|
+
}
|
|
1704
|
+
return messages.slice(cutIndex);
|
|
1705
|
+
};
|
|
1706
|
+
var createDbSliceTurnContextSource = (dispatch) => ({
|
|
1707
|
+
readRecord: async (dbKey) => {
|
|
1708
|
+
const result = await dispatch(read({ dbKey })).unwrap();
|
|
1709
|
+
return result ?? null;
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
var joinMapValues = (map) => Array.from(map.values()).join("");
|
|
1713
|
+
var hasImageInMessages = (messages) => {
|
|
1714
|
+
if (!Array.isArray(messages)) return false;
|
|
1715
|
+
return messages.some((msg) => {
|
|
1716
|
+
const content = msg.content;
|
|
1717
|
+
if (!Array.isArray(content)) return false;
|
|
1718
|
+
return content.some(
|
|
1719
|
+
(part) => part && typeof part === "object" && part.type === "image_url" && part.image_url && typeof part.image_url.url === "string" && part.image_url.url.trim() !== ""
|
|
1720
|
+
);
|
|
1721
|
+
});
|
|
1722
|
+
};
|
|
1723
|
+
var REJECT_NO_VISION_MESSAGE = "\u5F53\u524D Agent \u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C\u8BF7\u6539\u7528\u6587\u672C\u6216\u6587\u6863\u3002";
|
|
1724
|
+
function shouldRejectImageInputForAgent(agentConfig, messages) {
|
|
1725
|
+
const agentHasVision = resolveAgentImageInputSupport(agentConfig);
|
|
1726
|
+
if (!agentHasVision && hasImageInMessages(messages)) {
|
|
1727
|
+
return REJECT_NO_VISION_MESSAGE;
|
|
1728
|
+
}
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
var formatCurrentInputContext = (pendingFiles, currentInputMap) => {
|
|
1732
|
+
if (pendingFiles.length === 0 || currentInputMap.size === 0) {
|
|
1733
|
+
return joinMapValues(currentInputMap);
|
|
1734
|
+
}
|
|
1735
|
+
const relevantPendingFiles = pendingFiles.filter((file) => {
|
|
1736
|
+
const key = file.sourceDialogKey || file.dialogKey || file.pageKey;
|
|
1737
|
+
return key && currentInputMap.has(key);
|
|
1738
|
+
});
|
|
1739
|
+
if (relevantPendingFiles.length === 0) {
|
|
1740
|
+
return joinMapValues(currentInputMap);
|
|
1741
|
+
}
|
|
1742
|
+
const filesByGroup = /* @__PURE__ */ new Map();
|
|
1743
|
+
for (const file of relevantPendingFiles) {
|
|
1744
|
+
const groupKey = file.groupId || file.id;
|
|
1745
|
+
const group = filesByGroup.get(groupKey);
|
|
1746
|
+
if (group) {
|
|
1747
|
+
group.push(file);
|
|
1748
|
+
} else {
|
|
1749
|
+
filesByGroup.set(groupKey, [file]);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
let sourceCounter = 1;
|
|
1753
|
+
let output = "";
|
|
1754
|
+
filesByGroup.forEach((filesInGroup) => {
|
|
1755
|
+
const isGroup = filesInGroup.length > 1;
|
|
1756
|
+
const sourceName = isGroup ? filesInGroup[0].name.split(" (")[0] : filesInGroup[0].name;
|
|
1757
|
+
output += `--- Source ${sourceCounter}: "${sourceName}" ---
|
|
1758
|
+
`;
|
|
1759
|
+
filesInGroup.forEach((file) => {
|
|
1760
|
+
const key = file.sourceDialogKey || file.dialogKey || file.pageKey;
|
|
1761
|
+
if (!key) return;
|
|
1762
|
+
const content = currentInputMap.get(key);
|
|
1763
|
+
if (!content) return;
|
|
1764
|
+
if (isGroup) {
|
|
1765
|
+
output += `### Document: "${file.name}"
|
|
1766
|
+
${content}
|
|
1767
|
+
`;
|
|
1768
|
+
} else {
|
|
1769
|
+
output += `${content}
|
|
1770
|
+
`;
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
output += `--- End of Source ${sourceCounter} ---
|
|
1774
|
+
|
|
1775
|
+
`;
|
|
1776
|
+
sourceCounter++;
|
|
1777
|
+
});
|
|
1778
|
+
return output;
|
|
1779
|
+
};
|
|
1780
|
+
var validateAccessAndBalance = (agentConfig, state) => {
|
|
1781
|
+
const userBalance = selectCurrentUserBalance(state);
|
|
1782
|
+
const currentUserId = selectIdentityUserId(state);
|
|
1783
|
+
const isCustomApi = agentConfig.apiSource === "custom";
|
|
1784
|
+
const isCliApi = agentConfig.apiSource === "cli";
|
|
1785
|
+
const isDeviceLocalOwner = agentConfig.userId === "local" && !currentUserId;
|
|
1786
|
+
const isOwner = isDeviceLocalOwner || Boolean(currentUserId) && agentConfig.userId === currentUserId;
|
|
1787
|
+
if (!isOwner) {
|
|
1788
|
+
const hasWhitelist = Array.isArray(agentConfig.whitelist) && agentConfig.whitelist.length > 0;
|
|
1789
|
+
if (hasWhitelist) {
|
|
1790
|
+
const isUserInWhitelist = !!currentUserId && agentConfig.whitelist?.includes(currentUserId);
|
|
1791
|
+
if (!isUserInWhitelist) {
|
|
1792
|
+
return "\u60A8\u4E0D\u5728\u8BE5\u5E94\u7528\u7684\u767D\u540D\u5355\u4E2D\uFF0C\u65E0\u6CD5\u4F7F\u7528\u3002";
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
if (isCustomApi || isCliApi) {
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
const SUBSCRIPTION_OAUTH_REFS = /* @__PURE__ */ new Set([
|
|
1800
|
+
"cursor",
|
|
1801
|
+
"chatgpt",
|
|
1802
|
+
"xai",
|
|
1803
|
+
"antigravity",
|
|
1804
|
+
"claude"
|
|
1805
|
+
]);
|
|
1806
|
+
if (SUBSCRIPTION_OAUTH_REFS.has((agentConfig.apiKeyRef ?? "").trim().toLowerCase())) {
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|
|
1809
|
+
const isPlatformApi = agentConfig.apiSource === "platform" || agentConfig.useServerProxy === true;
|
|
1810
|
+
if (isDeviceLocalOwner && !isPlatformApi) {
|
|
1811
|
+
return null;
|
|
1812
|
+
}
|
|
1813
|
+
if (typeof userBalance !== "number") {
|
|
1814
|
+
const hasSessionUser = Boolean(
|
|
1815
|
+
state?.auth?.currentUser?.userId
|
|
1816
|
+
);
|
|
1817
|
+
if (!currentUserId || !hasSessionUser) {
|
|
1818
|
+
return "\u8BF7\u767B\u5F55\u540E\u4F7F\u7528\u5E73\u53F0\u6A21\u578B\uFF0C\u6216\u6539\u7528\u672C\u5730\u81EA\u5B9A\u4E49/API/CLI Agent\u3002";
|
|
1819
|
+
}
|
|
1820
|
+
return "\u6B63\u5728\u83B7\u53D6\u7528\u6237\u4F59\u989D\uFF0C\u8BF7\u7A0D\u5019...";
|
|
1821
|
+
}
|
|
1822
|
+
const serverPrices = getModelPricing(agentConfig.provider || "", agentConfig.model);
|
|
1823
|
+
if (!serverPrices && !hasExplicitAgentPricing(agentConfig)) {
|
|
1824
|
+
return "\u65E0\u6CD5\u83B7\u53D6\u6A21\u578B\u5B9A\u4EF7\u4FE1\u606F\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
1825
|
+
}
|
|
1826
|
+
const prices = getPrices(agentConfig, serverPrices ?? null);
|
|
1827
|
+
const maxPrice = getFinalPrice(prices);
|
|
1828
|
+
if (userBalance < maxPrice) {
|
|
1829
|
+
return "\u4F59\u989D\u4E0D\u8DB3\uFF0C\u8BF7\u5145\u503C\u540E\u518D\u8BD5\u3002";
|
|
1830
|
+
}
|
|
1831
|
+
return null;
|
|
1832
|
+
};
|
|
1833
|
+
var fetchMemoryOverlayContext = async (state, agentConfig, userInput, dialogConfig) => {
|
|
1834
|
+
const token = typeof state?.auth?.currentToken === "string" ? state.auth.currentToken : null;
|
|
1835
|
+
const currentServer = typeof state?.settings?.currentServer === "string" ? state.settings.currentServer : null;
|
|
1836
|
+
const agentKey = asOptionalTrimmedString(agentConfig.dbKey) ?? "";
|
|
1837
|
+
if (!token || !currentServer || !agentKey) return null;
|
|
1838
|
+
const baseUrl = resolveToolBaseUrl(currentServer);
|
|
1839
|
+
if (!baseUrl) return null;
|
|
1840
|
+
const inputText = typeof userInput === "string" ? userInput : JSON.stringify(userInput ?? "");
|
|
1841
|
+
const spaceId = asOptionalTrimmedString(dialogConfig?.spaceId) ?? (state?.space?.viewMode === "all" ? void 0 : asOptionalTrimmedString(state?.space?.currentSpaceId));
|
|
1842
|
+
try {
|
|
1843
|
+
const response = await fetch(`${baseUrl}/api/memory/query`, {
|
|
1844
|
+
method: "POST",
|
|
1845
|
+
headers: {
|
|
1846
|
+
"Content-Type": "application/json",
|
|
1847
|
+
Authorization: `Bearer ${token}`
|
|
1848
|
+
},
|
|
1849
|
+
body: JSON.stringify({
|
|
1850
|
+
agentKey,
|
|
1851
|
+
userInput: inputText,
|
|
1852
|
+
...spaceId ? { spaceId } : {}
|
|
1853
|
+
})
|
|
1854
|
+
});
|
|
1855
|
+
if (!response.ok) return null;
|
|
1856
|
+
const payload = await response.json().catch(() => null);
|
|
1857
|
+
return asOptionalTrimmedString(payload?.promptBlock) ?? null;
|
|
1858
|
+
} catch {
|
|
1859
|
+
return null;
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
var buildStaticContexts = async (state, dispatch, agentConfig, dialogConfig, referenceContentCache) => {
|
|
1863
|
+
const keySets = await getFullChatContextKeys(
|
|
1864
|
+
state,
|
|
1865
|
+
dispatch,
|
|
1866
|
+
agentConfig,
|
|
1867
|
+
"",
|
|
1868
|
+
// 静态上下文不需要 userInput
|
|
1869
|
+
void 0
|
|
1870
|
+
// 静态上下文不需要 dialogConfig
|
|
1871
|
+
);
|
|
1872
|
+
const finalKeys = deduplicateContextKeys(keySets);
|
|
1873
|
+
const fetchReferences = (keys) => fetchReferenceContents(keys, dispatch, {
|
|
1874
|
+
format: "simplified_markdown",
|
|
1875
|
+
inlineMentionMeta: true,
|
|
1876
|
+
preloaded: referenceContentCache
|
|
1877
|
+
});
|
|
1878
|
+
const [botInstructionsMap, botKnowledgeMap] = await Promise.all([
|
|
1879
|
+
fetchReferences(finalKeys.botInstructionsContext),
|
|
1880
|
+
fetchReferences(finalKeys.botKnowledgeContext)
|
|
1881
|
+
]);
|
|
1882
|
+
const globalPrompt = selectGlobalPrompt(state);
|
|
1883
|
+
const userTonePreset = selectUserTonePreset(state);
|
|
1884
|
+
const knowledgeCaptureLevel = selectKnowledgeCaptureLevel(state);
|
|
1885
|
+
const spaceContextLevel = selectSpaceContextLevel(state);
|
|
1886
|
+
const userRecentLimit = selectAiRecentContentLimit(state);
|
|
1887
|
+
const contextWindow = getModelContextWindow(agentConfig.model) || 128e3;
|
|
1888
|
+
const preloadPlan = resolveSpaceContextPreloadPlan(spaceContextLevel);
|
|
1889
|
+
const dynamicLimit = Math.floor(contextWindow * preloadPlan.preloadBudgetRatio / 150);
|
|
1890
|
+
const recentLimit = preloadPlan.includeRecentContent ? Math.max(3, Math.min(userRecentLimit, Math.max(3, dynamicLimit))) : 0;
|
|
1891
|
+
const turnContextSource = createDbSliceTurnContextSource(dispatch);
|
|
1892
|
+
let spaceContext = null;
|
|
1893
|
+
if (spaceContextLevel > 1) {
|
|
1894
|
+
const spaceLayer = await buildSpaceContextLayer({
|
|
1895
|
+
source: turnContextSource,
|
|
1896
|
+
spaceId: dialogConfig?.spaceId,
|
|
1897
|
+
recentContentLimit: recentLimit
|
|
1898
|
+
});
|
|
1899
|
+
spaceContext = spaceLayer ? spaceLayer.content.trim() : null;
|
|
1900
|
+
}
|
|
1901
|
+
const linkedSpaceIds = agentConfig.linkedSpaces || [];
|
|
1902
|
+
if (linkedSpaceIds.length > 0 && spaceContextLevel > 1) {
|
|
1903
|
+
const linkedSection = await buildLinkedSpacesSection({
|
|
1904
|
+
source: turnContextSource,
|
|
1905
|
+
linkedSpaceIds
|
|
1906
|
+
});
|
|
1907
|
+
if (linkedSection) {
|
|
1908
|
+
spaceContext = (spaceContext ?? "") + `
|
|
1909
|
+
|
|
1910
|
+
${linkedSection}`;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
const dialogPolicyContext = dialogConfig?.category === PERSONALIZATION_DIALOG_CATEGORY ? buildPersonalizationDialogPolicyContext() : null;
|
|
1914
|
+
return {
|
|
1915
|
+
botInstructionsContext: joinMapValues(botInstructionsMap),
|
|
1916
|
+
botKnowledgeContext: joinMapValues(botKnowledgeMap),
|
|
1917
|
+
spaceContext,
|
|
1918
|
+
userGlobalPrompt: globalPrompt,
|
|
1919
|
+
userPolicyContext: [
|
|
1920
|
+
buildStaticUserPolicyContext({
|
|
1921
|
+
agentConfig,
|
|
1922
|
+
settingsRecord: {
|
|
1923
|
+
userTonePreset,
|
|
1924
|
+
knowledgeCaptureLevel,
|
|
1925
|
+
spaceContextLevel,
|
|
1926
|
+
enableReadCurrentSpace: spaceContextLevel > 1
|
|
1927
|
+
}
|
|
1928
|
+
}),
|
|
1929
|
+
dialogPolicyContext
|
|
1930
|
+
].filter(Boolean).join("\n")
|
|
1931
|
+
};
|
|
1932
|
+
};
|
|
1933
|
+
var buildDynamicContexts = async (state, dispatch, agentConfig, userInput, runtimeOptions, referenceContentCache, dialogKey) => {
|
|
1934
|
+
let dialogSummary = null;
|
|
1935
|
+
let dialogConfig;
|
|
1936
|
+
if (dialogKey) {
|
|
1937
|
+
dialogConfig = selectById(state, dialogKey);
|
|
1938
|
+
if (dialogConfig?.summary) {
|
|
1939
|
+
dialogSummary = dialogConfig.summary;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
const keySets = await getFullChatContextKeys(
|
|
1943
|
+
state,
|
|
1944
|
+
dispatch,
|
|
1945
|
+
agentConfig,
|
|
1946
|
+
userInput,
|
|
1947
|
+
dialogConfig
|
|
1948
|
+
);
|
|
1949
|
+
const finalKeys = deduplicateContextKeys(keySets);
|
|
1950
|
+
const fetchReferences = (keys) => fetchReferenceContents(keys, dispatch, {
|
|
1951
|
+
format: "simplified_markdown",
|
|
1952
|
+
inlineMentionMeta: true,
|
|
1953
|
+
preloaded: referenceContentCache
|
|
1954
|
+
});
|
|
1955
|
+
const [currentInputMap, historyMap] = await Promise.all([
|
|
1956
|
+
fetchReferences(finalKeys.currentInputContext),
|
|
1957
|
+
fetchReferences(finalKeys.historyContext)
|
|
1958
|
+
]);
|
|
1959
|
+
const pendingFiles = selectPendingFiles(state);
|
|
1960
|
+
const formattedCurrentInputContext = formatCurrentInputContext(
|
|
1961
|
+
pendingFiles,
|
|
1962
|
+
currentInputMap
|
|
1963
|
+
);
|
|
1964
|
+
const dialogId = dialogConfig?.id ?? null;
|
|
1965
|
+
const currentDialogMessages = selectAllMsgs(state, dialogId);
|
|
1966
|
+
const currentDialogMessageIds = new Set(currentDialogMessages.map((msg) => msg.id));
|
|
1967
|
+
const currentDialogToolRuns = getAllToolRuns().filter(
|
|
1968
|
+
(run) => currentDialogMessageIds.has(run.messageId)
|
|
1969
|
+
);
|
|
1970
|
+
const editingContext = buildEditingContextSummary(state, runtimeOptions);
|
|
1971
|
+
const appWorkingMemory = buildRecentAppToolMemory(
|
|
1972
|
+
currentDialogMessages,
|
|
1973
|
+
currentDialogToolRuns
|
|
1974
|
+
);
|
|
1975
|
+
const memoryOverlay = await fetchMemoryOverlayContext(
|
|
1976
|
+
state,
|
|
1977
|
+
agentConfig,
|
|
1978
|
+
userInput,
|
|
1979
|
+
dialogConfig
|
|
1980
|
+
);
|
|
1981
|
+
return {
|
|
1982
|
+
currentInputContext: formattedCurrentInputContext.trim() || null,
|
|
1983
|
+
historyContext: joinMapValues(historyMap),
|
|
1984
|
+
editingContext,
|
|
1985
|
+
appWorkingMemory,
|
|
1986
|
+
memoryOverlay,
|
|
1987
|
+
dialogSummary,
|
|
1988
|
+
proactiveSummary: dialogConfig?.proactiveSummary || null,
|
|
1989
|
+
referenceKeys: dialogConfig?.referenceKeys || []
|
|
1990
|
+
};
|
|
1991
|
+
};
|
|
1992
|
+
var mergeContexts = (staticCtx, dynamicCtx) => ({
|
|
1993
|
+
botInstructionsContext: staticCtx.botInstructionsContext || void 0,
|
|
1994
|
+
botKnowledgeContext: staticCtx.botKnowledgeContext || void 0,
|
|
1995
|
+
spaceContext: staticCtx.spaceContext || void 0,
|
|
1996
|
+
userGlobalPrompt: staticCtx.userGlobalPrompt || void 0,
|
|
1997
|
+
userPolicyContext: staticCtx.userPolicyContext || void 0,
|
|
1998
|
+
currentInputContext: dynamicCtx.currentInputContext,
|
|
1999
|
+
historyContext: dynamicCtx.historyContext || void 0,
|
|
2000
|
+
editingContext: dynamicCtx.editingContext,
|
|
2001
|
+
appWorkingMemory: dynamicCtx.appWorkingMemory,
|
|
2002
|
+
memoryOverlay: dynamicCtx.memoryOverlay,
|
|
2003
|
+
dialogSummary: dynamicCtx.dialogSummary,
|
|
2004
|
+
proactiveSummary: dynamicCtx.proactiveSummary,
|
|
2005
|
+
referenceKeys: dynamicCtx.referenceKeys
|
|
2006
|
+
});
|
|
2007
|
+
var mergeAgentToolsWithRuntime = (agentConfig, referencedTools, mentionedTools, runtimeOptions, state) => {
|
|
2008
|
+
const rawBaseTools = Array.isArray(agentConfig.tools) ? agentConfig.tools : [];
|
|
2009
|
+
const agentEnabledPacks = agentConfig?.enabledPacks;
|
|
2010
|
+
const isInlineArtifact = isInlineVisualArtifactAgent(agentConfig);
|
|
2011
|
+
const effectiveEnabledPacks = Array.isArray(agentEnabledPacks) && agentEnabledPacks.length > 0 ? agentEnabledPacks : isInlineArtifact ? agentEnabledPacks ?? [] : ["long-term-memory"];
|
|
2012
|
+
const expandedPackTools = expandEnabledPacks(
|
|
2013
|
+
effectiveEnabledPacks,
|
|
2014
|
+
rawBaseTools
|
|
2015
|
+
);
|
|
2016
|
+
const baseTools = canonicalizeToolNames(expandedPackTools);
|
|
2017
|
+
const requiredSkillTools = canonicalizeToolNames(
|
|
2018
|
+
agentConfig.referencedTools ?? []
|
|
2019
|
+
);
|
|
2020
|
+
const recommendedSkillTools = canonicalizeToolNames(
|
|
2021
|
+
agentConfig.recommendedSkillTools ?? []
|
|
2022
|
+
);
|
|
2023
|
+
const recommendedSkillHints = [
|
|
2024
|
+
...new Set(asNonEmptyStringArray(agentConfig.recommendedSkillHints))
|
|
2025
|
+
];
|
|
2026
|
+
const skillPromptPatches = [
|
|
2027
|
+
...new Set(asNonEmptyStringArray(agentConfig.skillPromptPatches))
|
|
2028
|
+
];
|
|
2029
|
+
const webPadded = applyDefaultWebToolPacks({
|
|
2030
|
+
toolNames: baseTools,
|
|
2031
|
+
skipWeb: isInlineVisualArtifactAgent(agentConfig)
|
|
2032
|
+
});
|
|
2033
|
+
const enhancedTools = /* @__PURE__ */ new Set([
|
|
2034
|
+
...webPadded,
|
|
2035
|
+
...isInlineVisualArtifactAgent(agentConfig) ? [] : getRuntimeCoreTools(),
|
|
2036
|
+
...isInlineVisualArtifactAgent(agentConfig) ? [] : requiredSkillTools,
|
|
2037
|
+
...isInlineVisualArtifactAgent(agentConfig) ? [] : canonicalizeToolNames(referencedTools),
|
|
2038
|
+
...isInlineVisualArtifactAgent(agentConfig) ? [] : canonicalizeToolNames(mentionedTools)
|
|
2039
|
+
]);
|
|
2040
|
+
const extraTools = isInlineVisualArtifactAgent(agentConfig) ? [] : canonicalizeToolNames(runtimeOptions?.extraTools ?? []);
|
|
2041
|
+
for (const t of extraTools) {
|
|
2042
|
+
enhancedTools.add(t);
|
|
2043
|
+
}
|
|
2044
|
+
if (!isInlineVisualArtifactAgent(agentConfig)) {
|
|
2045
|
+
const viewMode = state ? selectViewMode(state) : "categories";
|
|
2046
|
+
if (viewMode === "all") {
|
|
2047
|
+
enhancedTools.delete("search_workspace");
|
|
2048
|
+
enhancedTools.add("search_all_spaces");
|
|
2049
|
+
} else {
|
|
2050
|
+
enhancedTools.delete("search_all_spaces");
|
|
2051
|
+
enhancedTools.add("search_workspace");
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return {
|
|
2055
|
+
...agentConfig,
|
|
2056
|
+
tools: applyDisabledTools(
|
|
2057
|
+
prioritizeToolNames(
|
|
2058
|
+
Array.from(enhancedTools),
|
|
2059
|
+
recommendedSkillTools
|
|
2060
|
+
),
|
|
2061
|
+
agentConfig?.disabledTools
|
|
2062
|
+
),
|
|
2063
|
+
recommendedSkillTools,
|
|
2064
|
+
recommendedSkillHints,
|
|
2065
|
+
skillPromptPatches
|
|
2066
|
+
};
|
|
2067
|
+
};
|
|
2068
|
+
var applyImageConfigRuntimeOverride = (agentConfig, runtimeOptions) => {
|
|
2069
|
+
const override = runtimeOptions?.imageConfigOverride;
|
|
2070
|
+
if (!override) {
|
|
2071
|
+
return agentConfig;
|
|
2072
|
+
}
|
|
2073
|
+
const baseImageConfig = agentConfig.imageConfig ?? {};
|
|
2074
|
+
return {
|
|
2075
|
+
...agentConfig,
|
|
2076
|
+
...override.imageModelOverride ? { model: override.imageModelOverride } : {},
|
|
2077
|
+
imageConfig: {
|
|
2078
|
+
...baseImageConfig,
|
|
2079
|
+
...override
|
|
2080
|
+
}
|
|
2081
|
+
};
|
|
2082
|
+
};
|
|
2083
|
+
var formatImageWaitHint = (range) => {
|
|
2084
|
+
if (!range || typeof range.min !== "number" || typeof range.max !== "number") {
|
|
2085
|
+
return void 0;
|
|
2086
|
+
}
|
|
2087
|
+
return `\u901A\u5E38\u9700\u8981 ${range.min}-${range.max} \u79D2`;
|
|
2088
|
+
};
|
|
2089
|
+
var resolveAgentImageModelIdentity = (agentConfig) => {
|
|
2090
|
+
let providerKey = (agentConfig.provider || "").toLowerCase();
|
|
2091
|
+
let modelName = agentConfig.model ?? "";
|
|
2092
|
+
if (modelName.includes("/")) {
|
|
2093
|
+
const slash = modelName.indexOf("/");
|
|
2094
|
+
if (!providerKey) providerKey = modelName.slice(0, slash);
|
|
2095
|
+
modelName = modelName.slice(slash + 1);
|
|
2096
|
+
}
|
|
2097
|
+
try {
|
|
2098
|
+
return {
|
|
2099
|
+
providerKey,
|
|
2100
|
+
modelConfig: getModelConfig(providerKey, modelName)
|
|
2101
|
+
};
|
|
2102
|
+
} catch {
|
|
2103
|
+
try {
|
|
2104
|
+
const detected = getProviderByModelName(modelName);
|
|
2105
|
+
if (!detected) {
|
|
2106
|
+
return { providerKey, modelConfig: null };
|
|
2107
|
+
}
|
|
2108
|
+
return {
|
|
2109
|
+
providerKey: detected,
|
|
2110
|
+
modelConfig: getModelConfig(detected, modelName)
|
|
2111
|
+
};
|
|
2112
|
+
} catch {
|
|
2113
|
+
return { providerKey, modelConfig: null };
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
var EXPLICIT_IMAGE_GENERATION_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
2118
|
+
"openAIGptImage",
|
|
2119
|
+
"openAIGptImageGenerate",
|
|
2120
|
+
"openAIGptImageEdit",
|
|
2121
|
+
"chatgptWebImageGenerate",
|
|
2122
|
+
"geminiFlashImage",
|
|
2123
|
+
"geminiProImagePreview"
|
|
2124
|
+
]);
|
|
2125
|
+
var agentUsesExplicitImageGenerationTool = (agentConfig) => {
|
|
2126
|
+
const toolNames = Array.isArray(agentConfig.tools) ? agentConfig.tools : Array.isArray(agentConfig.toolNames) ? agentConfig.toolNames : [];
|
|
2127
|
+
return toolNames.some(
|
|
2128
|
+
(name) => typeof name === "string" && EXPLICIT_IMAGE_GENERATION_TOOL_NAMES.has(name.trim())
|
|
2129
|
+
);
|
|
2130
|
+
};
|
|
2131
|
+
var resolveImageGenerationStreamingState = (agentConfig, args) => {
|
|
2132
|
+
const { modelConfig } = resolveAgentImageModelIdentity(agentConfig);
|
|
2133
|
+
const hasImageOutput = !!(modelConfig?.hasImageOutput ?? modelConfig?.supportsImageOutput) || agentConfig.imageConfig?.enabled === true || agentUsesExplicitImageGenerationTool(agentConfig);
|
|
2134
|
+
if (!hasImageOutput) {
|
|
2135
|
+
return void 0;
|
|
2136
|
+
}
|
|
2137
|
+
const currentProfile = modelConfig?.imageGenerationProfiles?.find(
|
|
2138
|
+
(profile) => profile.imageModel === modelConfig.name
|
|
2139
|
+
);
|
|
2140
|
+
const usesWebImageTool = agentUsesExplicitImageGenerationTool(agentConfig) && (Array.isArray(agentConfig.tools) ? agentConfig.tools : []).includes("chatgptWebImageGenerate");
|
|
2141
|
+
return {
|
|
2142
|
+
kind: "image_generation",
|
|
2143
|
+
stage: args?.stage ?? args?.previous?.stage ?? "submitted",
|
|
2144
|
+
startedAt: args?.previous?.startedAt ?? Date.now(),
|
|
2145
|
+
waitHint: formatImageWaitHint(modelConfig?.imageGenerationWaitTimeSeconds) ?? (usesWebImageTool ? "\u7F51\u9875\u751F\u56FE\u901A\u5E38\u9700\u8981 30 \u79D2\u5230\u51E0\u5206\u949F\uFF0C\u5207\u6362\u5BF9\u8BDD\u4E0D\u4F1A\u4E2D\u65AD" : "\u901A\u5E38\u9700\u8981\u51E0\u5341\u79D2"),
|
|
2146
|
+
profileLabel: currentProfile?.label
|
|
2147
|
+
};
|
|
2148
|
+
};
|
|
2149
|
+
|
|
2150
|
+
// packages/ai/agent/persistMessageWithFixedId.ts
|
|
2151
|
+
async function persistMessageWithFixedId(dispatch, message) {
|
|
2152
|
+
dispatch(addUserMessage(message));
|
|
2153
|
+
const { controller, ...messageToWrite } = message;
|
|
2154
|
+
try {
|
|
2155
|
+
const writeRequest = dispatch(
|
|
2156
|
+
write({
|
|
2157
|
+
data: {
|
|
2158
|
+
...messageToWrite,
|
|
2159
|
+
type: "msg" /* MSG */
|
|
2160
|
+
},
|
|
2161
|
+
customKey: message.dbKey
|
|
2162
|
+
})
|
|
2163
|
+
);
|
|
2164
|
+
if (writeRequest && typeof writeRequest.unwrap === "function") {
|
|
2165
|
+
await writeRequest.unwrap();
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
await writeRequest;
|
|
2169
|
+
} catch (error) {
|
|
2170
|
+
console.error(
|
|
2171
|
+
"[persistMessageWithFixedId] Failed to persist fixed-id message:",
|
|
2172
|
+
error
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
// packages/ai/agent/serverOrigin.ts
|
|
2178
|
+
var normalizeServerOrigin = (base) => {
|
|
2179
|
+
try {
|
|
2180
|
+
return new URL(base).origin.replace(/\/+$/, "");
|
|
2181
|
+
} catch {
|
|
2182
|
+
return null;
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
|
|
2186
|
+
// packages/app/utils/desktopAgentRuntimeTurnClient.ts
|
|
2187
|
+
var DESKTOP_AGENT_CONFIG_SNAPSHOT_BUILD_FAILED = "desktop_agent_config_snapshot_invalid";
|
|
2188
|
+
function buildDesktopAgentRuntimeTurnBody(args) {
|
|
2189
|
+
const continueDialogId = asTrimmedString(args.continueDialogId);
|
|
2190
|
+
let agentConfigSnapshot;
|
|
2191
|
+
if (args.agentConfigSnapshot && typeof args.agentConfigSnapshot === "object") {
|
|
2192
|
+
const built = buildDesktopAgentRuntimeAgentConfigSnapshot(
|
|
2193
|
+
args.agentConfigSnapshot,
|
|
2194
|
+
args.agentRef
|
|
2195
|
+
);
|
|
2196
|
+
if (!built) {
|
|
2197
|
+
throw new Error(DESKTOP_AGENT_CONFIG_SNAPSHOT_BUILD_FAILED);
|
|
2198
|
+
}
|
|
2199
|
+
agentConfigSnapshot = built;
|
|
2200
|
+
}
|
|
2201
|
+
let dialogHistorySnapshot;
|
|
2202
|
+
if (args.dialogHistorySnapshot && typeof args.dialogHistorySnapshot === "object") {
|
|
2203
|
+
dialogHistorySnapshot = args.dialogHistorySnapshot;
|
|
2204
|
+
} else if (Array.isArray(args.dialogMessages) && continueDialogId) {
|
|
2205
|
+
dialogHistorySnapshot = buildDesktopAgentRuntimeDialogHistorySnapshot({
|
|
2206
|
+
dialogId: continueDialogId,
|
|
2207
|
+
messages: args.dialogMessages,
|
|
2208
|
+
currentInput: args.input
|
|
2209
|
+
}) ?? void 0;
|
|
2210
|
+
}
|
|
2211
|
+
const body = {
|
|
2212
|
+
agentRef: args.agentRef,
|
|
2213
|
+
input: args.input,
|
|
2214
|
+
...args.runtimeContext ? { runtimeContext: args.runtimeContext } : {},
|
|
2215
|
+
...continueDialogId ? { continueDialogId } : {},
|
|
2216
|
+
...continueDialogId && asTrimmedString(args.dialogKey) ? { dialogKey: asTrimmedString(args.dialogKey) } : {},
|
|
2217
|
+
...args.cwd ? { cwd: args.cwd } : {},
|
|
2218
|
+
...args.restrictShellToWorkspace ? { restrictShellToWorkspace: true } : {},
|
|
2219
|
+
...args.workspaceToolsHint ? { workspaceToolsHint: true } : {},
|
|
2220
|
+
...agentConfigSnapshot ? { agentConfigSnapshot } : {},
|
|
2221
|
+
...dialogHistorySnapshot ? { dialogHistorySnapshot } : {}
|
|
2222
|
+
};
|
|
2223
|
+
assertDesktopAgentRuntimeTurnBodyHasNoRawSecrets(body);
|
|
2224
|
+
return body;
|
|
2225
|
+
}
|
|
2226
|
+
function normalizeDesktopAgentRuntimeTurnError(data) {
|
|
2227
|
+
return typeof data?.error === "string" ? data.error : "Failed to run desktop agent runtime turn";
|
|
2228
|
+
}
|
|
2229
|
+
async function* runDesktopAgentRuntimeTurnStream({
|
|
2230
|
+
fetchImpl = fetch,
|
|
2231
|
+
signal,
|
|
2232
|
+
...args
|
|
2233
|
+
}) {
|
|
2234
|
+
try {
|
|
2235
|
+
if (signal?.aborted) {
|
|
2236
|
+
yield { type: "error", error: "The operation was aborted." };
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
const response = await fetchImpl("/api/desktop/agent-runtime/turn", {
|
|
2240
|
+
method: "POST",
|
|
2241
|
+
headers: { "Content-Type": "application/json" },
|
|
2242
|
+
body: JSON.stringify(buildDesktopAgentRuntimeTurnBody(args)),
|
|
2243
|
+
signal
|
|
2244
|
+
});
|
|
2245
|
+
if (!response.ok) {
|
|
2246
|
+
const data = await response.clone().json().catch(() => null);
|
|
2247
|
+
yield {
|
|
2248
|
+
type: "error",
|
|
2249
|
+
error: data ? normalizeDesktopAgentRuntimeTurnError(data) : `HTTP ${response.status}: ${response.statusText}`
|
|
2250
|
+
};
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
2254
|
+
if (!contentType.includes("text/event-stream")) {
|
|
2255
|
+
const data = await response.json().catch(() => ({}));
|
|
2256
|
+
if (data?.ok === false) {
|
|
2257
|
+
yield { type: "error", error: normalizeDesktopAgentRuntimeTurnError(data) };
|
|
2258
|
+
} else if (data?.result) {
|
|
2259
|
+
yield { type: "done", result: data.result };
|
|
2260
|
+
} else {
|
|
2261
|
+
yield { type: "error", error: "Desktop runtime response did not include a result" };
|
|
2262
|
+
}
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
const reader = response.body?.getReader();
|
|
2266
|
+
if (!reader) {
|
|
2267
|
+
yield { type: "error", error: "Response body reader not available" };
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
const decoder = new TextDecoder();
|
|
2271
|
+
let buffer = "";
|
|
2272
|
+
const readLine = (line) => {
|
|
2273
|
+
const trimmed = line.trim();
|
|
2274
|
+
if (!trimmed || !trimmed.startsWith("data: ")) return null;
|
|
2275
|
+
try {
|
|
2276
|
+
return JSON.parse(trimmed.slice(6));
|
|
2277
|
+
} catch (e) {
|
|
2278
|
+
console.error("[desktop-client] Failed to parse stream line:", trimmed, e);
|
|
2279
|
+
return null;
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
try {
|
|
2283
|
+
while (true) {
|
|
2284
|
+
if (signal?.aborted) {
|
|
2285
|
+
yield { type: "error", error: "The operation was aborted." };
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
const { done, value } = await readStreamChunk(reader, { signal });
|
|
2289
|
+
if (done) break;
|
|
2290
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2291
|
+
const lines = buffer.split("\n");
|
|
2292
|
+
buffer = lines.pop() || "";
|
|
2293
|
+
for (const line of lines) {
|
|
2294
|
+
const event2 = readLine(line);
|
|
2295
|
+
if (event2) yield event2;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
buffer += decoder.decode();
|
|
2299
|
+
const event = readLine(buffer);
|
|
2300
|
+
if (event) yield event;
|
|
2301
|
+
} catch (error) {
|
|
2302
|
+
if (isAbortError(error) || signal?.aborted) {
|
|
2303
|
+
yield { type: "error", error: "The operation was aborted." };
|
|
2304
|
+
return;
|
|
2305
|
+
}
|
|
2306
|
+
yield {
|
|
2307
|
+
type: "error",
|
|
2308
|
+
error: toErrorMessage(error)
|
|
2309
|
+
};
|
|
2310
|
+
} finally {
|
|
2311
|
+
try {
|
|
2312
|
+
await reader.cancel();
|
|
2313
|
+
} catch {
|
|
2314
|
+
}
|
|
2315
|
+
try {
|
|
2316
|
+
reader.releaseLock();
|
|
2317
|
+
} catch {
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
} catch (error) {
|
|
2321
|
+
if (isAbortError(error) || signal?.aborted) {
|
|
2322
|
+
yield { type: "error", error: "The operation was aborted." };
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
yield {
|
|
2326
|
+
type: "error",
|
|
2327
|
+
error: toErrorMessage(error)
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
// packages/ai/agent/streamTurnMessageBuild.ts
|
|
2333
|
+
var buildMessageMetadata = (agentConfig) => {
|
|
2334
|
+
const rawName = asTrimmedString(agentConfig?.name);
|
|
2335
|
+
return {
|
|
2336
|
+
agentKey: agentConfig.dbKey,
|
|
2337
|
+
cybotKey: agentConfig.dbKey,
|
|
2338
|
+
...rawName ? { agentName: rawName } : {}
|
|
2339
|
+
};
|
|
2340
|
+
};
|
|
2341
|
+
var buildDesktopRuntimeToolMessagesForUi = ({
|
|
2342
|
+
dialogId,
|
|
2343
|
+
turnMessages
|
|
2344
|
+
}) => {
|
|
2345
|
+
if (!Array.isArray(turnMessages) || turnMessages.length === 0) return [];
|
|
2346
|
+
const toolNamesByCallId = /* @__PURE__ */ new Map();
|
|
2347
|
+
const activityByCallId = /* @__PURE__ */ new Map();
|
|
2348
|
+
const projected = [];
|
|
2349
|
+
for (const message of turnMessages) {
|
|
2350
|
+
if (Array.isArray(message?.tool_calls)) {
|
|
2351
|
+
for (const call of message.tool_calls) {
|
|
2352
|
+
const callId = asTrimmedString(call?.id);
|
|
2353
|
+
const toolName2 = asTrimmedString(call?.function?.name);
|
|
2354
|
+
if (callId && toolName2) toolNamesByCallId.set(callId, toolName2);
|
|
2355
|
+
if (callId) {
|
|
2356
|
+
try {
|
|
2357
|
+
const args = typeof call?.function?.arguments === "string" ? JSON.parse(call.function.arguments) : call?.function?.arguments;
|
|
2358
|
+
const rawActivity = isRecord(args) ? args._activity : void 0;
|
|
2359
|
+
const activity2 = isRecord(rawActivity) ? rawActivity : void 0;
|
|
2360
|
+
const legacyTitle = typeof activity2?.title === "string" && activity2.title.trim();
|
|
2361
|
+
const action = activity2?.action;
|
|
2362
|
+
const actionTitle = isRecord(action) && typeof action.title === "string" && action.title.trim();
|
|
2363
|
+
const plan = activity2?.plan;
|
|
2364
|
+
const hasPlan = isRecord(plan) && Array.isArray(plan.phases) && plan.phases.some((phase) => {
|
|
2365
|
+
if (!isRecord(phase)) return false;
|
|
2366
|
+
return typeof phase.title === "string" && !!phase.title.trim();
|
|
2367
|
+
});
|
|
2368
|
+
if (activity2 && (legacyTitle || actionTitle || hasPlan)) {
|
|
2369
|
+
activityByCallId.set(callId, args._activity);
|
|
2370
|
+
}
|
|
2371
|
+
} catch {
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
continue;
|
|
2376
|
+
}
|
|
2377
|
+
if (message?.role !== "tool") continue;
|
|
2378
|
+
const toolCallId = asTrimmedString(message.tool_call_id);
|
|
2379
|
+
const metadata = asRecordOrEmpty(message.tool_result_metadata);
|
|
2380
|
+
const metadataToolName = asTrimmedString(metadata.toolName);
|
|
2381
|
+
const toolName = metadataToolName || (toolCallId ? toolNamesByCallId.get(toolCallId) : "") || "tool";
|
|
2382
|
+
const { key: dbKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
2383
|
+
const resultActivity = metadata.activity && typeof metadata.activity === "object" ? metadata.activity : void 0;
|
|
2384
|
+
const callActivity = toolCallId ? activityByCallId.get(toolCallId) : void 0;
|
|
2385
|
+
const activity = resultActivity || callActivity;
|
|
2386
|
+
const mergedMetadata = activity ? { ...metadata, activity } : metadata;
|
|
2387
|
+
projected.push({
|
|
2388
|
+
id: messageId,
|
|
2389
|
+
dialogId,
|
|
2390
|
+
dbKey,
|
|
2391
|
+
role: "tool",
|
|
2392
|
+
content: projectDesktopToolUiContent({
|
|
2393
|
+
toolName,
|
|
2394
|
+
content: typeof message.content === "string" ? message.content : "",
|
|
2395
|
+
metadata: mergedMetadata
|
|
2396
|
+
}),
|
|
2397
|
+
isStreaming: false,
|
|
2398
|
+
toolName,
|
|
2399
|
+
...toolCallId ? { toolCallId } : {},
|
|
2400
|
+
...Object.keys(mergedMetadata).length ? { metadata: mergedMetadata } : {}
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
return projected;
|
|
2404
|
+
};
|
|
2405
|
+
var toolMessageWillPersist = (toolMsg) => {
|
|
2406
|
+
const content = toolMsg?.content;
|
|
2407
|
+
if (typeof content === "string") return content.trim().length > 0;
|
|
2408
|
+
return Array.isArray(content) && content.length > 0;
|
|
2409
|
+
};
|
|
2410
|
+
var shouldUseDesktopLocalRuntime = (agentConfig) => {
|
|
2411
|
+
if (!getIsDesktopApp()) return false;
|
|
2412
|
+
return agentConfig?.apiSource !== "cli";
|
|
2413
|
+
};
|
|
2414
|
+
var readCurrentDesktopMachineId = () => {
|
|
2415
|
+
const fromProcess = typeof process !== "undefined" ? (process.env?.NOLO_CURRENT_MACHINE_ID || process.env?.NOLO_MACHINE_ID || "").trim() : "";
|
|
2416
|
+
if (fromProcess) return fromProcess;
|
|
2417
|
+
const w = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
2418
|
+
const fromWindow = asTrimmedString(w?.__NOLO_CURRENT_MACHINE_ID__) || asTrimmedString(w?.__NOLO_MACHINE_ID__);
|
|
2419
|
+
return fromWindow;
|
|
2420
|
+
};
|
|
2421
|
+
var resolveRemoteBoundMachineId = (machineId) => {
|
|
2422
|
+
if (!machineId || !getIsDesktopApp()) return machineId;
|
|
2423
|
+
const currentMachineId = readCurrentDesktopMachineId();
|
|
2424
|
+
return currentMachineId && currentMachineId === machineId ? "" : machineId;
|
|
2425
|
+
};
|
|
2426
|
+
var resolveWebAgentRuntimeToolSurface = (agentConfig, state) => {
|
|
2427
|
+
const toolSurface = resolveRuntimeToolSurfaceForAgent({
|
|
2428
|
+
explicitToolNames: Array.isArray(agentConfig.tools) ? agentConfig.tools : [],
|
|
2429
|
+
currentUserId: selectIdentityUserId(state),
|
|
2430
|
+
agentOwnerId: typeof agentConfig.userId === "string" ? agentConfig.userId : null,
|
|
2431
|
+
agentKey: agentConfig.dbKey ?? agentConfig.agentKey,
|
|
2432
|
+
isPublic: agentConfig.isPublic === true,
|
|
2433
|
+
sharingLevel: typeof agentConfig.sharingLevel === "string" ? agentConfig.sharingLevel : null,
|
|
2434
|
+
runtimeHost: "web"
|
|
2435
|
+
});
|
|
2436
|
+
return {
|
|
2437
|
+
...agentConfig,
|
|
2438
|
+
tools: toolSurface.finalToolNames,
|
|
2439
|
+
runtimeToolSurface: toolSurface
|
|
2440
|
+
};
|
|
2441
|
+
};
|
|
2442
|
+
var formatMachineAgentRunError = async (response) => {
|
|
2443
|
+
const errorText = await response.text();
|
|
2444
|
+
let payload = null;
|
|
2445
|
+
try {
|
|
2446
|
+
payload = errorText ? JSON.parse(errorText) : null;
|
|
2447
|
+
} catch {
|
|
2448
|
+
payload = null;
|
|
2449
|
+
}
|
|
2450
|
+
const reason = typeof payload?.reason === "string" ? payload.reason : "";
|
|
2451
|
+
if (response.status === 409) {
|
|
2452
|
+
if (reason === "bound_machine_unavailable") {
|
|
2453
|
+
return "\u7ED1\u5B9A\u7684\u7535\u8111\u4E0D\u5728\u7EBF\u3002\u8BF7\u786E\u8BA4\u8FD9\u53F0\u7535\u8111\u5DF2\u5F00\u673A\u5E76\u91CD\u65B0\u8FD0\u884C\u8FDE\u63A5\u547D\u4EE4\u3002";
|
|
2454
|
+
}
|
|
2455
|
+
if (reason === "bound_machine_owner_mismatch") {
|
|
2456
|
+
return "\u8FD9\u53F0\u7535\u8111\u662F\u5728\u7EBF\u7684\uFF0C\u4F46\u5F53\u524D\u8D26\u53F7\u548C Agent \u7ED1\u5B9A\u8D26\u53F7\u4E0D\u4E00\u81F4\u3002\u8BF7\u91CD\u65B0\u7ED1\u5B9A Agent\uFF0C\u6216\u5728\u7ED1\u5B9A\u8D26\u53F7\u4E0B\u91CD\u65B0\u8FDE\u63A5\u8FD9\u53F0\u7535\u8111\u3002";
|
|
2457
|
+
}
|
|
2458
|
+
if (reason === "connector_offline") {
|
|
2459
|
+
return "\u7535\u8111\u5728\u7EBF\uFF0C\u4F46\u8FDE\u63A5\u5668\u672A\u8FDE\u63A5\u3002\u8BF7\u5728\u8FD9\u53F0\u7535\u8111\u4E0A\u91CD\u65B0\u8FD0\u884C\u8FDE\u63A5\u547D\u4EE4\u540E\u518D\u8BD5\u3002";
|
|
2460
|
+
}
|
|
2461
|
+
if (reason === "missing_capability") {
|
|
2462
|
+
return "\u8FD9\u53F0\u7535\u8111\u6CA1\u6709\u5BF9\u5E94\u7684 CLI \u80FD\u529B\u3002\u8BF7\u5B89\u88C5\u5BF9\u5E94 CLI\uFF0C\u6216\u628A Agent \u7ED1\u5B9A\u5230\u53E6\u4E00\u53F0\u7535\u8111\u3002";
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
const message = asOptionalTrimmedString(payload?.message) ?? asOptionalTrimmedString(payload?.error) ?? errorText.trim();
|
|
2466
|
+
return message || `Machine agent run failed (${response.status})`;
|
|
2467
|
+
};
|
|
2468
|
+
var normalizeThreadMetadataPatch = (value) => {
|
|
2469
|
+
if (!isRecord(value)) return null;
|
|
2470
|
+
const record = value;
|
|
2471
|
+
const changes = {};
|
|
2472
|
+
const threadKind = asOptionalTrimmedString(record.threadKind);
|
|
2473
|
+
if (threadKind) {
|
|
2474
|
+
changes.threadKind = threadKind;
|
|
2475
|
+
}
|
|
2476
|
+
const presentationIntent = asOptionalTrimmedString(record.presentationIntent);
|
|
2477
|
+
if (presentationIntent) {
|
|
2478
|
+
changes.presentationIntent = presentationIntent;
|
|
2479
|
+
}
|
|
2480
|
+
return Object.keys(changes).length > 0 ? changes : null;
|
|
2481
|
+
};
|
|
2482
|
+
var patchDialogThreadMetadata = async (dispatch, dialogKey, metadata) => {
|
|
2483
|
+
const changes = normalizeThreadMetadataPatch(metadata);
|
|
2484
|
+
if (!changes) return;
|
|
2485
|
+
await dispatch(
|
|
2486
|
+
patch({
|
|
2487
|
+
dbKey: dialogKey,
|
|
2488
|
+
changes
|
|
2489
|
+
})
|
|
2490
|
+
).unwrap?.();
|
|
2491
|
+
};
|
|
2492
|
+
var patchDialogActiveAgent = async (dispatch, dialogKey, agentKey) => {
|
|
2493
|
+
if (typeof agentKey !== "string" || !agentKey.trim()) return;
|
|
2494
|
+
await dispatch(
|
|
2495
|
+
patch({
|
|
2496
|
+
dbKey: dialogKey,
|
|
2497
|
+
changes: {
|
|
2498
|
+
primaryAgentKey: agentKey.trim()
|
|
2499
|
+
}
|
|
2500
|
+
})
|
|
2501
|
+
).unwrap?.();
|
|
2502
|
+
};
|
|
2503
|
+
function appendCliCapabilityWarnings(content, warnings) {
|
|
2504
|
+
if (!warnings.length) return content;
|
|
2505
|
+
const warningBlock = `
|
|
2506
|
+
|
|
2507
|
+
[CLI \u80FD\u529B\u63D0\u793A]
|
|
2508
|
+
${warnings.map((warning) => `- ${warning}`).join("\n")}`;
|
|
2509
|
+
return `${content}${warningBlock}`;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
// packages/ai/agent/streamTurnStreamConsumer.ts
|
|
2513
|
+
async function readAgentRunStreamChunk(reader, signal, isAborted) {
|
|
2514
|
+
if (isAborted() || signal?.aborted) {
|
|
2515
|
+
void reader.cancel().catch(() => {
|
|
2516
|
+
});
|
|
2517
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
2518
|
+
}
|
|
2519
|
+
if (!signal) {
|
|
2520
|
+
return reader.read();
|
|
2521
|
+
}
|
|
2522
|
+
let settled = false;
|
|
2523
|
+
return new Promise((resolve, reject) => {
|
|
2524
|
+
const finish = (cb) => {
|
|
2525
|
+
if (settled) return;
|
|
2526
|
+
settled = true;
|
|
2527
|
+
signal.removeEventListener("abort", onAbort);
|
|
2528
|
+
cb();
|
|
2529
|
+
};
|
|
2530
|
+
const onAbort = () => {
|
|
2531
|
+
void reader.cancel().catch(() => {
|
|
2532
|
+
});
|
|
2533
|
+
finish(
|
|
2534
|
+
() => reject(new DOMException("The operation was aborted.", "AbortError"))
|
|
2535
|
+
);
|
|
2536
|
+
};
|
|
2537
|
+
signal.addEventListener("abort", onAbort);
|
|
2538
|
+
reader.read().then(
|
|
2539
|
+
(result) => finish(() => resolve(result)),
|
|
2540
|
+
(err) => finish(() => reject(err))
|
|
2541
|
+
);
|
|
2542
|
+
});
|
|
2543
|
+
}
|
|
2544
|
+
async function consumeAgentRunStream(handlers) {
|
|
2545
|
+
const {
|
|
2546
|
+
reader,
|
|
2547
|
+
decoder,
|
|
2548
|
+
parseChunk,
|
|
2549
|
+
onPayload,
|
|
2550
|
+
isDoneEvent,
|
|
2551
|
+
isAborted,
|
|
2552
|
+
signal,
|
|
2553
|
+
onAbort
|
|
2554
|
+
} = handlers;
|
|
2555
|
+
let sawDone = false;
|
|
2556
|
+
try {
|
|
2557
|
+
while (true) {
|
|
2558
|
+
let done;
|
|
2559
|
+
let value;
|
|
2560
|
+
try {
|
|
2561
|
+
({ done, value } = await readAgentRunStreamChunk(
|
|
2562
|
+
reader,
|
|
2563
|
+
signal,
|
|
2564
|
+
isAborted
|
|
2565
|
+
));
|
|
2566
|
+
} catch (error) {
|
|
2567
|
+
if (isAbortError(error) || isAborted() || signal?.aborted) {
|
|
2568
|
+
await onAbort();
|
|
2569
|
+
return { outcome: "aborted" };
|
|
2570
|
+
}
|
|
2571
|
+
throw error;
|
|
2572
|
+
}
|
|
2573
|
+
if (isAborted() || signal?.aborted) {
|
|
2574
|
+
await onAbort();
|
|
2575
|
+
return { outcome: "aborted" };
|
|
2576
|
+
}
|
|
2577
|
+
if (done) {
|
|
2578
|
+
return { outcome: "streamEnded", sawDone };
|
|
2579
|
+
}
|
|
2580
|
+
const payloads = parseChunk(
|
|
2581
|
+
decoder.decode(value, { stream: true })
|
|
2582
|
+
);
|
|
2583
|
+
for (const payload of payloads) {
|
|
2584
|
+
if (isAborted() || signal?.aborted) {
|
|
2585
|
+
await onAbort();
|
|
2586
|
+
return { outcome: "aborted" };
|
|
2587
|
+
}
|
|
2588
|
+
const directive = await onPayload(payload);
|
|
2589
|
+
if (directive?.reject !== void 0) {
|
|
2590
|
+
return { outcome: "rejected", message: directive.reject };
|
|
2591
|
+
}
|
|
2592
|
+
if (directive?.abort) {
|
|
2593
|
+
await onAbort();
|
|
2594
|
+
return { outcome: "aborted" };
|
|
2595
|
+
}
|
|
2596
|
+
if (isDoneEvent(payload)) {
|
|
2597
|
+
sawDone = true;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
if (isAbortError(error) || isAborted() || signal?.aborted) {
|
|
2603
|
+
await onAbort();
|
|
2604
|
+
return { outcome: "aborted" };
|
|
2605
|
+
}
|
|
2606
|
+
throw error;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
// packages/ai/llm/isResponseAPIModel.ts
|
|
2611
|
+
var isResponseAPIModel = (agentConfig) => {
|
|
2612
|
+
if (agentConfig.provider !== "openai") return false;
|
|
2613
|
+
if (agentConfig.endpointKey === "responses") return true;
|
|
2614
|
+
if (!agentConfig.model) return false;
|
|
2615
|
+
try {
|
|
2616
|
+
return getModelConfig("openai", agentConfig.model).endpointKey === "responses";
|
|
2617
|
+
} catch {
|
|
2618
|
+
return false;
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2621
|
+
|
|
2622
|
+
// packages/ai/agent/streamTurnQuickChat.ts
|
|
2623
|
+
var QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS = 1e4;
|
|
2624
|
+
var QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS = 5e3;
|
|
2625
|
+
var EMPTY_DYNAMIC_CONTEXTS = {
|
|
2626
|
+
currentInputContext: null,
|
|
2627
|
+
historyContext: "",
|
|
2628
|
+
editingContext: null,
|
|
2629
|
+
appWorkingMemory: null,
|
|
2630
|
+
memoryOverlay: null,
|
|
2631
|
+
dialogSummary: null,
|
|
2632
|
+
proactiveSummary: null,
|
|
2633
|
+
referenceKeys: []
|
|
2634
|
+
};
|
|
2635
|
+
var logQuickChatPerfStage2 = (startedAt, stage, details = {}) => {
|
|
2636
|
+
if (!startedAt) return;
|
|
2637
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
2638
|
+
console.info("[QuickChatPerf]", {
|
|
2639
|
+
stage,
|
|
2640
|
+
elapsedMs: now - startedAt,
|
|
2641
|
+
...typeof performance !== "undefined" ? { atMs: now } : {},
|
|
2642
|
+
...details
|
|
2643
|
+
});
|
|
2644
|
+
};
|
|
2645
|
+
var readAgentConfigForTurn = async (dispatch, agentKey, quickChatPerfStartedAt) => {
|
|
2646
|
+
const readPromise = dispatch(read({ dbKey: agentKey })).unwrap();
|
|
2647
|
+
if (!quickChatPerfStartedAt) return readPromise;
|
|
2648
|
+
let timeoutId;
|
|
2649
|
+
try {
|
|
2650
|
+
return await Promise.race([
|
|
2651
|
+
readPromise,
|
|
2652
|
+
new Promise((_, reject) => {
|
|
2653
|
+
timeoutId = setTimeout(() => {
|
|
2654
|
+
logQuickChatPerfStage2(
|
|
2655
|
+
quickChatPerfStartedAt,
|
|
2656
|
+
"quick-chat-agent-config-read-timeout",
|
|
2657
|
+
{ agentKey, timeoutMs: QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS }
|
|
2658
|
+
);
|
|
2659
|
+
reject(
|
|
2660
|
+
new Error("\u8BFB\u53D6 Agent \u914D\u7F6E\u8D85\u65F6\uFF0C\u672A\u80FD\u542F\u52A8\u6A21\u578B\u56DE\u590D\u3002")
|
|
2661
|
+
);
|
|
2662
|
+
}, QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS);
|
|
2663
|
+
})
|
|
2664
|
+
]);
|
|
2665
|
+
} finally {
|
|
2666
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2667
|
+
}
|
|
2668
|
+
};
|
|
2669
|
+
var buildDynamicContextsForTurn = async (state, dispatch, agentConfig, userInput, runtimeOptions, mergedContentCache, dialogKey, quickChatPerfStartedAt) => {
|
|
2670
|
+
if (!quickChatPerfStartedAt) {
|
|
2671
|
+
return buildDynamicContexts(
|
|
2672
|
+
state,
|
|
2673
|
+
dispatch,
|
|
2674
|
+
agentConfig,
|
|
2675
|
+
userInput,
|
|
2676
|
+
runtimeOptions,
|
|
2677
|
+
mergedContentCache,
|
|
2678
|
+
dialogKey
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2681
|
+
let timeoutId;
|
|
2682
|
+
try {
|
|
2683
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
2684
|
+
timeoutId = setTimeout(() => {
|
|
2685
|
+
logQuickChatPerfStage2(
|
|
2686
|
+
quickChatPerfStartedAt,
|
|
2687
|
+
"quick-chat-dynamic-context-timeout",
|
|
2688
|
+
{ agentKey: agentConfig.dbKey, dialogKey, timeoutMs: QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS }
|
|
2689
|
+
);
|
|
2690
|
+
resolve(EMPTY_DYNAMIC_CONTEXTS);
|
|
2691
|
+
}, QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS);
|
|
2692
|
+
});
|
|
2693
|
+
logQuickChatPerfStage2(
|
|
2694
|
+
quickChatPerfStartedAt,
|
|
2695
|
+
"stream-agent-dynamic-context-starting",
|
|
2696
|
+
{ responseApi: isResponseAPIModel(agentConfig) }
|
|
2697
|
+
);
|
|
2698
|
+
const contextPromise = buildDynamicContexts(
|
|
2699
|
+
state,
|
|
2700
|
+
dispatch,
|
|
2701
|
+
agentConfig,
|
|
2702
|
+
userInput,
|
|
2703
|
+
runtimeOptions,
|
|
2704
|
+
mergedContentCache,
|
|
2705
|
+
dialogKey
|
|
2706
|
+
);
|
|
2707
|
+
return await Promise.race([
|
|
2708
|
+
contextPromise,
|
|
2709
|
+
timeoutPromise
|
|
2710
|
+
]);
|
|
2711
|
+
} finally {
|
|
2712
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2713
|
+
}
|
|
2714
|
+
};
|
|
2715
|
+
var finalizeQuickChatAgentTurnFailure = async (dispatch, dialogKey, agentKey, error) => {
|
|
2716
|
+
const dialogId = extractCustomId(dialogKey);
|
|
2717
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
2718
|
+
const reason = error instanceof Error && error.message.trim() ? error.message.trim() : typeof error === "string" && error.trim() ? error.trim() : error && typeof error === "object" && typeof error.message === "string" && error.message.trim() ? error.message.trim() : "\u672A\u80FD\u542F\u52A8\u6A21\u578B\u56DE\u590D\uFF0C\u8BF7\u91CD\u8BD5\u3002";
|
|
2719
|
+
await dispatch(
|
|
2720
|
+
messageStreamEnd({
|
|
2721
|
+
finalContentBuffer: [
|
|
2722
|
+
{ type: "text", text: `[\u9519\u8BEF: ${reason}]` }
|
|
2723
|
+
],
|
|
2724
|
+
totalUsage: null,
|
|
2725
|
+
msgKey,
|
|
2726
|
+
agentConfig: { dbKey: agentKey },
|
|
2727
|
+
dialogId,
|
|
2728
|
+
dialogKey,
|
|
2729
|
+
messageId,
|
|
2730
|
+
reasoningBuffer: ""
|
|
2731
|
+
})
|
|
2732
|
+
).unwrap?.();
|
|
2733
|
+
};
|
|
2734
|
+
var normalizeAgentRunUserInput = (userInput) => {
|
|
2735
|
+
if (typeof userInput === "string") return userInput;
|
|
2736
|
+
if (!Array.isArray(userInput)) return "";
|
|
2737
|
+
return userInput.filter((part) => {
|
|
2738
|
+
if (!part || typeof part !== "object") return false;
|
|
2739
|
+
if (part.type === "text") return typeof part.text === "string";
|
|
2740
|
+
return part.type === "image_url" && typeof part.image_url?.url === "string" && !!part.image_url.url.trim();
|
|
2741
|
+
});
|
|
2742
|
+
};
|
|
2743
|
+
var isSimpleTextInput = (userInput) => {
|
|
2744
|
+
if (typeof userInput === "string") return true;
|
|
2745
|
+
if (!Array.isArray(userInput)) return false;
|
|
2746
|
+
return userInput.every(
|
|
2747
|
+
(part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string"
|
|
2748
|
+
);
|
|
2749
|
+
};
|
|
2750
|
+
var canUseQuickChatEmptyDynamicContexts = (quickChatPerfStartedAt, userInput, runtimeOptions, dialogConfig, agentConfig) => {
|
|
2751
|
+
if (!quickChatPerfStartedAt) return false;
|
|
2752
|
+
if (runtimeOptions) return false;
|
|
2753
|
+
if (agentConfig?.tools?.includes("rememberMemory")) return false;
|
|
2754
|
+
if (!isSimpleTextInput(userInput)) return false;
|
|
2755
|
+
if (dialogConfig?.referenceKeys?.length) return false;
|
|
2756
|
+
return true;
|
|
2757
|
+
};
|
|
2758
|
+
var QUICK_CHAT_DIRECT_ANSWER_PATTERN = /(只回复|只输出|只回答|直接回复|直接回答|不要解释|无需解释|不用解释|简短回答|一句话)/i;
|
|
2759
|
+
var QUICK_CHAT_TOOL_INTENT_PATTERN = /(调用|转交|agent|助手|应用|网页|页面|图表|图片|生成图|画图|删除|清理|空间|商品|链接|https?:\/\/|www\.|@)/i;
|
|
2760
|
+
var shouldDisableQuickChatToolsForDirectAnswer = (quickChatPerfStartedAt, userInput, runtimeOptions, dialogConfig) => {
|
|
2761
|
+
if (!canUseQuickChatEmptyDynamicContexts(
|
|
2762
|
+
quickChatPerfStartedAt,
|
|
2763
|
+
userInput,
|
|
2764
|
+
runtimeOptions,
|
|
2765
|
+
dialogConfig,
|
|
2766
|
+
void 0
|
|
2767
|
+
)) {
|
|
2768
|
+
return false;
|
|
2769
|
+
}
|
|
2770
|
+
const text = extractAgentRunUserText(userInput);
|
|
2771
|
+
if (!text || text.length > 500) return false;
|
|
2772
|
+
if (!QUICK_CHAT_DIRECT_ANSWER_PATTERN.test(text)) return false;
|
|
2773
|
+
return !QUICK_CHAT_TOOL_INTENT_PATTERN.test(text);
|
|
2774
|
+
};
|
|
2775
|
+
var classifyQuickChatAccessError = (accessError) => {
|
|
2776
|
+
if (accessError.includes("\u83B7\u53D6\u7528\u6237\u4F59\u989D")) return "balance-loading";
|
|
2777
|
+
if (accessError.includes("\u4F59\u989D")) return "balance";
|
|
2778
|
+
if (accessError.includes("\u767D\u540D\u5355")) return "whitelist";
|
|
2779
|
+
if (accessError.includes("\u5B9A\u4EF7")) return "pricing";
|
|
2780
|
+
return "unknown";
|
|
2781
|
+
};
|
|
2782
|
+
var isUsableAgentConfig = (value) => !!value && typeof value === "object" && value.__ssrPreviewOnly !== true && typeof value.dbKey === "string" && !!value.dbKey && typeof value.model === "string" && !!value.model && typeof value.provider === "string" && !!value.provider;
|
|
2783
|
+
var extractAgentRunUserText = (userInput) => {
|
|
2784
|
+
if (typeof userInput === "string") return userInput.trim();
|
|
2785
|
+
if (!Array.isArray(userInput)) return "";
|
|
2786
|
+
return userInput.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join("").trim();
|
|
2787
|
+
};
|
|
2788
|
+
var hasAgentRunUserInputContent = (userInput) => {
|
|
2789
|
+
if (typeof userInput === "string") return userInput.trim().length > 0;
|
|
2790
|
+
return Array.isArray(userInput) && userInput.length > 0;
|
|
2791
|
+
};
|
|
2792
|
+
var isLastMessageMatchingUserInput = (visibleMessages, userInput) => {
|
|
2793
|
+
if (visibleMessages.length === 0) return false;
|
|
2794
|
+
const lastMsg = visibleMessages[visibleMessages.length - 1];
|
|
2795
|
+
if (lastMsg.role !== "user") return false;
|
|
2796
|
+
const content1 = lastMsg.content;
|
|
2797
|
+
const content2 = userInput;
|
|
2798
|
+
const normalize = (content) => {
|
|
2799
|
+
if (typeof content === "string") return content.trim();
|
|
2800
|
+
if (Array.isArray(content)) {
|
|
2801
|
+
if (content.length === 1 && content[0]?.type === "text") {
|
|
2802
|
+
return (content[0].text || "").trim();
|
|
2803
|
+
}
|
|
2804
|
+
return content.map((part) => {
|
|
2805
|
+
if (part?.type === "text") return { type: "text", text: part.text?.trim() };
|
|
2806
|
+
if (part?.type === "image_url") return { type: "image_url", url: part.image_url?.url };
|
|
2807
|
+
return part;
|
|
2808
|
+
});
|
|
2809
|
+
}
|
|
2810
|
+
return content;
|
|
2811
|
+
};
|
|
2812
|
+
const norm1 = normalize(content1);
|
|
2813
|
+
const norm2 = normalize(content2);
|
|
2814
|
+
if (typeof norm1 === "string" && typeof norm2 === "string") {
|
|
2815
|
+
return norm1 === norm2;
|
|
2816
|
+
}
|
|
2817
|
+
return JSON.stringify(norm1) === JSON.stringify(norm2);
|
|
2818
|
+
};
|
|
2819
|
+
var setLoopStopReason = (reason) => {
|
|
2820
|
+
const w = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
2821
|
+
if (w) w.__LOOP_STOP_REASON__ = reason;
|
|
2822
|
+
};
|
|
2823
|
+
var prewarmPreparedToolsForAgent = (agentConfig) => {
|
|
2824
|
+
const tools = agentConfig?.tools;
|
|
2825
|
+
if (!Array.isArray(tools) || tools.length === 0) return;
|
|
2826
|
+
try {
|
|
2827
|
+
prepareTools(tools, { provider: agentConfig.provider });
|
|
2828
|
+
} catch {
|
|
2829
|
+
}
|
|
2830
|
+
};
|
|
2831
|
+
var buildStaticContextsWithToolsPrewarm = async (state, dispatch, agentConfig, currentDialog, mergedContentCache) => {
|
|
2832
|
+
const [staticContexts] = await Promise.all([
|
|
2833
|
+
buildStaticContexts(
|
|
2834
|
+
state,
|
|
2835
|
+
dispatch,
|
|
2836
|
+
agentConfig,
|
|
2837
|
+
currentDialog ?? void 0,
|
|
2838
|
+
mergedContentCache
|
|
2839
|
+
),
|
|
2840
|
+
Promise.resolve().then(() => prewarmPreparedToolsForAgent(agentConfig))
|
|
2841
|
+
]);
|
|
2842
|
+
return staticContexts;
|
|
2843
|
+
};
|
|
2844
|
+
|
|
2845
|
+
// packages/ai/agent/streamAgentChatTurn.ts
|
|
2846
|
+
function hasInlineExecutedToolCalls(result) {
|
|
2847
|
+
const output = result?.output;
|
|
2848
|
+
return Array.isArray(output) && output.some((b) => b?.type === "toolCall" && b?.result != null);
|
|
2849
|
+
}
|
|
2850
|
+
var streamAgentChatTurnHandler = async (args, thunkApi) => {
|
|
2851
|
+
const {
|
|
2852
|
+
agentKey,
|
|
2853
|
+
userInput,
|
|
2854
|
+
dialogKey: explicitDialogKey,
|
|
2855
|
+
parentMessageId,
|
|
2856
|
+
runtimeOptions,
|
|
2857
|
+
quickChatPerfStartedAt
|
|
2858
|
+
} = args;
|
|
2859
|
+
const { getState, dispatch, rejectWithValue } = thunkApi;
|
|
2860
|
+
const state = getState();
|
|
2861
|
+
const loopController = new AbortController();
|
|
2862
|
+
const isTurnAborted = (error) => isAbortError(error) || loopController.signal.aborted || thunkApi.signal.aborted;
|
|
2863
|
+
const onAbort = () => loopController.abort();
|
|
2864
|
+
thunkApi.signal.addEventListener("abort", onAbort);
|
|
2865
|
+
let loopKey = null;
|
|
2866
|
+
let runtimeDialogKey = explicitDialogKey ?? null;
|
|
2867
|
+
let remoteTransientMessageId = null;
|
|
2868
|
+
let remoteTransientMessageFinalized = false;
|
|
2869
|
+
let modelRequestStarted = false;
|
|
2870
|
+
let turnAborted = false;
|
|
2871
|
+
if (explicitDialogKey) {
|
|
2872
|
+
const dialogId = extractCustomId(explicitDialogKey);
|
|
2873
|
+
const existingLoopKey = `loop:${dialogId}`;
|
|
2874
|
+
const activeControllers = selectActiveControllers(
|
|
2875
|
+
getState(),
|
|
2876
|
+
explicitDialogKey
|
|
2877
|
+
);
|
|
2878
|
+
if (activeControllers[existingLoopKey]) {
|
|
2879
|
+
console.warn(
|
|
2880
|
+
"[streamAgentChatTurn] Rejected concurrent turn for dialog",
|
|
2881
|
+
{ dialogId, agentKey }
|
|
2882
|
+
);
|
|
2883
|
+
return rejectWithValue("Agent is already responding for this dialog");
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
try {
|
|
2887
|
+
let totalTurnUsage = null;
|
|
2888
|
+
const agentRunUserInput = normalizeAgentRunUserInput(userInput);
|
|
2889
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-entered", {
|
|
2890
|
+
agentKey,
|
|
2891
|
+
dialogKey: explicitDialogKey ?? null
|
|
2892
|
+
});
|
|
2893
|
+
const cachedAgentConfig = selectById(
|
|
2894
|
+
getState(),
|
|
2895
|
+
agentKey
|
|
2896
|
+
);
|
|
2897
|
+
let rawAgentConfig = null;
|
|
2898
|
+
rawAgentConfig = isUsableAgentConfig(cachedAgentConfig) ? cachedAgentConfig : await readAgentConfigForTurn(
|
|
2899
|
+
dispatch,
|
|
2900
|
+
agentKey,
|
|
2901
|
+
quickChatPerfStartedAt
|
|
2902
|
+
);
|
|
2903
|
+
if (!rawAgentConfig) {
|
|
2904
|
+
return rejectWithValue(
|
|
2905
|
+
`Agent config not found for ID: ${agentKey}`
|
|
2906
|
+
);
|
|
2907
|
+
}
|
|
2908
|
+
if (runtimeOptions?.quickChatModelOverride) {
|
|
2909
|
+
rawAgentConfig = applyQuickChatModelOverride(
|
|
2910
|
+
rawAgentConfig,
|
|
2911
|
+
runtimeOptions.quickChatModelOverride
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
if (isLiveAudioOnlyAgent(rawAgentConfig)) {
|
|
2915
|
+
return rejectWithValue(
|
|
2916
|
+
"\u6B64 Agent \u4EC5\u652F\u6301\u5B9E\u65F6\u8BED\u97F3\u6A21\u5F0F\uFF0C\u8BF7\u4F7F\u7528\u8BED\u97F3\u9762\u677F\u8FDB\u884C\u5BF9\u8BDD\u3002"
|
|
2917
|
+
);
|
|
2918
|
+
}
|
|
2919
|
+
const agentConfig = resolveWebAgentRuntimeToolSurface(
|
|
2920
|
+
rawAgentConfig,
|
|
2921
|
+
getState()
|
|
2922
|
+
);
|
|
2923
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-config-read", {
|
|
2924
|
+
agentKey,
|
|
2925
|
+
model: agentConfig.model,
|
|
2926
|
+
provider: agentConfig.provider,
|
|
2927
|
+
apiSource: agentConfig.apiSource,
|
|
2928
|
+
source: rawAgentConfig === cachedAgentConfig ? "cache" : "read"
|
|
2929
|
+
});
|
|
2930
|
+
const gptProCheck = shouldBlockForGptPro(
|
|
2931
|
+
agentConfig,
|
|
2932
|
+
selectIdentityUser(getState())?.gptProAccess?.status
|
|
2933
|
+
);
|
|
2934
|
+
if (gptProCheck.blocked) {
|
|
2935
|
+
return rejectWithValue(gptProCheck.message);
|
|
2936
|
+
}
|
|
2937
|
+
const configuredBoundMachineId = asTrimmedString(
|
|
2938
|
+
agentConfig.runtimeBinding?.machineId
|
|
2939
|
+
);
|
|
2940
|
+
const boundMachineId = resolveRemoteBoundMachineId(configuredBoundMachineId);
|
|
2941
|
+
if (agentConfig.apiSource === "cli" || boundMachineId && !getIsDesktopApp()) {
|
|
2942
|
+
console.info("[streamAgentChatTurn] Triggered CLI/machine route. apiSource:", agentConfig.apiSource, "boundMachineId:", boundMachineId, "agentKey:", agentKey);
|
|
2943
|
+
const currentState = getState();
|
|
2944
|
+
const w2 = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
2945
|
+
if (w2) w2.__LOOP_STOP_REASON__ = null;
|
|
2946
|
+
const userText = extractAgentRunUserText(userInput);
|
|
2947
|
+
const prompt = buildCliPrompt(agentConfig.prompt, userText);
|
|
2948
|
+
const dialogConfig = selectDialogConfigByKey(currentState, explicitDialogKey) ?? selectCurrentDialogConfig(currentState);
|
|
2949
|
+
if (!dialogConfig) {
|
|
2950
|
+
return rejectWithValue("Dialog config not found");
|
|
2951
|
+
}
|
|
2952
|
+
const dialogKey2 = explicitDialogKey || dialogConfig.dbKey;
|
|
2953
|
+
if (!dialogKey2) {
|
|
2954
|
+
return rejectWithValue("\u5F53\u524D\u5BF9\u8BDD\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u53D1\u9001\u6D88\u606F\u3002");
|
|
2955
|
+
}
|
|
2956
|
+
runtimeDialogKey = dialogKey2;
|
|
2957
|
+
const dialogId2 = extractCustomId(dialogKey2);
|
|
2958
|
+
loopKey = `loop:${dialogId2}`;
|
|
2959
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey: dialogKey2 }));
|
|
2960
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId2);
|
|
2961
|
+
const cliMessageMetadata = buildMessageMetadata(agentConfig);
|
|
2962
|
+
if (boundMachineId) {
|
|
2963
|
+
const token = selectIdentityToken(currentState);
|
|
2964
|
+
const authHeader = token ? `Bearer ${token}` : "";
|
|
2965
|
+
const rawMessages = selectAllMsgs(currentState, dialogId2);
|
|
2966
|
+
const visibleMessages = buildAgentViewMessages(
|
|
2967
|
+
rawMessages,
|
|
2968
|
+
agentConfig.dbKey
|
|
2969
|
+
);
|
|
2970
|
+
const cleanedMessages = filterAndCleanMessages(visibleMessages);
|
|
2971
|
+
const currentServer2 = selectCurrentServer(currentState);
|
|
2972
|
+
remoteTransientMessageId = messageId;
|
|
2973
|
+
let accumulated2 = "";
|
|
2974
|
+
let totalTurnUsage2 = void 0;
|
|
2975
|
+
const buildMachineAssistantMessage = () => ({
|
|
2976
|
+
id: messageId,
|
|
2977
|
+
dbKey: msgKey,
|
|
2978
|
+
role: "assistant",
|
|
2979
|
+
content: accumulated2,
|
|
2980
|
+
...cliMessageMetadata,
|
|
2981
|
+
userId: selectIdentityUserId(getState())
|
|
2982
|
+
});
|
|
2983
|
+
dispatch(messageStreaming({
|
|
2984
|
+
id: messageId,
|
|
2985
|
+
dialogId: dialogId2,
|
|
2986
|
+
dbKey: msgKey,
|
|
2987
|
+
content: "",
|
|
2988
|
+
role: "assistant",
|
|
2989
|
+
...cliMessageMetadata
|
|
2990
|
+
}));
|
|
2991
|
+
const rejectMachineStream = async (message) => {
|
|
2992
|
+
if (accumulated2.length > 0) {
|
|
2993
|
+
await persistMessageWithFixedId(dispatch, buildMachineAssistantMessage());
|
|
2994
|
+
} else {
|
|
2995
|
+
dispatch(removeTransientMessage(messageId));
|
|
2996
|
+
}
|
|
2997
|
+
setLoopStopReason("error");
|
|
2998
|
+
remoteTransientMessageFinalized = true;
|
|
2999
|
+
return rejectWithValue(message);
|
|
3000
|
+
};
|
|
3001
|
+
const machineResponse = await fetch(`${currentServer2.replace(/\/+$/, "")}/api/agent/run`, {
|
|
3002
|
+
method: "POST",
|
|
3003
|
+
headers: {
|
|
3004
|
+
"Content-Type": "application/json",
|
|
3005
|
+
Accept: "text/event-stream",
|
|
3006
|
+
...authHeader ? { Authorization: authHeader } : {}
|
|
3007
|
+
},
|
|
3008
|
+
body: JSON.stringify({
|
|
3009
|
+
agentKey,
|
|
3010
|
+
userInput: agentRunUserInput,
|
|
3011
|
+
messages: cleanedMessages,
|
|
3012
|
+
stream: true,
|
|
3013
|
+
persistDialog: false,
|
|
3014
|
+
clientDialogId: dialogId2,
|
|
3015
|
+
runtimeContext: {
|
|
3016
|
+
surface: "web",
|
|
3017
|
+
host: "browser",
|
|
3018
|
+
runtime: "react",
|
|
3019
|
+
entrypoint: "chat-dialog",
|
|
3020
|
+
capabilities: ["streaming", "dialog-ui", "machine-bound-cli"]
|
|
3021
|
+
},
|
|
3022
|
+
...dialogConfig?.spaceId ? { spaceId: dialogConfig.spaceId } : {}
|
|
3023
|
+
}),
|
|
3024
|
+
signal: loopController.signal
|
|
3025
|
+
});
|
|
3026
|
+
if (!machineResponse.ok) {
|
|
3027
|
+
return await rejectMachineStream(
|
|
3028
|
+
await formatMachineAgentRunError(machineResponse)
|
|
3029
|
+
);
|
|
3030
|
+
}
|
|
3031
|
+
const reader2 = machineResponse.body?.getReader();
|
|
3032
|
+
if (!reader2) {
|
|
3033
|
+
return await rejectMachineStream("\u65E0\u6CD5\u8BFB\u53D6\u7535\u8111\u7AEF Agent \u6D41\u5F0F\u54CD\u5E94");
|
|
3034
|
+
}
|
|
3035
|
+
const decoder2 = new TextDecoder();
|
|
3036
|
+
const parseSSE = createSSEParser();
|
|
3037
|
+
const abortMachineStream = async () => {
|
|
3038
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "aborted";
|
|
3039
|
+
if (accumulated2.length <= 0) return;
|
|
3040
|
+
await persistMessageWithFixedId(dispatch, buildMachineAssistantMessage());
|
|
3041
|
+
};
|
|
3042
|
+
try {
|
|
3043
|
+
const result = await consumeAgentRunStream({
|
|
3044
|
+
reader: reader2,
|
|
3045
|
+
decoder: decoder2,
|
|
3046
|
+
parseChunk: (raw) => parseSSE(raw),
|
|
3047
|
+
isAborted: () => loopController.signal.aborted || thunkApi.signal.aborted,
|
|
3048
|
+
signal: loopController.signal,
|
|
3049
|
+
onAbort: abortMachineStream,
|
|
3050
|
+
isDoneEvent: (payload) => payload?.type === "done",
|
|
3051
|
+
onPayload: (payload) => {
|
|
3052
|
+
if (payload.type === "error") {
|
|
3053
|
+
return { reject: payload.message || "\u7535\u8111\u7AEF Agent \u6267\u884C\u5931\u8D25" };
|
|
3054
|
+
}
|
|
3055
|
+
if (payload.type === "text" && typeof payload.content === "string") {
|
|
3056
|
+
accumulated2 += payload.content;
|
|
3057
|
+
dispatch(messageStreaming({
|
|
3058
|
+
id: messageId,
|
|
3059
|
+
dialogId: dialogId2,
|
|
3060
|
+
dbKey: msgKey,
|
|
3061
|
+
content: accumulated2,
|
|
3062
|
+
role: "assistant",
|
|
3063
|
+
...cliMessageMetadata
|
|
3064
|
+
}));
|
|
3065
|
+
}
|
|
3066
|
+
if (payload.type === "done") {
|
|
3067
|
+
totalTurnUsage2 = payload.usage;
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
});
|
|
3071
|
+
if (result.outcome === "rejected") {
|
|
3072
|
+
return await rejectMachineStream(result.message);
|
|
3073
|
+
}
|
|
3074
|
+
if (result.outcome === "aborted") {
|
|
3075
|
+
return { aborted: true };
|
|
3076
|
+
}
|
|
3077
|
+
if (result.outcome === "streamEnded") {
|
|
3078
|
+
if (!result.sawDone) {
|
|
3079
|
+
dispatch(finalizeTransientMessageOnError({
|
|
3080
|
+
id: messageId,
|
|
3081
|
+
error: "\u7535\u8111\u7AEF Agent \u6D41\u5F0F\u54CD\u5E94\u88AB\u4E2D\u65AD,\u672A\u6536\u5230\u5B8C\u6210\u4FE1\u53F7"
|
|
3082
|
+
}));
|
|
3083
|
+
remoteTransientMessageFinalized = true;
|
|
3084
|
+
setLoopStopReason("error");
|
|
3085
|
+
return rejectWithValue(
|
|
3086
|
+
"\u7535\u8111\u7AEF Agent \u6D41\u5F0F\u54CD\u5E94\u88AB\u4E2D\u65AD,\u672A\u6536\u5230\u5B8C\u6210\u4FE1\u53F7"
|
|
3087
|
+
);
|
|
3088
|
+
}
|
|
3089
|
+
await persistMessageWithFixedId(dispatch, buildMachineAssistantMessage());
|
|
3090
|
+
remoteTransientMessageFinalized = true;
|
|
3091
|
+
}
|
|
3092
|
+
} finally {
|
|
3093
|
+
try {
|
|
3094
|
+
await reader2.cancel();
|
|
3095
|
+
} catch {
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
return {
|
|
3099
|
+
usage: totalTurnUsage2 ?? void 0
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
let cliSessionId = dialogConfig.cliSessionId ?? null;
|
|
3103
|
+
dispatch(messageStreaming({
|
|
3104
|
+
id: messageId,
|
|
3105
|
+
dialogId: dialogId2,
|
|
3106
|
+
dbKey: msgKey,
|
|
3107
|
+
content: "",
|
|
3108
|
+
role: "assistant",
|
|
3109
|
+
...cliMessageMetadata
|
|
3110
|
+
}));
|
|
3111
|
+
remoteTransientMessageId = messageId;
|
|
3112
|
+
const ensureCliSession = async () => {
|
|
3113
|
+
if (cliSessionId) {
|
|
3114
|
+
const existing = await getCliChatSession(
|
|
3115
|
+
{ getState },
|
|
3116
|
+
{ sessionId: cliSessionId }
|
|
3117
|
+
).catch(() => null);
|
|
3118
|
+
if (existing?.ok && existing?.session?.sessionId) {
|
|
3119
|
+
return cliSessionId;
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
const started = await startCliChatSession(
|
|
3123
|
+
{ getState },
|
|
3124
|
+
{
|
|
3125
|
+
cliProvider: agentConfig.cliProvider || "copilot",
|
|
3126
|
+
model: agentConfig.model || void 0,
|
|
3127
|
+
systemPrompt: agentConfig.prompt || void 0,
|
|
3128
|
+
reasoningEffort: agentConfig.reasoning_effort || agentConfig.reasoningEffort || void 0,
|
|
3129
|
+
temperature: agentConfig.temperature,
|
|
3130
|
+
topP: agentConfig.top_p,
|
|
3131
|
+
frequencyPenalty: agentConfig.frequency_penalty,
|
|
3132
|
+
presencePenalty: agentConfig.presence_penalty,
|
|
3133
|
+
maxTokens: agentConfig.max_tokens,
|
|
3134
|
+
enableThinking: agentConfig.enableThinking,
|
|
3135
|
+
thinkingBudget: agentConfig.thinkingBudget
|
|
3136
|
+
}
|
|
3137
|
+
);
|
|
3138
|
+
const newSessionId = typeof started?.sessionId === "string" ? started.sessionId : null;
|
|
3139
|
+
if (!newSessionId) {
|
|
3140
|
+
throw new Error("\u65E0\u6CD5\u521B\u5EFA CLI session\u3002");
|
|
3141
|
+
}
|
|
3142
|
+
cliSessionId = newSessionId;
|
|
3143
|
+
const patchResult = dispatch(
|
|
3144
|
+
patch({
|
|
3145
|
+
dbKey: dialogKey2,
|
|
3146
|
+
changes: {
|
|
3147
|
+
cliSessionId: newSessionId
|
|
3148
|
+
}
|
|
3149
|
+
})
|
|
3150
|
+
);
|
|
3151
|
+
try {
|
|
3152
|
+
if (typeof patchResult?.unwrap === "function") {
|
|
3153
|
+
await patchResult.unwrap();
|
|
3154
|
+
} else {
|
|
3155
|
+
await patchResult;
|
|
3156
|
+
}
|
|
3157
|
+
} catch {
|
|
3158
|
+
}
|
|
3159
|
+
return newSessionId;
|
|
3160
|
+
};
|
|
3161
|
+
const initialSessionId = await ensureCliSession();
|
|
3162
|
+
console.info("[streamAgentChatTurn] Calling CLI turn stream. Session ID:", initialSessionId);
|
|
3163
|
+
let resp = await createCliChatTurnStream(
|
|
3164
|
+
{
|
|
3165
|
+
getState
|
|
3166
|
+
},
|
|
3167
|
+
{
|
|
3168
|
+
sessionId: initialSessionId,
|
|
3169
|
+
prompt,
|
|
3170
|
+
model: agentConfig.model || void 0
|
|
3171
|
+
},
|
|
3172
|
+
loopController.signal
|
|
3173
|
+
);
|
|
3174
|
+
if (!resp.ok && resp.status === 404) {
|
|
3175
|
+
console.warn("[streamAgentChatTurn] CLI Session 404. Re-creating session...");
|
|
3176
|
+
cliSessionId = null;
|
|
3177
|
+
const renewedSessionId = await ensureCliSession();
|
|
3178
|
+
console.info("[streamAgentChatTurn] Retrying CLI turn stream. Session ID:", renewedSessionId);
|
|
3179
|
+
resp = await createCliChatTurnStream(
|
|
3180
|
+
{
|
|
3181
|
+
getState
|
|
3182
|
+
},
|
|
3183
|
+
{
|
|
3184
|
+
sessionId: renewedSessionId,
|
|
3185
|
+
prompt,
|
|
3186
|
+
model: agentConfig.model || void 0
|
|
3187
|
+
},
|
|
3188
|
+
loopController.signal
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
if (!resp.ok) {
|
|
3192
|
+
const err = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
3193
|
+
console.error("[streamAgentChatTurn] Local CLI fetch failed. Status:", resp.status, "Error details:", err);
|
|
3194
|
+
dispatch(removeTransientMessage(messageId));
|
|
3195
|
+
setLoopStopReason("error");
|
|
3196
|
+
remoteTransientMessageFinalized = true;
|
|
3197
|
+
return rejectWithValue(err.error || "CLI \u6267\u884C\u5931\u8D25");
|
|
3198
|
+
}
|
|
3199
|
+
const reader = resp.body?.getReader();
|
|
3200
|
+
if (!reader) {
|
|
3201
|
+
dispatch(removeTransientMessage(messageId));
|
|
3202
|
+
setLoopStopReason("error");
|
|
3203
|
+
remoteTransientMessageFinalized = true;
|
|
3204
|
+
return rejectWithValue("\u65E0\u6CD5\u8BFB\u53D6\u6D41\u5F0F\u54CD\u5E94");
|
|
3205
|
+
}
|
|
3206
|
+
let accumulated = "";
|
|
3207
|
+
let cliCapabilityWarnings = [];
|
|
3208
|
+
const decoder = new TextDecoder();
|
|
3209
|
+
const buildCliAssistantMessage = () => ({
|
|
3210
|
+
id: messageId,
|
|
3211
|
+
dbKey: msgKey,
|
|
3212
|
+
role: "assistant",
|
|
3213
|
+
content: accumulated,
|
|
3214
|
+
...cliMessageMetadata,
|
|
3215
|
+
userId: selectIdentityUserId(getState())
|
|
3216
|
+
});
|
|
3217
|
+
const rejectCliStream = async (message) => {
|
|
3218
|
+
if (accumulated.length > 0) {
|
|
3219
|
+
await persistMessageWithFixedId(dispatch, buildCliAssistantMessage());
|
|
3220
|
+
} else {
|
|
3221
|
+
dispatch(removeTransientMessage(messageId));
|
|
3222
|
+
}
|
|
3223
|
+
setLoopStopReason("error");
|
|
3224
|
+
remoteTransientMessageFinalized = true;
|
|
3225
|
+
return rejectWithValue(message);
|
|
3226
|
+
};
|
|
3227
|
+
const abortCliStream = async () => {
|
|
3228
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "aborted";
|
|
3229
|
+
if (accumulated.length <= 0) {
|
|
3230
|
+
return;
|
|
3231
|
+
}
|
|
3232
|
+
await persistMessageWithFixedId(dispatch, buildCliAssistantMessage());
|
|
3233
|
+
};
|
|
3234
|
+
try {
|
|
3235
|
+
const result = await consumeAgentRunStream({
|
|
3236
|
+
reader,
|
|
3237
|
+
decoder,
|
|
3238
|
+
parseChunk: (raw) => {
|
|
3239
|
+
const parsed = [];
|
|
3240
|
+
for (const line of raw.split("\n")) {
|
|
3241
|
+
if (!line.startsWith("data: ")) continue;
|
|
3242
|
+
try {
|
|
3243
|
+
parsed.push(JSON.parse(line.slice(6)));
|
|
3244
|
+
} catch {
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
return parsed;
|
|
3248
|
+
},
|
|
3249
|
+
isAborted: () => loopController.signal.aborted || thunkApi.signal.aborted,
|
|
3250
|
+
signal: loopController.signal,
|
|
3251
|
+
onAbort: abortCliStream,
|
|
3252
|
+
isDoneEvent: (payload) => payload?.done === true,
|
|
3253
|
+
onPayload: (payload) => {
|
|
3254
|
+
if (payload.error) {
|
|
3255
|
+
return { reject: payload.error };
|
|
3256
|
+
}
|
|
3257
|
+
if (payload.chunk) {
|
|
3258
|
+
accumulated += payload.chunk;
|
|
3259
|
+
dispatch(messageStreaming({
|
|
3260
|
+
id: messageId,
|
|
3261
|
+
dialogId: dialogId2,
|
|
3262
|
+
dbKey: msgKey,
|
|
3263
|
+
content: accumulated,
|
|
3264
|
+
role: "assistant",
|
|
3265
|
+
...cliMessageMetadata
|
|
3266
|
+
}));
|
|
3267
|
+
}
|
|
3268
|
+
if (payload.done && Array.isArray(payload.warnings)) {
|
|
3269
|
+
cliCapabilityWarnings = payload.warnings.filter(
|
|
3270
|
+
(warning) => typeof warning === "string" && warning.trim().length > 0
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
});
|
|
3275
|
+
if (result.outcome === "rejected") {
|
|
3276
|
+
return await rejectCliStream(result.message);
|
|
3277
|
+
}
|
|
3278
|
+
if (result.outcome === "aborted") {
|
|
3279
|
+
return { aborted: true };
|
|
3280
|
+
}
|
|
3281
|
+
if (result.outcome === "streamEnded") {
|
|
3282
|
+
if (!result.sawDone) {
|
|
3283
|
+
dispatch(finalizeTransientMessageOnError({
|
|
3284
|
+
id: messageId,
|
|
3285
|
+
error: "CLI \u6D41\u5F0F\u54CD\u5E94\u88AB\u4E2D\u65AD,\u672A\u6536\u5230\u5B8C\u6210\u4FE1\u53F7"
|
|
3286
|
+
}));
|
|
3287
|
+
remoteTransientMessageFinalized = true;
|
|
3288
|
+
setLoopStopReason("error");
|
|
3289
|
+
return rejectWithValue("CLI \u6D41\u5F0F\u54CD\u5E94\u88AB\u4E2D\u65AD,\u672A\u6536\u5230\u5B8C\u6210\u4FE1\u53F7");
|
|
3290
|
+
}
|
|
3291
|
+
if (cliCapabilityWarnings.length > 0) {
|
|
3292
|
+
accumulated = appendCliCapabilityWarnings(accumulated, cliCapabilityWarnings);
|
|
3293
|
+
dispatch(messageStreaming({
|
|
3294
|
+
id: messageId,
|
|
3295
|
+
dialogId: dialogId2,
|
|
3296
|
+
dbKey: msgKey,
|
|
3297
|
+
content: accumulated,
|
|
3298
|
+
role: "assistant",
|
|
3299
|
+
...cliMessageMetadata
|
|
3300
|
+
}));
|
|
3301
|
+
}
|
|
3302
|
+
await persistMessageWithFixedId(dispatch, buildCliAssistantMessage());
|
|
3303
|
+
remoteTransientMessageFinalized = true;
|
|
3304
|
+
}
|
|
3305
|
+
} finally {
|
|
3306
|
+
try {
|
|
3307
|
+
await reader.cancel();
|
|
3308
|
+
} catch {
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
3313
|
+
const currentDialog = selectDialogConfigByKey(state, explicitDialogKey) ?? selectCurrentDialogConfig(state);
|
|
3314
|
+
const activeDialogKey = currentDialog?.dbKey;
|
|
3315
|
+
const dialogKey = explicitDialogKey || activeDialogKey;
|
|
3316
|
+
if (!dialogKey) {
|
|
3317
|
+
return rejectWithValue("\u5F53\u524D\u5BF9\u8BDD\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u53D1\u9001\u6D88\u606F\u3002");
|
|
3318
|
+
}
|
|
3319
|
+
runtimeDialogKey = dialogKey;
|
|
3320
|
+
const dialogId = extractCustomId(dialogKey);
|
|
3321
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-dialog-resolved", {
|
|
3322
|
+
dialogKey,
|
|
3323
|
+
dialogId
|
|
3324
|
+
});
|
|
3325
|
+
if (shouldUseDesktopLocalRuntime(agentConfig)) {
|
|
3326
|
+
const desktopMessageMetadata = buildMessageMetadata(agentConfig);
|
|
3327
|
+
const w2 = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
3328
|
+
loopKey = `loop:${dialogId}`;
|
|
3329
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
3330
|
+
let currentContent = "";
|
|
3331
|
+
const assistantSegments = [];
|
|
3332
|
+
let assistantMessageKeys = null;
|
|
3333
|
+
let streamResult = null;
|
|
3334
|
+
let streamError = null;
|
|
3335
|
+
let reasoningBuffer = "";
|
|
3336
|
+
const activeToolMessages = /* @__PURE__ */ new Map();
|
|
3337
|
+
const ensureAssistantMessageKeys = () => {
|
|
3338
|
+
if (!assistantMessageKeys) {
|
|
3339
|
+
assistantMessageKeys = createDialogMessageKeyAndId(dialogId);
|
|
3340
|
+
assistantSegments.push({
|
|
3341
|
+
key: assistantMessageKeys.key,
|
|
3342
|
+
messageId: assistantMessageKeys.messageId,
|
|
3343
|
+
content: "",
|
|
3344
|
+
finalized: false,
|
|
3345
|
+
toolCallIds: []
|
|
3346
|
+
});
|
|
3347
|
+
remoteTransientMessageId = assistantMessageKeys.messageId;
|
|
3348
|
+
}
|
|
3349
|
+
return assistantMessageKeys;
|
|
3350
|
+
};
|
|
3351
|
+
const streamDesktopAssistantText = (text) => {
|
|
3352
|
+
currentContent += text;
|
|
3353
|
+
const { key: msgKey2, messageId: messageId2 } = ensureAssistantMessageKeys();
|
|
3354
|
+
const segment2 = assistantSegments[assistantSegments.length - 1];
|
|
3355
|
+
segment2.content = currentContent;
|
|
3356
|
+
dispatch(messageStreaming({
|
|
3357
|
+
id: messageId2,
|
|
3358
|
+
dialogId,
|
|
3359
|
+
dbKey: msgKey2,
|
|
3360
|
+
content: currentContent,
|
|
3361
|
+
role: "assistant",
|
|
3362
|
+
isStreaming: true,
|
|
3363
|
+
...desktopMessageMetadata
|
|
3364
|
+
}));
|
|
3365
|
+
};
|
|
3366
|
+
const finalizeCurrentAssistantSegmentForTool = () => {
|
|
3367
|
+
const segment2 = assistantSegments[assistantSegments.length - 1];
|
|
3368
|
+
if (segment2) {
|
|
3369
|
+
segment2.content = currentContent;
|
|
3370
|
+
segment2.finalized = true;
|
|
3371
|
+
}
|
|
3372
|
+
if (assistantMessageKeys && currentContent.length > 0) {
|
|
3373
|
+
dispatch(messageStreaming({
|
|
3374
|
+
id: assistantMessageKeys.messageId,
|
|
3375
|
+
dialogId,
|
|
3376
|
+
dbKey: assistantMessageKeys.key,
|
|
3377
|
+
content: currentContent,
|
|
3378
|
+
role: "assistant",
|
|
3379
|
+
isStreaming: false,
|
|
3380
|
+
...desktopMessageMetadata
|
|
3381
|
+
}));
|
|
3382
|
+
}
|
|
3383
|
+
assistantMessageKeys = null;
|
|
3384
|
+
currentContent = "";
|
|
3385
|
+
};
|
|
3386
|
+
try {
|
|
3387
|
+
const desktopAgentRef = agentConfig.dbKey || agentKey;
|
|
3388
|
+
const eventStream = runDesktopAgentRuntimeTurnStream({
|
|
3389
|
+
agentRef: desktopAgentRef,
|
|
3390
|
+
input: userInput,
|
|
3391
|
+
continueDialogId: dialogId,
|
|
3392
|
+
dialogKey,
|
|
3393
|
+
cwd: runtimeOptions?.cwd,
|
|
3394
|
+
restrictShellToWorkspace: runtimeOptions?.restrictShellToWorkspace === true,
|
|
3395
|
+
workspaceToolsHint: runtimeOptions?.workspaceToolsHint === true,
|
|
3396
|
+
agentConfigSnapshot: agentConfig,
|
|
3397
|
+
dialogMessages: selectAllMsgs(getState(), dialogId),
|
|
3398
|
+
signal: loopController.signal
|
|
3399
|
+
});
|
|
3400
|
+
for await (const event of eventStream) {
|
|
3401
|
+
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
3402
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "aborted";
|
|
3403
|
+
break;
|
|
3404
|
+
}
|
|
3405
|
+
if (event.type === "delta") {
|
|
3406
|
+
streamDesktopAssistantText(event.text);
|
|
3407
|
+
} else if (event.type === "thinking") {
|
|
3408
|
+
if (typeof event.content === "string") {
|
|
3409
|
+
reasoningBuffer += event.content;
|
|
3410
|
+
}
|
|
3411
|
+
} else if (event.type === "tool") {
|
|
3412
|
+
const toolEvent = event.event;
|
|
3413
|
+
const callId = toolEvent.toolCallId;
|
|
3414
|
+
if (!callId) continue;
|
|
3415
|
+
if (toolEvent.type === "tool-call") {
|
|
3416
|
+
attachToolCallIdToSegment(assistantSegments, callId);
|
|
3417
|
+
finalizeCurrentAssistantSegmentForTool();
|
|
3418
|
+
const { key: dbKey, messageId: toolMsgId } = createDialogMessageKeyAndId(dialogId);
|
|
3419
|
+
const toolMsg = {
|
|
3420
|
+
id: toolMsgId,
|
|
3421
|
+
dialogId,
|
|
3422
|
+
dbKey,
|
|
3423
|
+
role: "tool",
|
|
3424
|
+
content: "",
|
|
3425
|
+
isStreaming: true,
|
|
3426
|
+
toolName: toolEvent.toolName,
|
|
3427
|
+
toolCallId: callId,
|
|
3428
|
+
...toolEvent.argumentsPreview ? {
|
|
3429
|
+
toolPayload: {
|
|
3430
|
+
input: {
|
|
3431
|
+
command: toolEvent.argumentsPreview,
|
|
3432
|
+
cmd: toolEvent.argumentsPreview
|
|
3433
|
+
}
|
|
3434
|
+
},
|
|
3435
|
+
metadata: {
|
|
3436
|
+
argumentsPreview: toolEvent.argumentsPreview
|
|
3437
|
+
}
|
|
3438
|
+
} : {}
|
|
3439
|
+
};
|
|
3440
|
+
activeToolMessages.set(callId, toolMsg);
|
|
3441
|
+
dispatch(messageStreaming(toolMsg));
|
|
3442
|
+
} else if (toolEvent.type === "tool-result" || toolEvent.type === "tool-error") {
|
|
3443
|
+
const existing = activeToolMessages.get(callId);
|
|
3444
|
+
if (existing) {
|
|
3445
|
+
const isError = toolEvent.type === "tool-error";
|
|
3446
|
+
const existingMeta = asRecordOrEmpty(existing.metadata);
|
|
3447
|
+
const existingInput = isRecord(existing.toolPayload?.input) ? existing.toolPayload.input : {};
|
|
3448
|
+
const mergedMeta = {
|
|
3449
|
+
...existingMeta,
|
|
3450
|
+
...toolEvent.metadata,
|
|
3451
|
+
...isError ? { error: true, message: toolEvent.message } : {}
|
|
3452
|
+
};
|
|
3453
|
+
const toolResultMsg = {
|
|
3454
|
+
...existing,
|
|
3455
|
+
isStreaming: false,
|
|
3456
|
+
content: projectDesktopToolUiContent({
|
|
3457
|
+
toolName: toolEvent.toolName || existing.toolName,
|
|
3458
|
+
content: toolEvent.content,
|
|
3459
|
+
summary: toolEvent.summary,
|
|
3460
|
+
message: toolEvent.message,
|
|
3461
|
+
metadata: mergedMeta,
|
|
3462
|
+
argumentsPreview: asOptionalTrimmedString(mergedMeta.argumentsPreview) || asOptionalTrimmedString(existingInput.command) || asOptionalTrimmedString(existingInput.cmd) || void 0
|
|
3463
|
+
}),
|
|
3464
|
+
metadata: mergedMeta
|
|
3465
|
+
};
|
|
3466
|
+
activeToolMessages.set(callId, toolResultMsg);
|
|
3467
|
+
dispatch(messageStreaming(toolResultMsg));
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
} else if (event.type === "done") {
|
|
3471
|
+
streamResult = event.result;
|
|
3472
|
+
} else if (event.type === "error") {
|
|
3473
|
+
streamError = event.error;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
} catch (err) {
|
|
3477
|
+
if (isAbortError(err) || loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
3478
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "aborted";
|
|
3479
|
+
} else {
|
|
3480
|
+
streamError = err?.message || "Local turn read stream error";
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
if (loopController.signal.aborted || thunkApi.signal.aborted || typeof streamError === "string" && /operation was aborted/i.test(streamError)) {
|
|
3484
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "aborted";
|
|
3485
|
+
const stopKeys = assistantMessageKeys;
|
|
3486
|
+
if (stopKeys) {
|
|
3487
|
+
const last = assistantSegments[assistantSegments.length - 1];
|
|
3488
|
+
if (last && last.messageId === stopKeys.messageId) {
|
|
3489
|
+
last.content = currentContent;
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
const stopToolNameById = /* @__PURE__ */ new Map();
|
|
3493
|
+
for (const [callId, toolMsg] of activeToolMessages) {
|
|
3494
|
+
if (!toolMessageWillPersist(toolMsg)) continue;
|
|
3495
|
+
const toolName = toolMsg?.toolName;
|
|
3496
|
+
stopToolNameById.set(
|
|
3497
|
+
callId,
|
|
3498
|
+
typeof toolName === "string" && toolName ? toolName : "tool"
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
for (const segment2 of assistantSegments) {
|
|
3502
|
+
const hasContent = segment2.content.trim().length > 0;
|
|
3503
|
+
const hasToolCalls = segment2.toolCallIds && segment2.toolCallIds.length > 0;
|
|
3504
|
+
if (hasContent || hasToolCalls) {
|
|
3505
|
+
const stopToolCalls = buildMinimalToolCallsFromIds(
|
|
3506
|
+
segment2.toolCallIds,
|
|
3507
|
+
stopToolNameById
|
|
3508
|
+
);
|
|
3509
|
+
await persistMessageWithFixedId(dispatch, {
|
|
3510
|
+
id: segment2.messageId,
|
|
3511
|
+
dbKey: segment2.key,
|
|
3512
|
+
role: "assistant",
|
|
3513
|
+
content: segment2.content,
|
|
3514
|
+
...stopToolCalls.length > 0 ? { tool_calls: stopToolCalls } : {},
|
|
3515
|
+
...desktopMessageMetadata,
|
|
3516
|
+
userId: selectIdentityUserId(getState())
|
|
3517
|
+
});
|
|
3518
|
+
} else {
|
|
3519
|
+
dispatch(removeTransientMessage(segment2.messageId));
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
const durableTools = [];
|
|
3523
|
+
for (const toolMsg of activeToolMessages.values()) {
|
|
3524
|
+
const content = toolMsg?.content;
|
|
3525
|
+
const hasContent = typeof content === "string" ? content.trim().length > 0 : Array.isArray(content) && content.length > 0;
|
|
3526
|
+
if (!hasContent) {
|
|
3527
|
+
dispatch(removeTransientMessage(toolMsg.id));
|
|
3528
|
+
continue;
|
|
3529
|
+
}
|
|
3530
|
+
const stopped = { ...toolMsg, isStreaming: false };
|
|
3531
|
+
dispatch(messageStreaming(stopped));
|
|
3532
|
+
durableTools.push(stopped);
|
|
3533
|
+
}
|
|
3534
|
+
await persistToolMessages(dispatch, durableTools, {
|
|
3535
|
+
isStreaming: false,
|
|
3536
|
+
soft: true
|
|
3537
|
+
});
|
|
3538
|
+
remoteTransientMessageFinalized = true;
|
|
3539
|
+
return { aborted: true };
|
|
3540
|
+
}
|
|
3541
|
+
const finalizeDesktopTurnOnError = async (errorText) => {
|
|
3542
|
+
const errorToolNameById = /* @__PURE__ */ new Map();
|
|
3543
|
+
for (const [callId, toolMsg] of activeToolMessages) {
|
|
3544
|
+
if (!toolMessageWillPersist(toolMsg)) continue;
|
|
3545
|
+
const toolName = toolMsg?.toolName;
|
|
3546
|
+
errorToolNameById.set(
|
|
3547
|
+
callId,
|
|
3548
|
+
typeof toolName === "string" && toolName ? toolName : "tool"
|
|
3549
|
+
);
|
|
3550
|
+
}
|
|
3551
|
+
for (const segment2 of assistantSegments) {
|
|
3552
|
+
const hasContent = segment2.content.trim().length > 0;
|
|
3553
|
+
const hasToolCalls = segment2.toolCallIds && segment2.toolCallIds.length > 0;
|
|
3554
|
+
if (hasContent || hasToolCalls) {
|
|
3555
|
+
const errorToolCalls = buildMinimalToolCallsFromIds(
|
|
3556
|
+
segment2.toolCallIds,
|
|
3557
|
+
errorToolNameById
|
|
3558
|
+
);
|
|
3559
|
+
await persistMessageWithFixedId(dispatch, {
|
|
3560
|
+
id: segment2.messageId,
|
|
3561
|
+
dbKey: segment2.key,
|
|
3562
|
+
role: "assistant",
|
|
3563
|
+
content: segment2.content,
|
|
3564
|
+
...errorToolCalls.length > 0 ? { tool_calls: errorToolCalls } : {},
|
|
3565
|
+
...desktopMessageMetadata,
|
|
3566
|
+
userId: selectIdentityUserId(getState())
|
|
3567
|
+
});
|
|
3568
|
+
if (hasContent) {
|
|
3569
|
+
dispatch(finalizeTransientMessageOnError({
|
|
3570
|
+
id: segment2.messageId,
|
|
3571
|
+
error: errorText
|
|
3572
|
+
}));
|
|
3573
|
+
}
|
|
3574
|
+
} else {
|
|
3575
|
+
dispatch(finalizeTransientMessageOnError({
|
|
3576
|
+
id: segment2.messageId,
|
|
3577
|
+
error: errorText
|
|
3578
|
+
}));
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
const durableTools = [];
|
|
3582
|
+
for (const toolMsg of activeToolMessages.values()) {
|
|
3583
|
+
const content = toolMsg?.content;
|
|
3584
|
+
const hasContent = typeof content === "string" ? content.trim().length > 0 : Array.isArray(content) && content.length > 0;
|
|
3585
|
+
if (!hasContent) {
|
|
3586
|
+
dispatch(removeTransientMessage(toolMsg.id));
|
|
3587
|
+
continue;
|
|
3588
|
+
}
|
|
3589
|
+
const stopped = { ...toolMsg, isStreaming: false };
|
|
3590
|
+
dispatch(messageStreaming(stopped));
|
|
3591
|
+
durableTools.push(stopped);
|
|
3592
|
+
}
|
|
3593
|
+
await persistToolMessages(dispatch, durableTools, {
|
|
3594
|
+
isStreaming: false,
|
|
3595
|
+
soft: true
|
|
3596
|
+
});
|
|
3597
|
+
remoteTransientMessageFinalized = true;
|
|
3598
|
+
setLoopStopReason("error");
|
|
3599
|
+
};
|
|
3600
|
+
if (streamError) {
|
|
3601
|
+
await finalizeDesktopTurnOnError(streamError);
|
|
3602
|
+
return rejectWithValue(streamError);
|
|
3603
|
+
}
|
|
3604
|
+
if (!streamResult) {
|
|
3605
|
+
const message = "Local turn stream closed unexpectedly without result";
|
|
3606
|
+
await finalizeDesktopTurnOnError(message);
|
|
3607
|
+
return rejectWithValue(message);
|
|
3608
|
+
}
|
|
3609
|
+
const desktopTurnMessages = streamResult.turnMessages || [];
|
|
3610
|
+
const hasSegmentWithCallIds = assistantSegments.some(
|
|
3611
|
+
(s) => s.toolCallIds.length > 0
|
|
3612
|
+
);
|
|
3613
|
+
if (!hasSegmentWithCallIds) {
|
|
3614
|
+
const doneOnlyAssistantCalls = desktopTurnMessages.filter(
|
|
3615
|
+
(m) => m?.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0
|
|
3616
|
+
).flatMap((m) => m.tool_calls.map((tc) => tc?.id)).filter((id) => typeof id === "string");
|
|
3617
|
+
if (doneOnlyAssistantCalls.length > 0) {
|
|
3618
|
+
ensureAssistantMessageKeys();
|
|
3619
|
+
for (const callId of doneOnlyAssistantCalls) {
|
|
3620
|
+
attachToolCallIdToSegment(assistantSegments, callId);
|
|
3621
|
+
}
|
|
3622
|
+
if (currentContent.length === 0) {
|
|
3623
|
+
finalizeCurrentAssistantSegmentForTool();
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
if (activeToolMessages.size === 0) {
|
|
3628
|
+
for (const toolMessage of buildDesktopRuntimeToolMessagesForUi({
|
|
3629
|
+
dialogId,
|
|
3630
|
+
turnMessages: desktopTurnMessages
|
|
3631
|
+
})) {
|
|
3632
|
+
dispatch(messageStreaming(toolMessage));
|
|
3633
|
+
activeToolMessages.set(
|
|
3634
|
+
asTrimmedString(toolMessage.toolCallId) || toolMessage.id,
|
|
3635
|
+
toolMessage
|
|
3636
|
+
);
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
await persistToolMessages(
|
|
3640
|
+
dispatch,
|
|
3641
|
+
activeToolMessages.values(),
|
|
3642
|
+
{ isStreaming: false, soft: true }
|
|
3643
|
+
);
|
|
3644
|
+
const earlierFinalizedSegments = selectPersistableFinalizedSegments(assistantSegments);
|
|
3645
|
+
for (const segment2 of earlierFinalizedSegments) {
|
|
3646
|
+
const segmentToolCalls = resolveSegmentToolCalls(
|
|
3647
|
+
segment2.toolCallIds,
|
|
3648
|
+
desktopTurnMessages
|
|
3649
|
+
);
|
|
3650
|
+
dispatch(write({
|
|
3651
|
+
data: {
|
|
3652
|
+
id: segment2.messageId,
|
|
3653
|
+
dbKey: segment2.key,
|
|
3654
|
+
dialogId,
|
|
3655
|
+
content: segment2.content,
|
|
3656
|
+
role: "assistant",
|
|
3657
|
+
isStreaming: false,
|
|
3658
|
+
type: "msg" /* MSG */,
|
|
3659
|
+
...segmentToolCalls.length > 0 ? { tool_calls: segmentToolCalls } : {},
|
|
3660
|
+
...desktopMessageMetadata
|
|
3661
|
+
},
|
|
3662
|
+
customKey: segment2.key
|
|
3663
|
+
}));
|
|
3664
|
+
}
|
|
3665
|
+
const { key: msgKey, messageId } = ensureAssistantMessageKeys();
|
|
3666
|
+
const lastSegmentContent = hasInlineExecutedToolCalls(streamResult) ? currentContent || "" : streamResult.content || currentContent || "";
|
|
3667
|
+
const segment = assistantSegments[assistantSegments.length - 1];
|
|
3668
|
+
segment.content = lastSegmentContent;
|
|
3669
|
+
dispatch(messageStreaming({
|
|
3670
|
+
id: messageId,
|
|
3671
|
+
dialogId,
|
|
3672
|
+
dbKey: msgKey,
|
|
3673
|
+
content: lastSegmentContent,
|
|
3674
|
+
role: "assistant",
|
|
3675
|
+
isStreaming: false,
|
|
3676
|
+
...desktopMessageMetadata
|
|
3677
|
+
}));
|
|
3678
|
+
const lastSegmentToolCalls = segment ? resolveSegmentToolCalls(segment.toolCallIds, desktopTurnMessages) : [];
|
|
3679
|
+
await dispatch(messageStreamEnd({
|
|
3680
|
+
finalContentBuffer: [
|
|
3681
|
+
{
|
|
3682
|
+
type: "text",
|
|
3683
|
+
text: lastSegmentContent
|
|
3684
|
+
}
|
|
3685
|
+
],
|
|
3686
|
+
totalUsage: streamResult.usage ?? void 0,
|
|
3687
|
+
messageId,
|
|
3688
|
+
msgKey,
|
|
3689
|
+
agentConfig,
|
|
3690
|
+
dialogId,
|
|
3691
|
+
dialogKey,
|
|
3692
|
+
// 优先用本 turn 累计的 thinking SSE;为空时兜底 streamResult.reasoning_content
|
|
3693
|
+
// (provider 在 SSE 流内累计返回的 reasoning_content)。
|
|
3694
|
+
reasoningBuffer: reasoningBuffer || (typeof streamResult?.reasoning_content === "string" ? streamResult.reasoning_content : ""),
|
|
3695
|
+
toolCalls: lastSegmentToolCalls,
|
|
3696
|
+
// 把 provider 报告的 finish_reason 透传给 messageStreamEnd,
|
|
3697
|
+
// 由 assembleFinalAssistantMessage 决定是否写进最终 Message。
|
|
3698
|
+
finishReason: streamResult.finish_reason ?? void 0,
|
|
3699
|
+
messageMetadata: desktopMessageMetadata
|
|
3700
|
+
})).unwrap();
|
|
3701
|
+
remoteTransientMessageFinalized = true;
|
|
3702
|
+
return {
|
|
3703
|
+
usage: streamResult.usage ?? void 0
|
|
3704
|
+
};
|
|
3705
|
+
}
|
|
3706
|
+
const userInputText = extractAgentRunUserText(userInput);
|
|
3707
|
+
const explicitServerBase = asOptionalTrimmedString(args.serverBase) ?? null;
|
|
3708
|
+
const currentServer = selectCurrentServer(state);
|
|
3709
|
+
const normalizedRequestedServerBase = explicitServerBase && normalizeServerOrigin(explicitServerBase);
|
|
3710
|
+
const normalizedCurrentServer = normalizeServerOrigin(
|
|
3711
|
+
currentServer
|
|
3712
|
+
);
|
|
3713
|
+
const canProxyToExplicitServerBase = !Array.isArray(userInput) && !runtimeOptions?.extraTools?.length && !runtimeOptions?.editingTarget && !runtimeOptions?.imageConfigOverride;
|
|
3714
|
+
if (explicitServerBase && canProxyToExplicitServerBase) {
|
|
3715
|
+
if (normalizedRequestedServerBase && normalizedCurrentServer && normalizedRequestedServerBase === normalizedCurrentServer) {
|
|
3716
|
+
} else {
|
|
3717
|
+
const token = selectIdentityToken(state);
|
|
3718
|
+
const authHeader = token ? `Bearer ${token}` : "";
|
|
3719
|
+
const rawMessages = selectAllMsgs(state, dialogId);
|
|
3720
|
+
const visibleMessages = buildAgentViewMessages(
|
|
3721
|
+
rawMessages,
|
|
3722
|
+
agentConfig.dbKey
|
|
3723
|
+
);
|
|
3724
|
+
const cleanedMessages = filterAndCleanMessages(visibleMessages);
|
|
3725
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
3726
|
+
remoteTransientMessageId = messageId;
|
|
3727
|
+
const remoteMessageMetadata = buildMessageMetadata(agentConfig);
|
|
3728
|
+
let accumulated = "";
|
|
3729
|
+
let totalTurnUsage2 = void 0;
|
|
3730
|
+
const buildRemoteAssistantMessage = () => ({
|
|
3731
|
+
id: messageId,
|
|
3732
|
+
dbKey: msgKey,
|
|
3733
|
+
role: "assistant",
|
|
3734
|
+
content: accumulated,
|
|
3735
|
+
...remoteMessageMetadata,
|
|
3736
|
+
userId: selectIdentityUserId(getState())
|
|
3737
|
+
});
|
|
3738
|
+
loopKey = `loop:${dialogId}`;
|
|
3739
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
3740
|
+
dispatch(messageStreaming({
|
|
3741
|
+
id: messageId,
|
|
3742
|
+
dialogId,
|
|
3743
|
+
dbKey: msgKey,
|
|
3744
|
+
content: "",
|
|
3745
|
+
role: "assistant",
|
|
3746
|
+
...remoteMessageMetadata
|
|
3747
|
+
}));
|
|
3748
|
+
const rejectRemoteStream = async (message) => {
|
|
3749
|
+
if (accumulated.length > 0) {
|
|
3750
|
+
await persistMessageWithFixedId(dispatch, buildRemoteAssistantMessage());
|
|
3751
|
+
} else {
|
|
3752
|
+
dispatch(removeTransientMessage(messageId));
|
|
3753
|
+
}
|
|
3754
|
+
setLoopStopReason("error");
|
|
3755
|
+
remoteTransientMessageFinalized = true;
|
|
3756
|
+
return rejectWithValue(message);
|
|
3757
|
+
};
|
|
3758
|
+
const remoteRequestBody = JSON.stringify({
|
|
3759
|
+
agentKey,
|
|
3760
|
+
userInput: agentRunUserInput,
|
|
3761
|
+
messages: cleanedMessages,
|
|
3762
|
+
stream: true,
|
|
3763
|
+
persistDialog: false,
|
|
3764
|
+
clientDialogId: dialogId,
|
|
3765
|
+
runtimeContext: {
|
|
3766
|
+
surface: "web",
|
|
3767
|
+
host: "browser",
|
|
3768
|
+
runtime: "react",
|
|
3769
|
+
entrypoint: "chat-dialog",
|
|
3770
|
+
capabilities: ["streaming", "dialog-ui", "tool-cards"]
|
|
3771
|
+
},
|
|
3772
|
+
...currentDialog?.spaceId ? { spaceId: currentDialog.spaceId } : {}
|
|
3773
|
+
});
|
|
3774
|
+
const remoteRunUrl = `${explicitServerBase.replace(/\/+$/, "")}/api/agent/run`;
|
|
3775
|
+
const remoteResponse = await performServerProxyFetchWithRetry({
|
|
3776
|
+
execute: () => fetch(remoteRunUrl, {
|
|
3777
|
+
method: "POST",
|
|
3778
|
+
headers: {
|
|
3779
|
+
"Content-Type": "application/json",
|
|
3780
|
+
Accept: "text/event-stream",
|
|
3781
|
+
...authHeader ? { Authorization: authHeader } : {}
|
|
3782
|
+
},
|
|
3783
|
+
body: remoteRequestBody,
|
|
3784
|
+
signal: loopController.signal
|
|
3785
|
+
}),
|
|
3786
|
+
signal: loopController.signal,
|
|
3787
|
+
logPrefix: "[streamAgentChatTurn.remoteRun]"
|
|
3788
|
+
});
|
|
3789
|
+
if (!remoteResponse.ok) {
|
|
3790
|
+
const errorText = await remoteResponse.text();
|
|
3791
|
+
return await rejectRemoteStream(
|
|
3792
|
+
errorText || `Remote agent run failed (${remoteResponse.status})`
|
|
3793
|
+
);
|
|
3794
|
+
}
|
|
3795
|
+
const reader = remoteResponse.body?.getReader();
|
|
3796
|
+
if (!reader) {
|
|
3797
|
+
return await rejectRemoteStream("\u65E0\u6CD5\u8BFB\u53D6\u8FDC\u7AEF\u6D41\u5F0F\u54CD\u5E94");
|
|
3798
|
+
}
|
|
3799
|
+
const decoder = new TextDecoder();
|
|
3800
|
+
const parseSSE = createSSEParser();
|
|
3801
|
+
const abortRemoteStream = async () => {
|
|
3802
|
+
if (accumulated.length <= 0) return;
|
|
3803
|
+
await persistMessageWithFixedId(dispatch, buildRemoteAssistantMessage());
|
|
3804
|
+
};
|
|
3805
|
+
try {
|
|
3806
|
+
const result = await consumeAgentRunStream({
|
|
3807
|
+
reader,
|
|
3808
|
+
decoder,
|
|
3809
|
+
parseChunk: (raw) => parseSSE(raw),
|
|
3810
|
+
isAborted: () => loopController.signal.aborted || thunkApi.signal.aborted,
|
|
3811
|
+
signal: loopController.signal,
|
|
3812
|
+
onAbort: abortRemoteStream,
|
|
3813
|
+
isDoneEvent: (payload) => payload?.type === "done",
|
|
3814
|
+
onPayload: async (payload) => {
|
|
3815
|
+
if (payload.type === "error") {
|
|
3816
|
+
return { reject: payload.message || "\u8FDC\u7AEF Agent \u6267\u884C\u5931\u8D25" };
|
|
3817
|
+
}
|
|
3818
|
+
if (payload.type === "agent_handoff") {
|
|
3819
|
+
await patchDialogThreadMetadata(
|
|
3820
|
+
dispatch,
|
|
3821
|
+
dialogKey,
|
|
3822
|
+
payload.threadMetadata
|
|
3823
|
+
);
|
|
3824
|
+
await patchDialogActiveAgent(
|
|
3825
|
+
dispatch,
|
|
3826
|
+
dialogKey,
|
|
3827
|
+
payload.agentKey
|
|
3828
|
+
);
|
|
3829
|
+
}
|
|
3830
|
+
if (payload.type === "text" && typeof payload.content === "string") {
|
|
3831
|
+
accumulated += payload.content;
|
|
3832
|
+
dispatch(messageStreaming({
|
|
3833
|
+
id: messageId,
|
|
3834
|
+
dialogId,
|
|
3835
|
+
dbKey: msgKey,
|
|
3836
|
+
content: accumulated,
|
|
3837
|
+
role: "assistant",
|
|
3838
|
+
...remoteMessageMetadata
|
|
3839
|
+
}));
|
|
3840
|
+
}
|
|
3841
|
+
if (payload.type === "done") {
|
|
3842
|
+
totalTurnUsage2 = payload.usage;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
});
|
|
3846
|
+
if (result.outcome === "rejected") {
|
|
3847
|
+
return await rejectRemoteStream(result.message);
|
|
3848
|
+
}
|
|
3849
|
+
if (result.outcome === "aborted") {
|
|
3850
|
+
return { aborted: true };
|
|
3851
|
+
}
|
|
3852
|
+
if (result.outcome === "streamEnded") {
|
|
3853
|
+
if (!result.sawDone) {
|
|
3854
|
+
dispatch(finalizeTransientMessageOnError({
|
|
3855
|
+
id: messageId,
|
|
3856
|
+
error: "\u8FDC\u7AEF Agent \u6D41\u5F0F\u54CD\u5E94\u88AB\u4E2D\u65AD,\u672A\u6536\u5230\u5B8C\u6210\u4FE1\u53F7"
|
|
3857
|
+
}));
|
|
3858
|
+
remoteTransientMessageFinalized = true;
|
|
3859
|
+
setLoopStopReason("error");
|
|
3860
|
+
return rejectWithValue(
|
|
3861
|
+
"\u8FDC\u7AEF Agent \u6D41\u5F0F\u54CD\u5E94\u88AB\u4E2D\u65AD,\u672A\u6536\u5230\u5B8C\u6210\u4FE1\u53F7"
|
|
3862
|
+
);
|
|
3863
|
+
}
|
|
3864
|
+
await persistMessageWithFixedId(dispatch, buildRemoteAssistantMessage());
|
|
3865
|
+
remoteTransientMessageFinalized = true;
|
|
3866
|
+
}
|
|
3867
|
+
} finally {
|
|
3868
|
+
try {
|
|
3869
|
+
await reader.cancel();
|
|
3870
|
+
} catch {
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
return {
|
|
3874
|
+
usage: totalTurnUsage2 ?? void 0
|
|
3875
|
+
};
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
let extractedMentions;
|
|
3879
|
+
if (Array.isArray(userInput)) {
|
|
3880
|
+
extractedMentions = extractCategorizedMentions(userInput);
|
|
3881
|
+
}
|
|
3882
|
+
const mentionedTools = extractedMentions?.tools ?? [];
|
|
3883
|
+
const {
|
|
3884
|
+
references: normalizedReferences,
|
|
3885
|
+
contentByKey: referenceContentCache,
|
|
3886
|
+
referencedTools: referenceTools,
|
|
3887
|
+
recommendedSkillTools: referenceRecommendedSkillTools,
|
|
3888
|
+
recommendedSkillHints: referenceRecommendedSkillHints,
|
|
3889
|
+
skillPromptPatches: referenceSkillPromptPatches
|
|
3890
|
+
} = await resolveReferenceAssets(
|
|
3891
|
+
mergeReferences(agentConfig.references, (selectDialogConfigByKey(getState(), explicitDialogKey) ?? selectCurrentDialogConfig(getState()))?.extraReferences),
|
|
3892
|
+
dispatch
|
|
3893
|
+
);
|
|
3894
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-references-resolved", {
|
|
3895
|
+
referenceCount: normalizedReferences?.length ?? 0,
|
|
3896
|
+
referencedToolCount: referenceTools?.length ?? 0
|
|
3897
|
+
});
|
|
3898
|
+
const agentConfigWithReferences = {
|
|
3899
|
+
...agentConfig,
|
|
3900
|
+
references: normalizedReferences,
|
|
3901
|
+
referencedTools: referenceTools,
|
|
3902
|
+
recommendedSkillTools: referenceRecommendedSkillTools,
|
|
3903
|
+
recommendedSkillHints: referenceRecommendedSkillHints,
|
|
3904
|
+
skillPromptPatches: referenceSkillPromptPatches
|
|
3905
|
+
};
|
|
3906
|
+
const initialRawMsgs = selectAllMsgs(state, dialogId);
|
|
3907
|
+
const initialHistoryIds = new Set(initialRawMsgs.map((m) => m.id));
|
|
3908
|
+
const keySets = await getFullChatContextKeys(
|
|
3909
|
+
state,
|
|
3910
|
+
dispatch,
|
|
3911
|
+
agentConfigWithReferences,
|
|
3912
|
+
userInput,
|
|
3913
|
+
currentDialog ?? void 0
|
|
3914
|
+
);
|
|
3915
|
+
const finalKeys = deduplicateContextKeys(keySets);
|
|
3916
|
+
const allContextKeys = /* @__PURE__ */ new Set([
|
|
3917
|
+
...finalKeys.botInstructionsContext,
|
|
3918
|
+
...finalKeys.currentInputContext,
|
|
3919
|
+
...finalKeys.historyContext,
|
|
3920
|
+
...finalKeys.botKnowledgeContext
|
|
3921
|
+
]);
|
|
3922
|
+
const {
|
|
3923
|
+
tools: contextTools,
|
|
3924
|
+
contentByKey: contextContentCache,
|
|
3925
|
+
recommendedSkillTools: contextRecommendedSkillTools = [],
|
|
3926
|
+
recommendedSkillHints: contextRecommendedSkillHints = [],
|
|
3927
|
+
skillPromptPatches: contextSkillPromptPatches = []
|
|
3928
|
+
} = await resolveToolsFromKeys(
|
|
3929
|
+
Array.from(allContextKeys),
|
|
3930
|
+
dispatch,
|
|
3931
|
+
referenceContentCache
|
|
3932
|
+
);
|
|
3933
|
+
const mergedContentCache = new Map([
|
|
3934
|
+
...referenceContentCache,
|
|
3935
|
+
...contextContentCache
|
|
3936
|
+
]);
|
|
3937
|
+
const agentConfigWithTools = mergeAgentToolsWithRuntime(
|
|
3938
|
+
{
|
|
3939
|
+
...agentConfigWithReferences,
|
|
3940
|
+
recommendedSkillTools: [
|
|
3941
|
+
...agentConfigWithReferences.recommendedSkillTools ?? [],
|
|
3942
|
+
...contextRecommendedSkillTools
|
|
3943
|
+
],
|
|
3944
|
+
recommendedSkillHints: [
|
|
3945
|
+
...agentConfigWithReferences.recommendedSkillHints ?? [],
|
|
3946
|
+
...contextRecommendedSkillHints
|
|
3947
|
+
],
|
|
3948
|
+
skillPromptPatches: [
|
|
3949
|
+
...agentConfigWithReferences.skillPromptPatches ?? [],
|
|
3950
|
+
...contextSkillPromptPatches
|
|
3951
|
+
]
|
|
3952
|
+
},
|
|
3953
|
+
contextTools,
|
|
3954
|
+
mentionedTools,
|
|
3955
|
+
runtimeOptions,
|
|
3956
|
+
state
|
|
3957
|
+
);
|
|
3958
|
+
const agentConfigForCall = applyImageConfigRuntimeOverride(
|
|
3959
|
+
agentConfigWithTools,
|
|
3960
|
+
runtimeOptions
|
|
3961
|
+
);
|
|
3962
|
+
const effectiveAgentConfig = agentConfigForCall;
|
|
3963
|
+
const initialImageGenerationState = resolveImageGenerationStreamingState(
|
|
3964
|
+
effectiveAgentConfig
|
|
3965
|
+
);
|
|
3966
|
+
const streamingMessageMetadata = {
|
|
3967
|
+
...buildMessageMetadata(agentConfigForCall),
|
|
3968
|
+
...initialImageGenerationState ? { imageGenerationState: initialImageGenerationState } : {}
|
|
3969
|
+
};
|
|
3970
|
+
const isRespModel = resolveClientWire(
|
|
3971
|
+
resolveAgentCallPlan(agentConfigForCall, {})
|
|
3972
|
+
) === "responses";
|
|
3973
|
+
if (isRespModel) {
|
|
3974
|
+
const maxExecutionTime2 = selectMaxExecutionTime(state);
|
|
3975
|
+
const MAX_TIME_MS2 = maxExecutionTime2 > 0 ? maxExecutionTime2 : 24e4;
|
|
3976
|
+
const startTime2 = Date.now();
|
|
3977
|
+
const staticContexts2 = await buildStaticContextsWithToolsPrewarm(
|
|
3978
|
+
state,
|
|
3979
|
+
dispatch,
|
|
3980
|
+
agentConfigForCall,
|
|
3981
|
+
currentDialog ?? void 0,
|
|
3982
|
+
mergedContentCache
|
|
3983
|
+
);
|
|
3984
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-static-context-ready", {
|
|
3985
|
+
model: agentConfigForCall.model,
|
|
3986
|
+
responseApi: true
|
|
3987
|
+
});
|
|
3988
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-post-static-entered", {
|
|
3989
|
+
responseApi: true
|
|
3990
|
+
});
|
|
3991
|
+
let appendTempUserInput2 = true;
|
|
3992
|
+
let currentParentMessageId2 = parentMessageId ?? void 0;
|
|
3993
|
+
const w2 = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
3994
|
+
if (w2) w2.__LOOP_STOP_REASON__ = null;
|
|
3995
|
+
loopKey = `loop:${dialogId}`;
|
|
3996
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-active-controller-adding", {
|
|
3997
|
+
responseApi: true
|
|
3998
|
+
});
|
|
3999
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
4000
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-active-controller-added", {
|
|
4001
|
+
responseApi: true
|
|
4002
|
+
});
|
|
4003
|
+
for (; ; ) {
|
|
4004
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-loop-entered", {
|
|
4005
|
+
responseApi: true
|
|
4006
|
+
});
|
|
4007
|
+
const requestParentMessageId = currentParentMessageId2;
|
|
4008
|
+
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
4009
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-loop-aborted-before-context", {
|
|
4010
|
+
loopControllerAborted: loopController.signal.aborted,
|
|
4011
|
+
thunkSignalAborted: thunkApi.signal.aborted,
|
|
4012
|
+
responseApi: true
|
|
4013
|
+
});
|
|
4014
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "aborted";
|
|
4015
|
+
return { aborted: true };
|
|
4016
|
+
}
|
|
4017
|
+
const loopState = getState();
|
|
4018
|
+
const now = Date.now();
|
|
4019
|
+
if (now - startTime2 > MAX_TIME_MS2) {
|
|
4020
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-loop-timeout-before-context", {
|
|
4021
|
+
maxTimeMs: MAX_TIME_MS2,
|
|
4022
|
+
elapsedMs: now - startTime2,
|
|
4023
|
+
responseApi: true
|
|
4024
|
+
});
|
|
4025
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "timeout";
|
|
4026
|
+
break;
|
|
4027
|
+
}
|
|
4028
|
+
const accessError = validateAccessAndBalance(
|
|
4029
|
+
agentConfigForCall,
|
|
4030
|
+
loopState
|
|
4031
|
+
);
|
|
4032
|
+
if (accessError) {
|
|
4033
|
+
const accessErrorReason = classifyQuickChatAccessError(accessError);
|
|
4034
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-access-error-before-context", {
|
|
4035
|
+
hasAccessError: true,
|
|
4036
|
+
reason: accessErrorReason,
|
|
4037
|
+
responseApi: true
|
|
4038
|
+
});
|
|
4039
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted) {
|
|
4040
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-access-error-finalized", {
|
|
4041
|
+
reason: accessErrorReason,
|
|
4042
|
+
responseApi: true
|
|
4043
|
+
});
|
|
4044
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
4045
|
+
dispatch,
|
|
4046
|
+
runtimeDialogKey,
|
|
4047
|
+
agentKey,
|
|
4048
|
+
new Error(accessError)
|
|
4049
|
+
);
|
|
4050
|
+
}
|
|
4051
|
+
setLoopStopReason("error");
|
|
4052
|
+
return rejectWithValue(accessError);
|
|
4053
|
+
}
|
|
4054
|
+
const willSkipDynamicContext = canUseQuickChatEmptyDynamicContexts(
|
|
4055
|
+
quickChatPerfStartedAt,
|
|
4056
|
+
userInput,
|
|
4057
|
+
runtimeOptions,
|
|
4058
|
+
currentDialog,
|
|
4059
|
+
agentConfigForCall
|
|
4060
|
+
);
|
|
4061
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
4062
|
+
hasRuntimeOptions: !!runtimeOptions,
|
|
4063
|
+
isSimpleTextInput: isSimpleTextInput(userInput),
|
|
4064
|
+
referenceKeyCount: currentDialog?.referenceKeys?.length ?? 0,
|
|
4065
|
+
willSkipDynamicContext,
|
|
4066
|
+
responseApi: true
|
|
4067
|
+
});
|
|
4068
|
+
const dynamicContexts = willSkipDynamicContext ? (logQuickChatPerfStage2(
|
|
4069
|
+
quickChatPerfStartedAt,
|
|
4070
|
+
"stream-agent-dynamic-context-skipped",
|
|
4071
|
+
{
|
|
4072
|
+
reason: "simple-quick-chat-first-turn",
|
|
4073
|
+
responseApi: true
|
|
4074
|
+
}
|
|
4075
|
+
), EMPTY_DYNAMIC_CONTEXTS) : await buildDynamicContextsForTurn(
|
|
4076
|
+
loopState,
|
|
4077
|
+
dispatch,
|
|
4078
|
+
agentConfigForCall,
|
|
4079
|
+
userInput,
|
|
4080
|
+
runtimeOptions,
|
|
4081
|
+
mergedContentCache,
|
|
4082
|
+
dialogKey,
|
|
4083
|
+
quickChatPerfStartedAt
|
|
4084
|
+
);
|
|
4085
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
4086
|
+
responseApi: true
|
|
4087
|
+
});
|
|
4088
|
+
const contexts = mergeContexts(staticContexts2, dynamicContexts);
|
|
4089
|
+
const rawMessages = selectAllMsgs(loopState, dialogId);
|
|
4090
|
+
let visibleMessages = buildAgentViewMessages(
|
|
4091
|
+
rawMessages,
|
|
4092
|
+
agentConfigForCall.dbKey
|
|
4093
|
+
);
|
|
4094
|
+
if (appendTempUserInput2 && hasAgentRunUserInputContent(agentRunUserInput) && !isLastMessageMatchingUserInput(visibleMessages, agentRunUserInput)) {
|
|
4095
|
+
visibleMessages = [
|
|
4096
|
+
...visibleMessages,
|
|
4097
|
+
{
|
|
4098
|
+
id: `__tmp_user_${Date.now()}`,
|
|
4099
|
+
dbKey: "",
|
|
4100
|
+
role: "user",
|
|
4101
|
+
content: agentRunUserInput,
|
|
4102
|
+
thinkContent: "",
|
|
4103
|
+
cybotKey: agentConfigForCall.dbKey,
|
|
4104
|
+
isStreaming: false
|
|
4105
|
+
}
|
|
4106
|
+
];
|
|
4107
|
+
}
|
|
4108
|
+
const cleanedMessages = filterAndCleanMessages(visibleMessages);
|
|
4109
|
+
const ctxWindow = getModelContextWindow(agentConfigForCall.model) || 128e3;
|
|
4110
|
+
const summaryTokenCount = contexts.dialogSummary ? estimateTokenCount(contexts.dialogSummary) : 0;
|
|
4111
|
+
const processedMessages = trimMessagesWithSummary(
|
|
4112
|
+
compressOldToolResults(cleanedMessages),
|
|
4113
|
+
ctxWindow,
|
|
4114
|
+
summaryTokenCount
|
|
4115
|
+
);
|
|
4116
|
+
let firstDynamicIdx = processedMessages.findIndex(
|
|
4117
|
+
(m) => m.id && !initialHistoryIds.has(m.id)
|
|
4118
|
+
);
|
|
4119
|
+
if (firstDynamicIdx === -1) firstDynamicIdx = processedMessages.length;
|
|
4120
|
+
const stableMessages = processedMessages.slice(0, firstDynamicIdx);
|
|
4121
|
+
const dynamicMessages = processedMessages.slice(firstDynamicIdx);
|
|
4122
|
+
if (appendTempUserInput2) {
|
|
4123
|
+
const rejectReason = shouldRejectImageInputForAgent(
|
|
4124
|
+
agentConfigForCall,
|
|
4125
|
+
processedMessages
|
|
4126
|
+
);
|
|
4127
|
+
if (rejectReason) {
|
|
4128
|
+
setLoopStopReason("error");
|
|
4129
|
+
return rejectWithValue(rejectReason);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
const bodyData = generateRequestBody({
|
|
4133
|
+
agentConfig: effectiveAgentConfig,
|
|
4134
|
+
messages: dynamicMessages,
|
|
4135
|
+
stableMessages,
|
|
4136
|
+
userInput: userInputText,
|
|
4137
|
+
contexts
|
|
4138
|
+
});
|
|
4139
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-model-request-starting", {
|
|
4140
|
+
responseApi: true,
|
|
4141
|
+
dynamicMessageCount: dynamicMessages.length,
|
|
4142
|
+
stableMessageCount: stableMessages.length
|
|
4143
|
+
});
|
|
4144
|
+
modelRequestStarted = true;
|
|
4145
|
+
const meta = await sendOpenAIResponseRequest({
|
|
4146
|
+
bodyData,
|
|
4147
|
+
agentConfig: agentConfigForCall,
|
|
4148
|
+
thunkApi,
|
|
4149
|
+
dialogKey,
|
|
4150
|
+
parentMessageId: currentParentMessageId2,
|
|
4151
|
+
messageMetadata: streamingMessageMetadata,
|
|
4152
|
+
quickChatPerfStartedAt
|
|
4153
|
+
});
|
|
4154
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
4155
|
+
responseApi: true,
|
|
4156
|
+
hasToolCalls: meta.hasToolCalls,
|
|
4157
|
+
hasHandedOff: meta.hasHandedOff,
|
|
4158
|
+
hasPendingInteraction: meta.hasPendingInteraction
|
|
4159
|
+
});
|
|
4160
|
+
appendTempUserInput2 = false;
|
|
4161
|
+
currentParentMessageId2 = void 0;
|
|
4162
|
+
totalTurnUsage = updateTotalUsage(totalTurnUsage, meta.usage);
|
|
4163
|
+
if (meta.hasHandedOff) {
|
|
4164
|
+
if (!requestParentMessageId && meta.messageId) {
|
|
4165
|
+
dispatch(removeTransientMessage(meta.messageId));
|
|
4166
|
+
}
|
|
4167
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "handoff";
|
|
4168
|
+
break;
|
|
4169
|
+
}
|
|
4170
|
+
if (meta.hasPendingInteraction) {
|
|
4171
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "pending";
|
|
4172
|
+
break;
|
|
4173
|
+
}
|
|
4174
|
+
const afterTurnState = getState();
|
|
4175
|
+
const queuedMessages = selectPendingUserInputQueue(afterTurnState, dialogKey);
|
|
4176
|
+
if (queuedMessages.length > 0) {
|
|
4177
|
+
const queuedText = queuedMessages[0];
|
|
4178
|
+
const currentDialogConfig = selectDialogConfigByKey(afterTurnState, dialogKey) ?? selectCurrentDialogConfig(afterTurnState);
|
|
4179
|
+
if (!currentDialogConfig) {
|
|
4180
|
+
dispatch(clearPendingUserInputQueue({ dialogKey }));
|
|
4181
|
+
break;
|
|
4182
|
+
}
|
|
4183
|
+
await dispatch(
|
|
4184
|
+
prepareAndPersistUserMessage({
|
|
4185
|
+
userInput: queuedText,
|
|
4186
|
+
dialogConfig: currentDialogConfig
|
|
4187
|
+
})
|
|
4188
|
+
).unwrap();
|
|
4189
|
+
dispatch(dequeueUserInput({ dialogKey }));
|
|
4190
|
+
continue;
|
|
4191
|
+
}
|
|
4192
|
+
if (!meta.hasToolCalls) {
|
|
4193
|
+
if (w2) w2.__LOOP_STOP_REASON__ = "done";
|
|
4194
|
+
break;
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
return {
|
|
4198
|
+
usage: totalTurnUsage ?? void 0
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
const maxExecutionTime = selectMaxExecutionTime(state);
|
|
4202
|
+
const MAX_TIME_MS = maxExecutionTime > 0 ? maxExecutionTime : 24e4;
|
|
4203
|
+
const startTime = Date.now();
|
|
4204
|
+
const staticContexts = await buildStaticContextsWithToolsPrewarm(
|
|
4205
|
+
state,
|
|
4206
|
+
dispatch,
|
|
4207
|
+
agentConfigForCall,
|
|
4208
|
+
currentDialog ?? void 0,
|
|
4209
|
+
mergedContentCache
|
|
4210
|
+
);
|
|
4211
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-static-context-ready", {
|
|
4212
|
+
model: agentConfigForCall.model,
|
|
4213
|
+
responseApi: false
|
|
4214
|
+
});
|
|
4215
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-post-static-entered", {
|
|
4216
|
+
responseApi: false
|
|
4217
|
+
});
|
|
4218
|
+
let appendTempUserInput = true;
|
|
4219
|
+
let currentParentMessageId = parentMessageId ?? void 0;
|
|
4220
|
+
const w = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
4221
|
+
if (w) w.__LOOP_STOP_REASON__ = null;
|
|
4222
|
+
if (!isRespModel) {
|
|
4223
|
+
loopKey = `loop:${dialogId}`;
|
|
4224
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-active-controller-adding", {
|
|
4225
|
+
responseApi: false
|
|
4226
|
+
});
|
|
4227
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
4228
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-active-controller-added", {
|
|
4229
|
+
responseApi: false
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4232
|
+
for (; ; ) {
|
|
4233
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-loop-entered", {
|
|
4234
|
+
responseApi: false
|
|
4235
|
+
});
|
|
4236
|
+
const requestParentMessageId = currentParentMessageId;
|
|
4237
|
+
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
4238
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-loop-aborted-before-context", {
|
|
4239
|
+
loopControllerAborted: loopController.signal.aborted,
|
|
4240
|
+
thunkSignalAborted: thunkApi.signal.aborted,
|
|
4241
|
+
responseApi: false
|
|
4242
|
+
});
|
|
4243
|
+
if (w) w.__LOOP_STOP_REASON__ = "aborted";
|
|
4244
|
+
return { aborted: true };
|
|
4245
|
+
}
|
|
4246
|
+
const loopState = getState();
|
|
4247
|
+
const now = Date.now();
|
|
4248
|
+
if (now - startTime > MAX_TIME_MS) {
|
|
4249
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-loop-timeout-before-context", {
|
|
4250
|
+
maxTimeMs: MAX_TIME_MS,
|
|
4251
|
+
elapsedMs: now - startTime,
|
|
4252
|
+
responseApi: false
|
|
4253
|
+
});
|
|
4254
|
+
if (w) w.__LOOP_STOP_REASON__ = "timeout";
|
|
4255
|
+
break;
|
|
4256
|
+
}
|
|
4257
|
+
const accessError = validateAccessAndBalance(
|
|
4258
|
+
agentConfigForCall,
|
|
4259
|
+
loopState
|
|
4260
|
+
);
|
|
4261
|
+
if (accessError) {
|
|
4262
|
+
const accessErrorReason = classifyQuickChatAccessError(accessError);
|
|
4263
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-access-error-before-context", {
|
|
4264
|
+
hasAccessError: true,
|
|
4265
|
+
reason: accessErrorReason,
|
|
4266
|
+
responseApi: false
|
|
4267
|
+
});
|
|
4268
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted) {
|
|
4269
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-access-error-finalized", {
|
|
4270
|
+
reason: accessErrorReason,
|
|
4271
|
+
responseApi: false
|
|
4272
|
+
});
|
|
4273
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
4274
|
+
dispatch,
|
|
4275
|
+
runtimeDialogKey,
|
|
4276
|
+
agentKey,
|
|
4277
|
+
new Error(accessError)
|
|
4278
|
+
);
|
|
4279
|
+
}
|
|
4280
|
+
setLoopStopReason("error");
|
|
4281
|
+
return rejectWithValue(accessError);
|
|
4282
|
+
}
|
|
4283
|
+
const willSkipDynamicContext = canUseQuickChatEmptyDynamicContexts(
|
|
4284
|
+
quickChatPerfStartedAt,
|
|
4285
|
+
userInput,
|
|
4286
|
+
runtimeOptions,
|
|
4287
|
+
currentDialog,
|
|
4288
|
+
agentConfigForCall
|
|
4289
|
+
);
|
|
4290
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
4291
|
+
hasRuntimeOptions: !!runtimeOptions,
|
|
4292
|
+
isSimpleTextInput: isSimpleTextInput(userInput),
|
|
4293
|
+
referenceKeyCount: currentDialog?.referenceKeys?.length ?? 0,
|
|
4294
|
+
willSkipDynamicContext,
|
|
4295
|
+
responseApi: false
|
|
4296
|
+
});
|
|
4297
|
+
const dynamicContexts = willSkipDynamicContext ? (logQuickChatPerfStage2(
|
|
4298
|
+
quickChatPerfStartedAt,
|
|
4299
|
+
"stream-agent-dynamic-context-skipped",
|
|
4300
|
+
{
|
|
4301
|
+
reason: "simple-quick-chat-first-turn",
|
|
4302
|
+
responseApi: false
|
|
4303
|
+
}
|
|
4304
|
+
), EMPTY_DYNAMIC_CONTEXTS) : await buildDynamicContextsForTurn(
|
|
4305
|
+
loopState,
|
|
4306
|
+
dispatch,
|
|
4307
|
+
agentConfigForCall,
|
|
4308
|
+
userInput,
|
|
4309
|
+
runtimeOptions,
|
|
4310
|
+
mergedContentCache,
|
|
4311
|
+
dialogKey,
|
|
4312
|
+
quickChatPerfStartedAt
|
|
4313
|
+
);
|
|
4314
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
4315
|
+
responseApi: false
|
|
4316
|
+
});
|
|
4317
|
+
const contexts = mergeContexts(staticContexts, dynamicContexts);
|
|
4318
|
+
const rawMessages = selectAllMsgs(loopState, dialogId);
|
|
4319
|
+
let visibleMessages = buildAgentViewMessages(
|
|
4320
|
+
rawMessages,
|
|
4321
|
+
agentConfigForCall.dbKey
|
|
4322
|
+
);
|
|
4323
|
+
if (appendTempUserInput && hasAgentRunUserInputContent(agentRunUserInput) && !isLastMessageMatchingUserInput(visibleMessages, agentRunUserInput)) {
|
|
4324
|
+
visibleMessages = [
|
|
4325
|
+
...visibleMessages,
|
|
4326
|
+
{
|
|
4327
|
+
id: `__tmp_user_${Date.now()}`,
|
|
4328
|
+
dbKey: "",
|
|
4329
|
+
role: "user",
|
|
4330
|
+
content: agentRunUserInput,
|
|
4331
|
+
thinkContent: "",
|
|
4332
|
+
cybotKey: agentConfigForCall.dbKey,
|
|
4333
|
+
isStreaming: false
|
|
4334
|
+
}
|
|
4335
|
+
];
|
|
4336
|
+
}
|
|
4337
|
+
const cleanedMessages = filterAndCleanMessages(visibleMessages);
|
|
4338
|
+
const ctxWindow = getModelContextWindow(agentConfigForCall.model) || 128e3;
|
|
4339
|
+
const summaryTokenCount = contexts.dialogSummary ? estimateTokenCount(contexts.dialogSummary) : 0;
|
|
4340
|
+
const processedMessages = trimMessagesWithSummary(
|
|
4341
|
+
compressOldToolResults(cleanedMessages),
|
|
4342
|
+
ctxWindow,
|
|
4343
|
+
summaryTokenCount
|
|
4344
|
+
);
|
|
4345
|
+
let firstDynamicIdx = processedMessages.findIndex(
|
|
4346
|
+
(m) => m.id && !initialHistoryIds.has(m.id)
|
|
4347
|
+
);
|
|
4348
|
+
if (firstDynamicIdx === -1) firstDynamicIdx = processedMessages.length;
|
|
4349
|
+
const stableMessages = processedMessages.slice(0, firstDynamicIdx);
|
|
4350
|
+
const dynamicMessages = processedMessages.slice(firstDynamicIdx);
|
|
4351
|
+
if (appendTempUserInput) {
|
|
4352
|
+
const rejectReason = shouldRejectImageInputForAgent(
|
|
4353
|
+
agentConfigForCall,
|
|
4354
|
+
processedMessages
|
|
4355
|
+
);
|
|
4356
|
+
if (rejectReason) {
|
|
4357
|
+
setLoopStopReason("error");
|
|
4358
|
+
return rejectWithValue(rejectReason);
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
const bodyData = generateRequestBody({
|
|
4362
|
+
agentConfig: effectiveAgentConfig,
|
|
4363
|
+
messages: dynamicMessages,
|
|
4364
|
+
stableMessages,
|
|
4365
|
+
userInput: userInputText,
|
|
4366
|
+
contexts
|
|
4367
|
+
});
|
|
4368
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-model-request-starting", {
|
|
4369
|
+
responseApi: false,
|
|
4370
|
+
dynamicMessageCount: dynamicMessages.length,
|
|
4371
|
+
stableMessageCount: stableMessages.length
|
|
4372
|
+
});
|
|
4373
|
+
modelRequestStarted = true;
|
|
4374
|
+
const disableToolsForThisRequest = shouldDisableQuickChatToolsForDirectAnswer(
|
|
4375
|
+
quickChatPerfStartedAt,
|
|
4376
|
+
userInput,
|
|
4377
|
+
runtimeOptions,
|
|
4378
|
+
currentDialog
|
|
4379
|
+
);
|
|
4380
|
+
if (disableToolsForThisRequest) {
|
|
4381
|
+
logQuickChatPerfStage2(
|
|
4382
|
+
quickChatPerfStartedAt,
|
|
4383
|
+
"stream-agent-tools-disabled-for-direct-answer",
|
|
4384
|
+
{
|
|
4385
|
+
responseApi: false,
|
|
4386
|
+
toolCount: Array.isArray(agentConfigForCall.tools) ? agentConfigForCall.tools.length : 0
|
|
4387
|
+
}
|
|
4388
|
+
);
|
|
4389
|
+
}
|
|
4390
|
+
const meta = await sendOpenAICompletionsRequest({
|
|
4391
|
+
bodyData,
|
|
4392
|
+
agentConfig: agentConfigForCall,
|
|
4393
|
+
thunkApi,
|
|
4394
|
+
dialogKey,
|
|
4395
|
+
parentMessageId: currentParentMessageId,
|
|
4396
|
+
messageMetadata: streamingMessageMetadata,
|
|
4397
|
+
disableToolsForThisRequest,
|
|
4398
|
+
quickChatPerfStartedAt
|
|
4399
|
+
});
|
|
4400
|
+
logQuickChatPerfStage2(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
4401
|
+
responseApi: false,
|
|
4402
|
+
hasToolCalls: meta.hasToolCalls,
|
|
4403
|
+
hasHandedOff: meta.hasHandedOff,
|
|
4404
|
+
hasPendingInteraction: meta.hasPendingInteraction
|
|
4405
|
+
});
|
|
4406
|
+
appendTempUserInput = false;
|
|
4407
|
+
currentParentMessageId = void 0;
|
|
4408
|
+
totalTurnUsage = updateTotalUsage(totalTurnUsage, meta.usage);
|
|
4409
|
+
if (meta.hasHandedOff) {
|
|
4410
|
+
if (!requestParentMessageId && meta.messageId) {
|
|
4411
|
+
dispatch(removeTransientMessage(meta.messageId));
|
|
4412
|
+
}
|
|
4413
|
+
if (w) w.__LOOP_STOP_REASON__ = "handoff";
|
|
4414
|
+
break;
|
|
4415
|
+
}
|
|
4416
|
+
if (meta.hasPendingInteraction) {
|
|
4417
|
+
if (w) w.__LOOP_STOP_REASON__ = "pending";
|
|
4418
|
+
break;
|
|
4419
|
+
}
|
|
4420
|
+
const afterTurnState = getState();
|
|
4421
|
+
const queuedMessages = selectPendingUserInputQueue(afterTurnState, dialogKey);
|
|
4422
|
+
if (queuedMessages.length > 0) {
|
|
4423
|
+
const queuedText = queuedMessages[0];
|
|
4424
|
+
const currentDialogConfig = selectDialogConfigByKey(afterTurnState, dialogKey) ?? selectCurrentDialogConfig(afterTurnState);
|
|
4425
|
+
if (!currentDialogConfig) {
|
|
4426
|
+
dispatch(clearPendingUserInputQueue({ dialogKey }));
|
|
4427
|
+
break;
|
|
4428
|
+
}
|
|
4429
|
+
await dispatch(
|
|
4430
|
+
prepareAndPersistUserMessage({
|
|
4431
|
+
userInput: queuedText,
|
|
4432
|
+
dialogConfig: currentDialogConfig
|
|
4433
|
+
})
|
|
4434
|
+
).unwrap();
|
|
4435
|
+
dispatch(dequeueUserInput({ dialogKey }));
|
|
4436
|
+
continue;
|
|
4437
|
+
}
|
|
4438
|
+
if (!meta.hasToolCalls) {
|
|
4439
|
+
if (w) w.__LOOP_STOP_REASON__ = "done";
|
|
4440
|
+
break;
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
return {
|
|
4444
|
+
usage: totalTurnUsage ?? void 0
|
|
4445
|
+
};
|
|
4446
|
+
} catch (error) {
|
|
4447
|
+
if (isTurnAborted(error)) {
|
|
4448
|
+
turnAborted = true;
|
|
4449
|
+
if (remoteTransientMessageId && !remoteTransientMessageFinalized) {
|
|
4450
|
+
dispatch(removeTransientMessage(remoteTransientMessageId));
|
|
4451
|
+
remoteTransientMessageFinalized = true;
|
|
4452
|
+
}
|
|
4453
|
+
const w = typeof globalThis !== "undefined" && globalThis.window ? globalThis.window : null;
|
|
4454
|
+
if (w) w.__LOOP_STOP_REASON__ = "aborted";
|
|
4455
|
+
return { aborted: true };
|
|
4456
|
+
}
|
|
4457
|
+
console.error(
|
|
4458
|
+
`Error in streamAgentChatTurn for [${agentKey}]:`,
|
|
4459
|
+
error
|
|
4460
|
+
);
|
|
4461
|
+
if (remoteTransientMessageId && !remoteTransientMessageFinalized) {
|
|
4462
|
+
dispatch(finalizeTransientMessageOnError({
|
|
4463
|
+
id: remoteTransientMessageId,
|
|
4464
|
+
error: toErrorMessage(error)
|
|
4465
|
+
}));
|
|
4466
|
+
remoteTransientMessageFinalized = true;
|
|
4467
|
+
setLoopStopReason("error");
|
|
4468
|
+
}
|
|
4469
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted && !remoteTransientMessageId) {
|
|
4470
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
4471
|
+
dispatch,
|
|
4472
|
+
runtimeDialogKey,
|
|
4473
|
+
agentKey,
|
|
4474
|
+
error
|
|
4475
|
+
);
|
|
4476
|
+
} else if (!isTurnAborted(error)) {
|
|
4477
|
+
setLoopStopReason("error");
|
|
4478
|
+
}
|
|
4479
|
+
return rejectWithValue(
|
|
4480
|
+
error?.message || "An unexpected error occurred in streamAgentChatTurn."
|
|
4481
|
+
);
|
|
4482
|
+
} finally {
|
|
4483
|
+
if (loopKey && runtimeDialogKey) {
|
|
4484
|
+
dispatch(removeActiveController({ messageId: loopKey, dialogKey: runtimeDialogKey }));
|
|
4485
|
+
} else if (loopKey) {
|
|
4486
|
+
dispatch(removeActiveController(loopKey));
|
|
4487
|
+
}
|
|
4488
|
+
if (turnAborted) {
|
|
4489
|
+
dispatch(clearPendingUserInputQueue(runtimeDialogKey ? { dialogKey: runtimeDialogKey } : void 0));
|
|
4490
|
+
}
|
|
4491
|
+
if (runtimeDialogKey) {
|
|
4492
|
+
dispatch(runChatQueueTurnEnd({
|
|
4493
|
+
dialogKey: runtimeDialogKey,
|
|
4494
|
+
ok: !turnAborted,
|
|
4495
|
+
aborted: turnAborted
|
|
4496
|
+
}));
|
|
4497
|
+
}
|
|
4498
|
+
thunkApi.signal.removeEventListener("abort", onAbort);
|
|
4499
|
+
}
|
|
4500
|
+
};
|
|
4501
|
+
export {
|
|
4502
|
+
streamAgentChatTurnHandler
|
|
4503
|
+
};
|