@next-open-ai/openclawx 0.8.17 → 0.8.22
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 +8 -8
- package/apps/desktop/renderer/dist/assets/index-B0_RWD2F.css +10 -0
- package/apps/desktop/renderer/dist/assets/index-vZN87oBP.js +89 -0
- package/apps/desktop/renderer/dist/index.html +2 -2
- package/dist/core/agent/agent-manager.d.ts +10 -4
- package/dist/core/agent/agent-manager.js +49 -22
- package/dist/core/agent/proxy/adapters/coze-adapter.js +7 -0
- package/dist/core/agent/proxy/adapters/local-adapter.js +4 -11
- package/dist/core/agent/proxy/adapters/openclawx-adapter.js +7 -0
- package/dist/core/agent/proxy/adapters/opencode-adapter.d.ts +11 -0
- package/dist/core/agent/proxy/adapters/opencode-adapter.js +716 -0
- package/dist/core/agent/proxy/adapters/opencode-free-models.d.ts +20 -0
- package/dist/core/agent/proxy/adapters/opencode-free-models.js +14 -0
- package/dist/core/agent/proxy/adapters/opencode-local-runner.d.ts +5 -0
- package/dist/core/agent/proxy/adapters/opencode-local-runner.js +86 -0
- package/dist/core/agent/proxy/index.js +3 -1
- package/dist/core/agent/proxy/run-for-channel.js +1 -1
- package/dist/core/agent/proxy/types.d.ts +2 -0
- package/dist/core/agent/run.js +1 -1
- package/dist/core/config/desktop-config.d.ts +71 -3
- package/dist/core/config/desktop-config.js +222 -24
- package/dist/core/memory/compaction-extension.d.ts +4 -3
- package/dist/core/memory/compaction-extension.js +6 -14
- package/dist/core/memory/embedding-types.d.ts +10 -0
- package/dist/core/memory/embedding-types.js +5 -0
- package/dist/core/memory/embedding.d.ts +2 -1
- package/dist/core/memory/embedding.js +38 -6
- package/dist/core/memory/index.js +3 -0
- package/dist/core/memory/local-embedding-llama.d.ts +13 -0
- package/dist/core/memory/local-embedding-llama.js +76 -0
- package/dist/core/memory/local-embedding.d.ts +10 -0
- package/dist/core/memory/local-embedding.js +29 -0
- package/dist/core/memory/persist-compaction-on-close.d.ts +14 -0
- package/dist/core/memory/persist-compaction-on-close.js +32 -0
- package/dist/core/tools/bookmark-tool.d.ts +4 -0
- package/dist/core/tools/bookmark-tool.js +59 -3
- package/dist/core/tools/index.d.ts +2 -1
- package/dist/core/tools/index.js +2 -1
- package/dist/core/tools/memory-recall-tool.d.ts +6 -0
- package/dist/core/tools/memory-recall-tool.js +77 -0
- package/dist/gateway/channel/adapters/wechat.d.ts +24 -0
- package/dist/gateway/channel/adapters/wechat.js +205 -0
- package/dist/gateway/methods/agent-cancel.d.ts +3 -1
- package/dist/gateway/methods/agent-cancel.js +13 -2
- package/dist/gateway/methods/agent-chat.js +109 -23
- package/dist/gateway/methods/run-scheduled-task.js +3 -7
- package/dist/gateway/proxy-run-abort.d.ts +6 -0
- package/dist/gateway/proxy-run-abort.js +39 -0
- package/dist/gateway/server.js +62 -7
- package/dist/server/agent-config/agent-config.controller.d.ts +2 -2
- package/dist/server/agent-config/agent-config.controller.js +8 -4
- package/dist/server/agent-config/agent-config.module.js +3 -1
- package/dist/server/agent-config/agent-config.service.d.ts +41 -6
- package/dist/server/agent-config/agent-config.service.js +30 -3
- package/dist/server/agents/agents.service.js +1 -1
- package/dist/server/bootstrap.js +9 -2
- package/dist/server/config/config.controller.d.ts +31 -2
- package/dist/server/config/config.controller.js +14 -0
- package/dist/server/config/config.module.js +2 -2
- package/dist/server/config/config.service.d.ts +14 -1
- package/dist/server/config/config.service.js +1 -0
- package/dist/server/workspace/workspace.service.d.ts +7 -0
- package/dist/server/workspace/workspace.service.js +16 -0
- package/package.json +6 -1
- package/skills/url-bookmark/SKILL.md +12 -12
- package/apps/desktop/renderer/dist/assets/index-DmIfN-Vc.js +0 -89
- package/apps/desktop/renderer/dist/assets/index-DvB8yW8I.css +0 -10
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
import { addMemory } from "./index.js";
|
|
2
1
|
/**
|
|
3
|
-
* 创建用于在 session_compact
|
|
4
|
-
*
|
|
2
|
+
* 创建用于在 session_compact 事件时更新「当前 session 最新 compaction」的 extension factory。
|
|
3
|
+
* 发生 compaction 时只回调 onUpdateLatestCompaction(summary),由调用方保存;
|
|
4
|
+
* 向量库写入推迟到 SessionAgent 关闭时由 AgentManager 统一执行。
|
|
5
5
|
*/
|
|
6
|
-
export function createCompactionMemoryExtensionFactory(
|
|
6
|
+
export function createCompactionMemoryExtensionFactory(_sessionId, onUpdateLatestCompaction) {
|
|
7
7
|
return (pi) => {
|
|
8
|
-
pi.on("session_compact",
|
|
8
|
+
pi.on("session_compact", (event) => {
|
|
9
9
|
const summary = event.compactionEntry?.summary?.trim();
|
|
10
10
|
if (!summary)
|
|
11
11
|
return;
|
|
12
|
-
|
|
13
|
-
await addMemory(summary, {
|
|
14
|
-
infotype: "compaction",
|
|
15
|
-
sessionId,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
catch (_) {
|
|
19
|
-
// 写入失败不打断会话
|
|
20
|
-
}
|
|
12
|
+
onUpdateLatestCompaction(summary);
|
|
21
13
|
});
|
|
22
14
|
};
|
|
23
15
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding 抽象:便于后续切换不同实现(在线 OpenAPI、node-llama-cpp GGUF 等)。
|
|
3
|
+
* 调用方仅依赖 embed(text) => Promise<number[] | null>。
|
|
4
|
+
*/
|
|
5
|
+
export interface IEmbeddingProvider {
|
|
6
|
+
/** 单条文本向量化,不可用时返回 null */
|
|
7
|
+
embed(text: string): Promise<number[] | null>;
|
|
8
|
+
/** 提供方名称,用于日志 */
|
|
9
|
+
readonly name: string;
|
|
10
|
+
}
|
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 文本 embedding
|
|
3
|
-
*
|
|
2
|
+
* 文本 embedding 统一入口(供 memory 向量库等使用)。
|
|
3
|
+
*
|
|
4
|
+
* 使用逻辑:
|
|
5
|
+
* - 未配置在线 embedding:直接使用本地 embedding(GGUF)。
|
|
6
|
+
* - 已配置在线 embedding:先尝试在线;若请求失败(无法连接、超时、返回错误等),则回退到本地 embedding。
|
|
7
|
+
* - 本地也不可用时返回 null,长记忆空转。
|
|
4
8
|
*/
|
|
5
9
|
import { getRagEmbeddingConfigSync } from "../config/desktop-config.js";
|
|
6
10
|
import { embedRemote } from "./remote-embedding.js";
|
|
11
|
+
import { getLocalEmbeddingProvider, getLocalEmbeddingUnavailableReason } from "./local-embedding.js";
|
|
12
|
+
let fallbackLogged = false;
|
|
13
|
+
let unavailableLogged = false;
|
|
7
14
|
/**
|
|
8
|
-
* 对单条文本做 embedding
|
|
15
|
+
* 对单条文本做 embedding。
|
|
16
|
+
* 未配置在线或在线连接失败时使用本地 embedding;均不可用时返回 null(仅首次打日志)。
|
|
9
17
|
*/
|
|
10
18
|
export async function embed(text) {
|
|
11
19
|
const config = getRagEmbeddingConfigSync();
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
if (config) {
|
|
21
|
+
try {
|
|
22
|
+
const vec = await embedRemote(text, config);
|
|
23
|
+
if (Array.isArray(vec) && vec.length > 0)
|
|
24
|
+
return vec;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// 配置了在线但请求失败(无法连接等),下面使用本地
|
|
28
|
+
}
|
|
29
|
+
if (!fallbackLogged) {
|
|
30
|
+
fallbackLogged = true;
|
|
31
|
+
console.warn("[RAG embedding] 在线模型不可用,已切换至本地模型(GGUF)");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const local = await getLocalEmbeddingProvider();
|
|
35
|
+
if (local) {
|
|
36
|
+
const vec = await local.embed(text);
|
|
37
|
+
if (Array.isArray(vec) && vec.length > 0)
|
|
38
|
+
return vec;
|
|
39
|
+
}
|
|
40
|
+
if (!unavailableLogged) {
|
|
41
|
+
unavailableLogged = true;
|
|
42
|
+
const reason = getLocalEmbeddingUnavailableReason();
|
|
43
|
+
const reasonSuffix = reason ? ` 原因: ${reason}` : "(若为首次加载,请先执行 npm run build 后重启桌面端以查看具体原因)";
|
|
44
|
+
console.warn("[RAG embedding] 本地模型也不可用,长记忆将空转。" + reasonSuffix);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
15
47
|
}
|
|
@@ -26,6 +26,9 @@ export async function addMemory(text, metadata) {
|
|
|
26
26
|
createdAt: new Date().toISOString(),
|
|
27
27
|
};
|
|
28
28
|
await addToStore(id, vec, text, meta);
|
|
29
|
+
if (metadata.infotype === "experience") {
|
|
30
|
+
console.log(`[Memory] 经验已写入向量库 sessionId=${metadata.sessionId} id=${id}`);
|
|
31
|
+
}
|
|
29
32
|
return id;
|
|
30
33
|
}
|
|
31
34
|
/**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地 embedding(Electron 主进程):node-llama-cpp + GGUF。
|
|
3
|
+
* 需在安装依赖后对 Electron 的 Node ABI 执行 electron-rebuild(见 apps/desktop 的 rebuild:native)。
|
|
4
|
+
* 加载前会注册 ESM 钩子,将 "typescript" 解析为桩模块,避免依赖链加载 typescript.js(含 with 语句,ESM 下报错)。
|
|
5
|
+
*/
|
|
6
|
+
import type { IEmbeddingProvider } from "./embedding-types.js";
|
|
7
|
+
export declare function getLocalEmbeddingLlamaUnavailableReason(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* 获取基于 node-llama-cpp + GGUF 的本地 embedding 提供方。
|
|
10
|
+
* 仅在可解析到 node-llama-cpp 且模型可加载时返回,否则返回 null。
|
|
11
|
+
* @param modelPath 配置的 GGUF 路径或 hf: URI,为空时使用 DEFAULT_GGUF_MODEL
|
|
12
|
+
*/
|
|
13
|
+
export declare function getLocalEmbeddingLlamaProvider(modelPath: string | null): Promise<IEmbeddingProvider | null>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
let cached = null;
|
|
4
|
+
let initError = null;
|
|
5
|
+
let lastQueryError = null;
|
|
6
|
+
export function getLocalEmbeddingLlamaUnavailableReason() {
|
|
7
|
+
return initError?.message ?? lastQueryError ?? null;
|
|
8
|
+
}
|
|
9
|
+
function L2Normalize(vec) {
|
|
10
|
+
let sum = 0;
|
|
11
|
+
for (let i = 0; i < vec.length; i++)
|
|
12
|
+
sum += vec[i] * vec[i];
|
|
13
|
+
const norm = Math.sqrt(sum) || 1;
|
|
14
|
+
return vec.map((x) => x / norm);
|
|
15
|
+
}
|
|
16
|
+
/** 默认 GGUF embedding 模型(与 moltbot 一致),首次使用会按 node-llama-cpp 规则下载到缓存 */
|
|
17
|
+
const DEFAULT_GGUF_MODEL = "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf";
|
|
18
|
+
/**
|
|
19
|
+
* 获取基于 node-llama-cpp + GGUF 的本地 embedding 提供方。
|
|
20
|
+
* 仅在可解析到 node-llama-cpp 且模型可加载时返回,否则返回 null。
|
|
21
|
+
* @param modelPath 配置的 GGUF 路径或 hf: URI,为空时使用 DEFAULT_GGUF_MODEL
|
|
22
|
+
*/
|
|
23
|
+
export async function getLocalEmbeddingLlamaProvider(modelPath) {
|
|
24
|
+
if (cached)
|
|
25
|
+
return cached;
|
|
26
|
+
if (initError)
|
|
27
|
+
return null;
|
|
28
|
+
const effectivePath = (modelPath?.trim() || DEFAULT_GGUF_MODEL).trim();
|
|
29
|
+
try {
|
|
30
|
+
// Node 20.10+ 才提供 node:module 的 register;Electron 内置 Node 较旧时无此导出,跳过钩子
|
|
31
|
+
const nodeModule = await import("node:module");
|
|
32
|
+
if (typeof nodeModule.register === "function") {
|
|
33
|
+
const loaderUrl = new URL("../../../scripts/ts-stub-loader.mjs", import.meta.url).href;
|
|
34
|
+
await Promise.resolve(nodeModule.register(loaderUrl, import.meta.url));
|
|
35
|
+
}
|
|
36
|
+
const { getLlama, resolveModelFile, LlamaLogLevel } = await import("node-llama-cpp");
|
|
37
|
+
const llama = await getLlama({ logLevel: LlamaLogLevel.error });
|
|
38
|
+
const cacheDir = join(homedir(), ".cache", "llama");
|
|
39
|
+
const resolved = await resolveModelFile(effectivePath, cacheDir);
|
|
40
|
+
const model = await llama.loadModel({ modelPath: resolved });
|
|
41
|
+
const embeddingCtx = await model.createEmbeddingContext();
|
|
42
|
+
const provider = {
|
|
43
|
+
name: "llama-gguf",
|
|
44
|
+
async embed(text) {
|
|
45
|
+
try {
|
|
46
|
+
const embedding = await embeddingCtx.getEmbeddingFor(text);
|
|
47
|
+
const vec = Array.from(embedding.vector);
|
|
48
|
+
if (vec.length === 0) {
|
|
49
|
+
lastQueryError = "getEmbeddingFor 返回空向量";
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return L2Normalize(vec);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
56
|
+
lastQueryError = `llama embed 失败: ${err.message}`;
|
|
57
|
+
console.warn("[RAG embedding] node-llama-cpp embed 失败:", err.message);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
cached = provider;
|
|
63
|
+
return provider;
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
initError = e instanceof Error ? e : new Error(String(e));
|
|
67
|
+
console.warn("[RAG embedding] node-llama-cpp (GGUF) 不可用,原因:", initError.message);
|
|
68
|
+
if (initError.stack) {
|
|
69
|
+
console.warn("[RAG embedding] GGUF 加载失败堆栈:\n", initError.stack);
|
|
70
|
+
}
|
|
71
|
+
if (initError.message.includes("Unexpected token 'with'")) {
|
|
72
|
+
console.warn("[RAG embedding] 提示: 依赖链中某包在 ESM 下使用了禁止的 with 语句(如 typescript.js)。可配置在线 RAG 或升级 Electron 至 Node 22+。");
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地 embedding:仅 node-llama-cpp (GGUF)。不可用时返回 null,由上层决定是否使用在线 RAG。
|
|
3
|
+
*/
|
|
4
|
+
import type { IEmbeddingProvider } from "./embedding-types.js";
|
|
5
|
+
export declare function getLocalEmbeddingUnavailableReason(): string | null;
|
|
6
|
+
/**
|
|
7
|
+
* 获取本地 embedding 提供方(懒加载,失败后不再重试)。
|
|
8
|
+
* 仅使用 node-llama-cpp (GGUF)。不可用时返回 null。
|
|
9
|
+
*/
|
|
10
|
+
export declare function getLocalEmbeddingProvider(): Promise<IEmbeddingProvider | null>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getRagLocalModelPathSync } from "../config/desktop-config.js";
|
|
2
|
+
import { getLocalEmbeddingLlamaProvider, getLocalEmbeddingLlamaUnavailableReason, } from "./local-embedding-llama.js";
|
|
3
|
+
let cached = null;
|
|
4
|
+
let envLogged = false;
|
|
5
|
+
export function getLocalEmbeddingUnavailableReason() {
|
|
6
|
+
return getLocalEmbeddingLlamaUnavailableReason();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 获取本地 embedding 提供方(懒加载,失败后不再重试)。
|
|
10
|
+
* 仅使用 node-llama-cpp (GGUF)。不可用时返回 null。
|
|
11
|
+
*/
|
|
12
|
+
export async function getLocalEmbeddingProvider() {
|
|
13
|
+
if (cached)
|
|
14
|
+
return cached;
|
|
15
|
+
const provider = await getLocalEmbeddingLlamaProvider(getRagLocalModelPathSync());
|
|
16
|
+
if (provider) {
|
|
17
|
+
cached = provider;
|
|
18
|
+
if (!envLogged) {
|
|
19
|
+
envLogged = true;
|
|
20
|
+
console.warn("[RAG embedding] 本地模型使用 node-llama-cpp (GGUF)");
|
|
21
|
+
}
|
|
22
|
+
return cached;
|
|
23
|
+
}
|
|
24
|
+
if (!envLogged) {
|
|
25
|
+
envLogged = true;
|
|
26
|
+
console.warn("[RAG embedding] 本地模型不可用,长记忆将空转。可配置 RAG 在线模型或检查 GGUF 报错。");
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type AddMemoryCompaction = (text: string, meta: {
|
|
2
|
+
infotype: "compaction";
|
|
3
|
+
sessionId: string;
|
|
4
|
+
}) => Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* 将 map 中指定 compositeKey 的 summary 写入 addMemory 并从 map 删除;用于 Session 关闭前。
|
|
7
|
+
* 写入采用 fire-and-forget,不阻塞调用方,避免 agent.chat 等请求被 addMemory/embed 拖住导致无回复。
|
|
8
|
+
*/
|
|
9
|
+
export declare function persistStoredCompactionForSession(sessionLatestCompactionSummary: Map<string, string>, compositeKey: string, addMemory: AddMemoryCompaction): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* 将 map 中所有给定 keys(需以 sessionId + "::" 为前缀)的 summary 依次写入 addMemory 并删除;用于按业务 sessionId 关闭时。
|
|
12
|
+
* 写入采用 fire-and-forget,不阻塞调用方。
|
|
13
|
+
*/
|
|
14
|
+
export declare function persistStoredCompactionForBusinessSession(sessionLatestCompactionSummary: Map<string, string>, keysToProcess: string[], sessionId: string, addMemory: AddMemoryCompaction): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话关闭时将内存中缓存的 compaction summary 写入向量库的纯逻辑,便于单测且与 AgentManager 解耦。
|
|
3
|
+
*/
|
|
4
|
+
const COMPOSITE_KEY_SEP = "::";
|
|
5
|
+
/**
|
|
6
|
+
* 将 map 中指定 compositeKey 的 summary 写入 addMemory 并从 map 删除;用于 Session 关闭前。
|
|
7
|
+
* 写入采用 fire-and-forget,不阻塞调用方,避免 agent.chat 等请求被 addMemory/embed 拖住导致无回复。
|
|
8
|
+
*/
|
|
9
|
+
export async function persistStoredCompactionForSession(sessionLatestCompactionSummary, compositeKey, addMemory) {
|
|
10
|
+
const summary = sessionLatestCompactionSummary.get(compositeKey);
|
|
11
|
+
sessionLatestCompactionSummary.delete(compositeKey);
|
|
12
|
+
if (summary?.trim()) {
|
|
13
|
+
const sessionId = compositeKey.split(COMPOSITE_KEY_SEP)[0];
|
|
14
|
+
void addMemory(summary.trim(), { infotype: "compaction", sessionId }).catch(() => { });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 将 map 中所有给定 keys(需以 sessionId + "::" 为前缀)的 summary 依次写入 addMemory 并删除;用于按业务 sessionId 关闭时。
|
|
19
|
+
* 写入采用 fire-and-forget,不阻塞调用方。
|
|
20
|
+
*/
|
|
21
|
+
export async function persistStoredCompactionForBusinessSession(sessionLatestCompactionSummary, keysToProcess, sessionId, addMemory) {
|
|
22
|
+
const prefix = sessionId + COMPOSITE_KEY_SEP;
|
|
23
|
+
for (const key of keysToProcess) {
|
|
24
|
+
if (!key.startsWith(prefix))
|
|
25
|
+
continue;
|
|
26
|
+
const summary = sessionLatestCompactionSummary.get(key);
|
|
27
|
+
sessionLatestCompactionSummary.delete(key);
|
|
28
|
+
if (summary?.trim()) {
|
|
29
|
+
void addMemory(summary.trim(), { infotype: "compaction", sessionId }).catch(() => { });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
/**
|
|
3
|
+
* 新增一个收藏标签。当 get_bookmark_tags 返回的列表中缺少用户想要的标签时,可先调用本工具创建该标签,再使用 save_bookmark 保存收藏。
|
|
4
|
+
*/
|
|
5
|
+
export declare function createAddBookmarkTagTool(): ToolDefinition;
|
|
2
6
|
/**
|
|
3
7
|
* 获取当前系统中已维护的标签列表,供保存 URL 时选择匹配的标签。
|
|
4
8
|
*/
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { getBackendBaseUrl } from "../../gateway/backend-url.js";
|
|
3
3
|
const GetBookmarkTagsSchema = Type.Object({});
|
|
4
|
+
const AddBookmarkTagSchema = Type.Object({
|
|
5
|
+
tagName: Type.String({ description: "要新增的标签名称,如「技术」「待读」「美女」等" }),
|
|
6
|
+
});
|
|
4
7
|
const SaveBookmarkSchema = Type.Object({
|
|
5
8
|
url: Type.String({ description: "要收藏的 URL" }),
|
|
6
9
|
title: Type.Optional(Type.String({ description: "可选标题" })),
|
|
7
10
|
tagNames: Type.Optional(Type.Array(Type.String(), {
|
|
8
|
-
description: "标签名称列表,须与系统中已维护的标签一致。保存前应先用 get_bookmark_tags
|
|
11
|
+
description: "标签名称列表,须与系统中已维护的标签一致。保存前应先用 get_bookmark_tags 获取可用标签;若缺标签可先用 add_bookmark_tag 新增。",
|
|
9
12
|
})),
|
|
10
13
|
});
|
|
11
14
|
async function apiGet(path) {
|
|
@@ -39,6 +42,59 @@ async function apiPost(path, body) {
|
|
|
39
42
|
}
|
|
40
43
|
return res.json();
|
|
41
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* 新增一个收藏标签。当 get_bookmark_tags 返回的列表中缺少用户想要的标签时,可先调用本工具创建该标签,再使用 save_bookmark 保存收藏。
|
|
47
|
+
*/
|
|
48
|
+
export function createAddBookmarkTagTool() {
|
|
49
|
+
return {
|
|
50
|
+
name: "add_bookmark_tag",
|
|
51
|
+
label: "Add Bookmark Tag",
|
|
52
|
+
description: "在系统中新增一个收藏标签。当用户要保存链接到某个尚不存在的标签(如「美女」「技术」「待读」)时,先调用本工具创建该标签,再调用 get_bookmark_tags 获取最新列表并用 save_bookmark 保存。若标签已存在会提示无需重复创建。",
|
|
53
|
+
parameters: AddBookmarkTagSchema,
|
|
54
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
55
|
+
const tagName = (params.tagName ?? "").trim();
|
|
56
|
+
if (!tagName) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: "请提供要新增的标签名称。" }],
|
|
59
|
+
details: undefined,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const json = await apiPost("/tags", {
|
|
64
|
+
name: tagName,
|
|
65
|
+
});
|
|
66
|
+
const data = json.data;
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: `已新增标签「${data?.name ?? tagName}」,可直接用于 save_bookmark 的 tagNames。`,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
details: data,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
79
|
+
if (msg.includes("409") || msg.includes("已存在") || msg.includes("Conflict")) {
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `标签「${tagName}」已存在,无需重复创建。可直接在 save_bookmark 的 tagNames 中使用该名称。`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
details: undefined,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
content: [{ type: "text", text: `新增标签失败: ${msg}` }],
|
|
92
|
+
details: undefined,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
42
98
|
/**
|
|
43
99
|
* 获取当前系统中已维护的标签列表,供保存 URL 时选择匹配的标签。
|
|
44
100
|
*/
|
|
@@ -55,7 +111,7 @@ export function createGetBookmarkTagsTool() {
|
|
|
55
111
|
const names = data.map((t) => t.name);
|
|
56
112
|
const text = names.length > 0
|
|
57
113
|
? `当前可用标签:${names.join("、")}。请根据用户意图选择匹配的标签名用于 save_bookmark。`
|
|
58
|
-
: "
|
|
114
|
+
: "当前暂无标签。可使用 add_bookmark_tag 新增标签,或 save_bookmark 时不传 tagNames。";
|
|
59
115
|
return {
|
|
60
116
|
content: [{ type: "text", text }],
|
|
61
117
|
details: { tags: data },
|
|
@@ -78,7 +134,7 @@ export function createSaveBookmarkTool() {
|
|
|
78
134
|
return {
|
|
79
135
|
name: "save_bookmark",
|
|
80
136
|
label: "Save Bookmark",
|
|
81
|
-
description: "将用户提供的 URL
|
|
137
|
+
description: "将用户提供的 URL 保存到收藏库,并可关联一个或多个标签。标签名须为系统已有标签(get_bookmark_tags 返回);若缺少某标签可先用 add_bookmark_tag 新增。",
|
|
82
138
|
parameters: SaveBookmarkSchema,
|
|
83
139
|
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
84
140
|
const url = (params.url ?? "").trim();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { createBrowserTool, closeBrowser } from "./browser-tool.js";
|
|
2
2
|
export { createSaveExperienceTool } from "./save-experience-tool.js";
|
|
3
|
+
export { createMemoryRecallTool } from "./memory-recall-tool.js";
|
|
3
4
|
export { createInstallSkillTool } from "./install-skill-tool.js";
|
|
4
5
|
export { createSwitchAgentTool } from "./switch-agent-tool.js";
|
|
5
6
|
export { createListAgentsTool } from "./list-agents-tool.js";
|
|
6
7
|
export { createCreateAgentTool } from "./create-agent-tool.js";
|
|
7
|
-
export { createGetBookmarkTagsTool, createSaveBookmarkTool } from "./bookmark-tool.js";
|
|
8
|
+
export { createGetBookmarkTagsTool, createSaveBookmarkTool, createAddBookmarkTagTool, } from "./bookmark-tool.js";
|
package/dist/core/tools/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { createBrowserTool, closeBrowser } from "./browser-tool.js";
|
|
2
2
|
export { createSaveExperienceTool } from "./save-experience-tool.js";
|
|
3
|
+
export { createMemoryRecallTool } from "./memory-recall-tool.js";
|
|
3
4
|
export { createInstallSkillTool } from "./install-skill-tool.js";
|
|
4
5
|
export { createSwitchAgentTool } from "./switch-agent-tool.js";
|
|
5
6
|
export { createListAgentsTool } from "./list-agents-tool.js";
|
|
6
7
|
export { createCreateAgentTool } from "./create-agent-tool.js";
|
|
7
|
-
export { createGetBookmarkTagsTool, createSaveBookmarkTool } from "./bookmark-tool.js";
|
|
8
|
+
export { createGetBookmarkTagsTool, createSaveBookmarkTool, createAddBookmarkTagTool, } from "./bookmark-tool.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
/**
|
|
3
|
+
* 创建 memory_recall 工具:按需从向量库检索经验与对话摘要,供回答「过往工作、决定、日期、人、偏好、待办、复杂任务、定时任务、历史经验」等问题时使用。
|
|
4
|
+
* @param useLongMemory 当前智能体是否启用长记忆;为 false 时执行空转(不检索,直接返回未启用提示)。
|
|
5
|
+
*/
|
|
6
|
+
export declare function createMemoryRecallTool(useLongMemory: boolean): ToolDefinition;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { searchMemory } from "../memory/index.js";
|
|
3
|
+
const MemoryRecallSchema = Type.Object({
|
|
4
|
+
query: Type.String({
|
|
5
|
+
description: "语义检索查询,例如:过往决定、用户偏好、某日讨论、待办、历史经验、任务总结等",
|
|
6
|
+
}),
|
|
7
|
+
topK: Type.Optional(Type.Number({
|
|
8
|
+
description: "返回条数,默认 6",
|
|
9
|
+
minimum: 1,
|
|
10
|
+
maximum: 20,
|
|
11
|
+
})),
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* 创建 memory_recall 工具:按需从向量库检索经验与对话摘要,供回答「过往工作、决定、日期、人、偏好、待办、复杂任务、定时任务、历史经验」等问题时使用。
|
|
15
|
+
* @param useLongMemory 当前智能体是否启用长记忆;为 false 时执行空转(不检索,直接返回未启用提示)。
|
|
16
|
+
*/
|
|
17
|
+
export function createMemoryRecallTool(useLongMemory) {
|
|
18
|
+
return {
|
|
19
|
+
name: "memory_recall",
|
|
20
|
+
label: "Memory Recall",
|
|
21
|
+
description: "从长期记忆中语义检索与查询相关的经验总结和对话摘要。在回答与过往工作、决定、日期、人、偏好、待办、复杂任务、定时任务或需要历史经验相关的问题前,应先调用本工具获取相关记忆再作答。",
|
|
22
|
+
parameters: MemoryRecallSchema,
|
|
23
|
+
execute: async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
|
|
24
|
+
if (!useLongMemory) {
|
|
25
|
+
return {
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
type: "text",
|
|
29
|
+
text: "当前智能体未启用长记忆(经验),无法检索。",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
details: undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const query = (params.query ?? "").trim();
|
|
36
|
+
if (!query) {
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: "请提供检索查询(query)。" }],
|
|
39
|
+
details: undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const topK = Math.min(20, Math.max(1, params.topK ?? 6));
|
|
43
|
+
try {
|
|
44
|
+
const results = await searchMemory(query, topK);
|
|
45
|
+
if (results.length === 0) {
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: "未找到与查询相关的记忆。",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
details: { results: [], query, topK },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const lines = results.map((r, i) => `${i + 1}. [${r.metadata?.infotype ?? "memory"}]\n${(r.document ?? "").trim()}`);
|
|
57
|
+
const text = "以下为检索到的相关记忆:\n\n" + lines.join("\n\n");
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text }],
|
|
60
|
+
details: { results: results.length, query, topK },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: `记忆检索失败: ${msg}`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
details: undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 微信通道适配器:使用 Wechaty(Web/UOS 协议)接收消息,say() 发送回复。
|
|
3
|
+
* 不支持流式(微信无法编辑已发消息),回复在 agent_end 后一次性发送。
|
|
4
|
+
* 扫码:scan 事件产生二维码 URL,经 qrcode 库生成 base64 供前端展示。
|
|
5
|
+
*/
|
|
6
|
+
import type { IChannel } from "../types.js";
|
|
7
|
+
export interface WechatChannelConfig {
|
|
8
|
+
/** Puppet 名称,缺省使用 Wechaty 自带 puppet */
|
|
9
|
+
puppet?: string;
|
|
10
|
+
/** 默认绑定的 agentId */
|
|
11
|
+
defaultAgentId?: string;
|
|
12
|
+
}
|
|
13
|
+
type WechatLoginStatus = "scanning" | "logged_in" | "logged_out";
|
|
14
|
+
/** 供 Gateway API 查询当前二维码(base64 Data URL 或 null) */
|
|
15
|
+
export declare function getWechatQrCode(): string | null;
|
|
16
|
+
/** 供 Gateway API 查询登录状态 */
|
|
17
|
+
export declare function getWechatStatus(): {
|
|
18
|
+
status: WechatLoginStatus;
|
|
19
|
+
userName: string | null;
|
|
20
|
+
};
|
|
21
|
+
/** 重启 Wechaty 以刷新二维码(二维码过期后调用) */
|
|
22
|
+
export declare function refreshWechatQrCode(): Promise<void>;
|
|
23
|
+
export declare function createWechatChannel(config: WechatChannelConfig): IChannel;
|
|
24
|
+
export {};
|