nolo-cli 0.1.41 → 0.1.42
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 +27 -12
- package/agent-runtime/agentThread.ts +222 -0
- package/agent-runtime/agentThreadAdmission.ts +74 -0
- package/agent-runtime/dialogWritePlan.ts +42 -0
- package/agent-runtime/hostAdapter.ts +1 -0
- package/agent-runtime/index.ts +43 -0
- package/agent-runtime/localLoop.ts +31 -2
- package/agent-runtime/localToolPolicy.ts +1 -0
- package/agent-runtime/localWorkspaceTools.ts +1258 -78
- package/agent-runtime/noloWorkspaceTools.ts +20 -1
- package/agent-runtime/runtimeToolPolicy.ts +1 -5
- package/agent-runtime/runtimeToolSurface.ts +1 -0
- package/agentAliases.ts +29 -9
- package/agentInternalCommandEntries.ts +11 -3
- package/agentMachineCommands.ts +1 -0
- package/agentRecordCommands.ts +2 -2
- package/agentRecordHelpers.ts +59 -6
- package/agentRunCommand.ts +84 -19
- package/ai/agent/buildSystemPrompt.ts +14 -0
- package/ai/agent/cliExecutor.ts +202 -43
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/liveAudioModel.ts +1 -1
- package/ai/agent/streamAgentChatTurn.ts +154 -20
- package/ai/agent/streamAgentChatTurnUtils.ts +63 -0
- package/ai/ai.locale.ts +70 -0
- package/ai/chat/inlineImageUrlsForCustomProvider.ts +7 -2
- package/ai/chat/sendOpenAICompletionsRequest.ts +25 -1
- package/ai/llm/kimi.ts +0 -1
- package/ai/llm/openrouterModels.ts +27 -2
- package/ai/memory/policy.ts +49 -0
- package/ai/memory/queryShared.ts +10 -4
- package/ai/memory/recentRelationshipRecap.ts +4 -0
- package/ai/memory/remember.ts +48 -104
- package/ai/memory/scope.ts +74 -0
- package/ai/memory/types.ts +6 -0
- package/ai/memory/understanding.ts +10 -18
- package/ai/policy/runtimePolicy.ts +95 -23
- package/ai/tools/agent/agentTools.ts +0 -21
- package/ai/tools/agent/startAgentDialogTool.ts +2 -1
- package/ai/tools/index.ts +2 -20
- package/ai/tools/prepareTools.ts +171 -1
- package/ai/tools/readXhsProfileTool.ts +80 -69
- package/ai/tools/table/createTableTool.ts +11 -0
- package/ai/tools/toolPacks.ts +19 -0
- package/ai/types.ts +3 -0
- package/app/favorite/deletedFavoriteProjection.ts +31 -0
- package/app/hooks/deleteDbKey.ts +24 -3
- package/app/i18n/translations/interface.locale.ts +48 -16
- package/app/types/appSummary.ts +4 -0
- package/app/utils/desktopAgentRuntimeTurnClient.ts +2 -0
- package/auth/adminPermissions.ts +64 -4
- package/auth/routes.ts +5 -0
- package/chat/chat.locale.ts +4 -0
- package/chat/dialog/actions/handleSendMessageAction.ts +7 -2
- package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
- package/chat/dialog/dialogSlice.ts +117 -4
- package/chat/messages/activityCompletion.ts +97 -0
- package/chat/messages/imagePayloadPersistence.ts +22 -0
- package/chat/messages/messageContent.ts +20 -16
- package/chat/messages/messageSlice.ts +20 -1
- package/chat/messages/toolThunks.ts +32 -11
- package/chat/messages/types.ts +8 -1
- package/chat/messages/web/toolDisplayName.ts +686 -0
- package/cli/agentAliases.ts +29 -9
- package/cli/agentInternalCommandEntries.ts +11 -3
- package/cli/agentMachineCommands.ts +1 -0
- package/cli/agentRecordCommands.ts +2 -2
- package/cli/agentRecordHelpers.ts +59 -6
- package/cli/agentRunCommand.ts +84 -19
- package/cli/client/agentRun.ts +65 -253
- package/cli/client/localRuntimeAdapter.ts +179 -53
- package/cli/commandRegistry.ts +7 -4
- package/cli/dialogAttachmentCleanup.ts +151 -0
- package/cli/dialogCommands.ts +163 -61
- package/cli/dialogInternalCommandEntries.ts +1 -1
- package/cli/docCreateCommands.ts +345 -0
- package/cli/docInternalCommandEntries.ts +11 -0
- package/cli/docPageHelpers.ts +80 -0
- package/cli/docSkillHelpers.ts +67 -0
- package/cli/docSpaceHelpers.ts +46 -0
- package/cli/internalCommandEntries.ts +4 -0
- package/cli/machineCommands.ts +18 -34
- package/cli/memoryCommands.ts +253 -0
- package/cli/memoryInternalCommandEntries.ts +9 -0
- package/cli/qoderUsageProbe.ts +252 -0
- package/cli/scriptCommandEntries.ts +0 -3
- package/cli/secretScan.ts +31 -0
- package/cli/tableCommands.ts +248 -13
- package/cli/workflowInternalCommandEntries.ts +2 -3
- package/client/agentRun.test.ts +68 -84
- package/client/agentRun.ts +65 -253
- package/client/localRuntimeAdapter.test.ts +505 -74
- package/client/localRuntimeAdapter.ts +179 -53
- package/commandRegistry.ts +7 -4
- package/connector-experimental/capabilities.ts +10 -9
- package/create/space/sidebarVisibleTypes.ts +19 -0
- package/dialogAttachmentCleanup.ts +151 -0
- package/dialogCommands.ts +163 -61
- package/dialogInternalCommandEntries.ts +1 -1
- package/docCreateCommands.ts +345 -0
- package/docInternalCommandEntries.ts +11 -0
- package/docPageHelpers.ts +80 -0
- package/docSkillHelpers.ts +67 -0
- package/docSpaceHelpers.ts +46 -0
- package/integrations/google/models.ts +4 -4
- package/integrations/openai/generateOpenAIRequestBody.ts +0 -3
- package/integrations/xhs-reader/types.ts +36 -3
- package/integrations/xhs-reader/url.ts +24 -1
- package/internalCommandEntries.ts +4 -0
- package/machineCommands.ts +18 -34
- package/memoryCommands.ts +253 -0
- package/memoryInternalCommandEntries.ts +9 -0
- package/package.json +133 -114
- package/qoderUsageProbe.ts +252 -0
- package/render/contentIcon/types.ts +167 -0
- package/render/page/docSlice.ts +30 -3
- package/render/page/types.ts +2 -0
- package/render/table/createTableAction.ts +4 -0
- package/render/table/tableSlice.ts +61 -0
- package/render/table/types.ts +10 -0
- package/scriptCommandEntries.ts +0 -3
- package/secretScan.ts +31 -0
- package/server/handlers/agentRun/types.ts +24 -23
- package/tableCommands.ts +248 -13
- package/workflowInternalCommandEntries.ts +2 -3
- package/ai/tools/agent/taskRunTool.ts +0 -112
- package/cli/client/taskRunPrompt.ts +0 -27
- package/cli/taskRunCommand.ts +0 -265
- package/client/taskRunPrompt.ts +0 -27
- package/integrations/xhs-reader/analyze/profileAnalyzer.ts +0 -205
- package/integrations/xhs-reader/backends/playwrightProfileCollector.ts +0 -624
- package/integrations/xhs-reader/bridge/readXhsProfileWithBridge.ts +0 -227
- package/integrations/xhs-reader/client/xhsWebApiClient.ts +0 -229
- package/integrations/xhs-reader/normalize.ts +0 -390
- package/integrations/xhs-reader/orchestrator.ts +0 -178
- package/taskRunCommand.ts +0 -265
|
@@ -73,9 +73,6 @@ import { getIsDesktopApp } from "../../app/utils/env";
|
|
|
73
73
|
import { runDesktopAgentRuntimeTurn } from "../../app/utils/desktopAgentRuntimeTurnClient";
|
|
74
74
|
import { resolveRuntimeToolSurfaceForAgent } from "../../agent-runtime/runtimeToolSurface";
|
|
75
75
|
|
|
76
|
-
const BUILTIN_NOLO_AGENT_ID = "01NOLOAPPBLD000000019KCKT0";
|
|
77
|
-
const BUILTIN_NOLO_AGENT_KEY = `agent-pub-${BUILTIN_NOLO_AGENT_ID}`;
|
|
78
|
-
|
|
79
76
|
const buildParallelMessageMetadata = (
|
|
80
77
|
agentConfig: Pick<Agent, "dbKey" | "name">,
|
|
81
78
|
runtimeOptions?: AgentRuntimeOptions,
|
|
@@ -116,23 +113,141 @@ const filterMessagesForParallelBranch = (
|
|
|
116
113
|
});
|
|
117
114
|
};
|
|
118
115
|
|
|
119
|
-
const
|
|
120
|
-
|
|
116
|
+
const buildDesktopRuntimeToolMessagesForUi = ({
|
|
117
|
+
dialogId,
|
|
118
|
+
turnMessages,
|
|
119
|
+
}: {
|
|
120
|
+
dialogId: string;
|
|
121
|
+
turnMessages?: any[];
|
|
122
|
+
}) => {
|
|
123
|
+
if (!Array.isArray(turnMessages) || turnMessages.length === 0) return [];
|
|
124
|
+
|
|
125
|
+
const toolNamesByCallId = new Map<string, string>();
|
|
126
|
+
const activityByCallId = new Map<string, any>();
|
|
127
|
+
const projected: any[] = [];
|
|
128
|
+
|
|
129
|
+
for (const message of turnMessages) {
|
|
130
|
+
if (Array.isArray(message?.tool_calls)) {
|
|
131
|
+
for (const call of message.tool_calls) {
|
|
132
|
+
const callId = typeof call?.id === "string" ? call.id.trim() : "";
|
|
133
|
+
const toolName =
|
|
134
|
+
typeof call?.function?.name === "string"
|
|
135
|
+
? call.function.name.trim()
|
|
136
|
+
: "";
|
|
137
|
+
if (callId && toolName) toolNamesByCallId.set(callId, toolName);
|
|
138
|
+
|
|
139
|
+
// Extract _activity from tool call arguments for UI projection
|
|
140
|
+
if (callId) {
|
|
141
|
+
try {
|
|
142
|
+
const args =
|
|
143
|
+
typeof call?.function?.arguments === "string"
|
|
144
|
+
? JSON.parse(call.function.arguments)
|
|
145
|
+
: call?.function?.arguments;
|
|
146
|
+
const rawActivity =
|
|
147
|
+
args &&
|
|
148
|
+
typeof args === "object" &&
|
|
149
|
+
!Array.isArray(args)
|
|
150
|
+
? args._activity
|
|
151
|
+
: undefined;
|
|
152
|
+
const activity =
|
|
153
|
+
rawActivity &&
|
|
154
|
+
typeof rawActivity === "object" &&
|
|
155
|
+
!Array.isArray(rawActivity)
|
|
156
|
+
? rawActivity as Record<string, unknown>
|
|
157
|
+
: undefined;
|
|
158
|
+
const legacyTitle =
|
|
159
|
+
typeof activity?.title === "string" && activity.title.trim();
|
|
160
|
+
const action = activity?.action;
|
|
161
|
+
const actionTitle =
|
|
162
|
+
action &&
|
|
163
|
+
typeof action === "object" &&
|
|
164
|
+
!Array.isArray(action) &&
|
|
165
|
+
typeof (action as Record<string, unknown>).title === "string" &&
|
|
166
|
+
((action as Record<string, unknown>).title as string).trim();
|
|
167
|
+
const plan = activity?.plan;
|
|
168
|
+
const hasPlan =
|
|
169
|
+
plan &&
|
|
170
|
+
typeof plan === "object" &&
|
|
171
|
+
!Array.isArray(plan) &&
|
|
172
|
+
Array.isArray((plan as Record<string, unknown>).phases) &&
|
|
173
|
+
((plan as Record<string, unknown>).phases as unknown[]).some((phase) => {
|
|
174
|
+
if (!phase || typeof phase !== "object" || Array.isArray(phase)) return false;
|
|
175
|
+
const rawPhase = phase as Record<string, unknown>;
|
|
176
|
+
return typeof rawPhase.title === "string" && !!rawPhase.title.trim();
|
|
177
|
+
});
|
|
178
|
+
if (activity && (legacyTitle || actionTitle || hasPlan)) {
|
|
179
|
+
activityByCallId.set(callId, args._activity);
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
// Ignore malformed tool call arguments
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (message?.role !== "tool") continue;
|
|
190
|
+
const toolCallId =
|
|
191
|
+
typeof message.tool_call_id === "string"
|
|
192
|
+
? message.tool_call_id.trim()
|
|
193
|
+
: "";
|
|
194
|
+
const metadata =
|
|
195
|
+
message.tool_result_metadata &&
|
|
196
|
+
typeof message.tool_result_metadata === "object"
|
|
197
|
+
? message.tool_result_metadata
|
|
198
|
+
: {};
|
|
199
|
+
const metadataToolName =
|
|
200
|
+
typeof metadata.toolName === "string" ? metadata.toolName.trim() : "";
|
|
201
|
+
const toolName =
|
|
202
|
+
metadataToolName ||
|
|
203
|
+
(toolCallId ? toolNamesByCallId.get(toolCallId) : "") ||
|
|
204
|
+
"tool";
|
|
205
|
+
const { key: dbKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
206
|
+
|
|
207
|
+
// Resolve activity: prefer tool result metadata.activity, fall back to tool call _activity
|
|
208
|
+
const resultActivity =
|
|
209
|
+
metadata.activity && typeof metadata.activity === "object"
|
|
210
|
+
? metadata.activity
|
|
211
|
+
: undefined;
|
|
212
|
+
const callActivity = toolCallId
|
|
213
|
+
? activityByCallId.get(toolCallId)
|
|
214
|
+
: undefined;
|
|
215
|
+
const activity = resultActivity || callActivity;
|
|
216
|
+
|
|
217
|
+
// Merge activity into metadata so UI readers (ToolMessageGroup / extractActivityLines)
|
|
218
|
+
// that look at message.metadata.activity see it regardless of surface.
|
|
219
|
+
const mergedMetadata = activity
|
|
220
|
+
? { ...metadata, activity }
|
|
221
|
+
: metadata;
|
|
222
|
+
|
|
223
|
+
projected.push({
|
|
224
|
+
id: messageId,
|
|
225
|
+
dialogId,
|
|
226
|
+
dbKey,
|
|
227
|
+
role: "tool",
|
|
228
|
+
content: message.content ?? "",
|
|
229
|
+
isStreaming: false,
|
|
230
|
+
toolName,
|
|
231
|
+
...(toolCallId ? { toolCallId } : {}),
|
|
232
|
+
...(Object.keys(mergedMetadata).length ? { metadata: mergedMetadata } : {}),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return projected;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const extractDesktopRuntimeToolCallsForUi = (turnMessages?: any[]) => {
|
|
240
|
+
if (!Array.isArray(turnMessages) || turnMessages.length === 0) return [];
|
|
241
|
+
return turnMessages.flatMap((message) =>
|
|
242
|
+
Array.isArray(message?.tool_calls) ? message.tool_calls : []
|
|
243
|
+
);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const shouldUseDesktopLocalRuntime = (
|
|
121
247
|
agentConfig: Partial<Agent> | null | undefined,
|
|
122
248
|
) => {
|
|
123
249
|
if (!getIsDesktopApp()) return false;
|
|
124
|
-
|
|
125
|
-
requestedAgentKey,
|
|
126
|
-
agentConfig?.dbKey,
|
|
127
|
-
agentConfig?.agentKey,
|
|
128
|
-
agentConfig?.id,
|
|
129
|
-
];
|
|
130
|
-
return candidates.some((value) =>
|
|
131
|
-
typeof value === "string" &&
|
|
132
|
-
(value === BUILTIN_NOLO_AGENT_KEY ||
|
|
133
|
-
value === BUILTIN_NOLO_AGENT_ID ||
|
|
134
|
-
value.endsWith(`-${BUILTIN_NOLO_AGENT_ID}`))
|
|
135
|
-
);
|
|
250
|
+
return agentConfig?.apiSource !== "cli";
|
|
136
251
|
};
|
|
137
252
|
|
|
138
253
|
const resolveWebAgentRuntimeToolSurface = (
|
|
@@ -261,6 +376,7 @@ const EMPTY_DYNAMIC_CONTEXTS = {
|
|
|
261
376
|
historyContext: "",
|
|
262
377
|
editingContext: null,
|
|
263
378
|
appWorkingMemory: null,
|
|
379
|
+
memoryOverlay: null,
|
|
264
380
|
dialogSummary: null,
|
|
265
381
|
proactiveSummary: null,
|
|
266
382
|
referenceKeys: [],
|
|
@@ -454,9 +570,11 @@ const canUseQuickChatEmptyDynamicContexts = (
|
|
|
454
570
|
userInput: string | any[],
|
|
455
571
|
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
456
572
|
dialogConfig: DialogConfig | null,
|
|
573
|
+
agentConfig?: Agent | null,
|
|
457
574
|
) => {
|
|
458
575
|
if (!quickChatPerfStartedAt) return false;
|
|
459
576
|
if (runtimeOptions) return false;
|
|
577
|
+
if (agentConfig?.tools?.includes("rememberMemory")) return false;
|
|
460
578
|
if (!isSimpleTextInput(userInput)) return false;
|
|
461
579
|
if (dialogConfig?.referenceKeys?.length) return false;
|
|
462
580
|
return true;
|
|
@@ -478,6 +596,7 @@ const shouldDisableQuickChatToolsForDirectAnswer = (
|
|
|
478
596
|
userInput,
|
|
479
597
|
runtimeOptions,
|
|
480
598
|
dialogConfig,
|
|
599
|
+
undefined,
|
|
481
600
|
)) {
|
|
482
601
|
return false;
|
|
483
602
|
}
|
|
@@ -609,7 +728,8 @@ export const streamAgentChatTurnHandler = async (
|
|
|
609
728
|
// runtimeBinding selects the bound machine. Without it, custom provider
|
|
610
729
|
// requests stay on the normal OpenAI-compatible path, where
|
|
611
730
|
// useServerProxy decides server proxy vs current-client direct fetch.
|
|
612
|
-
if (agentConfig.apiSource === "cli" || boundMachineId) {
|
|
731
|
+
if (agentConfig.apiSource === "cli" || (boundMachineId && !getIsDesktopApp())) {
|
|
732
|
+
console.info("[streamAgentChatTurn] Triggered CLI/machine route. apiSource:", agentConfig.apiSource, "boundMachineId:", boundMachineId, "agentKey:", agentKey);
|
|
613
733
|
const currentState = getState() as RootState;
|
|
614
734
|
const w =
|
|
615
735
|
typeof globalThis !== "undefined" && (globalThis as any).window
|
|
@@ -859,6 +979,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
859
979
|
};
|
|
860
980
|
|
|
861
981
|
const initialSessionId = await ensureCliSession();
|
|
982
|
+
console.info("[streamAgentChatTurn] Calling CLI turn stream. Session ID:", initialSessionId);
|
|
862
983
|
let resp = await createCliChatTurnStream(
|
|
863
984
|
{
|
|
864
985
|
getState,
|
|
@@ -872,8 +993,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
872
993
|
);
|
|
873
994
|
|
|
874
995
|
if (!resp.ok && resp.status === 404) {
|
|
996
|
+
console.warn("[streamAgentChatTurn] CLI Session 404. Re-creating session...");
|
|
875
997
|
cliSessionId = null;
|
|
876
998
|
const renewedSessionId = await ensureCliSession();
|
|
999
|
+
console.info("[streamAgentChatTurn] Retrying CLI turn stream. Session ID:", renewedSessionId);
|
|
877
1000
|
resp = await createCliChatTurnStream(
|
|
878
1001
|
{
|
|
879
1002
|
getState,
|
|
@@ -889,6 +1012,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
889
1012
|
|
|
890
1013
|
if (!resp.ok) {
|
|
891
1014
|
const err = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
1015
|
+
console.error("[streamAgentChatTurn] Local CLI fetch failed. Status:", resp.status, "Error details:", err);
|
|
892
1016
|
return rejectWithValue(err.error || "CLI 执行失败");
|
|
893
1017
|
}
|
|
894
1018
|
|
|
@@ -1015,7 +1139,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1015
1139
|
dialogId,
|
|
1016
1140
|
});
|
|
1017
1141
|
|
|
1018
|
-
if (
|
|
1142
|
+
if (shouldUseDesktopLocalRuntime(agentConfig)) {
|
|
1019
1143
|
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
1020
1144
|
const desktopMessageMetadata = buildParallelMessageMetadata(
|
|
1021
1145
|
agentConfig,
|
|
@@ -1044,6 +1168,14 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1044
1168
|
return rejectWithValue(localResult.error);
|
|
1045
1169
|
}
|
|
1046
1170
|
|
|
1171
|
+
const desktopTurnMessages = (localResult.result as any).turnMessages;
|
|
1172
|
+
for (const toolMessage of buildDesktopRuntimeToolMessagesForUi({
|
|
1173
|
+
dialogId,
|
|
1174
|
+
turnMessages: desktopTurnMessages,
|
|
1175
|
+
})) {
|
|
1176
|
+
dispatch(messageStreaming(toolMessage));
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1047
1179
|
dispatch(messageStreaming({
|
|
1048
1180
|
id: messageId,
|
|
1049
1181
|
dialogId,
|
|
@@ -1066,7 +1198,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1066
1198
|
dialogId,
|
|
1067
1199
|
dialogKey,
|
|
1068
1200
|
reasoningBuffer: "",
|
|
1069
|
-
toolCalls:
|
|
1201
|
+
toolCalls: extractDesktopRuntimeToolCallsForUi(desktopTurnMessages),
|
|
1070
1202
|
messageMetadata: desktopMessageMetadata,
|
|
1071
1203
|
})).unwrap();
|
|
1072
1204
|
remoteTransientMessageFinalized = true;
|
|
@@ -1475,6 +1607,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1475
1607
|
userInput,
|
|
1476
1608
|
runtimeOptions,
|
|
1477
1609
|
currentDialog,
|
|
1610
|
+
agentConfigForCall,
|
|
1478
1611
|
);
|
|
1479
1612
|
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
1480
1613
|
hasRuntimeOptions: !!runtimeOptions,
|
|
@@ -1747,6 +1880,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1747
1880
|
userInput,
|
|
1748
1881
|
runtimeOptions,
|
|
1749
1882
|
currentDialog,
|
|
1883
|
+
agentConfigForCall,
|
|
1750
1884
|
);
|
|
1751
1885
|
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
1752
1886
|
hasRuntimeOptions: !!runtimeOptions,
|
|
@@ -58,6 +58,7 @@ import { buildRecentAppToolMemory } from "./appWorkingMemory";
|
|
|
58
58
|
import type { AgentRuntimeOptions } from "./types";
|
|
59
59
|
import { getModelConfig, getProviderByModelName, type Provider } from "../llm/providers";
|
|
60
60
|
import type { ImageGenerationState } from "../../chat/messages/types";
|
|
61
|
+
import { resolveToolBaseUrl } from "../tools/toolApiClient";
|
|
61
62
|
|
|
62
63
|
const BROWSER_UNAVAILABLE_CORE_TOOLS = new Set([
|
|
63
64
|
"queryModelUsage",
|
|
@@ -418,11 +419,65 @@ export interface DynamicContexts {
|
|
|
418
419
|
historyContext: string;
|
|
419
420
|
editingContext: string | null;
|
|
420
421
|
appWorkingMemory: string | null;
|
|
422
|
+
memoryOverlay: string | null;
|
|
421
423
|
dialogSummary: string | null;
|
|
422
424
|
proactiveSummary: string | null;
|
|
423
425
|
referenceKeys: string[];
|
|
424
426
|
}
|
|
425
427
|
|
|
428
|
+
export const fetchMemoryOverlayContext = async (
|
|
429
|
+
state: RootState,
|
|
430
|
+
agentConfig: Agent,
|
|
431
|
+
userInput: string | any[],
|
|
432
|
+
dialogConfig?: DialogConfig,
|
|
433
|
+
): Promise<string | null> => {
|
|
434
|
+
const token = typeof (state as any)?.auth?.currentToken === "string"
|
|
435
|
+
? (state as any).auth.currentToken
|
|
436
|
+
: null;
|
|
437
|
+
const currentServer = typeof (state as any)?.settings?.currentServer === "string"
|
|
438
|
+
? (state as any).settings.currentServer
|
|
439
|
+
: null;
|
|
440
|
+
const agentKey = typeof agentConfig.dbKey === "string" && agentConfig.dbKey.trim()
|
|
441
|
+
? agentConfig.dbKey.trim()
|
|
442
|
+
: "";
|
|
443
|
+
if (!token || !currentServer || !agentKey) return null;
|
|
444
|
+
|
|
445
|
+
const baseUrl = resolveToolBaseUrl(currentServer);
|
|
446
|
+
if (!baseUrl) return null;
|
|
447
|
+
|
|
448
|
+
const inputText = typeof userInput === "string"
|
|
449
|
+
? userInput
|
|
450
|
+
: JSON.stringify(userInput ?? "");
|
|
451
|
+
const spaceId =
|
|
452
|
+
typeof (dialogConfig as any)?.spaceId === "string" && (dialogConfig as any).spaceId.trim()
|
|
453
|
+
? (dialogConfig as any).spaceId.trim()
|
|
454
|
+
: typeof (state as any)?.space?.currentSpaceId === "string" && (state as any).space.currentSpaceId.trim()
|
|
455
|
+
? (state as any).space.currentSpaceId.trim()
|
|
456
|
+
: undefined;
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
const response = await fetch(`${baseUrl}/api/memory/query`, {
|
|
460
|
+
method: "POST",
|
|
461
|
+
headers: {
|
|
462
|
+
"Content-Type": "application/json",
|
|
463
|
+
Authorization: `Bearer ${token}`,
|
|
464
|
+
},
|
|
465
|
+
body: JSON.stringify({
|
|
466
|
+
agentKey,
|
|
467
|
+
userInput: inputText,
|
|
468
|
+
...(spaceId ? { spaceId } : {}),
|
|
469
|
+
}),
|
|
470
|
+
});
|
|
471
|
+
if (!response.ok) return null;
|
|
472
|
+
const payload = await response.json().catch(() => null);
|
|
473
|
+
return typeof payload?.promptBlock === "string" && payload.promptBlock.trim()
|
|
474
|
+
? payload.promptBlock.trim()
|
|
475
|
+
: null;
|
|
476
|
+
} catch {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
426
481
|
|
|
427
482
|
/**
|
|
428
483
|
* 构建静态上下文(Loop 外调用一次)
|
|
@@ -644,12 +699,19 @@ export const buildDynamicContexts = async (
|
|
|
644
699
|
currentDialogMessages,
|
|
645
700
|
currentDialogToolRuns,
|
|
646
701
|
);
|
|
702
|
+
const memoryOverlay = await fetchMemoryOverlayContext(
|
|
703
|
+
state,
|
|
704
|
+
agentConfig,
|
|
705
|
+
userInput,
|
|
706
|
+
dialogConfig,
|
|
707
|
+
);
|
|
647
708
|
|
|
648
709
|
return {
|
|
649
710
|
currentInputContext: formattedCurrentInputContext.trim() || null,
|
|
650
711
|
historyContext: joinMapValues(historyMap),
|
|
651
712
|
editingContext,
|
|
652
713
|
appWorkingMemory,
|
|
714
|
+
memoryOverlay,
|
|
653
715
|
dialogSummary,
|
|
654
716
|
proactiveSummary: dialogConfig?.proactiveSummary || null,
|
|
655
717
|
referenceKeys: dialogConfig?.referenceKeys || [],
|
|
@@ -672,6 +734,7 @@ export const mergeContexts = (
|
|
|
672
734
|
historyContext: dynamicCtx.historyContext || undefined,
|
|
673
735
|
editingContext: dynamicCtx.editingContext,
|
|
674
736
|
appWorkingMemory: dynamicCtx.appWorkingMemory,
|
|
737
|
+
memoryOverlay: dynamicCtx.memoryOverlay,
|
|
675
738
|
dialogSummary: dynamicCtx.dialogSummary,
|
|
676
739
|
proactiveSummary: dynamicCtx.proactiveSummary,
|
|
677
740
|
referenceKeys: dynamicCtx.referenceKeys,
|
package/ai/ai.locale.ts
CHANGED
|
@@ -289,6 +289,41 @@ export default {
|
|
|
289
289
|
max_tokens: "Maximum tokens for a single response. Suggestion: Do not set this unless you have a specific need (e.g., strict limit for very short replies).",
|
|
290
290
|
reasoning_effort: "Reasoning effort. Higher values are slower but more rigorous."
|
|
291
291
|
}
|
|
292
|
+
},
|
|
293
|
+
checkEnv: {
|
|
294
|
+
description: "Perform environment check and return structured results. check='context' shows platform, cwd and shells; check='build' verifies compile/build status.",
|
|
295
|
+
params: {
|
|
296
|
+
check: "The check item: 'context' returns current running environment, 'build' runs a build compile check.",
|
|
297
|
+
key: "Optional: probe a specific local tool (e.g. gemini, node, npm, bun, python, git, shell) without building."
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
deleteSpaces: {
|
|
301
|
+
description: "Find and delete spaces owned by the current user. Warning: first call returns confirmation preview, deletion only executes after confirmation.",
|
|
302
|
+
params: {
|
|
303
|
+
query: "Filter term for space search, can be space name or space ID (when combined with matchMode='spaceId').",
|
|
304
|
+
matchMode: "Match mode: prefix (name prefix), exact (full name match), contains (name contains), spaceId (match by ID). Default prefix.",
|
|
305
|
+
confirmedSpaceIds: "Optional list of space IDs to confirm for deletion. If omitted, all deletable spaces in the preview will be deleted."
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
listUserSpaces: {
|
|
309
|
+
description: "List all spaces that the current user belongs to, including space name, ID and role.",
|
|
310
|
+
params: {
|
|
311
|
+
brief: "If true, returns only brief summary to save context/tokens."
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
read: {
|
|
315
|
+
description: "Read content by its database key. Useful to query page contents, dialog history, or table records.",
|
|
316
|
+
params: {
|
|
317
|
+
dbKey: "The database key of the target record, e.g. PAGE-xxx, dialog-xxx, table-xxx.",
|
|
318
|
+
waitRemote: "If true, forces a remote synchronization read to fetch latest updates."
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
rememberMemory: {
|
|
322
|
+
description: "Write important user preferences or shared space agreements into episodic memory for long-term recall.",
|
|
323
|
+
params: {
|
|
324
|
+
content: "The memory content to remember, e.g., 'User prefers dark mode and uses TypeScript.'",
|
|
325
|
+
tags: "Optional tag array for classification and quick search."
|
|
326
|
+
}
|
|
292
327
|
}
|
|
293
328
|
},
|
|
294
329
|
},
|
|
@@ -580,6 +615,41 @@ export default {
|
|
|
580
615
|
max_tokens: "单次回答的最大 token 数。建议:除非有特殊需求(如强制控制回复极短),否则不要设置此项。",
|
|
581
616
|
reasoning_effort: "推理强度,数值越高越慢但更严谨。"
|
|
582
617
|
}
|
|
618
|
+
},
|
|
619
|
+
checkEnv: {
|
|
620
|
+
description: "执行环境检查并返回结构化结果。check='context' 可查看当前平台、工作目录与可用 shell;check='build' 可快速验证是否可编译。",
|
|
621
|
+
params: {
|
|
622
|
+
check: "检查项:context 返回当前运行环境;build 执行构建检查。",
|
|
623
|
+
key: "可选:探测某个本地工具,不执行 build。常用值如 gemini、node、npm、bun、python、git、shell。"
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
deleteSpaces: {
|
|
627
|
+
description: "按名称或 ID 查找并删除当前用户拥有的 Space。这是危险操作:第一次调用会返回待删除列表并等待用户确认;确认后才执行删除。",
|
|
628
|
+
params: {
|
|
629
|
+
query: "要删除的 Space 筛选词,例如 rn_owner_verify_0504,或配合 matchMode=spaceId 传入 space id。",
|
|
630
|
+
matchMode: "匹配方式。prefix=名称前缀,exact=名称完全匹配,contains=名称包含,spaceId=按 Space ID 匹配。默认 prefix。",
|
|
631
|
+
confirmedSpaceIds: "确认阶段要删除的 Space ID 列表。不传时,用户点击确认会删除预览列表中的全部可删除项。"
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
listUserSpaces: {
|
|
635
|
+
description: "列出当前用户拥有的或加入的所有 Space,返回 Space 名称、ID、成员角色等信息。",
|
|
636
|
+
params: {
|
|
637
|
+
brief: "是否返回精简信息。若为 true,只包含基本元信息,可节省上下文 Token。"
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
read: {
|
|
641
|
+
description: "根据数据库 Key 读取任意类型的数据记录(页面内容、对话历史、表格配置等)。",
|
|
642
|
+
params: {
|
|
643
|
+
dbKey: "要读取的记录 Key,例如 PAGE-xxx, dialog-xxx, table-xxx。",
|
|
644
|
+
waitRemote: "是否等待从远端同步最新数据后返回。默认 false。"
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
rememberMemory: {
|
|
648
|
+
description: "将值得长期保留的用户偏好或空间共识写入一条 episodic memory 记忆,供后续对话长期检索。",
|
|
649
|
+
params: {
|
|
650
|
+
content: "具体的记忆内容,例如:'用户更偏好使用 TypeScript 进行编码开发。'",
|
|
651
|
+
tags: "给这条记忆打上的标签数组,用于未来的归类 and 检索。"
|
|
652
|
+
}
|
|
583
653
|
}
|
|
584
654
|
},
|
|
585
655
|
},
|
|
@@ -13,11 +13,16 @@ type InlineOptions = {
|
|
|
13
13
|
const FILE_CONTENT_PATH = "/api/v1/db/file/content/";
|
|
14
14
|
|
|
15
15
|
export const shouldInlineImageUrlsForAgent = (
|
|
16
|
-
agentConfig:
|
|
16
|
+
agentConfig:
|
|
17
|
+
| { apiSource?: string | null; model?: string | null; provider?: string | null }
|
|
18
|
+
| null
|
|
19
|
+
| undefined,
|
|
17
20
|
) => {
|
|
18
21
|
const apiSource = agentConfig?.apiSource?.toLowerCase();
|
|
19
22
|
const provider = agentConfig?.provider?.toLowerCase();
|
|
20
|
-
|
|
23
|
+
const model = agentConfig?.model?.toLowerCase();
|
|
24
|
+
if (apiSource === "custom" || provider === "custom") return true;
|
|
25
|
+
return provider === "openrouter" && model === "minimax/minimax-m3";
|
|
21
26
|
};
|
|
22
27
|
|
|
23
28
|
const isInlineCandidate = (url: string) =>
|
|
@@ -118,6 +118,22 @@ function formatStreamErrorMessage(data: any): string {
|
|
|
118
118
|
return rawMessage;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
function getChoiceFinishErrorMessage(data: any, choice: any): string | null {
|
|
122
|
+
if (choice?.error || choice?.message || choice?.code) {
|
|
123
|
+
const message = formatStreamErrorMessage(choice);
|
|
124
|
+
if (message && message !== "Unknown error") return message;
|
|
125
|
+
}
|
|
126
|
+
if (data?.error || data?.message || data?.code) {
|
|
127
|
+
const message = formatStreamErrorMessage(data);
|
|
128
|
+
if (message && message !== "Unknown error") return message;
|
|
129
|
+
}
|
|
130
|
+
const messageContent = choice?.message?.content;
|
|
131
|
+
if (typeof messageContent === "string" && messageContent.trim()) {
|
|
132
|
+
return messageContent.trim();
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
121
137
|
/** 单次流式请求过程中的全部中间状态(显式 state) */
|
|
122
138
|
type StreamState = {
|
|
123
139
|
contentBuffer: MessageContentPart[];
|
|
@@ -854,11 +870,19 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
854
870
|
// 无论是否 handoff,都 finalize 当前 assistant 消息(stub)
|
|
855
871
|
streamState = await finalizeStream(streamState, finalizeCtx);
|
|
856
872
|
} else if (finishReason !== "stop") {
|
|
873
|
+
const finishErrorMessage =
|
|
874
|
+
finishReason === "error"
|
|
875
|
+
? getChoiceFinishErrorMessage(data, choice)
|
|
876
|
+
: null;
|
|
857
877
|
streamState = {
|
|
858
878
|
...streamState,
|
|
859
879
|
contentBuffer: appendTextChunk(
|
|
860
880
|
streamState.contentBuffer,
|
|
861
|
-
|
|
881
|
+
finishErrorMessage
|
|
882
|
+
? `\n[API Error] Error: ${finishErrorMessage}`
|
|
883
|
+
: finishReason === "error"
|
|
884
|
+
? "\n[API Error] Error: 模型响应以 error 结束,但上游未返回具体错误。请重试,或切换到其他支持图片输入的模型。"
|
|
885
|
+
: `\n[流结束原因: ${finishReason}]`
|
|
862
886
|
),
|
|
863
887
|
};
|
|
864
888
|
}
|
package/ai/llm/kimi.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export const FIREWORKS_KIMI_LATEST_MODEL = "accounts/fireworks/models/kimi-latest";
|
|
2
2
|
export const FIREWORKS_KIMI_CURRENT_MODEL = "accounts/fireworks/models/kimi-k2p6";
|
|
3
3
|
export const DEEPINFRA_KIMI_FALLBACK_MODEL = "moonshotai/Kimi-K2.6";
|
|
4
|
-
export const OPENROUTER_KIMI_FALLBACK_MODEL = "moonshotai/kimi-k2.6";
|
|
5
4
|
export const KIMI_PLATFORM_FALLBACK_STATUSES = [401, 402, 429, 500, 502, 503, 504];
|
|
6
5
|
|
|
7
6
|
export const isFireworksKimiModel = (model?: string | null): boolean =>
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Model } from "./types";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const OPENROUTER_PRICE_MULTIPLIER = 7;
|
|
4
|
+
|
|
5
|
+
export const OPENROUTER_MODELS: Model[] = [
|
|
6
|
+
{
|
|
7
|
+
name: "x-ai/grok-4.3",
|
|
8
|
+
displayName: "xAI: Grok 4.3",
|
|
9
|
+
hasVision: true,
|
|
10
|
+
contextWindow: 1_000_000,
|
|
11
|
+
supportsTool: true,
|
|
12
|
+
supportsReasoningEffort: true,
|
|
13
|
+
price: {
|
|
14
|
+
input: 1.25 * OPENROUTER_PRICE_MULTIPLIER,
|
|
15
|
+
output: 2.5 * OPENROUTER_PRICE_MULTIPLIER,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "minimax/minimax-m3",
|
|
20
|
+
displayName: "MiniMax: MiniMax M3",
|
|
21
|
+
hasVision: true,
|
|
22
|
+
contextWindow: 1_000_000,
|
|
23
|
+
supportsTool: true,
|
|
24
|
+
price: {
|
|
25
|
+
input: 0.3 * OPENROUTER_PRICE_MULTIPLIER,
|
|
26
|
+
output: 1.2 * OPENROUTER_PRICE_MULTIPLIER,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
4
29
|
];
|
|
5
30
|
|
|
6
31
|
export const openrouterModels = OPENROUTER_MODELS;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { MemorySubjectRef } from "./types";
|
|
2
|
+
|
|
3
|
+
export type MemoryOwnerFallback = "onSubjectMiss" | "always";
|
|
4
|
+
|
|
5
|
+
export type AgentMemoryPolicy = {
|
|
6
|
+
includeUserSubject: boolean;
|
|
7
|
+
ownerFallback: MemoryOwnerFallback;
|
|
8
|
+
allowDynamicGreetingMemory: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function isPublicAgentKey(agentKey?: string | null): boolean {
|
|
12
|
+
return typeof agentKey === "string" && /^agent-pub-/i.test(agentKey.trim());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resolveAgentMemoryPolicy(input: {
|
|
16
|
+
agentKey?: string | null;
|
|
17
|
+
isPublicAgent?: boolean | null;
|
|
18
|
+
}): AgentMemoryPolicy {
|
|
19
|
+
const publicAgent = input.isPublicAgent === true || isPublicAgentKey(input.agentKey);
|
|
20
|
+
if (publicAgent) {
|
|
21
|
+
return {
|
|
22
|
+
includeUserSubject: false,
|
|
23
|
+
ownerFallback: "onSubjectMiss",
|
|
24
|
+
allowDynamicGreetingMemory: false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
includeUserSubject: true,
|
|
30
|
+
ownerFallback: "always",
|
|
31
|
+
allowDynamicGreetingMemory: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function buildMemorySubjectsForAgent(input: {
|
|
36
|
+
userId?: string | null;
|
|
37
|
+
spaceId?: string | null;
|
|
38
|
+
agentKey: string;
|
|
39
|
+
policy?: AgentMemoryPolicy;
|
|
40
|
+
}): MemorySubjectRef[] {
|
|
41
|
+
const policy = input.policy ?? resolveAgentMemoryPolicy({ agentKey: input.agentKey });
|
|
42
|
+
return [
|
|
43
|
+
{ subjectType: "agent" as const, subjectId: input.agentKey },
|
|
44
|
+
policy.includeUserSubject && input.userId
|
|
45
|
+
? { subjectType: "user" as const, subjectId: input.userId }
|
|
46
|
+
: null,
|
|
47
|
+
input.spaceId ? { subjectType: "space" as const, subjectId: input.spaceId } : null,
|
|
48
|
+
].filter((value): value is MemorySubjectRef => !!value);
|
|
49
|
+
}
|
package/ai/memory/queryShared.ts
CHANGED
|
@@ -86,10 +86,12 @@ export const loadMemoryCandidatesFromDb = async (
|
|
|
86
86
|
subjects: MemorySubjectRef[];
|
|
87
87
|
kinds?: MemoryKind[];
|
|
88
88
|
ownerLimit?: number;
|
|
89
|
+
ownerFallback?: "onSubjectMiss" | "always";
|
|
89
90
|
}
|
|
90
91
|
): Promise<MemoryItem[]> => {
|
|
91
92
|
const kinds = input.kinds ?? ["episodic", "semantic", "procedural"];
|
|
92
93
|
const ownerLimit = input.ownerLimit ?? 12;
|
|
94
|
+
const ownerFallback = input.ownerFallback ?? "onSubjectMiss";
|
|
93
95
|
const ownerKeySet = new Set(
|
|
94
96
|
input.owners
|
|
95
97
|
.filter((owner) => owner.ownerId)
|
|
@@ -110,10 +112,14 @@ export const loadMemoryCandidatesFromDb = async (
|
|
|
110
112
|
)
|
|
111
113
|
: [];
|
|
112
114
|
const subjectHitCount = subjectResults.reduce((sum, items) => sum + items.length, 0);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
+
const shouldLoadOwnerFallback =
|
|
116
|
+
subjects.length === 0 || subjectHitCount === 0 || ownerFallback === "always";
|
|
117
|
+
const results = shouldLoadOwnerFallback
|
|
115
118
|
? await Promise.all(
|
|
116
|
-
|
|
119
|
+
[
|
|
120
|
+
...subjectResults,
|
|
121
|
+
...input.owners.map((owner) => loadOwnerItemsFromDb(db, owner, ownerLimit)),
|
|
122
|
+
]
|
|
117
123
|
)
|
|
118
124
|
: subjectResults;
|
|
119
125
|
|
|
@@ -131,7 +137,7 @@ export const loadMemoryCandidatesFromDb = async (
|
|
|
131
137
|
continue;
|
|
132
138
|
}
|
|
133
139
|
if (
|
|
134
|
-
!
|
|
140
|
+
!shouldLoadOwnerFallback &&
|
|
135
141
|
subjects.length > 0 &&
|
|
136
142
|
!subjects.some(
|
|
137
143
|
(subject) =>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createDialogKey, dialogMessageRange } from "../../database/keys";
|
|
2
2
|
import { extractCustomId } from "../../core/prefix";
|
|
3
|
+
import { resolveAgentMemoryPolicy } from "./policy";
|
|
3
4
|
|
|
4
5
|
interface RecentDialogLike {
|
|
5
6
|
dbKey?: string;
|
|
@@ -43,6 +44,9 @@ export const shouldUseRecentRelationshipRecap = (input: {
|
|
|
43
44
|
triggerType?: "user" | "api" | "localhost" | "scheduled_run";
|
|
44
45
|
}): boolean => {
|
|
45
46
|
if (!input.userId || !input.agentKey) return false;
|
|
47
|
+
if (!resolveAgentMemoryPolicy({ agentKey: input.agentKey }).allowDynamicGreetingMemory) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
46
50
|
if ((input.cybotsCount ?? 0) !== 1) return false;
|
|
47
51
|
if (input.inheritFromDialogKey) return false;
|
|
48
52
|
if (input.skipGreeting) return false;
|