feat: add governed chat agent selection
This commit is contained in:
@@ -24,6 +24,7 @@ def project_root() -> str:
|
||||
ROOT = project_root()
|
||||
BIN = os.path.join(ROOT, "packages", "casan-harness", "scripts", "bash")
|
||||
ROUTER = os.path.join(BIN, "prompt-mode-router.py")
|
||||
AGENT_RESOLVER = os.path.join(BIN, "chat-agent-resolver.py")
|
||||
READONLY = os.path.join(BIN, "chat-readonly.py")
|
||||
OPERATOR = os.path.join(BIN, "chat-operator.py")
|
||||
|
||||
@@ -36,13 +37,69 @@ def classify(message: str):
|
||||
return {"mode": "BLOCK", "risk": "high", "reason": "router_invalid_json", "matched_rules": [r.stderr.strip()]}
|
||||
|
||||
|
||||
def run_mode(args, binding):
|
||||
r = subprocess.run(args, cwd=ROOT, capture_output=True, text=True)
|
||||
try:
|
||||
payload = json.loads(r.stdout)
|
||||
payload["agent_binding"] = binding
|
||||
print(json.dumps(payload, ensure_ascii=False))
|
||||
except Exception:
|
||||
if r.stdout:
|
||||
sys.stdout.write(r.stdout)
|
||||
if r.stderr:
|
||||
sys.stderr.write(r.stderr)
|
||||
return r.returncode
|
||||
|
||||
|
||||
def run_and_passthrough(args):
|
||||
r = subprocess.run(args, cwd=ROOT, text=True)
|
||||
return r.returncode
|
||||
|
||||
|
||||
def default_agent_for_mode(mode: str) -> str:
|
||||
if mode == "OPERATOR":
|
||||
return "ops-operator"
|
||||
if mode == "CODEGEN":
|
||||
return "codegen-draft"
|
||||
return "evidence-reader"
|
||||
|
||||
|
||||
def bind_agent(args, router):
|
||||
agent = args.agent or default_agent_for_mode(router.get("mode", "READ_ONLY"))
|
||||
tools = []
|
||||
if router.get("mode") == "OPERATOR":
|
||||
# The operator primitive resolves the exact registered action later; the
|
||||
# agent bind still proves this turn is allowed to use the operator class.
|
||||
tools.append("run-chat-tests")
|
||||
cmd = [
|
||||
"python3", AGENT_RESOLVER, "bind",
|
||||
"--agent", agent,
|
||||
"--actor", args.actor,
|
||||
"--role", args.role,
|
||||
"--project", args.project,
|
||||
"--tenant", args.tenant,
|
||||
"--delegation-level", str(args.delegation_level),
|
||||
]
|
||||
if args.skill:
|
||||
cmd += ["--skill", args.skill]
|
||||
for tool in tools:
|
||||
cmd += ["--tool", tool]
|
||||
r = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
sys.stdout.write((r.stdout or r.stderr).strip() + "\n")
|
||||
return r.returncode, None
|
||||
try:
|
||||
return 0, json.loads(r.stdout)
|
||||
except Exception:
|
||||
print(json.dumps({"success": False, "decision": "DENIED", "reason": "agent_resolver_invalid_json"}, ensure_ascii=False))
|
||||
return 2, None
|
||||
|
||||
|
||||
def ask(args) -> int:
|
||||
router = classify(args.message)
|
||||
bind_rc, binding = bind_agent(args, router)
|
||||
if bind_rc != 0:
|
||||
return bind_rc
|
||||
common = [
|
||||
"--message", args.message,
|
||||
"--actor", args.actor,
|
||||
@@ -51,8 +108,8 @@ def ask(args) -> int:
|
||||
"--tenant", args.tenant,
|
||||
]
|
||||
if router.get("mode") == "OPERATOR":
|
||||
return run_and_passthrough(["python3", OPERATOR, "run", *common])
|
||||
return run_and_passthrough(["python3", READONLY, "ask", *common])
|
||||
return run_mode(["python3", OPERATOR, "run", *common], binding)
|
||||
return run_mode(["python3", READONLY, "ask", *common], binding)
|
||||
|
||||
|
||||
def verify_audit() -> int:
|
||||
@@ -65,9 +122,14 @@ def main() -> int:
|
||||
askp = sub.add_parser("ask")
|
||||
askp.add_argument("--message", required=True)
|
||||
askp.add_argument("--actor", default="anonymous")
|
||||
askp.add_argument("--role", default="viewer")
|
||||
askp.add_argument("--project", default="default")
|
||||
askp.add_argument("--chat-id", default="")
|
||||
askp.add_argument("--turn-id", default="")
|
||||
askp.add_argument("--tenant", default="default")
|
||||
askp.add_argument("--agent", default="")
|
||||
askp.add_argument("--skill", default="")
|
||||
askp.add_argument("--delegation-level", type=int, default=0)
|
||||
askp.set_defaults(func=ask)
|
||||
sub.add_parser("verify-audit").set_defaults(func=lambda _args: verify_audit())
|
||||
args = ap.parse_args()
|
||||
|
||||
Reference in New Issue
Block a user