ai: restore the quota probe on Codex, rewrite the footer
The Codex migration left /usage returning 501 and no quota signal for the
governor. Codex does expose one after all — it just isn't an HTTP endpoint.
Probe: `codex app-server` is a JSON-RPC-over-stdio surface whose
`account/rateLimits/read` returns the same snapshot the interactive TUI
shows. Handshake is initialize -> `initialized` NOTIFICATION -> read; without
the notification the read never answers. adolf-llm's /usage now drives that
and normalises the result.
Shape change, and why the consumers had to be rewritten rather than repointed:
Kimi reported fixed buckets (window_5h / weekly / window_7d). Codex reports up
to two plan-defined windows, `primary` (long) and `secondary` (shorter burst,
often null), so the payload is now {plan, pct, primary, secondary,
limit_reached} with each row as {pct, window_mins, window_label, resets}. `pct`
is the max across live windows — the single number a gate can read without
knowing which window binds.
Probing spawns a codex process (~2s), so results are cached in memory and on
the workspace volume with a 5min TTL, concurrent probes are de-duped, and a
failed refresh serves the last good reading tagged stale/as_of/age_s rather
than nothing. ?force=1 bypasses the TTL.
kimi-quota-footer-plugin -> codex-quota-footer-plugin (id, mount path and the
openclaw.json entry key all renamed together — they must agree or the plugin
silently fails to load). It now renders whatever windows the plan actually
has, shortest first, and flags limit_reached and stale readings. quota-command
updated for the same payload.
Verified: /usage returns live data (30d 4%, plan free), warm cache serves in
17ms vs ~2s cold, the gateway reaches the route, adolf loads
codex-quota-footer, and the formatter degrades to no footer on empty/null
payloads instead of breaking the reply.
Note: the account reports planType "free", not a paid ChatGPT plan.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Y5QPagv4iun1ghpwM96Ff
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{
|
||||
// Adolf P6 — OpenClaw gateway config for the "adolf" container.
|
||||
// Lives in the adolf-state VOLUME (mounted at /home/node/.openclaw), not
|
||||
// in the openai/ git repo. Secrets referenced below (${VAR}) are resolved
|
||||
// from this container's process env, itself sourced from openai/.env
|
||||
// in the ai/ git repo. Secrets referenced below (${VAR}) are resolved
|
||||
// from this container's process env, itself sourced from ai/.env
|
||||
// (gitignored) via docker-compose.yml — never inlined here.
|
||||
|
||||
gateway: {
|
||||
@@ -35,7 +35,7 @@
|
||||
// agent turn runs -- no plugin code needed, this is pure config. A voice
|
||||
// note sent to Adolf over the existing Matrix DM (source 1, see below)
|
||||
// gets transcribed by the `openai`-shaped entry below, which is redirected
|
||||
// via baseUrl/apiKey to the LOCAL faster-whisper server (openai/docker-
|
||||
// via baseUrl/apiKey to the LOCAL faster-whisper server (ai/docker-
|
||||
// compose.yml's `faster-whisper` service, same compose project as this
|
||||
// container, reachable by service name) instead of hosted OpenAI --
|
||||
// confirmed supported via src/media-understanding/runner.entries.ts's
|
||||
@@ -86,7 +86,7 @@
|
||||
models: [
|
||||
{
|
||||
provider: "openai",
|
||||
model: "deepdml/faster-whisper-large-v3-turbo-ct2", // must match WHISPER__MODEL in openai/docker-compose.yml
|
||||
model: "deepdml/faster-whisper-large-v3-turbo-ct2", // must match WHISPER__MODEL in ai/docker-compose.yml
|
||||
baseUrl: "http://faster-whisper:8000/v1",
|
||||
},
|
||||
],
|
||||
@@ -117,7 +117,7 @@
|
||||
},
|
||||
|
||||
// Model provider: adolf-llm (P2/P4), the Kimi-CLI OpenAI-compatible
|
||||
// wrapper on :8010. Its HTTP server (openai/adolf-llm/server.js) performs
|
||||
// wrapper on :8010. Its HTTP server (ai/adolf-llm/server.js) performs
|
||||
// NO api-key/Authorization validation at all -- ADOLF_KEY's value is
|
||||
// functionally irrelevant to adolf-llm itself. It's still wired through
|
||||
// env (not hardcoded) because OpenClaw's custom-provider schema requires
|
||||
@@ -182,7 +182,7 @@
|
||||
},
|
||||
},
|
||||
|
||||
// MCP registry (P6) -- same servers as openai/shared-mcp.json,
|
||||
// MCP registry (P6) -- same servers as ai/shared-mcp.json,
|
||||
// expressed in OpenClaw's own mcp.servers schema. `type: "http"` is
|
||||
// OpenClaw's documented CLI-native alias for transport: "streamable-http".
|
||||
mcp: {
|
||||
@@ -193,12 +193,12 @@
|
||||
// tool bundle is built). CORRECTION (kb#144 second pass, 2026-07-22):
|
||||
// this does NOT reach the model on Adolf's kimi backbone -- Kimi CLI
|
||||
// (inside the separate adolf-llm container) reads its own
|
||||
// project-root .mcp.json, seeded from openai/shared-mcp.json, and
|
||||
// project-root .mcp.json, seeded from ai/shared-mcp.json, and
|
||||
// applies ITS OWN enabledTools/disabledTools (McpServerCommonFields,
|
||||
// computeEnabledNames). Live wire.jsonl verification (restart + one
|
||||
// real turn) proved OpenClaw's toolFilter alone left Kimi's actual
|
||||
// tool counts unchanged. This block is still correct for OpenClaw's
|
||||
// own MCP client surface -- see openai/shared-mcp.json for the layer
|
||||
// own MCP client surface -- see ai/shared-mcp.json for the layer
|
||||
// that actually scopes what the model sees.
|
||||
//
|
||||
// Scoped to Adolf's CORE memory ops: recall/retain/reflect (the
|
||||
@@ -270,7 +270,7 @@
|
||||
// network_mode: host on the Agap host), not a second copy. It can
|
||||
// place real orders on live marketplace accounts, so it's gated by a
|
||||
// shared bearer token (MARKETPLACE_MCP_TOKEN in Vaultwarden / this
|
||||
// container's env, injected via openai/.env -> docker-compose.yml).
|
||||
// container's env, injected via ai/.env -> docker-compose.yml).
|
||||
// Reached via host.docker.internal, same reasoning as kanboard above.
|
||||
// kb#144: scoped to READ-ONLY discovery (find_best/search/product/
|
||||
// recommendations/reviews/compare/status). Cuts the checkout
|
||||
@@ -316,7 +316,7 @@
|
||||
// open JSON-RPC listener handed ha_call_service/wiki_edit/todoist
|
||||
// writes to anyone). Same pattern as marketplace above:
|
||||
// AGAP_MCP_TOKEN lives in Vaultwarden, is injected into this
|
||||
// container via openai/.env -> docker-compose.yml, and is only
|
||||
// container via ai/.env -> docker-compose.yml, and is only
|
||||
// substituted here -- never inlined. The token maps to agent id
|
||||
// `adolf` in AGAP_MCP_AGENT_TOKENS, which is also what the kb#147
|
||||
// vault gate reads to allow vw_* (adolf = trust_class trusted).
|
||||
@@ -340,7 +340,7 @@
|
||||
entries: {
|
||||
// Hindsight memory plugin (kb #75, H3) — installed external plugin
|
||||
// under .openclaw/extensions/hindsight-memory, bind-mounted read-only
|
||||
// from openai/hindsight-openclaw-plugin (see that project's
|
||||
// from ai/hindsight-openclaw-plugin (see that project's
|
||||
// docker-compose.yml adolf.volumes). Structural successor to
|
||||
// cognee-memory above: LLM-free recall inject (before_prompt_build) +
|
||||
// async retain (agent_end) against the hindsight service, bank
|
||||
@@ -356,7 +356,7 @@
|
||||
hooks: { allowConversationAccess: true, allowPromptInjection: true },
|
||||
},
|
||||
// Kimi quota readout (kb #62) — installed external plugin, bind-mounted
|
||||
// read-only from openai/quota-command-openclaw-plugin (see that
|
||||
// read-only from ai/quota-command-openclaw-plugin (see that
|
||||
// project's docker-compose.yml adolf.volumes) onto
|
||||
// .openclaw/extensions/quota-command. Registers a `/quota` native
|
||||
// command; no hooks, so no allowConversationAccess/allowPromptInjection
|
||||
@@ -364,10 +364,10 @@
|
||||
"quota-command": {
|
||||
enabled: true,
|
||||
},
|
||||
// Kimi quota footer (kb #85) — installed external plugin, bind-mounted
|
||||
// read-only from openai/kimi-quota-footer-plugin (see that project's
|
||||
// Codex quota footer (kb #85) — installed external plugin, bind-mounted
|
||||
// read-only from ai/codex-quota-footer-plugin (see that project's
|
||||
// docker-compose.yml adolf.volumes) onto
|
||||
// .openclaw/extensions/kimi-quota-footer. Appends a compact Kimi
|
||||
// .openclaw/extensions/codex-quota-footer. Appends a compact Codex
|
||||
// usage line to every outgoing reply via the reply_payload_sending
|
||||
// hook (not a raw conversation hook, so no allowConversationAccess/
|
||||
// allowPromptInjection opt-in needed), reusing the same LLM-free
|
||||
@@ -376,11 +376,11 @@
|
||||
// deliverOutboundPayloadsInternal) as long as channels.matrix.streaming
|
||||
// stays unset/"off" as it is today — see the plugin's index.js header
|
||||
// comment for the streaming caveat if that ever changes.
|
||||
"kimi-quota-footer": {
|
||||
"codex-quota-footer": {
|
||||
enabled: true,
|
||||
},
|
||||
// Todoist idea capture (kb#170 component 1) — installed external
|
||||
// plugin, bind-mounted read-only from openai/todoist-capture-plugin
|
||||
// plugin, bind-mounted read-only from ai/todoist-capture-plugin
|
||||
// (see that project's docker-compose.yml adolf.volumes) onto
|
||||
// .openclaw/extensions/todoist-capture. Registers a `/idea` native
|
||||
// command; no hooks (no allowConversationAccess/allowPromptInjection
|
||||
|
||||
Reference in New Issue
Block a user