nolo-cli 0.1.7 → 0.1.9
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 +107 -5
- package/agentRuntimeCommands.ts +464 -0
- package/ai/agent/_executeModel.ts +118 -0
- package/ai/agent/agentSlice.ts +525 -0
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/avatarUtils.ts +24 -0
- package/ai/agent/buildEditingContext.ts +373 -0
- package/ai/agent/buildSystemPrompt.ts +532 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +119 -0
- package/ai/agent/cliExecutor.ts +733 -0
- package/ai/agent/cliPrompt.ts +10 -0
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +234 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
- package/ai/agent/hooks/useAgentConfig.ts +61 -0
- package/ai/agent/hooks/useAgentDialog.ts +35 -0
- package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
- package/ai/agent/hooks/usePublicAgents.ts +473 -0
- package/ai/agent/machineRunPermissions.ts +95 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +238 -0
- package/ai/agent/runAgentClientLoop.ts +138 -0
- package/ai/agent/runtimeGuidance.ts +97 -0
- package/ai/agent/runtimeServerBase.ts +37 -0
- package/ai/agent/server/fetchPublicAgents.ts +128 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +1278 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
- package/ai/agent/types.ts +71 -0
- package/ai/agent/utils/imageOutput.ts +33 -0
- package/ai/agent/utils/sortUtils.ts +250 -0
- package/ai/agent/web/referencePickerUtils.ts +146 -0
- package/ai/ai.locale.ts +1075 -0
- package/ai/chat/accumulateToolCallChunks.ts +95 -0
- package/ai/chat/fetchUtils.native.ts +276 -0
- package/ai/chat/fetchUtils.ts +153 -0
- package/ai/chat/parseApiError.ts +64 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/sseClient.native.ts +91 -0
- package/ai/chat/sseClient.ts +67 -0
- package/ai/chat/streamReader.native.ts +31 -0
- package/ai/chat/streamReader.ts +62 -0
- package/ai/chat/updateTotalUsage.ts +72 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/calculateContextUsage.ts +133 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/index.ts +1 -0
- package/ai/llm/calculateGeminiImageTokens.ts +57 -0
- package/ai/llm/deepinfra.ts +28 -0
- package/ai/llm/fireworks.ts +50 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +84 -0
- package/ai/llm/getNoloKey.ts +31 -0
- package/ai/llm/getPricing.ts +199 -0
- package/ai/llm/hooks/useModelPricing.ts +75 -0
- package/ai/llm/imagePricing.ts +40 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/mimo.ts +71 -0
- package/ai/llm/mistral.ts +22 -0
- package/ai/llm/modelAvatar.ts +427 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +269 -0
- package/ai/llm/providers.ts +306 -0
- package/ai/llm/reasoningModels.ts +28 -0
- package/ai/llm/types.ts +59 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/capture.ts +148 -0
- package/ai/memory/consolidate.ts +104 -0
- package/ai/memory/delete.ts +147 -0
- package/ai/memory/overlay.ts +84 -0
- package/ai/memory/query.ts +38 -0
- package/ai/memory/queryShared.ts +160 -0
- package/ai/memory/rank.ts +105 -0
- package/ai/memory/recentRelationshipRecap.ts +249 -0
- package/ai/memory/remember.ts +167 -0
- package/ai/memory/runtime.ts +76 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +46 -0
- package/ai/memory/understanding.ts +349 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/messages/type.ts +20 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +440 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillDocProtocol.ts +434 -0
- package/ai/skills/skillReferenceSummary.ts +63 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +544 -0
- package/ai/token/db.ts +98 -0
- package/ai/token/externalToolCost.ts +330 -0
- package/ai/token/hooks/useRecords.ts +65 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/modelUsageQuery.ts +252 -0
- package/ai/token/normalizeUsage.ts +84 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +88 -0
- package/ai/token/query.ts +88 -0
- package/ai/token/queryUserTokens.ts +59 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/serverDialogProjection.ts +78 -0
- package/ai/token/serverTokenWriter.ts +143 -0
- package/ai/token/stats.ts +21 -0
- package/ai/token/tokenThunks.ts +24 -0
- package/ai/token/types.ts +93 -0
- package/ai/tools/agent/agentTools.ts +176 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/skillAgentPreset.ts +89 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +256 -0
- package/ai/tools/applyLineEditsTool.ts +312 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +65 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +111 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createPlanTool.ts +999 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/deepseekOcrTool.ts +34 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +64 -0
- package/ai/tools/execBashTool.ts +379 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +309 -0
- package/ai/tools/fetchWebpageTool.ts +84 -0
- package/ai/tools/geminiImagePreviewTool.ts +361 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +1858 -0
- package/ai/tools/listFilesTool.ts +82 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/modelUsageTools.ts +142 -0
- package/ai/tools/olmOcrTool.ts +34 -0
- package/ai/tools/openaiImageTool.ts +218 -0
- package/ai/tools/paddleOcrTool.ts +34 -0
- package/ai/tools/prepareTools.ts +23 -0
- package/ai/tools/readDocTool.ts +84 -0
- package/ai/tools/readFileTool.ts +211 -0
- package/ai/tools/readTool.ts +163 -0
- package/ai/tools/readXPostTool.ts +233 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchRepoTool.ts +115 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +315 -0
- package/ai/tools/table/rowTools.ts +366 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/toolApiClient.ts +198 -0
- package/ai/tools/toolNameAliases.ts +57 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/toolVisibility.ts +4 -0
- package/ai/tools/types.ts +20 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
- package/ai/tools/whisperTool.ts +77 -0
- package/ai/tools/writeFileTool.ts +210 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +55 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/client/agentRun.ts +198 -167
- package/client/compactDialog.ts +222 -0
- package/commandRegistry.ts +14 -0
- package/connector-experimental/capabilities.ts +73 -0
- package/connector-experimental/codexBinary.ts +41 -0
- package/connector-experimental/heartbeatLoop.ts +22 -0
- package/connector-experimental/index.ts +5 -0
- package/connector-experimental/machineInfo.ts +46 -0
- package/connector-experimental/protocol.ts +54 -0
- package/connectorWebSocketTarget.ts +29 -0
- package/defaultServer.ts +1 -0
- package/index.ts +158 -104
- package/machineCommands.ts +382 -0
- package/package.json +12 -2
- package/tui/readlineWorkspace.ts +50 -0
- package/tui/session.ts +40 -2
- package/updateCommands.ts +70 -5
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Message } from "chat/messages/types";
|
|
2
|
+
import type { ToolRun } from "ai/tools/toolRunSlice";
|
|
3
|
+
|
|
4
|
+
const APP_TOOL_NAMES = new Set([
|
|
5
|
+
"appList",
|
|
6
|
+
"appRead",
|
|
7
|
+
"appDeploy",
|
|
8
|
+
"appPreflight",
|
|
9
|
+
"appDelete",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const contentToText = (content: Message["content"] | undefined): string => {
|
|
13
|
+
if (!content) return "";
|
|
14
|
+
if (typeof content === "string") return content;
|
|
15
|
+
if (Array.isArray(content)) {
|
|
16
|
+
return content
|
|
17
|
+
.map((part) => ("text" in part ? part.text ?? "" : ""))
|
|
18
|
+
.join("\n");
|
|
19
|
+
}
|
|
20
|
+
return "";
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const parseAppListEntries = (text: string): string[] =>
|
|
24
|
+
text
|
|
25
|
+
.split("\n")
|
|
26
|
+
.map((line) => line.trim())
|
|
27
|
+
.filter((line) => line.startsWith("- **") && line.includes("(appId:"));
|
|
28
|
+
|
|
29
|
+
export const buildRecentAppToolMemory = (
|
|
30
|
+
messages: Message[],
|
|
31
|
+
toolRuns: ToolRun[],
|
|
32
|
+
): string | null => {
|
|
33
|
+
const messageById = new Map(messages.map((msg) => [msg.id, msg]));
|
|
34
|
+
const recentRuns = [...toolRuns]
|
|
35
|
+
.filter((run) => APP_TOOL_NAMES.has(run.toolName))
|
|
36
|
+
.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0))
|
|
37
|
+
.slice(0, 8);
|
|
38
|
+
|
|
39
|
+
if (recentRuns.length === 0) return null;
|
|
40
|
+
|
|
41
|
+
const lines: string[] = [
|
|
42
|
+
"这些信息来自当前对话里最近的 app 工具调用,不依赖右侧编辑态。",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const latestDeployLike = recentRuns.find((run) =>
|
|
46
|
+
["appDeploy", "appRead", "appPreflight"].includes(run.toolName),
|
|
47
|
+
);
|
|
48
|
+
if (latestDeployLike) {
|
|
49
|
+
const input = latestDeployLike.input ?? {};
|
|
50
|
+
const parts = [
|
|
51
|
+
`- 最近一次关键 app 操作: ${latestDeployLike.toolName}`,
|
|
52
|
+
typeof input.appId === "string" ? `appId=${input.appId}` : null,
|
|
53
|
+
typeof input.name === "string" ? `name=${input.name}` : null,
|
|
54
|
+
typeof input.framework === "string" ? `framework=${input.framework}` : null,
|
|
55
|
+
].filter(Boolean);
|
|
56
|
+
lines.push(parts.join(","));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const relatedMessages = recentRuns
|
|
60
|
+
.map((run) => messageById.get(run.messageId))
|
|
61
|
+
.filter((msg): msg is Message => !!msg);
|
|
62
|
+
|
|
63
|
+
const appListMessage = relatedMessages.find((msg) => msg.toolName === "appList");
|
|
64
|
+
if (appListMessage) {
|
|
65
|
+
const entries = parseAppListEntries(contentToText(appListMessage.content)).slice(0, 5);
|
|
66
|
+
if (entries.length > 0) {
|
|
67
|
+
lines.push("- 最近一次 appList 结果:");
|
|
68
|
+
lines.push(...entries.map((entry) => ` ${entry}`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const latestReadMessage = relatedMessages.find((msg) => msg.toolName === "appRead");
|
|
73
|
+
if (latestReadMessage) {
|
|
74
|
+
const text = contentToText(latestReadMessage.content);
|
|
75
|
+
const appId = text.match(/- appId:\s*(.+)/)?.[1]?.trim();
|
|
76
|
+
const url = text.match(/- 访问地址:\s*(.+)/)?.[1]?.trim();
|
|
77
|
+
if (appId || url) {
|
|
78
|
+
lines.push(
|
|
79
|
+
[
|
|
80
|
+
"- 最近一次 appRead 真值:",
|
|
81
|
+
appId ? `appId=${appId}` : null,
|
|
82
|
+
url ? `url=${url}` : null,
|
|
83
|
+
]
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.join(","),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const latestPreflightRun = recentRuns.find((run) => run.toolName === "appPreflight");
|
|
91
|
+
if (latestPreflightRun) {
|
|
92
|
+
lines.push(
|
|
93
|
+
[
|
|
94
|
+
"- 最近一次 appPreflight:",
|
|
95
|
+
latestPreflightRun.status === "failed" ? "失败" : "已执行",
|
|
96
|
+
latestPreflightRun.outputSummary ? `摘要=${latestPreflightRun.outputSummary}` : null,
|
|
97
|
+
]
|
|
98
|
+
.filter(Boolean)
|
|
99
|
+
.join(","),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const latestDeployMessage = relatedMessages.find((msg) => msg.toolName === "appDeploy");
|
|
104
|
+
if (latestDeployMessage) {
|
|
105
|
+
const text = contentToText(latestDeployMessage.content);
|
|
106
|
+
const appId = text.match(/- appId:\s*(.+)/)?.[1]?.trim();
|
|
107
|
+
const url = text.match(/- 访问地址:\s*(.+)/)?.[1]?.trim();
|
|
108
|
+
if (appId || url) {
|
|
109
|
+
lines.push(
|
|
110
|
+
[
|
|
111
|
+
"- 最近一次 appDeploy 结果:",
|
|
112
|
+
appId ? `appId=${appId}` : null,
|
|
113
|
+
url ? `url=${url}` : null,
|
|
114
|
+
]
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.join(","),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
lines.push(
|
|
122
|
+
"- 如果用户说“刚才那个 app / 那个网站”,优先把它理解为上面最近一次被读取、预检或部署的应用;若仍有歧义,再用 appList 或 appRead 确认。",
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// packages/ai/agent/avatarUtils.ts
|
|
2
|
+
// Resolves an agent's avatarFileId → a displayable URL
|
|
3
|
+
|
|
4
|
+
import { buildDatabaseFileContentUrl } from "database/fileUrl";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a URL for displaying the agent avatar, or null.
|
|
8
|
+
* - If avatarFileId is an http(s) URL → use directly
|
|
9
|
+
* - Otherwise build server file-content URL
|
|
10
|
+
*/
|
|
11
|
+
export function resolveAvatarUrl(
|
|
12
|
+
avatarFileId: string | undefined | null,
|
|
13
|
+
server: string | undefined | null
|
|
14
|
+
): string | null {
|
|
15
|
+
if (!avatarFileId) return null;
|
|
16
|
+
if (avatarFileId.startsWith("http") || avatarFileId.startsWith("blob:")) {
|
|
17
|
+
return avatarFileId;
|
|
18
|
+
}
|
|
19
|
+
if (avatarFileId.startsWith("/")) {
|
|
20
|
+
if (!server) return avatarFileId;
|
|
21
|
+
return `${server}${avatarFileId}`;
|
|
22
|
+
}
|
|
23
|
+
return buildDatabaseFileContentUrl(server, avatarFileId);
|
|
24
|
+
}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// 文件路径: ai/agent/buildEditingContext.ts
|
|
2
|
+
|
|
3
|
+
import type { RootState } from "app/store";
|
|
4
|
+
import type { AgentRuntimeOptions } from "./types";
|
|
5
|
+
import { selectCurrentTable, selectTableRows } from "render/table/tableSlice";
|
|
6
|
+
import { selectDoc } from "render/page/docSlice";
|
|
7
|
+
|
|
8
|
+
type AppConstraintPack = {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
rules: string[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const buildAppConstraintPacks = (args: {
|
|
15
|
+
framework: string;
|
|
16
|
+
fileNames: string[];
|
|
17
|
+
externalImports: string[];
|
|
18
|
+
}): AppConstraintPack[] => {
|
|
19
|
+
const imports = new Set(args.externalImports);
|
|
20
|
+
const fileSet = new Set(args.fileNames);
|
|
21
|
+
const packs: AppConstraintPack[] = [
|
|
22
|
+
{
|
|
23
|
+
id: "repair-loop",
|
|
24
|
+
title: "预检与定点修复",
|
|
25
|
+
rules: [
|
|
26
|
+
"每次改完代码后,先调用 appPreflight,只有预检通过后再调用 appDeploy。",
|
|
27
|
+
"如果 preflight / deploy 失败,优先根据返回的 issues 做定点修复,然后重新 preflight;不要无关重写整页应用。",
|
|
28
|
+
"如果工具结果里带有 repairPlan,默认立即执行 repairPlan 中的局部修复步骤,不要先停下来问用户。",
|
|
29
|
+
"如果工具结果明确说明是 HTML / 非 JSON 响应、transport failure 或 retryable=false,停止自动 deploy 重试;这说明当前是平台通道异常,不是代码问题。",
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "design-system",
|
|
34
|
+
title: "设计系统与小改动约束",
|
|
35
|
+
rules: [
|
|
36
|
+
"先检查当前应用是否已经有 theme / tokens / designSystem / 共享样式常量;如果已有,优先沿用这套设计系统,不要平行再造一套。",
|
|
37
|
+
"如果当前应用还没有设计系统,而用户需求涉及 UI / 样式 / 新页面,优先补一层最小共享 token(colors、typography、spacing、radius、shadow),再让组件消费这些 token。",
|
|
38
|
+
"如果当前应用是旧写法:视觉值散落在多个组件的硬编码 style / 常量里,而本次需求只是调字体、颜色、间距、圆角、阴影,默认执行一次最小 token 迁移,再在 token 层完成调整;不要继续把新数字散落写回多个位置。",
|
|
39
|
+
"当用户只是想调字体大小、字重、颜色、圆角、阴影、间距等视觉参数时,优先修改 token 或命中的局部组件,不要顺手重写整个页面结构。",
|
|
40
|
+
"除非用户明确要求整体改版或重做风格,否则不要连带修改布局、组件树、文案、数据流、路由或未命中的页面。",
|
|
41
|
+
"如果只是小改一处,尽量把变更收敛在少数文件,并保持未命中文件的结构与命名稳定。",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
if (args.framework === "react-spa" || fileSet.has("main.tsx") || fileSet.has("App.tsx")) {
|
|
47
|
+
packs.push({
|
|
48
|
+
id: "react-spa-core",
|
|
49
|
+
title: "React SPA 形态保持",
|
|
50
|
+
rules: [
|
|
51
|
+
'继续沿用 framework: "react-spa" + files,不要退回单文件 Worker。',
|
|
52
|
+
"保留稳定入口结构,优先维护 main.tsx + App.tsx,再局部修改组件文件。",
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (imports.has("react-icons/lu")) {
|
|
58
|
+
packs.push({
|
|
59
|
+
id: "react-icons-lu",
|
|
60
|
+
title: "Lucide 图标安全规则",
|
|
61
|
+
rules: [
|
|
62
|
+
"只能使用 react-icons/lu 中真实存在的图标名,不要猜测变体名。",
|
|
63
|
+
"如果不确定图标名,优先使用 LuCircle、LuCheck、LuX、LuInfo、LuArrowRight 这类基础图标。",
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (imports.has("leaflet") || imports.has("react-leaflet")) {
|
|
69
|
+
packs.push({
|
|
70
|
+
id: "leaflet",
|
|
71
|
+
title: "Leaflet 地图约束",
|
|
72
|
+
rules: [
|
|
73
|
+
"不要手动 import leaflet.css;平台会自动注入 Leaflet 样式。",
|
|
74
|
+
"修改地图时优先保留现有坐标、缩放和图层结构,只做必要的交互或视觉调整。",
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (imports.has("@xyflow/react")) {
|
|
80
|
+
packs.push({
|
|
81
|
+
id: "xyflow",
|
|
82
|
+
title: "Flow 图编辑约束",
|
|
83
|
+
rules: [
|
|
84
|
+
"不要新增外部 CSS import;需要样式时直接补最小必要内联样式或组件内 style 标签。",
|
|
85
|
+
"优先复用现有节点/边数据结构,避免重写整套 flow 画布。",
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (imports.has("echarts") || imports.has("echarts-for-react")) {
|
|
91
|
+
packs.push({
|
|
92
|
+
id: "echarts",
|
|
93
|
+
title: "图表改动约束",
|
|
94
|
+
rules: [
|
|
95
|
+
"优先局部修改 option 配置、数据映射和组件 props,不要重写整个图表页面。",
|
|
96
|
+
"响应式布局优先靠容器尺寸和现有组件结构调整,不要额外引入 CSS 文件。",
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (imports.has("docx") || imports.has("xlsx")) {
|
|
102
|
+
packs.push({
|
|
103
|
+
id: "file-processing",
|
|
104
|
+
title: "文档/表格处理约束",
|
|
105
|
+
rules: [
|
|
106
|
+
"不要把大型数据或模板内容直接内嵌进代码常量;优先保留运行时加载方式。",
|
|
107
|
+
"修改导入导出逻辑时,优先沿用现有文件处理链路和数据结构。",
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return packs;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const formatAppConstraintPacks = (packs: AppConstraintPack[]): string[] => {
|
|
116
|
+
if (packs.length === 0) return [];
|
|
117
|
+
|
|
118
|
+
return [
|
|
119
|
+
"",
|
|
120
|
+
"当前激活约束包(按当前应用依赖/形态动态注入):",
|
|
121
|
+
...packs.flatMap((pack) => [
|
|
122
|
+
`- ${pack.title}(${pack.id})`,
|
|
123
|
+
...pack.rules.map((rule) => ` - ${rule}`),
|
|
124
|
+
]),
|
|
125
|
+
];
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 根据当前 Redux 状态 + 本次调用的 runtimeOptions,生成「当前编辑对象」的自然语言描述。
|
|
130
|
+
*
|
|
131
|
+
* 设计目标:
|
|
132
|
+
* - 只返回一段 string 或 null,不关心 Prompt 结构。
|
|
133
|
+
* - 不涉及任何持久化(DialogConfig / AgentConfig),完全是运行时。
|
|
134
|
+
* - 目前重点支持:
|
|
135
|
+
* - kind === "table": 当前表格 + 部分行数据
|
|
136
|
+
* - kind === "page" | "article": 当前页面/文章的标题(正文后面可以再丰富)
|
|
137
|
+
*/
|
|
138
|
+
export const buildEditingContextSummary = (
|
|
139
|
+
state: RootState,
|
|
140
|
+
runtimeOptions?: AgentRuntimeOptions
|
|
141
|
+
): string | null => {
|
|
142
|
+
const targetKind = runtimeOptions?.editingTarget?.kind;
|
|
143
|
+
|
|
144
|
+
// 1) 表格场景:基于当前表 meta + 行数据
|
|
145
|
+
if (targetKind === "table") {
|
|
146
|
+
const table = selectCurrentTable(state);
|
|
147
|
+
const rows = selectTableRows(state);
|
|
148
|
+
const metadata = runtimeOptions?.editingTarget?.metadata;
|
|
149
|
+
const focusContext =
|
|
150
|
+
metadata && typeof metadata === "object" ? metadata.focusContext : null;
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if (!table) return null;
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
const columns = Array.isArray(table.columns) ? table.columns : [];
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
const columnSummaries = columns.length
|
|
160
|
+
? columns.map((c) => {
|
|
161
|
+
const displayName = c.label || c.name;
|
|
162
|
+
const type = c.type || "text";
|
|
163
|
+
const requiredFlag = c.required ? "必填" : "可选";
|
|
164
|
+
const primaryFlag = c.isPrimary ? ",主字段" : "";
|
|
165
|
+
const optionsStr =
|
|
166
|
+
Array.isArray(c.options) && c.options.length
|
|
167
|
+
? `,可选值:${c.options.join(" | ")}`
|
|
168
|
+
: "";
|
|
169
|
+
const descStr = c.description ? `。说明:${c.description}` : "";
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
return `- ${c.name}(显示名:${displayName},类型:${type},${requiredFlag}${primaryFlag}${optionsStr}${descStr})`;
|
|
173
|
+
})
|
|
174
|
+
: ["- (当前表尚未定义字段)"];
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
// 只给出前 20 行作为结构示例,避免 prompt 过长
|
|
178
|
+
const sampleRows = Array.isArray(rows) ? rows.slice(0, 20) : [];
|
|
179
|
+
const rowsPreview = sampleRows.length
|
|
180
|
+
? JSON.stringify(sampleRows, null, 2)
|
|
181
|
+
: "(当前表暂无行数据)";
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
const tableTitle = table.displayName ?? table.tableId ?? "(未命名表)";
|
|
185
|
+
const tableDesc = table.description
|
|
186
|
+
? `用途说明:${table.description}`
|
|
187
|
+
: "(暂无用途说明)";
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
// ✅ 新增:如果有表级 tags,把它们也告诉 Agent
|
|
191
|
+
const tableTagsLine =
|
|
192
|
+
Array.isArray(table.tags) && table.tags.length
|
|
193
|
+
? `- 关键词标签: ${table.tags.join(", ")}`
|
|
194
|
+
: null;
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
return [
|
|
198
|
+
"当前编辑目标:一张数据表(Editing Table)。",
|
|
199
|
+
`- 表 ID: ${table.tableId}`,
|
|
200
|
+
`- 显示名称: ${tableTitle}`,
|
|
201
|
+
`- ${tableDesc}`,
|
|
202
|
+
...(tableTagsLine ? [tableTagsLine] : []),
|
|
203
|
+
...(focusContext &&
|
|
204
|
+
typeof focusContext === "object" &&
|
|
205
|
+
"columnName" in focusContext
|
|
206
|
+
? [
|
|
207
|
+
"",
|
|
208
|
+
"当前焦点(Focus Context):",
|
|
209
|
+
`- 当前单元格列: ${String((focusContext as any).columnName ?? "(未知列)")}`,
|
|
210
|
+
...((focusContext as any).rowTitle
|
|
211
|
+
? [`- 当前行标题: ${String((focusContext as any).rowTitle)}`]
|
|
212
|
+
: []),
|
|
213
|
+
...((focusContext as any).rowIndex !== null &&
|
|
214
|
+
(focusContext as any).rowIndex !== undefined
|
|
215
|
+
? [`- 当前行号: ${Number((focusContext as any).rowIndex) + 1}`]
|
|
216
|
+
: []),
|
|
217
|
+
...((focusContext as any).cellPreview
|
|
218
|
+
? [`- 当前单元格内容预览: ${String((focusContext as any).cellPreview)}`]
|
|
219
|
+
: []),
|
|
220
|
+
]
|
|
221
|
+
: []),
|
|
222
|
+
"",
|
|
223
|
+
"字段定义(Field Schema):",
|
|
224
|
+
...columnSummaries,
|
|
225
|
+
"",
|
|
226
|
+
"【给 AI 的操作指南 / 非用户原话】",
|
|
227
|
+
"当用户希望在当前表中“新增一行 / 新增记录 / 插入一条 / 帮我记一条 xxx”时:",
|
|
228
|
+
"1. 必须调用工具 addTableRow(而不是只在回答中口头描述要新增的数据)。",
|
|
229
|
+
"2. 调用 addTableRow 时:",
|
|
230
|
+
" - 使用参数 values(一个对象),其中每个 key 必须是上面字段名之一(name,而不是 label)。",
|
|
231
|
+
' - 例如:{"values":{"title":"修 Bug #123","status":"todo","note":"高优先级"}}。',
|
|
232
|
+
" - 尽量从用户的自然语言中推断并填满所有相关字段;对于必填字段(required=true)尤其要注意。",
|
|
233
|
+
' - 用户没有提到的字段,可以使用空字符串 "" 或 null 作为占位。',
|
|
234
|
+
" - 绝不要传入空对象 {} 作为 values。",
|
|
235
|
+
"",
|
|
236
|
+
"以下是部分示例行(最多 20 行,用于帮助你理解列含义,请避免在回答中完整粘贴整表):",
|
|
237
|
+
rowsPreview,
|
|
238
|
+
].join("\n");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
// 2) 页面 / 文章场景:先只提供标题信息(正文后面可以在这里丰富)
|
|
245
|
+
if (targetKind === "page" || targetKind === "article") {
|
|
246
|
+
const page = selectDoc(state);
|
|
247
|
+
const metadata = runtimeOptions?.editingTarget?.metadata;
|
|
248
|
+
const focusContext =
|
|
249
|
+
metadata && typeof metadata === "object" ? metadata.focusContext : null;
|
|
250
|
+
if (!page) return null;
|
|
251
|
+
|
|
252
|
+
const title = page.title ?? "(未命名页面)";
|
|
253
|
+
|
|
254
|
+
return [
|
|
255
|
+
"当前编辑目标:一个页面 / 文章(Editing Document)。",
|
|
256
|
+
`- 标题: ${title}`,
|
|
257
|
+
...(focusContext &&
|
|
258
|
+
typeof focusContext === "object" &&
|
|
259
|
+
"anchorPath" in focusContext
|
|
260
|
+
? [
|
|
261
|
+
"",
|
|
262
|
+
"当前焦点(Focus Context):",
|
|
263
|
+
`- 光标是否折叠: ${Boolean((focusContext as any).isCollapsed) ? "是" : "否"}`,
|
|
264
|
+
...((focusContext as any).blockType
|
|
265
|
+
? [`- 当前块类型: ${String((focusContext as any).blockType)}`]
|
|
266
|
+
: []),
|
|
267
|
+
...((focusContext as any).selectedText
|
|
268
|
+
? [`- 当前选中文本: ${String((focusContext as any).selectedText)}`]
|
|
269
|
+
: []),
|
|
270
|
+
...((focusContext as any).anchorPath?.length
|
|
271
|
+
? [`- 当前锚点路径: ${(focusContext as any).anchorPath.join(" > ")}`]
|
|
272
|
+
: []),
|
|
273
|
+
]
|
|
274
|
+
: []),
|
|
275
|
+
"",
|
|
276
|
+
"如果用户要求你对这篇文章进行修改或优化,请在回答中明确指出修改方向。",
|
|
277
|
+
"如果存在当前选区或光标上下文,优先围绕该局部位置做定点改写,而不是泛泛重写整篇文档。",
|
|
278
|
+
].join("\n");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (targetKind === "app") {
|
|
282
|
+
const editingTarget = runtimeOptions?.editingTarget;
|
|
283
|
+
const metadata = editingTarget?.metadata;
|
|
284
|
+
const appId = editingTarget?.key ?? "(未知 appId)";
|
|
285
|
+
const title = editingTarget?.title ?? "(未命名应用)";
|
|
286
|
+
const framework =
|
|
287
|
+
typeof metadata?.framework === "string"
|
|
288
|
+
? metadata.framework
|
|
289
|
+
: "worker";
|
|
290
|
+
const appUrl =
|
|
291
|
+
typeof metadata?.appUrl === "string" ? metadata.appUrl : null;
|
|
292
|
+
const fileNames = Array.isArray(metadata?.fileNames)
|
|
293
|
+
? metadata.fileNames.filter((name): name is string => typeof name === "string")
|
|
294
|
+
: [];
|
|
295
|
+
const externalImports = Array.isArray(metadata?.externalImports)
|
|
296
|
+
? metadata.externalImports.filter(
|
|
297
|
+
(name): name is string => typeof name === "string"
|
|
298
|
+
)
|
|
299
|
+
: [];
|
|
300
|
+
const sourceSummary =
|
|
301
|
+
typeof editingTarget?.summary === "string" && editingTarget.summary.trim()
|
|
302
|
+
? editingTarget.summary.trim()
|
|
303
|
+
: null;
|
|
304
|
+
const constraintPacks = buildAppConstraintPacks({
|
|
305
|
+
framework,
|
|
306
|
+
fileNames,
|
|
307
|
+
externalImports,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return [
|
|
311
|
+
"当前编辑目标:一个 Web 应用(Editing App)。",
|
|
312
|
+
`- 应用 ID: ${appId}`,
|
|
313
|
+
`- 名称: ${title}`,
|
|
314
|
+
`- 技术形态: ${framework}`,
|
|
315
|
+
...(appUrl ? [`- 当前访问地址: ${appUrl}`] : []),
|
|
316
|
+
...(fileNames.length
|
|
317
|
+
? [`- 当前源码文件: ${fileNames.join(", ")}`]
|
|
318
|
+
: ["- 当前源码文件: (未提供多文件清单;可能是单文件源码,也可能只剩部署产物)"]),
|
|
319
|
+
...(externalImports.length
|
|
320
|
+
? [`- 当前依赖白名单命中: ${externalImports.join(", ")}`]
|
|
321
|
+
: []),
|
|
322
|
+
...(sourceSummary ? ["", sourceSummary] : []),
|
|
323
|
+
"",
|
|
324
|
+
"【给 AI 的操作指南 / 非用户原话】",
|
|
325
|
+
"1. 用户要求修改当前应用时,先调用 appRead 获取当前代码/文件,再基于现有实现修改。",
|
|
326
|
+
"2. 先识别当前应用是否已有 theme / tokens / design system;已有就优先改这层,没有再补一层最小共享 token,再基于 token 调整组件。",
|
|
327
|
+
"3. 如果当前应用还是旧写法:视觉值散落在组件硬编码 style 里,而用户只是做字体/颜色/间距等视觉微调,默认先把命中的视觉值抽到最小 token 层,再完成本次修改;除非用户明确要求不要重构。",
|
|
328
|
+
`4. 重新部署当前应用时,appDeploy 必须继续传同一个 appId(${appId}),避免创建新应用。`,
|
|
329
|
+
framework === "react-spa"
|
|
330
|
+
? '5. 当前应用是 React SPA,修改时继续沿用 framework: "react-spa" + files,不要退回单文件 Worker。'
|
|
331
|
+
: "5. 当前应用目前是 Worker 形态;如果需求变成复杂交互或图表,可以评估升级为 React SPA。",
|
|
332
|
+
...(fileNames.length
|
|
333
|
+
? []
|
|
334
|
+
: [
|
|
335
|
+
"6. 当前没有源码文件清单时,必须先用 appRead 判断读到的是可维护源码还是部署产物 / 打包 bundle。",
|
|
336
|
+
"7. 如果 appRead 返回的是 HTML 壳、importmap、压缩 bundle 或明显不是原始源码文件,禁止在未告知用户风险的情况下整站重写;应先说明“当前缺少原始源码快照,继续修改更像整体重建”,等用户确认后再继续。",
|
|
337
|
+
]),
|
|
338
|
+
"8. 如果用户只是要调字体大小、配色、圆角、阴影、留白等视觉细节,默认优先改设计 token 或命中的局部组件;对旧写法应用则优先做最小 token 迁移,不要只改散落硬编码。",
|
|
339
|
+
"9. 每次修改完成后,先 appPreflight,再 appDeploy;如果失败,按返回 issues 定点修复。",
|
|
340
|
+
"10. 回复用户时优先说明做了什么变化、现在应用可以怎么用,而不是直接堆代码。",
|
|
341
|
+
...formatAppConstraintPacks(constraintPacks),
|
|
342
|
+
].join("\n");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (targetKind === "image" || targetKind === "file") {
|
|
346
|
+
const editingTarget = runtimeOptions?.editingTarget;
|
|
347
|
+
const metadata = editingTarget?.metadata;
|
|
348
|
+
const title = editingTarget?.title ?? "(未命名对象)";
|
|
349
|
+
const objectKey = editingTarget?.key ?? "(未知 key)";
|
|
350
|
+
const fileId =
|
|
351
|
+
typeof metadata?.fileId === "string" ? metadata.fileId : null;
|
|
352
|
+
const url = typeof metadata?.url === "string" ? metadata.url : null;
|
|
353
|
+
const size =
|
|
354
|
+
typeof metadata?.size === "number" ? metadata.size : null;
|
|
355
|
+
|
|
356
|
+
return [
|
|
357
|
+
`当前编辑目标:一个${targetKind === "image" ? "图片" : "文件"}对象(Editing ${targetKind === "image" ? "Image" : "File"})。`,
|
|
358
|
+
`- 对象 key: ${objectKey}`,
|
|
359
|
+
`- 标题: ${title}`,
|
|
360
|
+
...(fileId ? [`- fileId: ${fileId}`] : []),
|
|
361
|
+
...(url ? [`- 资源地址: ${url}`] : []),
|
|
362
|
+
...(size !== null ? [`- 文件大小: ${size} bytes`] : []),
|
|
363
|
+
"",
|
|
364
|
+
"【给 AI 的操作指南 / 非用户原话】",
|
|
365
|
+
targetKind === "image"
|
|
366
|
+
? "当前阶段优先帮助用户理解图片内容、提炼重点、命名归类和下一步处理建议。不要假装已经完成复杂图片编辑。"
|
|
367
|
+
: "当前阶段优先帮助用户理解文件用途、提取处理思路、给出整理建议和下一步操作建议。不要假装已经完整解析文件内容。",
|
|
368
|
+
].join("\n");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 其它 kind 以后按需扩展
|
|
372
|
+
return null;
|
|
373
|
+
};
|