@amd-gaia/gaia 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,714 @@ All notable changes to `@amd-gaia/gaia` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this package adheres
5
5
  to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.1.1] — unreleased
7
+ ## [Unreleased]
8
+
9
+ - **Finished work is no longer replaced by "I can't confirm this is done".** A
10
+ redaction task that loaded the `document-extract` skill had its answer swapped
11
+ for a coverage warning about its own `redaction_log.csv`, and a task with two
12
+ named outputs only knew about the last one. Every file the request names, and
13
+ every file the turn wrote, is now an output and never a source; the skill
14
+ checks coverage only when the request names a source document or asks for
15
+ every item. No check replaces a non-empty answer any more: an unproven
16
+ save, test result or extraction keeps the answer and adds a short "I
17
+ couldn't confirm all of this" list after it; a turn that ends early keeps
18
+ what it said ahead of its report; and a tool that keeps failing ends with the
19
+ model's answer from what it found, then the failure. A failed image request
20
+ is no longer rewritten into advice to start GAIA with an `--sd` flag that
21
+ does not exist. `load_skill` no longer tells the model the extraction tools
22
+ cannot run when it is about to register them, and a rejected extractor reply
23
+ is now logged.
24
+ - **`run_python` works in the Agent UI without a project.** It ran from the
25
+ server's own folder, which is not an allowed path, so every approved snippet
26
+ was refused and the agent fell back to slower shell commands. With no project
27
+ and a working directory you did not allow, a snippet now runs in the agent's
28
+ scratch directory, and its result says so. A project outside the allowed
29
+ paths is still refused.
30
+ - **An "always" answer for a shell command no longer covers code in the
31
+ scratch folder.** A command run there or naming a path there — `cd` into it
32
+ then `python -m explore`, or `pytest`, `make`, `pip install`, `uv run` or
33
+ `go run` pointed at it — and a script handed a scratch path as an argument
34
+ now ask every time, with no `always_scope`, the same as a script there.
35
+ `always_scope` labels for commands that run code they do not show now say so:
36
+ `pytest (runs the test code it finds)`, `make (runs the Makefile's
37
+ commands)`, `python -m explore (runs that Python module)`.
38
+ - **Exploring data no longer asks twice per step.** Each step wrote a helper
39
+ script into the agent's own scratch folder and ran it, so a data task asked
40
+ for approval about 30 times. Writes inside that folder now run unasked
41
+ (writes anywhere else still ask). Running a script from it asks each time
42
+ and offers no `always_scope`, since what is in that folder changes without
43
+ a prompt.
44
+ - **A command no loaded skill grants asks you instead of failing.** `python
45
+ script.py`, `git commit` or `pip install` with no skill granting them used to
46
+ come back as "needs a skill grant … declares 'shell:execute:git' in its
47
+ SKILL.md", which nobody could act on. They now raise the approval prompt; a
48
+ skill grant still skips it for reads. A prompt's `summary` for a write or
49
+ code-running command ends with one plain line on what it can do, and the
50
+ `in <folder>` line also appears when an earlier `cd` moved the session. A
51
+ call you decline tells the agent not to reach the same result another way,
52
+ and a run nobody can answer refuses in one plain sentence.
53
+ - **Commands GAIA's checks don't cover ask once instead of failing.** `git
54
+ push`, `git fetch`, `python -c`, PowerShell the checks can't read, a line with
55
+ shell operators, or a path outside the allowed folders used to be refused
56
+ outright. They now raise a prompt whose `summary` is the exact line the shell
57
+ will run, its full folder and a risk sentence, with no `always_scope`; an
58
+ approval runs that line once. Still refused with no prompt: an encoded
59
+ PowerShell command or invisible characters, a line whose quoting can't be
60
+ split the way the shell would, such a line that also deletes or discards work
61
+ (`git push --force`, `git reset --hard`, `rm` outside the project), and
62
+ folders no approval can open.
63
+ - **`run_python` and `execute_python_file` work in the released binary.** The
64
+ binary has no Python interpreter, so it handed every script to its own
65
+ argument parser, which rejected it, and no calculation, chart or `.xlsx`
66
+ could be produced. It now runs scripts itself, with openpyxl, python-docx,
67
+ python-pptx, reportlab, pypdf, pandas, numpy, matplotlib and the full
68
+ standard library bundled for them. The Windows binary grows from 147 MB to
69
+ 168 MB.
70
+ - **Long Python data jobs can finish.** `run_python` gave up after 60 s and
71
+ the agent abandoned `execute_python_file` at 180 s even when it had asked
72
+ for 15 minutes, while the script kept running and left a corrupt workbook
73
+ behind. Python runs now get 120 s by default and up to 1800 s on request,
74
+ the agent waits that long, and a run that runs out of time is killed with
75
+ every process it started; the result names the files that changed while it
76
+ ran so the agent checks them instead of reporting them finished.
77
+ - **Document search says when it could not search.** With the embedding
78
+ model or Lemonade Server down, `query_documents` and `query_specific_file`
79
+ answered "no relevant information found", so the agent told the user their
80
+ documents held nothing on the topic. They now return an error naming the
81
+ cause (Lemonade unreachable, or the embedding model failing) and how to fix
82
+ it. `index_document` and `index_directory` report the same cause, and
83
+ `index_directory` stops at the first such file and reports an error when no
84
+ file could be indexed. Hints for a missing optional package (RAG, PDF, Excel,
85
+ HTML parsing) say in a frozen build that it isn't bundled, and from source
86
+ give the install command for the running Python.
87
+ - **A failed tool call is reported as a failure.** `run_shell_command` returned
88
+ `status: success` for a command that exited non-zero, so the TUI and Agent UI
89
+ showed a failed build or test run as a passed step. It is now `status: error`
90
+ with an `error` naming the failing step, its exit code and the last stderr
91
+ line; `stdout`, `stderr` and `return_code` are unchanged. grep's no-match and
92
+ diff's "inputs differ" (exit 1) stay a success and say so in `expected_exit`.
93
+ An MCP tool that answers `isError`, a DuckDuckGo search that was blocked, a
94
+ `tasklist`/`wmctrl`/`osascript` window listing that failed, a notification
95
+ whose PowerShell died, and an `open_url` with no browser to open are errors
96
+ too. Install hints in a frozen build no longer name the binary as a Python to
97
+ run pip with.
98
+ - **Reviewing a few documents reads them in full.** `read_file` on a Word,
99
+ PDF or Excel file returned `[Binary file, N bytes]`, so to compare a term
100
+ sheet against a draft agreement the agent indexed them and ran a handful of
101
+ searches, and missed a closing date that differed between the two because no
102
+ search asked for it. `read_file` now returns the extracted text, in parts sized
103
+ to the model's budget that say where the next part starts, and the prompt
104
+ tells the agent to read a few documents in full for a review, comparison or
105
+ reconciliation and keep indexing for large collections and single lookups.
106
+ - **`fetch_page` no longer drops the middle of a long page.** Text past
107
+ `max_length` was cut to its head and tail, which is where a product page keeps
108
+ its menus and footer, not its price. It now returns the first part with a
109
+ marker naming the offset of the next, takes `offset` to continue and `find` to
110
+ jump to the passages containing a word, says so when a word is not on the
111
+ page, and flags a page with almost no static text as rendered by JavaScript.
112
+ `Length:` now reports the whole page rather than the excerpt.
113
+ - **Long desktop tasks finish with their deliverables instead of a bare "I
114
+ can't confirm this is done".** Picking a controller from nine datasheets and
115
+ pricing a 40-unit BOM ran out of its 50 steps while still searching for
116
+ prices, and the reply dropped the recommendation the agent had already
117
+ written. The agent's default step limit is now **150** (a `/query`
118
+ `max_steps` or `GAIA_AGENT_MAX_STEPS` still wins); at 20, 10 and 5 steps left
119
+ it is told to write the requested files from what it has, marking unverified
120
+ values, and a turn that still runs out keeps its findings and lists what is
121
+ missing after them.
122
+ - **The model notice is one line.** Switching or restoring a model printed a
123
+ four-sentence paragraph that named the model twice and said "via Lemonade".
124
+ It now reads like "Switched to **DeepSeek-V4.1-Flash** on the AMD LLM Gateway
125
+ (cloud). Your messages, and anything read from files or memory, are sent
126
+ there." The `/model` list drops "via Lemonade" from its headings and marks
127
+ the configured default model with `(default)`.
128
+ - **Single-file search results name their file.** `query_specific_file` put the
129
+ path only at the top of its result, so each chunk arrived with no source and
130
+ the chunk cards showed none. Every chunk now carries `source_file`, and the
131
+ result lists it in `source_files`, as `query_documents` already did.
132
+ - **The turn log counts the answer of a streamed turn.** With `GAIA_TURN_LOG`
133
+ set, a turn whose answer streamed out recorded `answer_chars: 0`. It now
134
+ records the length of the answer the user saw.
135
+ - **`run_python` and `execute_python_file` work in the released binary.** The
136
+ binary has no Python interpreter, so it handed every script to its own
137
+ argument parser, which rejected it, and no calculation, chart or `.xlsx`
138
+ could be produced. It now runs scripts itself, with pypdf, pymupdf,
139
+ python-docx, python-pptx, openpyxl, xlsxwriter, reportlab, pandas, numpy,
140
+ matplotlib and the full standard library bundled for them, and both tools
141
+ name those libraries to the model. A script that starts `sys.executable`
142
+ (`-m`, `-c`, a file) runs too; `-m pip` says packages can't be installed
143
+ and lists what is bundled. The Windows binary grows from 147 MB to 168 MB.
144
+ - **A Python script that fails is reported as a failure.** A non-zero exit
145
+ came back as `status: success`, so the TUI showed a failed run as
146
+ successful. It is now `status: error`, with an `error` naming the exit code
147
+ and the last stderr line; `stdout`, `stderr` and `return_code` are unchanged.
148
+ - **`GAIA_ALLOWED_PATHS` confines a run to the folders you name.** The agent
149
+ could read anything under the user's home and nothing narrowed that short of
150
+ embedding it, so an evaluation run could read the graders' answer keys. Set
151
+ to absolute folders, it narrows every file tool, search, index and shell path
152
+ check to them — it never widens, an invalid value stops startup, and code the
153
+ agent runs (`run_python`, shell) is not confined.
154
+
155
+ - **The sidecar's start-up warm-up now makes the first question fast.** It
156
+ primed only the agent's own instructions, without the tool list or the rest
157
+ of the system prompt, so the first question re-read all ~19K prompt tokens
158
+ (34 s on Qwen3.6-35B-A3B). It now sends exactly what a turn sends, and the
159
+ first question reads only its own ~50 tokens (5 s). A warm-up that fails is
160
+ logged as a warning with its cause instead of an info line.
161
+ - **`/model` names the command that connects a cloud provider.** With no
162
+ Fireworks or AMD gateway connection, the list told users to "connect this
163
+ provider in the TUI provider settings", a screen with no obvious way in. It
164
+ now says to run `/provider`, the command that opens it.
165
+ - **The TUI's provider picker works after GAIA's Lemonade restarts.** When the
166
+ agent ran as the TUI's own child, its model-state ping kept naming the port
167
+ Lemonade started on, so after a restart the picker said "Lemonade did not
168
+ respond" while chat worked. With a local or cloud chat model the ping now
169
+ reports the port the agent talks to, and it still never starts a stopped
170
+ server. A Claude session still reports the port it started with.
171
+ - **The Agent UI's Skills settings list this agent's skills.** With the agent
172
+ installed from the Hub, the page said the agent package was not installed
173
+ and to run `gaia init`, which changed nothing: the bundled skills live
174
+ inside the binary, where the UI could not read them. `GET /v1/gaia/skills`
175
+ (contract **2.17**) now returns them.
176
+ - **A message sent right after Stop is answered instead of refused.** A stopped
177
+ turn holds its session until its model call returns, so the next `/query`
178
+ got `409 … is already running a turn` for as long as the stopped answer would
179
+ have taken — about 40 s for a long one. It now waits for the stopped turn to
180
+ finish (up to 240 s) and then runs. A turn that was not cancelled is still
181
+ refused at once.
182
+ - **Documents attached to a chat now reach the agent.** In the Agent UI, a
183
+ Hub-installed agent answered "no documents are indexed" about a file the user
184
+ had attached, because the relayed turn never carried the chat's files.
185
+ `/query` now takes a `documents` field (contract **2.16**): the agent indexes
186
+ them before the turn, granting each exact file and not its folder, including
187
+ a file attached partway through a conversation.
188
+ - **The first chat after the agent starts uses its own tools.** A session built
189
+ while the start-up warm-up was still building could end up running the
190
+ warm-up agent's tools, so a file it had just indexed read as access denied.
191
+ Agents are now built one at a time.
192
+ - **Web requests work behind a corporate TLS-inspecting proxy.** HTTPS now
193
+ trusts the operating system's certificate store, as pip does, instead of only
194
+ Python's bundled list, so a proxy root such as Zscaler's no longer fails every
195
+ fetch with `CERTIFICATE_VERIFY_FAILED`.
196
+ - **Starting the agent no longer downloads a model you skipped.** The HTTP
197
+ sidecar's start-up warm-up skips the default local model when it is not
198
+ downloaded, so someone using a cloud model is not handed a multi-GB download
199
+ they never asked for.
200
+
201
+ ## [0.3.0]
202
+
203
+ Released with GAIA 0.25.0, and requires it.
204
+
205
+ ### Changed
206
+
207
+ - **Requires `amd-gaia>=0.25.0` and `gaia-agent-chat>=0.2.0`.** Every file tool
208
+ now checks the allowed-folders rule, and that check ships in core 0.25.0; an
209
+ older core cannot import the chat agent this one builds on.
210
+ - **`~/.gaia/logs/gaia-agent.log` stays bounded.** It rotates like `gaia.log`
211
+ (10 MB, three backups; `GAIA_LOG_MAX_MB`, `GAIA_LOG_BACKUPS`) instead of
212
+ growing without limit.
213
+
214
+ - **Everyday questions get short, plain answers.** "Why is the sky blue?" got
215
+ 140 words with an equation, and a Wi-Fi question got diagnostic shell runs.
216
+ The voice skill now caps an everyday answer at three plain sentences, goes
217
+ one level deeper only when asked, honours a stored short/detailed preference,
218
+ and answers general-knowledge questions without running tools.
219
+ - **Memory off no longer loads the embedding model.** A private chat, or one
220
+ with memory switched off, used to load the ~300 MB embedder on its first turn
221
+ only to validate it and to score the always-on `gaia-voice` skill. It now
222
+ loads when memory is first used, such as when memory is switched back on or
223
+ the agent recalls something. A turn with only always-on skills loaded embeds
224
+ nothing.
225
+ - **Every tool is offered on every turn again, as in the Agent UI.** Per-turn
226
+ tool selection swapped about a dozen tools in and out at its cap, which broke
227
+ the local model's prompt cache (17s to first token on a one-line question,
228
+ 0.4s without it) and left out the tool a question needed: a CSV question was
229
+ given web tools and fetched an unrelated page. `GAIA_DYNAMIC_TOOLS=1` turns
230
+ selection back on. The registered count is 95: `load_tools`, the selector's
231
+ escape hatch, registers only while selection is on.
232
+ - **A turn's silent opening now says what the model is doing.** A thinking
233
+ model sat on "Getting started" for 10-20 s while it read the prompt and then
234
+ reasoned in a paragraph released only once finished. `status` events gain an
235
+ optional `phase` (`loading_model`, `downloading_model`, `reading`,
236
+ `reasoning`, `tool_call`) with a `words` or `chars` count, sent when each phase
237
+ actually starts; the terminal UI shows them as "Reading your request",
238
+ "Reasoning · 214 words" and so on. Clients that ignore the field see the same
239
+ sentence in `message`.
240
+ - **GPU models no longer load at 32K on an NPU-profile machine.** The NPU's
241
+ 32,768-token ceiling was applied to every model whenever `default_device` was
242
+ `npu`, so a GGUF model ran at half its window and long tasks overflowed. It now
243
+ binds NPU (FLM) models only. A model can also opt in to a window sized to the
244
+ machine's memory, up to its native maximum.
245
+ - **A stale `GAIA_SKILL_SET` now stops startup.** An undeclared name used to be
246
+ dropped silently, so the agent came up healthy with no skills. The sidecar
247
+ and the stdio entry now exit non-zero before serving, with a message naming
248
+ the valid sets; leave the variable unset to start without one.
249
+ - **The code index is built on first search, not at task start.** In 32
250
+ SWE-bench tasks the agent never searched it, yet every task embedded the whole
251
+ repository in the background: about 2,000 local embedding requests per six
252
+ tasks. The first `search_code_index` now builds it; `GAIA_PROJECT_MAP_AUTO_INDEX=1`
253
+ restores building at task start.
254
+ - **The first answer on a local model starts in seconds, not after a ~20 s
255
+ silence.** The terminal UI now shows a "Getting GAIA ready" stage before the
256
+ chat: the agent starts, loads its model and reads its system prompt there,
257
+ step by step. New stdio sentinel `warm_up` (answers `warmed_up`, or
258
+ `warm_up_skipped` for a remote model).
259
+ - **Permission prompts say what a command does, once.** Running the tests is
260
+ labelled `RUNS CODE`, not `DESTRUCTIVE`; the command appears once, relative to
261
+ the project; and `a` ("always this session") is offered for shell commands and
262
+ Python too, covering the command's family — `pytest` covers
263
+ `python -m pytest`, `cd proj && pytest 2>&1 | tail`. Deletes never offer it.
264
+ `needs_confirmation` gains an optional `risk` field.
265
+ - **Shift+Tab permission modes in the terminal UI**: ask → accept edits → full
266
+ access. Accept edits lets file edits inside the starting folder run unasked;
267
+ the stdio control verb is `accept_edits`, and `--accept-edits` starts in it.
268
+ Full access is still never turned on by a stray keypress.
269
+ - **An unanswered prompt is reported as a timeout.** `tool_decision` accepts
270
+ `timeout`; the agent tells the user the request expired rather than that they
271
+ refused it.
272
+ - The default chat model now follows the hardware. On a PC whose GPU has
273
+ ~27 GB for models — a 64 GB+ Strix Halo or a 32 GB GPU; the 23.3 GB model also
274
+ needs its context cache — `gaia init` sets up Qwen3.6 35B A3B (a 23 GB Lemonade
275
+ built-in MoE, run with thinking on) and records it as `default_model`; the
276
+ agent and its `GET /v1/gaia/init` readiness check use it for chat. Gemma 4 E4B
277
+ is still downloaded for vision. Every other PC, including a CPU-only one, keeps
278
+ Gemma alone.
279
+ - **Qwen3.6 gets the longest context this PC's memory holds.** Up to its native
280
+ 262,144 tokens on a Strix Halo (a 5.4 GB KV cache), about 152K on a 32 GB GPU,
281
+ never under 64K. Gemma stays at 64K.
282
+ - Qwen3.8 Flash Next (82 GB, multimodal, thinking on) is available on a
283
+ 128 GB Strix Halo and never picked as a default; it needs ~90 GB for models.
284
+ Choose it with `gaia config set default_model user.Qwen3.8-Flash-Next-GGUF`;
285
+ `gaia init` refuses it on a PC that cannot hold it.
286
+ - **Bypass permissions is now called full access, everywhere.** `--full-access`
287
+ and `/full-access` replace `--bypass-permissions` and `/bypass`; the old names
288
+ fail with a message naming the new one. `/full-access always` (or
289
+ `gaia config set full_access true`) keeps it on across launches.
290
+ - **The agent has to read a file before it changes it.** `edit_file`, and
291
+ `write_file` on an existing file, now refuse a file the agent hasn't read with
292
+ `read_file` in this session, or one that changed on disk since it did. Benchmark
293
+ runs caught the agent patching files it had never opened, from a grep snippet
294
+ or a guess; now it has to look first. A partial read counts, creating a file
295
+ needs no read, and the refusal comes before any approval prompt. It applies
296
+ with confirmations bypassed too.
297
+ - **The agent sees every installed skill and loads the one that fits.**
298
+ Previously a per-turn matcher scored the request against skill descriptions
299
+ and loaded a skill on 0 of 24 benchmark tasks, so most GitHub requests never
300
+ learned `gh` was available. The system prompt now lists each installed skill
301
+ in one line (~1,000 tokens for the starter pack), and refusing a skill-gated
302
+ CLI names the skill to load. `GAIA_SKILL_DISCOVERY=0` still hides the list.
303
+ **Removed:** `GAIA_SKILL_DISCOVERY_TAU` is now ignored, and
304
+ `GaiaAgentConfig(skill_discovery_threshold=…)` raises `TypeError` — drop the
305
+ argument. See the Agent Skills spec, "Skill catalogue".
306
+ - Contract `apiVersion` is now **2.14** (2.13 added `GET /memory`) for the two new routes and
307
+ the `claude` provider value. A differing major still raises
308
+ `VersionMismatchError`; a higher minor is accepted.
309
+ - **Changing `model` on a live `session_id` switches in place** instead of
310
+ returning 409, so the conversation and any loaded skills survive it. A switch
311
+ that fails still returns 409 and leaves the session on its previous model.
312
+
313
+ ### Fixed
314
+
315
+ - **Answering the agent's question from the Agent UI or the TUI works.**
316
+ `POST /query/{run_id}/respond` only took the answer as `response`, while
317
+ both front-ends send `{ request_id, value }` as the contract specifies, so
318
+ every answer was refused and the run waited until the question timed out.
319
+ It now takes `value`. `response` is still accepted; sending both, or
320
+ neither, is a 422.
321
+ - **A file you allow is usable by every tool, including document Q&A.** Indexing
322
+ or querying a file outside the chat's scope was refused with no prompt, and
323
+ approving it through another tool still left it unreadable to the document
324
+ index. Indexing now asks the same question `read_file` does, and one approval
325
+ covers both. A tool waiting on that question is no longer abandoned at its
326
+ 180 s limit while the question is still on screen. A refusal now tells the
327
+ model not to copy or move the file elsewhere to get around it.
328
+ - **`--use-claude` works with the downloaded binary.** The release build left
329
+ out the Anthropic client, so every Claude launch from the terminal UI, and
330
+ every `/model` switch to Claude, failed with "The 'anthropic' package is
331
+ required", which a frozen binary cannot act on. The client is now bundled, and
332
+ the release build fails if it is missing.
333
+
334
+ - **Code search works in an Agent UI chat that is not inside a project.** It
335
+ started in GAIA's own documents folder, which usually does not exist yet, so
336
+ every `search_code_index` failed with "repo_path does not exist". It now starts
337
+ in a repository the chat can reach, else any folder it can reach that exists.
338
+
339
+ - **`/model` no longer offers speech or other non-chat models as chat
340
+ targets.** Whisper was listed as a "chat-capable" local model; switching to it
341
+ reported success and broke the next turn. Transcription, speech, music,
342
+ classification, upscaling and 3D models are excluded; a model labeled `chat`
343
+ still qualifies even if it also transcribes.
344
+ - **The agent no longer starts an unrelated job after answering.** A bugfix
345
+ request loaded the `coding` skill, whose "find every call site" tip switched
346
+ on document inventorying; after the fix was done and verified, the agent spent
347
+ minutes extracting every file it had read until the user cancelled. Only a
348
+ skill built on the extraction tool turns inventorying on now. Separately,
349
+ once a turn has answered, a tool call on nothing the request touched is not
350
+ run and a second one ends the turn, and a tool that keeps failing with new
351
+ arguments is stopped like an identical repeat.
352
+ - A "no" now covers what an action does, not the one tool it was asked
353
+ through. After you decline `pytest`, or leave its prompt unanswered, the
354
+ agent no longer writes `run_tests.py` and runs it with another tool: any
355
+ call that would run the tests, reach the same host, or change the same file
356
+ is stopped for the rest of that request. Where the surface can ask, you get
357
+ the exact command and why, with Allow once / Don't run it; otherwise the
358
+ agent stops and asks in its reply.
359
+ - `/v1/gaia/query` now honours `provider` on an existing session. It used to
360
+ matter only when the session was created, so `provider: "lemonade"` could keep
361
+ sending a Claude session's conversation to Anthropic, and `provider: "claude"`
362
+ could run locally. A different provider now switches the session in place.
363
+ Naming a `model` that belongs to the other provider is a 400, on new and
364
+ existing sessions alike. Omitting `provider` and naming a Claude `model` now
365
+ starts a Claude session, the way it already switched an existing one — it used
366
+ to point the local backend at an id it cannot serve.
367
+ - "Lemonade is not reachable" errors no longer tell users to run
368
+ `lemonade-server serve`, a command current Lemonade installs don't have. The
369
+ `GET /v1/gaia/init` hint, run errors, and `/model` now say how to start
370
+ Lemonade on the user's own install (tray app, macOS app, service, or CLI).
371
+ `/model <unknown>` with nothing downloaded likewise names the download command
372
+ the host actually has, instead of the removed `lemonade-server pull`.
373
+ - `gaia serve` no longer exits 0 when Ctrl+C fails to stop the sidecar. The
374
+ error naming the surviving process and how to kill it was discarded, so the
375
+ next `serve` hit an unexplained port conflict. It now prints that error and
376
+ exits 1.
377
+ - Conversation state is saved under `~/.gaia/sessions` instead of the directory
378
+ the agent was started from. A `session_id` must be 1–128 characters from
379
+ `A-Z a-z 0-9 . _ -`; any other value is a 400 on `/query` and
380
+ `/sessions/{session_id}/bypass` instead of a 500. A corrupt saved session fails
381
+ the request instead of being silently replaced.
382
+ - `gaia hub install gaia` no longer refuses Intel Macs: the hub manifest now
383
+ lists `darwin-x64`, which the release already builds and the lock already ships.
384
+ - The hub install card advertises the declared npm package instead of an unpublished PyPI wheel.
385
+ - The readiness check (`GET /v1/gaia/init`) and the terminal session's health
386
+ report name GAIA's own Lemonade Server instead of Lemonade's default port.
387
+ - Clearing a TUI conversation now also clears the flagship stdio agent’s prior
388
+ conversation context, while preserving the selected model, skills, and permissions.
389
+ - Internal session deletion (not yet exposed by a route) refuses busy agents instead of closing them mid-turn.
390
+ - Scratch files no longer land in the user's project. The system temp dir was
391
+ out of scope, so the agent wrote throwaway test runners and intermediate files
392
+ into the repository instead. It now gets its own scratch directory, named in its
393
+ prompt and deleted when the agent closes; the rest of the temp dir stays denied.
394
+ - Every file tool now checks the same allowed-folders rule as `read_file`:
395
+ browsing, folder and content search, recent files, document indexing, folder
396
+ watching, code indexing, and where screenshots, transcripts and exports are
397
+ saved. A folder outside the scope gets the usual approval prompt, and a folder
398
+ you approve is readable by document indexing too.
399
+
400
+ ### Added
401
+
402
+ - **A committed, machine-readable `/query` contract.** `openapi.gaia.json` in
403
+ the Python package is generated from the live routes
404
+ (`python -m gaia_agent.export_openapi`) and checked for drift in CI, so a
405
+ typed client no longer has to reverse-engineer the body from prose —
406
+ `query`, `run_id`, and `context` are required, `run_id` must be a UUID, and
407
+ the bearer-auth posture is declared in the schema. Swagger UI (`/docs`) is
408
+ disabled on the sidecar — it loads its JS from a CDN, an unexpected network
409
+ call for an offline embedder — but `/openapi.json` is still served.
410
+ - **Programs the agent starts no longer inherit GAIA's internal credentials.**
411
+ Shell commands, MCP servers, CLI installs and sign-ins, native hub agents,
412
+ media tools and the Lemonade server get the sidecar's environment minus
413
+ GAIA's own tokens. Your own variables (`GH_TOKEN` and the like) still pass through, so CLI skills
414
+ keep working. An embedding app can withhold more names with
415
+ `GAIA_CHILD_ENV_DENY` (comma or space separated), and can stop GAIA loading
416
+ `.env` files with `GAIA_NO_DOTENV=1` — read from the real environment, so a
417
+ `.env` cannot set it.
418
+ - **The agent can drive a real browser.** Pages behind JavaScript or a login
419
+ used to be out of reach — `fetch_page` is one HTTP GET, so a signed-in inbox
420
+ or a dashboard came back empty. Eight new tools open a real Chromium, read
421
+ it, click and type in it, and sign in to a site; the password goes to the
422
+ human, never the model, and the session is stored encrypted. Acting inside a
423
+ session you signed in to asks first. Ships behind the optional `browser`
424
+ extra; without it none of the eight register. Registered tool count goes
425
+ 88 → 96.
426
+ - **Fast mode, for a session that's only conversation.** `GAIA_FAST=1` drops
427
+ the flagship to a plain conversational surface for the whole session —
428
+ 3,110 tokens of fixed prompt instead of 17,942 — so saying "hi" no longer
429
+ costs as much as a repo search. Session-scoped and one-way: a fast session
430
+ has no documents, files, web or skills and cannot pick them up mid-way.
431
+ - **`chat`, `doc` and `file` are no longer offered as agents.** New users saw
432
+ four entries in the picker where only one is the product. The three ids still
433
+ resolve, so existing sessions, `*-lite` aliases and eval scenarios keep
434
+ working — they are hidden, not deleted, and `ChatAgent` remains the
435
+ flagship's base class. A session created without an explicit agent now lands
436
+ on the flagship rather than a hidden agent.
437
+ - **Complete inventories from long documents.** Asking for every exercise,
438
+ action item or finding in a transcript now returns all of them, each with its
439
+ source quote, instead of a condensed list. The opt-in `document-extract` skill
440
+ drives new `extract_document_items` and `save_extracted_items` tools; a save is
441
+ reported only after the exact file is written and read back, and anything
442
+ unfinished is reported as incomplete. `gaia-voice` gains one routing line
443
+ (702 tokens).
444
+ - **The agent can set up a skill's CLI instead of handing the job back.** Asking
445
+ it to triage GitHub issues on a machine without `gh` used to end the
446
+ conversation. Three new tools — `check_cli_setup` (read-only), `install_cli`
447
+ and `sign_in_cli` — let it report exactly what is wrong, install the CLI with
448
+ the machine's package manager, and drive the browser sign-in. Both mutating
449
+ tools are confirmation-gated on every call and no skill grant pre-approves
450
+ them; over `/v1/gaia/query` they are refused, like every other gated tool
451
+ (§8). Registered tool count goes 83 → 86.
452
+ - **A shell command's `cd` now survives to the next one.** Every
453
+ `run_shell_command` call used to start from scratch, so `cd build` in one
454
+ call was invisible to the next. `get_shell_state` reads the session's
455
+ current directory, and `reset_shell_session` returns it to where the task
456
+ started. Registered tool count goes 86 → 88.
457
+ - **Say something while the agent is still working.** `POST
458
+ /v1/gaia/query/{run_id}/followup` hands a live run a message the user typed
459
+ after it started (contract **2.15**). The run is not interrupted and no
460
+ second turn starts — the agent folds the text into the turn already running
461
+ at its next step boundary, so a correction during a five-minute task changes
462
+ that task instead of arriving after it finished. Unknown run → `404`, an
463
+ agent that cannot take one → `409`; both loud, because the caller has already
464
+ taken the message from the user. See SPEC §5.6 and SKILL §7.
465
+ - **Approve a gated tool over HTTP.** `write_file`, `run_shell_command` and the
466
+ seven other confirmation-gated tools can now run through `/v1/gaia/query`:
467
+ the stream stays open on `needs_confirmation` and
468
+ `POST /v1/gaia/query/{run_id}/tool_decision` answers it. Previously the only
469
+ possible answer was a refusal, so those tools were unreachable over HTTP.
470
+ `POST /v1/gaia/sessions/{session_id}/bypass` turns the asking off for a
471
+ session. A run that cannot answer — no `session_id`, or
472
+ `can_answer_questions: false` — is still refused. See SKILL §8.
473
+ - **Claude as an inference backend.** `provider: "claude"` sends the
474
+ conversation to Anthropic's API instead of the local server; `model` then
475
+ names a Claude model. Anything outside `lemonade` / `claude` is still a 400.
476
+ - **`gaia-agent --serve` works from a pip install.** The console script pointed
477
+ past the transport dispatcher, so the documented HTTP mode exited with
478
+ "unrecognized arguments".
479
+ - **Tracked eval scorecard ([`SCORECARD.md`](./SCORECARD.md)).** The agent now
480
+ ships a per-release scorecard: judged-scenario pass rate (the aggregate)
481
+ plus per-category rates and the judge's average score across the
482
+ 13-category eval corpus (`eval/scenarios/gaia_*`), generated by
483
+ `hub/agents/gaia/python/packaging/gen_scorecard.py` from real
484
+ `gaia eval agent --agent-type gaia` runs. CI runs a PR subset
485
+ (`test_gaia_agent_eval.yml`), the weekly sweep runs the full corpus, and the
486
+ release pipeline gates on the committed card
487
+ (`scorecard-gate` + a judged eval subset in `release_agent_gaia.yml`).
488
+ The initially committed card is a harness validation measured with the
489
+ agent on `claude-haiku-4-5` (the dev environment cannot run Lemonade); the
490
+ first full runner refresh (`gaia_scorecard_refresh.yml`) replaces it with
491
+ the Gemma-4-E4B product baseline.
492
+ - **`capture_skill` — capture a skill from pasted `SKILL.md` text, a URL, or a
493
+ local folder, with code inert until trusted.** The agent can now bring a
494
+ skill into `~/.gaia/skills` straight from the conversation (68 registered
495
+ tools, up from 67). Every capture is confirmation-gated, security-audited (a
496
+ `BLOCK` verdict refuses it), SSRF-guarded on the URL path, and lands at the
497
+ `experimental` tier. Instructions load immediately; any `tools.py`/scripts
498
+ the bundle carries stay **inert** until the user runs
499
+ `gaia skill promote <name>` in a terminal, which re-audits and binds trust
500
+ to the audited bytes. Over `/query`, `capture_skill` is gated like the
501
+ other confirmation tools (SKILL §8; SPEC §5.2 documents the load-time
502
+ deferral).
503
+ - **`run_python`, always on.** A quick calculation or data transform is now one
504
+ confirmation-gated call that runs from the project root and returns what it
505
+ printed, instead of a throwaway script left in your repository. It joins the
506
+ always-on tool set (about 250 more prompt tokens per call) and the `shell`
507
+ bundle.
508
+ - Opt-in developer-mode skill and consent-gated MCP handoffs to Claude Code/Codex,
509
+ with managed worktrees, approved feedback snapshots and reported preview results.
510
+ Python `[mcp]` installation is required for the bridge; normal mode has no access.
511
+ - **The shell, always on inside a code repository.** When the project map
512
+ resolves to a repository (a VCS directory or a known manifest at its root),
513
+ `run_shell_command` is offered on every turn instead of only when the request
514
+ happens to sound like a shell request. Coding tasks such as "skip these tests
515
+ on PRs" previously ran without a shell and did every grep through `run_python`.
516
+ It follows the *same* root the map already uses, so the sidecar needs
517
+ `GAIA_PROJECT_ROOT=/path/to/repo` (or `GaiaAgentConfig(project_root=...)` when
518
+ embedding) to see your repository — its own working directory is whatever
519
+ started it, not yours. With no repository nothing changes, and the shell's
520
+ approval gate still applies either way. See SKILL §11.
521
+ - **`sleep`, always on.** The agent can now wait before retrying, e.g. until a
522
+ rate limit resets, instead of giving up; before, its only way to wait was
523
+ `time.sleep` inside a confirmation-gated `run_python`. Up to five minutes per
524
+ call, no approval needed, and Stop ends the wait within a second. It joins the
525
+ always-on tool set (about 190 more prompt tokens per call) and the
526
+ `loop_control` bundle (80 tools → 81).
527
+ - **`--bypass-permissions` now lifts the shell guardrails too.** It used to skip
528
+ only the confirmation prompt, which left the agent unable to run a build or a
529
+ test suite even with the user's blanket consent: no interpreter, test runner
530
+ or package manager was reachable, and nothing could write its output anywhere.
531
+ Under bypass, redirection (`>`, `>>`, `<`), backgrounding (`&`), substitution
532
+ (`` ` ``, `$()`) and the newline now parse and run — chaining with `&&` / `||`
533
+ / `;` / `|` already worked by default — the
534
+ read-only allowlist is replaced by a developer set (`node`, `npm`, `make`,
535
+ `cmake`, `go`, `cargo`, `sed`, `awk`, `curl`, `sleep`, `timeout`, `export`,
536
+ `cp`, `mv`, plus `python` / `python3` / `pytest` / `gh` / `git`), and the
537
+ shell rate limit is dropped. Off by default and byte-identical to before when
538
+ off. `git` in that set means its policy's outright refusals — push, reset,
539
+ rebase — also stop applying under bypass. `rm` stays excluded. Every command run this way is audit-logged with its full
540
+ arguments. Stdio only — an HTTP session's `/bypass` stops its approval prompts
541
+ but never lifts the shell gates, and the request body cannot ask for it.
542
+ Redirection has one exception: a command that
543
+ is nothing but a skill-granted CLI runs argv-only, so `>` there is refused
544
+ with an explanation instead of reaching the binary as text. See SPEC §5.5.
545
+
546
+ ### Notes
547
+
548
+ - Tracks sidecar contract `apiVersion` **2.14**; a differing major raises
549
+ `VersionMismatchError`.
550
+ - `GET /v1/gaia/memory` (contract 2.13) answers the same read-only snapshot the
551
+ stdio transport's `/memory` sentinel produces, so a daemon-supervised
552
+ install of the flagship exposes `/memory` too, not just a subprocess one.
553
+
554
+ ## [0.2.0] — 2026-09-16
555
+
556
+ ### Fixed
557
+
558
+ - Windows npm launchers now find the Python daemon CLI even when npm passes the
559
+ package script as argv[1], preserving unrelated tools in shared PATH directories.
560
+
561
+ ### Added
562
+
563
+ - **Image generation, reachable out of the box.** "Draw me a red bicycle" now
564
+ generates a PNG with local Stable Diffusion and reports the path; previously
565
+ the tools existed behind a flag nothing turned on, so the agent just said it
566
+ couldn't. Adds `generate_image`, `list_sd_models`, and `get_generation_history`
567
+ (70 tools → 73) plus an `image_gen` bundle so per-turn selection can find them.
568
+ Generating swaps the resident model, so the next reply waits for the chat model
569
+ to reload. The `image-gen` starter skill covers prompt expansion and iterating
570
+ on the previous image.
571
+ - TUI provider setup for Local, Fireworks AI, and AMD LLM Gateway, with masked
572
+ runtime API keys, discovered models, and remote-inference status.
573
+ - **A project map at task start.** In a code repository the agent now opens
574
+ every task knowing the directory shape, the likely entry points, which
575
+ commands are installed, and the three platform differences that change
576
+ command syntax — instead of discovering each one through a failed tool call.
577
+ Capped at 600 prompt tokens. If the repository has no code index the map
578
+ starts one in the background; `GAIA_PROJECT_MAP_AUTO_INDEX=0` turns that off,
579
+ and `GAIA_PROJECT_ROOT` picks the project when the working directory is not
580
+ it. See SKILL §11.
581
+ - **Three further client-visible refusals from `/query`.** Reusing a `run_id`
582
+ that is still in flight gets `409` — it used to replace the live run, leaving
583
+ it with no way to be cancelled. Supplying a `model` that differs from the one
584
+ the `session_id` was built with gets `409` rather than silently answering on
585
+ the old model. A request with an absent or empty `Host` header gets `400`
586
+ rather than being served, closing a DNS-rebinding check that failed open.
587
+ See SPEC §5 and §5.2.
588
+ - **Per-turn tool selection, now on by default for the flagship `full`
589
+ profile.** The model is sent about 28 of its 84 tools on any one call — a
590
+ fixed core plus whichever cohesion bundles the query matched — instead of the
591
+ whole registry every time. No capability is lost: `load_tools` is an escape
592
+ hatch the model calls mid-turn to pull in a bundle the selector missed; that
593
+ bundle is appended for the rest of the turn (briefly above the cap, which the
594
+ next turn restores) so the prompt already sent stays cached.
595
+ `GAIA_DYNAMIC_TOOLS=0` turns the selection off, `GAIA_DYNAMIC_TOOLS_MAX`
596
+ moves the cap and `GAIA_DYNAMIC_TOOLS_TAU` the match threshold.
597
+ - **One bundled skill ships enabled: `gaia-voice`.** It is a manifest `skills:`
598
+ entry, so it is always on and rendered in full on every LLM call — 702 tokens
599
+ of every prompt, and it declares no tools. It is the agent's honesty floor
600
+ (don't claim work you didn't do, don't present empty output as a result,
601
+ don't substitute a near-miss and report success), which is why it is not in an
602
+ opt-in bundle the way a task skill is. No *skill set* ships enabled: the
603
+ manifest's `skill_sets:` / `default_skill_set:` blocks stay commented out until
604
+ an eval measures what loading several bodies costs. See SKILL.md §10.
605
+ - **A materially smaller fixed prompt on every call.** That always-on
606
+ `gaia-voice` guidance was rewritten from 2,129 tokens to 676 with all of its
607
+ behavioural rules intact. It had been a rationale document — every rule
608
+ followed by the incident that motivated it — and the model needs the rule,
609
+ not the incident report.
610
+ - **The `.installed` record is written after a verified sidecar install.**
611
+ Staging the binary was only half an install: the daemon and the terminal UI
612
+ both key "this agent is installed" on `~/.gaia/agents/gaia/.installed`, and
613
+ without it the UI ran the REST sidecar as its own stdio child and the chat
614
+ filled with uvicorn's startup log. It is written on a cache hit as well as a
615
+ fresh download, so an install left by an earlier version repairs itself, and
616
+ only for this host's own platform — a `--platform` fetch stages a binary for a
617
+ different machine and records nothing. See SPEC §4.1.
618
+ - **`--allow-insecure-base-url`** — opt-in for a non-`https` `--base-url`, for a
619
+ trusted local mirror.
620
+
621
+ ### Changed
622
+
623
+ - **A `LEMONADE_BASE_URL` that already carries a path is now used exactly as
624
+ written.** Previously any URL not ending in `/api/v1` had that suffix appended,
625
+ so a reverse proxy configured as `https://proxy.example/lemonade` was silently
626
+ rewritten to `https://proxy.example/lemonade/api/v1`. It now resolves to
627
+ `https://proxy.example/lemonade` unchanged, and only a bare origin with no path
628
+ at all gains `/api/v1`. If a proxied install starts returning `404` after
629
+ upgrading, append the API path to the variable yourself.
630
+
631
+ ### Fixed
632
+
633
+ - **A second `gaia serve` no longer reports success against a server it does not
634
+ own.** With the port already taken, the incumbent answered `/health` while our
635
+ own sidecar was still unpacking, so the start "succeeded", printed a ready URL
636
+ for someone else's server, and the child then died of `EADDRINUSE`. The port is
637
+ now checked before anything is spawned, and a taken one fails naming the port
638
+ and how to find the process holding it.
639
+ - **Importing the package no longer changes a host app's error handling.** The
640
+ crash and signal handlers reaped every sidecar *before* checking whether the
641
+ host had its own handler, so an exception the host handled killed the sidecar
642
+ and the host's next request got an unexplained `ECONNREFUSED`.
643
+ - **`gaia run` no longer breaks the terminal UI's `PATH`.** Hiding our own `gaia`
644
+ shim removed the whole bin directory, which on a Homebrew or pipx layout also
645
+ took `python3`, `lemonade-server`, and the real `gaia` with it — so the UI
646
+ reported a missing CLI that we had hidden. A shared directory now moves to the
647
+ end of `PATH` instead of being dropped.
648
+ - **Flags a command does not read are refused, not ignored.** `run --port 9000`
649
+ parsed fine and came up on the default port; likewise `serve --component` and
650
+ `serve --cache-dir`.
651
+ - **A failed install says what to do.** A download that could not be moved into
652
+ place — usually a running sidecar holding the file on Windows — printed a raw
653
+ stack trace.
654
+ - **`taskkill` failures name their reason.** Its exit code and stderr were
655
+ discarded, so "Access is denied" surfaced ten seconds later as a generic
656
+ timeout.
657
+ - **A sidecar that survives shutdown is still reaped at exit.** `shutdown`
658
+ de-registered it before killing it, so one that survived both kill windows
659
+ became a permanent orphan holding the port.
660
+ - **`serve` removes its signal handlers once it stops**, so Ctrl+C keeps working
661
+ afterwards, and a repeat Ctrl+C during a slow teardown now says what it is
662
+ waiting for instead of looking frozen.
663
+ - **A non-JSON reply from a sidecar probe raises a typed error** rather than a
664
+ bare `SyntaxError` — the case where a proxy answers `200` with an HTML page.
665
+ - **`--port` no longer accepts what `Number()` would coerce**, so `--port 0x2710`
666
+ is rejected instead of quietly binding 10000.
667
+
668
+ ### Security
669
+
670
+ - **A desktop notification can no longer run code.** On Windows, `notify_desktop`
671
+ rendered its message box by pasting the title and body into a PowerShell command
672
+ string, so a `'` in either — text the model picks, and prompt-injected content
673
+ can steer it — closed the string literal and the remainder ran as PowerShell.
674
+ The command is now a fixed script that reads both values from the child's
675
+ environment, and the tool now needs your approval before it runs, like the
676
+ other tools that spawn a process (SKILL.md §8).
677
+ - **`resolveSidecarPath` / `resolveTuiPath` verify the binary before returning a
678
+ path that gets spawned.** Both fed `spawn()` from a predictable cache path with
679
+ no integrity check, so anything able to write `~/.gaia/agents/gaia/` got code
680
+ run — despite the package documenting the SHA verify as its security boundary.
681
+ They now re-hash against `binaries.lock.json`; pass `{ verify: false }` for a
682
+ binary you built yourself. Note this makes them proportional to the binary's
683
+ size — resolve once at startup, not per request.
684
+ - **`--base-url` requires `https`** unless `--allow-insecure-base-url` is passed.
685
+ The pinned SHA already made a plaintext mirror non-exploitable, but tampering
686
+ surfaced as a confusing `IntegrityError` instead of the transport failure it is.
687
+ - **A ~200MB artifact is never held in memory whole.** Downloads stream to disk,
688
+ and the cache-hit check that re-hashes an already-installed binary — which runs
689
+ on every `gaia run` — reads it in bounded chunks. Both hashes are computed
690
+ incrementally, and a download is still verified *before* the file is moved into
691
+ place.
692
+
693
+ ### Fixed
694
+
695
+ - **Esc stops a running turn in the terminal UI without killing the agent.**
696
+ The TUI used to kill the agent process, and on the released one-file binary
697
+ that killed only the launcher: the cancelled tool call ran to completion and
698
+ the surviving process consumed the next message. The first Esc now sends the
699
+ agent a `cancel` control message, so the turn ends and the session keeps its
700
+ loaded skills, "always" grants, history and full access. A second Esc stops
701
+ the whole process tree.
702
+ - **A restart after a hard stop no longer turns full access back on.**
703
+ The replacement agent is launched in the session's current permission mode
704
+ instead of from the original flags, and the TUI says what the restart lost.
705
+
706
+ ### Notes
707
+
708
+ - `gaia_agent` enforces a per-session caller-auth bearer on every `/v1/gaia/*`
709
+ request, plus a loopback `Host` allowlist and non-loopback `Origin` rejection.
710
+ This package mints no token, so a sidecar it spawns comes up in dev mode (token
711
+ check skipped, loudly warned, Host/Origin still enforced) — pass your own
712
+ through `spawnSidecar`'s `env` to turn it on. See SPEC §5.4.
713
+
714
+ ## [0.1.1] — 2026-08-21
8
715
 
9
716
  First working release. `npx @amd-gaia/gaia` is now the single command that gets a
10
717
  user running GAIA: it fetches and verifies everything GAIA needs and drops them
@@ -83,11 +790,9 @@ the terminal UI meant building it from source.
83
790
  building its own TUI. Each terminal-hub artifact is additionally cross-checked
84
791
  against the hub's own server-side SHA-256 before its hash enters the lock.
85
792
  - Requires Node.js 18+ (built-in `fetch`), a running Lemonade Server for
86
- inference, and the `gaia` Python CLI on `PATH` for the daemon the TUI starts.
793
+ inference, and the `gaia` Python CLI 0.25.0+ on `PATH` for the daemon the TUI
794
+ starts. 0.25.0 is the first core that can run this agent; on an earlier core
795
+ the UI starts with nothing behind it.
87
796
  - The sidecar has no arm64 Linux or arm64 Windows build. On those platforms the
88
797
  run stops with an error naming the platform and the supported set rather than
89
798
  launching a UI with no agent behind it.
90
- - `gaia_agent` 0.1.1 has no caller-auth token, so unlike
91
- `@amd-gaia/agent-email` this package mints and sends none.
92
- - Tracks sidecar contract `apiVersion` **2.12**; a differing major raises
93
- `VersionMismatchError`.