Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/web/vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"git": {
"deploymentEnabled": {
"fix/2168-mcp-routed-false-success": false
}
}
}
38 changes: 32 additions & 6 deletions src/agents/mcp_agent_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@

logger = logging.getLogger(__name__)

# Status for a route the network knows about but cannot execute. It always
# carries an "error" key so every caller that checks `"error" in result`
# (e.g. VideoPipelineOrchestrator._execute_agent_stage) treats it as failure.
NOT_IMPLEMENTED = "not_implemented"


def not_implemented_result(route: str, action: str, reason: str) -> dict[str, Any]:
"""Explicit failure for a tool route that was not executed."""
return {
"status": NOT_IMPLEMENTED,
"executed": False,
"route": route,
"action": action,
"error": f"{route} {action}: not implemented ({reason}); nothing was executed",
}

# Agent Network Configuration
AGENT_NETWORK_CONFIG = Path(__file__).parent.parent.parent / "config" / "agent_network.json"

Expand Down Expand Up @@ -204,19 +220,30 @@ async def _call_internal(self, endpoint: str, action: str, payload: dict) -> dic
return await self._call_knowledge_base(action, payload)
except Exception as e:
return {"error": f"Internal tool error: {str(e)}"}
return not_implemented_result(endpoint, action, f"no internal module handler for '{module_name}'")

async def _call_mcp(self, endpoint: str, action: str, payload: dict) -> dict:
"""Call MCP server via stdio/protocol"""
server_name = endpoint.replace("mcp://", "")
logger.info(f"MCP call: {server_name}.{action}")
return {"status": "routed", "server": server_name, "action": action}
# No MCP client/transport is wired into this network. Previously this
# returned {"status": "routed"} without doing anything, so the
# pipeline's deploy step (create_repo via mcp://github) reported
# success without deploying. Report the truth instead.
logger.error(
"MCP route not implemented: %s.%s was not executed",
server_name,
action,
extra={"route": endpoint, "action": action, "status": NOT_IMPLEMENTED},
)
return not_implemented_result(endpoint, action, "no MCP client transport is configured")

async def _call_http(self, endpoint: str, action: str, payload: dict) -> dict:
"""Call HTTP endpoint"""
logger.info(f"HTTP call: {endpoint}/{action}")

if endpoint.startswith("https://api.vercel.com"):
return await self._call_vercel_api(action, payload)
return not_implemented_result(endpoint, action, "no HTTP handler for this endpoint")

def get_pipeline_agents(self) -> list[str]:
"""Get ordered list of agents for video-to-software pipeline"""
Expand All @@ -242,7 +269,7 @@ async def _call_skill_builder(self, action: str, payload: dict) -> dict:
elif action == "suggest_fix":
return await build_validator.suggest_fix(**payload)
else:
return {"status": "pending_implementation", "action": action}
return not_implemented_result("internal://skill_builder", action, "no skill builder handler")

except Exception as e:
logger.error(f"Build validator error: {e}")
Expand Down Expand Up @@ -302,7 +329,7 @@ async def _call_knowledge_base(self, action: str, payload: dict) -> dict:
return {"status": "success", "stats": stats}

else:
return {"status": "unknown_action", "action": action}
return not_implemented_result("internal://knowledge_base", action, "no knowledge base handler")

except Exception as e:
logger.error(f"Knowledge base error: {e}")
Expand All @@ -326,8 +353,7 @@ async def _call_vercel_api(self, action: str, payload: dict) -> dict:
return await deployment_tool.deploy_to_github_and_vercel(**payload)

else:
# TODO: Implement other deployment features
return {"status": "pending_implementation", "action": action}
return not_implemented_result("https://api.vercel.com", action, "no deployment handler")

except Exception as e:
logger.error(f"Deployment error: {e}")
Expand Down
109 changes: 89 additions & 20 deletions src/agents/pipeline_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,34 @@ def filter(self, record):

# --- VERA imports (lazy-loaded to avoid import-time side effects) ---

class VeraInitError(RuntimeError):
"""VERA modules are installed but failed to initialize."""


# Stage results with one of these statuses are failures even without an
# "error" key (e.g. DeploymentMCPTool returns status "failed").
FAILED_STATUSES = frozenset({"failed", "error", "not_implemented"})


def _stage_failure_reason(result: Any) -> str | None:
"""Return why a routed stage result is a failure, or None if it succeeded."""
if not isinstance(result, dict):
return f"stage returned {type(result).__name__}, not a result dict"
if "error" in result:
return str(result["error"])
status = result.get("status")
if status in FAILED_STATUSES:
return f"stage reported status '{status}'"
return None


def _get_vera():
"""Lazy-load VERA components. Returns None for each component if VERA is unavailable."""
"""Lazy-load VERA components.

Returns None when the VERA modules are not installed. Raises VeraInitError
when they are installed but fail to initialize, so the orchestrator can
fail closed instead of silently running without its security layer.
"""
try:
from vera.identity import get_identity_service
from vera.firewall import get_firewall
Expand All @@ -81,12 +107,17 @@ def _get_vera():
"maturity": get_maturity_runtime(),
"enforcer": get_enforcer(),
}
except ImportError:
logger.info("VERA modules not available in this env (hardened local maturity still applied for pipeline agents)")
return None
except Exception as e:
logger.error(f"VERA initialization failed: {e} — running without security layer")
return None
if isinstance(e, ModuleNotFoundError) and e.name == "vera":
logger.info("VERA modules not available in this env (hardened local maturity still applied for pipeline agents)")
return None
logger.error(
"VERA initialization failed (%s): %s — all stages will be blocked",
type(e).__name__,
e,
exc_info=True,
)
raise VeraInitError(f"{type(e).__name__}: {e}") from e
Comment thread
vercel[bot] marked this conversation as resolved.
Resolved
Comment thread
groupthinking marked this conversation as resolved.
Resolved

