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
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
buildAgentFormDataFromGuidedDraft,
|
|
4
|
+
mergeGuidedAgentDraft,
|
|
5
|
+
validateGuidedAgentDraft,
|
|
6
|
+
} from "ai/agent/guidedCreation/draft";
|
|
7
|
+
import { DEFAULT_MODEL } from "ai/llm/providers";
|
|
8
|
+
import type {
|
|
9
|
+
GuidedAgentAssistantMessage,
|
|
10
|
+
GuidedAgentCapabilityId,
|
|
11
|
+
GuidedAgentDraft,
|
|
12
|
+
} from "ai/agent/guidedCreation/types";
|
|
13
|
+
import { mapCapabilityIdsToToolIds } from "ai/agent/guidedCreation/capabilities";
|
|
14
|
+
|
|
15
|
+
const EMPTY_DRAFT: GuidedAgentDraft = {
|
|
16
|
+
name: "",
|
|
17
|
+
introduction: "",
|
|
18
|
+
prompt: "",
|
|
19
|
+
promptSummary: "",
|
|
20
|
+
provider: DEFAULT_MODEL.provider,
|
|
21
|
+
model: DEFAULT_MODEL.name,
|
|
22
|
+
isPublic: false,
|
|
23
|
+
capabilityIds: [],
|
|
24
|
+
toolIds: [],
|
|
25
|
+
references: [],
|
|
26
|
+
tags: [],
|
|
27
|
+
unresolved: ["用途", "回答风格", "能力"],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const QUESTIONS = [
|
|
31
|
+
"你想让这个 AI 主要帮你完成什么?",
|
|
32
|
+
"它主要服务谁?希望用什么语气和风格回答?",
|
|
33
|
+
"它需要哪些能力:联网搜索、读写文档、处理表格、调用其他 AI、创建应用或代码?",
|
|
34
|
+
"有哪些资料、边界或禁区需要它遵守?",
|
|
35
|
+
"这个 AI 是只给自己用,还是公开给别人使用?",
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const textIncludes = (text: string, words: string[]) =>
|
|
39
|
+
words.some((word) => text.includes(word));
|
|
40
|
+
|
|
41
|
+
const inferCapabilityIds = (text: string): GuidedAgentCapabilityId[] => {
|
|
42
|
+
const lower = text.toLowerCase();
|
|
43
|
+
const ids: GuidedAgentCapabilityId[] = [];
|
|
44
|
+
if (textIncludes(lower, ["搜索", "联网", "网页", "资料", "最新", "research", "search"])) {
|
|
45
|
+
ids.push("webSearch");
|
|
46
|
+
}
|
|
47
|
+
if (textIncludes(lower, ["文档", "知识库", "页面", "资料", "doc", "docs", "knowledge"])) {
|
|
48
|
+
ids.push("docs");
|
|
49
|
+
}
|
|
50
|
+
if (textIncludes(lower, ["表格", "数据", "记录", "table", "sheet", "csv"])) {
|
|
51
|
+
ids.push("tables");
|
|
52
|
+
}
|
|
53
|
+
if (textIncludes(lower, ["多 agent", "多agent", "评审", "协作", "agent"])) {
|
|
54
|
+
ids.push("agents");
|
|
55
|
+
}
|
|
56
|
+
if (textIncludes(lower, ["应用", "代码", "app", "code", "开发", "生成页面"])) {
|
|
57
|
+
ids.push("apps");
|
|
58
|
+
}
|
|
59
|
+
return Array.from(new Set(ids));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const inferName = (text: string) => {
|
|
63
|
+
const cleaned = text
|
|
64
|
+
.replace(/[。!?!?,,;;::]/g, " ")
|
|
65
|
+
.replace(/\bAI\b/gi, "")
|
|
66
|
+
.replace(/帮我创建|创建一个|创建|面向/g, "")
|
|
67
|
+
.replace(/\s+/g, " ")
|
|
68
|
+
.trim();
|
|
69
|
+
if (!cleaned) return "";
|
|
70
|
+
const purpose = cleaned.slice(0, 18);
|
|
71
|
+
return `${purpose.replace(/\s+/g, "")}助手`;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const buildPrompt = (messages: GuidedAgentAssistantMessage[], draft: GuidedAgentDraft) => {
|
|
75
|
+
const userNeeds = messages
|
|
76
|
+
.filter((message) => message.role === "user")
|
|
77
|
+
.map((message) => `- ${message.content}`)
|
|
78
|
+
.join("\n");
|
|
79
|
+
return [
|
|
80
|
+
`你是${draft.name || "用户的专属 AI"}。`,
|
|
81
|
+
"你需要先理解用户目标,再给出可执行、清晰、有边界的帮助。",
|
|
82
|
+
"根据以下需求工作:",
|
|
83
|
+
userNeeds || "- 尚未补充具体需求",
|
|
84
|
+
"如果信息不足,先提出必要澄清问题;不要编造用户没有确认的资料来源。",
|
|
85
|
+
].join("\n");
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const buildNextQuestion = (draft: GuidedAgentDraft) => {
|
|
89
|
+
if (!draft.name || !draft.prompt) return QUESTIONS[1];
|
|
90
|
+
if (draft.capabilityIds.length === 0) return QUESTIONS[2];
|
|
91
|
+
if (!draft.introduction) return QUESTIONS[3];
|
|
92
|
+
if (draft.unresolved.includes("公开状态")) return QUESTIONS[4];
|
|
93
|
+
return "配置草稿已经可创建。你还想补充哪些资料、语气或使用边界?";
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const createAssistantDraft = (
|
|
97
|
+
messages: GuidedAgentAssistantMessage[],
|
|
98
|
+
current: GuidedAgentDraft
|
|
99
|
+
) => {
|
|
100
|
+
const userText = messages
|
|
101
|
+
.filter((message) => message.role === "user")
|
|
102
|
+
.map((message) => message.content)
|
|
103
|
+
.join("\n");
|
|
104
|
+
const capabilityIds = Array.from(
|
|
105
|
+
new Set([...current.capabilityIds, ...inferCapabilityIds(userText)])
|
|
106
|
+
);
|
|
107
|
+
const name = current.name || inferName(userText);
|
|
108
|
+
const isPublic =
|
|
109
|
+
current.isPublic ||
|
|
110
|
+
textIncludes(userText.toLowerCase(), ["公开", "分享", "public", "给别人"]);
|
|
111
|
+
const unresolved = [
|
|
112
|
+
!name ? "name" : "",
|
|
113
|
+
!userText.trim() ? "prompt" : "",
|
|
114
|
+
capabilityIds.length === 0 ? "能力" : "",
|
|
115
|
+
current.introduction || userText.length > 24 ? "" : "introduction",
|
|
116
|
+
textIncludes(userText.toLowerCase(), ["公开", "私有", "private", "public"]) ? "" : "公开状态",
|
|
117
|
+
].filter(Boolean);
|
|
118
|
+
const next: GuidedAgentDraft = mergeGuidedAgentDraft(current, {
|
|
119
|
+
name,
|
|
120
|
+
introduction:
|
|
121
|
+
current.introduction ||
|
|
122
|
+
(userText.trim()
|
|
123
|
+
? `面向你的需求,帮助整理、判断并执行:${userText.trim().slice(0, 70)}`
|
|
124
|
+
: ""),
|
|
125
|
+
promptSummary: userText.trim()
|
|
126
|
+
? `围绕“${userText.trim().slice(0, 46)}”提供结构化帮助,必要时先追问。`
|
|
127
|
+
: "",
|
|
128
|
+
capabilityIds,
|
|
129
|
+
toolIds: mapCapabilityIdsToToolIds(capabilityIds),
|
|
130
|
+
isPublic,
|
|
131
|
+
tags: Array.from(new Set([...current.tags, ...capabilityIds])),
|
|
132
|
+
unresolved,
|
|
133
|
+
});
|
|
134
|
+
return mergeGuidedAgentDraft(next, {
|
|
135
|
+
prompt: buildPrompt(messages, next),
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const createId = () => `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
140
|
+
|
|
141
|
+
export const useGuidedAgentCreation = (initialDraft?: Partial<GuidedAgentDraft> | null) => {
|
|
142
|
+
const [draft, setDraft] = React.useState<GuidedAgentDraft>(() =>
|
|
143
|
+
initialDraft ? mergeGuidedAgentDraft(EMPTY_DRAFT, initialDraft) : EMPTY_DRAFT
|
|
144
|
+
);
|
|
145
|
+
const [messages, setMessages] = React.useState<GuidedAgentAssistantMessage[]>([
|
|
146
|
+
{
|
|
147
|
+
id: "assistant-initial",
|
|
148
|
+
role: "assistant",
|
|
149
|
+
content: initialDraft?.name
|
|
150
|
+
? "我已经带入了当前对话整理出的草稿。你可以继续补充要求,或直接预览并创建。"
|
|
151
|
+
: QUESTIONS[0],
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
const [input, setInput] = React.useState("");
|
|
155
|
+
const [isThinking, setIsThinking] = React.useState(false);
|
|
156
|
+
const [error, setError] = React.useState<string | null>(null);
|
|
157
|
+
|
|
158
|
+
const submit = React.useCallback(async () => {
|
|
159
|
+
const content = input.trim();
|
|
160
|
+
if (!content || isThinking) return;
|
|
161
|
+
|
|
162
|
+
setInput("");
|
|
163
|
+
setError(null);
|
|
164
|
+
setIsThinking(true);
|
|
165
|
+
|
|
166
|
+
const userMessage: GuidedAgentAssistantMessage = {
|
|
167
|
+
id: createId(),
|
|
168
|
+
role: "user",
|
|
169
|
+
content,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const nextMessages = [...messages, userMessage];
|
|
174
|
+
const nextDraft = createAssistantDraft(nextMessages, draft);
|
|
175
|
+
const assistantMessage: GuidedAgentAssistantMessage = {
|
|
176
|
+
id: createId(),
|
|
177
|
+
role: "assistant",
|
|
178
|
+
content: buildNextQuestion(nextDraft),
|
|
179
|
+
};
|
|
180
|
+
setMessages([...nextMessages, assistantMessage]);
|
|
181
|
+
setDraft(nextDraft);
|
|
182
|
+
} catch {
|
|
183
|
+
setError("暂时没能整理这段需求,请换一种说法再试。");
|
|
184
|
+
} finally {
|
|
185
|
+
setIsThinking(false);
|
|
186
|
+
}
|
|
187
|
+
}, [draft, input, isThinking, messages]);
|
|
188
|
+
|
|
189
|
+
const toggleReference = React.useCallback((dbKey: string, selected: boolean) => {
|
|
190
|
+
setDraft((current) =>
|
|
191
|
+
mergeGuidedAgentDraft(current, {
|
|
192
|
+
references: current.references.map((reference) =>
|
|
193
|
+
reference.dbKey === dbKey ? { ...reference, selected } : reference
|
|
194
|
+
),
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}, []);
|
|
198
|
+
|
|
199
|
+
const validation = validateGuidedAgentDraft(draft);
|
|
200
|
+
const formInitialValues = React.useMemo(
|
|
201
|
+
() => buildAgentFormDataFromGuidedDraft(draft),
|
|
202
|
+
[draft]
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
draft,
|
|
207
|
+
messages,
|
|
208
|
+
input,
|
|
209
|
+
setInput,
|
|
210
|
+
submit,
|
|
211
|
+
isThinking,
|
|
212
|
+
error,
|
|
213
|
+
validation,
|
|
214
|
+
formInitialValues,
|
|
215
|
+
toggleReference,
|
|
216
|
+
};
|
|
217
|
+
};
|
package/ai/ai.locale.ts
CHANGED
|
@@ -27,6 +27,9 @@ export default {
|
|
|
27
27
|
textOnly: "Text-only",
|
|
28
28
|
details: "Details",
|
|
29
29
|
price: "Price",
|
|
30
|
+
modelCost: "Model cost",
|
|
31
|
+
inputCostPerMillionTokens: "Input",
|
|
32
|
+
outputCostPerMillionTokens: "Output",
|
|
30
33
|
referencePricePerTurn: "Per-turn reference price",
|
|
31
34
|
defaultImageProfileEstimate: "Default profile estimate",
|
|
32
35
|
perMillionTokens: "per 1M tokens",
|
|
@@ -251,7 +254,7 @@ export default {
|
|
|
251
254
|
description: "Create a new Agent (intelligent assistant/app) based on the given configuration and return the complete Agent information.",
|
|
252
255
|
params: {
|
|
253
256
|
name: "The name of the Agent, e.g., 'Meeting Minutes Assistant'.",
|
|
254
|
-
model: "The model name to use. Logic: Default 'xiaomi/mimo-v2.5-pro'. Complex tasks: 'openai/gpt-5.2'. Cost-saving: 'openai/gpt-5.1'. Aesthetic/Creative: 'gemini-3-pro-preview'.
|
|
257
|
+
model: "The model name to use. Logic: Default 'xiaomi/mimo-v2.5-pro'. Complex tasks: 'openai/gpt-5.2'. Cost-saving: 'openai/gpt-5.1'. Aesthetic/Creative: 'gemini-3-pro-preview'. Reliable low-cost summary/reasoning: 'deepseek-v4-pro'. Image Generation: 'gemini-3-pro-image-preview' (Best/Expensive) or 'gemini-2.5-flash-image' (Ordinary).",
|
|
255
258
|
provider: "The model provider identifier, must match the selected model, e.g., 'openrouter', 'deepseek', 'fireworks', 'mistral', etc.",
|
|
256
259
|
prompt: "System prompt/instruction describing the Agent's role, tone, and usage.",
|
|
257
260
|
introduction: "Introduction displayed in the app market or list, explanatory text for end users.",
|
|
@@ -318,6 +321,9 @@ export default {
|
|
|
318
321
|
textOnly: "纯文本",
|
|
319
322
|
details: "详细信息",
|
|
320
323
|
price: "价格",
|
|
324
|
+
modelCost: "模型成本",
|
|
325
|
+
inputCostPerMillionTokens: "输入",
|
|
326
|
+
outputCostPerMillionTokens: "输出",
|
|
321
327
|
referencePricePerTurn: "按次参考价",
|
|
322
328
|
defaultImageProfileEstimate: "默认档参考价",
|
|
323
329
|
perMillionTokens: "每百万Token",
|
|
@@ -539,7 +545,7 @@ export default {
|
|
|
539
545
|
description: "根据给定配置创建一个新的 Agent(智能体 / 应用),并返回完整的 Agent 信息。",
|
|
540
546
|
params: {
|
|
541
547
|
name: "Agent 的名称,例如:'会议纪要助手'。",
|
|
542
|
-
model: "默认使用 'xiaomi/mimo-v2.5-pro'。复杂任务: 'openai/gpt-5.2'。省成本但复杂: 'openai/gpt-5.1'。追求美观/创意: 'gemini-3-pro-preview'
|
|
548
|
+
model: "默认使用 'xiaomi/mimo-v2.5-pro'。复杂任务: 'openai/gpt-5.2'。省成本但复杂: 'openai/gpt-5.1'。追求美观/创意: 'gemini-3-pro-preview'。低成本摘要/推理: 'deepseek-v4-pro'。生成图片: 贵/高质量选 'gemini-3-pro-image-preview',普通选 'gemini-2.5-flash-image'。",
|
|
543
549
|
provider: "模型提供方标识,必须与所选模型相匹配,例如 'openrouter'、'deepseek'、'fireworks'、'mistral' 等。",
|
|
544
550
|
prompt: "系统提示词 / 指令,描述这个 Agent 的角色、语气和使用方式。",
|
|
545
551
|
introduction: "在应用市场或列表中展示的简介,面向终端用户的说明文案。",
|
|
@@ -606,6 +612,9 @@ export default {
|
|
|
606
612
|
textOnly: "純文字",
|
|
607
613
|
details: "詳細資訊",
|
|
608
614
|
price: "價格",
|
|
615
|
+
modelCost: "模型成本",
|
|
616
|
+
inputCostPerMillionTokens: "輸入",
|
|
617
|
+
outputCostPerMillionTokens: "輸出",
|
|
609
618
|
referencePricePerTurn: "按次參考價",
|
|
610
619
|
defaultImageProfileEstimate: "預設檔參考價",
|
|
611
620
|
perMillionTokens: "每百萬Token",
|
|
@@ -852,6 +861,9 @@ export default {
|
|
|
852
861
|
textOnly: "テキストのみ",
|
|
853
862
|
details: "詳細",
|
|
854
863
|
price: "価格",
|
|
864
|
+
modelCost: "モデルコスト",
|
|
865
|
+
inputCostPerMillionTokens: "入力",
|
|
866
|
+
outputCostPerMillionTokens: "出力",
|
|
855
867
|
referencePricePerTurn: "回ごとの参考価格",
|
|
856
868
|
defaultImageProfileEstimate: "デフォルト構成の参考価格",
|
|
857
869
|
perMillionTokens: "100万トークンあたり",
|
|
@@ -160,16 +160,19 @@ export type CompletionMeta = {
|
|
|
160
160
|
usage?: any;
|
|
161
161
|
};
|
|
162
162
|
|
|
163
|
+
const STREAM_READ_TIMEOUT_MS = 45_000;
|
|
164
|
+
|
|
163
165
|
const logQuickChatPerfStage = (
|
|
164
166
|
startedAt: number | undefined,
|
|
165
167
|
stage: string,
|
|
166
168
|
details?: Record<string, unknown>
|
|
167
169
|
) => {
|
|
168
170
|
if (!startedAt) return;
|
|
171
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
169
172
|
console.info("[QuickChatPerf]", {
|
|
170
173
|
stage,
|
|
171
|
-
elapsedMs:
|
|
172
|
-
...(typeof performance !== "undefined" ? { atMs:
|
|
174
|
+
elapsedMs: now - startedAt,
|
|
175
|
+
...(typeof performance !== "undefined" ? { atMs: now } : {}),
|
|
173
176
|
...(details ?? {}),
|
|
174
177
|
});
|
|
175
178
|
};
|
|
@@ -412,6 +415,28 @@ async function processAccumulatedToolCalls(
|
|
|
412
415
|
};
|
|
413
416
|
}
|
|
414
417
|
|
|
418
|
+
async function readStreamChunkWithTimeout(
|
|
419
|
+
reader: ReadableStreamDefaultReader<Uint8Array>
|
|
420
|
+
) {
|
|
421
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
422
|
+
try {
|
|
423
|
+
return await Promise.race([
|
|
424
|
+
reader.read(),
|
|
425
|
+
new Promise<ReadableStreamReadResult<Uint8Array>>((_, reject) => {
|
|
426
|
+
timeoutId = setTimeout(() => {
|
|
427
|
+
reject(
|
|
428
|
+
new Error(
|
|
429
|
+
`模型响应流 ${Math.round(STREAM_READ_TIMEOUT_MS / 1000)} 秒内没有返回新内容`
|
|
430
|
+
)
|
|
431
|
+
);
|
|
432
|
+
}, STREAM_READ_TIMEOUT_MS);
|
|
433
|
+
}),
|
|
434
|
+
]);
|
|
435
|
+
} finally {
|
|
436
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
415
440
|
/**
|
|
416
441
|
* 处理「流结束」场景(done === true)
|
|
417
442
|
*/
|
|
@@ -608,7 +633,7 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
608
633
|
let loggedFirstVisibleDelta = false;
|
|
609
634
|
|
|
610
635
|
while (true) {
|
|
611
|
-
const { done, value } = await reader
|
|
636
|
+
const { done, value } = await readStreamChunkWithTimeout(reader);
|
|
612
637
|
|
|
613
638
|
if (done) {
|
|
614
639
|
const completion = await handleStreamCompletion(
|
|
@@ -48,10 +48,11 @@ const logQuickChatPerfStage = (
|
|
|
48
48
|
details?: Record<string, unknown>
|
|
49
49
|
) => {
|
|
50
50
|
if (!startedAt) return;
|
|
51
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
51
52
|
console.info("[QuickChatPerf]", {
|
|
52
53
|
stage,
|
|
53
|
-
elapsedMs:
|
|
54
|
-
...(typeof performance !== "undefined" ? { atMs:
|
|
54
|
+
elapsedMs: now - startedAt,
|
|
55
|
+
...(typeof performance !== "undefined" ? { atMs: now } : {}),
|
|
55
56
|
...(details ?? {}),
|
|
56
57
|
});
|
|
57
58
|
};
|
package/ai/llm/getPricing.ts
CHANGED
|
@@ -209,6 +209,20 @@ export const formatPriceAmount = (amount: number): string => {
|
|
|
209
209
|
return amount.toFixed(6);
|
|
210
210
|
};
|
|
211
211
|
|
|
212
|
+
export const formatModelCostPerMillion = (
|
|
213
|
+
value?: number,
|
|
214
|
+
creditsUnit = "积分"
|
|
215
|
+
): string => {
|
|
216
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "未知";
|
|
217
|
+
if (value === 0) return "免费";
|
|
218
|
+
const decimals = Math.abs(value) < 1 ? 3 : 2;
|
|
219
|
+
const amount = value
|
|
220
|
+
.toFixed(decimals)
|
|
221
|
+
.replace(/\.0+$/, "")
|
|
222
|
+
.replace(/(\.\d*?[1-9])0+$/, "$1");
|
|
223
|
+
return `${amount} ${creditsUnit}`;
|
|
224
|
+
};
|
|
225
|
+
|
|
212
226
|
export const formatCompactTurnPrice = (
|
|
213
227
|
amount: number
|
|
214
228
|
): CompactTurnPriceDisplay => {
|
package/ai/llm/kimi.ts
CHANGED
|
@@ -7,6 +7,9 @@ export const KIMI_PLATFORM_FALLBACK_STATUSES = [401, 402, 429, 500, 502, 503, 50
|
|
|
7
7
|
export const isFireworksKimiModel = (model?: string | null): boolean =>
|
|
8
8
|
model === FIREWORKS_KIMI_LATEST_MODEL || model === FIREWORKS_KIMI_CURRENT_MODEL;
|
|
9
9
|
|
|
10
|
+
export const isDeepInfraKimiModel = (model?: string | null): boolean =>
|
|
11
|
+
model === DEEPINFRA_KIMI_FALLBACK_MODEL;
|
|
12
|
+
|
|
10
13
|
export const resolveFireworksKimiModel = (model?: string | null): string => {
|
|
11
14
|
if (model === FIREWORKS_KIMI_LATEST_MODEL) {
|
|
12
15
|
return FIREWORKS_KIMI_CURRENT_MODEL;
|
package/ai/llm/modelAvatar.ts
CHANGED
|
@@ -393,6 +393,7 @@ export async function getModelAvatarComponent(
|
|
|
393
393
|
if (cliProviderKey === "copilot") return get("copilot");
|
|
394
394
|
if (cliProviderKey === "codex") return get("openai");
|
|
395
395
|
if (cliProviderKey === "claude") return get("claude");
|
|
396
|
+
if (cliProviderKey === "agy") return get("gemini");
|
|
396
397
|
|
|
397
398
|
// ── 按 model 名称匹配(优先,处理 openrouter/deepinfra 等聚合场景) ──
|
|
398
399
|
if (m.includes("claude")) return get("claude");
|
package/ai/llm/providers.ts
CHANGED
|
@@ -267,7 +267,7 @@ export function getApiEndpoint(agent: Agent): string {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
/* custom provider / apiSource=custom 但未给 URL 的兜底
|
|
270
|
-
* 注意:apiSource="custom" 的 agent
|
|
270
|
+
* 注意:apiSource="custom" 的 agent 不需要 provider 字段,
|
|
271
271
|
* 使用 customProviderUrl + 自己的 apiKey,上面已经 return 了。
|
|
272
272
|
* 只有 customProviderUrl 为空时才会走到这里报错。
|
|
273
273
|
*/
|
|
@@ -156,8 +156,11 @@ export type TaskRunReview = {
|
|
|
156
156
|
export type TaskRunAutonomy = "manual" | "supervised" | "auto24h";
|
|
157
157
|
|
|
158
158
|
export type TaskRunBudgetRule = {
|
|
159
|
+
agentKey?: string;
|
|
159
160
|
provider?: string;
|
|
160
161
|
model?: string;
|
|
162
|
+
customProviderUrl?: string;
|
|
163
|
+
apiKeyHeader?: string;
|
|
161
164
|
freeUntil?: string;
|
|
162
165
|
dailyUsdLimit?: number;
|
|
163
166
|
hardStopAfter?: string;
|
|
@@ -249,20 +252,31 @@ export const DEFAULT_TASK_RUN_CONTROL_POLICY: TaskRunControlPolicy = {
|
|
|
249
252
|
budget: {
|
|
250
253
|
modelBudget: {
|
|
251
254
|
projectManager: {
|
|
255
|
+
agentKey: "agent-b2e06f801f-01MIMO25MONTH00000001PGT4X",
|
|
252
256
|
provider: "custom",
|
|
253
|
-
model: "
|
|
254
|
-
|
|
257
|
+
model: "mimo-v2.5-pro",
|
|
258
|
+
customProviderUrl: "https://token-plan-cn.xiaomimimo.com/v1",
|
|
259
|
+
apiKeyHeader: "api-key",
|
|
260
|
+
freeUntil: "2026-05-31",
|
|
261
|
+
note: "Default PM and execution routing uses the monthly Mimo custom-key pool; medium intelligence, no marginal cost through the current month.",
|
|
255
262
|
},
|
|
256
263
|
frontend: {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
264
|
+
agentKey: "agent-b2e06f801f-01MIMO25MONTH00000001PGT4X",
|
|
265
|
+
provider: "custom",
|
|
266
|
+
model: "mimo-v2.5-pro",
|
|
267
|
+
customProviderUrl: "https://token-plan-cn.xiaomimimo.com/v1",
|
|
268
|
+
apiKeyHeader: "api-key",
|
|
269
|
+
freeUntil: "2026-05-31",
|
|
270
|
+
note: "Frontend implementation uses the monthly Mimo custom-key pool by default; escalate to Codex only for quality gates or hard unblocks.",
|
|
261
271
|
},
|
|
262
272
|
mimoCustomKey: {
|
|
263
|
-
|
|
273
|
+
agentKey: "agent-b2e06f801f-01MIMO25MONTH00000001PGT4X",
|
|
274
|
+
provider: "custom",
|
|
275
|
+
model: "mimo-v2.5-pro",
|
|
276
|
+
customProviderUrl: "https://token-plan-cn.xiaomimimo.com/v1",
|
|
277
|
+
apiKeyHeader: "api-key",
|
|
264
278
|
freeUntil: "2026-05-31",
|
|
265
|
-
note: "
|
|
279
|
+
note: "Use the existing monthly Mimo custom-key agent/config for high-token routine implementation, long-context reads, table/doc processing, and low-risk probes.",
|
|
266
280
|
},
|
|
267
281
|
},
|
|
268
282
|
},
|
|
@@ -858,11 +872,11 @@ export function recordTaskRunReview(args: {
|
|
|
858
872
|
meta.review = review;
|
|
859
873
|
let nextMeta = meta;
|
|
860
874
|
const targetWorkItemId = args.targetWorkItemId ?? meta.review?.targetWorkItemId;
|
|
861
|
-
if (args.status === "needs_changes" && targetWorkItemId) {
|
|
875
|
+
if ((args.status === "needs_changes" || args.status === "blocked") && targetWorkItemId) {
|
|
862
876
|
nextMeta = updateTaskRunWorkItem({
|
|
863
877
|
meta: nextMeta,
|
|
864
878
|
id: targetWorkItemId,
|
|
865
|
-
status:
|
|
879
|
+
status: args.status,
|
|
866
880
|
artifactRefs: args.artifactIds,
|
|
867
881
|
dialogIds: args.dialogId ? [args.dialogId] : undefined,
|
|
868
882
|
now,
|
|
@@ -64,6 +64,7 @@ export type ModelUsageQueryResult = {
|
|
|
64
64
|
threshold: {
|
|
65
65
|
costCredits: number | null;
|
|
66
66
|
costUsd: number | null;
|
|
67
|
+
usedPercent: number | null;
|
|
67
68
|
exceeded: boolean;
|
|
68
69
|
};
|
|
69
70
|
};
|
|
@@ -83,6 +84,17 @@ const toNumber = (value: unknown): number =>
|
|
|
83
84
|
|
|
84
85
|
const round6 = (value: number): number => Number(value.toFixed(6));
|
|
85
86
|
|
|
87
|
+
const clampPercent = (value: number): number =>
|
|
88
|
+
Math.max(0, Math.min(100, Math.round(value)));
|
|
89
|
+
|
|
90
|
+
const calculateThresholdUsedPercent = (
|
|
91
|
+
costCredits: number,
|
|
92
|
+
thresholdCredits: number | null,
|
|
93
|
+
): number | null => {
|
|
94
|
+
if (thresholdCredits == null || thresholdCredits <= 0) return null;
|
|
95
|
+
return clampPercent((costCredits / thresholdCredits) * 100);
|
|
96
|
+
};
|
|
97
|
+
|
|
86
98
|
const normalizeDateKey = (value: unknown): string | null => {
|
|
87
99
|
if (typeof value !== "string" || !value.trim()) return null;
|
|
88
100
|
const trimmed = value.trim();
|
|
@@ -227,6 +239,10 @@ export async function queryModelUsage(
|
|
|
227
239
|
: thresholdCredits == null
|
|
228
240
|
? null
|
|
229
241
|
: round6(thresholdCredits / creditsPerUsd);
|
|
242
|
+
const thresholdUsedPercent = calculateThresholdUsedPercent(
|
|
243
|
+
total.costCredits,
|
|
244
|
+
thresholdCredits,
|
|
245
|
+
);
|
|
230
246
|
|
|
231
247
|
return {
|
|
232
248
|
scope,
|
|
@@ -246,6 +262,7 @@ export async function queryModelUsage(
|
|
|
246
262
|
threshold: {
|
|
247
263
|
costCredits: thresholdCredits,
|
|
248
264
|
costUsd: thresholdUsd,
|
|
265
|
+
usedPercent: thresholdUsedPercent,
|
|
249
266
|
exceeded: thresholdCredits == null ? false : total.costCredits > thresholdCredits,
|
|
250
267
|
},
|
|
251
268
|
};
|
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
createAgentToolFunctionSchema,
|
|
9
9
|
createAgentToolFunc,
|
|
10
10
|
} from "./createAgentTool";
|
|
11
|
+
import {
|
|
12
|
+
prepareAgentDraftToolFunctionSchema,
|
|
13
|
+
prepareAgentDraftToolFunc,
|
|
14
|
+
} from "./prepareAgentDraftTool";
|
|
11
15
|
import {
|
|
12
16
|
createSkillAgentToolFunctionSchema,
|
|
13
17
|
createSkillAgentToolFunc,
|
|
@@ -48,6 +52,23 @@ import {
|
|
|
48
52
|
import type { ToolDefinition } from "../index";
|
|
49
53
|
|
|
50
54
|
export const agentToolDefinitions: ToolDefinition[] = [
|
|
55
|
+
{
|
|
56
|
+
id: "prepareAgentDraft",
|
|
57
|
+
schema: prepareAgentDraftToolFunctionSchema,
|
|
58
|
+
executor: prepareAgentDraftToolFunc,
|
|
59
|
+
description: {
|
|
60
|
+
name: "prepareAgentDraft",
|
|
61
|
+
description:
|
|
62
|
+
"整理 Agent 创建草稿并交给用户预览确认,不创建真实 Agent。",
|
|
63
|
+
category: "计划与编排",
|
|
64
|
+
},
|
|
65
|
+
behavior: "data",
|
|
66
|
+
uiGroup: "agent",
|
|
67
|
+
capability: "general",
|
|
68
|
+
riskLevel: "low",
|
|
69
|
+
costLevel: "low",
|
|
70
|
+
defaultConsent: "auto",
|
|
71
|
+
},
|
|
51
72
|
{
|
|
52
73
|
id: "createAgent",
|
|
53
74
|
schema: createAgentToolFunctionSchema,
|