feat: add agent visibility APIs and harden runtime verification

Add Day 4 visibility endpoints and response models, strengthen collaboration/task verification behavior, and patch conversation schema startup migration for agent_state compatibility. Extend backend regression coverage for runtime schemas, verifier behavior, visibility APIs, router auth, and legacy conversation list loading.
This commit is contained in:
2026-04-04 00:56:03 +08:00
parent aa0ef0fbea
commit a7b6b5eb90
24 changed files with 2986 additions and 111 deletions

View File

@@ -1,10 +1,25 @@
from app.agents.schemas.event import AgentEvent
from app.agents.schemas.task import AgentTask, TaskResult, TaskLifecycleStatus, VerificationStatus
from app.agents.schemas.message import AgentMessage
from app.agents.schemas.task import (
AgentTask,
CollaborationBudget,
InterruptRecord,
RecoveryRecord,
TaskLifecycleStatus,
TaskResult,
TaskResultStatus,
VerificationStatus,
)
__all__ = [
"AgentEvent",
"AgentMessage",
"AgentTask",
"CollaborationBudget",
"InterruptRecord",
"RecoveryRecord",
"TaskLifecycleStatus",
"TaskResult",
"TaskResultStatus",
"VerificationStatus",
]

View File

@@ -11,6 +11,18 @@ AgentEventType = Literal[
"agent.tool.result",
"agent.verify.started",
"agent.verify.completed",
"agent.created",
"agent.spawn.blocked",
"agent.message.sent",
"agent.message.received",
"agent.interrupt.requested",
"agent.interrupt.completed",
"agent.recovery.started",
"agent.recovery.completed",
"agent.task.interrupted",
"agent.task.recovered",
"agent.task.reassigned",
"agent.collaboration.budget.updated",
"agent.error",
]
AgentEventSeverity = Literal["info", "warning", "error"]
@@ -24,5 +36,11 @@ class AgentEvent(BaseModel):
agent_id: str | None = None
sub_commander_id: str | None = None
task_id: str | None = None
parent_task_id: str | None = None
child_task_id: str | None = None
thread_id: str | None = None
message_id: str | None = None
interrupt_id: str | None = None
recovery_id: str | None = None
payload: dict[str, Any] = Field(default_factory=dict)
severity: AgentEventSeverity = "info"

View File

@@ -0,0 +1,29 @@
from __future__ import annotations
from datetime import datetime, timezone
from typing import Any, Literal
from pydantic import BaseModel, Field
AgentMessageType = Literal[
"task_request",
"task_update",
"handoff",
"verification_request",
"verification_feedback",
"interrupt_notice",
]
class AgentMessage(BaseModel):
message_id: str
thread_id: str
from_agent_id: str
to_agent_id: str
task_id: str | None = None
reply_to_message_id: str | None = None
message_type: AgentMessageType = "task_update"
content_summary: str
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
payload: dict[str, Any] = Field(default_factory=dict)

View File

@@ -8,6 +8,41 @@ from pydantic import BaseModel, Field
TaskLifecycleStatus = Literal["pending", "in_progress", "completed", "failed", "blocked"]
VerificationStatus = Literal["passed", "failed", "skipped"]
TaskResultStatus = Literal["completed", "failed", "blocked", "passed", "skipped"]
InterruptStatus = Literal["requested", "acknowledged", "resolved"]
BudgetMode = Literal["direct", "collaboration"]
class InterruptRecord(BaseModel):
interrupt_id: str
reason: str
status: InterruptStatus = "requested"
requested_by: str | None = None
source_event_id: str | None = None
requested_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
payload: dict[str, Any] = Field(default_factory=dict)
class RecoveryRecord(BaseModel):
recovery_id: str
source_interrupt_id: str | None = None
strategy: str | None = None
resumed_from_task_id: str | None = None
resumed_from_thread_id: str | None = None
recovered_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
payload: dict[str, Any] = Field(default_factory=dict)
class CollaborationBudget(BaseModel):
mode: BudgetMode = "direct"
max_parallel_tasks: int | None = None
remaining_parallel_tasks: int | None = None
max_tool_calls: int | None = None
remaining_tool_calls: int | None = None
max_iterations: int | None = None
remaining_iterations: int | None = None
escalation_threshold: int | None = None
metadata: dict[str, Any] = Field(default_factory=dict)
class AgentTask(BaseModel):
@@ -17,8 +52,16 @@ class AgentTask(BaseModel):
owner_agent_id: str | None = None
role: str | None = None
goal: str | None = None
parent_task_id: str | None = None
child_task_ids: list[str] = Field(default_factory=list)
thread_id: str | None = None
message_id: str | None = None
message_index: int | None = None
expected_evidence: list[dict[str, Any]] = Field(default_factory=list)
evidence: list[dict[str, Any]] = Field(default_factory=list)
interrupt_records: list[InterruptRecord | dict[str, Any]] = Field(default_factory=list)
recovery_records: list[RecoveryRecord | dict[str, Any]] = Field(default_factory=list)
collaboration_budget: CollaborationBudget | dict[str, Any] | None = None
result_summary: str | None = None
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
@@ -26,7 +69,17 @@ class AgentTask(BaseModel):
class TaskResult(BaseModel):
task_id: str
status: VerificationStatus
status: TaskResultStatus
summary: str | None = None
evidence: list[dict[str, Any]] = Field(default_factory=list)
owner_agent_id: str | None = None
parent_task_id: str | None = None
child_task_ids: list[str] = Field(default_factory=list)
thread_id: str | None = None
message_id: str | None = None
message_index: int | None = None
interrupt_records: list[InterruptRecord | dict[str, Any]] = Field(default_factory=list)
recovery_records: list[RecoveryRecord | dict[str, Any]] = Field(default_factory=list)
budget_snapshot: CollaborationBudget | dict[str, Any] | None = None
next_action: str | None = None
output_data: dict[str, Any] | None = None