--- title: Hosted Runtime summary: Maintainer guide to TerraFin's current hosted runtime, transcript-first session storage, adapters, and regression surfaces. read_when: - Maintaining the hosted agent loop - Changing runtime endpoints, transcript storage, tool adapters, or the browser widget - Verifying which files define the current hosted runtime behavior --- # Hosted Runtime This document is the implementation-focused companion to [architecture.md](./architecture.md). It answers a narrower question: > What is actually implemented today for TerraFin's hosted runtime, and where do > the important seams live? !!! note "Reference Boundary" TerraFin's transcript-first session persistence follows the same core shape used by OpenClaw and Claude Code: append-only per-session transcripts with a separate session index and explicit rewrite paths. TerraFin's runtime controller, financial capability kernel, task/approval flow, widget, and API integration remain TerraFin-specific. The orchestrator-agent-with-persona- subagents pattern also takes inspiration from the role-separation style in `ai-hedge-fund`, but TerraFin keeps shared capabilities and prompt-level persona policy instead of hardcoded per-guru analysis modules — see the diagrams in [architecture.md](./architecture.md#orchestrator-persona-subagents) for the authoritative shape. ## Current runtime shape Today the hosted runtime has: - a shared financial capability kernel - a hosted agent definition registry - a policy-enforcing runtime controller - a hosted tool adapter - a provider-agnostic hosted loop - a provider registry with OpenAI, Gemini, and GitHub Copilot adapters - Python, CLI, HTTP, notebook, and browser widget adapters - transcript-first local session history - hidden persona subagents (Buffett / Marks / Druckenmiller) reached by the main orchestrator agent via `consult_` tool-calls (see the architecture diagrams in [architecture.md](./architecture.md#orchestrator-persona-subagents)) - a structured internal tool-result/error protocol - transcript normalization and repair before model calls - a proactive context-budget manager with reactive fallback retries ## Transcript-first persistence Conversation history is no longer stored inside the hosted session record. Instead TerraFin now splits local hosted state into two layers: - transcript JSONL files: durable source of truth for message history - session index JSON: summary metadata for history/list/delete behavior Default layout under the unified TerraFin state dir: ```text .terrafin/agent/sessions/sessions.json .terrafin/agent/sessions/.jsonl ``` Transcript events are append-only and currently include: - `session_header` - `message` - `runtime_model` - `custom_title` - `compact_boundary` `message` events now carry structured internal content blocks as well as the public `role/content` shape. In practice that means TerraFin can persist: - assistant text - hidden internal tool-use turns - tool results - retryable tool-error results without exposing the hidden internal turns in the browser widget or public session APIs. Important consequences: - session list/history is transcript-derived - reopening a session reconstructs the conversation from transcript events - deleting a session archives the transcript file with a `.deleted.` suffix and removes it from active history - legacy embedded conversation blobs are ignored and not migrated - hidden internal guru sessions can still be recorded for runtime/debug purposes, but they are filtered out of normal public session history - hidden guru sessions are also blocked from normal public read/delete/task/approval routes even when a caller knows a session id - deleting a public parent session cascades hidden guru child cleanup Tasks, approvals, audit, and published view context still live in the hosted runtime/session store. Only conversation history moved to transcript files. ## Important files Module paths use the post-refactor canonical locations. Old top-level paths (e.g. `agent/loop.py`, `agent/transcript_store.py`) remain as compatibility shims — see [architecture.md § Current code map](./architecture.md#current-code-map) for the full old → new mapping. | File | What it owns | |------|---------------| | `src/TerraFin/agent/runtime/capability.py` | capability registry, session context, task registry, artifact tracking | | `src/TerraFin/agent/contracts/definitions.py` | hosted agent definitions and allowlists | | `src/TerraFin/agent/runtime/hosted.py` | session lifecycle, policy enforcement, task dispatch, transcript-aware session access | | `src/TerraFin/agent/runtime/loop.py` | hosted loop, immediate message append flow, provider state persistence | | `src/TerraFin/agent/contracts/conversation.py` | internal message/block protocol and conversation dataclasses | | `src/TerraFin/agent/guru/worker.py` | route planning, hidden guru execution, and structured memo synthesis | | `src/TerraFin/agent/tools/execution.py` | structured tool execution outcomes and tool-result message creation | | `src/TerraFin/agent/runtime/transcript_normalizer.py` | transcript repair, tool-use/tool-result pairing, internal/public view split | | `src/TerraFin/agent/runtime/context_budget.py` | proactive prompt-budget estimation and compaction levels | | `src/TerraFin/agent/runtime/recovery.py` | per-turn recovery budget / repeated-error policy | | `src/TerraFin/agent/storage/transcript_store.py` | append-only transcript store, `sessions.json` index, transcript readers, archive/rewrite helpers | | `src/TerraFin/agent/storage/session_store.py` | non-transcript hosted state: tasks, approvals, audit, view context, transient conversation attachment | | `src/TerraFin/agent/models/runtime.py` | provider registry, runtime-model binding, canonical `provider/model` refs | | `src/TerraFin/agent/models/providers/*.py` | provider adapters for OpenAI, Gemini, and GitHub Copilot | | `src/TerraFin/agent/tools/adapter.py` | function-callable tool definitions and tool execution bridge | | `src/TerraFin/agent/service/client.py` | Python transport adapter (`TerraFinAgentClient`) | | `src/TerraFin/agent/cli/main.py` | CLI adapter (`terrafin-agent`) | | `src/TerraFin/interface/agent/data_routes.py` | HTTP runtime endpoints | | `src/TerraFin/interface/frontend/src/agent/GlobalAgentWidget.tsx` | floating assistant widget | | `src/TerraFin/interface/frontend/src/AppRouter.tsx` | mounts the widget across the main pages | ## Runtime endpoint family Hosted runtime endpoints live under `/agent/api/runtime/*`. Current routes: - `GET /agent/api/runtime/agents` - `POST /agent/api/runtime/sessions` - `GET /agent/api/runtime/sessions` - `GET /agent/api/runtime/sessions/{session_id}` - `DELETE /agent/api/runtime/sessions/{session_id}` - `POST /agent/api/runtime/sessions/{session_id}/messages` - `GET /agent/api/runtime/sessions/{session_id}/approvals` - `GET /agent/api/runtime/tasks/{task_id}` - `POST /agent/api/runtime/tasks/{task_id}/cancel` - `GET /agent/api/runtime/approvals/{approval_id}` - `POST /agent/api/runtime/approvals/{approval_id}/approve` ## Recovery architecture The hosted loop now follows a stricter internal recovery path inspired by the kind of guardrails Claude Code uses internally, while keeping TerraFin's own surface and naming: - retryable tool/input failures stay inside the loop as structured tool-error results - fatal upstream auth/quota/provider failures are the main class still surfaced to users - transcripts are normalized before provider calls so orphaned tool results do not leak into the next turn - context is proactively compacted before provider calls, with reactive retry levels still kept as a last resort - `POST /agent/api/runtime/approvals/{approval_id}/deny` - `PUT /agent/api/runtime/view-contexts/{context_id}` - `GET /agent/api/runtime/view-contexts/{context_id}` The browser widget, notebook helpers, CLI runtime commands, and Python client all sit on top of this same contract. The runtime catalog intentionally exposes only public agents in the normal adapter surfaces. Hidden guru roles are internal runtime definitions, not default user-facing choices, and the public session-create route rejects them directly. ## Browser behavior The hosted runtime is not exposed through a dedicated `/agent` page anymore. Instead: - the browser UI is a floating assistant widget - it appears across main interface pages - it calls the same hosted runtime endpoints as every other adapter If the deployment does not expose `/agent/api/runtime/*`, the widget should fail with a clear runtime error rather than silently hanging. ## Current implementation status What is already there: - capability metadata and backgroundability markers - hosted agent definition registry - policy-enforced capability allowlists - hosted tool adapter - provider-backed model loop - transcript-first local session history - archived session delete behavior - hidden persona subagents reached by the main orchestrator agent via `consult_` tool-calls (authoritative shape: [architecture.md § Orchestrator + persona subagents](./architecture.md#orchestrator-persona-subagents)) - structured internal guru memos returned to the orchestrator through a dedicated memo tool-call contract, not JSON scraped from prose - notebook helper surface - browser widget over the runtime endpoints What is still intentionally lighter: - automatic transcript compaction - richer task progress UX - artifact history UI - MCP-like external adapter layer ## Regression surfaces When touching hosted runtime code, the highest-signal regression surfaces are: - transcript append order for `user -> assistant/tool -> assistant` - session reopen/history summaries derived from transcript + index - session delete/archive behavior - response parsing from each provider - semantic parity across Python, CLI, and HTTP - widget integration over `/agent/api/runtime/*` Current tests: - `tests/agent/test_runtime.py` - `tests/agent/test_hosted_runtime.py` - `tests/agent/test_tools.py` - `tests/agent/test_loop.py` - `tests/agent/test_transcript_store.py` - `tests/agent/test_openai_model.py` - `tests/agent/test_google_provider.py` - `tests/agent/test_github_copilot_provider.py` - `tests/agent/test_runtime_helpers.py` - `tests/agent/test_client.py` - `tests/agent/test_cli.py` - `tests/interface/test_agent_api.py` Useful commands: ```bash pytest tests/agent/test_runtime.py \ tests/agent/test_hosted_runtime.py \ tests/agent/test_tools.py \ tests/agent/test_loop.py \ tests/agent/test_transcript_store.py \ tests/agent/test_openai_model.py \ tests/agent/test_google_provider.py \ tests/agent/test_github_copilot_provider.py \ tests/agent/test_runtime_helpers.py \ tests/agent/test_client.py \ tests/agent/test_cli.py \ tests/interface/test_agent_api.py npm run build ``` ## Read next - [usage.md](./usage.md) - [architecture.md](./architecture.md) - [../interface.md](../interface.md)