@memtensor/memos-local-openclaw-plugin 1.0.4-beta.5 → 1.0.4-beta.7
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 +23 -23
- package/dist/capture/index.d.ts +1 -1
- package/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +28 -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 +18 -19
- 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/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +101 -81
- 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 +5 -2
- package/dist/index.js.map +1 -1
- package/dist/storage/sqlite.d.ts +54 -20
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +185 -101
- package/dist/storage/sqlite.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/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +1619 -629
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +14 -8
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +545 -141
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +355 -41
- package/package.json +1 -1
- package/skill/memos-memory-guide/SKILL.md +64 -26
- package/src/capture/index.ts +29 -1
- package/src/client/connector.ts +15 -21
- package/src/client/hub.ts +18 -0
- package/src/client/skill-sync.ts +14 -0
- package/src/hub/server.ts +88 -74
- package/src/hub/user-manager.ts +7 -3
- package/src/index.ts +7 -2
- package/src/storage/sqlite.ts +192 -122
- package/src/tools/memory-search.ts +2 -1
- package/src/viewer/html.ts +1619 -629
- package/src/viewer/server.ts +506 -128
package/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { ViewerServer } from "./src/viewer/server";
|
|
|
22
22
|
import { HubServer } from "./src/hub/server";
|
|
23
23
|
import { hubGetMemoryDetail, hubRequestJson, hubSearchMemories, hubSearchSkills, resolveHubClient } from "./src/client/hub";
|
|
24
24
|
import { getHubStatus, connectToHub } from "./src/client/connector";
|
|
25
|
-
import { fetchHubSkillBundle, publishSkillBundleToHub, restoreSkillBundleFromHub } from "./src/client/skill-sync";
|
|
25
|
+
import { fetchHubSkillBundle, publishSkillBundleToHub, restoreSkillBundleFromHub, unpublishSkillBundleFromHub } from "./src/client/skill-sync";
|
|
26
26
|
import { SkillEvolver } from "./src/skill/evolver";
|
|
27
27
|
import { SkillInstaller } from "./src/skill/installer";
|
|
28
28
|
import { Summarizer } from "./src/ingest/providers";
|
|
@@ -304,6 +304,89 @@ const memosLocalPlugin = {
|
|
|
304
304
|
}
|
|
305
305
|
};
|
|
306
306
|
|
|
307
|
+
const getCurrentOwner = () => `agent:${currentAgentId}`;
|
|
308
|
+
const resolveMemorySearchScope = (scope?: string): "local" | "group" | "all" =>
|
|
309
|
+
scope === "group" || scope === "all" ? scope : "local";
|
|
310
|
+
const resolveMemoryShareTarget = (target?: string): "agents" | "hub" | "both" =>
|
|
311
|
+
target === "hub" || target === "both" ? target : "agents";
|
|
312
|
+
const resolveMemoryUnshareTarget = (target?: string): "agents" | "hub" | "all" =>
|
|
313
|
+
target === "agents" || target === "hub" ? target : "all";
|
|
314
|
+
const resolveSkillPublishTarget = (target?: string, scope?: string): "agents" | "hub" => {
|
|
315
|
+
if (target === "hub") return "hub";
|
|
316
|
+
if (target === "agents") return "agents";
|
|
317
|
+
return scope === "public" || scope === "group" ? "hub" : "agents";
|
|
318
|
+
};
|
|
319
|
+
const resolveSkillHubVisibility = (visibility?: string, scope?: string): "public" | "group" =>
|
|
320
|
+
visibility === "group" || scope === "group" ? "group" : "public";
|
|
321
|
+
const resolveSkillUnpublishTarget = (target?: string): "agents" | "hub" | "all" =>
|
|
322
|
+
target === "hub" || target === "all" ? target : "agents";
|
|
323
|
+
|
|
324
|
+
const shareMemoryToHub = async (
|
|
325
|
+
chunkId: string,
|
|
326
|
+
input?: { visibility?: "public" | "group"; groupId?: string; hubAddress?: string; userToken?: string },
|
|
327
|
+
): Promise<{ memoryId: string; visibility: "public" | "group"; groupId: string | null }> => {
|
|
328
|
+
const chunk = store.getChunk(chunkId);
|
|
329
|
+
if (!chunk) {
|
|
330
|
+
throw new Error(`Memory not found: ${chunkId}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const visibility = input?.visibility === "group" ? "group" : "public";
|
|
334
|
+
const groupId = visibility === "group" ? (input?.groupId ?? null) : null;
|
|
335
|
+
const hubClient = await resolveHubClient(store, ctx, { hubAddress: input?.hubAddress, userToken: input?.userToken });
|
|
336
|
+
const response = await hubRequestJson(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/memories/share", {
|
|
337
|
+
method: "POST",
|
|
338
|
+
body: JSON.stringify({
|
|
339
|
+
memory: {
|
|
340
|
+
sourceChunkId: chunk.id,
|
|
341
|
+
role: chunk.role,
|
|
342
|
+
content: chunk.content,
|
|
343
|
+
summary: chunk.summary,
|
|
344
|
+
kind: chunk.kind,
|
|
345
|
+
groupId,
|
|
346
|
+
visibility,
|
|
347
|
+
},
|
|
348
|
+
}),
|
|
349
|
+
}) as { memoryId?: string; visibility?: "public" | "group" };
|
|
350
|
+
|
|
351
|
+
const now = Date.now();
|
|
352
|
+
const existing = store.getHubMemoryBySource(hubClient.userId, chunk.id);
|
|
353
|
+
store.upsertHubMemory({
|
|
354
|
+
id: response?.memoryId ?? existing?.id ?? `${chunk.id}-hub`,
|
|
355
|
+
sourceChunkId: chunk.id,
|
|
356
|
+
sourceUserId: hubClient.userId,
|
|
357
|
+
role: chunk.role,
|
|
358
|
+
content: chunk.content,
|
|
359
|
+
summary: chunk.summary ?? "",
|
|
360
|
+
kind: chunk.kind,
|
|
361
|
+
groupId,
|
|
362
|
+
visibility,
|
|
363
|
+
createdAt: existing?.createdAt ?? now,
|
|
364
|
+
updatedAt: now,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
memoryId: response?.memoryId ?? existing?.id ?? `${chunk.id}-hub`,
|
|
369
|
+
visibility,
|
|
370
|
+
groupId,
|
|
371
|
+
};
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const unshareMemoryFromHub = async (
|
|
375
|
+
chunkId: string,
|
|
376
|
+
input?: { hubAddress?: string; userToken?: string },
|
|
377
|
+
): Promise<void> => {
|
|
378
|
+
const chunk = store.getChunk(chunkId);
|
|
379
|
+
if (!chunk) {
|
|
380
|
+
throw new Error(`Memory not found: ${chunkId}`);
|
|
381
|
+
}
|
|
382
|
+
const hubClient = await resolveHubClient(store, ctx, { hubAddress: input?.hubAddress, userToken: input?.userToken });
|
|
383
|
+
await hubRequestJson(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/memories/unshare", {
|
|
384
|
+
method: "POST",
|
|
385
|
+
body: JSON.stringify({ sourceChunkId: chunk.id }),
|
|
386
|
+
});
|
|
387
|
+
store.deleteHubMemoryBySource(hubClient.userId, chunk.id);
|
|
388
|
+
};
|
|
389
|
+
|
|
307
390
|
// ─── Tool: memory_search ───
|
|
308
391
|
|
|
309
392
|
api.registerTool(
|
|
@@ -312,24 +395,43 @@ const memosLocalPlugin = {
|
|
|
312
395
|
label: "Memory Search",
|
|
313
396
|
description:
|
|
314
397
|
"Search long-term conversation memory for past conversations, user preferences, decisions, and experiences. " +
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"Pass only a short natural-language query (2-5 key words).",
|
|
398
|
+
"Use scope='local' for this agent plus local shared memories, or scope='group'/'all' to include Hub-shared memories. " +
|
|
399
|
+
"Supports optional maxResults, minScore, and role filtering when you need tighter control.",
|
|
318
400
|
parameters: Type.Object({
|
|
319
401
|
query: Type.String({ description: "Short natural language search query (2-5 key words)" }),
|
|
402
|
+
scope: Type.Optional(Type.String({ description: "Search scope: 'local' (default), 'group', or 'all'. Use group/all to include Hub-shared memories." })),
|
|
403
|
+
maxResults: Type.Optional(Type.Number({ description: "Maximum results to return. Default 10, max 20." })),
|
|
404
|
+
minScore: Type.Optional(Type.Number({ description: "Minimum score threshold for local recall. Default 0.45, floor 0.35." })),
|
|
405
|
+
role: Type.Optional(Type.String({ description: "Optional local role filter: 'user', 'assistant', 'tool', or 'system'." })),
|
|
406
|
+
hubAddress: Type.Optional(Type.String({ description: "Optional Hub address override for group/all search." })),
|
|
407
|
+
userToken: Type.Optional(Type.String({ description: "Optional Hub bearer token override for group/all search." })),
|
|
320
408
|
}),
|
|
321
409
|
execute: trackTool("memory_search", async (_toolCallId: any, params: any) => {
|
|
322
|
-
const {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
410
|
+
const {
|
|
411
|
+
query,
|
|
412
|
+
scope: rawScope,
|
|
413
|
+
maxResults,
|
|
414
|
+
minScore: rawMinScore,
|
|
415
|
+
role: rawRole,
|
|
416
|
+
hubAddress,
|
|
417
|
+
userToken,
|
|
418
|
+
} = params as {
|
|
419
|
+
query: string;
|
|
420
|
+
scope?: string;
|
|
421
|
+
maxResults?: number;
|
|
422
|
+
minScore?: number;
|
|
423
|
+
role?: string;
|
|
424
|
+
hubAddress?: string;
|
|
425
|
+
userToken?: string;
|
|
426
|
+
};
|
|
427
|
+
const role = rawRole === "user" || rawRole === "assistant" || rawRole === "tool" || rawRole === "system" ? rawRole : undefined;
|
|
428
|
+
const minScore = typeof rawMinScore === "number" ? Math.max(0.35, Math.min(1, rawMinScore)) : undefined;
|
|
429
|
+
const searchScope = resolveMemorySearchScope(rawScope);
|
|
430
|
+
const searchLimit = typeof maxResults === "number" ? Math.max(1, Math.min(20, Math.round(maxResults))) : 10;
|
|
329
431
|
|
|
330
432
|
const agentId = currentAgentId;
|
|
331
|
-
const ownerFilter = [
|
|
332
|
-
const effectiveMaxResults =
|
|
433
|
+
const ownerFilter = [getCurrentOwner(), "public"];
|
|
434
|
+
const effectiveMaxResults = searchLimit;
|
|
333
435
|
ctx.log.debug(`memory_search query="${query}" maxResults=${effectiveMaxResults} minScore=${minScore ?? 0.45} role=${role ?? "all"} owner=agent:${agentId}`);
|
|
334
436
|
const result = await engine.search({ query, maxResults: effectiveMaxResults, minScore, role, ownerFilter });
|
|
335
437
|
ctx.log.debug(`memory_search raw candidates: ${result.hits.length}`);
|
|
@@ -342,7 +444,7 @@ const memosLocalPlugin = {
|
|
|
342
444
|
original_excerpt: (h.original_excerpt ?? "").slice(0, 200),
|
|
343
445
|
}));
|
|
344
446
|
|
|
345
|
-
if (result.hits.length === 0) {
|
|
447
|
+
if (result.hits.length === 0 && searchScope === "local") {
|
|
346
448
|
return {
|
|
347
449
|
content: [{ type: "text", text: result.meta.note ?? "No relevant memories found." }],
|
|
348
450
|
details: { candidates: [], meta: result.meta },
|
|
@@ -366,11 +468,13 @@ const memosLocalPlugin = {
|
|
|
366
468
|
const indexSet = new Set(filterResult.relevant);
|
|
367
469
|
filteredHits = result.hits.filter((_, i) => indexSet.has(i + 1));
|
|
368
470
|
ctx.log.debug(`memory_search LLM filter: ${result.hits.length} → ${filteredHits.length} hits, sufficient=${sufficient}`);
|
|
369
|
-
} else {
|
|
471
|
+
} else if (searchScope === "local") {
|
|
370
472
|
return {
|
|
371
473
|
content: [{ type: "text", text: "No relevant memories found for this query." }],
|
|
372
474
|
details: { candidates: rawCandidates, filtered: [], meta: result.meta },
|
|
373
475
|
};
|
|
476
|
+
} else {
|
|
477
|
+
filteredHits = [];
|
|
374
478
|
}
|
|
375
479
|
}
|
|
376
480
|
|
|
@@ -846,7 +950,9 @@ ${detail.content}`,
|
|
|
846
950
|
{
|
|
847
951
|
name: "network_team_info",
|
|
848
952
|
label: "Network Team Info",
|
|
849
|
-
description:
|
|
953
|
+
description:
|
|
954
|
+
"Show current Hub connection status, signed-in user, role, and group memberships. " +
|
|
955
|
+
"Use this as a preflight check before any Hub share/unshare or Hub pull operation.",
|
|
850
956
|
parameters: Type.Object({}),
|
|
851
957
|
execute: trackTool("network_team_info", async () => {
|
|
852
958
|
const status = await getHubStatus(store, ctx.config);
|
|
@@ -1021,12 +1127,13 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1021
1127
|
api.registerTool(
|
|
1022
1128
|
{
|
|
1023
1129
|
name: "memory_write_public",
|
|
1024
|
-
label: "Write
|
|
1130
|
+
label: "Write Local Shared Memory",
|
|
1025
1131
|
description:
|
|
1026
|
-
"Write a piece of information to
|
|
1027
|
-
"Use this
|
|
1132
|
+
"Write a piece of information to local shared memory for all agents in this OpenClaw workspace. " +
|
|
1133
|
+
"Use this when you are creating a new shared note from scratch. This does not publish to Hub. " +
|
|
1134
|
+
"If you already have a memory chunk and want to expose it, use memory_share instead.",
|
|
1028
1135
|
parameters: Type.Object({
|
|
1029
|
-
content: Type.String({ description: "The content to write to
|
|
1136
|
+
content: Type.String({ description: "The content to write to local shared memory" }),
|
|
1030
1137
|
summary: Type.Optional(Type.String({ description: "Optional short summary of the content" })),
|
|
1031
1138
|
}),
|
|
1032
1139
|
execute: trackTool("memory_write_public", async (_toolCallId: any, params: any) => {
|
|
@@ -1071,7 +1178,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1071
1178
|
}
|
|
1072
1179
|
|
|
1073
1180
|
return {
|
|
1074
|
-
content: [{ type: "text", text: `
|
|
1181
|
+
content: [{ type: "text", text: `Memory shared to local agents successfully (id: ${chunkId}).` }],
|
|
1075
1182
|
details: { chunkId, owner: "public" },
|
|
1076
1183
|
};
|
|
1077
1184
|
}),
|
|
@@ -1079,6 +1186,164 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1079
1186
|
{ name: "memory_write_public" },
|
|
1080
1187
|
);
|
|
1081
1188
|
|
|
1189
|
+
api.registerTool(
|
|
1190
|
+
{
|
|
1191
|
+
name: "memory_share",
|
|
1192
|
+
label: "Share Memory",
|
|
1193
|
+
description:
|
|
1194
|
+
"Share an existing memory either with local OpenClaw agents, to the Hub team, or to both targets. " +
|
|
1195
|
+
"Use this only for an existing chunkId. Use target='agents' for local multi-agent sharing, target='hub' for team sharing, or target='both' for both. " +
|
|
1196
|
+
"If you need to create a brand new shared memory instead of exposing an existing one, use memory_write_public.",
|
|
1197
|
+
parameters: Type.Object({
|
|
1198
|
+
chunkId: Type.String({ description: "Existing local memory chunk ID to share" }),
|
|
1199
|
+
target: Type.Optional(Type.String({ description: "Share target: 'agents' (default), 'hub', or 'both'" })),
|
|
1200
|
+
visibility: Type.Optional(Type.String({ description: "Hub visibility when target includes hub: 'public' (default) or 'group'" })),
|
|
1201
|
+
groupId: Type.Optional(Type.String({ description: "Optional Hub group ID when visibility='group'" })),
|
|
1202
|
+
hubAddress: Type.Optional(Type.String({ description: "Optional Hub address override" })),
|
|
1203
|
+
userToken: Type.Optional(Type.String({ description: "Optional Hub bearer token override" })),
|
|
1204
|
+
}),
|
|
1205
|
+
execute: trackTool("memory_share", async (_toolCallId: any, params: any) => {
|
|
1206
|
+
const {
|
|
1207
|
+
chunkId,
|
|
1208
|
+
target: rawTarget,
|
|
1209
|
+
visibility: rawVisibility,
|
|
1210
|
+
groupId,
|
|
1211
|
+
hubAddress,
|
|
1212
|
+
userToken,
|
|
1213
|
+
} = params as {
|
|
1214
|
+
chunkId: string;
|
|
1215
|
+
target?: string;
|
|
1216
|
+
visibility?: string;
|
|
1217
|
+
groupId?: string;
|
|
1218
|
+
hubAddress?: string;
|
|
1219
|
+
userToken?: string;
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
const chunk = store.getChunk(chunkId);
|
|
1223
|
+
if (!chunk) {
|
|
1224
|
+
return { content: [{ type: "text", text: `Memory not found: ${chunkId}` }], details: { error: "not_found", chunkId } };
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const target = resolveMemoryShareTarget(rawTarget);
|
|
1228
|
+
const visibility = rawVisibility === "group" ? "group" : "public";
|
|
1229
|
+
const details: Record<string, unknown> = { chunkId, target };
|
|
1230
|
+
const messages: string[] = [];
|
|
1231
|
+
|
|
1232
|
+
if (target === "agents" || target === "both") {
|
|
1233
|
+
const local = store.markMemorySharedLocally(chunkId);
|
|
1234
|
+
if (!local.ok) {
|
|
1235
|
+
return { content: [{ type: "text", text: `Failed to share memory ${chunkId} to local agents.` }], details: { error: local.reason ?? "local_share_failed", chunkId, target } };
|
|
1236
|
+
}
|
|
1237
|
+
details.local = {
|
|
1238
|
+
shared: true,
|
|
1239
|
+
owner: local.owner,
|
|
1240
|
+
originalOwner: local.originalOwner ?? null,
|
|
1241
|
+
};
|
|
1242
|
+
messages.push("shared to local agents");
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (target === "hub" || target === "both") {
|
|
1246
|
+
const hub = await shareMemoryToHub(chunkId, { visibility, groupId, hubAddress, userToken });
|
|
1247
|
+
details.hub = {
|
|
1248
|
+
shared: true,
|
|
1249
|
+
memoryId: hub.memoryId,
|
|
1250
|
+
visibility: hub.visibility,
|
|
1251
|
+
groupId: hub.groupId,
|
|
1252
|
+
};
|
|
1253
|
+
messages.push(`shared to Hub (${hub.visibility})`);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
return {
|
|
1257
|
+
content: [{ type: "text", text: `Memory "${chunk.summary || chunk.id}" ${messages.join(" and ")}.` }],
|
|
1258
|
+
details,
|
|
1259
|
+
};
|
|
1260
|
+
}),
|
|
1261
|
+
},
|
|
1262
|
+
{ name: "memory_share" },
|
|
1263
|
+
);
|
|
1264
|
+
|
|
1265
|
+
api.registerTool(
|
|
1266
|
+
{
|
|
1267
|
+
name: "memory_unshare",
|
|
1268
|
+
label: "Unshare Memory",
|
|
1269
|
+
description:
|
|
1270
|
+
"Remove sharing from an existing memory. Use target='agents' to stop local multi-agent sharing, target='hub' to remove it from Hub, or target='all' (default) to remove both. " +
|
|
1271
|
+
"privateOwner is only needed for older public memories that were never tracked with an original owner.",
|
|
1272
|
+
parameters: Type.Object({
|
|
1273
|
+
chunkId: Type.String({ description: "Existing local memory chunk ID to unshare" }),
|
|
1274
|
+
target: Type.Optional(Type.String({ description: "Unshare target: 'agents', 'hub', or 'all' (default)" })),
|
|
1275
|
+
privateOwner: Type.Optional(Type.String({ description: "Optional owner to restore when converting a public memory back to private and no original owner was tracked" })),
|
|
1276
|
+
hubAddress: Type.Optional(Type.String({ description: "Optional Hub address override" })),
|
|
1277
|
+
userToken: Type.Optional(Type.String({ description: "Optional Hub bearer token override" })),
|
|
1278
|
+
}),
|
|
1279
|
+
execute: trackTool("memory_unshare", async (_toolCallId: any, params: any) => {
|
|
1280
|
+
const {
|
|
1281
|
+
chunkId,
|
|
1282
|
+
target: rawTarget,
|
|
1283
|
+
privateOwner,
|
|
1284
|
+
hubAddress,
|
|
1285
|
+
userToken,
|
|
1286
|
+
} = params as {
|
|
1287
|
+
chunkId: string;
|
|
1288
|
+
target?: string;
|
|
1289
|
+
privateOwner?: string;
|
|
1290
|
+
hubAddress?: string;
|
|
1291
|
+
userToken?: string;
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
const chunk = store.getChunk(chunkId);
|
|
1295
|
+
if (!chunk) {
|
|
1296
|
+
return { content: [{ type: "text", text: `Memory not found: ${chunkId}` }], details: { error: "not_found", chunkId } };
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
const target = resolveMemoryUnshareTarget(rawTarget);
|
|
1300
|
+
const details: Record<string, unknown> = { chunkId, target };
|
|
1301
|
+
const messages: string[] = [];
|
|
1302
|
+
|
|
1303
|
+
if (target === "agents" || target === "all") {
|
|
1304
|
+
const local = store.unmarkMemorySharedLocally(chunkId, privateOwner);
|
|
1305
|
+
if (!local.ok) {
|
|
1306
|
+
return {
|
|
1307
|
+
content: [{
|
|
1308
|
+
type: "text",
|
|
1309
|
+
text: local.reason === "original_owner_missing"
|
|
1310
|
+
? `Cannot restore memory "${chunk.summary || chunk.id}" to a private owner automatically. Pass privateOwner to unshare it locally.`
|
|
1311
|
+
: `Failed to stop local sharing for memory ${chunkId}.`,
|
|
1312
|
+
}],
|
|
1313
|
+
details: { error: local.reason ?? "local_unshare_failed", chunkId, target },
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
details.local = {
|
|
1317
|
+
shared: false,
|
|
1318
|
+
owner: local.owner,
|
|
1319
|
+
};
|
|
1320
|
+
messages.push("removed from local agent sharing");
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
if (target === "hub" || target === "all") {
|
|
1324
|
+
try {
|
|
1325
|
+
await unshareMemoryFromHub(chunkId, { hubAddress, userToken });
|
|
1326
|
+
details.hub = { shared: false };
|
|
1327
|
+
messages.push("removed from Hub");
|
|
1328
|
+
} catch (err) {
|
|
1329
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1330
|
+
if (target === "all" && msg.includes("hub client connection is not configured")) {
|
|
1331
|
+
details.hub = { shared: false, skipped: true, reason: "hub_not_configured" };
|
|
1332
|
+
} else {
|
|
1333
|
+
throw err;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
return {
|
|
1339
|
+
content: [{ type: "text", text: `Memory "${chunk.summary || chunk.id}" ${messages.join(" and ")}.` }],
|
|
1340
|
+
details,
|
|
1341
|
+
};
|
|
1342
|
+
}),
|
|
1343
|
+
},
|
|
1344
|
+
{ name: "memory_unshare" },
|
|
1345
|
+
);
|
|
1346
|
+
|
|
1082
1347
|
// ─── Tool: skill_search ───
|
|
1083
1348
|
|
|
1084
1349
|
api.registerTool(
|
|
@@ -1086,16 +1351,16 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1086
1351
|
name: "skill_search",
|
|
1087
1352
|
label: "Skill Search",
|
|
1088
1353
|
description:
|
|
1089
|
-
"Search available skills by natural language.
|
|
1090
|
-
"Use when you need a capability or guide and don't have a matching skill at hand.",
|
|
1354
|
+
"Search available skills by natural language. Use scope='mix' (default) for this agent plus local shared skills, 'self' for this agent only, 'public' for local shared skills only, or 'group'/'all' to include Hub skills as well. " +
|
|
1355
|
+
"Use this when you need a capability or guide and don't have a matching skill at hand.",
|
|
1091
1356
|
parameters: Type.Object({
|
|
1092
1357
|
query: Type.String({ description: "Natural language description of the needed skill" }),
|
|
1093
|
-
scope: Type.Optional(Type.String({ description: "Search scope: 'mix'
|
|
1358
|
+
scope: Type.Optional(Type.String({ description: "Search scope: 'mix' (default), 'self', 'public', 'group', or 'all'." })),
|
|
1094
1359
|
}),
|
|
1095
1360
|
execute: trackTool("skill_search", async (_toolCallId: any, params: any, context?: any) => {
|
|
1096
1361
|
const { query: skillQuery, scope: rawScope } = params as { query: string; scope?: string };
|
|
1097
1362
|
const scope = (rawScope === "self" || rawScope === "public") ? rawScope : "mix";
|
|
1098
|
-
const currentOwner =
|
|
1363
|
+
const currentOwner = getCurrentOwner();
|
|
1099
1364
|
|
|
1100
1365
|
if (rawScope === "group" || rawScope === "all") {
|
|
1101
1366
|
const [localHits, hub] = await Promise.all([
|
|
@@ -1111,7 +1376,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1111
1376
|
}
|
|
1112
1377
|
|
|
1113
1378
|
const localText = localHits.length > 0
|
|
1114
|
-
? localHits.map((h, i) => `${i + 1}. [${h.name}] ${h.description.slice(0, 150)}${h.visibility === "public" ? " (
|
|
1379
|
+
? localHits.map((h, i) => `${i + 1}. [${h.name}] ${h.description.slice(0, 150)}${h.visibility === "public" ? " (shared to local agents)" : ""}`).join("\n")
|
|
1115
1380
|
: "(none)";
|
|
1116
1381
|
const hubText = hub.hits.length > 0
|
|
1117
1382
|
? hub.hits.map((h, i) => `${i + 1}. [${h.name}] ${h.description.slice(0, 150)} (${h.visibility}${h.groupName ? `:${h.groupName}` : ""}, owner=${h.ownerName})`).join("\n")
|
|
@@ -1133,7 +1398,7 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1133
1398
|
}
|
|
1134
1399
|
|
|
1135
1400
|
const text = hits.map((h, i) =>
|
|
1136
|
-
`${i + 1}. [${h.name}] ${h.description}${h.visibility === "public" ? " (
|
|
1401
|
+
`${i + 1}. [${h.name}] ${h.description}${h.visibility === "public" ? " (shared to local agents)" : ""}`,
|
|
1137
1402
|
).join("\n");
|
|
1138
1403
|
|
|
1139
1404
|
return {
|
|
@@ -1151,31 +1416,54 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1151
1416
|
{
|
|
1152
1417
|
name: "skill_publish",
|
|
1153
1418
|
label: "Publish Skill",
|
|
1154
|
-
description:
|
|
1419
|
+
description:
|
|
1420
|
+
"Share a skill with local agents or publish it to the Hub. " +
|
|
1421
|
+
"Use target='agents' for local sharing, or target='hub' with visibility='public'/'group' for Hub publishing. " +
|
|
1422
|
+
"The old scope parameter is still accepted for backward compatibility.",
|
|
1155
1423
|
parameters: Type.Object({
|
|
1156
1424
|
skillId: Type.String({ description: "The skill ID to publish" }),
|
|
1157
|
-
|
|
1425
|
+
target: Type.Optional(Type.String({ description: "Publish target: 'agents' (default) or 'hub'." })),
|
|
1426
|
+
visibility: Type.Optional(Type.String({ description: "Hub visibility when target='hub': 'public' (default) or 'group'." })),
|
|
1427
|
+
scope: Type.Optional(Type.String({ description: "Deprecated alias: omit for local agents, or use 'public' / 'group' to publish to Hub." })),
|
|
1158
1428
|
groupId: Type.Optional(Type.String({ description: "Optional group ID when scope='group'" })),
|
|
1159
1429
|
hubAddress: Type.Optional(Type.String({ description: "Optional Hub address override for tests or manual routing" })),
|
|
1160
1430
|
userToken: Type.Optional(Type.String({ description: "Optional Hub bearer token override for tests" })),
|
|
1161
1431
|
}),
|
|
1162
1432
|
execute: trackTool("skill_publish", async (_toolCallId: any, params: any) => {
|
|
1163
|
-
const {
|
|
1433
|
+
const {
|
|
1434
|
+
skillId: pubSkillId,
|
|
1435
|
+
target: rawTarget,
|
|
1436
|
+
visibility: rawVisibility,
|
|
1437
|
+
scope,
|
|
1438
|
+
groupId,
|
|
1439
|
+
hubAddress,
|
|
1440
|
+
userToken,
|
|
1441
|
+
} = params as {
|
|
1442
|
+
skillId: string;
|
|
1443
|
+
target?: string;
|
|
1444
|
+
visibility?: string;
|
|
1445
|
+
scope?: string;
|
|
1446
|
+
groupId?: string;
|
|
1447
|
+
hubAddress?: string;
|
|
1448
|
+
userToken?: string;
|
|
1449
|
+
};
|
|
1164
1450
|
const skill = store.getSkill(pubSkillId);
|
|
1165
1451
|
if (!skill) {
|
|
1166
1452
|
return { content: [{ type: "text", text: `Skill not found: ${pubSkillId}` }] };
|
|
1167
1453
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1454
|
+
const target = resolveSkillPublishTarget(rawTarget, scope);
|
|
1455
|
+
const visibility = resolveSkillHubVisibility(rawVisibility, scope);
|
|
1456
|
+
if (target === "hub") {
|
|
1457
|
+
const published = await publishSkillBundleToHub(store, ctx, { skillId: pubSkillId, visibility, groupId, hubAddress, userToken });
|
|
1170
1458
|
return {
|
|
1171
|
-
content: [{ type: "text", text: `Skill "${skill.name}"
|
|
1172
|
-
details: { skillId: pubSkillId, name: skill.name, publishedToHub: true, hubSkillId: published.skillId, visibility: published.visibility },
|
|
1459
|
+
content: [{ type: "text", text: `Skill "${skill.name}" shared to Hub (${published.visibility}).` }],
|
|
1460
|
+
details: { skillId: pubSkillId, name: skill.name, target, publishedToHub: true, hubSkillId: published.skillId, visibility: published.visibility },
|
|
1173
1461
|
};
|
|
1174
1462
|
}
|
|
1175
1463
|
store.setSkillVisibility(pubSkillId, "public");
|
|
1176
1464
|
return {
|
|
1177
|
-
content: [{ type: "text", text: `Skill "${skill.name}" is now
|
|
1178
|
-
details: { skillId: pubSkillId, name: skill.name, visibility: "public", publishedToHub: false },
|
|
1465
|
+
content: [{ type: "text", text: `Skill "${skill.name}" is now shared with local agents.` }],
|
|
1466
|
+
details: { skillId: pubSkillId, name: skill.name, target, visibility: "public", publishedToHub: false },
|
|
1179
1467
|
};
|
|
1180
1468
|
}),
|
|
1181
1469
|
},
|
|
@@ -1188,20 +1476,46 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
|
|
|
1188
1476
|
{
|
|
1189
1477
|
name: "skill_unpublish",
|
|
1190
1478
|
label: "Unpublish Skill",
|
|
1191
|
-
description:
|
|
1479
|
+
description:
|
|
1480
|
+
"Stop sharing a skill with local agents, remove it from the Hub, or do both. " +
|
|
1481
|
+
"Use target='agents' (default), 'hub', or 'all'.",
|
|
1192
1482
|
parameters: Type.Object({
|
|
1193
1483
|
skillId: Type.String({ description: "The skill ID to unpublish" }),
|
|
1484
|
+
target: Type.Optional(Type.String({ description: "Unpublish target: 'agents' (default), 'hub', or 'all'." })),
|
|
1485
|
+
hubAddress: Type.Optional(Type.String({ description: "Optional Hub address override for tests or manual routing" })),
|
|
1486
|
+
userToken: Type.Optional(Type.String({ description: "Optional Hub bearer token override for tests" })),
|
|
1194
1487
|
}),
|
|
1195
1488
|
execute: trackTool("skill_unpublish", async (_toolCallId: any, params: any) => {
|
|
1196
|
-
const { skillId: unpubSkillId } = params as { skillId: string };
|
|
1489
|
+
const { skillId: unpubSkillId, target, hubAddress, userToken } = params as { skillId: string; target?: string; hubAddress?: string; userToken?: string };
|
|
1197
1490
|
const skill = store.getSkill(unpubSkillId);
|
|
1198
1491
|
if (!skill) {
|
|
1199
1492
|
return { content: [{ type: "text", text: `Skill not found: ${unpubSkillId}` }] };
|
|
1200
1493
|
}
|
|
1201
|
-
|
|
1494
|
+
const resolvedTarget = resolveSkillUnpublishTarget(target);
|
|
1495
|
+
const messages: string[] = [];
|
|
1496
|
+
const details: Record<string, unknown> = { skillId: unpubSkillId, name: skill.name, target: resolvedTarget };
|
|
1497
|
+
if (resolvedTarget === "hub" || resolvedTarget === "all") {
|
|
1498
|
+
try {
|
|
1499
|
+
await unpublishSkillBundleFromHub(store, ctx, { skillId: unpubSkillId, hubAddress, userToken });
|
|
1500
|
+
details.hub = { unpublished: true };
|
|
1501
|
+
messages.push("removed from Hub sharing");
|
|
1502
|
+
} catch (err) {
|
|
1503
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1504
|
+
if (resolvedTarget === "all" && msg.includes("hub client connection is not configured")) {
|
|
1505
|
+
details.hub = { unpublished: false, skipped: true, reason: "hub_not_configured" };
|
|
1506
|
+
} else {
|
|
1507
|
+
throw err;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
if (resolvedTarget === "agents" || resolvedTarget === "all") {
|
|
1512
|
+
store.setSkillVisibility(unpubSkillId, "private");
|
|
1513
|
+
details.local = { visibility: "private" };
|
|
1514
|
+
messages.push("limited to this agent");
|
|
1515
|
+
}
|
|
1202
1516
|
return {
|
|
1203
|
-
content: [{ type: "text", text: `Skill "${skill.name}"
|
|
1204
|
-
details
|
|
1517
|
+
content: [{ type: "text", text: `Skill "${skill.name}" ${messages.join(" and ")}.` }],
|
|
1518
|
+
details,
|
|
1205
1519
|
};
|
|
1206
1520
|
}),
|
|
1207
1521
|
},
|
package/package.json
CHANGED