# Local hardened maturity for pipeline agents (always-on fallback / hardening when full VERA unavailable)
# This ensures code-gen, deployer etc. are treated as level 1+ for security intent.
Expand Down Expand Up @@ -176,7 +207,13 @@ def __init__(self):
self.emitter = get_emitter()

# VERA security layer (None if VERA modules unavailable)
self._vera = _get_vera()
self._vera_init_error: str | None = None
try:
self._vera = _get_vera()
except VeraInitError as exc:
# Fail closed: VERA is present but broken, so no stage may run.
self._vera = None
self._vera_init_error = str(exc)
self._vera_credentials: dict[str, Any] = {} # agent_id → (token, credential)
if self._vera:
logger.info("VERA security layer active — all agent stages will be verified")
Expand Down Expand Up @@ -419,6 +456,15 @@ async def _execute_agent_stage(self, agent_id: str) -> PipelineResult:
action, payload = self._prepare_agent_action(agent_id)

# --- VERA pre-execution checks ---
vera_init_error = getattr(self, "_vera_init_error", None)
if vera_init_error:
blocked = PipelineResult(
agent_id, False, {}, (time.monotonic() - start_time) * 1000,
f"VERA: security layer failed to initialize ({vera_init_error}) — stage blocked (fail-closed)",
)
self._record_audit_stage(agent_id, action, blocked)
return blocked

if self._vera:
vera_check = self._vera_pre_check(agent_id, agent.name, action, payload)
if vera_check is not None:
Expand All @@ -442,20 +488,25 @@ async def _execute_agent_stage(self, agent_id: str) -> PipelineResult:

duration = (time.monotonic() - start_time) * 1000

if "error" in result:
failure_reason = _stage_failure_reason(result)
if failure_reason is not None:
Comment thread
groupthinking marked this conversation as resolved.
Resolved
# --- VERA: record failure proof + notify enforcer ---
if self._vera:
proof_result = dict(result) if isinstance(result, dict) else {}
proof_result.setdefault("error", failure_reason)
self._vera_record_proof(
agent_id, action, payload, result, duration,
agent_id, action, payload, proof_result, duration,
authorized=True, success=False,
)
self._vera["enforcer"].on_enforcement_event(
agent_id, "stage_error",
details=f"Stage {agent_id} returned error: {result.get('error', '')}",
details=f"Stage {agent_id} returned error: {failure_reason}",
)
self._vera["breakers"].get_breaker(agent_id).record_failure("error")

failed = PipelineResult(agent_id, False, result, duration, result["error"])
failed = PipelineResult(
agent_id, False, result if isinstance(result, dict) else {}, duration, failure_reason
)
self._record_audit_stage(agent_id, action, failed)
return failed

Expand Down Expand Up @@ -534,8 +585,12 @@ def _vera_pre_check(
maturity_level=initial_level,
)
self._vera_credentials[agent_id] = (token, credential)
except Exception:
self._vera_credentials[agent_id] = (None, None)
except Exception as exc: # exception-boundary: identity check failed; fail closed (block the stage)
logger.error(
"VERA credential issuance failed for %s (%s) — stage blocked",
agent_id, type(exc).__name__, exc_info=True,
)
return f"VERA: credential issuance failed ({type(exc).__name__}) — stage blocked (fail-closed)"
else:
self._vera_credentials[agent_id] = (None, None)

Expand All @@ -548,8 +603,12 @@ def _vera_pre_check(
verified = v["identity"].verify_credential(token) if token else None
if verified is None and not (v["identity"].is_revoked(agent_id) if hasattr(v["identity"], "is_revoked") else False):
maturity_level = _get_effective_maturity(agent_id)
except Exception:
pass
except Exception as exc: # exception-boundary: identity check failed; fail closed (block the stage)
Comment thread
groupthinking marked this conversation as resolved.
Resolved
logger.error(
"VERA credential verification failed for %s (%s) — stage blocked",
agent_id, type(exc).__name__, exc_info=True,
)
return f"VERA: credential verification failed ({type(exc).__name__}) — stage blocked (fail-closed)"

# 3. Gateway permission check (hardened: use effective level even without full VERA)
decision = None
Expand All @@ -568,12 +627,15 @@ def _vera_pre_check(
)
breaker.record_failure("auth_failure")
return f"VERA: Permission denied — {decision.reason}"
except Exception as exc:
logger.warning(
"VERA gateway check failed (non-blocking) for %s: %s",
except Exception as exc: # exception-boundary: gateway check failed; fail closed (block the stage)
logger.error(
"VERA gateway check failed for %s (%s): %s — stage blocked",
agent_id,
type(exc).__name__,
exc,
exc_info=True,
)
return f"VERA: gateway check failed ({type(exc).__name__}) — stage blocked (fail-closed)"
else:
# No full VERA: log once per agent but allow (hardened local maturity already applied above)
if not hasattr(self, "_vera_harden_logged"):
Expand Down Expand Up @@ -605,8 +667,15 @@ def _vera_pre_check(
details=f"Blocked input for {action}: {scan.details}",
)
return f"VERA: Input blocked by firewall — {scan.threat_level.value} threat"
except Exception as e:
logger.warning(f"VERA firewall scan error (non-blocking): {e}")
except Exception as e: # exception-boundary: firewall scan failed; fail closed (block the stage)
logger.error(
"VERA firewall scan failed for %s (%s): %s — stage blocked",
agent_id,
type(e).__name__,
e,
exc_info=True,
)
return f"VERA: firewall scan failed ({type(e).__name__}) — stage blocked (fail-closed)"

return None # All checks passed

Expand Down
Loading
Loading