From 5fd3c4ff0258decbd758bd107665230d8aebf6fa Mon Sep 17 00:00:00 2001 From: bolbol Date: Thu, 30 Jul 2026 08:40:51 +0000 Subject: [PATCH] feat(agents): has_scope_access lit desormais la table agents (DB = source de verite, plus de redeploiement pour ajouter un agent) --- app/scopes.py | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/app/scopes.py b/app/scopes.py index add2df2..cd9df9a 100644 --- a/app/scopes.py +++ b/app/scopes.py @@ -1,14 +1,20 @@ +import json +import sqlite3 from typing import List -# Scopes matrix according to prompt.md -# agent -> list of allowed scopes -AGENT_SCOPES = { +DB_PATH = "data/context_hub.db" + +# Filet de securite si la table agents est absente/vide (bootstrap avant premier +# seed, ou table non encore migree) -- la table agents (SQLite) est la source +# de verite reelle ; ce dict n'est qu'un repli pour ne jamais faire echouer une +# verification d'acces si la DB n'est pas encore prete. +_FALLBACK_AGENT_SCOPES = { "CLAUDE": ["infra", "llm", "nyora", "perso", "tt", "coding"], "HERMES_TT": ["infra", "llm", "tt"], "HERMES_NYORA": ["infra", "llm", "nyora"], "HERMES_PERSO": ["infra", "llm", "nyora", "perso"], "GEMINI": ["infra", "llm", "nyora", "coding"], - "NABIL": ["infra", "llm", "nyora", "perso", "tt", "coding"] # Assuming Nabil has access to all + "NABIL": ["infra", "llm", "nyora", "perso", "tt", "coding"], } # "coding" est transverse (memory_entries), pas dans ALL_SCOPES qui reste les @@ -17,9 +23,29 @@ AGENT_SCOPES = { ALL_SCOPES = ["infra", "llm", "nyora", "perso", "tt"] MEMORY_SCOPES = ["coding"] -def has_scope_access(agent_name: str, scope: str) -> bool: - allowed_scopes = AGENT_SCOPES.get(agent_name.upper(), []) - return scope in allowed_scopes def get_allowed_scopes(agent_name: str) -> List[str]: - return AGENT_SCOPES.get(agent_name.upper(), []) + """ + Lecture synchrone directe de la table `agents` (SQLite tolere plusieurs + lecteurs concurrents sans conflit). Volontairement synchrone : has_scope_access + est appele dans une douzaine d'endroits (api.py, mcp.py) qui ne sont pas tous + async-friendly a moindre risque -- garder cette fonction sync evite de toucher + chaque appelant. Le cout d'une connexion sqlite3 par appel est negligeable + pour un si petit fichier et un si faible volume de requetes. + """ + agent_name = agent_name.upper() + try: + con = sqlite3.connect(DB_PATH) + cur = con.cursor() + cur.execute("SELECT scopes FROM agents WHERE agent_name=?", (agent_name,)) + row = cur.fetchone() + con.close() + if row: + return json.loads(row[0]) + except Exception: + pass + return _FALLBACK_AGENT_SCOPES.get(agent_name, []) + + +def has_scope_access(agent_name: str, scope: str) -> bool: + return scope in get_allowed_scopes(agent_name)