Seshat AIDocumentation

Documentation / Concepts

Inside the runtime

A tour of how the Seshat runtime is built: layers, one turn of the loop, one tool call, and what happens when a provider fails. With counts you can reproduce.

Architecture gives the short version. This page goes one level down, for people who want to extend the runtime, embed it, or simply judge how it is made. Everything below was checked against the source on 12 October 2026, dev branch, commit e151301. Counts change with the code: the last section shows how to recompute them.

The layers, and which way dependencies point

internal/ · 43 packagesseshat (cmd/cli)Terminal chat, run, sessions,memory, doctor, loginseshat-grpc (cmd/grpc)Port 50051: query, streaming,MCP, skills, healthA Go hostSeshatOS, SeshatCloud or your ownapplication, embedding the SDKpkg/sdk: Client, Session, ClientConfigThe single assembly point: tools, MCP, RAG, memory,vector storage, sandbox, hooks, monitoringpkg/: public API39 packages: types, tool contract,providers, config, storage, RAG…internal/engine: Engine, Session, LoopThe state machine of one turn: iteration cap,compaction, stop hooks, token budgetExecutionprompt: cached sectionsproviders: 16, retry, fallbackexecution: orchestratortools: the built-in registrypermissions: 6 modes, classifierState and datadb: SQLite, sessions, goals, tasksruntime: compaction, statememory: long-term graphvector, storagePlatform servicesweb and browser, sandboxagent, mailbox, teamautomation, auth (OAuth)monitoring: OpenTelemetryhooks, modes (plan, execute…)uses
Dependencies point one way: entry points call the SDK, the SDK calls the engine and the subsystems.

The direction is one-way: cmd/* calls pkg/sdk, which calls internal/*. A test, internal/archtest/boundary_test.go, fails when an internal/ package imports pkg/ outside a short, explicit allow-list. The public surface has 39 packages under pkg/ (types, the tool contract, providers, config, storage, vector, RAG, web, skills, MCP, workflow, connectors and more), and the implementation has 43 under internal/.

The terminal interface (tui/internal/seshattui) is a fork of the Charm Crush interface, under the FSL-1.1-MIT license. It drives the SDK through a bridge in its workspace/ package, like any other host.

What lives in internal/

GroupPackagesRole
Executionprompt, providers, execution, tools, permissionsBuilds the system prompt in cached sections, talks to the models, runs tool calls, and decides what is allowed.
State and datadb, runtime, memory, vector, storageSQLite for sessions, checkpoints, goals, vectors and tasks. Compaction and state backends (SQLite, files, memory). A long-term memory graph.
Platform servicesweb and the browser, sandbox, agent, mailbox, team, automation, auth, monitoring, hooks, modesWeb access, Docker or Landlock isolation, sub-agents and teams, scheduled work, OAuth, OpenTelemetry and Prometheus, shell hooks, and the plan and execute modes.

One turn: what the loop does

A user message starts a turn. A turn is a loop of up to 100 iterations by default (one model call, then its tools, then the next model call), inside a session limited to 100 turns by default in the SDK. Every transition is recorded as one of Continue, ContinueWithRecovery or Terminate, so a turn can always be explained afterwards.

Loop.Runquery_start hook, state initialisedIterations < 100 ?Terminatemax_iterations_reachedPrepare the iterationiteration_start hook, stage promptToolsets, auto-compactioncompacts when the context fillsModel call, streamedretry, model and provider fallbackRecoverable errorbackoff, then recoveryTool requests ?Run the toolsparallel if safe, else in seriesAppend the resultstool_uses_complete hookStop hooks and nudgesa hook may ask to continue, or theloop nudges the model (max 3)End of the turnquery_complete hook, resultnoyeserroryesnodonenext iterationcontinue
One turn of Loop.Run. The model call is the core step, the dashed block runs the tools, amber is recovery, red ends the turn on the iteration cap, green is the normal end.
  1. Start. The query_start hook runs with the session, the turn, the permission mode and the model. It only observes.
  2. Iteration begins. The iteration counter goes up and the iteration_start hook can stop the turn. If stage detection is on, the system prompt is refreshed when the stage of the conversation changed (for example after a tool result).
  3. Preparation. Messages are prepared, the toolsets are resolved, and auto-compaction runs when the context is getting full. A failed compaction ends the turn instead of sending a broken request.
  4. Model call, streamed. Retries and fallbacks happen here (see the last section).
  5. Does the answer contain tool requests? If yes, the orchestrator runs them (in parallel when they are safe to run together, in series otherwise), appends the results, and the tool_uses_complete hook fires. If a tool_search call discovered deferred tools, the loop continues with a recovery transition so the new tools are available.
  6. No tool requests. Before the turn can end, the loop looks for a reason to go on. If the model hit its output limit, it is told to resume. If it only announced a next step (“I will now…”), it is nudged to continue, at most three times. If a token budget remains, the loop can continue for it. Last, the stop hooks run and may ask for one more cycle.
  7. End. The query_complete hook receives the result and a recovery context.

A recoverable error during the model call goes through tryRecovery with an exponential backoff, then the loop starts the iteration again. The turn ends when the model has nothing left to do, a hook stops it, the iteration cap is reached (max_iterations_reached), or the context is cancelled.

One tool call: the pipeline

Every tool call goes through the same stages, and each one reports its progress so interfaces can show it. An OpenTelemetry span named tool <name> wraps the call.

Gates: they can refuseValidateinput check,then backfillPre-hooksmay stop orrewrite inputSafety checkrisky patterns,not bypassablePermissionslocal, thenglobal, 6 modesCallthe tool runsspan: tool namePost-hooksafter the callFormattool-defined,UTF-8 safe cutBefore the pipeline, tools declared concurrency-safe run in one parallel batch; the others run one after the other.
The two highlighted stages can refuse the call. The safety check is not disabled by the most permissive permission mode.
  1. Validate the input, then fill in what hooks and permissions need to see.
  2. Pre-hooks can stop the call or rewrite its input.
  3. Safety check. A checker looks for dangerous patterns and can refuse with a stated reason. It is not disabled by the most permissive permission mode.
  4. Permissions, local then global. There are six modes: onRequest (the default), auto (a model-based classifier decides), acceptEdits, bypass, never and granular.
  5. The call itself.
  6. Post-hooks, then the tool formats its own result, truncated on a character boundary so it never cuts a UTF-8 sequence.

Before the pipeline, calls are partitioned: tools declared safe for concurrency run in one parallel batch, the others run one after the other.

When a provider fails

Seshat reaches 16 providers through one interface: Anthropic, OpenAI, Codex, Mistral, Gemini, MiniMax, OpenRouter, DeepSeek, OpenCode, Z-AI, Ollama, Bedrock, Vertex, Foundry, Kimi and Workers AI. Failures are handled in layers, from the cheapest to the most drastic:

Model callfrom the loopProvider clientwire-format adapterProvider API16 providers, one interfaceCircuit breakerone per providerclosed, open, half-openRecoverable error ?TerminalerrorRetryjittered backoffFallback modelFallbackModels listFallback provideronly if configuredTerminalerrorerrornoyesstill failingexhaustedexhaustedA success at any stage returns the response to the loop.
Failures are handled from the cheapest remedy to the most drastic. Any success returns the response to the loop.
LayerWhat it does
RetryExponential backoff with jitter. Errors are classified (client, authentication, rate limit, network, overload) so only the retryable ones are retried.
Fallback modelIf retries fail, the next model in Routing.FallbackModels is tried.
Fallback providerIf the models are exhausted, a configured fallback provider is tried through its own client. Nothing leaves your chosen vendor unless you list one.
Circuit breakerOne per provider. After 5 consecutive failures the circuit opens and calls fail immediately without touching the provider. After 60 seconds it half-opens and lets 3 trial calls through; if they succeed, it closes.

A non-recoverable error is terminal at once: the loop does not retry what cannot succeed.

By the numbers

Counted on 12 October 2026, dev branch, commit e151301.

WhatCount
Go version1.26.9
Go files1,232 (947 without tests)
Lines of Go, without tests214,361
Lines of Go, tests only65,483
Packages under internal/43
Packages under pkg/40
Providers16
Built-in tools in the default registry129 (95 enabled without keys)

The terminal interface, a fork, and the tools make up the largest share of the code. The agent core itself (engine, execution, prompt and permissions) is much smaller.

Where to go next

Updated on 2026-10-13