Skip to content

Release 0.238

0.238.18: memory_config saves, and the oauth2 binding survives the editor

  • One memory_config schema. The agent POST and PATCH routes held two copies that had drifted (a create with chunk_limit, corpus_map_* or the Journal keys was a 400). Both use lib/agent-memory-config-schema.ts. A key sent as null is now removed, so a field cleared in the form goes back to its default. AgentMemoryConfigDTO gains corpus_map_limit, corpus_map_chars, max_tool_calls and max_calls_per_tool.
  • The tool-group editor has no oauth2 field, and a save from it dropped the stored client-credentials binding. An absent oauth2 now keeps the stored one; oauth2: null still 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_chars per 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_ensure defaults the placement to a Bearer Authorization header.
  • 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’s base_url origin; the token, client id and secret are scrubbed from every result and error.
  • Fix: tool_group_ensure no longer drops a re-declared base_url, secret_ref or auth_template on 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’s assistant_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 in find_window (kind thread). 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/continue and …/:id/summarize. /thread and /messages answer 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_persona is 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 its updated_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.
  • parsePdf returned 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.