Release 0.238
0.238.18: memory_config saves, and the oauth2 binding survives the editor
- One
memory_configschema. The agent POST and PATCH routes held two copies that had drifted (a create withchunk_limit,corpus_map_*or the Journal keys was a 400). Both uselib/agent-memory-config-schema.ts. A key sent asnullis now removed, so a field cleared in the form goes back to its default.AgentMemoryConfigDTOgainscorpus_map_limit,corpus_map_chars,max_tool_callsandmax_calls_per_tool. - The tool-group editor has no
oauth2field, and a save from it dropped the stored client-credentials binding. An absentoauth2now keeps the stored one;oauth2: nullstill clears it.
0.238.17: the corpus map in about 2k tokens, every branch shown
The “what exists” block rendered up to 24k characters (about 7.6k tokens a
turn) and filled its budget alphabetically, so on a big brain tables and
tasks never appeared. The prompt-block audit of 2026-10-05 found no
answer-quality gain from the block at that size.
- Budget 6,500 characters by default (
memory_config.corpus_map_charsper agent), shared round robin across branches, newest items first. - Branch headers carry the corpus-wide count. Three or more file titles that differ only in digits fold into one line; other types fold only on an exact duplicate, so dated titles keep their own line.
- Page summaries are left out; tables keep their schema digest. The block claims to be complete only when it is. docs/memory.md.
0.238.16: the deferred tool catalog lists groups no flow holds
Under params.tool_loading = 'deferred', a group no flow holds (an owner’s
API integration, an MCP or OpenAPI connector) gets its own catalog line,
and tool_search takes that group slug as its flow. A tool’s first
description sentence reaches the catalog only when the brain wrote it
(builtins and owner-written http tools); MCP tools, OpenAPI-compiled tools
and recipes stay names only. The search rule wording is unchanged (a
stricter one lost on the bench). docs/tools-and-skills.md.
0.238.15: the delegate roster shrinks lines instead of dropping delegates
Over its 1,200-character budget the roster dropped delegates from the end of
delegate_to, so a parent with many specialists never delegated to the
hidden ones. Every delegate now stays: lines shrink lowest rank first, to one
group chunk and then to the bare name. The tail is cut (and the cut said)
only when even the bare names overflow.
0.238.14: http tools fill omitted inputs from their schema defaults
An optional {param} the caller left out dropped its query pair, so a
paging field with default: 50 sent an unpaged request. The dispatcher now
fills absent top-level fields from the tool’s input_schema defaults before
templating. A field that is present (even null) is left alone.
0.238.13: OAuth2 client credentials for integration groups
- An integration group can carry
oauth2: a token URL and vault refs for the client id and secret. Tool templates place the token with{{oauth:<group-slug>}};tool_group_ensuredefaults the placement to a BearerAuthorizationheader. - At call time the dispatcher trades the credentials for a token through
safeFetch(the same egress rules as every api-tool call), keeps it in process memory until shortly before it expires, fetches one at a time per group, and on a 401 replaces the token once and retries once. The token goes only to the group’sbase_urlorigin; the token, client id and secret are scrubbed from every result and error. - Fix:
tool_group_ensureno longer drops a re-declaredbase_url,secret_reforauth_templateon an existing group.
0.238.12: client code caps end at now
The client code send caps counted codes created after “now minus the
window” with no upper end, so codes stamped in the future (a test fixture)
counted against every real request. The caps and
clientCodesSentLast24h count only the window before now. No change on a
live box.
0.238.9: New chat, Previous chats
“New chat” (web) and /new (Telegram) close the agent’s open chat and start
a fresh one. The old chat stays saved and searchable under Previous chats.
docs/conversation.md section 6c.
- Migration 0231 (
0231_chat_threads):chat_threads, one row per thread, a time range over the agent’sassistant_messages. Messages never move. No row means the old single thread. - What a turn reads: the history window, digests, history recall and the follow-up enrichment read only the open thread.
- The archive summary: one summarizer call per archive writes one note
(
data.kind: chat_archive, embedded, never extracted). It comes back by relevance and infind_window(kindthread). No trigger or timer runs it; a failed call leaves a plain title and a retry route. A closed range is digested alone, so no digest spans the cut. - Continue from this seeds the new chat with the archived thread’s summary, writing the summary first when it is missing.
- Routes:
GET/POST /api/assistant/threads,GET /api/assistant/threads/:id,POST …/:id/continueand…/:id/summarize./threadand/messagesanswer the open thread only (messages?thread=<id>pages an archived one). - Deleting an agent removes its archive notes with the digests.
0.238.8: the reflector skips MCP-answered turns
A turn an MCP client answered as the agent (responder_turn_record, channel
mcp) neither wakes the reflector nor reaches what it reads, so persona
notes never learn from a test model. The summarizer still reads these turns
into digests. docs/connecting-claude.md.
0.238.7: deferred tool loading (opt in per agent)
An agent with params.tool_loading = 'deferred' is sent a fixed core of its
granted tools plus tool_search and use_tool. Every other granted tool is
listed by name in a catalog in the first (cached) system block.
tool_search ranks the deferred tools in code (BM25 over tool cards,
synonyms, a usage prior) and returns their full schemas; the model calls a
loaded tool by name or through use_tool, and both dispatch, validate,
guard and trace as the real tool. An ungranted name is still refused. The
tools sent depend only on the grant, so the cached prefix does not move.
Absent or 'full' keeps the old behaviour.
- The core goes out in a fixed list order (search and read first).
update_personais in the core. - A deferred tool called by name with bad arguments gets its real input schema back, once per turn.
- Bench (101 cases, right first tool): about 11k instead of 58.5k tool tokens per call; Claude scored 88 to 90 against 91 with the full list.
- docs/tools-and-skills.md, “Deferred tool loading”.
0.238.6: responder_turn_record
Opt-in write after responder_turn_input: the user’s message and the MCP
client’s reply land in the agent’s conversation (channel mcp), so the
Assistant window, the history window, digests and replay see them. The
reply’s model is the client’s; data.authored_by names the client and
model, and a trace (mcp_turn_record) names who answered. Owner connector
only; team and client responders are refused. A channel mcp reply sends no
push. replay_window’s app arm now reads web, mobile and mcp turns (it read
web only, so mobile turns were missing too). No new trigger or cron.
docs/connecting-claude.md, docs/conversation.md.
0.238.5: box-maintain containers see the file bytes
box-maintain.sh containers now mount the mantle_web volumes read only.
Without /data/files, ocr-rescan counted every PDF as unreadable.
0.238.4: responder_turn_input
responder_turn_input (MCP, owner surface) returns one responder turn’s
exact input up to the model call, with no model call: the composed prompt,
the retrieval for the message, the history and the tool list. An MCP client
can answer as the agent with its own model and see what the agent saw. It
shares the sim’s read path. Tools default to name and first sentence;
schemas_for fetches full schemas. A peer needs it named; team and client
responders are refused. The sim’s caller history is now cut to the agent’s
history window and drives the follow-up enrichment. docs/connecting-claude.md.
0.238.3: ocr-rescan for scans indexed wrong
Before 0.238.2 a scanned PDF of two or more pages was indexed as its own
page markers, and a one-page scan stuck at body_too_short. pnpm maintain ocr-rescan (dry run by default) prints counts, pages, the models that will
run and an estimated cost from the live catalog. --apply clears the bad
text, summary, embedding and chunks and re-queues each file through the
normal extract queue in batches; --limit=N to start small. Ids and counts
only.
0.238.2: extract skips are stamped, and scans OCR again
- A node the extractor reads and finds nothing in (no parser, body too
short, media, encrypted PDF, missing bytes, a digest, an empty Telegram
turn) kept no embedding, so the boot drain and every provider recovery
queued it again. Such skips now stamp
data.extract_skipped = { reason, at }, and the drain leaves the node alone while the stamp is newer than itsupdated_at. An edit makes the stamp stale; a successful pass removes it.pnpm maintain extract-skip-stamp(dry run;--apply) stamps the old loops in plain SQL. parsePdfreturned pdf-parse’s-- N of M --page markers for a PDF with no text layer. Markers alone now parse to an empty string, and the scan takes the OCR path.
0.238.0: provider outages are visible and recover without a restart
An embedding account with no credits answered 429, every extract job dead-lettered for days, chat turns lost their context, and nobody was told. Fixing the account did not move the backlog until a restart.
- Error classes (
provider-error.ts): account errors (no credits, refused key, no key, unknown model) apart from transient ones. A no-credits 429 no longer waits through the rate-limit backoff. Embedding and chat failover also fail over on an account error. - Migration 0230 (
0230_provider_alerts):provider_alerts, one row per brain and subject, fixed reasons only. Every embed and extractor chat call reports its outcome; a call that works closes the alert. Admins see it in Needs you, the live stream and one phone push. - The circuit (
provider-circuit.ts): a confirmed account error pauses the extract queue and probes at 5, 10, 20, 40 minutes, then hourly. When a probe works the queue resumes, dead letters are re-driven and unextracted nodes swept, with no restart. A settings save or Try again (POST /api/embedding/recover) probes at once. The boot line says PAUSED while the circuit holds the queue. - Same-model backup: Settings suggests OpenRouter for OpenAI direct and
the reverse; onboarding sets it when the key is saved. The OpenAI adapter
drops an
openai/prefix, so one slug serves both routes.