nolo-cli 0.1.41 → 0.1.43
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 +141 -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
package/ai/tools/prepareTools.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import i18n from "../../app/i18n";
|
|
1
2
|
import {
|
|
2
3
|
toolRegistry,
|
|
3
4
|
toolDefinitionsByName,
|
|
@@ -7,6 +8,167 @@ import {
|
|
|
7
8
|
import { canonicalizeToolNames } from "./toolNameAliases";
|
|
8
9
|
import { sanitizeToolForProvider } from "./toolSchemaCompatibility";
|
|
9
10
|
|
|
11
|
+
function getTranslation(keys: string[], defaultVal: string): string {
|
|
12
|
+
for (const key of keys) {
|
|
13
|
+
if (i18n.exists(key)) {
|
|
14
|
+
return i18n.t(key);
|
|
15
|
+
}
|
|
16
|
+
const aiKey = key.startsWith("ai:") ? key : `ai:${key}`;
|
|
17
|
+
if (i18n.exists(aiKey)) {
|
|
18
|
+
return i18n.t(aiKey);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return defaultVal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function translateSchema(schema: any): any {
|
|
25
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
26
|
+
|
|
27
|
+
const nextSchema = JSON.parse(JSON.stringify(schema));
|
|
28
|
+
const toolName = nextSchema.name;
|
|
29
|
+
if (!toolName) return nextSchema;
|
|
30
|
+
|
|
31
|
+
const walk = (node: any, path: string) => {
|
|
32
|
+
if (!node || typeof node !== "object") return;
|
|
33
|
+
|
|
34
|
+
if (typeof node.description === "string") {
|
|
35
|
+
const keysToTry = [
|
|
36
|
+
`${path}.description`,
|
|
37
|
+
`${path}.desc`,
|
|
38
|
+
path,
|
|
39
|
+
node.description,
|
|
40
|
+
];
|
|
41
|
+
node.description = getTranslation(keysToTry, node.description);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (node.parameters && typeof node.parameters === "object") {
|
|
45
|
+
walk(node.parameters, path);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (node.properties && typeof node.properties === "object") {
|
|
49
|
+
for (const [key, child] of Object.entries(node.properties)) {
|
|
50
|
+
const nextPath = path.endsWith(".params") ? `${path}.${key}` : `${path}.params.${key}`;
|
|
51
|
+
walk(child, nextPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (node.items && typeof node.items === "object") {
|
|
56
|
+
walk(node.items, path);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const compositions = ["anyOf", "oneOf", "allOf"] as const;
|
|
60
|
+
for (const comp of compositions) {
|
|
61
|
+
if (Array.isArray(node[comp])) {
|
|
62
|
+
node[comp].forEach((child: any, idx: number) => {
|
|
63
|
+
const suffix = child.type === "string" ? "stringDesc" : child.type === "object" ? "objectDesc" : `${idx}`;
|
|
64
|
+
walk(child, `${path}.${suffix}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
walk(nextSchema, `tools.${toolName}`);
|
|
71
|
+
return nextSchema;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buildActivitySchema() {
|
|
75
|
+
const refs = {
|
|
76
|
+
type: "array",
|
|
77
|
+
items: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
type: { type: "string", enum: ["file", "terminal", "url"] },
|
|
81
|
+
path: { type: "string" },
|
|
82
|
+
id: { type: "string" },
|
|
83
|
+
label: { type: "string" },
|
|
84
|
+
url: { type: "string" },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
const action = {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
title: { type: "string" },
|
|
92
|
+
kind: {
|
|
93
|
+
type: "string",
|
|
94
|
+
enum: ["read", "write", "edit", "search", "terminal", "version", "test", "build", "preview", "other"],
|
|
95
|
+
},
|
|
96
|
+
detail: { type: "string" },
|
|
97
|
+
refs,
|
|
98
|
+
},
|
|
99
|
+
required: ["title"],
|
|
100
|
+
};
|
|
101
|
+
const planPhase = {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
id: { type: "string" },
|
|
105
|
+
title: { type: "string" },
|
|
106
|
+
index: { type: "number" },
|
|
107
|
+
status: { type: "string", enum: ["pending", "running", "success", "failed"] },
|
|
108
|
+
},
|
|
109
|
+
required: ["id", "title"],
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
type: "object",
|
|
113
|
+
description:
|
|
114
|
+
"Optional human-readable activity metadata for UI progress display only. Use plan for the visible task skeleton, phase for the current task step, and action for this specific tool call.",
|
|
115
|
+
properties: {
|
|
116
|
+
plan: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
title: { type: "string" },
|
|
120
|
+
phases: {
|
|
121
|
+
type: "array",
|
|
122
|
+
items: planPhase,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
required: ["phases"],
|
|
126
|
+
},
|
|
127
|
+
phase: {
|
|
128
|
+
type: "object",
|
|
129
|
+
properties: {
|
|
130
|
+
id: { type: "string" },
|
|
131
|
+
title: { type: "string" },
|
|
132
|
+
index: { type: "number" },
|
|
133
|
+
total: { type: "number" },
|
|
134
|
+
status: { type: "string", enum: ["pending", "running", "success", "failed"] },
|
|
135
|
+
},
|
|
136
|
+
required: ["id", "title"],
|
|
137
|
+
},
|
|
138
|
+
action,
|
|
139
|
+
title: { type: "string" },
|
|
140
|
+
kind: action.properties.kind,
|
|
141
|
+
detail: { type: "string" },
|
|
142
|
+
refs,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function addActivityMetadataToToolSchema(tool: any) {
|
|
148
|
+
if (!tool?.function?.parameters || typeof tool.function.parameters !== "object") {
|
|
149
|
+
return tool;
|
|
150
|
+
}
|
|
151
|
+
const parameters = tool.function.parameters;
|
|
152
|
+
if (parameters.type !== "object" || !parameters.properties || typeof parameters.properties !== "object") {
|
|
153
|
+
return tool;
|
|
154
|
+
}
|
|
155
|
+
if (parameters.properties._activity) return tool;
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
...tool,
|
|
159
|
+
function: {
|
|
160
|
+
...tool.function,
|
|
161
|
+
parameters: {
|
|
162
|
+
...parameters,
|
|
163
|
+
properties: {
|
|
164
|
+
...parameters.properties,
|
|
165
|
+
_activity: buildActivitySchema(),
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
10
172
|
export const prepareTools = (
|
|
11
173
|
toolNames: string[],
|
|
12
174
|
options?: { provider?: string; disabledToolNames?: string[] }
|
|
@@ -17,7 +179,15 @@ export const prepareTools = (
|
|
|
17
179
|
);
|
|
18
180
|
return canonicalizeToolNames(toolNames)
|
|
19
181
|
.filter((toolName) => !disabledToolNames.has(toolName))
|
|
20
|
-
.map((toolName: string) =>
|
|
182
|
+
.map((toolName: string) => {
|
|
183
|
+
const regTool = toolRegistry[toolName];
|
|
184
|
+
if (!regTool) return regTool;
|
|
185
|
+
return {
|
|
186
|
+
...regTool,
|
|
187
|
+
function: translateSchema(regTool.function),
|
|
188
|
+
};
|
|
189
|
+
})
|
|
190
|
+
.map(addActivityMetadataToToolSchema)
|
|
21
191
|
.map((tool) => sanitizeToolForProvider(tool, options?.provider))
|
|
22
192
|
.filter(Boolean); // 过滤掉未找到的工具
|
|
23
193
|
};
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
* read_xhs_profile Tool
|
|
3
3
|
*
|
|
4
4
|
* Read-only tool for collecting XHS (Xiaohongshu) profile data:
|
|
5
|
-
* profile info
|
|
5
|
+
* anonymous public profile info and public notes visible without login.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Anonymous-only: does not use XHS login state, cookies, or persistent profile.
|
|
8
8
|
* No write actions (no like/comment/follow/collect/post/delete).
|
|
9
9
|
* Cookies and xsecToken are never exposed in output.
|
|
10
10
|
* Conservative by default: reads the initially loaded profile state only.
|
|
11
|
-
* Extra scrolling
|
|
11
|
+
* Extra scrolling requires explicit user intent. Direct detail/comment API
|
|
12
|
+
* enrichment is disabled so reads stay close to visible desktop browsing.
|
|
12
13
|
* The tool clamps extended collection unless extendedCollectionConsent is true.
|
|
13
14
|
*/
|
|
14
15
|
|
|
@@ -17,21 +18,23 @@ import type {
|
|
|
17
18
|
XhsReadResult,
|
|
18
19
|
XhsProfileCollection,
|
|
19
20
|
XhsCollectionStatus,
|
|
21
|
+
XhsParsedProfileUrl,
|
|
20
22
|
} from "../../integrations/xhs-reader/types";
|
|
23
|
+
import { redactXhsSensitiveValue } from "../../integrations/xhs-reader/redaction";
|
|
21
24
|
import { parseXhsProfileUrl } from "../../integrations/xhs-reader/url";
|
|
22
25
|
import { getRequestConfig, ToolApiError } from "./toolApiClient";
|
|
23
26
|
|
|
24
27
|
export const readXhsProfileFunctionSchema = {
|
|
25
28
|
name: "read_xhs_profile",
|
|
26
29
|
description:
|
|
27
|
-
"
|
|
30
|
+
"以未登录匿名访客身份读取小红书用户主页的公开可见信息。默认只读取页面初始加载出的资料和笔记列表,不自动翻页,不抓评论或批量详情。只有当用户明确要求更多笔记/翻页时,才可显式请求一次可见页面滚动。不会使用账号、cookie 或持久浏览器 profile。",
|
|
28
31
|
parameters: {
|
|
29
32
|
type: "object",
|
|
30
33
|
properties: {
|
|
31
34
|
url: {
|
|
32
35
|
type: "string",
|
|
33
36
|
description:
|
|
34
|
-
"小红书用户主页 URL,例如 https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556
|
|
37
|
+
"小红书用户主页 URL,例如 https://www.xiaohongshu.com/user/profile/5d2be8720000000010007556。用户从首页/feed 复制的 profile 链接可以带 xsec_token/xsec_source;token 只用于匿名桌面导航,输出会脱敏。",
|
|
35
38
|
},
|
|
36
39
|
maxScrollPages: {
|
|
37
40
|
type: "number",
|
|
@@ -42,29 +45,29 @@ export const readXhsProfileFunctionSchema = {
|
|
|
42
45
|
includeComments: {
|
|
43
46
|
type: "boolean",
|
|
44
47
|
description:
|
|
45
|
-
"
|
|
48
|
+
"是否读取匿名公开笔记详情页首屏可见评论。只有 extendedCollectionConsent=true、collectionMode=\"assisted\"、assistedAction=\"read_visible_details\" 时才会生效;不会通过直接 API 收集评论。",
|
|
46
49
|
default: false,
|
|
47
50
|
},
|
|
48
51
|
maxCommentPagesPerNote: {
|
|
49
52
|
type: "number",
|
|
50
53
|
description:
|
|
51
|
-
"
|
|
54
|
+
"保留兼容字段,当前固定为 1 且不会触发评论采集。",
|
|
52
55
|
default: 1,
|
|
53
56
|
},
|
|
54
57
|
minLikesForDetail: {
|
|
55
58
|
type: "number",
|
|
56
59
|
description:
|
|
57
|
-
"
|
|
60
|
+
"已停用:不再通过直接 API 收集笔记详情。",
|
|
58
61
|
},
|
|
59
62
|
minCommentsForCollect: {
|
|
60
63
|
type: "number",
|
|
61
64
|
description:
|
|
62
|
-
"
|
|
65
|
+
"已停用:不再通过直接 API 收集评论。",
|
|
63
66
|
},
|
|
64
67
|
extendedCollectionConsent: {
|
|
65
68
|
type: "boolean",
|
|
66
69
|
description:
|
|
67
|
-
"
|
|
70
|
+
"扩展采集确认开关。只有用户在本轮明确要求更多笔记、翻页、公开笔记详情或首屏评论时才可设为 true;否则必须省略或为 false。匿名模式不会采集登录后内容。",
|
|
68
71
|
default: false,
|
|
69
72
|
},
|
|
70
73
|
collectionMode: {
|
|
@@ -76,34 +79,29 @@ export const readXhsProfileFunctionSchema = {
|
|
|
76
79
|
},
|
|
77
80
|
assistedAction: {
|
|
78
81
|
type: "string",
|
|
79
|
-
enum: ["snapshot", "read_more_notes", "
|
|
82
|
+
enum: ["snapshot", "read_more_notes", "read_visible_details", "discover_indexed_notes"],
|
|
80
83
|
description:
|
|
81
|
-
"assisted 模式下的具体动作:snapshot 只读快照,read_more_notes
|
|
84
|
+
"assisted 模式下的具体动作:snapshot 只读快照,read_more_notes 最多做一次可见页面滚动,read_visible_details 匿名打开最多 3 条公开笔记页读取页面可见详情和首屏评论,discover_indexed_notes 读取外部搜索索引发现的公开笔记 URL。",
|
|
82
85
|
default: "snapshot",
|
|
83
86
|
},
|
|
87
|
+
indexedNoteUrls: {
|
|
88
|
+
type: "array",
|
|
89
|
+
items: { type: "string" },
|
|
90
|
+
description:
|
|
91
|
+
"外部搜索索引发现的公开小红书笔记 URL(xiaohongshu.com/explore/...)。仅当 assistedAction=\"discover_indexed_notes\" 且 extendedCollectionConsent=true 时生效,最多读取 3 条;不要填搜索页 URL。",
|
|
92
|
+
},
|
|
84
93
|
maxAssistedSteps: {
|
|
85
94
|
type: "number",
|
|
86
95
|
description:
|
|
87
|
-
"assisted
|
|
96
|
+
"assisted 模式最大步数。当前固定为 1,只允许一次可见页面滚动。",
|
|
88
97
|
default: 1,
|
|
89
98
|
minimum: 1,
|
|
90
|
-
maximum:
|
|
91
|
-
},
|
|
92
|
-
profileDir: {
|
|
93
|
-
type: "string",
|
|
94
|
-
description:
|
|
95
|
-
"桌面端本地 Playwright 持久 profile 目录,用于复用小红书登录态。不要传入用户日常 Chrome profile。默认使用专用缓存目录。",
|
|
99
|
+
maximum: 1,
|
|
96
100
|
},
|
|
97
101
|
headless: {
|
|
98
102
|
type: "boolean",
|
|
99
103
|
description:
|
|
100
|
-
"是否用 headless
|
|
101
|
-
default: true,
|
|
102
|
-
},
|
|
103
|
-
keepOpen: {
|
|
104
|
-
type: "boolean",
|
|
105
|
-
description:
|
|
106
|
-
"调试时是否保持浏览器上下文打开(方便手动登录)。默认 false。",
|
|
104
|
+
"是否用 headless 模式启动匿名浏览器。默认 false,优先打开可见桌面浏览器。",
|
|
107
105
|
default: false,
|
|
108
106
|
},
|
|
109
107
|
},
|
|
@@ -125,12 +123,11 @@ export interface ReadXhsProfileArgs {
|
|
|
125
123
|
minLikesForDetail?: number;
|
|
126
124
|
minCommentsForCollect?: number;
|
|
127
125
|
extendedCollectionConsent?: boolean;
|
|
128
|
-
profileDir?: string;
|
|
129
126
|
headless?: boolean;
|
|
130
|
-
keepOpen?: boolean;
|
|
131
127
|
collectionMode?: "conservative" | "assisted";
|
|
132
|
-
assistedAction?: "snapshot" | "read_more_notes" | "
|
|
128
|
+
assistedAction?: "snapshot" | "read_more_notes" | "read_visible_details" | "discover_indexed_notes";
|
|
133
129
|
maxAssistedSteps?: number;
|
|
130
|
+
indexedNoteUrls?: string[];
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
export function normalizeXhsProfileReadArgs(
|
|
@@ -157,7 +154,7 @@ export function normalizeXhsProfileReadArgs(
|
|
|
157
154
|
// Consent granted → determine mode
|
|
158
155
|
const mode = args.collectionMode === "assisted" ? "assisted" : "conservative";
|
|
159
156
|
const action = args.assistedAction ?? "snapshot";
|
|
160
|
-
const steps =
|
|
157
|
+
const steps = 1;
|
|
161
158
|
|
|
162
159
|
if (mode === "conservative") {
|
|
163
160
|
// Consent alone is not enough for extended collection. The model must
|
|
@@ -184,10 +181,14 @@ export function normalizeXhsProfileReadArgs(
|
|
|
184
181
|
if (action === "read_more_notes") {
|
|
185
182
|
maxScrollPages = clamp(maxScrollPages ?? steps, 1, steps);
|
|
186
183
|
includeComments = false;
|
|
187
|
-
} else if (action === "
|
|
184
|
+
} else if (action === "read_visible_details") {
|
|
185
|
+
maxScrollPages = 0;
|
|
188
186
|
includeComments = true;
|
|
189
|
-
maxCommentPagesPerNote =
|
|
187
|
+
maxCommentPagesPerNote = 3;
|
|
188
|
+
} else if (action === "discover_indexed_notes") {
|
|
190
189
|
maxScrollPages = 0;
|
|
190
|
+
includeComments = true;
|
|
191
|
+
maxCommentPagesPerNote = 3;
|
|
191
192
|
} else {
|
|
192
193
|
// snapshot: no scrolling, no comments
|
|
193
194
|
maxScrollPages = 0;
|
|
@@ -200,12 +201,15 @@ export function normalizeXhsProfileReadArgs(
|
|
|
200
201
|
maxScrollPages,
|
|
201
202
|
includeComments,
|
|
202
203
|
maxCommentPagesPerNote,
|
|
203
|
-
minLikesForDetail:
|
|
204
|
-
minCommentsForCollect:
|
|
204
|
+
minLikesForDetail: undefined,
|
|
205
|
+
minCommentsForCollect: undefined,
|
|
205
206
|
extendedCollectionConsent: true,
|
|
206
207
|
collectionMode: mode,
|
|
207
208
|
assistedAction: action,
|
|
208
209
|
maxAssistedSteps: steps,
|
|
210
|
+
indexedNoteUrls: action === "discover_indexed_notes"
|
|
211
|
+
? (args.indexedNoteUrls ?? []).slice(0, 3)
|
|
212
|
+
: undefined,
|
|
209
213
|
};
|
|
210
214
|
}
|
|
211
215
|
|
|
@@ -213,10 +217,10 @@ function clamp(value: number, min: number, max: number): number {
|
|
|
213
217
|
return Math.min(Math.max(value, min), max);
|
|
214
218
|
}
|
|
215
219
|
|
|
216
|
-
function assertXhsProfileUrl(url: string) {
|
|
220
|
+
function assertXhsProfileUrl(url: string): XhsParsedProfileUrl {
|
|
217
221
|
// Reuse parseXhsProfileUrl for validation
|
|
218
222
|
try {
|
|
219
|
-
parseXhsProfileUrl(url);
|
|
223
|
+
return parseXhsProfileUrl(url);
|
|
220
224
|
} catch {
|
|
221
225
|
throw new Error(
|
|
222
226
|
"read_xhs_profile 需要一个有效的小红书用户主页 URL(xiaohongshu.com/user/profile/<24位hexID>)。",
|
|
@@ -370,15 +374,15 @@ function formatDisplay(result: XhsReadResult<XhsProfileCollection>) {
|
|
|
370
374
|
const hints: string[] = [];
|
|
371
375
|
if (result.code === "login_required") {
|
|
372
376
|
hints.push(
|
|
373
|
-
"
|
|
377
|
+
"匿名公开访问遇到登录墙:当前用户主页对未登录访客不可见,read_xhs_profile 不会请求登录或复用账号。",
|
|
374
378
|
);
|
|
375
379
|
} else if (result.code === "blocked") {
|
|
376
380
|
hints.push(
|
|
377
|
-
"
|
|
381
|
+
"匿名公开访问被安全检查拦截。请稍后重试,或接受匿名模式下无法读取该页面。",
|
|
378
382
|
);
|
|
379
383
|
} else if (result.code === "empty_profile_state") {
|
|
380
384
|
hints.push(
|
|
381
|
-
"
|
|
385
|
+
"页面加载但未获取到笔记数据。建议:确认该用户有公开笔记;如果页面需要登录,匿名模式会停止而不是切换账号。",
|
|
382
386
|
);
|
|
383
387
|
}
|
|
384
388
|
return [
|
|
@@ -387,8 +391,8 @@ function formatDisplay(result: XhsReadResult<XhsProfileCollection>) {
|
|
|
387
391
|
"",
|
|
388
392
|
"可能的解决步骤:",
|
|
389
393
|
"- 确认 URL 是有效的小红书用户主页链接",
|
|
390
|
-
"-
|
|
391
|
-
"-
|
|
394
|
+
"- 如果页面只对登录用户可见,接受匿名模式无法读取该内容",
|
|
395
|
+
"- 如果是临时网络或页面加载问题,可以稍后重试或增加 timeoutMs",
|
|
392
396
|
"- 检查 Playwright chromium 是否已安装",
|
|
393
397
|
...hints,
|
|
394
398
|
]
|
|
@@ -424,6 +428,11 @@ function formatDisplay(result: XhsReadResult<XhsProfileCollection>) {
|
|
|
424
428
|
if (noteDetails.length > 0) {
|
|
425
429
|
parts.push(`已获取详情: ${noteDetails.length} 篇`);
|
|
426
430
|
}
|
|
431
|
+
if (result.data.indexedDiscovery) {
|
|
432
|
+
parts.push(
|
|
433
|
+
`外部索引公开笔记: ${result.data.indexedDiscovery.verifiedNoteUrls.length}/${result.data.indexedDiscovery.acceptedNoteUrls.length} 已校验(${result.data.indexedDiscovery.requestedNoteUrls.length} 个候选)`,
|
|
434
|
+
);
|
|
435
|
+
}
|
|
427
436
|
|
|
428
437
|
// Top notes
|
|
429
438
|
if (analysis.highestLikedNote) {
|
|
@@ -477,7 +486,8 @@ function formatDisplay(result: XhsReadResult<XhsProfileCollection>) {
|
|
|
477
486
|
const actionLabels: Record<string, string> = {
|
|
478
487
|
snapshot: "快照",
|
|
479
488
|
read_more_notes: "读取更多笔记",
|
|
480
|
-
|
|
489
|
+
read_visible_details: "读取公开详情与首屏评论",
|
|
490
|
+
discover_indexed_notes: "读取外部索引公开笔记",
|
|
481
491
|
};
|
|
482
492
|
parts.push(`采集模式: ${modeLabel}`);
|
|
483
493
|
parts.push(`采集动作: ${actionLabels[cs.action] ?? cs.action}`);
|
|
@@ -505,27 +515,27 @@ export async function readXhsProfileFunc(
|
|
|
505
515
|
displayData: string;
|
|
506
516
|
}> {
|
|
507
517
|
const url = String(args?.url ?? "").trim();
|
|
508
|
-
assertXhsProfileUrl(url);
|
|
518
|
+
const parsedUrl = assertXhsProfileUrl(url);
|
|
509
519
|
|
|
510
520
|
const readerArgs: ReadXhsProfileArgs = normalizeXhsProfileReadArgs({
|
|
511
|
-
url,
|
|
521
|
+
url: parsedUrl.navigationUrl,
|
|
512
522
|
maxScrollPages: args.maxScrollPages,
|
|
513
523
|
includeComments: args.includeComments,
|
|
514
524
|
maxCommentPagesPerNote: args.maxCommentPagesPerNote,
|
|
515
525
|
minLikesForDetail: args.minLikesForDetail,
|
|
516
526
|
minCommentsForCollect: args.minCommentsForCollect,
|
|
517
527
|
extendedCollectionConsent: args.extendedCollectionConsent,
|
|
518
|
-
profileDir: args.profileDir?.trim() || undefined,
|
|
519
528
|
headless: args.headless,
|
|
520
|
-
keepOpen: args.keepOpen,
|
|
521
529
|
collectionMode: args.collectionMode,
|
|
522
530
|
assistedAction: args.assistedAction,
|
|
523
531
|
maxAssistedSteps: args.maxAssistedSteps,
|
|
532
|
+
indexedNoteUrls: args.indexedNoteUrls,
|
|
524
533
|
});
|
|
525
534
|
|
|
526
|
-
const
|
|
535
|
+
const rawDataUnredacted =
|
|
527
536
|
(await context.reader?.(readerArgs)) ??
|
|
528
537
|
(await readWithDefaultBridge(readerArgs, thunkApi));
|
|
538
|
+
const rawData = redactXhsSensitiveValue(rawDataUnredacted) as XhsReadResult<XhsProfileCollection>;
|
|
529
539
|
|
|
530
540
|
// Build and attach collectionStatus
|
|
531
541
|
if (rawData.ok) {
|
|
@@ -573,15 +583,28 @@ function computeNextSuggestedAction(
|
|
|
573
583
|
args: ReadXhsProfileArgs,
|
|
574
584
|
data: XhsProfileCollection,
|
|
575
585
|
): XhsCollectionStatus["nextSuggestedAction"] {
|
|
576
|
-
const { diagnostic, notes, analysis
|
|
586
|
+
const { diagnostic, notes, analysis } = data;
|
|
577
587
|
const action = args.assistedAction ?? "snapshot";
|
|
578
588
|
|
|
579
|
-
|
|
589
|
+
if (
|
|
590
|
+
(diagnostic?.loginDetected ||
|
|
591
|
+
diagnostic?.code === "login_required" ||
|
|
592
|
+
(diagnostic?.code === "empty_profile_state" && notes.length === 0)) &&
|
|
593
|
+
action !== "discover_indexed_notes"
|
|
594
|
+
) {
|
|
595
|
+
return {
|
|
596
|
+
action: "discover_indexed_notes",
|
|
597
|
+
label: "外部索引找公开笔记",
|
|
598
|
+
reason: "站内匿名搜索不可用;可用 Google 等外部索引查找公开 note URL 后再读取可见页面",
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Login wall or empty state after indexed discovery → stop; anonymous mode must not request login.
|
|
580
603
|
if (diagnostic?.loginDetected || diagnostic?.code === "login_required") {
|
|
581
604
|
return {
|
|
582
|
-
action: "
|
|
583
|
-
label: "
|
|
584
|
-
reason: "
|
|
605
|
+
action: "stop_anonymous_unavailable",
|
|
606
|
+
label: "匿名不可见",
|
|
607
|
+
reason: "检测到登录提示;匿名公开模式不会登录或复用账号",
|
|
585
608
|
};
|
|
586
609
|
}
|
|
587
610
|
|
|
@@ -594,28 +617,16 @@ function computeNextSuggestedAction(
|
|
|
594
617
|
};
|
|
595
618
|
}
|
|
596
619
|
|
|
597
|
-
|
|
598
|
-
const hasHighCommentNotes = notes.some((n) => {
|
|
599
|
-
// Check noteDetails for comment counts
|
|
600
|
-
const detail = data.noteDetails.find((d) => d.noteId === n.noteId);
|
|
601
|
-
return detail && detail.metrics.commentCount > 5;
|
|
602
|
-
});
|
|
603
|
-
const hasComments = Object.keys(commentsByNote).length > 0;
|
|
604
|
-
|
|
605
|
-
if (
|
|
606
|
-
(action === "snapshot" || action === "read_more_notes") &&
|
|
607
|
-
hasHighCommentNotes &&
|
|
608
|
-
!hasComments
|
|
609
|
-
) {
|
|
620
|
+
if (notes.length > 0 && action === "snapshot") {
|
|
610
621
|
return {
|
|
611
|
-
action: "
|
|
612
|
-
label: "
|
|
613
|
-
reason: "
|
|
622
|
+
action: "read_visible_details",
|
|
623
|
+
label: "读取公开详情与首屏评论",
|
|
624
|
+
reason: "当前已有公开笔记列表,可匿名打开公开笔记页读取页面可见详情",
|
|
614
625
|
};
|
|
615
626
|
}
|
|
616
627
|
|
|
617
628
|
// Analysis exists and has data → suggest saving to table
|
|
618
|
-
if (analysis.totalNotes > 0 &&
|
|
629
|
+
if (analysis.totalNotes > 0 && action === "read_more_notes") {
|
|
619
630
|
return {
|
|
620
631
|
action: "save_to_table",
|
|
621
632
|
label: "保存到表格",
|
|
@@ -24,6 +24,7 @@ interface ToolTableColumnInput {
|
|
|
24
24
|
type CreateTableToolArgs = {
|
|
25
25
|
spaceId?: string;
|
|
26
26
|
title?: string;
|
|
27
|
+
purpose?: string;
|
|
27
28
|
description?: string;
|
|
28
29
|
tags?: string[];
|
|
29
30
|
categoryId?: string;
|
|
@@ -83,6 +84,11 @@ export const createTableFunctionSchema = {
|
|
|
83
84
|
description:
|
|
84
85
|
"表的显示名称,可以使用中文,例如「任务清单」「客户管理」。",
|
|
85
86
|
},
|
|
87
|
+
purpose: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description:
|
|
90
|
+
"可选:机器可读用途,用于之后在当前用户/Space 范围内查找这类表,例如 agent_eval_workbench。不要用它写自然语言说明。",
|
|
91
|
+
},
|
|
86
92
|
description: {
|
|
87
93
|
type: "string",
|
|
88
94
|
description:
|
|
@@ -184,6 +190,10 @@ export async function createTableFunc(
|
|
|
184
190
|
typeof args?.title === "string" && args.title.trim()
|
|
185
191
|
? args.title.trim()
|
|
186
192
|
: undefined;
|
|
193
|
+
const purpose =
|
|
194
|
+
typeof args?.purpose === "string" && args.purpose.trim()
|
|
195
|
+
? args.purpose.trim()
|
|
196
|
+
: undefined;
|
|
187
197
|
const description =
|
|
188
198
|
typeof args?.description === "string" && args.description.trim()
|
|
189
199
|
? args.description.trim()
|
|
@@ -254,6 +264,7 @@ export async function createTableFunc(
|
|
|
254
264
|
const createArgs: any = {
|
|
255
265
|
spaceId,
|
|
256
266
|
title,
|
|
267
|
+
purpose,
|
|
257
268
|
description,
|
|
258
269
|
tags,
|
|
259
270
|
categoryId,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 能力分级定义 (Capability Tiers)
|
|
3
|
+
*/
|
|
4
|
+
export const TOOL_PACKS = {
|
|
5
|
+
// L1 - 核心:交互 + 记忆读写 + 自我更新,所有 Agent 必有
|
|
6
|
+
CORE: ["ui_ask_choice", "rememberMemory", "read", "searchDialogMessages", "createDoc", "updateDoc", "search_workspace", "search_all_spaces", "updateSelf", "queryModelUsage", "createDialogGoal", "getDialogGoal", "completeDialogGoal", "createScheduledTask", "notifyUser"],
|
|
7
|
+
// L2 - 联网搜索:配置了 web-capable tools 的 Agent 默认加,纯 QA bot 不加
|
|
8
|
+
LIGHT_WEB: ["exa_search", "read_x_post", "read_xhs_profile"],
|
|
9
|
+
// L3 - 深度浏览器:全套复杂网页交互
|
|
10
|
+
FULL_BROWSER: [
|
|
11
|
+
"browser_openSession",
|
|
12
|
+
"browser_closeSession",
|
|
13
|
+
"browser_click",
|
|
14
|
+
"browser_typeText",
|
|
15
|
+
"browser_readContent",
|
|
16
|
+
"browser_selectOption",
|
|
17
|
+
"fetchWebpage",
|
|
18
|
+
],
|
|
19
|
+
} as const;
|
package/ai/types.ts
CHANGED
|
@@ -44,6 +44,9 @@ export interface Contexts {
|
|
|
44
44
|
/** 用户偏好与自动化边界(tone / capture / read policy) */
|
|
45
45
|
userPolicyContext?: string | null;
|
|
46
46
|
|
|
47
|
+
/** 长期记忆召回层,由服务端 memory API 基于当前用户、space、agent 和输入生成 */
|
|
48
|
+
memoryOverlay?: string | null;
|
|
49
|
+
|
|
47
50
|
/** 对话增量摘要:已压缩的历史消息概要 */
|
|
48
51
|
dialogSummary?: string | null;
|
|
49
52
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isAgentKey,
|
|
3
|
+
isFileKey,
|
|
4
|
+
isPageKey,
|
|
5
|
+
isTableMetaKey,
|
|
6
|
+
} from "../../database/keys";
|
|
7
|
+
|
|
8
|
+
type DeletedFavoriteProjectionRemoval = {
|
|
9
|
+
targetType: "agent" | "content";
|
|
10
|
+
id: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const resolveDeletedFavoriteProjectionRemoval = (
|
|
14
|
+
contentKey: string
|
|
15
|
+
): DeletedFavoriteProjectionRemoval | null => {
|
|
16
|
+
if (!contentKey) return null;
|
|
17
|
+
|
|
18
|
+
if (isAgentKey(contentKey)) {
|
|
19
|
+
return { targetType: "agent", id: contentKey };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (
|
|
23
|
+
isPageKey(contentKey) ||
|
|
24
|
+
isTableMetaKey(contentKey) ||
|
|
25
|
+
isFileKey(contentKey)
|
|
26
|
+
) {
|
|
27
|
+
return { targetType: "content", id: contentKey };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
};
|