@hedgehog-finance/hedgehog-plugin 1.0.21 → 1.0.23
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/dist/index.d.ts +4 -3
- package/dist/index.js +49 -6
- package/dist/index.js.map +1 -1
- package/dist/setup-api.d.ts +2 -0
- package/dist/setup-api.js +8 -0
- package/dist/setup-api.js.map +1 -0
- package/dist/src/channel.js +25 -23
- package/dist/src/channel.js.map +1 -1
- package/dist/src/core/database.js +449 -39
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/dailyMorningBriefingCron.d.ts +46 -0
- package/dist/src/dailyMorningBriefingCron.js +82 -0
- package/dist/src/dailyMorningBriefingCron.js.map +1 -0
- package/dist/src/features/chartOutput.d.ts +2 -0
- package/dist/src/features/chartOutput.js +35 -0
- package/dist/src/features/chartOutput.js.map +1 -0
- package/dist/src/features/dailyMorningBriefing/schema.d.ts +56 -0
- package/dist/src/features/dailyMorningBriefing/schema.js +22 -0
- package/dist/src/features/dailyMorningBriefing/schema.js.map +1 -0
- package/dist/src/features/dailyMorningBriefing/tools.d.ts +12 -0
- package/dist/src/features/dailyMorningBriefing/tools.js +204 -0
- package/dist/src/features/dailyMorningBriefing/tools.js.map +1 -0
- package/dist/src/features/deepReasoning/schema.d.ts +43 -0
- package/dist/src/features/deepReasoning/schema.js +17 -0
- package/dist/src/features/deepReasoning/schema.js.map +1 -0
- package/dist/src/features/deepReasoning/tools.d.ts +12 -0
- package/dist/src/features/deepReasoning/tools.js +163 -0
- package/dist/src/features/deepReasoning/tools.js.map +1 -0
- package/dist/src/features/index.d.ts +2 -1
- package/dist/src/features/index.js +9 -1
- package/dist/src/features/index.js.map +1 -1
- package/dist/src/features/informationVerification/schema.d.ts +43 -0
- package/dist/src/features/informationVerification/schema.js +17 -0
- package/dist/src/features/informationVerification/schema.js.map +1 -0
- package/dist/src/features/informationVerification/tools.d.ts +12 -0
- package/dist/src/features/informationVerification/tools.js +162 -0
- package/dist/src/features/informationVerification/tools.js.map +1 -0
- package/dist/src/features/notes/schema.d.ts +136 -39
- package/dist/src/features/notes/schema.js +13 -10
- package/dist/src/features/notes/schema.js.map +1 -1
- package/dist/src/features/notes/tools.d.ts +1 -0
- package/dist/src/features/notes/tools.js +47 -14
- package/dist/src/features/notes/tools.js.map +1 -1
- package/dist/src/features/pluginInfo/schema.d.ts +79 -0
- package/dist/src/features/pluginInfo/schema.js +14 -0
- package/dist/src/features/pluginInfo/schema.js.map +1 -0
- package/dist/src/features/pluginInfo/tools.d.ts +1 -0
- package/dist/src/features/pluginInfo/tools.js +157 -2
- package/dist/src/features/pluginInfo/tools.js.map +1 -1
- package/dist/src/features/profileLibrary/schema.d.ts +34 -6
- package/dist/src/features/profileLibrary/schema.js +1 -1
- package/dist/src/features/profileLibrary/schema.js.map +1 -1
- package/dist/src/features/stockAnalysis/schema.d.ts +224 -31
- package/dist/src/features/stockAnalysis/schema.js +76 -12
- package/dist/src/features/stockAnalysis/schema.js.map +1 -1
- package/dist/src/features/stockAnalysis/tools.d.ts +6 -4
- package/dist/src/features/stockAnalysis/tools.js +389 -44
- package/dist/src/features/stockAnalysis/tools.js.map +1 -1
- package/dist/src/features/stockBasic/schema.d.ts +149 -0
- package/dist/src/features/stockBasic/schema.js +26 -0
- package/dist/src/features/stockBasic/schema.js.map +1 -0
- package/dist/src/features/stockBasic/tools.d.ts +12 -0
- package/dist/src/features/stockBasic/tools.js +124 -0
- package/dist/src/features/stockBasic/tools.js.map +1 -0
- package/dist/src/features/watchlist/logic.d.ts +3 -3
- package/dist/src/features/watchlist/logic.js +47 -46
- package/dist/src/features/watchlist/logic.js.map +1 -1
- package/dist/src/features/watchlist/schema.d.ts +89 -54
- package/dist/src/features/watchlist/schema.js +7 -4
- package/dist/src/features/watchlist/schema.js.map +1 -1
- package/dist/src/features/watchlist/tools.d.ts +106 -59
- package/dist/src/features/watchlist/tools.js +182 -104
- package/dist/src/features/watchlist/tools.js.map +1 -1
- package/dist/src/openclawConfig.d.ts +6 -0
- package/dist/src/openclawConfig.js +74 -0
- package/dist/src/openclawConfig.js.map +1 -0
- package/dist/src/openclawConstants.d.ts +4 -0
- package/dist/src/openclawConstants.js +10 -0
- package/dist/src/openclawConstants.js.map +1 -0
- package/dist/src/runtime.js +20 -0
- package/dist/src/runtime.js.map +1 -1
- package/index.ts +52 -5
- package/openclaw.plugin.json +24 -0
- package/package.json +20 -5
- package/setup-api.ts +10 -0
- package/src/channel.ts +26 -25
- package/src/core/database.ts +447 -40
- package/src/dailyMorningBriefingCron.ts +129 -0
- package/src/features/chartOutput.ts +35 -0
- package/src/features/dailyMorningBriefing/schema.ts +38 -0
- package/src/features/dailyMorningBriefing/tools.ts +246 -0
- package/src/features/deepReasoning/schema.ts +22 -0
- package/src/features/deepReasoning/tools.ts +182 -0
- package/src/features/index.ts +11 -2
- package/src/features/informationVerification/schema.ts +22 -0
- package/src/features/informationVerification/tools.ts +181 -0
- package/src/features/notes/schema.ts +17 -12
- package/src/features/notes/tools.ts +54 -17
- package/src/features/pluginInfo/schema.ts +19 -0
- package/src/features/pluginInfo/tools.ts +173 -2
- package/src/features/profileLibrary/schema.ts +1 -1
- package/src/features/stockAnalysis/schema.ts +99 -17
- package/src/features/stockAnalysis/tools.ts +447 -49
- package/src/features/stockBasic/schema.ts +33 -0
- package/src/features/stockBasic/tools.ts +157 -0
- package/src/features/watchlist/logic.ts +56 -53
- package/src/features/watchlist/schema.ts +11 -6
- package/src/features/watchlist/tools.ts +191 -106
- package/src/openclawConfig.ts +101 -0
- package/src/openclawConstants.ts +11 -0
- package/src/runtime.ts +19 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const BuildInformationVerificationMessageParamsSchema = z.object({
|
|
4
|
+
newsId: z.string().trim().min(1).describe("新闻 ID,例如 news-5"),
|
|
5
|
+
sourceTitle: z.string().trim().min(1).describe("新闻标题"),
|
|
6
|
+
sourceContent: z.string().trim().min(1).describe("新闻正文")
|
|
7
|
+
});
|
|
8
|
+
export type BuildInformationVerificationMessageParams = z.infer<typeof BuildInformationVerificationMessageParamsSchema>;
|
|
9
|
+
|
|
10
|
+
export const QueryInformationVerificationHistoryParamsSchema = z.object({
|
|
11
|
+
page: z.number().int().min(1).default(1).describe("页码"),
|
|
12
|
+
pageSize: z.number().int().min(1).max(50).default(10).describe("每页数量,默认 10")
|
|
13
|
+
});
|
|
14
|
+
export type QueryInformationVerificationHistoryParams = z.infer<typeof QueryInformationVerificationHistoryParamsSchema>;
|
|
15
|
+
|
|
16
|
+
export const GetInformationVerificationDetailParamsSchema = z.object({
|
|
17
|
+
id: z.string().trim().min(1).optional().describe("记录 ID"),
|
|
18
|
+
sourceId: z.string().trim().min(1).optional().describe("新闻来源 ID,例如 news-5")
|
|
19
|
+
}).refine((value) => value.id || value.sourceId, {
|
|
20
|
+
message: "id 或 sourceId 至少提供一个"
|
|
21
|
+
});
|
|
22
|
+
export type GetInformationVerificationDetailParams = z.infer<typeof GetInformationVerificationDetailParamsSchema>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { getDB } from "../../core/database.js";
|
|
2
|
+
import {
|
|
3
|
+
BuildInformationVerificationMessageParams,
|
|
4
|
+
BuildInformationVerificationMessageParamsSchema,
|
|
5
|
+
GetInformationVerificationDetailParamsSchema,
|
|
6
|
+
QueryInformationVerificationHistoryParamsSchema
|
|
7
|
+
} from "./schema.js";
|
|
8
|
+
|
|
9
|
+
interface RuntimeTool {
|
|
10
|
+
name: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
description: string;
|
|
13
|
+
parameters: unknown;
|
|
14
|
+
registerTool?: boolean;
|
|
15
|
+
execute(params: unknown, ctx?: { userId: string }): Promise<string>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const INFORMATION_VERIFICATION_SKILL = "hedgehog-information-verification";
|
|
19
|
+
|
|
20
|
+
const BuildInformationVerificationMessageAgentToolSchema = {
|
|
21
|
+
type: "object",
|
|
22
|
+
additionalProperties: false,
|
|
23
|
+
required: ["newsId", "sourceTitle", "sourceContent"],
|
|
24
|
+
properties: {
|
|
25
|
+
newsId: { type: "string", description: "新闻 ID,例如 news-5" },
|
|
26
|
+
sourceTitle: { type: "string", description: "新闻标题" },
|
|
27
|
+
sourceContent: { type: "string", description: "新闻正文" }
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function buildContent(args: BuildInformationVerificationMessageParams): string {
|
|
32
|
+
return [
|
|
33
|
+
"对这条新闻进行信息求证",
|
|
34
|
+
"",
|
|
35
|
+
`新闻标题:${args.sourceTitle}`,
|
|
36
|
+
"",
|
|
37
|
+
"新闻正文:",
|
|
38
|
+
args.sourceContent
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function buildInformationVerificationMessage(args: BuildInformationVerificationMessageParams): string {
|
|
43
|
+
const buildGeneratingSaveParams = () => JSON.stringify({
|
|
44
|
+
sourceId: args.newsId,
|
|
45
|
+
sourceTitle: args.sourceTitle,
|
|
46
|
+
status: "generating",
|
|
47
|
+
content: ""
|
|
48
|
+
});
|
|
49
|
+
const buildFinalSaveParams = (status: "completed" | "failed") => JSON.stringify({
|
|
50
|
+
sourceId: args.newsId,
|
|
51
|
+
status,
|
|
52
|
+
content: "..."
|
|
53
|
+
});
|
|
54
|
+
return JSON.stringify({
|
|
55
|
+
cw_system_prompt: [
|
|
56
|
+
`开始分析前必须先调用 save_information_verification,参数为 ${buildGeneratingSaveParams()};如果返回 skipped=true,必须立即停止,不要继续调用 skill 或生成正文。`,
|
|
57
|
+
`必须触发并使用 ${INFORMATION_VERIFICATION_SKILL} skill 生成信息求证与置信度审计报告。`,
|
|
58
|
+
`生成成功后必须调用 save_information_verification,参数为 ${buildFinalSaveParams("completed")},content 传最终完整求证报告正文。`,
|
|
59
|
+
`生成失败后必须调用 save_information_verification,参数为 ${buildFinalSaveParams("failed")},content 存放完整错误信息。`
|
|
60
|
+
].join("\n"),
|
|
61
|
+
cw_context: JSON.stringify({
|
|
62
|
+
sourceId: args.newsId,
|
|
63
|
+
sourceTitle: args.sourceTitle
|
|
64
|
+
}),
|
|
65
|
+
cw_content: buildContent(args),
|
|
66
|
+
cw_output: [
|
|
67
|
+
`输出结构以 ${INFORMATION_VERIFICATION_SKILL} skill 的交付模板为准。`
|
|
68
|
+
].join("\n")
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function selectGeneratingInformationVerification(userId: string, sourceId: string) {
|
|
73
|
+
const db = getDB();
|
|
74
|
+
return db.prepare(`
|
|
75
|
+
SELECT id, sourceId, sourceTitle, status, content, createdAt, updatedAt
|
|
76
|
+
FROM news_fact_check_analysis
|
|
77
|
+
WHERE userId = ? AND sourceId = ? AND status = 'generating'
|
|
78
|
+
ORDER BY updatedAt DESC, createdAt DESC
|
|
79
|
+
LIMIT 1
|
|
80
|
+
`).get(userId, sourceId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const informationVerificationTools: Record<string, RuntimeTool> = {
|
|
84
|
+
query_information_verification_history: {
|
|
85
|
+
name: "query_information_verification_history",
|
|
86
|
+
label: "查询信息求证列表",
|
|
87
|
+
description: "分页查询信息求证记录列表。返回记录标识、来源 ID、标题、状态和时间字段;列表结果不包含 content,详情请使用 get_information_verification_detail 查询。",
|
|
88
|
+
parameters: QueryInformationVerificationHistoryParamsSchema,
|
|
89
|
+
registerTool: false,
|
|
90
|
+
async execute(params, ctx) {
|
|
91
|
+
const args = QueryInformationVerificationHistoryParamsSchema.parse(params ?? {});
|
|
92
|
+
const db = getDB();
|
|
93
|
+
const userId = ctx?.userId || "default";
|
|
94
|
+
const offset = (args.page - 1) * args.pageSize;
|
|
95
|
+
const rows = db.prepare(`
|
|
96
|
+
SELECT id, sourceId, sourceTitle, status, createdAt, updatedAt
|
|
97
|
+
FROM news_fact_check_analysis
|
|
98
|
+
WHERE userId = ?
|
|
99
|
+
ORDER BY updatedAt DESC, createdAt DESC
|
|
100
|
+
LIMIT ? OFFSET ?
|
|
101
|
+
`).all(userId, args.pageSize, offset);
|
|
102
|
+
const countRow = db.prepare(`
|
|
103
|
+
SELECT COUNT(*) AS total FROM news_fact_check_analysis WHERE userId = ?
|
|
104
|
+
`).get(userId) as { total: number };
|
|
105
|
+
const total = countRow.total || 0;
|
|
106
|
+
return JSON.stringify({
|
|
107
|
+
success: true,
|
|
108
|
+
data: rows,
|
|
109
|
+
pagination: {
|
|
110
|
+
page: args.page,
|
|
111
|
+
pageSize: args.pageSize,
|
|
112
|
+
total,
|
|
113
|
+
totalPages: Math.ceil(total / args.pageSize)
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
get_information_verification_detail: {
|
|
119
|
+
name: "get_information_verification_detail",
|
|
120
|
+
label: "查询信息求证详情",
|
|
121
|
+
description: "根据记录 ID 或 sourceId 查询信息求证完整详情,包含 content 正文及所有元数据。",
|
|
122
|
+
parameters: GetInformationVerificationDetailParamsSchema,
|
|
123
|
+
registerTool: false,
|
|
124
|
+
async execute(params, ctx) {
|
|
125
|
+
const args = GetInformationVerificationDetailParamsSchema.parse(params);
|
|
126
|
+
const db = getDB();
|
|
127
|
+
if (args.sourceId) {
|
|
128
|
+
const row = db.prepare(`
|
|
129
|
+
SELECT id, sourceId, sourceTitle, status, content, createdAt, updatedAt
|
|
130
|
+
FROM news_fact_check_analysis
|
|
131
|
+
WHERE sourceId = ?
|
|
132
|
+
ORDER BY updatedAt DESC, createdAt DESC
|
|
133
|
+
LIMIT 1
|
|
134
|
+
`).get(args.sourceId);
|
|
135
|
+
return JSON.stringify({ success: true, data: row || null });
|
|
136
|
+
}
|
|
137
|
+
const row = db.prepare(`
|
|
138
|
+
SELECT id, sourceId, sourceTitle, status, content, createdAt, updatedAt
|
|
139
|
+
FROM news_fact_check_analysis
|
|
140
|
+
WHERE id = ?
|
|
141
|
+
ORDER BY updatedAt DESC, createdAt DESC
|
|
142
|
+
LIMIT 1
|
|
143
|
+
`).get(args.id);
|
|
144
|
+
return JSON.stringify({ success: true, data: row || null });
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
build_information_verification_message: {
|
|
148
|
+
name: "build_information_verification_message",
|
|
149
|
+
label: "构建信息求证消息",
|
|
150
|
+
description: "根据新闻 ID、标题和正文构建用于主动 RPC 发起 Agent 信息求证任务的标准消息。该工具只返回提示词消息体,不触发定时任务,也不保存分析结果。",
|
|
151
|
+
parameters: BuildInformationVerificationMessageAgentToolSchema,
|
|
152
|
+
registerTool: false,
|
|
153
|
+
async execute(params, ctx) {
|
|
154
|
+
const args = BuildInformationVerificationMessageParamsSchema.parse(params);
|
|
155
|
+
const userId = ctx?.userId || "default";
|
|
156
|
+
const generating = selectGeneratingInformationVerification(userId, args.newsId);
|
|
157
|
+
if (generating) {
|
|
158
|
+
return JSON.stringify({
|
|
159
|
+
success: true,
|
|
160
|
+
skipped: true,
|
|
161
|
+
reason: "already_generating",
|
|
162
|
+
data: generating
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const message = buildInformationVerificationMessage(args);
|
|
166
|
+
return JSON.stringify({
|
|
167
|
+
success: true,
|
|
168
|
+
data: {
|
|
169
|
+
message,
|
|
170
|
+
payload: JSON.parse(message),
|
|
171
|
+
sourceId: args.newsId,
|
|
172
|
+
saveParams: {
|
|
173
|
+
sourceId: args.newsId,
|
|
174
|
+
sourceTitle: args.sourceTitle
|
|
175
|
+
},
|
|
176
|
+
skill: INFORMATION_VERIFICATION_SKILL
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from "
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
const ExchangeEnum = z.enum(["SSE", "SZSE", "NASDAQ", "NYSE", "AMEX", "HKEX"]);
|
|
4
4
|
const ProfileLibraryInputSchema = z.object({
|
|
@@ -8,12 +8,12 @@ const ProfileLibraryInputSchema = z.object({
|
|
|
8
8
|
|
|
9
9
|
export const AddStockNoteParamsSchema = z.object({
|
|
10
10
|
watchlistId: z.string().trim().min(1).optional().describe("自选股 ID,优先使用该字段绑定股票"),
|
|
11
|
-
|
|
12
|
-
exchange: ExchangeEnum.optional().describe("交易所;未传 watchlistId 时需与
|
|
11
|
+
stock_code: z.string().trim().min(1).optional().describe("股票代码;未传 watchlistId 时需与 exchange 一起定位股票"),
|
|
12
|
+
exchange: ExchangeEnum.optional().describe("交易所;未传 watchlistId 时需与 stock_code 一起定位股票"),
|
|
13
13
|
note: z.string().trim().min(1).max(200).describe("笔记内容,200 字以内"),
|
|
14
14
|
profileLibraryIds: z.array(ProfileLibraryInputSchema).optional().describe("关联资料库列表,格式为 { id, title }")
|
|
15
|
-
}).refine((value) => Boolean(value.watchlistId || (value.
|
|
16
|
-
message: "watchlistId 或
|
|
15
|
+
}).refine((value) => Boolean(value.watchlistId || (value.stock_code && value.exchange)), {
|
|
16
|
+
message: "watchlistId 或 stock_code + exchange 必须传一个"
|
|
17
17
|
});
|
|
18
18
|
export type AddStockNoteParams = z.infer<typeof AddStockNoteParamsSchema>;
|
|
19
19
|
|
|
@@ -25,12 +25,12 @@ export type DeleteStockNoteParams = z.infer<typeof DeleteStockNoteParamsSchema>;
|
|
|
25
25
|
export const UpdateStockNoteParamsSchema = z.object({
|
|
26
26
|
id: z.string().trim().min(1).describe("笔记 ID"),
|
|
27
27
|
watchlistId: z.string().trim().min(1).optional().describe("自选股 ID;传入则重新绑定股票"),
|
|
28
|
-
|
|
29
|
-
exchange: ExchangeEnum.optional().describe("交易所;与
|
|
28
|
+
stock_code: z.string().trim().min(1).optional().describe("股票代码;与 exchange 一起传入可重新绑定股票"),
|
|
29
|
+
exchange: ExchangeEnum.optional().describe("交易所;与 stock_code 一起传入可重新绑定股票"),
|
|
30
30
|
note: z.string().trim().min(1).max(200).optional().describe("笔记内容,200 字以内"),
|
|
31
31
|
profileLibraryIds: z.array(ProfileLibraryInputSchema).optional().describe("关联资料库列表,格式为 { id, title };传入则覆盖原有关联")
|
|
32
|
-
}).refine((value) => !(value.
|
|
33
|
-
message: "
|
|
32
|
+
}).refine((value) => !(value.stock_code && !value.exchange) && !(!value.stock_code && value.exchange), {
|
|
33
|
+
message: "stock_code 与 exchange 必须同时传入"
|
|
34
34
|
});
|
|
35
35
|
export type UpdateStockNoteParams = z.infer<typeof UpdateStockNoteParamsSchema>;
|
|
36
36
|
|
|
@@ -41,7 +41,7 @@ export type GetStockNoteByIdParams = z.infer<typeof GetStockNoteByIdParamsSchema
|
|
|
41
41
|
|
|
42
42
|
export const QueryStockNotesParamsSchema = z.object({
|
|
43
43
|
watchlistId: z.string().trim().min(1).optional().describe("自选股 ID"),
|
|
44
|
-
|
|
44
|
+
stock_code: z.string().trim().min(1).optional().describe("股票代码"),
|
|
45
45
|
exchange: ExchangeEnum.optional().describe("交易所"),
|
|
46
46
|
keyword: z.string().trim().optional().describe("模糊查询关键词,可匹配股票代码、股票名称或笔记内容"),
|
|
47
47
|
page: z.number().int().min(1).optional().describe("页码,从 1 开始,默认 1"),
|
|
@@ -52,8 +52,8 @@ export type QueryStockNotesParams = z.infer<typeof QueryStockNotesParamsSchema>;
|
|
|
52
52
|
export interface StockNoteRow {
|
|
53
53
|
id: string;
|
|
54
54
|
watchlistId: string;
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
stock_code: string;
|
|
56
|
+
stock_name: string;
|
|
57
57
|
exchange: string;
|
|
58
58
|
market: string;
|
|
59
59
|
note: string;
|
|
@@ -73,3 +73,8 @@ export interface StockNote extends StockNoteRow {
|
|
|
73
73
|
title: string;
|
|
74
74
|
}[];
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
export const GetStockNoteParamsSchema = z.object({
|
|
78
|
+
stock_code: z.string().trim().min(1).describe("股票代码")
|
|
79
|
+
});
|
|
80
|
+
export type GetStockNoteParams = z.infer<typeof GetStockNoteParamsSchema>;
|
|
@@ -13,21 +13,33 @@ import {
|
|
|
13
13
|
StockNoteProfileLibraryRow,
|
|
14
14
|
StockNoteRow,
|
|
15
15
|
UpdateStockNoteParams,
|
|
16
|
-
UpdateStockNoteParamsSchema
|
|
16
|
+
UpdateStockNoteParamsSchema,
|
|
17
|
+
GetStockNoteParamsSchema,
|
|
18
|
+
GetStockNoteParams
|
|
17
19
|
} from "./schema.js";
|
|
18
20
|
|
|
19
21
|
interface RuntimeTool {
|
|
20
22
|
name: string;
|
|
23
|
+
label?: string;
|
|
21
24
|
description: string;
|
|
22
25
|
parameters: unknown;
|
|
23
26
|
registerTool?: boolean;
|
|
24
27
|
execute(params: unknown, ctx: { userId: string }): Promise<string>;
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
const GetStockNoteAgentToolSchema = {
|
|
31
|
+
type: "object",
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
required: ["stock_code"],
|
|
34
|
+
properties: {
|
|
35
|
+
stock_code: { type: "string", description: "股票代码,例如:000001.SZ 或 600000.SH" }
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
27
39
|
interface WatchlistStock {
|
|
28
40
|
id: string;
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
stock_code: string;
|
|
42
|
+
stock_name: string;
|
|
31
43
|
exchange: string;
|
|
32
44
|
market: string;
|
|
33
45
|
}
|
|
@@ -77,8 +89,8 @@ function getNoteSelectSql(whereSql: string): string {
|
|
|
77
89
|
SELECT
|
|
78
90
|
sn.id,
|
|
79
91
|
sn.watchlistId,
|
|
80
|
-
w.
|
|
81
|
-
w.
|
|
92
|
+
w.stock_code,
|
|
93
|
+
w.stock_name,
|
|
82
94
|
w.exchange,
|
|
83
95
|
w.market,
|
|
84
96
|
sn.note,
|
|
@@ -93,22 +105,22 @@ function getNoteSelectSql(whereSql: string): string {
|
|
|
93
105
|
function resolveWatchlistStock(
|
|
94
106
|
db: ReturnType<typeof getDB>,
|
|
95
107
|
userId: string,
|
|
96
|
-
args: { watchlistId?: string;
|
|
108
|
+
args: { watchlistId?: string; stock_code?: string; exchange?: string }
|
|
97
109
|
): WatchlistStock | null {
|
|
98
110
|
if (args.watchlistId) {
|
|
99
111
|
return db.prepare(`
|
|
100
|
-
SELECT id,
|
|
112
|
+
SELECT id, stock_code, stock_name, exchange, market
|
|
101
113
|
FROM watchlist
|
|
102
114
|
WHERE id = ? AND userId = ? AND isDeleted = 0
|
|
103
115
|
`).get(args.watchlistId.trim(), userId) as WatchlistStock | undefined || null;
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
if (args.
|
|
118
|
+
if (args.stock_code && args.exchange) {
|
|
107
119
|
return db.prepare(`
|
|
108
|
-
SELECT id,
|
|
120
|
+
SELECT id, stock_code, stock_name, exchange, market
|
|
109
121
|
FROM watchlist
|
|
110
|
-
WHERE userId = ? AND
|
|
111
|
-
`).get(userId, args.
|
|
122
|
+
WHERE userId = ? AND stock_code = ? AND exchange = ? AND isDeleted = 0
|
|
123
|
+
`).get(userId, args.stock_code.trim(), args.exchange) as WatchlistStock | undefined || null;
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
return null;
|
|
@@ -234,10 +246,10 @@ export const noteTools: Record<string, RuntimeTool> = {
|
|
|
234
246
|
return JSON.stringify({ success: false, error: "笔记不存在" });
|
|
235
247
|
}
|
|
236
248
|
|
|
237
|
-
const stock = (args.watchlistId || args.
|
|
249
|
+
const stock = (args.watchlistId || args.stock_code || args.exchange)
|
|
238
250
|
? resolveWatchlistStock(db, uId, args)
|
|
239
251
|
: null;
|
|
240
|
-
if ((args.watchlistId || args.
|
|
252
|
+
if ((args.watchlistId || args.stock_code || args.exchange) && !stock) {
|
|
241
253
|
return JSON.stringify({ success: false, error: "股票不存在或未在自选列表中" });
|
|
242
254
|
}
|
|
243
255
|
const profileLibraries = args.profileLibraryIds === undefined ? undefined : uniqueProfileLibraries(args.profileLibraryIds);
|
|
@@ -316,9 +328,9 @@ export const noteTools: Record<string, RuntimeTool> = {
|
|
|
316
328
|
conditions.push("sn.watchlistId = ?");
|
|
317
329
|
params.push(args.watchlistId);
|
|
318
330
|
}
|
|
319
|
-
if (args.
|
|
320
|
-
conditions.push("w.
|
|
321
|
-
params.push(args.
|
|
331
|
+
if (args.stock_code) {
|
|
332
|
+
conditions.push("w.stock_code = ?");
|
|
333
|
+
params.push(args.stock_code);
|
|
322
334
|
}
|
|
323
335
|
if (args.exchange) {
|
|
324
336
|
conditions.push("w.exchange = ?");
|
|
@@ -327,7 +339,7 @@ export const noteTools: Record<string, RuntimeTool> = {
|
|
|
327
339
|
const keyword = args.keyword?.trim();
|
|
328
340
|
if (keyword) {
|
|
329
341
|
const pattern = `%${escapeLikePattern(keyword)}%`;
|
|
330
|
-
conditions.push("(w.
|
|
342
|
+
conditions.push("(w.stock_code LIKE ? ESCAPE '\\' OR w.stock_name LIKE ? ESCAPE '\\' OR sn.note LIKE ? ESCAPE '\\')");
|
|
331
343
|
params.push(pattern, pattern, pattern);
|
|
332
344
|
}
|
|
333
345
|
|
|
@@ -348,5 +360,30 @@ export const noteTools: Record<string, RuntimeTool> = {
|
|
|
348
360
|
return JSON.stringify({ success: false, error: e.message });
|
|
349
361
|
}
|
|
350
362
|
}
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
get_stock_note: {
|
|
366
|
+
name: "get_stock_note",
|
|
367
|
+
label: "拉取股票笔记",
|
|
368
|
+
description: "根据股票代码拉取股票笔记。",
|
|
369
|
+
parameters: GetStockNoteAgentToolSchema,
|
|
370
|
+
registerTool: true,
|
|
371
|
+
execute: async (rawArgs: unknown) => {
|
|
372
|
+
try {
|
|
373
|
+
const args = GetStockNoteParamsSchema.parse(rawArgs);
|
|
374
|
+
const db = getDB();
|
|
375
|
+
const code = args.stock_code.trim().toUpperCase().replace(/\.SS$/i, ".SH");
|
|
376
|
+
const rows = db.prepare(`
|
|
377
|
+
SELECT sn.id, sn.note, sn.createdAt, sn.updatedAt
|
|
378
|
+
FROM stock_notes sn
|
|
379
|
+
JOIN watchlist w ON w.id = sn.watchlistId
|
|
380
|
+
WHERE w.stock_code = ? AND w.isDeleted = 0
|
|
381
|
+
ORDER BY sn.updatedAt DESC, sn.createdAt DESC
|
|
382
|
+
`).all(code) as { id: string; note: string; createdAt: string; updatedAt: string }[];
|
|
383
|
+
return JSON.stringify({ success: true, data: rows });
|
|
384
|
+
} catch (e: any) {
|
|
385
|
+
return JSON.stringify({ success: false, error: e.message });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
351
388
|
}
|
|
352
389
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const GetPluginVersionParamsSchema = z.object({}).nullish();
|
|
4
|
+
|
|
5
|
+
export const GetSkillVersionsParamsSchema = z.object({}).nullish();
|
|
6
|
+
|
|
7
|
+
export const UpdateSkillVersionsParamsSchema = z.object({
|
|
8
|
+
versions: z.record(z.string()).optional(),
|
|
9
|
+
skills: z.array(z.object({
|
|
10
|
+
name: z.string(),
|
|
11
|
+
version: z.string()
|
|
12
|
+
})).optional()
|
|
13
|
+
}).refine(params => Boolean(params.versions || params.skills), {
|
|
14
|
+
message: "versions or skills is required"
|
|
15
|
+
});
|
|
16
|
+
export type UpdateSkillVersionsParams = z.infer<typeof UpdateSkillVersionsParamsSchema>;
|
|
17
|
+
|
|
18
|
+
export const BuildUpdateSkillVersionsMessageParamsSchema = UpdateSkillVersionsParamsSchema;
|
|
19
|
+
export type BuildUpdateSkillVersionsMessageParams = z.infer<typeof BuildUpdateSkillVersionsMessageParamsSchema>;
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import {
|
|
4
|
+
import { getDB } from "../../core/database.js";
|
|
5
|
+
import {
|
|
6
|
+
BuildUpdateSkillVersionsMessageParams,
|
|
7
|
+
BuildUpdateSkillVersionsMessageParamsSchema,
|
|
8
|
+
GetPluginVersionParamsSchema,
|
|
9
|
+
GetSkillVersionsParamsSchema,
|
|
10
|
+
UpdateSkillVersionsParams,
|
|
11
|
+
UpdateSkillVersionsParamsSchema
|
|
12
|
+
} from "./schema.js";
|
|
5
13
|
|
|
6
14
|
interface RuntimeTool {
|
|
7
15
|
name: string;
|
|
8
16
|
description: string;
|
|
9
17
|
parameters: unknown;
|
|
10
18
|
registerTool?: boolean;
|
|
19
|
+
agentToolTarget?: "main";
|
|
11
20
|
execute(params: unknown, ctx: { userId: string }): Promise<string>;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
const
|
|
23
|
+
const UPDATE_SKILL_VERSIONS_TOOL_NAME = "update_hedgehog_skill_versions";
|
|
24
|
+
const HEDGEHOG_INIT_SKILL_NAME = "hedgehog-init";
|
|
15
25
|
|
|
16
26
|
let cachedPluginVersion: string | null = null;
|
|
17
27
|
|
|
@@ -46,6 +56,119 @@ function getPluginVersion(): string {
|
|
|
46
56
|
return cachedPluginVersion;
|
|
47
57
|
}
|
|
48
58
|
|
|
59
|
+
function getSkillVersions() {
|
|
60
|
+
const rows = getDB().prepare(`
|
|
61
|
+
SELECT skillName, version, createdAt, updatedAt
|
|
62
|
+
FROM skill_versions
|
|
63
|
+
ORDER BY skillName ASC
|
|
64
|
+
`).all() as { skillName: string; version: string; createdAt: string; updatedAt: string }[];
|
|
65
|
+
const versions: Record<string, string> = {};
|
|
66
|
+
const skills = rows.map(row => {
|
|
67
|
+
versions[row.skillName] = row.version;
|
|
68
|
+
return {
|
|
69
|
+
name: row.skillName,
|
|
70
|
+
version: row.version,
|
|
71
|
+
createdAt: row.createdAt,
|
|
72
|
+
updatedAt: row.updatedAt
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
skills,
|
|
78
|
+
versions
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeSkillVersionUpdates(params: UpdateSkillVersionsParams) {
|
|
83
|
+
const updates = new Map<string, string>();
|
|
84
|
+
|
|
85
|
+
if (params.versions) {
|
|
86
|
+
for (const [name, version] of Object.entries(params.versions)) {
|
|
87
|
+
const trimmedName = name.trim();
|
|
88
|
+
const trimmedVersion = version.trim();
|
|
89
|
+
if (trimmedName && trimmedVersion) updates.set(trimmedName, trimmedVersion);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (params.skills) {
|
|
94
|
+
for (const skill of params.skills) {
|
|
95
|
+
const trimmedName = skill.name.trim();
|
|
96
|
+
const trimmedVersion = skill.version.trim();
|
|
97
|
+
if (trimmedName && trimmedVersion) updates.set(trimmedName, trimmedVersion);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return Array.from(updates.entries()).map(([name, version]) => ({ name, version }));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function updateSkillVersions(params: UpdateSkillVersionsParams) {
|
|
105
|
+
const updates = normalizeSkillVersionUpdates(params);
|
|
106
|
+
const db = getDB();
|
|
107
|
+
const stmt = db.prepare(`
|
|
108
|
+
INSERT INTO skill_versions (skillName, version, updatedAt)
|
|
109
|
+
VALUES (?, ?, STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW'))
|
|
110
|
+
ON CONFLICT(skillName) DO UPDATE SET
|
|
111
|
+
version = excluded.version,
|
|
112
|
+
updatedAt = excluded.updatedAt
|
|
113
|
+
`);
|
|
114
|
+
|
|
115
|
+
db.exec("BEGIN");
|
|
116
|
+
try {
|
|
117
|
+
for (const update of updates) {
|
|
118
|
+
stmt.run(update.name, update.version);
|
|
119
|
+
}
|
|
120
|
+
db.exec("COMMIT");
|
|
121
|
+
} catch (e) {
|
|
122
|
+
if (db.inTransaction) db.exec("ROLLBACK");
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
updated: updates,
|
|
128
|
+
...getSkillVersions()
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildUpdateSkillVersionsMessage(params: BuildUpdateSkillVersionsMessageParams): string {
|
|
133
|
+
const updates = normalizeSkillVersionUpdates(params);
|
|
134
|
+
const toolParams = {
|
|
135
|
+
skills: updates
|
|
136
|
+
};
|
|
137
|
+
const includesHedgehogInit = updates.some(update => update.name === HEDGEHOG_INIT_SKILL_NAME);
|
|
138
|
+
|
|
139
|
+
if (includesHedgehogInit) {
|
|
140
|
+
return JSON.stringify({
|
|
141
|
+
cw_system_prompt: [
|
|
142
|
+
"必须触发并使用 hedgehog-init skill 更新 hedgehog-workspace 的系统提示词到最新版本。",
|
|
143
|
+
`只有在 hedgehog-init skill 的系统提示词更新流程成功后,才可以调用 ${UPDATE_SKILL_VERSIONS_TOOL_NAME} 工具。`,
|
|
144
|
+
`成功后必须调用 ${UPDATE_SKILL_VERSIONS_TOOL_NAME} 工具更新本地数据库中的 skill 版本号,参数为 ${JSON.stringify(toolParams)}。`,
|
|
145
|
+
"如果系统提示词更新失败,禁止调用 update_hedgehog_skill_versions,必须直接说明失败原因。"
|
|
146
|
+
].join("\n"),
|
|
147
|
+
cw_context: JSON.stringify({
|
|
148
|
+
action: "update_hedgehog_init_system_prompt",
|
|
149
|
+
skills: updates
|
|
150
|
+
}),
|
|
151
|
+
cw_content: "Skill升级了,帮我更新`hedgehog-workspace`系统提示词到最新版本。",
|
|
152
|
+
cw_output: "系统提示词更新成功后,调用 update_hedgehog_skill_versions 工具写入版本,并返回简短结果。"
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return JSON.stringify({
|
|
157
|
+
cw_system_prompt: [
|
|
158
|
+
"必须先更新 hedgehog-workspace 中的 hedgehog skills。",
|
|
159
|
+
`只有在 hedgehog skills 更新成功后,才可以调用 ${UPDATE_SKILL_VERSIONS_TOOL_NAME} 工具。`,
|
|
160
|
+
`更新成功后必须调用 ${UPDATE_SKILL_VERSIONS_TOOL_NAME} 工具更新本地数据库中的 skill 版本号,参数为 ${JSON.stringify(toolParams)}。`,
|
|
161
|
+
"如果 hedgehog skills 更新失败,禁止调用 update_hedgehog_skill_versions,必须直接说明失败原因。"
|
|
162
|
+
].join("\n"),
|
|
163
|
+
cw_context: JSON.stringify({
|
|
164
|
+
action: "update_hedgehog_skills",
|
|
165
|
+
skills: updates
|
|
166
|
+
}),
|
|
167
|
+
cw_content: "帮我更新`hedgehog-workspace`的hedgehog skills",
|
|
168
|
+
cw_output: "hedgehog skills 更新成功后,调用 update_hedgehog_skill_versions 工具写入版本,并返回简短结果。"
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
49
172
|
export const pluginInfoTools: Record<string, RuntimeTool> = {
|
|
50
173
|
get_plugin_version: {
|
|
51
174
|
name: "get_plugin_version",
|
|
@@ -59,5 +182,53 @@ export const pluginInfoTools: Record<string, RuntimeTool> = {
|
|
|
59
182
|
version: getPluginVersion()
|
|
60
183
|
});
|
|
61
184
|
}
|
|
185
|
+
},
|
|
186
|
+
get_skill_versions: {
|
|
187
|
+
name: "get_skill_versions",
|
|
188
|
+
description: "获取数据库中记录的所有 skill 版本号",
|
|
189
|
+
parameters: GetSkillVersionsParamsSchema,
|
|
190
|
+
registerTool: false,
|
|
191
|
+
async execute(params: unknown) {
|
|
192
|
+
GetSkillVersionsParamsSchema.parse(params);
|
|
193
|
+
return JSON.stringify({
|
|
194
|
+
success: true,
|
|
195
|
+
...getSkillVersions()
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
update_hedgehog_skill_versions: {
|
|
200
|
+
name: "update_hedgehog_skill_versions",
|
|
201
|
+
description: "更新数据库中记录的 skill 版本号",
|
|
202
|
+
parameters: UpdateSkillVersionsParamsSchema,
|
|
203
|
+
registerTool: false,
|
|
204
|
+
agentToolTarget: "main",
|
|
205
|
+
async execute(params: unknown) {
|
|
206
|
+
const parsed = UpdateSkillVersionsParamsSchema.parse(params);
|
|
207
|
+
return JSON.stringify({
|
|
208
|
+
success: true,
|
|
209
|
+
...updateSkillVersions(parsed)
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
build_update_skill_versions_message: {
|
|
214
|
+
name: "build_update_skill_versions_message",
|
|
215
|
+
description: "构建用于主动 RPC 发起 Agent 更新数据库 skill 版本号任务的标准消息。该方法只返回提示词消息体,不直接更新数据库。",
|
|
216
|
+
parameters: BuildUpdateSkillVersionsMessageParamsSchema,
|
|
217
|
+
registerTool: false,
|
|
218
|
+
async execute(params: unknown) {
|
|
219
|
+
const parsed = BuildUpdateSkillVersionsMessageParamsSchema.parse(params);
|
|
220
|
+
const message = buildUpdateSkillVersionsMessage(parsed);
|
|
221
|
+
return JSON.stringify({
|
|
222
|
+
success: true,
|
|
223
|
+
data: {
|
|
224
|
+
message,
|
|
225
|
+
payload: JSON.parse(message),
|
|
226
|
+
tool: UPDATE_SKILL_VERSIONS_TOOL_NAME,
|
|
227
|
+
updateParams: {
|
|
228
|
+
skills: normalizeSkillVersionUpdates(parsed)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
62
233
|
}
|
|
63
234
|
};
|