@dataclass(frozen=True)
class ToolRequest:
tool_name: str # Name of the tool to call
arguments: dict[str, Any] # Tool arguments
call_id: str # Unique call identifier (uuid4 hex, auto-generated)
reasoning: str | None = None # Optional model reasoning for the callImmutable. call_id defaults to uuid4().hex if not provided.
@dataclass(frozen=True)
class FinalAnswer:
content: str # The model's final textual answer
metadata: dict[str, Any] # Optional metadata dict (default: {})When the decide() function returns this type, the run loop terminates with status='completed'.
Decision = Union[ToolRequest, FinalAnswer]The return type of DecisionFn. Exactly one of ToolRequest or FinalAnswer.
DecisionFn = Callable[[dict[str, Any]], Decision]Pre-conditions:
- Receives
context: dict[str, Any]with at minimum:context['task']: the task stringcontext['observations']: list of previous tool call observations
- May read
context.get('decision_summary'),context.get('compacted_summary'),context.get('memory_keys').
Post-conditions:
- Must return either a
FinalAnsweror aToolRequest. - May set
context['_cost_cents'],context['_input_tokens'],context['_output_tokens']for budget tracking.
@dataclass(frozen=True)
class ApprovalRequest:
call_id: str
tool_name: str
arguments: dict[str, Any]
reason: str
annotations: dict[str, bool] # {'read_only', 'destructive', 'idempotent'}
run_id: Optional[str] = None
workspace_secret: Optional[bytes] = NoneMethod:
def to_dict(self) -> dict[str, Any]Returns serializable dict with argument_digest (HMAC-SHA256 if workspace_secret is set, SHA256 prefix otherwise) and argument_digest_version ('v2' or 'v1'). Arguments are redacted via redact_tool_arguments().
ApprovalHandler = Callable[[ApprovalRequest], bool]Called when a tool call requires human-in-the-loop approval. Must return True (approved) or False (denied).
BudgetPromptHandler = Callable[[dict[str, Any]], bool]Called when cost reaches 90% of budget. Receives a payload dict. Must return True to continue or False to cancel.
@dataclass(frozen=True)
class RunResult:
run_id: str
final_answer: Optional[FinalAnswer] # None on failure or pending_approval
iterations: int # Total iterations executed
tool_calls: int # Total tool call attempts (including failures)
status: str # See status values below
metadata: dict[str, Any] # Additional metadata
error_message: Optional[str] # Set on failure
cost_cents: float # Accumulated cost in cents
input_tokens: int # Accumulated input tokens
output_tokens: int # Accumulated output tokensStatus values:
Actual status strings are constructed from the ErrorCategory enum (teaagent/errors.py):
| Value | Meaning |
|---|---|
'completed' |
FinalAnswer returned by decide |
'pending_approval' |
Run paused, waiting for human approval |
'failed:model_logic' |
Iteration budget exhausted, model logic error, or budget exceeded |
'failed:permission' |
Tool was blocked and cannot be approved |
'failed:system' |
Unexpected exception or run cancelled |
'failed:transient' |
Transient error (retryable) |
Module: teaagent.runner._core
The main run loop orchestrator.
AgentRunner(
*,
registry: ToolRegistry,
audit: AuditLogger,
budget: Optional[RunBudget] = None,
approval_policy: Optional[ApprovalPolicy] = None,
approval_handler: Optional[ApprovalHandler] = None,
budget_prompt_handler: Optional[BudgetPromptHandler] = None,
budget_monitor: Optional[BudgetMonitor] = None,
compactor: Optional[ContextCompactor] = None,
compact_after_observations: int = 20,
compaction_warning_threshold: float = 0.6,
max_context_tokens: int = 200000,
checkpoint_store: Any = None,
cancel_token: Optional[threading.Event] = None,
file_policy: Optional[FilePolicy] = None,
auto_mode_config: Optional[AutoModeConfig] = None,
jit_state: Optional[JITApprovalState] = None,
workspace_root: Optional[Path] = None,
require_plan: bool = False,
skip_plan_check: bool = False,
show_summary: bool = True,
scratchpad: Any = None,
decision_log: Any = None,
) -> NonePre-conditions:
registrymust be a validToolRegistry.auditmust be a validAuditLogger.budget.validate()must pass (called in__init__).
Post-conditions:
self.approval_manageris initialized.self.plan_validatoris initialized.self.auto_mode_manageris initialized.- If
workspace_rootis provided,load_plugins(registry)is called. - If
permission_mode == READ_ONLY, lint errors are collected and stored onplan_validator.
Raises:
- Any exception from
budget.validate()if budget is invalid.
def run(
self,
*,
task: str,
decide: DecisionFn,
run_id: Optional[str] = None,
initial_observations: Optional[list[dict[str, Any]]] = None,
initial_context_extra: Optional[dict[str, Any]] = None,
run_started_extra: Optional[dict[str, Any]] = None,
) -> RunResultPre-conditions:
taskmust be a non-empty string.decidemust be callable and returnDecision.- If
run_idis provided, it should be unique to avoid audit record collisions.
Post-conditions:
- Always returns a
RunResult; never raises. - Exactly one terminal audit event (
run_completed,run_failed, orrun_paused) is recorded. RunResult.run_idequalsrun_idif provided, or a newuuid4().hex.
Raises: Nothing (all exceptions are caught internally).
Internal helper. Not part of the public runner package API (not re-exported from __init__.py).
Returns True only if destructive=True and permission_mode == PROMPT.
Builds an ApprovalRequest with an optional workspace HMAC secret for v2 digest.
Emits tool_call_pending_approval audit event. Calls approval_handler if set. Returns True if approved, False otherwise. Records run_paused and saves checkpoint when no handler is set.
Emits tool_call_blocked audit event.
Internal helper.
Raises: ToolPermissionError if auto mode is active and tool_name is not allowed.
Returns ApprovalPolicy(permission_mode=DANGER_FULL_ACCESS, allow_all_destructive=True, full_access_acknowledged=True) when auto mode is active, None otherwise. The AutoModeGuard still scopes which tools can use that temporary policy.
Returns auto mode activity summary from AutoModeGuard.summary().
Internal helper.
Delegates to governance.plan_gate.assert_write_allowed. Raises ToolPermissionError if write is not allowed under the current plan contract.
Returns an error string (blocking message) if permission_mode == READ_ONLY and lint errors are present, otherwise None.