Skip to content

fix(email-agent): make undo window configurable for chat-speed bulk ops - #2449

Merged
itomek merged 3 commits into
mainfrom
autofix/issue-2447
Jul 24, 2026
Merged

fix(email-agent): make undo window configurable for chat-speed bulk ops#2449
itomek merged 3 commits into
mainfrom
autofix/issue-2447

Conversation

@github-actions

@github-actions github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

The bulk-archive undo window was 30s — calibrated for an instant UI button — but a chat-mediated multi-item archive runs through the LLM tool-loop and can take longer than that on a local model, so the "undo within the window" offer was already stale by the time the user could act. The default is now 120s (still overridable via GAIA_EMAIL_UNDO_WINDOW_SECONDS), applied consistently across the tools — the prior autofix only touched config.py and left hardcoded getattr(..., 30) fallbacks in the archive / organize / phishing tools plus the generated openapi.email.json / specification.html, which this syncs.

Test plan

  • Unit — test_undo_window_config_2447.py6 passed; rest-contract asserts undo_window_seconds == 120
  • Real-world, macOS — bulk-archived 3 messages via the real chat tool-loop (real Metal inference), then undo_archive_batch restored all 3 to INBOX; undo_window_seconds=120 confirmed. (The Mac's fast GPU kept the archive under 30s, so the original >30s failure wasn't reproduced live; 120s comfortably covers a >4× slower run.)
  • Agent UI — not captured: the email sidecar exposes no REST-injectable no-OAuth mailbox seam (FakeGmailBackend is a Python-construction seam only) and live OAuth is non-interactive on the keychain-locked box. Follow-up: expose the fake-backend seam via an env hook for no-OAuth UI regression.

Closes #2447

@github-actions github-actions Bot added the agent::email Email agent changes label Jul 24, 2026
@itomek-amd
itomek-amd marked this pull request as ready for review July 24, 2026 04:13
@github-actions

Copy link
Copy Markdown
Contributor Author

Verdict: Request changes — the code change is correct and well-tested, but the "raise the default to 120s" sweep is incomplete, so the package would ship docs that contradict themselves.

This PR raises the email agent's undo window default from 30s to 120s (still overridable via GAIA_EMAIL_UNDO_WINDOW_SECONDS, with a fail-loud error on a bad value) so a slow chat-mediated bulk archive stays undoable after it finishes. The config change, validation, and tests are solid.

Two things to fix before merge:

  1. Stale "30s" undo-window text left behind in several docs. The sweep updated the endpoint blocks but missed the summary paragraphs and the npm-side integrator docs. The result is that the same shipped spec file states both "reversible for 120s" and "reversible within a 30-second undo window", and the npm SPEC.md / SKILL.md still tell integrators the window is 30s. Per GAIA's doc-sync rule a behavior change must update every doc that describes it — please finish the sweep and regenerate the rendered spec.
  2. The PR description contradicts the diff. The visible description says "The 30s default is unchanged (no doc/contract churn)" and the technical notes argue against bumping the default — but the code does raise it to 120s and does churn the contract surfaces (matching the CHANGELOG entry). Update the description so the merge record matches what shipped.

Real-world evidence

evidence-bundle.md is present and shows real, non-inference execution (not fabricated): a live FastAPI TestClient against the actual gaia_agent_email app returned undo_window_seconds: 120 in the archive response body; direct env resolution confirmed override (=4545), default (unset → 120), and fail-loud (=notanumberConfigurationError); and pytest ran green — 115 passed (contract + new config suite) and 34 passed (openapi conformance). This confirms the code behaves as intended, so my verdict rests entirely on the doc-sync gap, not on the runtime behavior. Screenshot is N/A — this is a backend config default with no rendered-pixel change; the underlying-route evidence the CI lane can produce is present.

🔍 Technical details

Issues

🟡 Incomplete doc-sync — stale "30s" undo-window claims (multiple files)

The default-change sweep updated the endpoint descriptions but missed these, leaving contradictory undo-window durations in shipped docs:

  • spec_html.py:916 — summary paragraph still reads "reversible within a 30-second undo window", while the endpoint blocks in the same render now say 120s. Because specification.html is a pure render of this function (guarded by tests/test_spec_html_artifact.py), specification.html:226 inherits the stale text and the drift test still passes (both are stale together) — so CI won't catch it.

      <code>/v1/email/send</code>. Both are reversible within a 120-second undo window via
    

    After editing, regenerate the artifact: python -m gaia_agent_email.spec_html.

  • hub/agents/email/npm/SPEC.md:245 — "Both are reversible inside the 30s undo window:"

  • hub/agents/email/npm/SKILL.md:108, 110, 125, 363 — "within the 30s window" / "within 30s"

CHANGELOG.md:137 (npm) — the "reversible within 30 seconds" line is a historical past-version entry describing when the capability shipped; leave it as-is (rewriting history would be wrong). GAIA's hub doc-sync rule (README/SPEC/SKILL/CHANGELOG must move together) is exactly the surface at risk here.

🟡 PR description describes an abandoned approach. The visible body ("30s default is unchanged") and the technical-details section ("Why not just bump the default?") describe a keep-30s-add-override design, but the diff raises the default to 120s across the contract surfaces and the CHANGELOG entry is titled "Undo window default raised to 120s". Please align the description with the shipped change so the squash-merge record is accurate.

🟢 Weak override test (tests/test_undo_window_config_2447.py:29) — test_env_override_raises_window sets GAIA_EMAIL_UNDO_WINDOW_SECONDS=120 and asserts 120, which equals the default, so it can't distinguish "override honored" from "override ignored". Use a non-default value (the evidence bundle used 45):

def test_env_override_raises_window(monkeypatch):
  monkeypatch.setenv("GAIA_EMAIL_UNDO_WINDOW_SECONDS", "45")
  assert default_undo_window_seconds() == 45
  assert EmailAgentConfig().undo_window_seconds == 45

Strengths

  • Fail-loud configdefault_undo_window_seconds() (config.py:66) raises an actionable ConfigurationError on a malformed/non-positive override instead of silently defaulting, matching the No-Silent-Fallbacks rule; the validate() guard mirrors the existing followup_window_days pattern.
  • Single source of truth — routing the default through field(default_factory=...) means the override flows to both the chat-tool path and the REST _undo_window_seconds() path with no duplication.
  • Good test + evidence coverage — unset/override/blank/non-integer/non-positive/validate cases plus a real end-to-end HTTP response in the evidence bundle.

github-actions Bot and others added 3 commits July 24, 2026 00:48
The 30s undo window is calibrated for an instant-UI-button undo. A
chat-mediated bulk archive runs through the LLM tool-loop and can take
longer than 30s to complete on a slow local model, so the closing
"undo within the window" offer is already stale by the time the user
acts. Make the floor overridable via GAIA_EMAIL_UNDO_WINDOW_SECONDS so
those deployments can raise it; a malformed override fails loudly.

Closes #2447
…2447)

8cc970d raised the undo-window default 30s -> 120s in config.py but left
the REST contract test, endpoint docstrings, generated OpenAPI/HTML specs,
and tool-mixin fallback defaults still saying 30s.
@itomek
itomek force-pushed the autofix/issue-2447 branch from 6916ed9 to 8eb425b Compare July 24, 2026 04:48
@itomek
itomek enabled auto-merge July 24, 2026 04:49
@github-actions

Copy link
Copy Markdown
Contributor Author

🟡 One stale "30-second" claim slipped through in the mailbox-actions section intro of the contract spec — the endpoint blocks now say 120s, but the lead paragraph still says 30s, so the spec contradicts itself.

🔍 Technical details

hub/agents/email/python/gaia_agent_email/spec_html.py:916 — the <p> intro for the "Mailbox actions — archive & quarantine" section:

Both are reversible within a 30-second undo window via

