Files
ciregenzandClaude Opus 4.6 b6f45e8412 [eric] multi-provider agent loop, PostHog analytics, settings overhaul
Multi-provider support (WIP - not fully tested):
- Owned agent loop replacing claude_agent_sdk (agent_loop.py, mcp_client.py)
- Provider adapters: Anthropic (native), OpenAI-compat (any endpoint), Gemini (native + schema cleaning)
- 19 models across 9 providers (Anthropic, OpenAI, Google, xAI, Meta, DeepSeek, Mistral, Qwen, Cohere)
- OpenRouter integration for 300+ models via single API key
- Builtin tool reimplementations (Read, Write, Edit, Glob, Grep, Bash, WebSearch, WebFetch, AskUserQuestion)
- Standalone MCP client manager (stdio/sse/http)
- Frontend: grouped model dropdown, provider selection, dynamic context windows

Analytics (tested):
- PostHog integration as single analytics source
- Tracks: app.opened, session.started/completed, tool.called, tool.approval_resolved, error.occurred
- Rich session data: user messages, assistant messages, session titles, tools used, MCP servers, task categories
- PostHog dashboard with 14 insights created via API
- Usage stats in Settings (Usage tab) with pixel-art bars

Settings (tested):
- 4 tabs: General, Models, Usage, Commands
- Model Providers tab with OpenRouter (recommended), Anthropic, OpenAI, Google key fields
- "Get key" links for each provider
- Usage tab with session/cost/tool stats + analytics opt-in toggle
- analytics_opt_in defaults to true, installation_id auto-generated

Merged haik/updates-v1 (tested):
- Sub-agent spawning, chat branching, browser control improvements
- Settings: auto_select_mode, expand_new_chats, auto_reveal_sub_agents, dev_mode

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:50:21 -07:00

79 lines
3.0 KiB
Python

from pydantic import BaseModel, Field
from typing import Optional, Literal, Any
from datetime import datetime
from uuid import uuid4
class AgentConfig(BaseModel):
name: str = Field(default_factory=lambda: f"Agent-{uuid4().hex[:6]}")
provider: str = "anthropic"
model: str = "sonnet"
mode: str = "agent"
system_prompt: Optional[str] = None
allowed_tools: list[str] = Field(default_factory=lambda: ["Read", "Edit", "Write", "Bash", "Glob", "Grep", "AskUserQuestion"])
max_turns: Optional[int] = None
target_directory: Optional[str] = None # if None, uses repo root
dashboard_id: Optional[str] = None
class ApprovalRequest(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
session_id: str
tool_name: str
tool_input: dict[str, Any]
created_at: datetime = Field(default_factory=datetime.now)
class ApprovalResponse(BaseModel):
request_id: str
behavior: Literal["allow", "deny"]
message: Optional[str] = None
updated_input: Optional[dict[str, Any]] = None
class Message(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
role: Literal["user", "assistant", "tool_call", "tool_result", "system"]
content: Any # str or list of content blocks
timestamp: datetime = Field(default_factory=datetime.now)
branch_id: str = "main"
parent_id: Optional[str] = None
context_paths: Optional[list[dict]] = None
attached_skills: Optional[list[dict]] = None
forced_tools: Optional[list[str]] = None
images: Optional[list[dict]] = None
hidden: bool = False
class MessageBranch(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
parent_branch_id: Optional[str] = None
fork_point_message_id: Optional[str] = None
created_at: datetime = Field(default_factory=datetime.now)
class ToolGroupMeta(BaseModel):
id: str
name: str
svg: str = ""
is_refined: bool = False
class AgentSession(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
name: str
status: Literal["running", "waiting_approval", "completed", "error", "stopped"] = "running"
provider: str = "anthropic"
model: str = "sonnet"
mode: str = "agent"
sdk_session_id: Optional[str] = None
system_prompt: Optional[str] = None
allowed_tools: list[str] = Field(default_factory=list)
max_turns: Optional[int] = None
cwd: Optional[str] = None
created_at: datetime = Field(default_factory=datetime.now)
closed_at: Optional[datetime] = None
cost_usd: float = 0.0
tokens: dict[str, int] = Field(default_factory=lambda: {"input": 0, "output": 0})
messages: list[Message] = Field(default_factory=list)
pending_approvals: list[ApprovalRequest] = Field(default_factory=list)
branches: dict[str, "MessageBranch"] = Field(default_factory=lambda: {"main": MessageBranch(id="main")})
active_branch_id: str = "main"
tool_group_meta: dict[str, "ToolGroupMeta"] = Field(default_factory=dict)
dashboard_id: Optional[str] = None
browser_id: Optional[str] = None
parent_session_id: Optional[str] = None