Kernel Internals

⚫ A document for those who write or modify the kernel code itself. The full specification is in SPEC.md (English, source of truth); here is a brief map. Semantics come from the sources agent_os_core.


14.1 Model

The kernel is a single async runtime (Tokio) with an event loop that:

  1. maintains a process table (DashMap<Pid, AgentProcess>);
  2. calls the scheduler (schedule() → ScheduleDecision) on every tick;
  3. dispatches turns (run_turn(pid)), invoking the provider and running the tool-call loop;
  4. limits resources (budgets, groups, concurrency limit) before dispatch;
  5. routes IPC;
  6. mediates every tool call;
  7. emits events to listeners;
  8. manages sessions (SessionRegistry).

The kernel holds optional service handles, wired in once at load time by the host (message_store, state_store, provider, pricing, etc.) — if a handle is not wired in, the corresponding syscalls respond "not configured".

Modularity. Features (cron, workflow, connectors, memory, model_router, channels, …) have been moved out of the kernel into modules and are wired in as host ports. Instead of the typed fields cron_service/workflow_service/connector_service on Kernel, there is now a single neutral seam kernel::KernelServices, which the host fills from the ports agent_os.Cron / agent_os.Workflow / agent_os.Connectors (kernel.services().set_*). The kernel does not depend on any single feature; details are in docs/27-core-modularization.md §5 and 32-building-a-module.

Load cycle:

loop {
    decision = schedule()
    match decision {
        Run(pid)       → run_turn(pid)
        Idle           → sleep(50ms)
        AtConcurrencyLimit → sleep(50ms)
        Shutdown       → break
    }
}

14.2 Core Abstractions

OS Concept Equivalent Module
Process AgentProcess — PID, state machine, context, tools, budget process.rs
Kernel Kernel — event loop kernel.rs
CPU InferenceProvider — pluggable LLM provider.rs
RAM ContextWindow — memory with eviction context.rs
I/O device Tool tool.rs
cgroups/ulimits TokenBudget / BudgetGroup budget.rs
System call Syscall syscall.rs
Signal Signal ipc.rs
fork() fork() — COW context, group inheritance process.rs
Process table DashMap<Pid, AgentProcess> kernel.rs
Driver ToolHandler tool.rs

Process State Machine

spawn → Created → (init) → Ready ⇄ Running → (exit) → Terminated
                              │
                              └→ Blocked{reason} → (unblock) → Ready

Turn and Tool-Call Loop

  1. The kernel selects process P.
  2. It sends P's context + the latest tool results to the provider.
  3. The provider streams tokens.
  4. If the LLM requests a tool call, the kernel pauses the stream, validates the call, executes the handler (with a deadline), adds the result as a tool message, and resumes inference.
  5. The turn ends when the agent yields, blocks, its quantum expires, or its budget is exhausted.

Concurrency is metered by Semaphore only around provider.stream — tool calls and hooks run without a permit, so a slow tool does not "starve" other sessions.

Context Window

ContextWindow { max_tokens, messages, eviction, summary }. Eviction policies: Fifo (drop old ones), SummaryFirst { keep } (collapse old ones into a summary, default keep=8), SlidingWindow { n }. Eviction also cleans up orphaned tool results whose parent assistant message has been removed.

Budgets

TokenBudget { max_per_turn, bucket: TokenBucket, max_total, … }. TokenBucket — a classic leaky bucket (capacity + refill_rate). A group BudgetGroup adds two optional layers: per-user (a sub-bucket per user_id) and per-session. Check before dispatch: group → per-user → per-session (can_consume_for); an exhausted layer — BudgetLevel::{Group,User,Session}.

IPC and Syscalls

MessagePayload: Text, Json, Signal (Terminate/Pause/Resume/Interrupt), SpawnRequest, SpawnResult.

Syscalls: Spawn, Fork, Kill, Yield, Send/Recv, ToolCall, SetState/GetState/DeleteState, Checkpoint/Restore.

Hooks

HookHandler with points: before_user_message (transform/inject/suppress/reply/ notice), before_tool_call (rewrite args / deny / short-circuit), after_tool_result (redact/transform), before_inference (request mutation; deny → block, response → synthetic response), before_retry (allow/deny retry + backoff), before_syscall (syscall/IPC filter, deny), after_turn, after_assistant_message (output guardrail), on_token (streaming moderation), before_context_evict (custom compaction), after_loop, on_spawn/on_terminate/on_error, on_session_created/on_session_closed, on_budget_threshold. Plus merge_user (identity merging), memory_inspect (inspection), apply_feedback (like/dislike).