This template string was not updated in this PR. Because specification.html is generated from spec_html.py, the rendered spec at specification.html:226 carries the same stale claim. The individual archive_block and quarantine_block descriptions (lines 620/646 in spec_html.py) were updated correctly, leaving the section header/intro inconsistent with them.

Fix: change a 30-second undo windowa 120-second undo window in spec_html.py and regenerate specification.html.

@itomek
itomek added this pull request to the merge queue Jul 24, 2026
Merged via the queue into main with commit 524d328 Jul 24, 2026
28 of 29 checks passed
@itomek
itomek deleted the autofix/issue-2447 branch July 24, 2026 12:45
pull Bot pushed a commit to bhardwajRahul/gaia that referenced this pull request Jul 25, 2026
…md#2458)

The amd#2163 bulk-undo feature works mechanically but was unreachable in a
normal two-turn "archive several… then undo" flow — the exact scenario
it shipped for. Before: "undo that" with no id demanded an internal
batch uuid the user can't know, and the first attempt at fixing that
stored the recall as a plain in-memory attribute on the agent object —
which the email sidecar discards and rebuilds fresh on every single
`/v1/email/query` request, so it never actually worked across turns.
After: `undo_archive_batch` recalls the most recent still-undoable
archive batch from the persisted action log, which survives across
requests because it's keyed by the sqlite file, not the Python instance
— a bare "undo that" now restores the most recently archived batch
without the user ever seeing an id, whether that's the same turn or a
fresh request.

Closes amd#2456

## Test plan

- [x] `python -m pytest
hub/agents/email/python/tests/test_undo_reachable_2456.py
hub/agents/email/python/tests/test_bulk_archive_gate_2163.py
hub/agents/email/python/tests/test_rest_contract.py -q` passes (119
tests) — including a new boundary test that builds two SEPARATE
`EmailTriageAgent` instances against the same mailbox/db (mirroring how
the sidecar really constructs one agent per request) and confirms the
second instance's id-less undo still restores the first instance's batch
- [x] `python -m pytest hub/agents/email/python/tests/ -q` passes (947
tests)
- [x] `python util/lint.py --all` passes
(Black/isort/Pylint/Flake8/Bandit clean; MyPy warnings are pre-existing
and non-blocking)
- [ ] **On-hardware, real two-turn flow (Gemma-4-E4B):** turn 1 "archive
those N promos"; turn 2 "undo that — put them back", across the real
`/v1/email/query` request boundary the Agent UI drives. Confirm both
messages return to INBOX (Gmail + `undone_at` in `state.db`), with no
prompt for a batch id.

