feat: research agent

This commit is contained in:
2026-07-19 19:36:49 +02:00
parent 8eeac8f76f
commit a52ebabf7f
8 changed files with 117 additions and 3 deletions
+35
View File
@@ -26,10 +26,15 @@ class CodexClient:
codex_home: Path,
schemas_dir: Path,
timeout_seconds: int,
research_model: str,
research_reasoning: str,
context7_api_key: str | None,
) -> None:
self.codex_home = codex_home
self.schemas_dir = schemas_dir
self.timeout_seconds = timeout_seconds
self.context7_api_key = context7_api_key
self._write_research_agent(research_model, research_reasoning)
async def login_ready(self) -> bool:
try:
@@ -174,8 +179,38 @@ class CodexClient:
allowed = {"PATH", "LANG", "LC_ALL", "SSL_CERT_FILE", "CODEX_CA_CERTIFICATE"}
environment = {key: value for key, value in os.environ.items() if key in allowed}
environment["CODEX_HOME"] = str(self.codex_home)
if self.context7_api_key:
environment["CONTEXT7_API_KEY"] = self.context7_api_key
return environment
def _write_research_agent(self, model: str, reasoning: str) -> None:
agents_dir = self.codex_home / "agents"
agents_dir.mkdir(parents=True, exist_ok=True)
agent = f'''name = "research"
description = "Research specialist for current docs, web evidence, and public code examples."
model = {json.dumps(model)}
model_reasoning_effort = {json.dumps(reasoning)}
default_permissions = "agentci-research"
web_search = "live"
developer_instructions = """
Research external, current, or unfamiliar technical facts for the parent agent.
Use Context7 for library documentation, gh_grep for real public-code examples, and web search for
primary sources or broader verification. Prefer authoritative sources, report links, distinguish
facts from inference, and return a concise evidence-focused summary. You may inspect the workspace
but must not modify it. Never include secrets or proprietary source in external queries.
"""
[mcp_servers.context7]
url = "https://mcp.context7.com/mcp"
[mcp_servers.context7.env_http_headers]
CONTEXT7_API_KEY = "CONTEXT7_API_KEY"
[mcp_servers.gh_grep]
url = "https://mcp.grep.app"
'''
(agents_dir / "research.toml").write_text(agent)
def _session_id(output: str) -> str | None:
for line in output.splitlines():
+4 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from functools import cached_property
from pathlib import Path
from pydantic import Field, field_validator
from pydantic import Field, SecretStr, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -30,6 +30,9 @@ class Settings(BaseSettings):
plan_reasoning: str = "medium"
implement_model: str = "gpt-5.6-sol"
implement_reasoning: str = "high"
research_model: str = "gpt-5.6-luna"
research_reasoning: str = "high"
context7_api_key: SecretStr | None = None
plan_review_rounds: int = Field(default=4, ge=1, le=20)
implement_review_rounds: int = Field(default=3, ge=1, le=20)
turn_timeout_seconds: int = Field(default=3600, ge=60)
+7
View File
@@ -53,6 +53,13 @@ async def build_container(settings: Settings) -> Container:
codex_home=settings.codex_home,
schemas_dir=package_dir / "prompts" / "schemas",
timeout_seconds=settings.turn_timeout_seconds,
research_model=settings.research_model,
research_reasoning=settings.research_reasoning,
context7_api_key=(
settings.context7_api_key.get_secret_value()
if settings.context7_api_key is not None
else None
),
)
prompts = PromptLibrary()
context = ContextBuilder(gitea, storage)