Skip to content

AgentOS 2026.8.29

Choose a tag to compare

@github-actions github-actions released this 29 Aug 14:29
· 178 commits to main since this release
53f1359

A release about reaching the agent by email, and about the places where output left the process without being counted — bytes into memory, secrets into the transcript.

Email is a channel

A mailbox is now a first-class channel (type = "email"). Inbound over IMAP polling, outbound over SMTP with In-Reply-To/References so a reply stays in the mail thread it answers. There is no platform app to register — IMAP/SMTP credentials are the whole setup.

One mail thread is one session. Quoted history is stripped before the text reaches the model, HTML-only mail is flattened to text, and inbound attachments plus generated artifacts ride the shared attachment pipeline under the usual size limits.

Access is fail-closed: allowed_senders is required and lists exact addresses or *@domain patterns. Mail from the agent's own address, and anything marked auto-generated (Auto-Submitted, X-Autoreply, List-Id, Precedence: bulk), is dropped — an autoresponder on the other end cannot start a mail loop. (#369)

Alongside it, a DM-shaped channel whose surface is itself threaded can opt into one session per thread with metadata['dm_thread_scoped']. Adapters that do not set it keep one session per peer, so Slack, Discord and Telegram DM keys are unchanged.

A memory view for what the agent knows

The Web UI console gets a browsable /memory view — sidebar entry, g m chord — with a curated-memory editor (entry count, size, live save), a knowledge-base document table with per-file status and manual ingest, a raw source-file explorer, and a semantic search explorer.

Behind it, memory.ingest grows multi-format text extraction and directory ingestion — PDF, DOCX, PPTX, Markdown, text, CSV, JSON/YAML and code files — scanning <workspace>/knowledge_base/. It is exposed as memory.curated.* and memory.knowledge_base.* JSON-RPC methods and as agentos memory ingest / agentos memory curated on the CLI. (#368)

Output that left without being counted

Three redaction and download-cap holes, all the same shape: a check that ran after the damage.

  • execute_code output is redacted. shell.py already ran every output surface through redact_terminal_output; execute_code bypassed redaction entirely, so a script printing os.environ or reading a credential file leaked every secret verbatim into the transcript. Redaction now happens at _execution_result_json, the one choke point for all eight return paths. (#490)
  • Credentials are masked in git tool output. git_diff returns working-tree and staged content verbatim, so a .env committed once kept reaching the model in cleartext on every diff while read_file and grep_search already redacted. Masking happens at the single _run_git chokepoint, on the sandboxed and subprocess paths, on success and failure alike. A companion fix teaches the assignment pass about diff markers: +MY_SECRET=… went unmasked where MY_SECRET=… was masked. (#496)
  • The image tool and the GitHub skill hub stop reading at the cap. _fetch_image_url buffered the whole response body and only then compared it against the 20 MB ceiling; GitHubSource.fetch() buffered every blob of a skill directory with no per-blob cap and no total budget. Both now stream and stop the read the moment the accumulated size passes the limit — 20 MB for an image, 8 MiB per blob against a 32 MiB total for the hub — and the hub fails closed rather than installing a truncated bundle. A timeout bounds time, not bytes. (#506, #510)

Fixes

  • Slack and scheduler webhooks survive a blip. Both routed their HTTP calls straight at httpx and failed on the first error; they now go through the same retry_request helper Discord uses — exponential backoff with jitter on 429 (honouring Retry-After), 500/502/503/504, connect errors and read timeouts. Fatal statuses such as 400/401 still fail on the first attempt. Retrying a read timeout can duplicate a delivery the receiver already accepted, the same trade-off Discord has always made; the webhook payload's jobId is the receiver's dedupe key. (#469)
  • A backing-off cron no longer busy-spins SQLite. next_due_at looked only at next_run_at while iter_due also waits on backoff_until, so after a few failures on a frequent schedule the timer woke early, yielded nothing, and hammered the database for the whole backoff window. It now reports MIN(MAX(next_run_at, backoff_until)). (#537)
  • A one-shot AT schedule in the past is refused. It used to be stored with next_run_at behind us and delete_after_run=True, firing immediately on the next tick with a stale payload. (#486)
  • MCP stdio reads with readexactly. read(n) can return a short buffer, which truncated chunked tool results into a json.loads failure; EOF now raises a clear truncated-body error. (#537)
  • Telegram entity offsets are sliced on the UTF-16 grid. Offsets and lengths are UTF-16 code units but were applied to a Python str by code point, so an emoji before /help@mybot in a group made the bot ignore a command aimed at it. (#537)
  • An exhausted provider failover chain says so. next_fallback_after_failure() advanced the chain index unbounded and surfaced a bare out-of-range IndexError from _build_provider instead of No more provider fallbacks available. (#488)
  • Discord keeps dispatching after a reconnect. Opcode 7/9 and a dropped socket reconnected and then returned from _dispatch_loop — heartbeat resumed and health still read connected, but messages and slash commands were never read again. (#538)
  • Cancelling xAI sign-in stops the poll loop. Cancel only reset the visible card, so an expiry could paint an error after dismissal and a restarted sign-in could be wiped by the old loop completing. (#538)
  • A real nested workspace/ folder is no longer stripped. Any absolute path containing a workspace segment was rewritten from the last such segment, so reads and writes landed on a sibling file. Paths already inside the root are left alone; sandbox /workspace/... still remaps. (#538)
  • The projects page header stacks on mobile instead of overflowing.

Thanks to @iamhaniofficial, @Carlys17, @tejajakarulloh and @bukeeastrey.