@memtensor/memos-local-openclaw-plugin 1.0.6 → 1.0.7-beta.3
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 +4 -0
- package/index.ts +28 -29
- package/package.json +1 -1
- package/scripts/postinstall.cjs +18 -9
- package/skill/memos-memory-guide/SKILL.md +5 -2
- package/src/ingest/providers/index.ts +14 -1
- package/src/recall/engine.ts +1 -1
- package/src/shared/llm-call.ts +14 -1
- package/src/storage/sqlite.ts +10 -2
- package/src/viewer/html.ts +31 -27
- package/src/viewer/server.ts +14 -2
- package/prebuilds/darwin-arm64/better_sqlite3.node +0 -0
- package/prebuilds/darwin-x64/better_sqlite3.node +0 -0
- package/prebuilds/linux-x64/better_sqlite3.node +0 -0
- package/prebuilds/win32-x64/better_sqlite3.node +0 -0
- package/telemetry.credentials.json +0 -5
package/.env.example
CHANGED
|
@@ -18,6 +18,10 @@ SUMMARIZER_TEMPERATURE=0
|
|
|
18
18
|
# Port for the web-based Memory Viewer (default: 18799)
|
|
19
19
|
# VIEWER_PORT=18799
|
|
20
20
|
|
|
21
|
+
# ─── Tavily Search (optional) ───
|
|
22
|
+
# API key for Tavily web search (get from https://app.tavily.com)
|
|
23
|
+
# TAVILY_API_KEY=tvly-your-tavily-api-key
|
|
24
|
+
|
|
21
25
|
# ─── Telemetry (opt-out) ───
|
|
22
26
|
# Anonymous usage analytics to help improve the plugin.
|
|
23
27
|
# No memory content, queries, or personal data is ever sent — only tool names, latencies, and version info.
|
package/index.ts
CHANGED
|
@@ -307,6 +307,7 @@ const memosLocalPlugin = {
|
|
|
307
307
|
// Current agent ID — updated by hooks, read by tools for owner isolation.
|
|
308
308
|
// Falls back to "main" when no hook has fired yet (single-agent setups).
|
|
309
309
|
let currentAgentId = "main";
|
|
310
|
+
const getCurrentOwner = () => `agent:${currentAgentId}`;
|
|
310
311
|
|
|
311
312
|
// ─── Check allowPromptInjection policy ───
|
|
312
313
|
// When allowPromptInjection=false, the prompt mutation fields (such as prependContext) in the hook return value
|
|
@@ -354,7 +355,6 @@ const memosLocalPlugin = {
|
|
|
354
355
|
}
|
|
355
356
|
};
|
|
356
357
|
|
|
357
|
-
const getCurrentOwner = () => `agent:${currentAgentId}`;
|
|
358
358
|
const resolveMemorySearchScope = (scope?: string): "local" | "group" | "all" =>
|
|
359
359
|
scope === "group" || scope === "all" ? scope : "local";
|
|
360
360
|
const resolveMemoryShareTarget = (target?: string): "agents" | "hub" | "both" =>
|
|
@@ -445,7 +445,7 @@ const memosLocalPlugin = {
|
|
|
445
445
|
// ─── Tool: memory_search ───
|
|
446
446
|
|
|
447
447
|
api.registerTool(
|
|
448
|
-
{
|
|
448
|
+
(context) => ({
|
|
449
449
|
name: "memory_search",
|
|
450
450
|
label: "Memory Search",
|
|
451
451
|
description:
|
|
@@ -461,7 +461,7 @@ const memosLocalPlugin = {
|
|
|
461
461
|
hubAddress: Type.Optional(Type.String({ description: "Optional Hub address override for group/all search." })),
|
|
462
462
|
userToken: Type.Optional(Type.String({ description: "Optional Hub bearer token override for group/all search." })),
|
|
463
463
|
}),
|
|
464
|
-
execute: trackTool("memory_search", async (_toolCallId: any, params: any
|
|
464
|
+
execute: trackTool("memory_search", async (_toolCallId: any, params: any) => {
|
|
465
465
|
const {
|
|
466
466
|
query,
|
|
467
467
|
scope: rawScope,
|
|
@@ -482,9 +482,6 @@ const memosLocalPlugin = {
|
|
|
482
482
|
const role = rawRole === "user" || rawRole === "assistant" || rawRole === "tool" || rawRole === "system" ? rawRole : undefined;
|
|
483
483
|
const minScore = typeof rawMinScore === "number" ? Math.max(0.35, Math.min(1, rawMinScore)) : undefined;
|
|
484
484
|
let searchScope = resolveMemorySearchScope(rawScope);
|
|
485
|
-
if (searchScope === "local" && ctx.config?.sharing?.enabled) {
|
|
486
|
-
searchScope = "all";
|
|
487
|
-
}
|
|
488
485
|
const searchLimit = typeof maxResults === "number" ? Math.max(1, Math.min(20, Math.round(maxResults))) : 10;
|
|
489
486
|
|
|
490
487
|
const agentId = context?.agentId ?? currentAgentId;
|
|
@@ -504,7 +501,7 @@ const memosLocalPlugin = {
|
|
|
504
501
|
|
|
505
502
|
// Split local results: pure-local vs hub-memory (Hub role's hub_memories mixed in by RecallEngine)
|
|
506
503
|
const localHits = result.hits.filter((h) => h.origin !== "hub-memory");
|
|
507
|
-
const hubLocalHits = result.hits.filter((h) => h.origin === "hub-memory");
|
|
504
|
+
const hubLocalHits = searchScope !== "local" ? result.hits.filter((h) => h.origin === "hub-memory") : [];
|
|
508
505
|
|
|
509
506
|
const rawLocalCandidates = localHits.map((h) => ({
|
|
510
507
|
chunkId: h.ref.chunkId,
|
|
@@ -669,14 +666,14 @@ const memosLocalPlugin = {
|
|
|
669
666
|
},
|
|
670
667
|
};
|
|
671
668
|
}),
|
|
672
|
-
},
|
|
669
|
+
}),
|
|
673
670
|
{ name: "memory_search" },
|
|
674
671
|
);
|
|
675
672
|
|
|
676
673
|
// ─── Tool: memory_timeline ───
|
|
677
674
|
|
|
678
675
|
api.registerTool(
|
|
679
|
-
{
|
|
676
|
+
(context) => ({
|
|
680
677
|
name: "memory_timeline",
|
|
681
678
|
label: "Memory Timeline",
|
|
682
679
|
description:
|
|
@@ -686,7 +683,7 @@ const memosLocalPlugin = {
|
|
|
686
683
|
chunkId: Type.String({ description: "The chunkId from a memory_search hit" }),
|
|
687
684
|
window: Type.Optional(Type.Number({ description: "Context window ±N (default 2)" })),
|
|
688
685
|
}),
|
|
689
|
-
execute: trackTool("memory_timeline", async (_toolCallId: any, params: any
|
|
686
|
+
execute: trackTool("memory_timeline", async (_toolCallId: any, params: any) => {
|
|
690
687
|
const agentId = context?.agentId ?? currentAgentId;
|
|
691
688
|
ctx.log.debug(`memory_timeline called (agent=${agentId})`);
|
|
692
689
|
const { chunkId, window: win } = params as {
|
|
@@ -730,14 +727,14 @@ const memosLocalPlugin = {
|
|
|
730
727
|
details: { entries, anchorRef: { sessionKey: anchorChunk.sessionKey, chunkId, turnId: anchorChunk.turnId, seq: anchorChunk.seq } },
|
|
731
728
|
};
|
|
732
729
|
}),
|
|
733
|
-
},
|
|
730
|
+
}),
|
|
734
731
|
{ name: "memory_timeline" },
|
|
735
732
|
);
|
|
736
733
|
|
|
737
734
|
// ─── Tool: memory_get ───
|
|
738
735
|
|
|
739
736
|
api.registerTool(
|
|
740
|
-
{
|
|
737
|
+
(context) => ({
|
|
741
738
|
name: "memory_get",
|
|
742
739
|
label: "Memory Get",
|
|
743
740
|
description:
|
|
@@ -748,7 +745,7 @@ const memosLocalPlugin = {
|
|
|
748
745
|
Type.Number({ description: `Max chars (default ${DEFAULTS.getMaxCharsDefault}, max ${DEFAULTS.getMaxCharsMax})` }),
|
|
749
746
|
),
|
|
750
747
|
}),
|
|
751
|
-
execute: trackTool("memory_get", async (_toolCallId: any, params: any
|
|
748
|
+
execute: trackTool("memory_get", async (_toolCallId: any, params: any) => {
|
|
752
749
|
const { chunkId, maxChars } = params as { chunkId: string; maxChars?: number };
|
|
753
750
|
const limit = Math.min(maxChars ?? DEFAULTS.getMaxCharsDefault, DEFAULTS.getMaxCharsMax);
|
|
754
751
|
|
|
@@ -774,7 +771,7 @@ const memosLocalPlugin = {
|
|
|
774
771
|
},
|
|
775
772
|
};
|
|
776
773
|
}),
|
|
777
|
-
},
|
|
774
|
+
}),
|
|
778
775
|
{ name: "memory_get" },
|
|
779
776
|
);
|
|
780
777
|
|
|
@@ -1296,7 +1293,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1296
1293
|
const viewerPort = (pluginCfg as any).viewerPort ?? (gatewayPort + 10);
|
|
1297
1294
|
|
|
1298
1295
|
api.registerTool(
|
|
1299
|
-
{
|
|
1296
|
+
(context) => ({
|
|
1300
1297
|
name: "memory_viewer",
|
|
1301
1298
|
label: "Open Memory Viewer",
|
|
1302
1299
|
description:
|
|
@@ -1304,10 +1301,11 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1304
1301
|
"or access their stored memories, or asks where the memory dashboard is. " +
|
|
1305
1302
|
"Returns the URL the user can open in their browser.",
|
|
1306
1303
|
parameters: Type.Object({}),
|
|
1307
|
-
execute: trackTool("memory_viewer", async () => {
|
|
1304
|
+
execute: trackTool("memory_viewer", async (_toolCallId: any, params: any) => {
|
|
1308
1305
|
ctx.log.debug(`memory_viewer called`);
|
|
1309
1306
|
telemetry.trackViewerOpened();
|
|
1310
|
-
const
|
|
1307
|
+
const agentId = context?.agentId ?? context?.profileId ?? currentAgentId;
|
|
1308
|
+
const url = `http://127.0.0.1:${viewerPort}?agentId=${encodeURIComponent(agentId)}`;
|
|
1311
1309
|
return {
|
|
1312
1310
|
content: [
|
|
1313
1311
|
{
|
|
@@ -1328,7 +1326,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1328
1326
|
details: { viewerUrl: url },
|
|
1329
1327
|
};
|
|
1330
1328
|
}),
|
|
1331
|
-
},
|
|
1329
|
+
}),
|
|
1332
1330
|
{ name: "memory_viewer" },
|
|
1333
1331
|
);
|
|
1334
1332
|
|
|
@@ -1401,9 +1399,11 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1401
1399
|
name: "memory_share",
|
|
1402
1400
|
label: "Share Memory",
|
|
1403
1401
|
description:
|
|
1404
|
-
"Share an existing memory
|
|
1405
|
-
"
|
|
1406
|
-
"
|
|
1402
|
+
"Share an existing stored memory (requires a real chunkId from the database) to the Hub team, or to both targets. " +
|
|
1403
|
+
"If you want to share content from the conversation, please first retrieve the memories related to that content to obtain the correct chunkId(s), then proceed with the sharing. " +
|
|
1404
|
+
"target='agents' (default): when retrieved memories would clearly help other agents in the same OpenClaw workspace, you may share proactively without asking the user. " +
|
|
1405
|
+
"target='hub' or 'both': do not share to the team Hub without explicit user consent when the content would benefit collaborators—explain briefly, ask first, and only call hub/both after they agree (Hub must be configured). " +
|
|
1406
|
+
"To create a brand-new shared note with no existing chunk, use memory_write_public.",
|
|
1407
1407
|
parameters: Type.Object({
|
|
1408
1408
|
chunkId: Type.String({ description: "Existing local memory chunk ID to share" }),
|
|
1409
1409
|
target: Type.Optional(Type.String({ description: "Share target: 'agents' (default), 'hub', or 'both'" })),
|
|
@@ -1557,7 +1557,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1557
1557
|
// ─── Tool: skill_search ───
|
|
1558
1558
|
|
|
1559
1559
|
api.registerTool(
|
|
1560
|
-
{
|
|
1560
|
+
(context) => ({
|
|
1561
1561
|
name: "skill_search",
|
|
1562
1562
|
label: "Skill Search",
|
|
1563
1563
|
description:
|
|
@@ -1567,10 +1567,11 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1567
1567
|
query: Type.String({ description: "Natural language description of the needed skill" }),
|
|
1568
1568
|
scope: Type.Optional(Type.String({ description: "Search scope: 'mix' (default), 'self', 'public', 'group', or 'all'." })),
|
|
1569
1569
|
}),
|
|
1570
|
-
execute: trackTool("skill_search", async (_toolCallId: any, params: any
|
|
1570
|
+
execute: trackTool("skill_search", async (_toolCallId: any, params: any) => {
|
|
1571
1571
|
const { query: skillQuery, scope: rawScope } = params as { query: string; scope?: string };
|
|
1572
1572
|
const scope = (rawScope === "self" || rawScope === "public") ? rawScope : "mix";
|
|
1573
|
-
const
|
|
1573
|
+
const agentId = context?.agentId ?? currentAgentId;
|
|
1574
|
+
const currentOwner = `agent:${agentId}`;
|
|
1574
1575
|
|
|
1575
1576
|
if (rawScope === "group" || rawScope === "all") {
|
|
1576
1577
|
const [localHits, hub] = await Promise.all([
|
|
@@ -1632,7 +1633,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1632
1633
|
details: { query: skillQuery, scope, hits },
|
|
1633
1634
|
};
|
|
1634
1635
|
}),
|
|
1635
|
-
},
|
|
1636
|
+
}),
|
|
1636
1637
|
{ name: "skill_search" },
|
|
1637
1638
|
);
|
|
1638
1639
|
|
|
@@ -1777,7 +1778,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1777
1778
|
if (!allowPromptInjection) return {};
|
|
1778
1779
|
if (!event.prompt || event.prompt.length < 3) return;
|
|
1779
1780
|
|
|
1780
|
-
const recallAgentId = hookCtx?.agentId ?? "main";
|
|
1781
|
+
const recallAgentId = hookCtx?.agentId ?? (event as any)?.agentId ?? (event as any)?.profileId ?? "main";
|
|
1781
1782
|
currentAgentId = recallAgentId;
|
|
1782
1783
|
const recallOwnerFilter = [`agent:${recallAgentId}`, "public"];
|
|
1783
1784
|
ctx.log.info(`auto-recall: agentId=${recallAgentId} (from hookCtx)`);
|
|
@@ -1988,7 +1989,6 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1988
1989
|
try {
|
|
1989
1990
|
const skillCandidateMap = new Map<string, { name: string; description: string; skillId: string; source: string }>();
|
|
1990
1991
|
|
|
1991
|
-
// Source 1: direct skill search based on user query
|
|
1992
1992
|
try {
|
|
1993
1993
|
const directSkillHits = await engine.searchSkills(query, "mix" as any, getCurrentOwner());
|
|
1994
1994
|
for (const sh of directSkillHits.slice(0, skillLimit + 2)) {
|
|
@@ -2000,7 +2000,6 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
2000
2000
|
ctx.log.debug(`auto-recall-skill: direct search failed: ${err}`);
|
|
2001
2001
|
}
|
|
2002
2002
|
|
|
2003
|
-
// Source 2: skills linked to tasks from memory hits
|
|
2004
2003
|
const taskIds = new Set<string>();
|
|
2005
2004
|
for (const h of filteredHits) {
|
|
2006
2005
|
if (h.taskId) {
|
|
@@ -2090,7 +2089,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
2090
2089
|
if (!event.success || !event.messages || event.messages.length === 0) return;
|
|
2091
2090
|
|
|
2092
2091
|
try {
|
|
2093
|
-
const captureAgentId = hookCtx?.agentId ?? "main";
|
|
2092
|
+
const captureAgentId = hookCtx?.agentId ?? event?.agentId ?? event?.profileId ?? "main";
|
|
2094
2093
|
currentAgentId = captureAgentId;
|
|
2095
2094
|
const captureOwner = `agent:${captureAgentId}`;
|
|
2096
2095
|
const sessionKey = hookCtx?.sessionKey ?? "default";
|
package/package.json
CHANGED
package/scripts/postinstall.cjs
CHANGED
|
@@ -61,21 +61,30 @@ function cleanStaleArtifacts() {
|
|
|
61
61
|
installedVer = pkg.version || "unknown";
|
|
62
62
|
} catch { /* ignore */ }
|
|
63
63
|
|
|
64
|
+
const nodeMajor = process.versions.node.split(".")[0];
|
|
65
|
+
const currentFingerprint = `${installedVer}+node${nodeMajor}`;
|
|
66
|
+
|
|
64
67
|
const markerPath = path.join(pluginDir, ".installed-version");
|
|
65
|
-
let
|
|
66
|
-
try {
|
|
68
|
+
let prevFingerprint = "";
|
|
69
|
+
try { prevFingerprint = fs.readFileSync(markerPath, "utf-8").trim(); } catch { /* first install */ }
|
|
70
|
+
|
|
71
|
+
const writeMarker = () => {
|
|
72
|
+
try { fs.writeFileSync(markerPath, currentFingerprint + "\n", "utf-8"); } catch { /* ignore */ }
|
|
73
|
+
};
|
|
67
74
|
|
|
68
|
-
if (
|
|
69
|
-
log(`Version unchanged (${
|
|
75
|
+
if (prevFingerprint === currentFingerprint) {
|
|
76
|
+
log(`Version unchanged (${currentFingerprint}), skipping artifact cleanup.`);
|
|
70
77
|
return;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
if (
|
|
74
|
-
log(`
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
if (!prevFingerprint) {
|
|
81
|
+
log(`Fresh install: ${GREEN}${currentFingerprint}${RESET}`);
|
|
82
|
+
writeMarker();
|
|
83
|
+
return;
|
|
77
84
|
}
|
|
78
85
|
|
|
86
|
+
log(`Environment changed: ${DIM}${prevFingerprint}${RESET} → ${GREEN}${currentFingerprint}${RESET}`);
|
|
87
|
+
|
|
79
88
|
const dirsToClean = ["dist", "node_modules"];
|
|
80
89
|
let cleaned = 0;
|
|
81
90
|
for (const dir of dirsToClean) {
|
|
@@ -99,7 +108,7 @@ function cleanStaleArtifacts() {
|
|
|
99
108
|
}
|
|
100
109
|
}
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
writeMarker();
|
|
103
112
|
|
|
104
113
|
if (cleaned > 0) {
|
|
105
114
|
ok(`Cleaned ${cleaned} stale artifact(s). Fresh install will follow.`);
|
|
@@ -73,8 +73,11 @@ Two sharing planes exist and must not be confused:
|
|
|
73
73
|
### memory_share
|
|
74
74
|
|
|
75
75
|
- **What it does:** Share an existing memory either with local OpenClaw agents, to the team, or to both.
|
|
76
|
-
- **When to call:**
|
|
77
|
-
-
|
|
76
|
+
- **When to call:**
|
|
77
|
+
- If you want to share conversation content to team or hub, first retrieve memories related to that content to obtain the right `chunkId`(s), then share.
|
|
78
|
+
- `target='agents'` (default): When those memories would clearly help other agents in the same workspace, you may share proactively without asking the user.
|
|
79
|
+
- `target='hub'` or `'both'`: Only after explicit user consent when the content would benefit collaborators—explain briefly, ask first, then call `hub`/`both` (Hub must be configured). Never silently Hub-share.
|
|
80
|
+
- **Do not use when:** You are creating a brand-new shared note with **no** existing chunk—use `memory_write_public` instead.
|
|
78
81
|
- **Parameters:**
|
|
79
82
|
- `chunkId` (string, **required**) — Existing memory chunk ID.
|
|
80
83
|
- `target` (string, optional) — `'agents'` (default), `'hub'`, or `'both'`.
|
|
@@ -8,6 +8,19 @@ 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
|
+
* Resolve a SecretInput (string | SecretRef) to a plain string.
|
|
13
|
+
* Supports env-sourced SecretRef from OpenClaw's credential system.
|
|
14
|
+
*/
|
|
15
|
+
function resolveApiKey(
|
|
16
|
+
input: string | { source: string; provider?: string; id: string } | undefined,
|
|
17
|
+
): string | undefined {
|
|
18
|
+
if (!input) return undefined;
|
|
19
|
+
if (typeof input === "string") return input;
|
|
20
|
+
if (input.source === "env") return process.env[input.id];
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
/**
|
|
12
25
|
* Detect provider type from provider key name or base URL.
|
|
13
26
|
*/
|
|
@@ -68,7 +81,7 @@ function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | undefined {
|
|
|
68
81
|
if (!providerCfg) return undefined;
|
|
69
82
|
|
|
70
83
|
const baseUrl: string | undefined = providerCfg.baseUrl;
|
|
71
|
-
const apiKey
|
|
84
|
+
const apiKey = resolveApiKey(providerCfg.apiKey);
|
|
72
85
|
if (!baseUrl || !apiKey) return undefined;
|
|
73
86
|
|
|
74
87
|
const provider = detectProvider(providerKey, baseUrl);
|
package/src/recall/engine.ts
CHANGED
|
@@ -77,7 +77,7 @@ export class RecallEngine {
|
|
|
77
77
|
}
|
|
78
78
|
const shortTerms = [...new Set([...spaceSplit, ...cjkBigrams])];
|
|
79
79
|
const patternHits = shortTerms.length > 0
|
|
80
|
-
? this.store.patternSearch(shortTerms, { limit: candidatePool })
|
|
80
|
+
? this.store.patternSearch(shortTerms, { limit: candidatePool, ownerFilter })
|
|
81
81
|
: [];
|
|
82
82
|
const patternRanked = patternHits.map((h, i) => ({
|
|
83
83
|
id: h.chunkId,
|
package/src/shared/llm-call.ts
CHANGED
|
@@ -2,6 +2,19 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import type { SummarizerConfig, SummaryProvider, Logger, PluginContext, OpenClawAPI } from "../types";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a SecretInput (string | SecretRef) to a plain string.
|
|
7
|
+
* Supports env-sourced SecretRef from OpenClaw's credential system.
|
|
8
|
+
*/
|
|
9
|
+
function resolveApiKey(
|
|
10
|
+
input: string | { source: string; provider?: string; id: string } | undefined,
|
|
11
|
+
): string | undefined {
|
|
12
|
+
if (!input) return undefined;
|
|
13
|
+
if (typeof input === "string") return input;
|
|
14
|
+
if (input.source === "env") return process.env[input.id];
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
/**
|
|
6
19
|
* Detect provider type from provider key name or base URL.
|
|
7
20
|
*/
|
|
@@ -56,7 +69,7 @@ export function loadOpenClawFallbackConfig(log: Logger): SummarizerConfig | unde
|
|
|
56
69
|
if (!providerCfg) return undefined;
|
|
57
70
|
|
|
58
71
|
const baseUrl: string | undefined = providerCfg.baseUrl;
|
|
59
|
-
const apiKey
|
|
72
|
+
const apiKey = resolveApiKey(providerCfg.apiKey);
|
|
60
73
|
if (!baseUrl || !apiKey) return undefined;
|
|
61
74
|
|
|
62
75
|
const provider = detectProvider(providerKey, baseUrl);
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -1207,7 +1207,7 @@ export class SqliteStore {
|
|
|
1207
1207
|
|
|
1208
1208
|
// ─── Pattern Search (LIKE-based, for CJK text where FTS tokenization is weak) ───
|
|
1209
1209
|
|
|
1210
|
-
patternSearch(patterns: string[], opts: { role?: string; limit?: number } = {}): Array<{ chunkId: string; content: string; role: string; createdAt: number }> {
|
|
1210
|
+
patternSearch(patterns: string[], opts: { role?: string; limit?: number; ownerFilter?: string[] } = {}): Array<{ chunkId: string; content: string; role: string; createdAt: number }> {
|
|
1211
1211
|
if (patterns.length === 0) return [];
|
|
1212
1212
|
const limit = opts.limit ?? 10;
|
|
1213
1213
|
|
|
@@ -1216,13 +1216,21 @@ export class SqliteStore {
|
|
|
1216
1216
|
const roleClause = opts.role ? " AND c.role = ?" : "";
|
|
1217
1217
|
const params: (string | number)[] = patterns.map(p => `%${p}%`);
|
|
1218
1218
|
if (opts.role) params.push(opts.role);
|
|
1219
|
+
|
|
1220
|
+
let ownerClause = "";
|
|
1221
|
+
if (opts.ownerFilter && opts.ownerFilter.length > 0) {
|
|
1222
|
+
const placeholders = opts.ownerFilter.map(() => "?").join(",");
|
|
1223
|
+
ownerClause = ` AND c.owner IN (${placeholders})`;
|
|
1224
|
+
params.push(...opts.ownerFilter);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1219
1227
|
params.push(limit);
|
|
1220
1228
|
|
|
1221
1229
|
try {
|
|
1222
1230
|
const rows = this.db.prepare(`
|
|
1223
1231
|
SELECT c.id as chunk_id, c.content, c.role, c.created_at
|
|
1224
1232
|
FROM chunks c
|
|
1225
|
-
WHERE (${whereClause})${roleClause} AND c.dedup_status = 'active'
|
|
1233
|
+
WHERE (${whereClause})${roleClause}${ownerClause} AND c.dedup_status = 'active'
|
|
1226
1234
|
ORDER BY c.created_at DESC
|
|
1227
1235
|
LIMIT ?
|
|
1228
1236
|
`).all(...params) as Array<{ chunk_id: string; content: string; role: string; created_at: number }>;
|
package/src/viewer/html.ts
CHANGED
|
@@ -1251,7 +1251,6 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1251
1251
|
<option value="" data-i18n="filter.allagents">All agents</option>
|
|
1252
1252
|
</select>
|
|
1253
1253
|
<select id="memorySearchScope" class="filter-select" onchange="onMemoryScopeChange()" style="display:none">
|
|
1254
|
-
<option value="local" data-i18n="scope.thisAgent">This Agent</option>
|
|
1255
1254
|
<option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
|
|
1256
1255
|
<option value="hub" data-i18n="scope.hub">Team</option>
|
|
1257
1256
|
</select>
|
|
@@ -1295,7 +1294,6 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1295
1294
|
<button class="filter-chip" data-task-status="completed" onclick="setTaskStatusFilter(this,'completed')" data-i18n="tasks.status.completed">Completed</button>
|
|
1296
1295
|
<button class="filter-chip" data-task-status="skipped" onclick="setTaskStatusFilter(this,'skipped')" data-i18n="tasks.status.skipped">Skipped</button>
|
|
1297
1296
|
<select id="taskSearchScope" class="scope-select" onchange="onTaskScopeChange()" style="display:none">
|
|
1298
|
-
<option value="local" data-i18n="scope.thisAgent">This Agent</option>
|
|
1299
1297
|
<option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
|
|
1300
1298
|
<option value="hub" data-i18n="scope.hub">Team</option>
|
|
1301
1299
|
</select>
|
|
@@ -1337,7 +1335,6 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
1337
1335
|
<span class="search-icon">🔍</span>
|
|
1338
1336
|
<input type="text" id="skillSearchInput" placeholder="Search skills..." data-i18n-ph="skills.search.placeholder" oninput="debounceSkillSearch()">
|
|
1339
1337
|
<select id="skillSearchScope" class="scope-select" onchange="onSkillScopeChange()" style="display:none">
|
|
1340
|
-
<option value="local" data-i18n="scope.thisAgent">This Agent</option>
|
|
1341
1338
|
<option value="allLocal" data-i18n="scope.thisDevice">This Device</option>
|
|
1342
1339
|
<option value="hub" data-i18n="scope.hub">Team</option>
|
|
1343
1340
|
</select>
|
|
@@ -2030,10 +2027,18 @@ input,textarea,select{font-family:inherit;font-size:inherit}
|
|
|
2030
2027
|
|
|
2031
2028
|
<script>
|
|
2032
2029
|
let activeSession=null,activeRole='',editingId=null,searchTimer=null,memoryCache={},currentPage=1,totalPages=1,totalCount=0,PAGE_SIZE=40,metricsDays=30;
|
|
2033
|
-
let memorySearchScope='
|
|
2030
|
+
let memorySearchScope='allLocal',skillSearchScope='allLocal',taskSearchScope='allLocal';
|
|
2034
2031
|
let _lastMemoriesFingerprint='',_lastTasksFingerprint='',_lastSkillsFingerprint='';
|
|
2035
2032
|
let _embeddingWarningShown=false;
|
|
2036
2033
|
let _currentAgentOwner='agent:main';
|
|
2034
|
+
try {
|
|
2035
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
2036
|
+
const agentId = urlParams.get('agentId');
|
|
2037
|
+
if (agentId) {
|
|
2038
|
+
_currentAgentOwner = 'agent:' + agentId;
|
|
2039
|
+
}
|
|
2040
|
+
} catch(e) {}
|
|
2041
|
+
|
|
2037
2042
|
|
|
2038
2043
|
/* ─── i18n ─── */
|
|
2039
2044
|
const I18N={
|
|
@@ -3786,35 +3791,34 @@ function switchView(view){
|
|
|
3786
3791
|
}
|
|
3787
3792
|
|
|
3788
3793
|
function onMemoryScopeChange(){
|
|
3789
|
-
memorySearchScope=document.getElementById('memorySearchScope')?.value||'
|
|
3794
|
+
memorySearchScope=document.getElementById('memorySearchScope')?.value||'allLocal';
|
|
3790
3795
|
try{localStorage.setItem('memos_memorySearchScope',memorySearchScope);}catch(e){}
|
|
3791
3796
|
currentPage=1;
|
|
3792
3797
|
activeSession=null;activeRole='';
|
|
3793
3798
|
_lastMemoriesFingerprint='';
|
|
3794
3799
|
var isHub=memorySearchScope==='hub';
|
|
3795
|
-
var isLocal=memorySearchScope==='local';
|
|
3796
3800
|
var ownerSel=document.getElementById('filterOwner');
|
|
3797
3801
|
var filterBar=document.getElementById('filterBar');
|
|
3798
3802
|
var dateFilter=document.querySelector('.date-filter');
|
|
3799
|
-
if(ownerSel){ownerSel.style.display=
|
|
3803
|
+
if(ownerSel){ownerSel.style.display=isHub?'none':'';if(isHub)ownerSel.value='';}
|
|
3800
3804
|
if(filterBar) filterBar.style.display=isHub?'none':'';
|
|
3801
3805
|
if(dateFilter) dateFilter.style.display=isHub?'none':'';
|
|
3802
3806
|
if(document.getElementById('searchInput').value.trim()) doSearch(document.getElementById('searchInput').value);
|
|
3803
3807
|
else if(isHub) { document.getElementById('sharingSearchMeta').textContent=''; loadHubMemories(); }
|
|
3804
3808
|
else {
|
|
3805
3809
|
document.getElementById('sharingSearchMeta').textContent='';
|
|
3806
|
-
var ownerArg=
|
|
3810
|
+
var ownerArg=undefined;
|
|
3807
3811
|
loadStats(ownerArg); loadMemories();
|
|
3808
3812
|
}
|
|
3809
3813
|
}
|
|
3810
3814
|
|
|
3811
3815
|
function onSkillScopeChange(){
|
|
3812
|
-
skillSearchScope=document.getElementById('skillSearchScope')?.value||'
|
|
3816
|
+
skillSearchScope=document.getElementById('skillSearchScope')?.value||'allLocal';
|
|
3813
3817
|
loadSkills();
|
|
3814
3818
|
}
|
|
3815
3819
|
|
|
3816
3820
|
function onTaskScopeChange(){
|
|
3817
|
-
taskSearchScope=document.getElementById('taskSearchScope')?.value||'
|
|
3821
|
+
taskSearchScope=document.getElementById('taskSearchScope')?.value||'allLocal';
|
|
3818
3822
|
tasksPage=0;
|
|
3819
3823
|
loadTasks();
|
|
3820
3824
|
}
|
|
@@ -5864,16 +5868,14 @@ function setTaskStatusFilter(btn,status){
|
|
|
5864
5868
|
|
|
5865
5869
|
async function loadTasks(silent){
|
|
5866
5870
|
const scope=document.getElementById('taskSearchScope')?document.getElementById('taskSearchScope').value:taskSearchScope;
|
|
5867
|
-
taskSearchScope=scope||'
|
|
5871
|
+
taskSearchScope=scope||'allLocal';
|
|
5868
5872
|
if(taskSearchScope==='hub'){ return loadHubTasks(); }
|
|
5869
5873
|
const list=document.getElementById('tasksList');
|
|
5870
5874
|
if(!silent) list.innerHTML='<div class="spinner"></div>';
|
|
5871
5875
|
try{
|
|
5872
5876
|
const params=new URLSearchParams({limit:String(TASKS_PER_PAGE),offset:String(tasksPage*TASKS_PER_PAGE)});
|
|
5873
5877
|
if(tasksStatusFilter) params.set('status',tasksStatusFilter);
|
|
5874
|
-
if(taskSearchScope==='local') params.set('owner','agent:main');
|
|
5875
5878
|
var baseP=new URLSearchParams();
|
|
5876
|
-
if(taskSearchScope==='local') baseP.set('owner','agent:main');
|
|
5877
5879
|
const [data,allD,activeD,compD,skipD]=await Promise.all([
|
|
5878
5880
|
fetch('/api/tasks?'+params).then(r=>r.json()),
|
|
5879
5881
|
fetch('/api/tasks?limit=1&offset=0&'+baseP).then(r=>r.json()),
|
|
@@ -6180,7 +6182,7 @@ async function loadSkills(silent){
|
|
|
6180
6182
|
if(!silent) list.innerHTML='<div class="spinner"></div>';
|
|
6181
6183
|
var hubSection=document.getElementById('hubSkillsSection');
|
|
6182
6184
|
if(hubList){
|
|
6183
|
-
if(skillSearchScope==='
|
|
6185
|
+
if(skillSearchScope==='allLocal'){
|
|
6184
6186
|
if(hubSection) hubSection.style.display='none';
|
|
6185
6187
|
}else{
|
|
6186
6188
|
if(hubSection) hubSection.style.display='block';
|
|
@@ -6190,7 +6192,7 @@ async function loadSkills(silent){
|
|
|
6190
6192
|
|
|
6191
6193
|
const query=(document.getElementById('skillSearchInput')?.value||'').trim();
|
|
6192
6194
|
const scope=document.getElementById('skillSearchScope') ? document.getElementById('skillSearchScope').value : skillSearchScope;
|
|
6193
|
-
skillSearchScope=scope||'
|
|
6195
|
+
skillSearchScope=scope||'allLocal';
|
|
6194
6196
|
|
|
6195
6197
|
try{
|
|
6196
6198
|
const params=new URLSearchParams();
|
|
@@ -6260,7 +6262,7 @@ async function loadSkills(silent){
|
|
|
6260
6262
|
|
|
6261
6263
|
list.innerHTML=renderLocalCards(localSkills);
|
|
6262
6264
|
|
|
6263
|
-
if(skillSearchScope==='
|
|
6265
|
+
if(skillSearchScope==='allLocal'){
|
|
6264
6266
|
if(hubSection) hubSection.style.display='none';
|
|
6265
6267
|
document.getElementById('skillSearchMeta').textContent=query?(t('skills.search.local')+' '+localSkills.length):'';
|
|
6266
6268
|
document.getElementById('skillsTotalCount').textContent=formatNum(localSkills.length);
|
|
@@ -7445,7 +7447,7 @@ async function _livePollTick(){
|
|
|
7445
7447
|
var _searchVal=(document.getElementById('searchInput')||{}).value||'';
|
|
7446
7448
|
if(!_searchVal.trim()){
|
|
7447
7449
|
if(memorySearchScope==='hub') await loadHubMemories(true);
|
|
7448
|
-
else{var _pollOwner=
|
|
7450
|
+
else{var _pollOwner=undefined;await loadStats(_pollOwner);await loadMemories(null,true);}
|
|
7449
7451
|
}
|
|
7450
7452
|
}
|
|
7451
7453
|
else if(_activeView==='tasks') await loadTasks(true);
|
|
@@ -7718,7 +7720,7 @@ function stopNotifPoll(){ }
|
|
|
7718
7720
|
/* ─── Data loading ─── */
|
|
7719
7721
|
async function loadAll(){
|
|
7720
7722
|
await loadStats();
|
|
7721
|
-
var initOwner=
|
|
7723
|
+
var initOwner=undefined;
|
|
7722
7724
|
if(initOwner) await loadStats(initOwner);
|
|
7723
7725
|
await Promise.all([loadMemories(),loadSharingStatus(false)]);
|
|
7724
7726
|
checkMigrateStatus();
|
|
@@ -7738,7 +7740,7 @@ async function loadStats(ownerFilter){
|
|
|
7738
7740
|
d=await r.json();
|
|
7739
7741
|
}catch(e){ d={}; }
|
|
7740
7742
|
if(!d||typeof d!=='object') d={};
|
|
7741
|
-
if(d.currentAgentOwner) _currentAgentOwner=d.currentAgentOwner;
|
|
7743
|
+
if(d.currentAgentOwner && !new URLSearchParams(window.location.search).get('agentId')) _currentAgentOwner=d.currentAgentOwner;
|
|
7742
7744
|
const tm=d.totalMemories||0;
|
|
7743
7745
|
const dedupB=d.dedupBreakdown||{};
|
|
7744
7746
|
const activeCount=dedupB.active||tm;
|
|
@@ -7845,12 +7847,13 @@ function getFilterParams(){
|
|
|
7845
7847
|
if(dt) p.set('dateTo',dt);
|
|
7846
7848
|
const sort=document.getElementById('filterSort').value;
|
|
7847
7849
|
if(sort==='oldest') p.set('sort','oldest');
|
|
7848
|
-
const scope=memorySearchScope||'
|
|
7849
|
-
if(scope==='
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7850
|
+
const scope=memorySearchScope||'allLocal';
|
|
7851
|
+
if(scope==='allLocal'){
|
|
7852
|
+
const owner=document.getElementById('filterOwner').value;
|
|
7853
|
+
if(owner) {
|
|
7854
|
+
p.set('owner',owner);
|
|
7855
|
+
_currentAgentOwner = owner;
|
|
7856
|
+
}
|
|
7854
7857
|
}
|
|
7855
7858
|
return p;
|
|
7856
7859
|
}
|
|
@@ -7947,7 +7950,7 @@ async function doSearch(query){
|
|
|
7947
7950
|
return;
|
|
7948
7951
|
}
|
|
7949
7952
|
currentPage=1;
|
|
7950
|
-
var scope=document.getElementById('memorySearchScope')?.value||memorySearchScope||'
|
|
7953
|
+
var scope=document.getElementById('memorySearchScope')?.value||memorySearchScope||'allLocal';
|
|
7951
7954
|
var list=document.getElementById('memoryList');
|
|
7952
7955
|
list.innerHTML='<div class="spinner"></div>';
|
|
7953
7956
|
if(scope==='hub'){
|
|
@@ -9126,7 +9129,8 @@ async function checkForUpdate(){
|
|
|
9126
9129
|
/* ─── Init ─── */
|
|
9127
9130
|
try{
|
|
9128
9131
|
var savedScope=localStorage.getItem('memos_memorySearchScope');
|
|
9129
|
-
if(savedScope
|
|
9132
|
+
if(savedScope==='local') savedScope='allLocal';
|
|
9133
|
+
if(savedScope&&(savedScope==='allLocal'||savedScope==='hub')){
|
|
9130
9134
|
memorySearchScope=savedScope;
|
|
9131
9135
|
var scopeEl=document.getElementById('memorySearchScope');
|
|
9132
9136
|
if(scopeEl) scopeEl.value=savedScope;
|
package/src/viewer/server.ts
CHANGED
|
@@ -3258,7 +3258,8 @@ export class ViewerServer {
|
|
|
3258
3258
|
const providerCfg = providerKey
|
|
3259
3259
|
? raw?.models?.providers?.[providerKey]
|
|
3260
3260
|
: Object.values(raw?.models?.providers ?? {})[0] as Record<string, unknown> | undefined;
|
|
3261
|
-
|
|
3261
|
+
const resolvedKey = ViewerServer.resolveApiKeyValue(providerCfg?.apiKey);
|
|
3262
|
+
if (!providerCfg || !providerCfg.baseUrl || !resolvedKey) {
|
|
3262
3263
|
this.jsonResponse(res, { available: false });
|
|
3263
3264
|
return;
|
|
3264
3265
|
}
|
|
@@ -3268,6 +3269,17 @@ export class ViewerServer {
|
|
|
3268
3269
|
}
|
|
3269
3270
|
}
|
|
3270
3271
|
|
|
3272
|
+
private static resolveApiKeyValue(
|
|
3273
|
+
input: unknown,
|
|
3274
|
+
): string | undefined {
|
|
3275
|
+
if (!input) return undefined;
|
|
3276
|
+
if (typeof input === "string") return input;
|
|
3277
|
+
if (typeof input === "object" && input !== null && (input as any).source === "env") {
|
|
3278
|
+
return process.env[(input as any).id];
|
|
3279
|
+
}
|
|
3280
|
+
return undefined;
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3271
3283
|
private findPluginPackageJson(): string | null {
|
|
3272
3284
|
let dir = __dirname;
|
|
3273
3285
|
for (let i = 0; i < 6; i++) {
|
|
@@ -3455,7 +3467,7 @@ export class ViewerServer {
|
|
|
3455
3467
|
|
|
3456
3468
|
this.log.info(`update-install: running postinstall...`);
|
|
3457
3469
|
execFile(process.execPath, ["scripts/postinstall.cjs"], { cwd: extDir, timeout: 180_000 }, (postErr, postOut, postStderr) => {
|
|
3458
|
-
|
|
3470
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
3459
3471
|
|
|
3460
3472
|
if (postErr) {
|
|
3461
3473
|
this.log.warn(`update-install: postinstall failed: ${postErr.message}`);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"endpoint": "https://proj-xtrace-e218d9316b328f196a3c640cc7ca84-cn-hangzhou.cn-hangzhou.log.aliyuncs.com/rum/web/v2?workspace=default-cms-1026429231103299-cn-hangzhou&service_id=a3u72ukxmr@066657d42a13a9a9f337f",
|
|
3
|
-
"pid": "a3u72ukxmr@066657d42a13a9a9f337f",
|
|
4
|
-
"env": "prod"
|
|
5
|
-
}
|