diff --git a/apps/memos-local-openclaw/src/index.ts b/apps/memos-local-openclaw/src/index.ts index abf3e3590..32bb6b19e 100644 --- a/apps/memos-local-openclaw/src/index.ts +++ b/apps/memos-local-openclaw/src/index.ts @@ -68,8 +68,8 @@ export function initPlugin(opts: PluginInitOptions = {}): MemosLocalPlugin { const tools: ToolDefinition[] = [ createMemorySearchTool(engine, store, ctx, sharedState), - createMemoryTimelineTool(store), - createMemoryGetTool(store), + createMemoryTimelineTool(store, workspaceDir), + createMemoryGetTool(store, workspaceDir), createNetworkMemoryDetailTool(store, ctx), ]; diff --git a/apps/memos-local-openclaw/src/tools/memory-get.ts b/apps/memos-local-openclaw/src/tools/memory-get.ts index 5dde5c0ac..82cb94afd 100644 --- a/apps/memos-local-openclaw/src/tools/memory-get.ts +++ b/apps/memos-local-openclaw/src/tools/memory-get.ts @@ -1,13 +1,10 @@ import type { SqliteStore } from "../storage/sqlite"; import type { ToolDefinition, GetResult, ChunkRef } from "../types"; import { DEFAULTS } from "../types"; +import { resolveDefaultOwner, resolveOwnerFilter } from "./resolve-owner"; -function resolveOwnerFilter(owner: unknown): string[] { - const resolvedOwner = typeof owner === "string" && owner.trim().length > 0 ? owner : "agent:main"; - return resolvedOwner === "public" ? ["public"] : [resolvedOwner, "public"]; -} - -export function createMemoryGetTool(store: SqliteStore): ToolDefinition { +export function createMemoryGetTool(store: SqliteStore, workspaceDir?: string): ToolDefinition { + const defaultOwner = resolveDefaultOwner(workspaceDir); return { name: "memory_get", description: @@ -41,7 +38,7 @@ export function createMemoryGetTool(store: SqliteStore): ToolDefinition { DEFAULTS.getMaxCharsMax, ); - const chunk = store.getChunksByRef(ref, resolveOwnerFilter(input.owner)); + const chunk = store.getChunksByRef(ref, resolveOwnerFilter(input.owner, defaultOwner)); if (!chunk) { return { error: `Chunk not found: ${ref.chunkId}` }; diff --git a/apps/memos-local-openclaw/src/tools/memory-search.ts b/apps/memos-local-openclaw/src/tools/memory-search.ts index 43cad5bc8..d76bb67e0 100644 --- a/apps/memos-local-openclaw/src/tools/memory-search.ts +++ b/apps/memos-local-openclaw/src/tools/memory-search.ts @@ -3,11 +3,7 @@ import type { HubScope, HubSearchResult } from "../sharing/types"; import type { RecallEngine } from "../recall/engine"; import type { PluginContext, ToolDefinition } from "../types"; import type { SqliteStore } from "../storage/sqlite"; - -function resolveOwnerFilter(owner: unknown): string[] { - const resolvedOwner = typeof owner === "string" && owner.trim().length > 0 ? owner : "agent:main"; - return resolvedOwner === "public" ? ["public"] : [resolvedOwner, "public"]; -} +import { resolveDefaultOwner, resolveOwnerFilter } from "./resolve-owner"; function resolveScope(scope: unknown): HubScope { return scope === "group" || scope === "all" ? scope : "local"; @@ -25,6 +21,7 @@ function emptyHubResult(scope: HubScope): HubSearchResult { } export function createMemorySearchTool(engine: RecallEngine, store?: SqliteStore, ctx?: PluginContext, sharedState?: { lastSearchTime: number }): ToolDefinition { + const defaultOwner = resolveDefaultOwner(ctx?.workspaceDir); return { name: "memory_search", description: @@ -64,7 +61,7 @@ export function createMemorySearchTool(engine: RecallEngine, store?: SqliteStore const query = (input.query as string) ?? ""; const maxResults = input.maxResults as number | undefined; const minScore = input.minScore as number | undefined; - const ownerFilter = resolveOwnerFilter(input.owner); + const ownerFilter = resolveOwnerFilter(input.owner, defaultOwner); const scope = resolveScope(input.scope); const localSearch = engine.search({ diff --git a/apps/memos-local-openclaw/src/tools/memory-timeline.ts b/apps/memos-local-openclaw/src/tools/memory-timeline.ts index 92d4031f4..f2f0f94f0 100644 --- a/apps/memos-local-openclaw/src/tools/memory-timeline.ts +++ b/apps/memos-local-openclaw/src/tools/memory-timeline.ts @@ -1,13 +1,10 @@ import type { SqliteStore } from "../storage/sqlite"; import type { ToolDefinition, TimelineResult, TimelineEntry, ChunkRef } from "../types"; import { DEFAULTS } from "../types"; +import { resolveDefaultOwner, resolveOwnerFilter } from "./resolve-owner"; -function resolveOwnerFilter(owner: unknown): string[] { - const resolvedOwner = typeof owner === "string" && owner.trim().length > 0 ? owner : "agent:main"; - return resolvedOwner === "public" ? ["public"] : [resolvedOwner, "public"]; -} - -export function createMemoryTimelineTool(store: SqliteStore): ToolDefinition { +export function createMemoryTimelineTool(store: SqliteStore, workspaceDir?: string): ToolDefinition { + const defaultOwner = resolveDefaultOwner(workspaceDir); return { name: "memory_timeline", description: @@ -38,7 +35,7 @@ export function createMemoryTimelineTool(store: SqliteStore): ToolDefinition { const ref = input.ref as ChunkRef; const window = (input.window as number) ?? DEFAULTS.timelineWindowDefault; - const ownerFilter = resolveOwnerFilter(input.owner); + const ownerFilter = resolveOwnerFilter(input.owner, defaultOwner); const anchorChunk = store.getChunksByRef(ref, ownerFilter); if (!anchorChunk) { return { entries: [], anchorRef: ref } satisfies TimelineResult; diff --git a/apps/memos-local-openclaw/src/tools/resolve-owner.ts b/apps/memos-local-openclaw/src/tools/resolve-owner.ts new file mode 100644 index 000000000..cd5ac1c71 --- /dev/null +++ b/apps/memos-local-openclaw/src/tools/resolve-owner.ts @@ -0,0 +1,30 @@ +import * as path from "path"; + +/** + * Derive the default owner from the workspace directory name. + * + * Convention: a workspace directory named "workspace-" maps to + * "agent:". If the directory name does not follow that pattern + * (or no workspaceDir is provided) the fallback is "agent:main". + */ +export function resolveDefaultOwner(workspaceDir?: string): string { + if (workspaceDir) { + const base = path.basename(workspaceDir); + const match = base.match(/^workspace-(.+)$/); + if (match) { + return `agent:${match[1]}`; + } + } + return "agent:main"; +} + +/** + * Build the owner filter array used for queries. + * + * If the caller supplied an explicit `owner` value it takes precedence; + * otherwise the `defaultOwner` (derived from workspace context) is used. + */ +export function resolveOwnerFilter(owner: unknown, defaultOwner: string = "agent:main"): string[] { + const resolvedOwner = typeof owner === "string" && owner.trim().length > 0 ? owner : defaultOwner; + return resolvedOwner === "public" ? ["public"] : [resolvedOwner, "public"]; +}