mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 04:18:44 +00:00
feat(team): share leader memory with team members (read-only)
Team members dispatched via team tasks can now read the leader's memory files (MEMORY.md, memory/*.md) as a fallback when they have no own memory. Memory writes are blocked for members — only the leader can save memory files. This follows the existing team_workspace context propagation pattern: dispatch metadata → RunRequest → context → tools. Affected tools: read_file (interceptor), memory_get, memory_search, list_files (interceptor). All include leader fallback with per-user → global scope cascade.
This commit is contained in:
1 parent
e9342533e0
commit
ee9f5b8aaf
10 files changed
+523
-7
No files matched your search
@@ -117,7 +117,16 @@ func (m *MemoryInterceptor) ReadFile(ctx context.Context, path string) (string,
|
||||
content, err = m.memStore.GetDocument(ctx, agentStr, "", relPath)
|
||||
}
|
||||
if err != nil {
|
||||
// Not found is OK — return empty
|
||||
// Not found in own memory — try leader's memory as fallback (team members only).
|
||||
if leaderID := LeaderAgentIDFromCtx(ctx); leaderID != "" && leaderID != agentStr {
|
||||
leaderContent, lerr := m.memStore.GetDocument(ctx, leaderID, userID, relPath)
|
||||
if lerr != nil && userID != "" {
|
||||
leaderContent, lerr = m.memStore.GetDocument(ctx, leaderID, "", relPath)
|
||||
}
|
||||
if lerr == nil && leaderContent != "" {
|
||||
return leaderContent, true, nil
|
||||
}
|
||||
}
|
||||
slog.Debug("memory interceptor: document not found", "path", path, "agent", agentStr)
|
||||
return "", true, nil
|
||||
}
|
||||
@@ -147,11 +156,17 @@ func (m *MemoryInterceptor) WriteFile(ctx context.Context, path, content string,
|
||||
return MemoryWriteResult{}, nil // no agent context
|
||||
}
|
||||
|
||||
// Block memory writes for team members — memory is leader-only.
|
||||
agentStr := agentID.String()
|
||||
if leaderID := LeaderAgentIDFromCtx(ctx); leaderID != "" && leaderID != agentStr {
|
||||
return MemoryWriteResult{Handled: true}, fmt.Errorf(
|
||||
"memory is read-only for team members — only the team leader can save memory files")
|
||||
}
|
||||
|
||||
// Normalize absolute path to workspace-relative for DB storage
|
||||
relPath := normalizeToRelative(path, ws)
|
||||
|
||||
userID := store.MemoryUserID(ctx)
|
||||
agentStr := agentID.String()
|
||||
|
||||
var previousContent string
|
||||
|
||||
@@ -208,11 +223,30 @@ func (m *MemoryInterceptor) ListFiles(ctx context.Context, path string) (string,
|
||||
}
|
||||
|
||||
userID := store.MemoryUserID(ctx)
|
||||
docs, err := m.memStore.ListDocuments(ctx, agentID.String(), userID)
|
||||
agentStr := agentID.String()
|
||||
docs, err := m.memStore.ListDocuments(ctx, agentStr, userID)
|
||||
if err != nil {
|
||||
return "", true, err
|
||||
}
|
||||
|
||||
// Merge leader's documents for team members (fallback, deduplicate by path).
|
||||
if leaderID := LeaderAgentIDFromCtx(ctx); leaderID != "" && leaderID != agentStr {
|
||||
seen := make(map[string]bool, len(docs))
|
||||
for _, d := range docs {
|
||||
seen[d.Path] = true
|
||||
}
|
||||
leaderDocs, _ := m.memStore.ListDocuments(ctx, leaderID, userID)
|
||||
// Also try global scope if per-user returned nothing.
|
||||
if len(leaderDocs) == 0 && userID != "" {
|
||||
leaderDocs, _ = m.memStore.ListDocuments(ctx, leaderID, "")
|
||||
}
|
||||
for _, d := range leaderDocs {
|
||||
if !seen[d.Path] {
|
||||
docs = append(docs, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(docs) == 0 {
|
||||
return "", true, nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user