Skip to content

AgentOS 2026.8.9

Choose a tag to compare

@github-actions github-actions released this 09 Aug 06:30
· 515 commits to main since this release
74effd5

A channels-and-internals release. Telegram replies stream instead of arriving all at once at the end; the seven GMGN skills stop reading as one skill listed seven times; a model's price and context window are declared once rather than restated in five places; and shell workspace lockdown stops missing a redirection that has no space around the >.

Added

  • Telegram replies stream. Telegram was the only major chat adapter without send_streaming, so resolve_channel_stream_policy picked typing_final from method presence: a typing indicator for the whole run, then the entire answer dropped in at once. It now posts one message and edits it as text arrives, driven by the same StreamThrottle Slack and Discord use — first post, then edits, never two concurrent flushes, accumulated text preserved when a flush fails. streaming_reply_kwargs carries chat_id and thread_id from the inbound message, so a stream lands in the originating chat and forum topic rather than nowhere at all on a bot with no default_chat_id, which is the normal deployment. A missing target raises instead of returning a soft "unsupported": the runtime relay only replays undelivered chunks when stream_error is set, so a quiet return would drop the user's whole answer. (Fixes #141)

    • Throttled to what Telegram actually allows. Edits default to update_interval_ms=1200. Telegram's practical ceiling is around one edit per second per chat and the relay's own coalescing defaults to 0, so without a throttle every text delta becomes an API call.

    • A 429 burst degrades instead of failing. Rate limits now surface as TelegramFloodError carrying retry_after, read from both HTTP 429 and ok: false bodies with parameters.retry_after. That feeds FloodStrikeBackoff — defined for exactly this and until now with zero callers anywhere in the tree. Three floods in 30s stop the edit loop and the remainder is delivered as a single final message, full text, no lost chunks.

    • Answers past 4096 characters roll over into a new message instead of failing, and the stream keeps editing the newest one. The budget is measured on the rendered HTML, since escaping and tags expand the text.

    • The manifest stops under-reporting. ChannelCapabilityProfile gains a streaming field mapped to ChannelCapabilities.STREAMING, declared by every adapter that implements send_streaming — Slack, Discord, Telegram, Microsoft Teams. The tag was previously reachable only through a magic transports=("streaming",) string that no adapter set, so the manifest and the Channels page claimed none of them streamed.

  • The GMGN skills are grouped, and wear their own mark. The Skills page already shipped an AgentOS Crypto Skills heading — skillGroupKey() splits it out of the shipped group on category: crypto — but all seven GMGN skills declared category: trading and rendered under "AgentOS Normal Skills". The heading existed and the skills it was built for were not in it; only senior-unilp-manager ever reached it. All seven now declare category: crypto, one line each, with no loader, wire, or snapshot-schema change since category was already parsed and serialized. Cards in that group also wore the generic package glyph, which for seven siblings from one upstream reads as one skill listed seven times: the GMGN mark now ships as a bundled asset badged with the emoji each SKILL.md already declared — 🔎 📈 💼 🛰️ 🧊 💱 🍳 — so the brand is shared and the icon is per-skill, across both the card and the detail dialog. Skill names are unchanged; renaming gmgn-*crypto-* would churn NOTICE, THIRD_PARTY_NOTICES.md, four test modules, the docs and every user's invocation for no grouping benefit the category does not already provide. (Fixes #246)

    • The mark is chosen on provenance, not on the name. It resolves on provenance.origin: gmgn-mit, never on the gmgn- prefix — a name is not a brand, which is the rule skillPublisherId exists to enforce. Provenance is self-declared frontmatter, so the trust sits in the crypto group key: it only resolves for a shipped/bundled skill, the same gate that keeps a hand-dropped directory out of a heading carrying the AgentOS name. Nothing routes through publisher, and the artwork is bundled, so no card fetches a remote image.

Changed

  • One declaration per model. A model's facts were restated in five places keyed five different ways with nothing tying them together, so bumping a tier default meant editing four or five files by hand. Worse, both lookup tables fail open in opposite directions — pricing prefix-matches down to an older model, the catalog falls through to a generic constant — so a forgotten entry produced a plausible wrong number rather than an error. That already happened once, in #139. src/agentos/model_registry.py now declares each model once: price, context window, max output, image support, and any provider-scoped window override. pricing.py and model_catalog.py derive their tables from it, and config.py builds every tier through a helper that raises at import time on a model that is not declared. Divergent id spellings are handled by DELIBERATE_SPELLING_DIVERGENCES — a bare id and its vendor-prefixed twin are different wire ids and may legitimately differ, but every difference must be declared with a written reason, and the test compares declared against observed in both directions so a forgotten edit and a stale allowlist entry both fail. (Fixes #140)

    • No prices or windows change. The first commit, on the unmodified tree, freezes what 588 model ids resolve to — every pricing prefix, catalog key, tier default and migration id, each probed bare and with four suffixes. The suffixes are the point: the pricing table is an ordered startswith scan, so an exact-id-only snapshot would miss a reordering regression. That golden file passes unchanged at every step, and tier default dicts were separately diffed across all ten profiles as byte-identical.

    • One deletion worth reading closely. The eleven-row "Bankr LLM Gateway fallback" block in _PRICING_TABLE is gone, along with the two "keep both lists in sync" comments guarding it. The block was unreachable — _lookup_static_price consults _PRICE_OVERRIDES first and every id in it was already an override — and three rows were doubly unreachable, sitting behind a shorter earlier prefix carrying a different price.

Fixed

  • Shell workspace lockdown no longer misses a redirection with no whitespace around it. --workspace-lockdown and workspace_write_deny_globs both decide what a command writes through _shell_write_targets(), whose redirection regex was anchored with (?:^|\s). The operator had to be preceded by whitespace or start-of-string, so echo x>/etc/passwd — valid shell — parsed as containing no write target and passed both checks. >& was missing from the operator set entirely, so cmd >&/etc/x slipped through even with a space, and the noclobber override >| was never handled. The tee parser carried the same anchor bug and is fixed alongside it. Descriptor duplication and closing (2>&1, >&2, 2>&-) are blanked before the scan, so the descriptor number after >& is never mistaken for a path — without that, dropping the anchor would turn every 2>&1 into a bogus write to a file named 1. <, >, ( and ) are excluded from the target character class, so cat<in>/etc/x resolves to /etc/x and process substitution >(cmd) does not produce a garbage target. (Fixes #197)

    • Only two of the six reported repro lines were actually broken. cmd 2>/tmp/leak.txt, true | cat >/tmp/out.txt, true; echo x >/tmp/out.txt and (echo x) >/tmp/out.txt all have whitespace before the operator and were already blocked. The real gaps were the no-whitespace form and >&, plus a third the issue did not list: cat<in>/etc/x.

    • The docs now say what lockdown is. This is a regex over command text and will never be airtight: a command can still write outside the workspace through python -c, cp, dd of=, sed -i, eval, or a path built from a shell variable. None of those are new and none are fixed here. docs/tools-and-sandbox.md described lockdown as "writes must stay inside the workspace" with no caveat, and now states plainly that shell lockdown is defense in depth rather than a security boundary, pointing at the OS sandbox for real containment.


Full changelog: v2026.8.7...v2026.8.9