Composition: Hook.priority (higher — earlier), enabled, activation conditions when { templates, agents, users, sessions }, global (directory config/hooks/*.yaml). Every call goes through Kernel::run_hook (catch_unwind + timeout hook_secs) and is accounted for in HookMetric per pair (hook, point) (GET /v1/hooks/metrics). HookOutcome { inject, replace_message, reply, notice, suppress }.


14.3 Scheduler

SchedulingPolicy: RoundRobin (default), PriorityPreemptive, FairShare, DeadlineDriven. Scheduler { policy, time_slice, max_concurrent }. ScheduleDecision: Run(pid) | Idle | AtConcurrencyLimit | Shutdown.


14.4 Inference Provider

trait InferenceProvider {
    fn provider_type(&self) -> &str;
    async fn stream(&self, request) -> Result<Box<dyn Stream<Item=Result<InferenceEvent, InferenceError>>>>;
}
enum InferenceEvent { Token(String), ToolCall(ToolCallRequest), Done(InferenceStats) }

The concrete implementation is OpenAiProvider (reqwest + SSE). Pricing estimates cost from statistics.


14.5 Storage Subsystems

Layer Where Source of truth for
Text logs (tracing) stdout → agent_os.log operational viewing
Event store (EventDb) logs/events.db audit (one line per event)
Message store logs/messages.db transcripts + FAQ cache, full-text search
JSONL traces logs/process-{pid}.jsonl raw process event stream
State store logs/state.db scoped key-value (session/user/agent/team)
Checkpoints CheckpointStore recovery/migration

14.6 Events and Listeners

The kernel emits typed ProcessEvent through the bus: TurnStart, Token, ToolCall, ToolResult, ToolProgress, TurnEnd, StateChange, Error, Spawned, Terminated, BudgetConsumed, BudgetExhausted, RateLimited, ScheduleBlocked, session lifecycle events, Cost, workflow steps. Listeners implement ProcessListener (all methods are no-op by default).


14.7 Loading

  1. The host loads the config and builds Kernel.
  2. ToolRegistry registers built-in and user tools; HookRegistry — hooks.
  3. InferenceProvider connects to the LLM.
  4. Service handles are connected (message_store, state_store, cron_service, workflow_service, pricing).
  5. Agent templates are loaded (built-in + YAML).
  6. Bootstrap agents are spawned (or lazily on the first session request).
  7. The scheduler loop starts.
  8. The host listens for external requests (HTTP + SSE).

14.8 Design decisions

  1. Cooperative multitasking by default; preemption is optional.
  2. spawn and fork — both supported; fork always inherits the budget group.
  3. Cost accounting at the tool level — each ToolResult carries ToolCost.
  4. I/O through the core — agents never call tools directly.
  5. Opt-in subsystems — when not connected, they respond "not configured" rather than crashing.

14.9 Non-goals

Multi-tenancy (one instance = one tenant), distributed scheduler, fine-tuning, graph workflows in the core (that's the server layer), real-time guarantees.


14.10 Source map

Concern Module
Core, turn loop crates/kernel/src/kernel.rs
Process, spec, fork crates/kernel/src/process.rs
State machine crates/kernel/src/state.rs
Context, eviction crates/kernel/src/context.rs
Budgets, groups, cost crates/kernel/src/budget.rs
Scheduler crates/kernel/src/scheduler.rs
Provider (trait) crates/kernel/src/provider.rs
OpenAI provider crates/kernel/src/openai_provider.rs
Tools crates/kernel/src/tool.rs
Hooks crates/kernel/src/hook.rs, hooks/
IPC, signals crates/kernel/src/ipc.rs
Syscalls crates/kernel/src/syscall.rs
State store crates/kernel/src/state_store.rs
Message store crates/kernel/src/message_store.rs
Events, listeners crates/kernel/src/event.rs, listener.rs
Event DB crates/kernel/src/event_db.rs
Checkpoints crates/kernel/src/checkpoint.rs
JSONL logger crates/kernel/src/jsonl_logger.rs
Subagents crates/kernel/src/subagent.rs
Cron crates/kernel/src/cron.rs
Workflows crates/kernel/src/workflow.rs
Pricing crates/kernel/src/pricing.rs
Metrics (Prometheus /metrics) crates/kernel/src/metrics.rs
Side-effect idempotency crates/kernel/src/side_effect.rs
Durable sessions (opt-in persistence) crates/kernel/src/durable.rs
Durable pending (approvals/input/await_event) crates/kernel/src/pending.rs
Config loader crates/kernel/src/config.rs
Script runtimes crates/kernel/src/script_engine/, js_runtime/

14.11 Build and tests

Cargo workspace of three crates:

Crate Purpose
agent_os_core core: processes, scheduler, budgets, events, provider abstraction, hooks, message store. No HTTP and no concrete provider
agent_os_tools tool implementations (scrapers, archive, RSS, PDF, JSON, scripts) and built-ins
agent_web_bot HTTP server (axum), OpenAI provider, widget, admin UI, proxy, Telegram bots, config loader. The binary is a single CLI agent-os
cargo build                       # debug-сборка всех бинарников
cargo run --bin agent-os -- serve # запуск сервера (грузит config/server.yaml)

cargo test -p agent_os_core       # юнит-тесты ядра/инструментов/хуков
cargo test -p agent_web_bot       # тесты сервера, auth, proxy, login, sessions
cargo test -p agent_web_bot --lib # HTTP-слой (роутинг, auth, login, access-лог)

The widget in widget/ is built automatically build.rs at agent_web_bot (via npm) — a separate npm run build is not needed. Integration tests crates/host/tests/ require a live LLM key and are not run by default.