Add Phase 7: polish and hardening — retry, truncation, sessions, shutdown
- Config extensions: retry backoff, truncation threshold, session persistence - LLM retry with exponential backoff + jitter on transient errors (5xx, connection) - Conversation truncation: drops oldest messages preserving first user + recent N - Session persistence: auto-save/restore with atomic writes, cleanup of old files - Graceful shutdown: SIGTERM handler, cancel() on AgentLoop, save-on-exit - Partial message recovery on mid-stream interruption - New slash commands: /save, /session - 18 new tests (5 retry, 5 truncation, 4 session, 4 integration workflows) - README.md and docs/tools.md documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,9 @@ class LLMConfig(BaseModel):
|
||||
temperature: float = Field(default=0.1, description="Sampling temperature")
|
||||
max_tokens: int = Field(default=4096, description="Maximum tokens in LLM response")
|
||||
timeout: int = Field(default=120, description="Request timeout in seconds")
|
||||
max_retries: int = Field(default=3, description="Max retry attempts on transient errors")
|
||||
retry_backoff_base: float = Field(default=1.0, description="Base seconds for exponential backoff")
|
||||
retry_backoff_max: float = Field(default=30.0, description="Maximum backoff seconds")
|
||||
|
||||
|
||||
class AgentConfig(BaseModel):
|
||||
@@ -28,6 +31,12 @@ class AgentConfig(BaseModel):
|
||||
workspace_root: Path = Field(
|
||||
default=Path("."), description="Root directory for file operations"
|
||||
)
|
||||
truncation_keep_recent: int = Field(
|
||||
default=10, description="Number of recent messages to preserve during truncation"
|
||||
)
|
||||
truncation_threshold: float = Field(
|
||||
default=0.85, description="Token budget fraction that triggers truncation"
|
||||
)
|
||||
|
||||
|
||||
class PermissionsConfig(BaseModel):
|
||||
@@ -60,6 +69,19 @@ class ToolsConfig(BaseModel):
|
||||
filesystem: FilesystemToolConfig = Field(default_factory=FilesystemToolConfig)
|
||||
|
||||
|
||||
class SessionConfig(BaseModel):
|
||||
"""Session persistence configuration."""
|
||||
|
||||
session_dir: Path = Field(
|
||||
default=Path(".sneakycode/sessions"), description="Directory for session files"
|
||||
)
|
||||
auto_save: bool = Field(default=True, description="Auto-save session after each turn")
|
||||
max_session_age_hours: int = Field(
|
||||
default=72, description="Max age in hours before session files are cleaned up"
|
||||
)
|
||||
offer_resume: bool = Field(default=True, description="Offer to resume previous sessions on startup")
|
||||
|
||||
|
||||
class DisplayConfig(BaseModel):
|
||||
"""Terminal display preferences."""
|
||||
|
||||
@@ -76,6 +98,7 @@ class AppConfig(BaseModel):
|
||||
permissions: PermissionsConfig = Field(default_factory=PermissionsConfig)
|
||||
tools: ToolsConfig = Field(default_factory=ToolsConfig)
|
||||
display: DisplayConfig = Field(default_factory=DisplayConfig)
|
||||
session: SessionConfig = Field(default_factory=SessionConfig)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def resolve_workspace_root(self) -> "AppConfig":
|
||||
|
||||
Reference in New Issue
Block a user