feat(server): integrate MCP queries and investigation messaging
Add project-bound OAuth MCP queries and controlled analysis with scoped evidence, limits, timeouts and a nearby-model spatial query using EPSG:4326 inputs. Default nearby searches to 200 meters and five results. Integrate investigation messaging/presentation, health/readiness and typed history; remove duplicate web search and fix online simulation and compressed baseline writes. Include project migrations 023-025; keep local credentials and demo seeding excluded. Validation: 319 focused pytest checks; real PostGIS temporary-table regression; read-only Jinqian bridge radius comparison; cross-language MCP discovery/calls.
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"""Actor-private messaging APIs; the Agent verifies its session ownership."""
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from psycopg import AsyncConnection
|
||||
|
||||
from app.auth.permissions import OPERATIONAL_TASK_VIEW, OPERATIONAL_TASK_WRITE, require_permission
|
||||
from app.auth.project_dependencies import ProjectContext, get_project_pg_connection
|
||||
from app.domain.domain_errors import DomainError
|
||||
from app.domain.schemas.agent_messages import (
|
||||
AgentMessage, AgentMessageCreate, AgentMessageDelivery, AgentMessagePage, AgentMessageReply,
|
||||
)
|
||||
from app.infra.db.postgresql.agent_messages import AgentMessageRepository
|
||||
from app.services.agent_messages import AgentMessageService
|
||||
|
||||
|
||||
router = APIRouter(prefix="/agent-messages")
|
||||
|
||||
|
||||
def _service(ctx: ProjectContext, conn: AsyncConnection) -> AgentMessageService:
|
||||
return AgentMessageService(AgentMessageRepository(conn, ctx.project_id), ctx)
|
||||
|
||||
|
||||
def _raise_domain(exc: DomainError):
|
||||
raise HTTPException(status_code=exc.status, detail=exc.payload()) from exc
|
||||
|
||||
|
||||
@router.post("", response_model=AgentMessage, status_code=201)
|
||||
async def create_agent_message(
|
||||
command: AgentMessageCreate,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_WRITE)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).create(command)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.get("", response_model=AgentMessagePage)
|
||||
async def list_agent_messages(
|
||||
session_id: str = Query(min_length=1, max_length=500),
|
||||
limit: int = Query(default=50, ge=1, le=100),
|
||||
cursor: str | None = None,
|
||||
pending_only: bool = False,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).list_session(session_id, limit, cursor, pending_only)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.get("/pending", response_model=AgentMessagePage)
|
||||
async def list_pending_agent_messages(
|
||||
limit: int = Query(default=50, ge=1, le=100),
|
||||
cursor: str | None = None,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).pending(limit, cursor)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.get("/{message_id}", response_model=AgentMessage)
|
||||
async def get_agent_message(
|
||||
message_id: UUID,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).get(message_id)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.post("/{message_id}/reply", response_model=AgentMessage, status_code=201)
|
||||
async def reply_agent_message(
|
||||
message_id: UUID, command: AgentMessageReply,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_WRITE)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).reply(message_id, command)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.post("/{message_id}/delivery", response_model=AgentMessage)
|
||||
async def record_agent_message_delivery(
|
||||
message_id: UUID, command: AgentMessageDelivery,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).delivery(message_id, command)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
Reference in New Issue
Block a user