nolo-cli 0.1.23 → 0.1.26
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/README.md +25 -3
- package/agent-runtime/hybridRecordStore.ts +30 -9
- package/agent-runtime/localLoop.ts +12 -4
- package/agent-runtime/localToolPolicy.ts +22 -10
- package/agent-runtime/localWorkspaceTools.ts +140 -374
- package/agent-runtime/runtimeToolPolicy.ts +43 -2
- package/agent-runtime/types.ts +5 -0
- package/agentAliases.ts +1 -1
- package/agentCommandSupport.ts +77 -0
- package/agentInternalCommandEntries.ts +34 -0
- package/agentListCommands.ts +123 -0
- package/agentListHelpers.ts +187 -0
- package/agentMachineCommands.ts +402 -0
- package/agentRecordCommands.ts +141 -0
- package/agentRecordHelpers.ts +395 -0
- package/agentRunCommand.ts +1 -2
- package/agentRuntimeCommands.ts +7 -701
- package/ai/agent/_executeModel.ts +29 -3
- package/ai/agent/buildSystemPrompt.ts +17 -0
- package/ai/agent/cliChatClient.ts +38 -4
- package/ai/agent/cliExecutor.ts +304 -25
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/guidedCreation/assistant.ts +82 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/draft.ts +62 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/machineRunPermissions.ts +21 -3
- package/ai/agent/runtimeGuidance.ts +1 -1
- package/ai/agent/server/fetchPublicAgents.ts +6 -1
- package/ai/agent/streamAgentChatTurn.ts +492 -19
- package/ai/agent/web/useGuidedAgentCreation.ts +217 -0
- package/ai/ai.locale.ts +14 -2
- package/ai/chat/sendOpenAICompletionsRequest.ts +28 -3
- package/ai/chat/sendOpenAIResponseRequest.ts +3 -2
- package/ai/llm/getPricing.ts +14 -0
- package/ai/llm/kimi.ts +3 -0
- package/ai/llm/modelAvatar.ts +1 -0
- package/ai/llm/providers.ts +1 -1
- package/ai/taskRun/taskRunProtocol.ts +24 -10
- package/ai/token/modelUsageQuery.ts +17 -0
- package/ai/tools/agent/agentTools.ts +21 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/checkEnvTool.ts +52 -20
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
- package/ai/tools/index.ts +24 -14
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +4 -2
- package/ai/tools/readTool.ts +31 -0
- package/ai/tools/toolApiClient.ts +35 -1
- package/ai/tools/toolNameAliases.ts +13 -0
- package/cliCommandAdapters.ts +28 -0
- package/cliCommandDispatch.ts +66 -0
- package/cliCommandFactories.ts +34 -0
- package/cliCommandTypes.ts +49 -0
- package/cliEnvHelpers.ts +44 -0
- package/client/agentRun.test.ts +123 -19
- package/client/agentRun.ts +40 -1
- package/client/hybridRecordStore.test.ts +42 -0
- package/client/hybridRecordStore.ts +11 -0
- package/client/localRuntimeAdapter.test.ts +189 -21
- package/client/localRuntimeAdapter.ts +84 -8
- package/client/localRuntimeDryRun.test.ts +5 -2
- package/client/taskRunPrompt.ts +1 -0
- package/commandRegistry.ts +11 -61
- package/connector-experimental/capabilities.ts +1 -0
- package/database/server/coreDataProxy.ts +43 -26
- package/index.ts +54 -191
- package/internalCommandEntries.ts +12 -0
- package/machineCommands.ts +301 -32
- package/offlineMarxistsAgentCommand.ts +314 -0
- package/package.json +20 -2
- package/runtimeDoctorCommands.ts +118 -0
- package/scriptCommandEntries.ts +71 -0
- package/systemInternalCommandEntries.ts +71 -0
- package/workflowInternalCommandEntries.ts +11 -0
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
getFullChatContextKeys,
|
|
27
27
|
deduplicateContextKeys,
|
|
28
28
|
} from "ai/agent/getFullChatContextKeys";
|
|
29
|
-
import type { Agent } from "app/types";
|
|
29
|
+
import type { Agent, DialogConfig } from "app/types";
|
|
30
30
|
import { isResponseAPIModel } from "ai/llm/isResponseAPIModel";
|
|
31
31
|
import { getModelContextWindow } from "ai/llm/getModelContextWindow";
|
|
32
32
|
import { resolveAgentImageInputSupport } from "ai/llm/agentCapabilities";
|
|
@@ -56,7 +56,11 @@ import {
|
|
|
56
56
|
import { buildCliPrompt } from "./cliPrompt";
|
|
57
57
|
import { createCliChatTurnStream } from "./cliChatClient";
|
|
58
58
|
import { getCliChatSession, startCliChatSession } from "./cliChatClient";
|
|
59
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
messageStreamEnd,
|
|
61
|
+
messageStreaming,
|
|
62
|
+
prepareAndPersistUserMessage,
|
|
63
|
+
} from "chat/messages/messageSlice";
|
|
60
64
|
import { selectCurrentToken, selectUserId } from "auth/authSlice";
|
|
61
65
|
import { persistMessageWithFixedId } from "./persistMessageWithFixedId";
|
|
62
66
|
import { updateTotalUsage } from "../chat/updateTotalUsage";
|
|
@@ -65,6 +69,11 @@ import {
|
|
|
65
69
|
extractAgentRuntimeServerBase,
|
|
66
70
|
normalizeServerOrigin,
|
|
67
71
|
} from "./runtimeServerBase";
|
|
72
|
+
import { getIsDesktopApp } from "app/utils/env";
|
|
73
|
+
import { runDesktopAgentRuntimeTurn } from "app/utils/desktopAgentRuntimeTurnClient";
|
|
74
|
+
|
|
75
|
+
const BUILTIN_NOLO_AGENT_ID = "01NOLOAPPBLD000000019KCKT0";
|
|
76
|
+
const BUILTIN_NOLO_AGENT_KEY = `agent-pub-${BUILTIN_NOLO_AGENT_ID}`;
|
|
68
77
|
|
|
69
78
|
const buildParallelMessageMetadata = (
|
|
70
79
|
agentConfig: Pick<Agent, "dbKey" | "name">,
|
|
@@ -106,6 +115,25 @@ const filterMessagesForParallelBranch = (
|
|
|
106
115
|
});
|
|
107
116
|
};
|
|
108
117
|
|
|
118
|
+
const isDesktopBuiltinNoloAgent = (
|
|
119
|
+
requestedAgentKey: string,
|
|
120
|
+
agentConfig: Partial<Agent> | null | undefined,
|
|
121
|
+
) => {
|
|
122
|
+
if (!getIsDesktopApp()) return false;
|
|
123
|
+
const candidates = [
|
|
124
|
+
requestedAgentKey,
|
|
125
|
+
agentConfig?.dbKey,
|
|
126
|
+
agentConfig?.agentKey,
|
|
127
|
+
agentConfig?.id,
|
|
128
|
+
];
|
|
129
|
+
return candidates.some((value) =>
|
|
130
|
+
typeof value === "string" &&
|
|
131
|
+
(value === BUILTIN_NOLO_AGENT_KEY ||
|
|
132
|
+
value === BUILTIN_NOLO_AGENT_ID ||
|
|
133
|
+
value.endsWith(`-${BUILTIN_NOLO_AGENT_ID}`))
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
109
137
|
const formatMachineAgentRunError = async (response: Response): Promise<string> => {
|
|
110
138
|
const errorText = await response.text();
|
|
111
139
|
let payload: any = null;
|
|
@@ -149,20 +177,171 @@ export interface StreamAgentChatTurnArgs {
|
|
|
149
177
|
quickChatPerfStartedAt?: number;
|
|
150
178
|
}
|
|
151
179
|
|
|
180
|
+
const QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS = 10_000;
|
|
181
|
+
const QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS = 5_000;
|
|
182
|
+
const EMPTY_DYNAMIC_CONTEXTS = {
|
|
183
|
+
currentInputContext: null,
|
|
184
|
+
historyContext: "",
|
|
185
|
+
editingContext: null,
|
|
186
|
+
appWorkingMemory: null,
|
|
187
|
+
dialogSummary: null,
|
|
188
|
+
proactiveSummary: null,
|
|
189
|
+
referenceKeys: [],
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
function appendCliCapabilityWarnings(content: string, warnings: string[]): string {
|
|
193
|
+
if (!warnings.length) return content;
|
|
194
|
+
const warningBlock = `\n\n[CLI 能力提示]\n${warnings.map((warning) => `- ${warning}`).join("\n")}`;
|
|
195
|
+
return `${content}${warningBlock}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
152
198
|
const logQuickChatPerfStage = (
|
|
153
199
|
startedAt: number | undefined,
|
|
154
200
|
stage: string,
|
|
155
201
|
details: Record<string, unknown> = {}
|
|
156
202
|
) => {
|
|
157
203
|
if (!startedAt) return;
|
|
204
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
158
205
|
console.info("[QuickChatPerf]", {
|
|
159
206
|
stage,
|
|
160
|
-
elapsedMs:
|
|
161
|
-
...(typeof performance !== "undefined" ? { atMs:
|
|
207
|
+
elapsedMs: now - startedAt,
|
|
208
|
+
...(typeof performance !== "undefined" ? { atMs: now } : {}),
|
|
162
209
|
...details,
|
|
163
210
|
});
|
|
164
211
|
};
|
|
165
212
|
|
|
213
|
+
const readAgentConfigForTurn = async (
|
|
214
|
+
dispatch: any,
|
|
215
|
+
agentKey: string,
|
|
216
|
+
quickChatPerfStartedAt?: number,
|
|
217
|
+
): Promise<Agent> => {
|
|
218
|
+
const readPromise = dispatch(read({ dbKey: agentKey })).unwrap() as Promise<Agent>;
|
|
219
|
+
if (!quickChatPerfStartedAt) return readPromise;
|
|
220
|
+
|
|
221
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
222
|
+
try {
|
|
223
|
+
return await Promise.race([
|
|
224
|
+
readPromise,
|
|
225
|
+
new Promise<Agent>((_, reject) => {
|
|
226
|
+
timeoutId = setTimeout(() => {
|
|
227
|
+
logQuickChatPerfStage(
|
|
228
|
+
quickChatPerfStartedAt,
|
|
229
|
+
"quick-chat-agent-config-read-timeout",
|
|
230
|
+
{
|
|
231
|
+
agentKey,
|
|
232
|
+
timeoutMs: QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS,
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
reject(
|
|
236
|
+
new Error("读取 Agent 配置超时,未能启动模型回复。"),
|
|
237
|
+
);
|
|
238
|
+
}, QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS);
|
|
239
|
+
}),
|
|
240
|
+
]);
|
|
241
|
+
} finally {
|
|
242
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const buildDynamicContextsForTurn = async (
|
|
247
|
+
state: RootState,
|
|
248
|
+
dispatch: any,
|
|
249
|
+
agentConfig: Agent,
|
|
250
|
+
userInput: string | any[],
|
|
251
|
+
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
252
|
+
mergedContentCache: Map<string, any>,
|
|
253
|
+
dialogKey: string | undefined,
|
|
254
|
+
quickChatPerfStartedAt?: number,
|
|
255
|
+
) => {
|
|
256
|
+
if (!quickChatPerfStartedAt) {
|
|
257
|
+
return buildDynamicContexts(
|
|
258
|
+
state,
|
|
259
|
+
dispatch,
|
|
260
|
+
agentConfig,
|
|
261
|
+
userInput,
|
|
262
|
+
runtimeOptions,
|
|
263
|
+
mergedContentCache,
|
|
264
|
+
dialogKey,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
269
|
+
try {
|
|
270
|
+
const timeoutPromise = new Promise<typeof EMPTY_DYNAMIC_CONTEXTS>((resolve) => {
|
|
271
|
+
timeoutId = setTimeout(() => {
|
|
272
|
+
logQuickChatPerfStage(
|
|
273
|
+
quickChatPerfStartedAt,
|
|
274
|
+
"quick-chat-dynamic-context-timeout",
|
|
275
|
+
{
|
|
276
|
+
agentKey: agentConfig.dbKey,
|
|
277
|
+
dialogKey,
|
|
278
|
+
timeoutMs: QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
resolve(EMPTY_DYNAMIC_CONTEXTS);
|
|
282
|
+
}, QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
logQuickChatPerfStage(
|
|
286
|
+
quickChatPerfStartedAt,
|
|
287
|
+
"stream-agent-dynamic-context-starting",
|
|
288
|
+
{
|
|
289
|
+
responseApi: isResponseAPIModel(agentConfig),
|
|
290
|
+
},
|
|
291
|
+
);
|
|
292
|
+
const contextPromise = buildDynamicContexts(
|
|
293
|
+
state,
|
|
294
|
+
dispatch,
|
|
295
|
+
agentConfig,
|
|
296
|
+
userInput,
|
|
297
|
+
runtimeOptions,
|
|
298
|
+
mergedContentCache,
|
|
299
|
+
dialogKey,
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return await Promise.race([
|
|
303
|
+
contextPromise,
|
|
304
|
+
timeoutPromise,
|
|
305
|
+
]);
|
|
306
|
+
} finally {
|
|
307
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const finalizeQuickChatAgentTurnFailure = async (
|
|
312
|
+
dispatch: any,
|
|
313
|
+
dialogKey: string,
|
|
314
|
+
agentKey: string,
|
|
315
|
+
error: unknown,
|
|
316
|
+
) => {
|
|
317
|
+
const dialogId = extractCustomId(dialogKey);
|
|
318
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
319
|
+
const reason =
|
|
320
|
+
error instanceof Error && error.message.trim()
|
|
321
|
+
? error.message.trim()
|
|
322
|
+
: "未能启动模型回复,请重试。";
|
|
323
|
+
|
|
324
|
+
await dispatch(
|
|
325
|
+
messageStreamEnd({
|
|
326
|
+
finalContentBuffer: [
|
|
327
|
+
{
|
|
328
|
+
type: "text",
|
|
329
|
+
text: `[错误: ${reason}]`,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
totalUsage: null,
|
|
333
|
+
msgKey,
|
|
334
|
+
agentConfig: {
|
|
335
|
+
dbKey: agentKey,
|
|
336
|
+
},
|
|
337
|
+
dialogId,
|
|
338
|
+
dialogKey,
|
|
339
|
+
messageId,
|
|
340
|
+
reasoningBuffer: "",
|
|
341
|
+
})
|
|
342
|
+
).unwrap?.();
|
|
343
|
+
};
|
|
344
|
+
|
|
166
345
|
const normalizeAgentRunUserInput = (userInput: string | any[]) => {
|
|
167
346
|
if (typeof userInput === "string") {
|
|
168
347
|
return userInput;
|
|
@@ -181,9 +360,68 @@ const normalizeAgentRunUserInput = (userInput: string | any[]) => {
|
|
|
181
360
|
});
|
|
182
361
|
};
|
|
183
362
|
|
|
363
|
+
const isSimpleTextInput = (userInput: string | any[]) => {
|
|
364
|
+
if (typeof userInput === "string") return true;
|
|
365
|
+
if (!Array.isArray(userInput)) return false;
|
|
366
|
+
return userInput.every(
|
|
367
|
+
(part) =>
|
|
368
|
+
part &&
|
|
369
|
+
typeof part === "object" &&
|
|
370
|
+
part.type === "text" &&
|
|
371
|
+
typeof part.text === "string",
|
|
372
|
+
);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const canUseQuickChatEmptyDynamicContexts = (
|
|
376
|
+
quickChatPerfStartedAt: number | undefined,
|
|
377
|
+
userInput: string | any[],
|
|
378
|
+
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
379
|
+
dialogConfig: DialogConfig | null,
|
|
380
|
+
) => {
|
|
381
|
+
if (!quickChatPerfStartedAt) return false;
|
|
382
|
+
if (runtimeOptions) return false;
|
|
383
|
+
if (!isSimpleTextInput(userInput)) return false;
|
|
384
|
+
if (dialogConfig?.referenceKeys?.length) return false;
|
|
385
|
+
return true;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const QUICK_CHAT_DIRECT_ANSWER_PATTERN =
|
|
389
|
+
/(只回复|只输出|只回答|直接回复|直接回答|不要解释|无需解释|不用解释|简短回答|一句话)/i;
|
|
390
|
+
const QUICK_CHAT_TOOL_INTENT_PATTERN =
|
|
391
|
+
/(调用|转交|agent|助手|应用|网页|页面|图表|图片|生成图|画图|删除|清理|空间|商品|链接|https?:\/\/|www\.|@)/i;
|
|
392
|
+
|
|
393
|
+
const shouldDisableQuickChatToolsForDirectAnswer = (
|
|
394
|
+
quickChatPerfStartedAt: number | undefined,
|
|
395
|
+
userInput: string | any[],
|
|
396
|
+
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
397
|
+
dialogConfig: DialogConfig | null,
|
|
398
|
+
) => {
|
|
399
|
+
if (!canUseQuickChatEmptyDynamicContexts(
|
|
400
|
+
quickChatPerfStartedAt,
|
|
401
|
+
userInput,
|
|
402
|
+
runtimeOptions,
|
|
403
|
+
dialogConfig,
|
|
404
|
+
)) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
const text = extractAgentRunUserText(userInput);
|
|
408
|
+
if (!text || text.length > 500) return false;
|
|
409
|
+
if (!QUICK_CHAT_DIRECT_ANSWER_PATTERN.test(text)) return false;
|
|
410
|
+
return !QUICK_CHAT_TOOL_INTENT_PATTERN.test(text);
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const classifyQuickChatAccessError = (accessError: string) => {
|
|
414
|
+
if (accessError.includes("获取用户余额")) return "balance-loading";
|
|
415
|
+
if (accessError.includes("余额")) return "balance";
|
|
416
|
+
if (accessError.includes("白名单")) return "whitelist";
|
|
417
|
+
if (accessError.includes("定价")) return "pricing";
|
|
418
|
+
return "unknown";
|
|
419
|
+
};
|
|
420
|
+
|
|
184
421
|
const isUsableAgentConfig = (value: unknown): value is Agent =>
|
|
185
422
|
!!value &&
|
|
186
423
|
typeof value === "object" &&
|
|
424
|
+
(value as any).__ssrPreviewOnly !== true &&
|
|
187
425
|
typeof (value as Agent).dbKey === "string" &&
|
|
188
426
|
!!(value as Agent).dbKey &&
|
|
189
427
|
typeof (value as Agent).model === "string" &&
|
|
@@ -244,6 +482,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
244
482
|
let runtimeDialogKey: string | null = explicitDialogKey ?? null;
|
|
245
483
|
let remoteTransientMessageId: string | null = null;
|
|
246
484
|
let remoteTransientMessageFinalized = false;
|
|
485
|
+
let modelRequestStarted = false;
|
|
247
486
|
|
|
248
487
|
try {
|
|
249
488
|
let totalTurnUsage: any = null;
|
|
@@ -256,7 +495,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
256
495
|
const cachedAgentConfig = selectById(getState() as RootState, agentKey);
|
|
257
496
|
const agentConfig = isUsableAgentConfig(cachedAgentConfig)
|
|
258
497
|
? cachedAgentConfig
|
|
259
|
-
:
|
|
498
|
+
: await readAgentConfigForTurn(
|
|
499
|
+
dispatch,
|
|
500
|
+
agentKey,
|
|
501
|
+
quickChatPerfStartedAt,
|
|
502
|
+
);
|
|
260
503
|
if (!agentConfig) {
|
|
261
504
|
return rejectWithValue(`Agent config not found for ID: ${agentKey}`);
|
|
262
505
|
}
|
|
@@ -486,6 +729,14 @@ export const streamAgentChatTurnHandler = async (
|
|
|
486
729
|
cliProvider: agentConfig.cliProvider || "copilot",
|
|
487
730
|
model: agentConfig.model || undefined,
|
|
488
731
|
systemPrompt: agentConfig.prompt || undefined,
|
|
732
|
+
reasoningEffort: agentConfig.reasoning_effort || undefined,
|
|
733
|
+
temperature: agentConfig.temperature,
|
|
734
|
+
topP: agentConfig.top_p,
|
|
735
|
+
frequencyPenalty: agentConfig.frequency_penalty,
|
|
736
|
+
presencePenalty: agentConfig.presence_penalty,
|
|
737
|
+
maxTokens: agentConfig.max_tokens,
|
|
738
|
+
enableThinking: agentConfig.enableThinking,
|
|
739
|
+
thinkingBudget: agentConfig.thinkingBudget,
|
|
489
740
|
},
|
|
490
741
|
);
|
|
491
742
|
|
|
@@ -557,6 +808,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
557
808
|
}
|
|
558
809
|
|
|
559
810
|
let accumulated = "";
|
|
811
|
+
let cliCapabilityWarnings: string[] = [];
|
|
560
812
|
const decoder = new TextDecoder();
|
|
561
813
|
const buildCliAssistantMessage = () => ({
|
|
562
814
|
id: messageId,
|
|
@@ -616,7 +868,9 @@ export const streamAgentChatTurnHandler = async (
|
|
|
616
868
|
...cliMessageMetadata,
|
|
617
869
|
}));
|
|
618
870
|
}
|
|
619
|
-
|
|
871
|
+
if (payload.done && Array.isArray(payload.warnings)) {
|
|
872
|
+
cliCapabilityWarnings = payload.warnings.filter((warning): warning is string => typeof warning === "string" && warning.trim().length > 0);
|
|
873
|
+
}
|
|
620
874
|
} catch {
|
|
621
875
|
// 忽略解析失败的行
|
|
622
876
|
}
|
|
@@ -628,6 +882,18 @@ export const streamAgentChatTurnHandler = async (
|
|
|
628
882
|
return;
|
|
629
883
|
}
|
|
630
884
|
|
|
885
|
+
if (cliCapabilityWarnings.length > 0) {
|
|
886
|
+
accumulated = appendCliCapabilityWarnings(accumulated, cliCapabilityWarnings);
|
|
887
|
+
dispatch(messageStreaming({
|
|
888
|
+
id: messageId,
|
|
889
|
+
dialogId,
|
|
890
|
+
dbKey: msgKey,
|
|
891
|
+
content: accumulated,
|
|
892
|
+
role: "assistant",
|
|
893
|
+
...cliMessageMetadata,
|
|
894
|
+
}));
|
|
895
|
+
}
|
|
896
|
+
|
|
631
897
|
// 持久化最终消息:用已有 ID,避免 prepareAndPersistMessage 重新生成 ID 导致重复
|
|
632
898
|
await persistMessageWithFixedId(dispatch, buildCliAssistantMessage());
|
|
633
899
|
} finally {
|
|
@@ -658,6 +924,49 @@ export const streamAgentChatTurnHandler = async (
|
|
|
658
924
|
dialogId,
|
|
659
925
|
});
|
|
660
926
|
|
|
927
|
+
if (isDesktopBuiltinNoloAgent(agentKey, agentConfig)) {
|
|
928
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
929
|
+
const desktopMessageMetadata = buildParallelMessageMetadata(
|
|
930
|
+
agentConfig,
|
|
931
|
+
runtimeOptions,
|
|
932
|
+
);
|
|
933
|
+
remoteTransientMessageId = messageId;
|
|
934
|
+
loopKey = `loop:${dialogId}`;
|
|
935
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
936
|
+
dispatch(messageStreaming({
|
|
937
|
+
id: messageId,
|
|
938
|
+
dialogId,
|
|
939
|
+
dbKey: msgKey,
|
|
940
|
+
content: "",
|
|
941
|
+
role: "assistant",
|
|
942
|
+
...desktopMessageMetadata,
|
|
943
|
+
}));
|
|
944
|
+
|
|
945
|
+
const localResult = await runDesktopAgentRuntimeTurn({
|
|
946
|
+
agentRef: agentConfig.dbKey || agentKey,
|
|
947
|
+
input: userInput,
|
|
948
|
+
continueDialogId: dialogId,
|
|
949
|
+
});
|
|
950
|
+
if (!localResult.ok) {
|
|
951
|
+
dispatch(removeTransientMessage(messageId));
|
|
952
|
+
remoteTransientMessageFinalized = true;
|
|
953
|
+
return rejectWithValue(localResult.error);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
dispatch(messageStreaming({
|
|
957
|
+
id: messageId,
|
|
958
|
+
dialogId,
|
|
959
|
+
dbKey: msgKey,
|
|
960
|
+
content: localResult.result.content || "",
|
|
961
|
+
role: "assistant",
|
|
962
|
+
...desktopMessageMetadata,
|
|
963
|
+
}));
|
|
964
|
+
remoteTransientMessageFinalized = true;
|
|
965
|
+
return {
|
|
966
|
+
usage: localResult.result.usage ?? undefined,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
661
970
|
const userInputText = extractAgentRunUserText(userInput);
|
|
662
971
|
|
|
663
972
|
const explicitServerBase =
|
|
@@ -956,6 +1265,9 @@ export const streamAgentChatTurnHandler = async (
|
|
|
956
1265
|
model: agentConfigForCall.model,
|
|
957
1266
|
responseApi: true,
|
|
958
1267
|
});
|
|
1268
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-post-static-entered", {
|
|
1269
|
+
responseApi: true,
|
|
1270
|
+
});
|
|
959
1271
|
|
|
960
1272
|
let appendTempUserInput = true;
|
|
961
1273
|
let currentParentMessageId = parentMessageId ?? undefined;
|
|
@@ -964,11 +1276,25 @@ export const streamAgentChatTurnHandler = async (
|
|
|
964
1276
|
if (w) w.__LOOP_STOP_REASON__ = null;
|
|
965
1277
|
|
|
966
1278
|
loopKey = `loop:${dialogId}`;
|
|
1279
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-adding", {
|
|
1280
|
+
responseApi: true,
|
|
1281
|
+
});
|
|
967
1282
|
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
1283
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-added", {
|
|
1284
|
+
responseApi: true,
|
|
1285
|
+
});
|
|
968
1286
|
|
|
969
1287
|
for (;;) {
|
|
1288
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-entered", {
|
|
1289
|
+
responseApi: true,
|
|
1290
|
+
});
|
|
970
1291
|
const requestParentMessageId = currentParentMessageId;
|
|
971
1292
|
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
1293
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-aborted-before-context", {
|
|
1294
|
+
loopControllerAborted: loopController.signal.aborted,
|
|
1295
|
+
thunkSignalAborted: thunkApi.signal.aborted,
|
|
1296
|
+
responseApi: true,
|
|
1297
|
+
});
|
|
972
1298
|
if (w) w.__LOOP_STOP_REASON__ = "aborted";
|
|
973
1299
|
break;
|
|
974
1300
|
}
|
|
@@ -976,6 +1302,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
976
1302
|
const loopState = getState() as RootState;
|
|
977
1303
|
const now = Date.now();
|
|
978
1304
|
if (now - startTime > MAX_TIME_MS) {
|
|
1305
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-timeout-before-context", {
|
|
1306
|
+
maxTimeMs: MAX_TIME_MS,
|
|
1307
|
+
elapsedMs: now - startTime,
|
|
1308
|
+
responseApi: true,
|
|
1309
|
+
});
|
|
979
1310
|
if (w) w.__LOOP_STOP_REASON__ = "timeout";
|
|
980
1311
|
break;
|
|
981
1312
|
}
|
|
@@ -985,18 +1316,60 @@ export const streamAgentChatTurnHandler = async (
|
|
|
985
1316
|
loopState,
|
|
986
1317
|
);
|
|
987
1318
|
if (accessError) {
|
|
1319
|
+
const accessErrorReason = classifyQuickChatAccessError(accessError);
|
|
1320
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-before-context", {
|
|
1321
|
+
hasAccessError: true,
|
|
1322
|
+
reason: accessErrorReason,
|
|
1323
|
+
responseApi: true,
|
|
1324
|
+
});
|
|
1325
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted) {
|
|
1326
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-finalized", {
|
|
1327
|
+
reason: accessErrorReason,
|
|
1328
|
+
responseApi: true,
|
|
1329
|
+
});
|
|
1330
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
1331
|
+
dispatch,
|
|
1332
|
+
runtimeDialogKey,
|
|
1333
|
+
agentKey,
|
|
1334
|
+
new Error(accessError),
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
988
1337
|
return rejectWithValue(accessError);
|
|
989
1338
|
}
|
|
990
1339
|
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
dispatch,
|
|
994
|
-
agentConfigForCall,
|
|
1340
|
+
const willSkipDynamicContext = canUseQuickChatEmptyDynamicContexts(
|
|
1341
|
+
quickChatPerfStartedAt,
|
|
995
1342
|
userInput,
|
|
996
1343
|
runtimeOptions,
|
|
997
|
-
|
|
998
|
-
dialogKey,
|
|
1344
|
+
currentDialog,
|
|
999
1345
|
);
|
|
1346
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
1347
|
+
hasRuntimeOptions: !!runtimeOptions,
|
|
1348
|
+
isSimpleTextInput: isSimpleTextInput(userInput),
|
|
1349
|
+
referenceKeyCount: currentDialog?.referenceKeys?.length ?? 0,
|
|
1350
|
+
willSkipDynamicContext,
|
|
1351
|
+
responseApi: true,
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
const dynamicContexts = willSkipDynamicContext
|
|
1355
|
+
? (logQuickChatPerfStage(
|
|
1356
|
+
quickChatPerfStartedAt,
|
|
1357
|
+
"stream-agent-dynamic-context-skipped",
|
|
1358
|
+
{
|
|
1359
|
+
reason: "simple-quick-chat-first-turn",
|
|
1360
|
+
responseApi: true,
|
|
1361
|
+
},
|
|
1362
|
+
), EMPTY_DYNAMIC_CONTEXTS)
|
|
1363
|
+
: await buildDynamicContextsForTurn(
|
|
1364
|
+
loopState,
|
|
1365
|
+
dispatch,
|
|
1366
|
+
agentConfigForCall,
|
|
1367
|
+
userInput,
|
|
1368
|
+
runtimeOptions,
|
|
1369
|
+
mergedContentCache,
|
|
1370
|
+
dialogKey,
|
|
1371
|
+
quickChatPerfStartedAt,
|
|
1372
|
+
);
|
|
1000
1373
|
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
1001
1374
|
responseApi: true,
|
|
1002
1375
|
});
|
|
@@ -1072,6 +1445,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1072
1445
|
dynamicMessageCount: dynamicMessages.length,
|
|
1073
1446
|
stableMessageCount: stableMessages.length,
|
|
1074
1447
|
});
|
|
1448
|
+
modelRequestStarted = true;
|
|
1075
1449
|
|
|
1076
1450
|
const meta: CompletionMeta = await sendOpenAIResponseRequest({
|
|
1077
1451
|
bodyData,
|
|
@@ -1162,6 +1536,9 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1162
1536
|
model: agentConfigForCall.model,
|
|
1163
1537
|
responseApi: false,
|
|
1164
1538
|
});
|
|
1539
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-post-static-entered", {
|
|
1540
|
+
responseApi: false,
|
|
1541
|
+
});
|
|
1165
1542
|
|
|
1166
1543
|
let appendTempUserInput = true;
|
|
1167
1544
|
let currentParentMessageId = parentMessageId ?? undefined;
|
|
@@ -1171,13 +1548,27 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1171
1548
|
|
|
1172
1549
|
if (!isRespModel) {
|
|
1173
1550
|
loopKey = `loop:${dialogId}`;
|
|
1551
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-adding", {
|
|
1552
|
+
responseApi: false,
|
|
1553
|
+
});
|
|
1174
1554
|
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
1555
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-added", {
|
|
1556
|
+
responseApi: false,
|
|
1557
|
+
});
|
|
1175
1558
|
}
|
|
1176
1559
|
|
|
1177
1560
|
for (;;) {
|
|
1561
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-entered", {
|
|
1562
|
+
responseApi: false,
|
|
1563
|
+
});
|
|
1178
1564
|
const requestParentMessageId = currentParentMessageId;
|
|
1179
1565
|
// 每轮开始前检查是否已中止
|
|
1180
1566
|
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
1567
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-aborted-before-context", {
|
|
1568
|
+
loopControllerAborted: loopController.signal.aborted,
|
|
1569
|
+
thunkSignalAborted: thunkApi.signal.aborted,
|
|
1570
|
+
responseApi: false,
|
|
1571
|
+
});
|
|
1181
1572
|
if (w) w.__LOOP_STOP_REASON__ = "aborted";
|
|
1182
1573
|
break;
|
|
1183
1574
|
}
|
|
@@ -1185,6 +1576,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1185
1576
|
const loopState = getState() as RootState;
|
|
1186
1577
|
const now = Date.now();
|
|
1187
1578
|
if (now - startTime > MAX_TIME_MS) {
|
|
1579
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-timeout-before-context", {
|
|
1580
|
+
maxTimeMs: MAX_TIME_MS,
|
|
1581
|
+
elapsedMs: now - startTime,
|
|
1582
|
+
responseApi: false,
|
|
1583
|
+
});
|
|
1188
1584
|
if (w) w.__LOOP_STOP_REASON__ = "timeout";
|
|
1189
1585
|
break;
|
|
1190
1586
|
}
|
|
@@ -1195,19 +1591,61 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1195
1591
|
loopState,
|
|
1196
1592
|
);
|
|
1197
1593
|
if (accessError) {
|
|
1594
|
+
const accessErrorReason = classifyQuickChatAccessError(accessError);
|
|
1595
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-before-context", {
|
|
1596
|
+
hasAccessError: true,
|
|
1597
|
+
reason: accessErrorReason,
|
|
1598
|
+
responseApi: false,
|
|
1599
|
+
});
|
|
1600
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted) {
|
|
1601
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-finalized", {
|
|
1602
|
+
reason: accessErrorReason,
|
|
1603
|
+
responseApi: false,
|
|
1604
|
+
});
|
|
1605
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
1606
|
+
dispatch,
|
|
1607
|
+
runtimeDialogKey,
|
|
1608
|
+
agentKey,
|
|
1609
|
+
new Error(accessError),
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1198
1612
|
return rejectWithValue(accessError);
|
|
1199
1613
|
}
|
|
1200
1614
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
loopState,
|
|
1204
|
-
dispatch,
|
|
1205
|
-
agentConfigForCall,
|
|
1615
|
+
const willSkipDynamicContext = canUseQuickChatEmptyDynamicContexts(
|
|
1616
|
+
quickChatPerfStartedAt,
|
|
1206
1617
|
userInput,
|
|
1207
1618
|
runtimeOptions,
|
|
1208
|
-
|
|
1209
|
-
dialogKey,
|
|
1619
|
+
currentDialog,
|
|
1210
1620
|
);
|
|
1621
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
1622
|
+
hasRuntimeOptions: !!runtimeOptions,
|
|
1623
|
+
isSimpleTextInput: isSimpleTextInput(userInput),
|
|
1624
|
+
referenceKeyCount: currentDialog?.referenceKeys?.length ?? 0,
|
|
1625
|
+
willSkipDynamicContext,
|
|
1626
|
+
responseApi: false,
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
// 🚀 优化:每轮只构建动态上下文(currentInput、history、editingContext、dialogSummary)
|
|
1630
|
+
const dynamicContexts = willSkipDynamicContext
|
|
1631
|
+
? (logQuickChatPerfStage(
|
|
1632
|
+
quickChatPerfStartedAt,
|
|
1633
|
+
"stream-agent-dynamic-context-skipped",
|
|
1634
|
+
{
|
|
1635
|
+
reason: "simple-quick-chat-first-turn",
|
|
1636
|
+
responseApi: false,
|
|
1637
|
+
},
|
|
1638
|
+
), EMPTY_DYNAMIC_CONTEXTS)
|
|
1639
|
+
: await buildDynamicContextsForTurn(
|
|
1640
|
+
loopState,
|
|
1641
|
+
dispatch,
|
|
1642
|
+
agentConfigForCall,
|
|
1643
|
+
userInput,
|
|
1644
|
+
runtimeOptions,
|
|
1645
|
+
mergedContentCache,
|
|
1646
|
+
dialogKey,
|
|
1647
|
+
quickChatPerfStartedAt,
|
|
1648
|
+
);
|
|
1211
1649
|
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
1212
1650
|
responseApi: false,
|
|
1213
1651
|
});
|
|
@@ -1286,6 +1724,27 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1286
1724
|
dynamicMessageCount: dynamicMessages.length,
|
|
1287
1725
|
stableMessageCount: stableMessages.length,
|
|
1288
1726
|
});
|
|
1727
|
+
modelRequestStarted = true;
|
|
1728
|
+
|
|
1729
|
+
const disableToolsForThisRequest =
|
|
1730
|
+
shouldDisableQuickChatToolsForDirectAnswer(
|
|
1731
|
+
quickChatPerfStartedAt,
|
|
1732
|
+
userInput,
|
|
1733
|
+
runtimeOptions,
|
|
1734
|
+
currentDialog,
|
|
1735
|
+
);
|
|
1736
|
+
if (disableToolsForThisRequest) {
|
|
1737
|
+
logQuickChatPerfStage(
|
|
1738
|
+
quickChatPerfStartedAt,
|
|
1739
|
+
"stream-agent-tools-disabled-for-direct-answer",
|
|
1740
|
+
{
|
|
1741
|
+
responseApi: false,
|
|
1742
|
+
toolCount: Array.isArray(agentConfigForCall.tools)
|
|
1743
|
+
? agentConfigForCall.tools.length
|
|
1744
|
+
: 0,
|
|
1745
|
+
},
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1289
1748
|
|
|
1290
1749
|
const meta: CompletionMeta = await sendOpenAICompletionsRequest({
|
|
1291
1750
|
bodyData,
|
|
@@ -1297,6 +1756,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1297
1756
|
agentConfigForCall,
|
|
1298
1757
|
runtimeOptions,
|
|
1299
1758
|
),
|
|
1759
|
+
disableToolsForThisRequest,
|
|
1300
1760
|
quickChatPerfStartedAt,
|
|
1301
1761
|
});
|
|
1302
1762
|
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
@@ -1383,6 +1843,19 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1383
1843
|
dispatch(removeTransientMessage(remoteTransientMessageId));
|
|
1384
1844
|
remoteTransientMessageFinalized = true;
|
|
1385
1845
|
}
|
|
1846
|
+
if (
|
|
1847
|
+
quickChatPerfStartedAt &&
|
|
1848
|
+
runtimeDialogKey &&
|
|
1849
|
+
!modelRequestStarted &&
|
|
1850
|
+
!remoteTransientMessageId
|
|
1851
|
+
) {
|
|
1852
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
1853
|
+
dispatch,
|
|
1854
|
+
runtimeDialogKey,
|
|
1855
|
+
agentKey,
|
|
1856
|
+
error,
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1386
1859
|
|
|
1387
1860
|
return rejectWithValue(
|
|
1388
1861
|
error?.message ||
|