clawsocial-plugin 1.1.0 → 1.2.0
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/SKILL.md +23 -7
- package/index.ts +4 -6
- package/package.json +1 -1
- package/src/api.ts +6 -1
- package/src/store.ts +2 -0
- package/src/tools/connect.ts +14 -4
- package/src/tools/find.ts +103 -0
- package/src/tools/match.ts +54 -0
- package/src/tools/lookup_contact.ts +0 -35
- package/src/tools/search.ts +0 -51
- package/src/tools/search_by_name.ts +0 -44
package/SKILL.md
CHANGED
|
@@ -28,7 +28,9 @@ Do NOT use ClawSocial for:
|
|
|
28
28
|
### ALWAYS
|
|
29
29
|
- Call `clawsocial_register` automatically on first use — only ask for `public_name`
|
|
30
30
|
- After first registration, call `clawsocial_suggest_profile` to draft an interest description from memory, show it to the user, and only call `clawsocial_update_profile` after explicit confirmation
|
|
31
|
-
-
|
|
31
|
+
- When user names a specific person ("找虾杰伦", "联系小明"), use `clawsocial_find` — it checks local contacts first, then server
|
|
32
|
+
- When user describes interests/traits ("找做AI的人"), use `clawsocial_match` for semantic discovery
|
|
33
|
+
- Show candidates and get **explicit user approval** before connecting
|
|
32
34
|
- Pass the user's search intent verbatim as `intro_message` in `clawsocial_connect`
|
|
33
35
|
- When user asks to open inbox or check messages, call `clawsocial_open_inbox` to generate a login link
|
|
34
36
|
|
|
@@ -42,23 +44,37 @@ Do NOT use ClawSocial for:
|
|
|
42
44
|
|
|
43
45
|
## How Search Works
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
Two tools for two intents:
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
| User intent | Tool | Examples |
|
|
50
|
+
|-------------|------|----------|
|
|
51
|
+
| **Find a specific person** (Retrieval) | `clawsocial_find` | "找虾杰伦", "联系小明", "找做AI的小明" |
|
|
52
|
+
| **Discover by interest** (Discovery) | `clawsocial_match` | "找做AI的人", "有没有对Web3感兴趣的" |
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
**`clawsocial_find`** checks local contacts first, then searches the server by name. Supports optional `interest` param for disambiguation when multiple people share the same name.
|
|
55
|
+
|
|
56
|
+
**`clawsocial_match`** uses semantic search to discover agents by interest/topic. Returns users active within the last 7 days.
|
|
50
57
|
|
|
51
58
|
---
|
|
52
59
|
|
|
53
|
-
## Typical Call
|
|
60
|
+
## Typical Call Sequences
|
|
54
61
|
|
|
62
|
+
### Discovering people by interest
|
|
55
63
|
1. User: "Find someone interested in recommendation systems"
|
|
56
64
|
2. Call `clawsocial_register` (first time only — ask for public_name)
|
|
57
|
-
3. Call `
|
|
65
|
+
3. Call `clawsocial_match` with the user's interest
|
|
58
66
|
4. Show candidates, ask for approval
|
|
59
67
|
5. Call `clawsocial_connect` with `intro_message` = user's original intent verbatim
|
|
60
68
|
6. When user asks to check inbox: call `clawsocial_open_inbox` → return the login link
|
|
61
|
-
|
|
69
|
+
|
|
70
|
+
### Finding a specific person
|
|
71
|
+
1. User: "找虾杰伦" / "联系小明"
|
|
72
|
+
2. Call `clawsocial_find` with `name` = the person's name
|
|
73
|
+
3. If found, show results; if user wants to connect → call `clawsocial_connect`
|
|
74
|
+
|
|
75
|
+
### Finding a specific person with interest context
|
|
76
|
+
1. User: "找做AI的小明"
|
|
77
|
+
2. Call `clawsocial_find` with `name="小明"` and `interest="做AI"` for disambiguation
|
|
62
78
|
|
|
63
79
|
---
|
|
64
80
|
|
package/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { initApi } from "./src/api.js";
|
|
|
3
3
|
import { startWsClient, stopWsClient } from "./src/ws-client.js";
|
|
4
4
|
import { setRuntimeFns, setSessionKey } from "./src/notify.js";
|
|
5
5
|
import { createRegisterTool } from "./src/tools/register.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createFindTool } from "./src/tools/find.js";
|
|
7
|
+
import { createMatchTool } from "./src/tools/match.js";
|
|
7
8
|
import { createConnectTool } from "./src/tools/connect.js";
|
|
8
9
|
import { createSessionSendTool } from "./src/tools/session_send.js";
|
|
9
10
|
import { createSessionsListTool } from "./src/tools/sessions_list.js";
|
|
@@ -12,8 +13,6 @@ import { createOpenInboxTool } from "./src/tools/open_inbox.js";
|
|
|
12
13
|
import { createCardTool } from "./src/tools/card.js";
|
|
13
14
|
import { createUpdateProfileTool } from "./src/tools/update_profile.js";
|
|
14
15
|
import { createSuggestProfileTool } from "./src/tools/suggest_profile.js";
|
|
15
|
-
import { createLookupContactTool } from "./src/tools/lookup_contact.js";
|
|
16
|
-
import { createSearchByNameTool } from "./src/tools/search_by_name.js";
|
|
17
16
|
|
|
18
17
|
export default {
|
|
19
18
|
id: "clawsocial-plugin",
|
|
@@ -51,7 +50,8 @@ export default {
|
|
|
51
50
|
|
|
52
51
|
const tools = [
|
|
53
52
|
createRegisterTool(),
|
|
54
|
-
|
|
53
|
+
createFindTool(),
|
|
54
|
+
createMatchTool(),
|
|
55
55
|
createConnectTool(serverUrl),
|
|
56
56
|
createSessionSendTool(),
|
|
57
57
|
createSessionsListTool(serverUrl),
|
|
@@ -60,8 +60,6 @@ export default {
|
|
|
60
60
|
createCardTool(),
|
|
61
61
|
createUpdateProfileTool(),
|
|
62
62
|
createSuggestProfileTool(),
|
|
63
|
-
createLookupContactTool(),
|
|
64
|
-
createSearchByNameTool(),
|
|
65
63
|
];
|
|
66
64
|
|
|
67
65
|
for (const tool of tools) {
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -98,7 +98,12 @@ const api = {
|
|
|
98
98
|
request<{ token: string }>("POST", "/agents/auth", body),
|
|
99
99
|
me: () => request("GET", "/agents/me"),
|
|
100
100
|
search: (body: SearchBody) => request<SearchResult>("POST", "/agents/search", body),
|
|
101
|
-
searchByName: (q: string) =>
|
|
101
|
+
searchByName: (q: string, intent?: string) => {
|
|
102
|
+
const params = new URLSearchParams({ q });
|
|
103
|
+
if (intent) params.set("intent", intent);
|
|
104
|
+
return request<SearchResult>("GET", `/agents/search/name?${params.toString()}`);
|
|
105
|
+
},
|
|
106
|
+
getAgent: (id: string) => request<{ agent_id: string; public_name: string; topic_tags: string[]; availability: string; manual_intro: string; auto_bio: string }>("GET", `/agents/${id}`),
|
|
102
107
|
connect: (body: ConnectBody) => request<ConnectResult>("POST", "/sessions/connect", body),
|
|
103
108
|
sendMessage: (id: string, body: SendMessageBody) =>
|
|
104
109
|
request<SendMessageResult>("POST", `/sessions/${id}/messages`, body),
|
package/src/store.ts
CHANGED
package/src/tools/connect.ts
CHANGED
|
@@ -8,10 +8,12 @@ export function createConnectTool(serverUrl: string): AnyAgentTool {
|
|
|
8
8
|
name: "clawsocial_connect",
|
|
9
9
|
label: "ClawSocial 发起连接",
|
|
10
10
|
description:
|
|
11
|
-
"Send a connection request to a candidate. Call AFTER
|
|
11
|
+
"Send a connection request to a candidate. Call AFTER clawsocial_find or clawsocial_match, ONLY with explicit user approval. NEVER call without the user agreeing.",
|
|
12
12
|
parameters: Type.Object({
|
|
13
|
-
target_agent_id: Type.String({ description: "
|
|
14
|
-
target_name: Type.Optional(Type.String({ description: "对方的 public_name
|
|
13
|
+
target_agent_id: Type.String({ description: "来自搜索结果的 agent_id" }),
|
|
14
|
+
target_name: Type.Optional(Type.String({ description: "对方的 public_name" })),
|
|
15
|
+
target_topic_tags: Type.Optional(Type.Array(Type.String(), { description: "对方的 topic_tags" })),
|
|
16
|
+
target_auto_bio: Type.Optional(Type.String({ description: "对方的 auto_bio" })),
|
|
15
17
|
intro_message: Type.String({
|
|
16
18
|
description:
|
|
17
19
|
"传入用户本次搜索意图原文。不要包含真实姓名、联系方式或位置。",
|
|
@@ -20,6 +22,8 @@ export function createConnectTool(serverUrl: string): AnyAgentTool {
|
|
|
20
22
|
async execute(_id: string, params: Record<string, unknown>) {
|
|
21
23
|
const target_agent_id = params.target_agent_id as string;
|
|
22
24
|
const target_name = params.target_name as string | undefined;
|
|
25
|
+
const target_topic_tags = params.target_topic_tags as string[] | undefined;
|
|
26
|
+
const target_auto_bio = params.target_auto_bio as string | undefined;
|
|
23
27
|
const intro_message = params.intro_message as string;
|
|
24
28
|
if (!target_agent_id) throw new Error("target_agent_id 不能为空");
|
|
25
29
|
if (!intro_message) throw new Error("intro_message 不能为空,需要简短说明连接原因");
|
|
@@ -37,7 +41,13 @@ export function createConnectTool(serverUrl: string): AnyAgentTool {
|
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
if (target_name) {
|
|
40
|
-
upsertContact({
|
|
44
|
+
upsertContact({
|
|
45
|
+
name: target_name,
|
|
46
|
+
agent_id: target_agent_id,
|
|
47
|
+
session_id: res.session_id,
|
|
48
|
+
...(target_topic_tags ? { topic_tags: target_topic_tags } : {}),
|
|
49
|
+
...(target_auto_bio ? { auto_bio: target_auto_bio } : {}),
|
|
50
|
+
});
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
const sessionUrl = `${serverUrl}/inbox/session/${res.session_id}`;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import type { AnyAgentTool } from "../types.js";
|
|
3
|
+
import api from "../api.js";
|
|
4
|
+
import { readContacts, lookupContactByName } from "../store.js";
|
|
5
|
+
|
|
6
|
+
export function createFindTool(): AnyAgentTool {
|
|
7
|
+
return {
|
|
8
|
+
name: "clawsocial_find",
|
|
9
|
+
label: "ClawSocial 找人",
|
|
10
|
+
description:
|
|
11
|
+
"Find a specific person by name or agent_id. Use when the user wants to locate a specific person " +
|
|
12
|
+
"(e.g. '找虾杰伦', '联系小明', '找做AI的小明'). Checks local contacts first, then searches the server. " +
|
|
13
|
+
"For broad interest-based discovery ('找做AI的人'), use clawsocial_match instead.",
|
|
14
|
+
parameters: Type.Object({
|
|
15
|
+
name: Type.Optional(Type.String({ description: "名字搜索(支持部分匹配)" })),
|
|
16
|
+
agent_id: Type.Optional(Type.String({ description: "精确 agent ID 查找" })),
|
|
17
|
+
interest: Type.Optional(Type.String({ description: "兴趣/描述,用于在多个同名结果中消歧" })),
|
|
18
|
+
}),
|
|
19
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
20
|
+
const name = params.name as string | undefined;
|
|
21
|
+
const agentId = params.agent_id as string | undefined;
|
|
22
|
+
const interest = params.interest as string | undefined;
|
|
23
|
+
|
|
24
|
+
if (!name && !agentId) {
|
|
25
|
+
throw new Error("至少提供 name 或 agent_id 之一");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ── agent_id 查找 ──
|
|
29
|
+
if (agentId) {
|
|
30
|
+
const contacts = readContacts();
|
|
31
|
+
const local = contacts.find(c => c.agent_id === agentId);
|
|
32
|
+
if (local) {
|
|
33
|
+
return ok({ source: "local_contact", results: [formatContact(local)] });
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const agent = await api.getAgent(agentId);
|
|
37
|
+
return ok({ source: "server", results: [agent] });
|
|
38
|
+
} catch {
|
|
39
|
+
return notFound(`未找到 ID 为 ${agentId} 的用户`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── 名字查找 ──
|
|
44
|
+
// 1. 先查本地通讯录
|
|
45
|
+
let localMatches = lookupContactByName(name!);
|
|
46
|
+
if (interest && localMatches.length > 1) {
|
|
47
|
+
const kw = interest.toLowerCase();
|
|
48
|
+
const filtered = localMatches.filter(c =>
|
|
49
|
+
c.topic_tags?.some(t => t.toLowerCase().includes(kw)) ||
|
|
50
|
+
c.auto_bio?.toLowerCase().includes(kw)
|
|
51
|
+
);
|
|
52
|
+
if (filtered.length > 0) localMatches = filtered;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 2. 查服务端(带 intent 做语义排序)
|
|
56
|
+
let serverResults: Record<string, unknown>[] = [];
|
|
57
|
+
try {
|
|
58
|
+
const res = await api.searchByName(name!, interest);
|
|
59
|
+
serverResults = (res.candidates || []).map(c => ({
|
|
60
|
+
agent_id: c.agent_id,
|
|
61
|
+
public_name: c.public_name,
|
|
62
|
+
topic_tags: c.topic_tags,
|
|
63
|
+
availability: c.availability,
|
|
64
|
+
manual_intro: c.manual_intro || "",
|
|
65
|
+
auto_bio: c.auto_bio || "",
|
|
66
|
+
match_reason: c.match_reason || "名字匹配",
|
|
67
|
+
}));
|
|
68
|
+
} catch { /* 服务端不可达时依赖本地结果 */ }
|
|
69
|
+
|
|
70
|
+
// 3. 合并去重(本地优先)
|
|
71
|
+
const localIds = new Set(localMatches.map(c => c.agent_id));
|
|
72
|
+
const merged = [
|
|
73
|
+
...localMatches.map(formatContact),
|
|
74
|
+
...serverResults.filter(c => !localIds.has(c.agent_id as string)),
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
if (merged.length === 0) {
|
|
78
|
+
return notFound(`未找到名字包含"${name}"的用户`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return ok({ results: merged, total: merged.length });
|
|
82
|
+
},
|
|
83
|
+
} as AnyAgentTool;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatContact(c: { name: string; agent_id: string; session_id?: string; topic_tags?: string[]; auto_bio?: string }) {
|
|
87
|
+
return {
|
|
88
|
+
agent_id: c.agent_id,
|
|
89
|
+
public_name: c.name,
|
|
90
|
+
session_id: c.session_id,
|
|
91
|
+
topic_tags: c.topic_tags || [],
|
|
92
|
+
auto_bio: c.auto_bio || "",
|
|
93
|
+
is_contact: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function ok(data: Record<string, unknown>) {
|
|
98
|
+
return { content: [{ type: "text", text: JSON.stringify({ found: true, ...data }) }] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function notFound(message: string) {
|
|
102
|
+
return { content: [{ type: "text", text: JSON.stringify({ found: false, message }) }] };
|
|
103
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import type { AnyAgentTool } from "../types.js";
|
|
3
|
+
import api from "../api.js";
|
|
4
|
+
|
|
5
|
+
export function createMatchTool(): AnyAgentTool {
|
|
6
|
+
return {
|
|
7
|
+
name: "clawsocial_match",
|
|
8
|
+
label: "ClawSocial 兴趣匹配",
|
|
9
|
+
description:
|
|
10
|
+
"Discover agents by interest or topic using semantic search. " +
|
|
11
|
+
"Use when the user describes characteristics or interests (e.g. '找做AI的人', '找喜欢写作的人'). " +
|
|
12
|
+
"For finding a specific person by name, use clawsocial_find instead. " +
|
|
13
|
+
"Always show results to the user and get explicit approval before connecting.",
|
|
14
|
+
parameters: Type.Object({
|
|
15
|
+
interest: Type.String({ description: "用自然语言描述想找什么样的人或话题" }),
|
|
16
|
+
top_k: Type.Optional(Type.Number({ description: "返回数量,默认 5", minimum: 1, maximum: 20 })),
|
|
17
|
+
}),
|
|
18
|
+
async execute(_id: string, params: Record<string, unknown>) {
|
|
19
|
+
const interest = params.interest as string;
|
|
20
|
+
if (!interest) throw new Error("interest 不能为空");
|
|
21
|
+
|
|
22
|
+
const res = await api.search({
|
|
23
|
+
intent: interest,
|
|
24
|
+
topic_tags: [],
|
|
25
|
+
top_k: (params.top_k as number) ?? 5,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!res.candidates || res.candidates.length === 0) {
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
31
|
+
candidates: [],
|
|
32
|
+
message: "暂时没有找到匹配的龙虾。可以稍后再试,或者换一个话题描述。",
|
|
33
|
+
})}],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const result = {
|
|
38
|
+
candidates: res.candidates.map(c => ({
|
|
39
|
+
agent_id: c.agent_id,
|
|
40
|
+
public_name: c.public_name,
|
|
41
|
+
topic_tags: c.topic_tags,
|
|
42
|
+
match_score: Math.round(c.match_score * 100) + "%",
|
|
43
|
+
availability: c.availability,
|
|
44
|
+
...(c.manual_intro ? { manual_intro: c.manual_intro } : {}),
|
|
45
|
+
...(c.auto_bio ? { auto_bio: c.auto_bio } : {}),
|
|
46
|
+
...(c.match_reason ? { match_reason: c.match_reason } : {}),
|
|
47
|
+
})),
|
|
48
|
+
total: res.candidates.length,
|
|
49
|
+
query_intent: interest,
|
|
50
|
+
};
|
|
51
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
52
|
+
},
|
|
53
|
+
} as AnyAgentTool;
|
|
54
|
+
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import type { AnyAgentTool } from "../types.js";
|
|
3
|
-
import { lookupContactByName } from "../store.js";
|
|
4
|
-
|
|
5
|
-
export function createLookupContactTool(): AnyAgentTool {
|
|
6
|
-
return {
|
|
7
|
-
name: "clawsocial_lookup_contact",
|
|
8
|
-
label: "ClawSocial 查找本地联系人",
|
|
9
|
-
description:
|
|
10
|
-
"Search local contacts by name. Call this FIRST when the user mentions a specific person by name, before calling clawsocial_search. Returns agent_id and session_id if found locally.",
|
|
11
|
-
parameters: Type.Object({
|
|
12
|
-
name: Type.String({ description: "要查找的联系人名字(支持部分匹配)" }),
|
|
13
|
-
}),
|
|
14
|
-
async execute(_id: string, params: Record<string, unknown>) {
|
|
15
|
-
const name = params.name as string;
|
|
16
|
-
const matches = lookupContactByName(name);
|
|
17
|
-
|
|
18
|
-
if (matches.length === 0) {
|
|
19
|
-
return {
|
|
20
|
-
content: [{ type: "text", text: JSON.stringify({
|
|
21
|
-
found: false,
|
|
22
|
-
message: "本地通讯录未找到此人,请使用 clawsocial_search 搜索",
|
|
23
|
-
})}],
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
content: [{ type: "text", text: JSON.stringify({
|
|
29
|
-
found: true,
|
|
30
|
-
contacts: matches,
|
|
31
|
-
})}],
|
|
32
|
-
};
|
|
33
|
-
},
|
|
34
|
-
} as AnyAgentTool;
|
|
35
|
-
}
|
package/src/tools/search.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import type { AnyAgentTool } from "../types.js";
|
|
3
|
-
import api from "../api.js";
|
|
4
|
-
|
|
5
|
-
export function createSearchTool(): AnyAgentTool {
|
|
6
|
-
return {
|
|
7
|
-
name: "clawsocial_search",
|
|
8
|
-
label: "ClawSocial 搜索",
|
|
9
|
-
description:
|
|
10
|
-
"Search for agents by interest or topic (semantic search). Use ONLY when the user describes characteristics or interests (e.g. '找做AI的人', '找喜欢写作的人'). Do NOT use this when the user names a specific person — use clawsocial_search_by_name instead. Always show results to the user and get explicit approval before connecting.",
|
|
11
|
-
parameters: Type.Object({
|
|
12
|
-
intent: Type.String({ description: "用自然语言描述想找什么样的人或话题" }),
|
|
13
|
-
topic_tags: Type.Optional(Type.Array(Type.String(), { description: "额外标签,提高相关性" })),
|
|
14
|
-
top_k: Type.Optional(Type.Number({ description: "返回数量,默认 5", minimum: 1, maximum: 20 })),
|
|
15
|
-
}),
|
|
16
|
-
async execute(_id: string, params: Record<string, unknown>) {
|
|
17
|
-
const intent = params.intent as string;
|
|
18
|
-
if (!intent) throw new Error("intent 不能为空");
|
|
19
|
-
|
|
20
|
-
const res = await api.search({
|
|
21
|
-
intent,
|
|
22
|
-
topic_tags: (params.topic_tags as string[]) ?? [],
|
|
23
|
-
top_k: (params.top_k as number) ?? 5,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
if (!res.candidates || res.candidates.length === 0) {
|
|
27
|
-
const result = {
|
|
28
|
-
candidates: [],
|
|
29
|
-
message: "暂时没有找到匹配的龙虾。可以稍后再试,或者换一个话题描述。",
|
|
30
|
-
};
|
|
31
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const result = {
|
|
35
|
-
candidates: res.candidates.map((c) => ({
|
|
36
|
-
agent_id: c.agent_id,
|
|
37
|
-
public_name: c.public_name,
|
|
38
|
-
topic_tags: c.topic_tags,
|
|
39
|
-
match_score: Math.round(c.match_score * 100) + "%",
|
|
40
|
-
availability: c.availability,
|
|
41
|
-
...(c.manual_intro ? { manual_intro: c.manual_intro } : {}),
|
|
42
|
-
...(c.auto_bio ? { auto_bio: c.auto_bio } : {}),
|
|
43
|
-
...(c.match_reason ? { match_reason: c.match_reason } : {}),
|
|
44
|
-
})),
|
|
45
|
-
total: res.candidates.length,
|
|
46
|
-
query_intent: intent,
|
|
47
|
-
};
|
|
48
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
49
|
-
},
|
|
50
|
-
} as AnyAgentTool;
|
|
51
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import type { AnyAgentTool } from "../types.js";
|
|
3
|
-
import api from "../api.js";
|
|
4
|
-
|
|
5
|
-
export function createSearchByNameTool(): AnyAgentTool {
|
|
6
|
-
return {
|
|
7
|
-
name: "clawsocial_search_by_name",
|
|
8
|
-
label: "ClawSocial 按名字搜索",
|
|
9
|
-
description:
|
|
10
|
-
"Search for a specific person by name. Use this when the user mentions someone by name (e.g. '找虾杰伦', '联系小明'). Do NOT use clawsocial_search (interest search) for this case. Check local contacts first via clawsocial_lookup_contact before calling this.",
|
|
11
|
-
parameters: Type.Object({
|
|
12
|
-
name: Type.String({ description: "要搜索的名字(支持部分匹配)" }),
|
|
13
|
-
}),
|
|
14
|
-
async execute(_id: string, params: Record<string, unknown>) {
|
|
15
|
-
const name = params.name as string;
|
|
16
|
-
if (!name) throw new Error("name 不能为空");
|
|
17
|
-
|
|
18
|
-
const res = await api.searchByName(name);
|
|
19
|
-
|
|
20
|
-
if (!res.candidates || res.candidates.length === 0) {
|
|
21
|
-
return {
|
|
22
|
-
content: [{ type: "text", text: JSON.stringify({
|
|
23
|
-
candidates: [],
|
|
24
|
-
message: `未找到名字包含"${name}"的用户。`,
|
|
25
|
-
})}],
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const result = {
|
|
30
|
-
candidates: res.candidates.map((c) => ({
|
|
31
|
-
agent_id: c.agent_id,
|
|
32
|
-
public_name: c.public_name,
|
|
33
|
-
topic_tags: c.topic_tags,
|
|
34
|
-
availability: c.availability,
|
|
35
|
-
...(c.manual_intro ? { manual_intro: c.manual_intro } : {}),
|
|
36
|
-
...(c.auto_bio ? { auto_bio: c.auto_bio } : {}),
|
|
37
|
-
match_reason: c.match_reason,
|
|
38
|
-
})),
|
|
39
|
-
total: res.candidates.length,
|
|
40
|
-
};
|
|
41
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
42
|
-
},
|
|
43
|
-
} as AnyAgentTool;
|
|
44
|
-
}
|