fix(context): share agent scoped context data

This commit is contained in:
Duy Nguyen committed 2026-05-17 15:33:27 +07:00
1 parent 536ab4ac6c
commit 2fa715ba4e
5 files changed
+84 -6

No files matched your search

+4
View File
@@ -156,6 +156,9 @@ func (l *Loop) injectContext(ctx context.Context, req *RunRequest) (contextSetup
}
// Apply user isolation layer via pipeline.
shared := l.shouldShareWorkspace(req.UserID, req.PeerKind)
if shared {
ctx = store.WithSharedContext(ctx)
}
effectiveWorkspace := tools.ResolveWorkspace(ws,
tools.UserChatLayer(tools.SanitizePathSegment(req.UserID), shared),
)
@@ -359,6 +362,7 @@ func (l *Loop) injectContext(ctx context.Context, req *RunRequest) (contextSetup
SharedMemory: store.IsSharedMemory(ctx),
SharedKG: store.IsSharedKG(ctx),
SharedSessions: store.IsSharedSessions(ctx),
SharedContext: store.IsSharedContext(ctx),
RestrictToWorkspace: l.restrictToWs != nil && *l.restrictToWs,
BuiltinToolSettings: l.builtinToolSettings,
ChannelType: req.ChannelType,
+28
View File
@@ -31,6 +31,8 @@ const (
SharedKGKey contextKey = "goclaw_shared_kg"
// SharedSessionsKey indicates sessions should be shared across all users (no per-group scoping).
SharedSessionsKey contextKey = "goclaw_shared_sessions"
// SharedContextKey indicates context files should be read/written at agent scope.
SharedContextKey contextKey = "goclaw_shared_context"
// ShellDenyGroupsKey holds per-agent shell deny group overrides.
ShellDenyGroupsKey contextKey = "goclaw_shell_deny_groups"
// AgentKeyKey is the context key for the agent key/name (string identifier, e.g. "default").
@@ -282,6 +284,32 @@ func MemoryUserID(ctx context.Context) string {
return UserIDFromContext(ctx)
}
// WithSharedContext returns a context flagged for shared context files.
func WithSharedContext(ctx context.Context) context.Context {
return context.WithValue(ctx, SharedContextKey, true)
}
// IsSharedContext returns true if context files should use agent-level scope.
func IsSharedContext(ctx context.Context) bool {
if v, ok := ctx.Value(SharedContextKey).(bool); ok {
return v
}
if rc := RunContextFromCtx(ctx); rc != nil {
return rc.SharedContext
}
return false
}
// ContextUserID returns the userID to use for context-file operations.
// Shared workspace mode maps virtual context files to the agent-level store so
// read_file/write_file behavior matches the visible shared workspace path.
func ContextUserID(ctx context.Context) string {
if IsSharedContext(ctx) {
return ""
}
return UserIDFromContext(ctx)
}
// KGUserID returns the userID to use for knowledge graph operations.
// Returns "" (agent-level scope) when shared KG is active, otherwise the per-user ID.
func KGUserID(ctx context.Context) string {
+2 -1
View File
@@ -34,6 +34,7 @@ type RunContext struct {
SharedMemory bool
SharedKG bool
SharedSessions bool
SharedContext bool
RestrictToWorkspace bool
// Tool configuration
@@ -52,7 +53,7 @@ type RunContext struct {
TeamID string
WorkspaceChannel string
WorkspaceChatID string
TeamIsolated bool // true when team.workspace_scope != "shared" — drives chat_id filtering in vault search
TeamIsolated bool // true when team.workspace_scope != "shared" — drives chat_id filtering in vault search
TeamTaskID string
DelegationID string // delegation identifier for vault auto-linking (empty when not in delegation)
LeaderAgentID string // leader's agent UUID for member memory read fallback
+36 -4
View File
@@ -20,7 +20,15 @@ func (s *SQLiteMemoryStore) GetDocument(ctx context.Context, agentID, userID, pa
var content string
var err error
if userID == "" {
if store.IsSharedMemory(ctx) {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return "", tcErr
}
err = s.db.QueryRowContext(ctx,
"SELECT content FROM memory_documents WHERE agent_id = ? AND path = ?"+tc+" ORDER BY updated_at DESC LIMIT 1",
append([]any{aid, path}, tcArgs...)...).Scan(&content)
} else if userID == "" {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return "", tcErr
@@ -69,7 +77,15 @@ func (s *SQLiteMemoryStore) DeleteDocument(ctx context.Context, agentID, userID,
var res sql.Result
var err error
if userID == "" {
if store.IsSharedMemory(ctx) {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return tcErr
}
res, err = s.db.ExecContext(ctx,
"DELETE FROM memory_documents WHERE agent_id = ? AND path = ?"+tc,
append([]any{agentID, path}, tcArgs...)...)
} else if userID == "" {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return tcErr
@@ -100,7 +116,15 @@ func (s *SQLiteMemoryStore) ListDocuments(ctx context.Context, agentID, userID s
var rows *sql.Rows
var err error
if userID == "" {
if store.IsSharedMemory(ctx) {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return nil, tcErr
}
rows, err = s.db.QueryContext(ctx,
"SELECT path, hash, user_id, updated_at FROM memory_documents WHERE agent_id = ?"+tc,
append([]any{agentID}, tcArgs...)...)
} else if userID == "" {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return nil, tcErr
@@ -147,7 +171,15 @@ func (s *SQLiteMemoryStore) IndexDocument(ctx context.Context, agentID, userID,
// Get document ID
var docID string
if userID == "" {
if store.IsSharedMemory(ctx) {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return tcErr
}
err = s.db.QueryRowContext(ctx,
"SELECT id FROM memory_documents WHERE agent_id = ? AND path = ?"+tc+" ORDER BY updated_at DESC LIMIT 1",
append([]any{agentID, path}, tcArgs...)...).Scan(&docID)
} else if userID == "" {
tc, tcArgs, tcErr := scopeClause(ctx)
if tcErr != nil {
return tcErr
+14 -1
View File
@@ -48,7 +48,20 @@ func (s *SQLiteMemoryStore) likeSearch(ctx context.Context, query, agentID, user
var q string
var args []any
if userID != "" {
if store.IsSharedMemory(ctx) {
tc, tcArgs, err := scopeClause(ctx)
if err != nil {
return nil, err
}
q = `SELECT path, start_line, end_line, text, user_id
FROM memory_chunks
WHERE agent_id = ?
AND text LIKE ? ESCAPE '\'` + tc + `
ORDER BY updated_at DESC
LIMIT ?`
args = append([]any{agentID, pattern}, tcArgs...)
args = append(args, limit)
} else if userID != "" {
tc, tcArgs, err := scopeClause(ctx)
if err != nil {
return nil, err