oats.core
Core configuration, profiles, tokens, and bus infrastructure.
oats.core.config
Configuration management with hierarchical loading.
Config priority (highest to lowest): 1. Environment variables set CODER_CONFIG_FILE to your coder.json file export CODER_CONFIG_FILE=./oats/config/coder.json’)
- class oats.core.config.ProviderConfig(**data)[source]
Bases:
BaseModelConfiguration for an AI provider.
- enabled: bool
- model_config: ClassVar[ConfigDict] = {}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class oats.core.config.ModelConfig(**data)[source]
Bases:
BaseModelConfiguration for model selection.
- model_config: ClassVar[ConfigDict] = {'protected_namespaces': ()}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- provider_id: str
- model_id: str
- class oats.core.config.HookEntry(**data)[source]
Bases:
BaseModelA single hook configuration entry.
- event: str
- command: str
- timeout: int
- model_config: ClassVar[ConfigDict] = {}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class oats.core.config.HookConfig(**data)[source]
Bases:
BaseModelHook system configuration.
- hooks: list[HookEntry]
- model_config: ClassVar[ConfigDict] = {}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class oats.core.config.PermissionConfig(**data)[source]
Bases:
BaseModelPermission system configuration.
- bash: str
- model_config: ClassVar[ConfigDict] = {}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class oats.core.config.Config(_case_sensitive=None, _nested_model_default_partial_update=None, _env_prefix=None, _env_prefix_target=None, _env_file=PosixPath('.'), _env_file_encoding=None, _env_ignore_empty=None, _env_nested_delimiter=None, _env_nested_max_split=None, _env_parse_none_str=None, _env_parse_enums=None, _cli_prog_name=None, _cli_parse_args=None, _cli_settings_source=None, _cli_parse_none_str=None, _cli_hide_none_type=None, _cli_avoid_json=None, _cli_enforce_required=None, _cli_use_class_docs_for_groups=None, _cli_exit_on_error=None, _cli_prefix=None, _cli_flag_prefix_char=None, _cli_implicit_flags=None, _cli_ignore_unknown_args=None, _cli_kebab_case=None, _cli_shortcuts=None, _secrets_dir=None, _build_sources=None, **values)[source]
Bases:
BaseSettingsMain application configuration.
Loads from environment variables and config files.
- model: ModelConfig
- permission: PermissionConfig
- hooks: HookConfig
- data_dir: Path
- project_dir: Path
- server_host: str
- server_port: int
- debug: bool
- model_config: ClassVar[SettingsConfigDict] = {'arbitrary_types_allowed': True, 'case_sensitive': False, 'cli_avoid_json': False, 'cli_enforce_required': False, 'cli_exit_on_error': True, 'cli_flag_prefix_char': '-', 'cli_hide_none_type': False, 'cli_ignore_unknown_args': False, 'cli_implicit_flags': False, 'cli_kebab_case': False, 'cli_parse_args': None, 'cli_parse_none_str': None, 'cli_prefix': '', 'cli_prog_name': None, 'cli_shortcuts': None, 'cli_use_class_docs_for_groups': False, 'enable_decoding': True, 'env_file': None, 'env_file_encoding': None, 'env_ignore_empty': False, 'env_nested_delimiter': '__', 'env_nested_max_split': None, 'env_parse_enums': None, 'env_parse_none_str': None, 'env_prefix': 'CODER_', 'env_prefix_target': 'variable', 'extra': 'forbid', 'json_file': None, 'json_file_encoding': None, 'nested_model_default_partial_update': False, 'protected_namespaces': ('model_validate', 'model_dump', 'settings_customise_sources'), 'secrets_dir': None, 'toml_file': None, 'validate_default': True, 'yaml_config_section': None, 'yaml_file': None, 'yaml_file_encoding': None}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- classmethod load(project_dir=None)[source]
Load configuration from all sources.
Merges configs in priority order.
- Return type:
Config
- oats.core.config.get_config(project_dir=None, reload=False, verbose=False)[source]
Get the global config instance, loading if necessary.
- Return type:
Config
oats.core.profiles
Feature profiles for coder — configurable packaging for different device targets.
Profiles let you run coder as slim and lightweight or as fully featured. Set via CODER_PROFILE env var or programmatically.
Built-in profiles:
minimal — Core file/code tools only. No network, no browser, no cloud. Ideal for air-gapped devices, embedded, or CI runners.
standard — Adds web search, S3, planning, memory, agents. Good default for most dev workstations.
full — Everything enabled: browser/playwright, MCP, LSP, scraping
custom — User controls each feature group via individual env vars.
Individual feature groups can always be overridden regardless of profile:
CODER_FEATURE_BROWSER=0 disables browser even on 'full'
- class oats.core.profiles.FeatureProfile(name, core_tools=True, web_tools=True, s3_storage=False, database=False, redis=False, browser=False, scraping=False, mcp=False, lsp=False, planning=True, memory=True, agents=True)[source]
Bases:
objectDeclares which feature groups are enabled for a profile.
- name: str
- core_tools: bool = True
- web_tools: bool = True
- s3_storage: bool = False
- database: bool = False
- redis: bool = False
- browser: bool = False
- scraping: bool = False
- mcp: bool = False
- lsp: bool = False
- planning: bool = True
- memory: bool = True
- agents: bool = True
- __init__(name, core_tools=True, web_tools=True, s3_storage=False, database=False, redis=False, browser=False, scraping=False, mcp=False, lsp=False, planning=True, memory=True, agents=True)
- oats.core.profiles.get_profile()[source]
Return the active feature profile (cached after first call).
- Return type:
FeatureProfile
- oats.core.profiles.is_feature_enabled(group)[source]
Check if a feature group is enabled, with env override support.
- Return type:
- Env override always wins:
CODER_FEATURE_BROWSER=1 → True (even if profile is ‘minimal’)
Otherwise falls back to the active profile.
- oats.core.profiles.list_profiles()[source]
Return the names of all built-in profiles.
oats.core.tokens
Shared token estimation.
Used by the context ledger, session token budget, and conversation compactor
so all three agree on what “tokens” means. Previously each site used
len(text) // 4, which under-counts code (~2.5 chars/token) and is close
to right for English prose (~3.5–4). The divergence matters when the budget
decision (trigger compaction, elide sections) is based on these estimates:
the real input payload routinely exceeds the approximation by 10–30%.
When tiktoken is importable we use cl100k_base as a stand-in — it’s
not Claude’s exact BPE, but it’s a far better estimator than chars/4 for
both prose and code and it’s stable across calls (so relative comparisons
between sections are faithful).
When tiktoken is not available we fall back to len(text) / 3.5,
which biases toward over-estimating. That’s the safer direction for
budget decisions — we’d rather compact a turn early than blow the window.
The tokenizer (if any) is loaded lazily and memoized on the module so we
don’t pay encoding-loader cost on every count_tokens() call.
- oats.core.tokens.count_tokens(text)[source]
Return an estimated token count for
text.Uses tiktoken’s cl100k_base when available, otherwise a conservative chars/3.5 approximation. Empty/None returns 0.
- Return type:
- oats.core.tokens.count_message_tokens(messages)[source]
Sum token estimate across a list of session Message objects.
Accepts anything with
get_text_content(),get_tool_calls(), andget_tool_results()so the existing SessionTokenBudget / compactor can call through without importing the Message type.- Return type:
oats.core.bus
Event bus for pub/sub pattern communication.
- class oats.core.bus.EventType(*values)[source]
-
Event types for the event bus.
- SESSION_CREATED = 'session.created'
- SESSION_UPDATED = 'session.updated'
- SESSION_DELETED = 'session.deleted'
- SESSION_BUDGET = 'session.budget'
- SESSION_COMPACTED = 'session.compacted'
- SESSION_TASK_BUDGET = 'session.task_budget'
- MESSAGE_CREATED = 'message.created'
- MESSAGE_UPDATED = 'message.updated'
- MESSAGE_PART_CREATED = 'message.part.created'
- MESSAGE_PART_UPDATED = 'message.part.updated'
- TOOL_START = 'tool.start'
- TOOL_COMPLETE = 'tool.complete'
- TOOL_ERROR = 'tool.error'
- PROVIDER_REQUEST = 'provider.request'
- PROVIDER_RESPONSE = 'provider.response'
- PROVIDER_ERROR = 'provider.error'
- PERMISSION_REQUEST = 'permission.request'
- PERMISSION_GRANTED = 'permission.granted'
- PERMISSION_DENIED = 'permission.denied'
- HOOK_FIRED = 'hook.fired'
- HOOK_BLOCKED = 'hook.blocked'
- FILE_CHANGED = 'file.changed'
- AGENT_STARTED = 'agent.started'
- AGENT_COMPLETED = 'agent.completed'
- class oats.core.bus.Event(type, data=<factory>, source=None)[source]
Bases:
objectAn event published to the bus.
- type: EventType | str
- __init__(type, data=<factory>, source=None)
- class oats.core.bus.EventBus[source]
Bases:
objectSimple pub/sub event bus for internal communication.
Supports both sync and async handlers.
- __init__()[source]
- subscribe(event_type, handler)[source]
Subscribe to events of a specific type.
Returns an unsubscribe function.
- subscribe_all(handler)[source]
Subscribe to all events.
Returns an unsubscribe function.
- once(event_type, handler)[source]
Subscribe to a single event of a specific type.
The handler will be automatically unsubscribed after being called once. Returns an unsubscribe function.
- publish_sync(event)[source]
Publish an event synchronously (creates a new event loop if needed).
- Return type:
oats.core.storage
File-based JSON storage system with locking.
- exception oats.core.storage.StorageError[source]
Bases:
ExceptionBase exception for storage errors.
- class oats.core.storage.Storage(namespace, model_class)[source]
Bases:
Generic[T]Generic file-based storage for Pydantic models.
Provides CRUD operations with file locking for concurrent access.
- namespace: str
- model_class: type[T]
- property storage_dir: Path
Get the storage directory for this namespace.
- async delete(id)[source]
Delete an item. Returns True if deleted, False if not found.
- Return type:
- __init__(namespace, model_class)
oats.core.features
Lightweight runtime feature flags for oats coder.
These are intentionally env-driven so new agent/runtime behaviors can be tested safely without requiring a larger rollout framework.
- oats.core.features.deferred_tools_enabled()[source]
Enable ToolSearch-style deferred tool loading.
- Return type:
- oats.core.features.rich_compaction_enabled()[source]
Enable denser engineering-oriented compaction prompts.
- Return type:
- oats.core.features.streaming_tool_assembly_enabled()[source]
Enable robust assembly of fragmented streamed tool calls.
- Return type:
- oats.core.features.strict_tool_schemas_enabled()[source]
Pass strict tool schema hints through to compatible providers.
- Return type:
- oats.core.features.active_tool_guidance_enabled()[source]
Explain active/deferred tools in the system prompt.
- Return type:
- oats.core.features.reactive_compaction_candidate_enabled()[source]
Enable prompt-too-long recovery by compacting the session and retrying the request instead of failing immediately.
- Return type:
- oats.core.features.context_collapse_candidate_enabled()[source]
Enable state-capsule style compaction that preserves summarized history as a system message for stronger continuation.
- Return type:
- oats.core.features.lsp_tools_candidate_enabled()[source]
Enable LSP-backed code-intelligence tools.
- Return type:
- oats.core.features.token_budget_candidate_enabled()[source]
Enable lightweight per-turn token budgeting and prompt guidance.
- Return type:
- oats.core.features.task_budget_candidate_enabled()[source]
Enable lightweight task-loop budgeting and anti-churn guidance.
- Return type:
- oats.core.features.result_retention_enabled()[source]
Enable compressed retention of large tool outputs in session history.
- Return type:
- oats.core.features.trajectory_store_enabled()[source]
Log every user prompt and tool outcome to the on-disk trajectory store.
- Return type:
- oats.core.features.trajectory_retrieval_enabled()[source]
Inject top-K past trajectories into the system prompt. Requires trajectory_store.
- Return type:
- oats.core.features.cron_enabled()[source]
Enable the coder cron/task scheduler tick in the interactive session.
- Return type:
- oats.core.features.plugins_enabled()[source]
Enable the declarative plugin loader (oats.plugins.loader.install).
- Return type:
- oats.core.features.should_disable_streaming(model_id=None)[source]
Check if streaming should be disabled for the current model.
Returns True if: - CODER_FEATURE_NON_STREAMING is explicitly enabled, OR - The model ID looks like Gemma 4 (auto-detection)
Auto-detection can be overridden with CODER_FEATURE_NON_STREAMING=0.
- Return type:
oats.core.id
ID generation utilities using uuid
oats.core.offline
Offline-mode helpers.
Coder2’s core turn loop (provider → tools → session) does not phone home. All external traffic is either:
The LLM provider endpoint — user-configured; point at a local vLLM on localhost for a fully offline setup.
Explicit user-initiated actions via plugins (corpus fetchers, playwright, mattermost relays). These are surfaced in /help and the user has to invoke them directly.
When CODER_OFFLINE_STRICT=1 is set, anything that would reach outside
localhost/127.0.0.1/.local/.internal has to consult
require_network() first. The flag is advisory (we don’t patch the
kernel namespace) — it gives plugin authors a single hook to check before
opening a socket, and a single audit point users can grep for.
Typical usage inside a tool implementation:
from oats.core.offline import require_network
require_network(label="corpus:github", url=url) # raises if blocked
- exception oats.core.offline.OfflineBlockedError[source]
Bases:
RuntimeErrorRaised when strict offline mode refuses an external request.
- oats.core.offline.offline_strict()[source]
True if
CODER_OFFLINE_STRICT=1. Re-read per call so users can toggle it without restarting the session.- Return type:
- oats.core.offline.is_local_url(url)[source]
Best-effort classification: does
urltarget this machine or LAN?- Return type:
- oats.core.offline.require_network(*, label, url='')[source]
Gate an outbound network operation on strict-offline mode.
Raises
OfflineBlockedErrorwhenCODER_OFFLINE_STRICT=1AND the target (url) is not local. Local targets (localhost, private-range IPs,.local) always pass. Plugins invoking explicit user actions can skip this — the point of strict mode is to gate implicit or accidental egress.- Return type: