Clapilot-Agent Request Execution Flows
Detailed native request routing, execution backend mapping, and prompt-assembly flow for web chat and external channels.
This page documents the active native clapilot-agent request path, not the old compatibility runtime.
It focuses on two questions:
- How a web chat turn or channel message becomes a concrete provider request.
- How the final model input is assembled from system prompts, memory, tools, history, and current input.
Primary implementation sources:
src/app/api/chat/route.tssrc/app/api/agent-runtime/channels/[channel]/inbound/route.tsservices/clapilot-agent/src/channels/index.mjsservices/clapilot-agent/src/sessions/index.mjsservices/clapilot-agent/src/providers/index.mjsservices/clapilot-agent/src/memory/index.mjs
Flow 1: Request routing, model selection, bridge selection, provider call
What actually happens
- Web chat starts in
src/app/api/chat/route.ts. The route normalizes attachments, page context, document references, RAG hints, and the session model override, then forwards the run to nativeclapilot-agent. - Channel traffic starts in
src/app/api/agent-runtime/channels/[channel]/inbound/route.ts, which forwards the raw inbound webhook to/internal/channels/:channel/inbound. - Inside
services/clapilot-agent/src/channels/index.mjs, the runtime parses the provider-specific payload, deduplicates by external event id, resolves either the fallbackchannel:<channel>:<threadKey>session or the mapped canonical web/team-chat session key from the approval metadata, checks approval state, performs channel-specific normalization such as Telegram voice-note transcription, and decides whether the thread stays on the standard native path or escalates into the coding/orchestrator path. - Both paths that stay in the standard native agent loop end up in
sessions.executeRun()insideservices/clapilot-agent/src/sessions/index.mjs. executeRun()resolves the base model from the explicit run payload, stored session override, or the globalnative_model_routing.prioritylist throughproviders.resolveProviderSelection(). When adaptive routing is enabled, the native policy layer evaluates only the configured priority candidates before final provider resolution. Shadow mode records its recommendation without changing execution; apply mode can change the model and Clapilot-code profile. Explicit model/profile choices, specialized agents, Mixture-of-Agents presets, public-channel bridge restrictions, and active tool-loop continuity remain protected.resolveProviderSelection()inservices/clapilot-agent/src/providers/index.mjsdoes not just pick a model name. It also resolves the provider row, the effectiveprovider/modelref, context-window and output-token limits, and the fallback chain.sessions.executeRun()then chooses the runtime core plus the execution adapter:- non-specialized providers use either Clapilot-code (Assistant) (
native) or Clapilot-code (Coding) (embedded_pi) depending on the explicit run override, the selected provider'smetadata.agent_core, andnative_model_routing.non_specialized_agent_core, in that order - OpenAI, Azure OpenAI, and OpenAI-compatible rows use the OpenAI-like HTTP adapter.
- Rich multimodal payloads prefer the Responses-style path.
- Anthropic API-key rows use the Anthropic Messages API.
- Anthropic
setup-tokenrows use the Claude CLI bridge. - OpenAI rows in Codex auth mode use the Codex bridge.
- Bedrock rows use the Converse adapter.
- non-specialized providers use either Clapilot-code (Assistant) (
- Every upstream attempt is written into
agent_model_request_logs, so one native run can still produce multiple provider requests when fallback or tool loops happen.
OpenAI and Azure OpenAI streaming chat-completions requests explicitly request the terminal usage chunk. OpenAI-family request log metadata includes toolPhase, toolLoopStep, previousToolDurationMs, and the applied provider deadline, while prompt_layer_tokens_json.provider_input_estimate supplies the prompt-size dimension. Streaming requests use a 75-second time-to-first-response/idle deadline that refreshes on data or heartbeat activity; non-streaming requests use a separate five-minute wall-clock cap. Before visible output, expiry enters the normal configured fallback path instead of leaving the chat pending for minutes.
9. Provider timeout retries use bounded exponential backoff with positive decorrelated jitter: the capped exponential delay receives up to another 50 percent random delay. OpenAI-family adapters do not stack their older transient retry loop on top of timeout retries. The process-local circuit breaker counts one failure only after a complete provider run exhausts its retries, then skips that provider/model temporarily after repeated failed runs.
10. Subscription/session quota exhaustion is classified separately as quota_exhausted. Before any visible delta or tool side effect, the runtime cools down that model until the provider reset (or a conservative 15-minute default) and immediately tries the next approved configured fallback. If every approved model is quota-blocked, scheduled work retains its original job row and is rescheduled with exponential jittered backoff, never dead-lettered merely because its ordinary retry limit was reached.
11. Retry and fallback replay is allowed only before irreversible progress. Once the runtime has emitted a delta, emitted a tool event, or invoked a tool, any later provider failure aborts that run instead of replaying the original messages and potentially duplicating output or side effects.
Backend matrix: which provider uses which engine
Adapter: native OpenAI family adapter
Endpoint:
/responses for rich content, otherwise /chat/completionsTransport:
httpcodex_oauthAdapter:
runCodexSubscriptionBridge() in sessionsTransport:
orchestrator_bridgeUnder the hood: creates an orchestrator session with
provider: "codex", not the generic OpenAI HTTP adapterAdapter: native Anthropic adapter
Endpoint:
/v1/messagesTransport:
httpoauth_token / setup-token flowAdapter:
runClaudeSubscriptionBridge()Transport:
cli_bridgeUnder the hood: local
claude CLI with --output-format stream-json and MCP config injectionAdapter: native OpenAI-like adapter
Endpoint: Azure
/openai/v1 pathsTransport:
httpAdapter: native OpenAI-like adapter
Endpoint:
/chat/completionsTransport:
httpAdapter: native Bedrock adapter
Endpoint:
/model/<model>/converseTransport:
httpWhat is actually under the hood
openaiwith a normal API key does not go through PI or the Codex bridge. It stays on the native OpenAI-family HTTP adapter.azure_openai,openai_compatible,ollama,aws_bedrock, andanthropicwith an API key also stay on direct native provider adapters. Ollama uses its native chat/embedding API rather than the OpenAI-compatible adapter.anthropicwithoauth_tokenis not PI-backed. It switches into the Claude CLI bridge and runs through the localclaudebinary.- Claude subscription chat is now resumable per Clapilot session: Clapilot stores a hidden Claude bridge session id and follow-up turns reuse that same Claude Code conversation instead of launching a fully stateless CLI run.
openaiwithcodex_oauthis the main path that switches away from the normal native HTTP adapter. That path goes throughrunCodexSubscriptionBridge(), which now keeps a hidden Codex app-server thread per Clapilot session and sends follow-up turns into that same external thread.- Codex bridge steering is direct: queued web-chat follow-ups can call
POST /api/chat/steer, which resolves the hidden:subscription-bridge:codexsession and forwards the message through Codex app-serverturn/steer. - Claude bridge steering is direct while the CLI bridge process is alive: Clapilot starts the bridge with
--input-format stream-json, keeps stdin open for the active run, and writes queued follow-ups as realtime user-message events. - Normal native/embedded-PI runs also have live steering while the run is active:
POST /api/chat/steerqueues aLIVE USER STEERnotice. An active tool receives it in that tool result; if the provider is generating a final answer without a tool call, the runtime aborts that request, keeps the same stream open, and immediately starts a continuation provider step. Any text already emitted and the continuation text are preserved in order. - Native steering is accepted only while a provider or tool step guarantees a consumer. A request landing in the short transition/finalization gap returns
reason=native_boundary_transition; the UI keeps it queued and the normal dispatcher starts it after the current run, preventing an acknowledged follow-up from being dropped. - If the agent runtime restarted during a chat run, the replacement process has no in-memory steering channel for that dead run. The steer endpoint checks the latest persisted run and verifies that its idempotency key still owns the pending chat placeholder: matching terminal runs return
retryAsNewTurn=true, the web route immediately reconciles the stale placeholder, and the client restarts the queued message as a normal new turn. A persistedqueued/runningrow without a live channel is not duplicated; it remains queued withreason=run_not_steerable. An older terminal run without the pending placeholder also stays non-recoverable so it cannot race a newer turn still in preflight. - The web UI shows the queued-message direct-send button only for model entries marked
supportsSteeringby/api/chat/models: Codex and Claude subscription rows are always marked, and non-specialized rows are marked when their active profile uses theembedded_piwire value. - Clapilot-code (Coding) (
embedded_pi) is the provider-agnostic coding-agent profile for providers that do not already require the specialized Codex or Claude subscription runtimes. - Clapilot-code (Coding) does not swap the model backend to Codex. It keeps the selected provider/model transport and changes the session-layer profile, prompt contract, tool profile, and tool-loop budget.
- The
embedded_piwire path has no external harness-level interrupt primitive. Clapilot therefore owns interruption itself: follow-ups are delivered at the current tool-result boundary, or the runtime aborts the in-flight provider HTTP request and starts the continuation inside the same native run. - The older
embedded_pibookkeeping markers around memory flush / compaction remain internal compatibility identifiers; the user-facing profile name is Clapilot-code (Coding). - Adaptive route decisions and their terminal technical outcomes are persisted in
agent_adaptive_route_decisionsand linked one-to-one toagent_runs. Provider retries and fallback still belong to the existing provider layer after the initial route decision. - There is also a separate channel-orchestrator escalation path for coding-style work in
services/clapilot-agent/src/channels/index.mjs, but that is not the default execution backend for ordinary provider rows.
Clapilot-code robustness
The shared native provider loop hardens Clapilot-code (Assistant) (native) and Clapilot-code (Coding) (embedded_pi) against malformed output from weak, open, and local models. The normalization layer runs after each provider response is decoded and before assistant history is appended or tools are dispatched. It is shared by OpenAI Responses, OpenAI-compatible Chat Completions, Anthropic Messages, Bedrock Converse, and Gemini GenerateContent.
- closed reasoning blocks such as
<think>...</think>are removed; an unterminated reasoning block is removed only when its opening tag occurs at a block boundary, so ordinary prose that mentions a tag is preserved. Repeated orphan closing markers from local model templates are treated as a malformed reasoning boundary: intermediate content is removed while the trailing final answer is preserved - tool calls emitted in text through
<tool_call>,<tool_calls>,<function_call>, or boundary-gated Gemma<function name="...">blocks are salvaged when no structured tool call is present - malformed tool-argument JSON receives conservative repairs for invalid string control characters, trailing junk, and one unambiguous missing string/container close
- lone UTF-16 surrogate code points are removed before text reaches JSON, history, or database paths
- structured tool calls always take precedence over text salvage, preventing double execution; salvaged and repaired calls still pass through the existing repeat guard and tool-step budget
The strong-model invariant is losslessness: clean assistant text with valid structured tool calls and no relevant tag markers takes an early fast path. The original text and structured tool-call array pass through unchanged; normalization only attempts to recover output that is already malformed or encoded in the wrong channel.
Tool schemas are normalized per provider without weakening the default path for strong providers. Gemini always receives its restricted supported schema subset, including const to enum conversion and conservative combinator flattening. Other providers default to metadata.tool_schema_compat: "off"; a local or grammar-constrained backend can opt into "strict" to remove schema keywords that commonly break llama.cpp and JSON-schema-to-grammar converters. The setting applies at every native transport tool mapper: OpenAI Chat Completions, OpenAI Responses (including xAI), Ollama Chat, Anthropic Messages, Bedrock Converse, and Gemini GenerateContent.
Provider failures also pass through a shared classifier with stable reason codes such as auth, billing, rate_limit, quota_exhausted, overloaded, timeout, context_overflow, model_not_found, and content_policy_blocked. A compress_context action now gets one same-provider retry when the provider-neutral message history has safe older complete turns to remove. The retry preserves all system messages, the final user turn, and the most recent complete turns. It is guarded once per request and is forbidden after any delta, tool event, or tool execution; if compression cannot help or the compressed attempt fails, the existing configured fallback/abort path continues unchanged. Ordinary rate limits enter the stepped cooldown. Quota exhaustion uses provider reset metadata when available, tries only configured approved fallbacks, and otherwise keeps automation work queued. Quota retries do not publish transient failure notifications, preventing Teamchat storms; the eventual successful run uses the existing claimed-job and delivery guards and therefore delivers once. Interactive chat receives a localized neutral status instead of upstream provider text. Metrics expose providerQuotaLimited, providerQuotaResetKnown, and providerQuotaFallbacks in the runtime snapshot.
Entry-point differences worth knowing
- Web chat uses
/internal/runsfor the normal streaming path when the native runtime client is active. - Web chat uses
/internal/responseswhen the current request contains image/file parts that must be sent as multimodal content. - The lower-level
/internal/chat/completionsand/internal/responseshandlers both still end insessions.executeRun(). - Specialized chat agents still enter through the same native execution endpoints, but they now pass an explicit
specializedAgentexecution profile intosessions.executeRun()instead of pretending to be the defaultagent:mainpath. - Direct
@agentHandlementions anddelegate_to_specialized_agentno longer force the parent chat request to await the specialist. Clapilot persists a visible pending assistant bubble, writes one row intospecialized_agent_tasks, and lets a detached app-side worker run the specialist later. - Delegated specialist runs can now trigger a second asynchronous main-agent run after the specialist finishes, so the original main chat can continue with a later follow-up message instead of blocking the original turn.
- Generic fan-out subagents use
spawn_clapilot_subagentsinstead of temporary specialized-agent records. Clapilot writes aclapilot_subagent_batchesrow plus oneclapilot_subagent_tasksrow per worker, streams each worker into a visible pending assistant bubble, then triggers one asynchronous main-agent callback when the whole batch has no queued/running workers left. - Telegram can stream native deltas directly while keeping the same visible reply message updated in place.
- Channel model choice is layered: session override, then channel default model, then global default model.
- External bridge follow-up state is now split cleanly:
embedded_piandnativekeep continuity entirely inside Clapilot's native session state- Claude subscription keeps both the native Clapilot session state and a resumable hidden Claude CLI session id
- Codex subscription keeps both the native Clapilot session state and a resumable hidden Codex thread managed through the orchestrator session broker
Flow 2: Input enrichment, prompt assembly, memory, tools, and final model payload
Layer order in practice
The final prompt assembled inside sessions.executeRun() follows this order:
- Runtime-owned system prompt from
buildNativeSystemPrompt(),buildEmbeddedPiSystemPrompt(), or their compact variants. - Persisted compaction summary, if a previous safeguard compaction already exists.
- Bootstrap prompt-file block from
memory.bootstrap(). - Retrieved memory context from native recall storage.
- Approved learning objects from the native learning ledger, capped as the separate
approved_learning_objectsprompt layer. - Any extra system prompt passed in by the app or channel layer.
- Replayed recent session history from
agent_runs, trimmed to fit the remaining budget. - The current request message, including multimodal content parts when applicable.
Where each layer comes from
- Clapilot-side enrichment is done in
src/app/api/chat/route.ts.- It adds chat behavior rules, optional canvas instructions, route/module/page context, selected document references, and RAG context.
- With attachments, it converts the latest turn into
input_image/input_fileparts.
- Native memory bootstrap is done in
services/clapilot-agent/src/memory/index.mjs.memory.bootstrap()loads prompt files, syncsmemory/**/*.md, retrieves approved canonical assertions through lexical/vector reciprocal-rank fusion plus compatible durable-memory hits, and appends session-graph summaries.
- Approved learning retrieval is done by
services/clapilot-agent/src/learning-objects/index.mjsand wired intosessions.executeRun().- Only approved, visible, unexpired objects are eligible.
- Durable facts can become prompt context, hot snapshots stay scoped to their session/channel/entity lifetime, and procedure proposals are rendered only as approved hints rather than executable tools.
- If the prompt is over budget, the runtime drops this layer before trimming stored conversation history and records
learning.used_in_promptaudit events only for objects actually injected. - Agents can explicitly query the same approved/visible corpus with read-only
learning_searchandlearning_get_objecttools when the user asks what Clapilot has learned; candidates and rejected objects stay in the Learning settings/control-plane flow.
- Unified run-time context retrieval is done in
services/clapilot-agent/src/sessions/index.mjs.context_searchis the preferred first-pass tool for broad internal knowledge, Wiki synthesis, learned facts, memory, session summaries, and Knowledge Graph lookup.context_getreads one source-qualified result (learning:<id>,wiki:<id-or-slug>,memory:<id>,history:<id>,knowledge_claim:<id>, orknowledge_entity:<id>) before the agent relies on it as evidence.- Source-specific
memory_*,learning_*,wiki_*, andknowledge_*tools remain available for drill-down, graph traversal, and writes.
- Native runtime prompt construction is done in
services/clapilot-agent/src/sessions/index.mjs.getNativeTools()decides the tool catalog from current text, UI context, user access, and the selected tool profile.buildNativeSystemPrompt()andbuildEmbeddedPiSystemPrompt()add runtime identity, tooling rules, workspace and docs guidance, current time, and session metadata.- specialist runs now bypass the normal broad page-context prompt path, use a compact runtime prompt builder, and apply a strict allowlist from
specializedAgent.allowedToolNames. - specialist model routing is now layered: explicit runtime override, then
specialized_agents.default_model_ref, then the current chat/session model, then the global provider default. - detached specialist executions are coordinated by
src/lib/specialized-agent-tasks.ts; it claims queued tasks, runs the scoped specialist session, finalizes the visible specialist message, and optionally schedules a follow-upagent:maincallback run with the specialist result. For Team Chat@agentHandlementions, every explicitly mentioned invited specialist receives its own queued task and pending bubble in parallel. The worker adds only a compact visible room-context block of up to 10 relevant previous messages to each specialist request so specialists can understand the recent discussion without inheriting Angela's broader transcript replay. - When the resolved profile is Clapilot-code (Coding) (
embedded_pi), non-specialized providers use the Coding prompt contract and a larger tool-loop budget while still using the same provider adapter.
- Pre-send maintenance is also done in
services/clapilot-agent/src/sessions/index.mjs.maybeFlushSessionMemory()writes durable notes before the run when the transcript is getting too large.maybeCompactSessionHistory()persists a structured session summary when older history needs to be compressed.
- Post-response maintenance is detached from the provider response path.
runPostResponseMemoryMaintenance()stores summaries, records lossless replay messages, compacts the context graph, stores shared facts, and then asks the learning ledger to extract safe candidates from the same shared-facts output.- Learning extraction creates canonical durable facts or low-risk procedure drafts. Safe evidence-backed facts use the opt-out activation path immediately. The protected Learning Curator later uses its configured model to check only new or changed facts, keeping by default and rejecting only high-confidence false, unsafe, duplicate, non-durable, or clearly worthless content.
Important behavior details
lightContextkeeps the run smaller, but still allows prompt files and session memory in a reduced form. Approved-learning prompt injection is skipped in this mode.minimalContextis the stricter mode used for ultra-low-context models. It skips the normal bootstrap, retrieval, compaction-summary injection, approved-learning injection, and stored-history replay path.- Read-only Context and Learning tools remain discoverable through native tool routing, skills mode, compact tools, and specialist core allowlists; they are separate from automatic prompt injection and still enforce visibility and approval at execution time.
- Conservative learning extraction is also skipped for
lightContext,minimalContext, public embeds, specialized-agent runs, ultra tool-profile runs, and failed runs. Codex/Claude subscription-bridge runs are allowed by default because candidate creation reuses the already available shared-facts output; operators can setCLAPILOT_AGENT_LEARNING_EXTRACTION_ALLOW_SUBSCRIPTION_RUNTIME=falseto disable that path. Safe durable facts activate optimistically, while procedure proposals, corrections, conflicts, weak evidence, and unsafe categories still require review. The asynchronous model-backed curator can later reject an active fact without overriding a manual decision. - Specialized agents are isolated by session key (
agent:<specialistHandle>:openai-user:<chat-boundary>), so they replay only their own prior specialist turns for that chat boundary instead of the main agent's hidden session history. In Team Chat, prior specialist replies remain visible transcript messages, so later normal Angela turns can still use them through normal group history replay. - The runtime always chooses tools before the provider request, not after. Tool schemas are part of the actual provider payload.
- The model can trigger multiple provider requests in one run because the provider loop may need tool-call follow-ups, retries, or fallbacks.
Token efficiency: prompt caching and tool catalog
The assembled prompt is dominated by the native tool schemas (~33–45k tokens if the full catalog — currently well over 200 tools — were attached wholesale) and the static system blocks (~5–6k). Three mechanisms keep the per-turn cost down:
-
Provider prompt caching. The session runtime tags the stable prefix — runtime system prompt, persisted compaction summary, bootstrap prompt files, and stable app/UI system context — with
cache: "stable". For text-only native turns it then builds a[# Trusted Current Turn Context]user message containing current time, retrieved memory, approved Learning context, optional Mixture-of-Agents synthesis, and the raw request. That exact augmented turn is stored asagent_runs.input_payload.promptCacheReplayand replayed before the next turn when its stable-prefix hash still matches. The resulting provider conversation is append-only instead of rebuilding volatile system blocks ahead of history on every request.- Anthropic:
buildAnthropicSystemBlocks()inservices/clapilot-agent/src/providers/index.mjsconverts the system field into the array form and places a singlecache_control: { type: "ephemeral" }breakpoint on the last block in the contiguous-from-start stable run. A second breakpoint is placed on the last entry of the tools array bybuildAnthropicTools(..., { cache: true }). Reads cost ~10% of writes after the first turn. - OpenAI / Azure OpenAI: automatic caching activates on stable prefixes ≥1024 tokens;
runOpenAiLike()addsprompt_cache_key(preferspayload.userId, falls back tosessionKey) to keep matching prefixes on the same cache shard for higher hit rate.openai_compatible(vLLM, llama.cpp/llama-server, LM Studio, local MiniMax, ...) is excluded by default because many local servers strict-validate the request body and reject unknown fields. Flip Settings → ClapilotAICore → Runtime → "prompt_cache_key fuer openai_compatible" to opt in once you've verified your local server tolerates and routes by it. First-class Ollama requests use the native Ollama adapter and never receive this OpenAI-only field. - Gemini: implicit caching is automatic on Gemini 2.5+ when the prefix is stable; no body changes are needed. Explicit
cachedContentis a future workstream. - AWS Bedrock (Converse):
buildBedrockSystemBlocks()appends a{ cachePoint: { type: "default" } }marker after the last contiguous-from-start stable system block;buildBedrockTools(..., { cache: true })appends the same marker after the last tool intoolConfig.tools. Supported on Anthropic Claude and Amazon Nova models served via Bedrock. - Clapilot-code (Coding) (
agentCore = embedded_pi): uses the samepayload.messagesshape as Clapilot-code (Assistant) (native;buildEmbeddedPiSystemPrompt()wrapsbuildNativeSystemPrompt()), so the cache hint flows through unchanged and the underlying provider runner applies caching identically. - Claude CLI / Claude subscription bridge: the bridge does not place its own
cache_controlmarkers — the spawnedclaudeCLI handles caching with Anthropic internally (Claude Code uses caching aggressively by default). Clapilot sends the user conversation through stdin and supplies the runtime rules through a protected per-request--system-prompt-file, never through argv or environment values. The system portion is deterministically bounded while preserving its beginning and end. Large MCP tool schemas and client context also use per-request protected temporary files whose paths, but not contents, are supplied to the child process. Before every Claude CLI spawn, the runtime checks the combined argv/environment byte footprint and rejects unsafe input with a value-redacted size diagnostic. This avoids LinuxE2BIGfailures without demoting system instructions or exposing prompts and secrets in process arguments while keeping the MCP tool surface small (see "Bridge tool specs" below). - Codex OAuth bridge: the Codex app-server handles OpenAI caching internally for the model loop. Clapilot's contribution is the lean MCP tool surface only.
- Kill switch: flip Settings → ClapilotAICore → Runtime → "Prompt-Caching aktiv" off to fall back to the legacy uncached path (system as a single concatenated string for Anthropic/Bedrock, no
cache_control/cachePoint, noprompt_cache_key). The flag lives inapp_settings.prompt_caching_enabled(migration149_app_settings_runtime_caching.sql) and is re-read by the agent every 5 seconds, so changes take effect on the next tool-loop step without a restart.
- Anthropic:
-
Progressive tool disclosure.
compact_toolsis the effective default profile (seesessions.executeRun()auto-promotion of"full"→"compact_tools"). The model sees the always-on core tools plus a one-line-per-family manifest, and pulls full schemas for any other family on demand by callingtool_catalog_expand. For cache-enabled OpenAI-compatible providers, the initial compact surface is selected from stable session/page context rather than the current query text, preventing a different phrase from rewriting the early prompt/tool prefix. Query-specific tools remain reachable through the catalog expansion path. -
No duplicated tool catalog.
buildNativeSystemPrompt()no longer emits a per-tool- name: <first sentence>text catalog (which previously duplicated the nativetools:array at roughly 4–6k tokens for the current catalog size). Instead it ships a one-line summary ("N native tools are attached via the provider tools API") plus the on-demand family manifest. Flip Settings → ClapilotAICore → Runtime → "Tool-Text-Katalog im System-Prompt" on (stored inapp_settings.include_tool_text_catalog) to restore the text catalog when a particular runtime (e.g. some local OpenAI-compatible models) measurably benefits from a text TOC for tool-selection accuracy.
Cache interaction with progressive disclosure: a successful tool_catalog_expand call mutates the tools array for the current provider loop and may require a new tools-cache write for that step. The following user turn starts again from the stable compact surface, so query-specific expansions do not permanently churn the session's initial cache prefix.
Observing cache effectiveness
Per-request cache counters land in two new columns on agent_model_request_logs (migration 145_agent_request_cache_tokens.sql):
cache_creation_input_tokens— tokens written into the provider cache on that request (Anthropiccache_creation_input_tokens, Bedrock ConversecacheWriteInputTokenCount). Costs ~1.25× normal input tokens. OpenAI and Gemini don't surface this — their write cost is folded into the regular input count.cache_read_input_tokens— tokens served from the provider cache (Anthropiccache_read_input_tokens, OpenAIprompt_tokens_details.cached_tokens, BedrockcacheReadInputTokenCount, GeminicachedContentTokenCount). Costs ~10% of normal input tokens. This is the high-signal metric.
Extraction happens in extractUsageMetrics() (services/clapilot-agent/src/request-logs.mjs) which walks every known field name across providers. Insertion happens in recordModelRequestLog() in the same file.
The aggregated stats are exposed at Settings → Clapilot AI Core → Logs (/settings/clapilotaicore/logs), which renders ClapilotAICoreRequestLogsPanel. Four cache-specific stat cards sit beneath the existing token totals:
- Cache-Reads — cumulative
cache_read_input_tokensacross matching requests. - Cache-Writes — cumulative
cache_creation_input_tokens(only Anthropic + Bedrock will be non-zero). - Cache-Hit-Quote —
cache_read / (cache_read + input_tokens)computed only over cache-eligible requests (those with at least one non-zero cache counter). Restricting the denominator avoids the long tail of embeddings,openai_compatiblerequests, and pre-caching logs deflating the ratio. - Cache-faehige Requests — count of requests that had any cache activity at all.
Each row in the log table also shows a Cache column with R <reads> · W <writes> and the same numbers inside the expanded detail view. The raw provider usage object is still rendered in full under "Usage JSON" so any extra fields (e.g. Anthropic's cache_creation_5m_input_tokens for the 5-minute beta cache, OpenAI's audio_tokens) remain inspectable.
If you want to verify the wiring end-to-end on a fresh deployment: trigger any multi-turn Anthropic chat; from turn 2 onward the Cache-Reads counter should climb past zero, and the Cache-Hit-Quote should settle at ~80–90% on the stable tools+system prefix.
Bridge tool specs (Claude CLI bridge, Codex OAuth bridge)
Both subprocess bridges expose Clapilot's tool catalog through the same MCP stdio server (scripts/claude_clapilot_mcp.mjs) and use a two-tier surface mirroring the native compact_tools profile:
- Direct visible tools (advertised via MCP
tools/list, included in Claude CLI's--allowedToolsflag, what the upstream model actually sees in its tools array):tool_catalog_search,tool_execute, plus theCOMPACT_TOOLS_DIRECT_TOOL_NAMESset (context/memory/knowledge/learning recall, web search, navigation, copilot UI, session status, generated-video start/status, delegation — 23 tools). The model can call these in one round-trip without atool_catalog_search→tool_executedetour. For the Claude CLI bridge, direct visible comes frompayload.tools(typically already the compact_tools subset); for the Codex bridge,buildCodexClapilotToolSpecs()selects them from the full catalog usingCOMPACT_TOOLS_DIRECT_TOOL_NAMESimported fromsessions/index.mjs. - Hidden tools (stored in the MCP spec JSON, surfaced only as
tool_catalog_searchresponse payloads): every other tool in the catalog. Tools that are already direct visible are de-duplicated from the hidden set so their schemas never appear twice in the bridge's context.
Both spec builders apply compactToolDescription (first sentence, ≤120 chars) and compactJsonSchema (per-property descriptions trimmed to 90 chars, session_key descriptions stripped at any depth) so both the visible tools API and the tool_catalog_search response payloads stay small no matter how verbose the underlying tool definitions grow. Previously the Codex builder allowed description lengths up to 20k chars per tool — the compaction reduces typical search-result payloads by ~30–40%.
Specialized-agent runs on either bridge skip the hidden catalog entirely and expose only the explicit visible allowlist (still compacted).
Cross-reference
- Native loop internals, tool iteration, steering, guards, and persistence: Clapilot-code Loop
- High-level overview: Runtime Flows
- Model selection and adapters: Clapilot-Agent Providers + Models
- Session state and history replay: Clapilot-Agent Sessions
- Channel-specific ingress and approvals: Clapilot-Agent Channels
