multi-tenant Phase 1: user registry + per-user memory

- CDK: add agent-claw-users DynamoDB table (actor_id PK, RETAIN policy)
- CDK: grant agent-runner read/write on users table; add USERS_TABLE_NAME env
- CDK: fix cdk.json app field (was object, must be command string)
- CDK: add UsersTableName output
- agent-runner: get_or_create_user() auto-registers users on first contact
  (stores display_name, telegram_username, created_at, allowed)
- agent-runner: pass user_profile in AgentCore payload
- prompt_builder: split base prompt (cached) from per-user context (injected per-call)
  removes USER.md/MEMORY.md from shared load; user name/username injected dynamically
- main.py: extract user_profile from payload, build user_context string for prompt
This commit is contained in:
daniel
2026-05-06 20:36:22 -05:00
parent 732b00fb66
commit 893c110729
16 changed files with 726 additions and 501 deletions

View File

@@ -24,6 +24,29 @@ def get_agentcore():
return _agentcore
def get_or_create_user(actor_id: str, from_info: dict) -> dict:
"""Look up user in registry, auto-registering on first contact."""
table_name = os.environ.get('USERS_TABLE_NAME', '')
if not table_name:
return {'actor_id': actor_id, 'display_name': from_info.get('from_name', actor_id)}
table = get_ddb().Table(table_name)
response = table.get_item(Key={'actor_id': actor_id})
item = response.get('Item')
if item:
return item
now = int(time.time())
item = {
'actor_id': actor_id,
'display_name': from_info.get('from_name') or actor_id,
'telegram_username': from_info.get('from_username', ''),
'created_at': str(now),
'allowed': True,
}
table.put_item(Item=item)
print(f'[agent-runner] Registered new user: {actor_id}')
return item
def get_or_create_session(actor_id: str) -> str:
"""Look up active session for actor, or create a new one."""
table = get_ddb().Table(os.environ['SESSION_TABLE_NAME'])
@@ -72,9 +95,13 @@ def handler(event, context):
chat_id = first.get('chat_id', '')
actor_id = f"{channel}:{chat_id}"
# ── User registry ─────────────────────────────────────────────────────
from_info = first.get('messages', [{}])[0]
user_profile = get_or_create_user(actor_id, from_info)
# ── Get or create AgentCore session ──────────────────────────────────
session_id = get_or_create_session(actor_id)
print(f"[agent-runner] actor={actor_id} session={session_id}")
print(f"[agent-runner] actor={actor_id} session={session_id} user={user_profile.get('display_name', '')}")
# ── Bundle messages ───────────────────────────────────────────────────
if len(records) == 1:
@@ -91,6 +118,11 @@ def handler(event, context):
'prompt': prompt,
'actor_id': actor_id,
'session_id': session_id,
'user_profile': {
'display_name': user_profile.get('display_name', actor_id),
'telegram_username': user_profile.get('telegram_username', ''),
'allowed': user_profile.get('allowed', True),
},
'channel_adapter': {
'type': channel,
'target_id': str(chat_id),