> ⚠️ **Needs manual validation** — the automated checks now exercise the
real
> per-request agent boundary (two separate instances, not the same
object
> across simulated turns), but still can't confirm the local LLM
actually
> calls `undo_archive_batch` with no id on a conversational "undo that,"
or
> that the 120s window (amd#2447) holds up against issue amd#2456's own
measured
> single-turn latency (40–140s on Gemma-class hardware).

<details>
<summary>🔍 Technical details</summary>

**Why the first attempt didn't work.** The email sidecar is a stateless
per-request stub (`query_routes.py` module docstring: "context is
pushed,
never pulled... the sidecar stays stateless") — `build_query_agent()`
constructs a brand-new `EmailTriageAgent` on every `/v1/email/query`
call, and
both the Agent UI relay (`src/gaia/ui/email_sidecar/relay.py`) and the
CLI
drive this same endpoint, not the older stateful `/v1/email/agent/*`
surface
that caches one agent per `session_id`. The `QueryRequest` body also
carries no
`session_id` — only a per-run `run_id` (cancellation only). Storing
`_last_archive_batch_id` as a plain instance attribute meant it was
discarded
before the very next request. The original unit tests didn't catch this
because they called `_reset_organize_counter()` twice on the *same*
agent
object to simulate "the next turn" — they never crossed the real request
boundary.

**The fix.** `action_store.py` gains
`fetch_last_undoable_batch_id(db, *, window_seconds)`: it ranks archive
batches by completion time (the same completion-anchored window
`fetch_batch_undoable` already uses, amd#2163) and returns the most recent
one
that still has at least one `undone_at IS NULL` row.
`organize_tools.py`'s
`undo_archive_batch` checks the in-memory `_last_archive_batch_id` first
(a
same-instance fast path, still useful within one turn), then falls back
to
this DB query — the persisted, cross-request source of truth. No new
identity/session key was needed: `state.db` is already
one-per-local-install
(`EmailAgentConfig.resolved_db_path`, default `~/.gaia/email/state.db`),
so
"most recent batch in this db" already IS "most recent batch for this
user" —
there's no multi-tenant boundary at this layer to accidentally cross.

**Tests.** `test_undo_reachable_2456.py` gains
`test_undo_with_no_id_restores_across_fresh_agent_instance`: builds
agent A,
archives a batch through it, closes it, then builds a *separate* agent B
against the same live mailbox and `state.db` (exactly how
`build_query_agent`
reconstructs an agent per request) and confirms a bare
`undo_archive_batch()`
call on B restores A's batch. This test fails against the prior
instance-attribute-only code and passes with the DB-backed recall; the
existing same-instance regression tests are kept unchanged.

**Everything else — the rebase onto main, amd#2449's 120s window, the
`spec_html`/`specification.html` doc-sync — is unchanged from the
original
PR; only the batch-recall persistence mechanism was reworked.**

**Eval note:** `undo_archive_batch`'s docstring/schema was already
flagged as
an LLM-affecting tool-schema change (required → optional `batch_id`) in
the
original PR; that still applies here (the schema itself is unchanged by
this
follow-up) and a `gaia eval agent` run is recommended before merge,
alongside
the on-hardware check above.
</details>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Tomasz Iniewicz <itomek@users.noreply.github.com>
Co-authored-by: Ovtcharov <kovtchar@amd.com>
@itomek itomek mentioned this pull request Aug 10, 2026
8 tasks
pull Bot pushed a commit to bhardwajRahul/gaia that referenced this pull request Aug 13, 2026
# GAIA v0.23.0 Release Notes

GAIA v0.23.0 makes the agents easier to get, safer to run, and easier to
extend. You can now browse, install, and run agents straight from the
terminal with `gaia hub`, and add new capabilities to an agent as
signed, auditable skills. Under the surface it's a security release: the
local API and MCP bridge no longer expose themselves to the network by
default, the confirmation prompt that pauses an agent before it sends
mail, writes a file, or runs a command now works from the terminal and
over the local API and MCP — not just inside the graphical app — and an
agent can no longer quietly reach into your `~/.gaia` config or slip
crafted SQL into the database agent. Connecting a Microsoft account is
now an explicit Personal-or-Work/School choice with a zero-setup
sign-in.

**Why upgrade:**
- **Get agents from the terminal** — `gaia hub` browses, installs
(behind a trust prompt for unverified agents), runs, and removes agents
without leaving the shell.
- **Every agent asks before it acts** — the confirmation gate for
sending mail, writing files, and running commands now works in the
terminal, over the local API, and across MCP tools, not only in the
Agent UI.
- **Safer by default** — the MCP bridge binds to localhost, the local
API refuses credentialed cross-origin requests from arbitrary sites, MCP
servers launch without a shell, and an agent can't write into `~/.gaia`
or reach the database with crafted SQL.
- **Build and share skills safely** — `gaia skill` makes skills
first-class: create, import, sign with trust tiers, and audit them
before sharing. They're opt-in — you add the ones you want.
- **Connect a Microsoft account without a secret** — Personal and
Work/School are now separate connectors with device-code sign-in and no
client secret required.

<Note>
**The email agent is beta and CLI-first this release.** It runs locally
and never sends,
forwards, or deletes without your confirmation — that safety gate is
verified. This cycle
was mostly robustness and correctness: sturdier Outlook and calendar
handling, honest
reporting when a scan is truncated, and a long list of fixes (see Bug
Fixes). It's still
early — a full inbox triage can currently time out on larger mailboxes,
and autonomy is
experimental and not yet wired up in the packaged sidecar. Treat its
output as a draft to
review, and please report what you run into.
</Note>


## Breaking Changes

### `GAIA_MICROSOFT_TENANT` is gone

The Microsoft connector was split into two explicit connectors —
Personal and Work/School — each with its own hard-coded tenant, so the
`GAIA_MICROSOFT_TENANT` environment variable no longer does anything and
has been removed (PR [amd#2729](amd#2729)). If
you set it to work around the old single-connector tenant guessing, drop
it and pick the connector that matches your account instead (see
*Microsoft accounts* below).


## What's New

### Install and run agents from the terminal — `gaia hub`

Getting an agent used to mean the graphical app or a manual pip install.
Now the hub is in your shell: `gaia hub list` shows the catalog, `gaia
hub install <agent> --trust` installs one (the `--trust` is required for
an unverified agent — it will not install silently), and `gaia hub
uninstall <agent>` removes it. The install → run → uninstall round-trip
works end to end against the live catalog, with the trust prompt
actually enforced (PRs [amd#2484](amd#2484),
[amd#2530](amd#2530),
[amd#2708](amd#2708)). Try it: `gaia hub
list`.


### Every agent asks before it acts — beyond the Agent UI

The confirmation prompt that pauses an agent before a consequential
action — sending or deleting mail, writing a file, running a shell
command — used to work only inside the Agent UI; from a terminal, the
local API, or an MCP tool call those actions could run unprompted. This
release closes those paths: the gate now fires from a plain terminal,
through the `gaia api` server, and across MCP tool calls, classifying a
tool as read-only or mutating and failing closed when unsure. The agent
stops and asks before the action, declining leaves nothing changed, and
setting `GAIA_AUTO_APPROVE_TOOLS=1` in your environment is the explicit
way to opt out (PRs [amd#2475](amd#2475),
[amd#2544](amd#2544),
[amd#2846](amd#2846),
[amd#2854](amd#2854)).


### Safer by default — a security-focused release

Several local exposures are closed this release. The MCP bridge binds to
`127.0.0.1` by default instead of every interface, so it isn't reachable
from other machines on your network unless you pass a bind-all host, and
it can now require an `--auth-token` that is actually enforced rather
than ignored. The local API server no longer echoes an arbitrary origin
back with credentials allowed — a cross-origin request from a site that
isn't allow-listed is refused. MCP servers are launched without going
through a shell, so a server name can't smuggle shell metacharacters; an
agent can no longer write into your `~/.gaia` configuration; and SQL
supplied by the model is blocked from reaching the database agent's
statements rather than being executed (PRs
[amd#2246](amd#2246),
[amd#2238](amd#2238),
[amd#2344](amd#2344),
[amd#2844](amd#2844),
[amd#2847](amd#2847),
[amd#2860](amd#2860)).


### Build and share skills — `gaia skill`

A skill gives an agent a new capability from a folder with a manifest —
no new Python, no forking the agent. `gaia skill create <name>`
scaffolds one, `gaia skill import` adds a skill so an agent can discover
it, and `gaia skill list` / `info` show what's installed and the
permissions each one declares. Sharing is guarded: skills carry
signatures with trust tiers — an unsigned or untrusted skill is capped
at the lowest tier, and tampering is caught by checksum — a pre-publish
audit rejects a skill that attempts prompt injection or
`shell=True`/`eval`, and `gaia skill migrate` converts skills authored
in other formats. Skills are opt-in: no agent loads them automatically
yet, so you add the ones you want (PRs
[amd#2669](amd#2669),
[amd#2692](amd#2692),
[amd#2702](amd#2702),
[amd#2693](amd#2693)). Try it: `gaia skill
list`.


### Microsoft accounts: Personal and Work/School, no secret required

Connecting a Microsoft account is now two clear choices instead of one
connector guessing your tenant. `gaia connectors list` shows Microsoft
as two distinct connectors — Personal and Work/School — each with a
zero-setup device-code sign-in (a code and a URL to visit) and no client
secret required for a public app registration (PRs
[amd#2718](amd#2718),
[amd#2364](amd#2364)).


### Lemonade Server 11.5.0

This release runs against Lemonade Server 11.5.0 — the version installed
by `gaia init` and pinned across CI and the installer.


## Bug Fixes

A selection of the user-visible fixes this release — the full list is in
the changelog below.

- **Triage now paginates large inboxes and reports truncation honestly**
(PR [amd#2646](amd#2646)) — no more silently
dropping mail past a hidden limit.
- **Thread messages come back sorted and numbered** (PR
[amd#2570](amd#2570)) — "reply to 3" hits the
message shown at position 3, not raw backend order.
- **The inbox pre-scan stops reporting a guess as a verdict** (PR
[amd#2587](amd#2587)) — an uncertain
classification is surfaced as uncertain.
- **An email conversation survives its turns** (PR
[amd#2837](amd#2837)) — a follow-up question
keeps the session context instead of starting over.
- **A bare reconnect no longer guts a mailbox** (PR
[amd#2733](amd#2733)) — reconnecting an
account keeps its existing grants instead of wiping them.
- **Restore from Trash anytime** (PR
[amd#2542](amd#2542)) — undo an archive/trash
without a narrow time window; the dead permanent-delete path was
removed.
- **A low-priority sender no longer forces a promotional label** (PR
[amd#2774](amd#2774)) — sender priority stops
overriding the actual content classification.
- **The agent survives an OpenMP double-init** (PR
[amd#2508](amd#2508)) — a mid-conversation
native-library clash no longer kills the run.
- **Large tool results are truncated to valid JSON** (PR
[amd#2645](amd#2645)) — an oversized result no
longer produces unparseable output.
- **A missing model surfaces as a real 404** (PR
[amd#2245](amd#2245)) — the builder names the
missing model instead of a generic placeholder.
- **GPU is detected on all platforms and default_device is honoured**
(PR [amd#2244](amd#2244)).
- **A sidecar that is alive but has stopped serving is now detected**
(PR [amd#2707](amd#2707)) — a wedged agent
process is caught instead of hanging.
- **The model-slot lease is held across inference, not just the load**
(PR [amd#2394](amd#2394)) — a second agent
can't evict the model mid-generation.
- **A browser that never launched is surfaced** (PR
[amd#2507](amd#2507)) — a failed OAuth browser
open reports an actionable error instead of hanging.
- **Stop actually aborts in-flight streaming** (PR
[amd#2166](amd#2166)) — the Agent UI Stop
button ends generation immediately.


## Full Changelog

**331 commits** since v0.22.0:

<details>
<summary>Expand full changelog (331 commits)</summary>

- `96ce0d1e` — feat(email): recognize the third mailbox connector —
Gmail, Outlook personal, Microsoft work (amd#2896)
- `88ecc18e` — fix(email): scoped 'anything suspicious?' query no longer
dumps the full triage report (amd#2910)
- `1c75ccee` — fix(skills): reject version pins GAIA cannot read instead
of matching them (amd#2928)
- `29a5c364` — docs(plans): assess readiness for the generic gaia-agent
(amd#2926)
- `e3b28653` — fix(tui): stop cancel-then-resend from racing the
daemon's session lock (amd#2912)
- `b38f5e60` — fix(tui): anchor TTFT on first inference token, use real
token counts (amd#2911)
- `779d7b00` — fix(email): reply/draft/send actions no longer report
failure after they already succeeded (amd#2908)
- `f8d4610c` — ci(eval): queue eval runs instead of cancelling the
pending one (amd#2921)
- `611b1928` — feat(hub,skills): pre-publish security audit gate for
marketplace skills (amd#2702)
- `c83047b9` — feat(skills): gaia skill migrate — OpenClaw/Hermes skills
to GAIA format (amd#2693)
- `ca17067a` — test(skills,hub): cover the signing and lane checks
amd#2668/amd#2692 shipped untested (amd#2907)
- `a951567a` — fix(eval): drop the judge temperature pin the model now
rejects (amd#2905)
- `236f8a58` — feat(skills): gaia skill publish/install with
signature-backed security tiers (amd#2692)
- `d26b15da` — ci(eval): trigger the Gemma eval on the PR diff, not the
push (amd#2897)
- `be15258a` — fix(cpp): normalize CRLF so Windows-authored skills parse
identically (amd#2906)
- `84415b06` — feat(cpp): text extraction and chunking (amd#2822)
- `6352f264` — fix(database): make the SQL read-only authorizer disarm
on Python 3.10 (amd#2904)
- `4709a728` — feat(hub,skills): publish and serve skills as a
first-class hub catalog lane (amd#2668)
- `245cb72e` — fix(api): refuse approval-gated tools instead of
auto-approving them (amd#2854)
- `e803103a` — fix(mcp): enforce --auth-token on the MCP bridge instead
of ignoring it (amd#2844)
- `a6dc1fa3` — fix(ci): run the Gemma-4-E4B agent eval in PowerShell on
its Windows pool (amd#2773)
- `75b61f4f` — feat(cpp): SKILL.md format parser and validation (amd#2824)
- `b89c6414` — feat(cpp): gaia::HttpClient — a general HTTP client
abstraction (amd#2809)
- `5e6b1d43` — fix(security): launch MCP servers without a shell,
protect ~/.gaia from agent writes (amd#2847)
- `768be2e2` — feat(skills): ship a ten-skill starter pack with a guide
and honesty guards (amd#2697)
- `340bf2c2` — feat(cpp): interactive TUI — event loop, streaming
render, modals (amd#2825)
- `acaad161` — feat(cpp): native OpenAI tool calling and conversational
response mode (amd#2821)
- `23ae5d63` — feat(cpp): SQLite integration and gaia::Database (amd#2816)
- `4f2a6686` — feat(cpp): gaia::VectorIndex — flat vector index with
persistence (amd#2807)
- `b1a7e8df` — fix(email): ship with Agent Skills off until the eval
gate covers them (amd#2848)
- `34c26577` — ci(webui): run the Agent UI Vitest job on Node 22 so the
suite executes (amd#2898)
- `20036474` — docs: escape MDX-breaking literals so Mintlify validation
passes (amd#2903)
- `07b0858e` — ci(tui): run on stacked PRs, add -race, lint, and a
per-OS test matrix (amd#2696)
- `e19d46be` — feat(cpp): harden the coding toolbelt — stale-write
rejection, ignore-aware search, persistent shell (amd#2823)
- `7621b223` — ci(claude): move every Claude workflow to Opus 5 and run
the audits nightly (amd#2859)
- `d086faa4` — fix(claude): correct the agent/skill prompts and make
plain language the default (amd#2862)
- `990d19ff` — fix(cpp): gate MCP tools behind user confirmation in the
C++ SDK (amd#2851)
- `878d473f` — fix(mcp): gate MCP write tools behind the user
confirmation prompt (amd#2846)
- `c2c68064` — fix(security): stop LLM-supplied SQL reaching
DatabaseAgent statements (CWE-89) (amd#2860)
- `e936d3d9` — docs(release): rebuild v0.23.0 notes to hardware-verified
features only
- `be03c7c8` — fix(init): stop reporting success gaia init did not
deliver (amd#2889)
- `75804a15` — docs(dev): require --extra-index-url for every uv pip
install on Linux (amd#2878)
- `4a24d663` — ci(email): run the triage eval on PRs that touch the
email agent (amd#2849)
- `c3d9ec11` — fix(code): route orchestrated tool calls through the
confirmation gate (amd#2853)
- `c59e5d8a` — fix(hub): stop the terminal hub publishing against a core
that cannot serve it (amd#2712)
- `04f18294` — chore(deps-dev): bump the agent-ui-dependencies group in
/src/gaia/apps/webui with 6 updates (amd#2750)
- `ce4fcbf3` — fix(web): bracket pinned IPv6 addresses in URLs (amd#2739)
- `e7e93287` — fix(installer): install the terminal hub on macOS and
Windows (amd#2708)
- `3cda9f77` — feat(cpp): MCP server registry — resolve server ids from
mcp.json (amd#2820)
- `c09764da` — fix(ci): validate the STX CMake cache instead of trusting
bin/cmake.exe (amd#2818)
- `616014d7` — docs(plans): scope C++ framework parity for
domain-specific agents (amd#2806)
- `f6433039` — ci(security): audit allowlist soundness + add PSIRT/CVSS
triage skill (amd#2752)
- `5c9f2b60` — chore(deps): bump the github-actions group with 2 updates
(amd#2751)
- `c103f6cd` — feat(discovery): real macOS and Linux branches for
day-zero scanners (amd#1956) (amd#2747)
- `e7e362e1` — chore(deps-dev): update mcp requirement from
\<2.0,>=1.1.0 to >=1.1.0,\<3.0 in the python-dependencies group (amd#2749)
- `ca9e5461` — chore(deps): bump the root-npm-dependencies group with 2
updates (amd#2748)
- `f62ca240` — fix(website): unbreak the Railway deploy, red since July
31 (amd#2855)
- `88d130ec` — feat(email): render the triage list from the scan, not
from the model (amd#2858)
- `bf9eb183` — ci(windows): pin ffmpeg version to drop the gyan.dev
dependency (amd#2852)
- `c531ca69` — fix(email): a conversation now survives its turns —
session_id on /query (amd#2837)
- `bcde95ee` — fix(email-agent): stop meeting/invite answers from
inventing what tools never said (amd#2833)
- `162274a5` — fix(tui): 'triage my inbox' draws its card under the
question again (amd#2845)
- `bbf69fd6` — feat(email,skills): bundled skills + account-keyed
skill-set selection (amd#2695)
- `abed9f1c` — fix(security): close find -exec / write side-doors in
shell command whitelist (CWE-184) (amd#2740)
- `0d1258a1` — fix(email-agent): get_thread renders a table card instead
of relying on model prose (amd#2788)
- `300163dd` — fix(email-agent): search_messages defaults to
metadata-only, fixing context overflow on counting questions (amd#2782)
- `cc331b78` — fix(email): low-priority-sender match no longer forces
PROMOTIONAL (amd#2774)
- `68736511` — fix(email): meeting proposal in a confidently-classified
message no longer vanishes from needs_you (amd#2779)
- `e135b8ef` — fix(ci): post PR reviews from the workflow instead of
hoping the model does (amd#2719)
- `efd3812b` — feat(email/tui): resolve "reply to 1" to the message the
card actually shows (amd#2761)
- `ed70db73` — fix(email-agent): search_messages states an exact, stable
message count (amd#2760)
- `42fdfdcd` — fix(email/tui): one triage card that tells you what to
do, not what was classified (amd#2757)
- `3803aa06` — fix(email): stop leaking classifier internals into triage
card rationale (amd#2754)
- `2c11ac0b` — fix(tui): sanitize agent error text (amd#2753)
- `9bf0042a` — docs(tui): rewrite the terminal hub README for newcomers
(amd#2717)
- `1cdd9746` — docs(skills): implementation spec for Agent Skills v2
adaptive skills (amd#2685)
- `50a5b862` — fix(website): pin the deploy toolchain so CI and
production build alike (amd#2691)
- `500cdac6` — fix(mcp): cap the mcp dependency below 2.0 (amd#2694)
- `3686e069` — docs(guides): document the terminal hub, and how to
actually get it (amd#2698)
- `99508ec5` — docs(plans): preserve the TUI packaging design and
binary-distribution plan (amd#2699)
- `21c02f69` — fix(hub): mark the email agent verified so first install
is not refused (amd#2703)
- `8d07ee3d` — docs(website): correct every deployment instruction in
the README (amd#2689)
- `dff8f1b2` — refactor(memory): rename synthesis Skill dataclass to
DistilledProcedure (amd#2684)
- `d2832fd5` — # feat(email): opt-in on-device SLM classifiers for
phishing and triage category (amd#2568)
- `a02f2f0c` — fix(connectors,email): a bare reconnect no longer guts a
mailbox (amd#2733)
- `bc0d4632` — fix(website): reach the hero terminal, marquee, and code
blocks by keyboard (amd#2706)
- `82635362` — fix(website-router): track apex Worker, fail loudly on
origin errors (amd#2688)
- `5d269094` — feat(skills): SKILL.md loader, validator, discovery +
gaia skill CLI core (amd#2669)
- `0ae019c8` — fix(daemon): detect a sidecar that is alive but has
stopped serving (amd#2707)
- `4551ec5b` — fix(website): stop offering Intel Macs a DMG they cannot
run (amd#2701)
- `bc8a7e29` — docs(spec): Gatekeeper blocks the browser download, not
curl | sh (amd#2732)
- `0d170869` — fix(website): make the hub agent rows readable in both
themes (amd#2711)
- `d5ae430d` — fix(tui): a consequential readiness check now holds the
screen (amd#2731)
- `d6bd047f` — fix(email): stop Gmail 429-ing every scan — chunk at 25
and retry the rate limit (amd#2727)
- `93216bc0` — chore(connectors): remove GAIA_MICROSOFT_TENANT — dead
since the connector split (amd#2729)
- `e3a6958a` — fix(tui): show a failed tool's own error instead of an
"Invalid card" box (amd#2726)
- `6efc520b` — fix(email-agent): surface connector errors from autonomy
runs instead of a bare HTTP 500 (amd#2640)
- `f0543406` — feat(connectors): split Microsoft into Personal +
Work/School connectors (amd#2718)
- `b8a97cbb` — fix(connectors): stop requiring a client secret from
secretless public PKCE clients (amd#2630)
- `70e4eb12` — fix(website): stop a published agent rendering twice on
the hub page (amd#2690)
- `282e30e0` — fix(tui): name the binary the user actually invoked, and
fix the setup hint (amd#2700)
- `d95866ff` — fix(installer): repair the Lemonade download URLs and add
macOS support (amd#2704)
- `3bc0b612` — fix(hub): stop components publishing against a core that
cannot serve them (amd#2705)
- `c068166d` — Email Triage draft/proposal SDK (amd#2551)
- `cb90bab3` — fix(email): priority senders never force urgent;
informational tail auditable (amd#2658)
- `ab41b115` — fix(email): stop the assistant from narrating what the
turn's tools don't support (amd#2659)
- `7a93bfbe` — fix(email-agent): propagate the autonomy kill switch to
the scheduler (amd#2657)
- `4de65cff` — fix(email-agent): require the conflict tool for conflict
verdicts (amd#2656)
- `84404a6e` — perf(email): metadata-first scan + read-mail pre-scan
coverage (amd#2661)
- `733822e2` — fix(tui): restore the attention card on direct chat
--agent launches (amd#2655)
- `d4d5a7cc` — fix(mcp): cap the mcp dependency below 2.0
- `1d867eed` — fix(email): strip infrastructure banners from bodies
before the prompt (amd#2650)
- `22d8b2d4` — fix(email-agent): kill a running autonomy cycle and keep
partial reports (amd#2652)
- `a4f4b39b` — fix(email): paginate the triage scan and report
truncation honestly (amd#2646)
- `7adc4882` — fix(tui): dedup attention rows and stop the card
interrupting turns (amd#2648)
- `24e8d5ab` — fix(email): thread summaries keep the newest message's
open asks (amd#2644)
- `c3ef3796` — fix(agents): truncate large tool results to valid JSON
(amd#2645)
- `d633b387` — docs(connectors): document which Google scopes to declare
in the Console (amd#2612)
- `4ad367a9` — fix(connectors): derive --grant-agent scopes from the
agent's own declaration (amd#2610)
- `f7d5d7d9` — chore(deps): bump the root-npm-dependencies group across
1 directory with 3 updates (amd#2503)
- `c84d9a79` — fix(tui): make the terminal hub readable on a light
terminal background (amd#2611)
- `bcffbc8d` — feat(email): recover the attention view and
waiting-on-you detector onto main (amd#2604)
- `b63a9f0d` — feat(email): guided Outlook mailbox setup — walk, verify,
and answer questions in chat (amd#2598)
- `e0d5014c` — fix(daemon): dev-mode start-agent refuses a checkout
mismatch instead of silently serving a stale build (amd#2592)
- `29dc0f9f` — feat(email): find meeting proposals during the inbox scan
(amd#2589)
- `22b0fa04` — fix(email): pre-scan stops reporting a guess as a verdict
(amd#2587)
- `c211e870` — fix(daemon): start the daemon clock so scheduled work can
fire (amd#2586)
- `358fd6e1` — fix(email-agent): add preference removal tools and a
truthful read-back (amd#2520) (amd#2541)
- `d01b9d43` — fix(email): don't cancel a retrying agent for a
recoverable tool error (amd#2572)
- `7fdcd6b2` — fix(email): surface degraded memory state and diagnose
the real cause (amd#2577)
- `1c42d842` — fix(email): autonomy /run refuses while off; add gaia
email autonomy CLI (amd#2578)
- `25b6cacd` — fix(email): draft_reply/draft_forward compose the body,
don't ask for it (amd#2576)
- `a050c64d` — fix(email): briefing carries a structured breakdown, not
one sentence (amd#2575)
- `8da002f0` — fix(email): resolve relative snooze/schedule times
agent-side (amd#2574)
- `ee4af04b` — fix(email): get_thread returns messages sorted and
numbered, not raw backend order (amd#2570)
- `9269a306` — fix(email): give list_inbox/search_messages a combined
envelope budget (amd#2546)
- `c4495a0f` — fix(agents): dispatch Python-call-style embedded tool
syntax (amd#2573)
- `f6103a03` — feat(email-agent): broaden autonomy candidates, add undo
surface and per-message decisions (amd#2545)
- `d975853c` — fix(email): normalize calendar time bounds to RFC 3339
before Google (amd#2579)
- `fdf665f0` — feat(website): redesign landing, Agent Hub, and agent
detail pages (amd#2566)
- `000ab88e` — fix(ui): reject null bytes in upload-path; unrot 8 stale
UI/journey tests (amd#2565)
- `0d6ca966` — feat(tui): tool-confirmation modal for
destructive/external actions (amd#2544)
- `4131420a` — fix(agent): recover from context overflow on
NPU/FastFlowLM (amd#2543)
- `35062526` — fix(email): restore from Trash anytime, drop dead
permanent_delete (amd#2542)
- `2a1767e0` — chore(release): bump the hub component manifests to
0.23.0
- `b5d5bf54` — feat(hub): publish the terminal hub and Agent UI as R2
hub packages (amd#2530)
- `014cbcbc` — fix(email): catch the contract guards up to schema 2.6,
and de-race the heartbeat test (amd#2549)
- `9f70be13` — fix(ui): serve the Lemonade start hint instead of
hardcoding a dead command (amd#2510)
- `26127e80` — feat(release): publish the terminal hub binary and
install it (amd#2522)
- `59374508` — fix(tui): unbreak build_tui on main — allowlist the
bare-host remedy (amd#2548)
- `1b189215` — fix(daemon): unbreak Unit Tests on main — remedy
docstring names an unparseable command (amd#2534)
- `9bdd7eb3` — docs(release): name the Lemonade version v0.23.0 actually
ships (amd#2509)
- `21d40c33` — fix(llm): detect Lemonade on macOS; stop printing
commands that don't work (amd#2497)
- `e272fbae` — fix(tui): keep a valid hub selection when switching tabs
(amd#2482)
- `1d0454b6` — fix(tui): prove the mailbox is usable before the gate
clears a launch (amd#2494)
- `ee54b779` — feat(email): agent-led mailbox onboarding — the agent
sets up its own access (amd#2496)
- `cc73e244` — fix(tui): stop the hub offering agents it cannot run or
launch (amd#2492)
- `5b378ebe` — fix(tests): make unit suite hermetic by blocking real
network connections (amd#2500)
- `238fe9ac` — feat(tui): install, run and uninstall agents from the TUI
(amd#2484)
- `ce730876` — feat(tui): draw tool_result render cards, starting with
the inbox pre-scan (amd#2485)
- `7fdd43ea` — chore(deps-dev): bump electron from 43.1.1 to 43.2.0 in
/src/gaia/apps/jira/webui in the jira-app-dependencies group (amd#2501)
- `c940fd0a` — chore(deps-dev): bump electron from 43.1.1 to 43.2.0 in
/src/gaia/apps/example/webui in the example-app-dependencies group
(amd#2502)
- `7f569605` — feat(daemon): install, uninstall and catalog hub agents
from the daemon (amd#2477)
- `61ac101c` — docs(plans): design the TUI user journey around the email
agent (amd#2480)
- `fd66ba95` — fix(email): fail loudly on a dead worker and reconcile
the package docs (amd#2479)
- `1340b3b7` — fix(agents): actually ask before running
confirmation-gated tools (amd#2475)
- `5f283e10` — feat(tui): control API + MCP server for driving the live
TUI (amd#2478)
- `12016d5d` — feat(tui): stream agents over the daemon HTTP/SSE relay
(amd#2476)
- `554ef27c` — chore(deps): bump electron from 43.1.1 to 43.2.0 in
/hub/agents/emr/python/gaia_agent_emr/dashboard/electron in the
emr-dashboard-dependencies group (amd#2504)
- `4822e2ed` — chore(deps-dev): bump the agent-ui-dependencies group in
/src/gaia/apps/webui with 6 updates (amd#2505)
- `3fc0d2e1` — chore(deps): bump the github-actions group with 4 updates
(amd#2506)
- `47c2d1b2` — fix(connectors): surface a browser that never launched
(amd#2507)
- `1763606f` — fix(agents): stop the OpenMP double-init from killing the
agent mid-conversation (amd#2508)
- `56215070` — fix(hub): carry requirements.min_lemonade_version through
the manifest parser (amd#2493)
- `02cf9984` — fix(mcp): keep console logs off stdout in stdio
transports (amd#2473)
- `d6c02c2f` — chore(deps): bump Lemonade Server to v11.5.0 (amd#2424)
- `2898f1b5` — fix(agents): don't dedup errored mutation retries (amd#2464
batch dead-end) (amd#2465)
- `867bc677` — fix(email): recall last archive batch so undo reaches
across turns (amd#2458)
- `9073ec20` — fix(email): strip LLM quoting from ARCHIVE_MESSAGE_BATCH
ids (amd#2457)
- `529d11b1` — ci(review): allow fork-PR checkout under
pull_request_target (checkout@v7) (amd#2461)
- `524d3282` — fix(email-agent): make undo window configurable for
chat-speed bulk ops (amd#2449)
- `61dc3a1f` — fix(email-agent): surface actionable Lemonade-down copy
in gaia email -q (amd#2453)
- `d78115a9` — fix(memory): guard against self-supersede hiding recalled
preferences (amd#2452)
- `6bf96e56` — fix(email-agent): isolate per-provider failures in read
fan-out (amd#2451)
- `acd20400` — fix(agents): reject unexpected tool kwargs with a
structured error (amd#2450)
- `f99ccaea` — fix(daemon): drop --reload from dev-mode email sidecar
spawn (macOS) (amd#2442)
- `c2337178` — fix(email-agent): verify archive left inbox + fix
same-day search miss (amd#2438)
- `3f6af0ff` — fix(email): don't misclassify timeouts as Lemonade-down
(amd#2139 follow-up) (amd#2454)
- `9386e1b1` — fix(email-agent): resolve draft/reply target from sender
or topic (amd#2403) (amd#2437)
- `229e61da` — fix(email-agent): never auto-archive IMPORTANT /
security-sender mail (amd#2426) (amd#2435)
- `e6ac57f9` — fix(email-agent): persist preferences to state.db so they
survive without the embedder (amd#2427) (amd#2434)
- `4301c897` — fix(agent-email): actionable copy for Lemonade-down
/query errors (amd#2432)
- `1bb2cccd` — fix(daemon/email-agent): dev-mode sidecar 'Empty module
name' on macOS (bad PYTHON_KEYRING_BACKEND) (amd#2443)
- `7a44bfd5` — fix(email): bulk-archive undo survives the whole run via
a per-turn batch handle (amd#2439)
- `5101b853` — fix(daemon): re-forward OAuth tokens on expiry so
sidecars self-recover (amd#2436)
- `55010368` — fix(email-agent): applying an existing label fails with
'Invalid label' (T14) (amd#2433)
- `ce83feb9` — fix(email-agent/ui): de-jargon the send confirmation
surface (amd#2407)
- `4bb196fb` — fix(connectors): name consumers-tenant migration on
personal-account app rejection (amd#2391)
- `6b29a4d6` — fix(email-agent): construct with zero connectors instead
of 502 (amd#2423)
- `f3af7bff` — fix(agent-ui): treat ctx_size=0 as unknown, not a
too-small window (amd#2402)
- `f22b0f4a` — fix(ci): run evidence stage via direct claude CLI, not
the GitHub-coupled action (amd#2430)
- `60334202` — fix(email-agent): add live mailbox connection-status tool
(amd#2405)
- `4c370b23` — fix(ui): surface actionable sidecar HTTP errors instead
of generic crash card (amd#2422)
- `7fc559af` — test(ci): evidence lane exercises UI-backed routes +
spot-regresses adjacent ops (amd#2421)
- `9be31036` — security(ci): harden the evidence stage against env dumps
/ credential flows (amd#2417)
- `0f709733` — fix(daemon): validate custody rag/query 'k' to a bounded
positive int (amd#2390)
- `ca50bf8a` — ci(review): broaden evidence gate + require a
verdict-linked evidence section (amd#2415)
- `59c736df` — ci(review): fold gaia-testing evidence into the PR review
comment (Phase 1: CLI/API/MCP) (amd#2414)
- `1a233d94` — test(connectors): fix main red — amd#2408 install test vs
amd#2410 trust gate (amd#2412)
- `78de5bfd` — fix(electron): resume periodic update checks after a
no-feed start (amd#2389)
- `a20c04b4` — fix(agent-ui): left-align installed-agent hub cards on
home screen (amd#2398)
- `236ba233` — fix(connectors): register hub-installed sidecar agents so
email grant works on fresh install (amd#2411)
- `b343f396` — fix(security): harden install trust gate and
analyze_data_file sandbox (amd#2410)
- `765cb544` — fix(daemon): hold the model-slot lease across inference,
not just the load (amd#2394)
- `27be002e` — fix(onboarding): neutral NPU wording in first-run
Hardware check (amd#2399)
- `c86a46e5` — docs(release): note email is Linux + API/CLI only this
release (Windows amd#1648)
- `e658cb15` — docs(testing): bind real-world evidence contract to the
changed surface (amd#2376)
- `feb00155` — docs(email): correct earn-trust claim — positive-outcome
accrual not yet wired (amd#2392)
- `7aee219a` — fix(email): commit autonomy dedup INSERT so it survives
headless teardown (amd#2393)
- `6b6a8dab` — fix(daemon): map NotGrantedError to 403 in forward_all
route (amd#2395)
- `5b2e6be8` — docs(release): keep only verified user-facing features in
What's New
- `e11740e7` — docs(release): fix Agent UI heading — drop 'update'
(auto-update was trimmed)
- `a011e33d` — docs(release): trim v0.23.0 notes to features verified
working
- `ce5ddd84` — docs(release): correct v0.23.0 notes to match verified
behavior
- `c62a3e27` — Release v0.23.0
- `5f624706` — fix(hub): importable CLI wheel agents + chat distribution
via gaia init (amd#2373)
- `3ad39e60` — fix(sidecar): actionable user-mode binary error (amd#2347)
(amd#2357)
- `bfc0f5b8` — fix(lemonade): recognize grouped amd_gpu/nvidia_gpu
device keys in validation (amd#2368)
- `0553c8b0` — feat(email): full autonomy — earn-trust engine, learning
loop, scheduled driver (amd#2363)
- `0284300b` — feat(connectors): support work/school Outlook +
zero-setup device-code sign-in (amd#2364)
- `1aaba5cc` — feat(lint): require security suppressions to be reviewed
in an allowlist (amd#2343)
- `19b1a4c5` — test(daemon): de-flake sidecar stop test on the
pid-liveness check (amd#2349)
- `174e9d0c` — refactor(chat): extract ProfileSpec, honest manifest,
lazy RAG — one class → separable profiles (amd#2323) (amd#2362)
- `57f970b0` — chore(audit): drop the security dimension from the weekly
audit (amd#2348)
- `23181040` — feat(security): proactive Claude security-audit workflow
+ CVSS/SARIF tooling (amd#2346)
- `d605caec` — fix(security): enforce --allowed-paths sandbox on file
read tools (amd#2344)
- `1c8a91c9` — fix(security): remove pre-existing bandit HIGH findings
and enable the HIGH gate (amd#2350)
- `4b5c16b7` — ci(labeler): add tui/daemon/sidecar auto-label rules
(amd#2356)
- `a905b057` — chore(deps): bump the github-actions group with 2 updates
(amd#2341)
- `b3f793af` — fix(routing): default unknown language to TypeScript, not
a process kill (amd#2337)
- `48286dbd` — fix(init): stop Rich eating bracketed tokens in gaia init
output (amd#2340)
- `638a7643` — docs(skills): add porting-agent-to-hub — the legacy-agent
port flow (amd#2338)
- `d2c00b55` — fix(hub): harden agent-archive extraction against path
traversal (amd#2342)
- `a4417656` — fix(ci): repair the startup-failing GAIA CLI aggregate
workflow (amd#2307)
- `0835a250` — fix(ci): make the weekly eval and runner heartbeat
monitor actually run (amd#2306)
- `977c158f` — fix(cli): add --layout hub to gaia agent init for the
agent-first hub tree (amd#2295)
- `558a73e6` — chore(deps): bump the github-actions group with 3 updates
(amd#2294)
- `f8bff000` — chore(deps-dev): bump electron from 43.1.0 to 43.1.1 in
the root-npm-dependencies group (amd#2292)
- `fb179e32` — ci(eval): gate the Gemma-4-E4B consolidation on agent
evals (amd#2283)
- `6b2aa2db` — chore(deps-dev): bump electron from 43.1.0 to 43.1.1 in
/src/gaia/apps/jira/webui in the jira-app-dependencies group (amd#2291)
- `5837bdac` — chore(deps-dev): bump electron from 43.1.0 to 43.1.1 in
/src/gaia/apps/example/webui in the example-app-dependencies group
(amd#2290)
- `3ce4f790` — test(agents): stub live Lemonade probe in
context-overflow tests (amd#2288)
- `1886a92f` — chore(deps): bump electron from 43.1.0 to 43.1.1 in
/hub/agents/emr/python/gaia_agent_emr/dashboard/electron in the
emr-dashboard-dependencies group (amd#2289)
- `0a308449` — feat(agents): consolidate every agent onto Gemma-4-E4B at
one context size (amd#2284)
- `4392e06a` — chore(deps-dev): bump the agent-ui-dependencies group in
/src/gaia/apps/webui with 3 updates (amd#2293)
- `d9b11ec7` — refactor(hub): agent-first layout —
hub/agents/\<id>/\<lang> (amd#2060)
- `5f15b333` — feat(daemon): broker-wire the remaining direct model-load
surfaces (amd#2286)
- `3affe149` — fix(ui): detect GPU from real Lemonade payload shapes
(amd#2285)
- `4f888b78` — fix(hub): report file-based custom agents' real health,
not always 'error' (amd#2277)
- `36c29873` — fix(cli): detect GPU on all platforms and honour
default_device (amd#2244)
- `6c6f8a34` — test(audit): coverage for schedule CLI, perf-vis, VLM
extraction, PDF gen/export; fix silent table-row loss (amd#2259)
- `2425afe8` — fix(builder): surface model-not-found (404) instead of a
generic placeholder (amd#2245)
- `3a0eebe7` — ci(hub): wire nine hub package test suites into CI; fix
.cjs docs-link guard gap (amd#2258)
- `3e82ca61` — docs(connectors): correct the client_id_hash claim in the
OAuth runbook (amd#2264)
- `9f7dca84` — fix(ci): fix Lemonade startup and bash-on-PATH in the doc
walkthrough (amd#2281)
- `53f4b529` — fix(daemon): owner-only DACL for the Windows launch
secret (amd#2250) (amd#2282)
- `ee8825fa` — fix(security): require allowed_dir in compute_file_hash
(amd#2280)
- `46fb7687` — spec(factory): define the dogfooding loop — Claudia as
live validation runtime (amd#2234)
- `1a679f02` — feat(ui): always-available OAuth client field in Settings
(amd#2104 interim) (amd#2265)
- `45c796b1` — test(jira): unit tests for JiraAgent HTTP boundary and
config discovery (amd#1991) (amd#2263)
- `bed702eb` — feat(ci): execution-based weekly doc walkthrough (amd#2278)
- `f8f309e6` — fix(ui): stop silently accepting unimplemented agent_mode
'autonomous' (amd#2257)
- `3b0938ed` — fix(ci): weekly audit cross-links the prior parent
instead of auto-closing it (amd#2254)
- `78306480` — test(audit): risk-bearing coverage — jira, trust-gate,
flag-precedence, amd#1655 boundaries (amd#2253)
- `1aa39ca2` — fix(observability): real system-metrics polling + real
rollbackAction (amd#2251)
- `384b3ca8` — fix(security): bind MCP bridge to loopback, not all
interfaces (amd#2246)
- `037f4370` — fix(cli): implement/gate stubbed api-status, eval flags,
schedule --skill (amd#2247)
- `2b6ed53e` — fix(security): rate-limit exposed routes, harden JS
ReDoS/XSS/cleartext-logging (amd#2237)
- `6c6b3baf` — fix(packaging): stop the amd-gaia[agents] extra from
downgrading the core wheel (amd#2262)
- `8744a51a` — fix(security): validate user-influenced file paths
(py/path-injection) (amd#2252)
- `c921fa7b` — feat(email): quality + robustness batch
(amd#2110/amd#2113/amd#2114/amd#2115/amd#2116) (amd#2192)
- `b9391ec2` — fix(security): parameterize SQL, redact sensitive logs,
harden ReDoS regexes (amd#2239)
- `09e0bac4` — fix(security): stop leaking stack traces at API
boundaries + least-privilege workflow permissions (amd#2236)
- `3df5a3db` — fix(security): tighten API CORS — no wildcard origin with
credentials (amd#2238)
- `a3db04ef` — feat(connectors): OAuth forward-out to sidecars (V2-14)
(amd#2203)
- `e0886cbf` — refactor(daemon): reconcile the clocks into one
daemon-owned scheduler (V2-15) (amd#2199)
- `15459153` — feat(daemon): /host/v1 custody API v1 with per-agent
scoping (V2-12) (amd#2197)
- `e226d584` — feat(webui): group session sidebar by agent +
session-state polish (amd#2193)
- `6e48cb22` — feat(daemon): host-owned model-slot broker serializes
loads (V2-11) (amd#2194)
- `c42bb354` — fix(website): correct hub install command and clarify
agent availability (amd#2207)
- `2f18de85` — fix(tests): align email CLI dispatch test with amd#2191
thin-client contract (amd#2209)
- `a2ad4eff` — fix(tests): repair stale gaia.ui.email_sidecar.manager
import after amd#2144 (amd#2208)
- `2b89643c` — fix(webui): repair broken main — AgentHubView imported
deleted AgentHubGrid (amd#2206)
- `68773d46` — test(eval): sidecar eval harness + distributed-seams
suite (V2-19) (amd#2202)
- `d7cffc72` — feat(agent-ui): first-run onboarding wizard — hardware
pre-flight, in-app model download, connect-on-install (amd#2204)
- `5bc7d325` — feat(electron): in-app install, R2 auto-update feed,
gaia:// deep links (amd#2196)
- `eb6b34bf` — feat(daemon): one-time versioned migration of ~/.gaia
state (V2-13) (amd#2200)
- `73b8f98c` — refactor(api): remove the last in-process email mount;
relay via daemon (amd#2176) (amd#2205)
- `e4ea0e33` — feat(connectors): grant the mailbox to the email agent in
the same connect flow (amd#2195)
- `2bf405db` — feat(webui): in-app Hub page with catalog lanes + install
trust gate (amd#2201)
- `459efba9` — feat(api): relay /v1/\<agent>/query through the daemon
(V2-17) (amd#2198)
- `ada5b95a` — feat(cli): gaia email attaches to the daemon — thin
client (V2-8) (amd#2191)
- `a49d257d` — fix(webui): reachable Agent Hub + installed-agent
discovery and per-session picker (amd#2190)
- `7c98b51a` — docs(connectors): rewrite the Google client-ID
walkthrough for the current console (amd#2189)
- `a94a8126` — feat(daemon): deliver sidecar launch secret via 0600
file, not bare env (amd#2149) (amd#2186)
- `5601e2a5` — feat(daemon): streaming SSE reverse-proxy for agent
routes (amd#2150) (amd#2188)
- `1465bc73` — feat(agents): proactive lifecycle hooks with
approval-gated proposals (amd#1484) (amd#2187)
- `2c4ff395` — test(chat): mirror the amd-gaia floor guard from amd#2169;
harden version parsing (amd#2184)
- `2271278c` — fix(llm): make swallowed model-load failures loud in
_ensure_model_loaded (amd#2185)
- `2e0294f9` — feat(hub): add multi-component type discriminator to the
manifest (amd#1716) (amd#2183)
- `b78b71d1` — test(chat): guard the amd-gaia dependency floor against
amd#2112 regression (amd#2174)
- `33caec54` — refactor(ui): extract _best_effort_cancel helper for the
relay cancel paths (amd#2173)
- `ba58e31a` — feat(email): fast dev-iteration loop for the email agent
SDK (amd#2083)
- `f79c4177` — fix(webui): pin explicitly-set session titles — stop
auto-retitle churn (amd#2165) (amd#2171)
- `ae741690` — fix(email): error on an explicitly-targeted unconnected
mailbox instead of substituting (amd#2164) (amd#2172)
- `4dafa091` — fix(email): normalize date operators in search_messages
(amd#2161) (amd#2170)
- `dd2fa328` — fix(email): raise amd-gaia floor to match
get_embedding_model_for_device (amd#2112) (amd#2169)
- `011b6e3c` — fix(email): default calendar list to a forward window
when range args are absent (amd#2162) (amd#2168)
- `ed7d2969` — fix(ui): propagate cancel to the email sidecar on relay
timeout/crash (amd#2158) (amd#2167)
- `d8daf66d` — fix(agent): abort in-flight streaming generation on
Agent-UI Stop (amd#2166)
- `c907f6c4` — feat(memory): adaptive, review-gated onboarding
conversation (amd#1955) (amd#2143)
- `def5e979` — fix(ui): stop blaming Lemonade for cancelled/empty chat
turns (amd#2141)
- `553cb541` — fix(email): consolidate Agent UI pre-scan across every
connected mailbox (amd#2129)
- `5446af2c` — fix(agent-email): drop $orderby from Outlook get_thread
to avoid Graph InefficientFilter (amd#2140)
- `8ba66f4d` — fix(email): REST triage honors the LLM's is_spam verdict
(amd#2125)
- `aa44c2c8` — refactor(daemon): daemon-supervised agent sidecars (V2-6)
(amd#2144)
- `ac3cc4cd` — fix(eval): fail the briefing eval loudly on a
zero-case/zero-judged run (amd#2123)
- `c9566b19` — chore(deps): bump Lemonade Server to v11.0.0 (amd#2130)
- `47c1142a` — feat(ui): route email chat through the sidecar
/v1/email/query loop (V2-10) (amd#2136)
- `b340427d` — docs(skill): correct the release skill against what
v0.22.0 actually did (amd#2133)
- `9f1ece0f` — feat(webui): render→component map + generic render
primitives (V2-9) (amd#2131)
</details>

Full Changelog:
[v0.22.0...v0.23.0](amd/gaia@v0.22.0...v0.23.0)

---

## How these notes were verified

Every **What's New** entry was exercised on real hardware, not inferred
from green CI. Re-verified end to end after merging current `main` (334
commits since v0.22.0), because the delta touched the MCP bridge, hub,
skills, and connectors — the exact surfaces the notes assert.

<details>
<summary>Verification results, what broadened, and what was
excluded</summary>

**Re-verified against the current pin and kept:** `gaia hub`
install/run/uninstall (trust gate enforced); the confirmation gate now
covering terminal + `gaia api` + MCP tool calls (fail-closed classifier
proven model-free — amd#2846/amd#2854); localhost binding + stricter CORS +
enforced MCP `--auth-token` + shell-less MCP launch + `~/.gaia` write
guard + CWE-89 SQL block (amd#2844/amd#2847/amd#2860, live probes); `gaia skill`
create/import/list/info plus signed-tier + pre-publish-audit + migrate
(amd#2692/amd#2702/amd#2693); the Microsoft connector split (two connectors,
secretless PKCE — amd#2896's third connector is Gmail, so the wording is
not stale).

**Broadened this cycle:** the confirmation-gate claim (blocking gap
amd#2846 merged) and the security section (four verified hardening items).
The skills section is stated **opt-in** (amd#2848: no shipped agent loads
skills by default).

**Removed:** the onboarding / day-zero discovery headline — the scanner
works on macOS, but the first-run onboarding flow it feeds is not ready
to ship, so it is not claimed (the underlying commit amd#2747 remains in
the changelog).

**Excluded (not claimable):** a skills-by-default claim (false — amd#2848);
the C++ SDK (real and CI-green but ships no user-facing artifact and
predates this release); the prebuilt terminal-hub binary ("not yet
distributed"). Claims that failed live verification remain filed as open
bugs, not shipped: amd#2883, amd#2884, amd#2885, amd#2893, amd#2894. Email ships as a
beta note whose only asserted behavior — never sending or deleting
without confirmation — is verified.

</details>

## Release checklist
- [x] `util/validate_release_notes.py` passes
- [x] `docs && npx mintlify validate` passes
- [x] `src/gaia/version.py` → `0.23.0`; `LEMONADE_VERSION` → `11.5.0`
- [x] webui `package.json` / `package-lock.json` → `0.23.0`
- [x] Navbar label → `v0.23.0 · Lemonade 11.5.0`
- [x] All 334 commits in range represented in the changelog
- [x] Branch merged current with `main`; `setup.py` keeps `mcp<2.0`
- [ ] Review from @kovtcharov-amd addressed

---------

Co-authored-by: Kalin Ovtcharov <kalin@extropolis.ai>
Co-authored-by: k <k@e>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent::email Email agent changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(email-agent): bulk-archive undo window (30s) is too short for chat-mediated undo

1 participant