fix: derive default owner from workspaceDir instead of hardcoding agent:main#1374
fix: derive default owner from workspaceDir instead of hardcoding agent:main#1374zerone0x wants to merge 1 commit intoMemTensor:mainfrom
Conversation
…ent:main" The memory_search, memory_get, and memory_timeline tools all hardcoded owner="agent:main" when no explicit owner parameter was provided. This broke multi-agent isolation since all agents would query as "agent:main" regardless of their actual identity. Now the default owner is derived from the workspace directory name using the convention workspace-<name> → agent:<name>, falling back to "agent:main" only when the directory doesn't follow that pattern. The shared logic lives in a new resolve-owner.ts helper module. Closes MemTensor#1318 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR removes the hardcoded owner="agent:main" default from the memory_search, memory_get, and memory_timeline tools by deriving the default owner from the workspace directory name (e.g., workspace-dev → agent:dev) and centralizing the logic in a shared helper.
Changes:
- Added
resolve-owner.tsto derivedefaultOwnerfromworkspaceDirand build the owner filter array. - Updated
memory_search,memory_get, andmemory_timelineto use the shared owner-resolution helper instead of hardcodingagent:main. - Wired
workspaceDirintomemory_getandmemory_timelinetool creation viasrc/index.ts.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/memos-local-openclaw/src/tools/resolve-owner.ts | New helper for deriving defaultOwner from workspaceDir and producing owner filters. |
| apps/memos-local-openclaw/src/tools/memory-search.ts | Switches to shared default owner + owner filter resolution. |
| apps/memos-local-openclaw/src/tools/memory-get.ts | Accepts workspaceDir to derive default owner; uses shared owner filter logic. |
| apps/memos-local-openclaw/src/tools/memory-timeline.ts | Accepts workspaceDir to derive default owner; uses shared owner filter logic. |
| apps/memos-local-openclaw/src/index.ts | Passes workspaceDir into memory_get/memory_timeline tool factories. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const base = path.basename(workspaceDir); | ||
| const match = base.match(/^workspace-(.+)$/); | ||
| if (match) { | ||
| return `agent:${match[1]}`; |
There was a problem hiding this comment.
resolveDefaultOwner returns agent:${match[1]} directly from the directory name capture. If the workspace directory name has leading/trailing whitespace (or other unexpected characters), this can create an owner value that will never match DB rows. Consider trimming/sanitizing the captured agent name (and optionally constraining it to an allowed character set) before building the owner string.
| return `agent:${match[1]}`; | |
| const agentName = match[1].trim(); | |
| if (agentName.length > 0) { | |
| return `agent:${agentName}`; | |
| } |
| /** | ||
| * Derive the default owner from the workspace directory name. | ||
| * | ||
| * Convention: a workspace directory named "workspace-<agentName>" maps to | ||
| * "agent:<agentName>". 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"; | ||
| } |
There was a problem hiding this comment.
Owner default resolution is now central to multi-agent isolation behavior, but there are no tests covering the new workspace-<name> → agent:<name> mapping (including fallback to agent:main and explicit owner precedence). Adding a small unit test for resolveDefaultOwner/resolveOwnerFilter would prevent regressions.
| * 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; |
There was a problem hiding this comment.
resolveOwnerFilter checks owner.trim().length > 0 but then uses the untrimmed owner value. This means inputs like "public " or "agent:dev\n" will produce an ownerFilter that won’t match stored owners (and "public " won’t be treated as the special public case). Consider normalizing to the trimmed string before comparisons/returning the filter.
| const resolvedOwner = typeof owner === "string" && owner.trim().length > 0 ? owner : defaultOwner; | |
| const trimmedOwner = typeof owner === "string" ? owner.trim() : ""; | |
| const resolvedOwner = trimmedOwner.length > 0 ? trimmedOwner : defaultOwner; |
Summary
owner="agent:main"default inmemory_search,memory_get, andmemory_timelinetoolsworkspace-<name>→agent:<name>, withagent:mainas fallbackresolve-owner.tshelper moduleCloses #1318
Test plan
tsc --noEmit)workspace-devdirectory → owner defaults toagent:devworkspace-javisdirectory → owner defaults toagent:javisagent:mainownerparameter still takes precedence over the default🤖 Generated with Claude Code