Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
209b123cd8 | ||
|
|
60f6b580e7 | ||
|
|
cb4d86c0bc | ||
|
|
b08142aec1 |
@@ -0,0 +1,68 @@
|
||||
"""User profile isolation layer for multi-user agents.
|
||||
|
||||
Global agent instructions define agent behavior. User preferences must be scoped
|
||||
by user identity and session, never stored in a shared USER.md.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
|
||||
def utc_now() -> str:
|
||||
return datetime.now(timezone.utc).isoformat()
|
||||
|
||||
|
||||
def safe_user_key(user_id: str) -> str:
|
||||
return sha256(user_id.encode("utf-8")).hexdigest()[:24]
|
||||
|
||||
|
||||
@dataclass
|
||||
class UserProfile:
|
||||
user_key: str
|
||||
display_name: str | None = None
|
||||
preferences: dict[str, Any] = field(default_factory=dict)
|
||||
created_at: str = field(default_factory=utc_now)
|
||||
updated_at: str = field(default_factory=utc_now)
|
||||
|
||||
def to_dict(self):
|
||||
return asdict(self)
|
||||
|
||||
|
||||
class UserProfileStore:
|
||||
def __init__(self, root: str = "runtime/user-profiles"):
|
||||
self.root = Path(root)
|
||||
self.root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def get(self, user_id: str) -> dict[str, Any]:
|
||||
path = self.root / f"{safe_user_key(user_id)}.json"
|
||||
if not path.exists():
|
||||
return UserProfile(user_key=safe_user_key(user_id)).to_dict()
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
def update(self, user_id: str, updates: dict[str, Any]) -> dict[str, Any]:
|
||||
profile = self.get(user_id)
|
||||
if "display_name" in updates:
|
||||
profile["display_name"] = updates["display_name"]
|
||||
profile.setdefault("preferences", {}).update(updates.get("preferences", {}))
|
||||
profile["updated_at"] = utc_now()
|
||||
path = self.root / f"{safe_user_key(user_id)}.json"
|
||||
path.write_text(json.dumps(profile, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
return profile
|
||||
|
||||
def runtime_context(self, user_id: str) -> dict[str, Any]:
|
||||
profile = self.get(user_id)
|
||||
return {
|
||||
"user_profile": profile,
|
||||
"source": "user_profile_store",
|
||||
"shared_agent_defaults": "USER.md",
|
||||
"isolation": True,
|
||||
}
|
||||
|
||||
|
||||
__all__ = ["UserProfile", "UserProfileStore", "safe_user_key"]
|
||||
@@ -0,0 +1,20 @@
|
||||
user_profile_isolation:
|
||||
enabled: true
|
||||
principle: one_agent_many_users_requires_scoped_user_context
|
||||
global_files:
|
||||
USER.md: agent_defaults_only
|
||||
user_data:
|
||||
storage: runtime/user-profiles
|
||||
isolation_key: user_id_hash
|
||||
git_commit_required: false
|
||||
reason: user preferences are runtime data, not shared configuration
|
||||
forbidden:
|
||||
- shared_user_preferences_in_USER_MD
|
||||
- cross_user_profile_access
|
||||
- user_identity_data_in_public_agent_config
|
||||
audit:
|
||||
enabled: true
|
||||
events:
|
||||
- profile_created
|
||||
- profile_updated
|
||||
- context_loaded
|
||||
@@ -0,0 +1,30 @@
|
||||
# CMD-20260709-enable-user-profile-isolation
|
||||
|
||||
## Goal
|
||||
|
||||
Fix multi-user agent context leakage caused by shared USER.md personalization.
|
||||
|
||||
## Rules
|
||||
|
||||
- USER.md is agent-level default behavior only.
|
||||
- User names, preferences and personalized settings must come from user profile storage.
|
||||
- Every WeChat/user session loads only its own profile by user_id.
|
||||
|
||||
## Deploy
|
||||
|
||||
1. Deploy user profile isolation runtime layer.
|
||||
2. Create runtime/user-profiles storage.
|
||||
3. Migrate existing shared personalization from USER.md into explicit owner profile if needed.
|
||||
4. Remove user-specific names from shared USER.md.
|
||||
5. Verify two users cannot read each other's profiles.
|
||||
|
||||
## Validation signals
|
||||
|
||||
- AIOS_USER_PROFILE_ISOLATION_READY
|
||||
- AIOS_MULTIUSER_CONTEXT_SAFE
|
||||
|
||||
## Governance
|
||||
|
||||
- User preferences are runtime data, not Git configuration.
|
||||
- No personal profile data in shared Git files.
|
||||
- Audit profile create/update/context load events.
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "branch",
|
||||
"branch": "patch/user-profile-isolation",
|
||||
"base": "main",
|
||||
"created_by": "chatgpt-gitops-bot",
|
||||
"created_at": "2026-07-09T01:19:21Z",
|
||||
"direct_main_push": false,
|
||||
"owner_approval_required": true
|
||||
}
|
||||
Reference in New Issue
Block a user