@memtensor/memos-local-openclaw-plugin 1.0.4-beta.1 → 1.0.4-beta.10
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/.env.example +7 -0
- package/README.md +24 -24
- package/dist/capture/index.d.ts +1 -1
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +34 -2
- package/dist/capture/index.js.map +1 -1
- package/dist/client/connector.d.ts +1 -2
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +93 -26
- package/dist/client/connector.js.map +1 -1
- package/dist/client/hub.d.ts.map +1 -1
- package/dist/client/hub.js +22 -0
- package/dist/client/hub.js.map +1 -1
- package/dist/client/skill-sync.d.ts +7 -0
- package/dist/client/skill-sync.d.ts.map +1 -1
- package/dist/client/skill-sync.js +10 -0
- package/dist/client/skill-sync.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -2
- package/dist/config.js.map +1 -1
- package/dist/hub/server.d.ts +7 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +277 -87
- package/dist/hub/server.js.map +1 -1
- package/dist/hub/user-manager.d.ts +2 -0
- package/dist/hub/user-manager.d.ts.map +1 -1
- package/dist/hub/user-manager.js +5 -1
- package/dist/hub/user-manager.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +37 -6
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/recall/engine.d.ts.map +1 -1
- package/dist/recall/engine.js +91 -1
- package/dist/recall/engine.js.map +1 -1
- package/dist/shared/llm-call.d.ts +1 -0
- package/dist/shared/llm-call.d.ts.map +1 -1
- package/dist/shared/llm-call.js +82 -8
- package/dist/shared/llm-call.js.map +1 -1
- package/dist/sharing/types.d.ts +1 -1
- package/dist/sharing/types.d.ts.map +1 -1
- package/dist/skill/evolver.d.ts +2 -0
- package/dist/skill/evolver.d.ts.map +1 -1
- package/dist/skill/evolver.js +3 -0
- package/dist/skill/evolver.js.map +1 -1
- package/dist/storage/ensure-binding.d.ts +12 -0
- package/dist/storage/ensure-binding.d.ts.map +1 -0
- package/dist/storage/ensure-binding.js +53 -0
- package/dist/storage/ensure-binding.js.map +1 -0
- package/dist/storage/sqlite.d.ts +74 -20
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +286 -118
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/telemetry.d.ts +12 -5
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +156 -40
- package/dist/telemetry.js.map +1 -1
- package/dist/tools/memory-search.d.ts +3 -1
- package/dist/tools/memory-search.d.ts.map +1 -1
- package/dist/tools/memory-search.js +3 -1
- package/dist/tools/memory-search.js.map +1 -1
- package/dist/types.d.ts +1 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +2660 -889
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +30 -8
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +965 -193
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +384 -43
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -2
- package/scripts/postinstall.cjs +1 -1
- package/skill/memos-memory-guide/SKILL.md +64 -26
- package/src/capture/index.ts +37 -1
- package/src/client/connector.ts +91 -28
- package/src/client/hub.ts +18 -0
- package/src/client/skill-sync.ts +14 -0
- package/src/config.ts +0 -2
- package/src/hub/server.ts +259 -78
- package/src/hub/user-manager.ts +7 -3
- package/src/index.ts +10 -2
- package/src/ingest/providers/index.ts +41 -7
- package/src/recall/engine.ts +84 -1
- package/src/shared/llm-call.ts +97 -9
- package/src/sharing/types.ts +1 -1
- package/src/skill/evolver.ts +5 -0
- package/src/storage/ensure-binding.ts +52 -0
- package/src/storage/sqlite.ts +295 -144
- package/src/telemetry.ts +172 -41
- package/src/tools/memory-search.ts +2 -1
- package/src/types.ts +1 -2
- package/src/viewer/html.ts +2660 -889
- package/src/viewer/server.ts +888 -177
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import type { SummarizerConfig, Logger, OpenClawAPI } from "../../types";
|
|
3
|
+
import type { SummarizerConfig, SummaryProvider, Logger, OpenClawAPI } from "../../types";
|
|
4
4
|
import { summarizeOpenAI, summarizeTaskOpenAI, generateTaskTitleOpenAI, judgeNewTopicOpenAI, filterRelevantOpenAI, judgeDedupOpenAI, parseFilterResult, parseDedupResult } from "./openai";
|
|
5
5
|
import type { FilterResult, DedupResult } from "./openai";
|
|
6
6
|
export type { FilterResult, DedupResult } from "./openai";
|
|
@@ -8,6 +8,40 @@ import { summarizeAnthropic, summarizeTaskAnthropic, generateTaskTitleAnthropic,
|
|
|
8
8
|
import { summarizeGemini, summarizeTaskGemini, generateTaskTitleGemini, judgeNewTopicGemini, filterRelevantGemini, judgeDedupGemini } from "./gemini";
|
|
9
9
|
import { summarizeBedrock, summarizeTaskBedrock, generateTaskTitleBedrock, judgeNewTopicBedrock, filterRelevantBedrock, judgeDedupBedrock } from "./bedrock";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Detect provider type from provider key name or base URL.
|
|
13
|
+
*/
|
|
14
|
+
function detectProvider(
|
|
15
|
+
providerKey: string | undefined,
|
|
16
|
+
baseUrl: string,
|
|
17
|
+
): SummaryProvider {
|
|
18
|
+
const key = providerKey?.toLowerCase() ?? "";
|
|
19
|
+
const url = baseUrl.toLowerCase();
|
|
20
|
+
if (key.includes("anthropic") || url.includes("anthropic")) return "anthropic";
|
|
21
|
+
if (key.includes("gemini") || url.includes("generativelanguage.googleapis.com")) {
|
|
22
|
+
return "gemini";
|
|
23
|
+
}
|
|
24
|
+
if (key.includes("bedrock") || url.includes("bedrock")) return "bedrock";
|
|
25
|
+
return "openai_compatible";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return the correct endpoint for a given provider and base URL.
|
|
30
|
+
*/
|
|
31
|
+
function normalizeEndpointForProvider(
|
|
32
|
+
provider: SummaryProvider,
|
|
33
|
+
baseUrl: string,
|
|
34
|
+
): string {
|
|
35
|
+
const stripped = baseUrl.replace(/\/+$/, "");
|
|
36
|
+
if (provider === "anthropic") {
|
|
37
|
+
if (stripped.endsWith("/v1/messages")) return stripped;
|
|
38
|
+
return `${stripped}/v1/messages`;
|
|
39
|
+
}
|
|
40
|
+
if (stripped.endsWith("/chat/completions")) return stripped;
|
|
41
|
+
if (stripped.endsWith("/completions")) return stripped;
|
|
42
|
+
return `${stripped}/chat/completions`;
|
|
43
|
+
}
|
|
44
|
+
|
|
11
45
|
/**
|
|
12
46
|
* Build a SummarizerConfig from OpenClaw's native model configuration (openclaw.json).
|
|
13
47
|
* This serves as the final fallback when both strongCfg and plugin summarizer fail or are absent.
|
|
@@ -15,7 +49,8 @@ import { summarizeBedrock, summarizeTaskBedrock, generateTaskTitleBedrock, judge
|
|
|
15
49
|
function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | undefined {
|
|
16
50
|
try {
|
|
17
51
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
18
|
-
const
|
|
52
|
+
const ocHome = process.env.OPENCLAW_STATE_DIR || path.join(home, ".openclaw");
|
|
53
|
+
const cfgPath = path.join(ocHome, "openclaw.json");
|
|
19
54
|
if (!fs.existsSync(cfgPath)) return undefined;
|
|
20
55
|
|
|
21
56
|
const raw = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
|
|
@@ -36,13 +71,12 @@ function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | undefined {
|
|
|
36
71
|
const apiKey: string | undefined = providerCfg.apiKey;
|
|
37
72
|
if (!baseUrl || !apiKey) return undefined;
|
|
38
73
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
: baseUrl.replace(/\/+$/, "") + "/chat/completions";
|
|
74
|
+
const provider = detectProvider(providerKey, baseUrl);
|
|
75
|
+
const endpoint = normalizeEndpointForProvider(provider, baseUrl);
|
|
42
76
|
|
|
43
|
-
log.debug(`OpenClaw fallback model: ${modelId} via ${baseUrl}`);
|
|
77
|
+
log.debug(`OpenClaw fallback model: ${modelId} via ${baseUrl} (${provider})`);
|
|
44
78
|
return {
|
|
45
|
-
provider
|
|
79
|
+
provider,
|
|
46
80
|
endpoint,
|
|
47
81
|
apiKey,
|
|
48
82
|
model: modelId,
|
package/src/recall/engine.ts
CHANGED
|
@@ -74,10 +74,60 @@ export class RecallEngine {
|
|
|
74
74
|
score: 1 / (i + 1),
|
|
75
75
|
}));
|
|
76
76
|
|
|
77
|
+
// Step 1c: Hub memories search (when sharing is enabled and hub_memories exist)
|
|
78
|
+
let hubMemFtsRanked: Array<{ id: string; score: number }> = [];
|
|
79
|
+
let hubMemVecRanked: Array<{ id: string; score: number }> = [];
|
|
80
|
+
let hubMemPatternRanked: Array<{ id: string; score: number }> = [];
|
|
81
|
+
if (query && this.ctx.config.sharing?.enabled) {
|
|
82
|
+
try {
|
|
83
|
+
const hubFtsHits = this.store.searchHubMemories(query, { maxResults: candidatePool });
|
|
84
|
+
hubMemFtsRanked = hubFtsHits.map(({ hit }, i) => ({
|
|
85
|
+
id: `hubmem:${hit.id}`, score: 1 / (i + 1),
|
|
86
|
+
}));
|
|
87
|
+
} catch { /* hub_memories table may not exist */ }
|
|
88
|
+
if (shortTerms.length > 0) {
|
|
89
|
+
try {
|
|
90
|
+
const hubPatternHits = this.store.hubMemoryPatternSearch(shortTerms, { limit: candidatePool });
|
|
91
|
+
hubMemPatternRanked = hubPatternHits.map((h, i) => ({
|
|
92
|
+
id: `hubmem:${h.memoryId}`, score: 1 / (i + 1),
|
|
93
|
+
}));
|
|
94
|
+
} catch { /* best-effort */ }
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const hubMemEmbs = this.store.getVisibleHubMemoryEmbeddings("");
|
|
98
|
+
if (hubMemEmbs.length > 0) {
|
|
99
|
+
const qv = await this.embedder.embedQuery(query).catch(() => null);
|
|
100
|
+
if (qv) {
|
|
101
|
+
const scored: Array<{ id: string; score: number }> = [];
|
|
102
|
+
for (const e of hubMemEmbs) {
|
|
103
|
+
let dot = 0, nA = 0, nB = 0;
|
|
104
|
+
for (let i = 0; i < qv.length && i < e.vector.length; i++) {
|
|
105
|
+
dot += qv[i] * e.vector[i]; nA += qv[i] * qv[i]; nB += e.vector[i] * e.vector[i];
|
|
106
|
+
}
|
|
107
|
+
const sim = nA > 0 && nB > 0 ? dot / (Math.sqrt(nA) * Math.sqrt(nB)) : 0;
|
|
108
|
+
if (sim > 0.3) {
|
|
109
|
+
scored.push({ id: `hubmem:${e.memoryId}`, score: sim });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
scored.sort((a, b) => b.score - a.score);
|
|
113
|
+
hubMemVecRanked = scored.slice(0, candidatePool);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch { /* best-effort */ }
|
|
117
|
+
const hubTotal = hubMemFtsRanked.length + hubMemVecRanked.length + hubMemPatternRanked.length;
|
|
118
|
+
if (hubTotal > 0) {
|
|
119
|
+
this.ctx.log.debug(`recall: hub_memories candidates: fts=${hubMemFtsRanked.length}, vec=${hubMemVecRanked.length}, pattern=${hubMemPatternRanked.length}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
77
123
|
// Step 2: RRF fusion
|
|
78
124
|
const ftsRanked = ftsCandidates.map((c) => ({ id: c.chunkId, score: c.score }));
|
|
79
125
|
const vecRanked = vecCandidates.map((c) => ({ id: c.chunkId, score: c.score }));
|
|
80
|
-
const
|
|
126
|
+
const allRankedLists = [ftsRanked, vecRanked, patternRanked];
|
|
127
|
+
if (hubMemFtsRanked.length > 0) allRankedLists.push(hubMemFtsRanked);
|
|
128
|
+
if (hubMemVecRanked.length > 0) allRankedLists.push(hubMemVecRanked);
|
|
129
|
+
if (hubMemPatternRanked.length > 0) allRankedLists.push(hubMemPatternRanked);
|
|
130
|
+
const rrfScores = rrfFuse(allRankedLists, recallCfg.rrfK);
|
|
81
131
|
|
|
82
132
|
if (rrfScores.size === 0) {
|
|
83
133
|
this.recordQuery(query, maxResults, minScore, 0);
|
|
@@ -101,6 +151,11 @@ export class RecallEngine {
|
|
|
101
151
|
|
|
102
152
|
// Step 4: Time decay
|
|
103
153
|
const withTs = mmrResults.map((r) => {
|
|
154
|
+
if (r.id.startsWith("hubmem:")) {
|
|
155
|
+
const memId = r.id.slice(7);
|
|
156
|
+
const mem = this.store.getHubMemoryById(memId);
|
|
157
|
+
return { ...r, createdAt: mem?.createdAt ?? 0 };
|
|
158
|
+
}
|
|
104
159
|
const chunk = this.store.getChunk(r.id);
|
|
105
160
|
return { ...r, createdAt: chunk?.createdAt ?? 0 };
|
|
106
161
|
});
|
|
@@ -128,6 +183,34 @@ export class RecallEngine {
|
|
|
128
183
|
const hits: SearchHit[] = [];
|
|
129
184
|
for (const candidate of normalized) {
|
|
130
185
|
if (hits.length >= maxResults) break;
|
|
186
|
+
|
|
187
|
+
if (candidate.id.startsWith("hubmem:")) {
|
|
188
|
+
const memId = candidate.id.slice(7);
|
|
189
|
+
const mem = this.store.getHubMemoryById(memId);
|
|
190
|
+
if (!mem) continue;
|
|
191
|
+
if (roleFilter && mem.role !== roleFilter) continue;
|
|
192
|
+
hits.push({
|
|
193
|
+
summary: mem.summary || mem.content.slice(0, 200),
|
|
194
|
+
original_excerpt: mem.content,
|
|
195
|
+
ref: {
|
|
196
|
+
sessionKey: `hub-shared:${mem.sourceUserId}`,
|
|
197
|
+
chunkId: mem.id,
|
|
198
|
+
turnId: "",
|
|
199
|
+
seq: 0,
|
|
200
|
+
},
|
|
201
|
+
score: Math.round(candidate.score * 1000) / 1000,
|
|
202
|
+
taskId: null,
|
|
203
|
+
skillId: null,
|
|
204
|
+
owner: `hub-user:${mem.sourceUserId}`,
|
|
205
|
+
source: {
|
|
206
|
+
ts: mem.createdAt,
|
|
207
|
+
role: (mem.role || "assistant") as any,
|
|
208
|
+
sessionKey: `hub-shared:${mem.sourceUserId}`,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
131
214
|
const chunk = this.store.getChunk(candidate.id);
|
|
132
215
|
if (!chunk) continue;
|
|
133
216
|
if (roleFilter && chunk.role !== roleFilter) continue;
|
package/src/shared/llm-call.ts
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
-
import type { SummarizerConfig, Logger, PluginContext, OpenClawAPI } from "../types";
|
|
3
|
+
import type { SummarizerConfig, SummaryProvider, Logger, PluginContext, OpenClawAPI } from "../types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Detect provider type from provider key name or base URL.
|
|
7
|
+
*/
|
|
8
|
+
function detectProvider(providerKey: string | undefined, baseUrl: string): SummaryProvider {
|
|
9
|
+
const key = providerKey?.toLowerCase() ?? "";
|
|
10
|
+
const url = baseUrl.toLowerCase();
|
|
11
|
+
if (key.includes("anthropic") || url.includes("anthropic")) return "anthropic";
|
|
12
|
+
if (key.includes("gemini") || url.includes("generativelanguage.googleapis.com")) {
|
|
13
|
+
return "gemini";
|
|
14
|
+
}
|
|
15
|
+
if (key.includes("bedrock") || url.includes("bedrock")) return "bedrock";
|
|
16
|
+
return "openai_compatible";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Return the correct default endpoint for a given provider.
|
|
21
|
+
*/
|
|
22
|
+
function defaultEndpointForProvider(provider: SummaryProvider, baseUrl: string): string {
|
|
23
|
+
const stripped = baseUrl.replace(/\/+$/, "");
|
|
24
|
+
if (provider === "anthropic") {
|
|
25
|
+
if (stripped.endsWith("/v1/messages")) return stripped;
|
|
26
|
+
return `${stripped}/v1/messages`;
|
|
27
|
+
}
|
|
28
|
+
if (stripped.endsWith("/chat/completions")) return stripped;
|
|
29
|
+
if (stripped.endsWith("/completions")) return stripped;
|
|
30
|
+
return `${stripped}/chat/completions`;
|
|
31
|
+
}
|
|
4
32
|
|
|
5
33
|
/**
|
|
6
34
|
* Build a SummarizerConfig from OpenClaw's native model configuration (openclaw.json).
|
|
@@ -30,13 +58,12 @@ export function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | unde
|
|
|
30
58
|
const apiKey: string | undefined = providerCfg.apiKey;
|
|
31
59
|
if (!baseUrl || !apiKey) return undefined;
|
|
32
60
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
: baseUrl.replace(/\/+$/, "") + "/chat/completions";
|
|
61
|
+
const provider = detectProvider(providerKey, baseUrl);
|
|
62
|
+
const endpoint = defaultEndpointForProvider(provider, baseUrl);
|
|
36
63
|
|
|
37
|
-
log.debug(`OpenClaw fallback model: ${modelId} via ${baseUrl}`);
|
|
64
|
+
log.debug(`OpenClaw fallback model: ${modelId} via ${baseUrl} (${provider})`);
|
|
38
65
|
return {
|
|
39
|
-
provider
|
|
66
|
+
provider,
|
|
40
67
|
endpoint,
|
|
41
68
|
apiKey,
|
|
42
69
|
model: modelId,
|
|
@@ -70,23 +97,34 @@ export interface LLMCallOptions {
|
|
|
70
97
|
openclawAPI?: OpenClawAPI;
|
|
71
98
|
}
|
|
72
99
|
|
|
73
|
-
function
|
|
100
|
+
function normalizeOpenAIEndpoint(url: string): string {
|
|
74
101
|
const stripped = url.replace(/\/+$/, "");
|
|
75
102
|
if (stripped.endsWith("/chat/completions")) return stripped;
|
|
76
103
|
if (stripped.endsWith("/completions")) return stripped;
|
|
77
104
|
return `${stripped}/chat/completions`;
|
|
78
105
|
}
|
|
79
106
|
|
|
107
|
+
function normalizeAnthropicEndpoint(url: string): string {
|
|
108
|
+
const stripped = url.replace(/\/+$/, "");
|
|
109
|
+
if (stripped.endsWith("/v1/messages")) return stripped;
|
|
110
|
+
if (stripped.endsWith("/messages")) return stripped;
|
|
111
|
+
return `${stripped}/v1/messages`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isAnthropicProvider(cfg: SummarizerConfig): boolean {
|
|
115
|
+
return cfg.provider === "anthropic";
|
|
116
|
+
}
|
|
117
|
+
|
|
80
118
|
/**
|
|
81
119
|
* Make a single LLM call with the given config. Throws on failure.
|
|
82
120
|
* When cfg.provider === "openclaw", delegates to the OpenClaw host completion API.
|
|
121
|
+
* Dispatches to Anthropic or OpenAI-compatible format based on provider.
|
|
83
122
|
*/
|
|
84
123
|
export async function callLLMOnce(
|
|
85
124
|
cfg: SummarizerConfig,
|
|
86
125
|
prompt: string,
|
|
87
126
|
opts: LLMCallOptions = {},
|
|
88
127
|
): Promise<string> {
|
|
89
|
-
// Handle openclaw provider via host completion API
|
|
90
128
|
if (cfg.provider === "openclaw") {
|
|
91
129
|
const api = opts.openclawAPI;
|
|
92
130
|
if (!api) {
|
|
@@ -101,7 +139,57 @@ export async function callLLMOnce(
|
|
|
101
139
|
return response.text.trim();
|
|
102
140
|
}
|
|
103
141
|
|
|
104
|
-
|
|
142
|
+
if (isAnthropicProvider(cfg)) {
|
|
143
|
+
return callLLMOnceAnthropic(cfg, prompt, opts);
|
|
144
|
+
}
|
|
145
|
+
return callLLMOnceOpenAI(cfg, prompt, opts);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function callLLMOnceAnthropic(
|
|
149
|
+
cfg: SummarizerConfig,
|
|
150
|
+
prompt: string,
|
|
151
|
+
opts: LLMCallOptions = {},
|
|
152
|
+
): Promise<string> {
|
|
153
|
+
const endpoint = normalizeAnthropicEndpoint(
|
|
154
|
+
cfg.endpoint ?? "https://api.anthropic.com/v1/messages",
|
|
155
|
+
);
|
|
156
|
+
const model = cfg.model ?? "claude-3-haiku-20240307";
|
|
157
|
+
const headers: Record<string, string> = {
|
|
158
|
+
"Content-Type": "application/json",
|
|
159
|
+
"x-api-key": cfg.apiKey ?? "",
|
|
160
|
+
"anthropic-version": "2023-06-01",
|
|
161
|
+
...cfg.headers,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const resp = await fetch(endpoint, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers,
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
model,
|
|
169
|
+
temperature: opts.temperature ?? 0.1,
|
|
170
|
+
max_tokens: opts.maxTokens ?? 1024,
|
|
171
|
+
messages: [{ role: "user", content: prompt }],
|
|
172
|
+
}),
|
|
173
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 30_000),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (!resp.ok) {
|
|
177
|
+
const body = await resp.text();
|
|
178
|
+
throw new Error(`LLM call failed (${resp.status}): ${body}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const json = (await resp.json()) as { content: Array<{ type: string; text: string }> };
|
|
182
|
+
return json.content.find((c) => c.type === "text")?.text?.trim() ?? "";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function callLLMOnceOpenAI(
|
|
186
|
+
cfg: SummarizerConfig,
|
|
187
|
+
prompt: string,
|
|
188
|
+
opts: LLMCallOptions = {},
|
|
189
|
+
): Promise<string> {
|
|
190
|
+
const endpoint = normalizeOpenAIEndpoint(
|
|
191
|
+
cfg.endpoint ?? "https://api.openai.com/v1/chat/completions",
|
|
192
|
+
);
|
|
105
193
|
const model = cfg.model ?? "gpt-4o-mini";
|
|
106
194
|
const headers: Record<string, string> = {
|
|
107
195
|
"Content-Type": "application/json",
|
package/src/sharing/types.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
export type HubScope = "local" | "group" | "all";
|
|
13
13
|
export type SharedVisibility = "group" | "public";
|
|
14
14
|
export type UserRole = "admin" | "member";
|
|
15
|
-
export type UserStatus = "pending" | "active" | "blocked" | "rejected";
|
|
15
|
+
export type UserStatus = "pending" | "active" | "blocked" | "rejected" | "removed";
|
|
16
16
|
|
|
17
17
|
export type { ClientModeConfig, HubModeConfig, SharingCapabilities, SharingConfig, SharingRole };
|
|
18
18
|
|
package/src/skill/evolver.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { SkillUpgrader } from "./upgrader";
|
|
|
12
12
|
import { SkillInstaller } from "./installer";
|
|
13
13
|
import { buildSkillConfigChain, callLLMWithFallback } from "../shared/llm-call";
|
|
14
14
|
|
|
15
|
+
export type SkillEvolvedCallback = (skillName: string, upgradeType: "created" | "upgraded") => void;
|
|
16
|
+
|
|
15
17
|
export class SkillEvolver {
|
|
16
18
|
private evaluator: SkillEvaluator;
|
|
17
19
|
private generator: SkillGenerator;
|
|
@@ -19,6 +21,7 @@ export class SkillEvolver {
|
|
|
19
21
|
private installer: SkillInstaller;
|
|
20
22
|
private processing = false;
|
|
21
23
|
private queue: Task[] = [];
|
|
24
|
+
onSkillEvolved: SkillEvolvedCallback | null = null;
|
|
22
25
|
|
|
23
26
|
constructor(
|
|
24
27
|
private store: SqliteStore,
|
|
@@ -279,6 +282,7 @@ Use selectedIndex 0 when none is highly relevant.`;
|
|
|
279
282
|
if (upgraded) {
|
|
280
283
|
this.store.linkTaskSkill(task.id, freshSkill.id, "evolved_from", freshSkill.version + 1);
|
|
281
284
|
this.installer.syncIfInstalled(freshSkill.name);
|
|
285
|
+
this.onSkillEvolved?.(freshSkill.name, "upgraded");
|
|
282
286
|
} else {
|
|
283
287
|
this.store.linkTaskSkill(task.id, freshSkill.id, "applied_to", freshSkill.version);
|
|
284
288
|
}
|
|
@@ -307,6 +311,7 @@ Use selectedIndex 0 when none is highly relevant.`;
|
|
|
307
311
|
this.markChunksWithSkill(chunks, skill.id);
|
|
308
312
|
this.store.linkTaskSkill(task.id, skill.id, "generated_from", 1);
|
|
309
313
|
this.store.setTaskSkillMeta(task.id, { skillStatus: "generated", skillReason: evalResult.reason });
|
|
314
|
+
this.onSkillEvolved?.(skill.name, "created");
|
|
310
315
|
|
|
311
316
|
const autoInstall = this.ctx.config.skillEvolution?.autoInstall ?? DEFAULTS.skillAutoInstall;
|
|
312
317
|
if (autoInstall && skill.status === "active") {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync } from "fs";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ensure the better-sqlite3 native binary is available.
|
|
8
|
+
*
|
|
9
|
+
* OpenClaw installs plugins with `--ignore-scripts`, which skips
|
|
10
|
+
* the native compilation step. This function checks for the binary
|
|
11
|
+
* and restores it from bundled prebuilds if missing.
|
|
12
|
+
*/
|
|
13
|
+
export function ensureSqliteBinding(log?: { info: (msg: string) => void; warn: (msg: string) => void }): void {
|
|
14
|
+
const _req = typeof require !== "undefined" ? require : createRequire(__filename);
|
|
15
|
+
const bsqlPkg = _req.resolve("better-sqlite3/package.json");
|
|
16
|
+
const bsqlDir = path.dirname(bsqlPkg);
|
|
17
|
+
const bindingPath = path.join(bsqlDir, "build", "Release", "better_sqlite3.node");
|
|
18
|
+
|
|
19
|
+
if (existsSync(bindingPath)) return;
|
|
20
|
+
|
|
21
|
+
const platform = `${process.platform}-${process.arch}`;
|
|
22
|
+
const pluginRoot = path.resolve(__dirname, "..", "..");
|
|
23
|
+
const prebuildSrc = path.join(pluginRoot, "prebuilds", platform, "better_sqlite3.node");
|
|
24
|
+
|
|
25
|
+
if (existsSync(prebuildSrc)) {
|
|
26
|
+
log?.info(`[ensure-binding] Copying prebuild for ${platform}...`);
|
|
27
|
+
mkdirSync(path.dirname(bindingPath), { recursive: true });
|
|
28
|
+
copyFileSync(prebuildSrc, bindingPath);
|
|
29
|
+
log?.info(`[ensure-binding] Prebuild installed successfully.`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
log?.warn(`[ensure-binding] No prebuild for ${platform}, attempting npm rebuild...`);
|
|
34
|
+
try {
|
|
35
|
+
const installDir = path.resolve(bsqlDir, "..", "..");
|
|
36
|
+
execSync("npm rebuild better-sqlite3", {
|
|
37
|
+
cwd: installDir,
|
|
38
|
+
stdio: "pipe",
|
|
39
|
+
timeout: 180_000,
|
|
40
|
+
});
|
|
41
|
+
if (existsSync(bindingPath)) {
|
|
42
|
+
log?.info(`[ensure-binding] Rebuilt better-sqlite3 successfully.`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
} catch { /* fall through */ }
|
|
46
|
+
|
|
47
|
+
throw new Error(
|
|
48
|
+
`better-sqlite3 native binary not found for ${platform}.\n` +
|
|
49
|
+
`Prebuild not bundled and npm rebuild failed.\n` +
|
|
50
|
+
`Fix: cd ${path.resolve(bsqlDir, "..", "..")} && npm rebuild better-sqlite3`,
|
|
51
|
+
);
|
|
52
|
+
}
|