@amd-gaia/gaia 0.3.0 → 0.3.3

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/README.md CHANGED
@@ -39,7 +39,10 @@ however you arrive at the terminal UI, it behaves identically.
39
39
  - **Node.js 18+** (for the built-in `fetch`).
40
40
  - **[Lemonade Server](https://amd-gaia.ai/docs/reference/dev)** running locally —
41
41
  it hosts the model the agent thinks with. GAIA tells you if it isn't up.
42
- - The `gaia` Python CLI **0.25.0 or newer** on `PATH` for the daemon the terminal
42
+ - **On Linux x64, glibc 2.34 or newer** for the agent sidecar (Ubuntu 22.04,
43
+ Debian 12 and RHEL 9 or later). An older distro cannot start the agent. Check
44
+ with `ldd --version`.
45
+ - The `gaia` Python CLI **0.25.3 or newer** on `PATH` for the daemon the terminal
43
46
  UI starts. An earlier core cannot run this agent, so the UI comes up with
44
47
  nothing behind it. Install it with
45
48
  `curl -fsSL https://amd-gaia.ai/install.sh | sh` (Windows:
@@ -94,8 +97,10 @@ Common options:
94
97
  | `--component <name>` | Fetch only `sidecar` or `tui` | `fetch` |
95
98
  | `--port <n>` | Sidecar bind port (default `8141`) | `serve` |
96
99
 
97
- A flag a command does not read is **refused**, not ignored — `gaia run --port
98
- 9000` exits 2 rather than silently coming up on the default port.
100
+ A flag a command does not read, or an option the launcher does not know, is
101
+ **refused**, not ignored — `gaia run --port 9000` and `gaia --forse` exit 2
102
+ rather than silently doing something else. Arguments intended for the terminal
103
+ UI must come after a bare `--`, for example `gaia -- --debug`.
99
104
 
100
105
  Set `DEBUG=gaia` for download, spawn, and sidecar output on stderr. Diagnostics
101
106
  never touch stdout, which the terminal UI owns.
@@ -194,6 +199,77 @@ and eviction behavior.
194
199
  has not read yet before answering, and can read those exact files and nothing
195
200
  else beside them. See [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
196
201
 
202
+ **Private chat, or memory off? Send `memory` every turn** (contract ≥ 2.18):
203
+ `"private"` or `"off"` stops the turn saving anything to memory, including the
204
+ errors and lessons the agent records on its own; omit it and memory stays as the
205
+ agent has it. Send `allowed_paths` with the folders your UI shows the chat can
206
+ read, so the agent works in the same scope. Send `working_directory` (contract
207
+ ≥ 2.24) with the absolute chat workspace so bare file names resolve in the same
208
+ folder the UI shows; it is separate from the access allowlist. Binaries older
209
+ than 2.18 reject `allowed_paths` and older than 2.24 reject `working_directory`,
210
+ so probe `/version` first. See
211
+ [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
212
+
213
+ **Internet switched off for the chat? Send `internet_off`** (contract ≥
214
+ 2.23) every turn: `true` refuses web search, page fetches, the browser,
215
+ connector, mailbox and Agent Hub tools and commands that contact another host;
216
+ `false` allows them.
217
+ Omit it and the agent keeps its own setting. Binaries older than 2.23 reject
218
+ the field with `422`, so probe `/version` first, do not answer an
219
+ internet-off turn on an older one, and cancel its running turn when the switch
220
+ goes off. If the user flips the switch while a turn is
221
+ running, `POST /v1/gaia/sessions/{session_id}/internet` with
222
+ `{"internet_off": true}` so the next tool call follows it rather than the next
223
+ turn. Post it only once `/query` has answered: before then the turn's own
224
+ `internet_off` replaces it, so re-read the switch at that point and post it if
225
+ it changed. See [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
226
+
227
+ **Showing what a turn changed, with undo? Send `changes_turn_id`** (contract ≥
228
+ 2.19) along with `session_id`: the agent copies each file before it writes it,
229
+ into the snapshot store under its GAIA home, keyed by the session and that turn
230
+ id. The copies are complete before the stream's terminal event, so you can read
231
+ and restore them as soon as the stream ends. Omit it and nothing is copied. See
232
+ [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
233
+
234
+ **Approving tool calls by policy? Match on `args`.** A `needs_confirmation`
235
+ event's `args` are exactly the arguments the model sent for the gated call,
236
+ each value capped at 64,000 characters; `args_truncated: true` means one was
237
+ cut, so don't auto-approve that call. What the agent adds to its own prompt —
238
+ the folder a shell call runs in, a risk note, the full path of a relative
239
+ write — comes in `prompt_context`, never
240
+ in `args`. See [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
241
+
242
+ **Serving the turn from the Agent UI? Send `surface: "agent_ui"`** (contract ≥
243
+ 2.20), and the agent points recurring requests at the **Scheduled tasks** page
244
+ instead of saying it cannot schedule. Older binaries reject it with `422`.
245
+
246
+ **Calling it headless?** From contract 2.20 a failed model request names the
247
+ host it went through, and its advice points at the model server the GAIA host
248
+ uses (`LEMONADE_BASE_URL`) rather than at the TUI's settings. A front-end with
249
+ its own settings screen sends `surface` (`"tui"`, `"agent_ui"` or `"cli"`) to
250
+ get its own wording; a headless caller omits it. See
251
+ [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
252
+
253
+ **Showing the model's reasoning?** Send `reasoning: true` (contract ≥ 2.22)
254
+ and it arrives as `reasoning` events with a `delta`, apart from `status` lines
255
+ and the answer. Join the deltas as they come. Older binaries reject the field
256
+ with `422`. See [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
257
+
258
+ **Showing how full the model's window is?** From contract 2.21 the `final`
259
+ event carries `context`: the window size, the tokens the next call will take,
260
+ and what takes them (prompt, tools, documents, history). Older binaries leave
261
+ it out. It also carries `performance`, the turn's model and tool timings and
262
+ each model call's token counts, when the binary sends it. See
263
+ [`SPEC.md` §5.1](./SPEC.md#51-endpoints).
264
+
265
+ **Keeping a multi-turn conversation? Send `turns`, not `context`** (contract ≥
266
+ 2.25). Send `turns: []` on the first turn so its `final` carries
267
+ `model_messages`, store that with the reply, and send them back, oldest first,
268
+ so the agent sees the tool calls it made and what they returned.
269
+ Text-only `context` loses them, and the agent can't explain its own last turn.
270
+ An empty `turns` list clears a retained session's history. Older binaries
271
+ reject `turns` with `422`.
272
+
197
273
  Every failure throws a typed error (`IntegrityError`, `PlatformError`,
198
274
  `HealthTimeoutError`, `VersionMismatchError`, `BinaryNotFoundError`) with a message
199
275
  that names what failed and what to do about it.
package/SKILL.md CHANGED
@@ -65,7 +65,8 @@ A second sidecar started here would only fight the daemon's own for port 8141. U
65
65
 
66
66
  Other commands: `gaia fetch` (download + verify, print JSON, exit),
67
67
  `gaia serve` (sidecar alone), `gaia version` (per-component version, source URL,
68
- and platform matrix). Anything after a bare `--` goes to the TUI verbatim.
68
+ and platform matrix). Unknown or misplaced launcher options are rejected;
69
+ anything intended for the TUI goes after a bare `--` and is passed through verbatim.
69
70
 
70
71
  ### Where each binary comes from
71
72
 
@@ -117,6 +118,10 @@ Windows sidecar build**.
117
118
  | `linux-arm64` | **no** | yes |
118
119
  | `win32-arm64` | **no** | yes |
119
120
 
121
+ The `linux-x64` sidecar needs **glibc 2.34 or newer** (Ubuntu 22.04, Debian 12,
122
+ RHEL 9 or later); an older distro fails at launch, so check `ldd --version`
123
+ before blaming the package.
124
+
120
125
  Resolving the sidecar on those two keys raises `PlatformError` naming the
121
126
  platform and the supported set. It is not silently skipped, and the TUI is never
122
127
  launched with no agent behind it. `npx @amd-gaia/gaia version` prints the matrix
@@ -247,7 +252,7 @@ curl http://127.0.0.1:8141/health
247
252
  ## 7. Call `POST /v1/gaia/query`
248
253
 
249
254
  This is the whole agent surface. There is **no typed query client** in this
250
- package — call it with plain `fetch`. Contract version **2.17**; the stream is
255
+ package — call it with plain `fetch`. Contract version **2.25**; the stream is
251
256
  `text/event-stream` terminated by **exactly one** `final` or `error`.
252
257
 
253
258
  Request body (`extra: "forbid"` — an unknown field is a **422**, not ignored):
@@ -256,9 +261,17 @@ Request body (`extra: "forbid"` — an unknown field is a **422**, not ignored):
256
261
  |---|---|---|
257
262
  | `query` | yes | Non-empty. |
258
263
  | `run_id` | yes | **You mint it**, and it must be a UUID (non-UUID → 422). It is the cancel handle, valid from the instant the request is sent. |
259
- | `context` | yes | Transcript slice, pushed in the body — may be `[]`, never absent. Each item `{ role, content }`; `role` ∈ `user` / `assistant` / `system` / `tool`. |
264
+ | `context` | yes | Transcript slice, pushed in the body — may be `[]`, never absent. Each item `{ role, content }`; `role` ∈ `user` / `assistant` / `system` / `tool`. If it doesn't fit beside the agent's prompt and tools in the model's window, the agent keeps the newest whole turns that do and says so in a `status` event; one turn bigger than that is kept with its longest messages cut. |
260
265
  | `session_id` | no | Contract ≥ 2.12. **Pass it.** The agent persists its indexed-document set per session — without it, it forgets a document between the turn that indexed it and the next question. 1–128 characters from `A-Z a-z 0-9 . _ -` (a UUID works); anything else is a **400**. |
261
266
  | `documents` | no | Contract ≥ 2.16. Absolute paths of the files the user attached to this conversation — send the whole set **every turn**. The agent indexes any it has not read yet before the turn runs, granting each exact file and not its folder. A relative path is a **422**. A missing file, a folder, or a refused secret is named as not indexed in the turn's `index_documents` step, and the turn still runs. |
267
+ | `allowed_paths` | no | Contract ≥ 2.18. Absolute folder or file paths the conversation may read — the scope your UI shows. A new session gets exactly this scope; a retained session adds any entry it lacks and never drops one until the session ends. `GAIA_ALLOWED_PATHS` still narrows it. A relative path is a **422**. Omit it for the agent's default scope. |
268
+ | `working_directory` | no | Contract ≥ 2.24. Absolute chat workspace used to resolve relative file paths for reads, writes, edits and approval prompts. It is separate from `allowed_paths`; a relative path is a **422**. Send the same folder the UI shows. |
269
+ | `memory` | no | Contract ≥ 2.18. `"on"`, `"private"` or `"off"`. Under `"private"` or `"off"` the turn saves nothing to memory, including errors and lessons the agent saves on its own. A retained session keeps the last value: send it every turn. |
270
+ | `internet_off` | no | Contract ≥ 2.23. `true` refuses web search and page fetches, the browser, connector, mailbox and Agent Hub tools and any call or shell command that contacts another host, with a message telling the model the internet is off for this chat; `false` allows them. A retained session keeps the last value: send it every turn. Omit it to leave the agent as it is. To change it mid-turn, `POST /v1/gaia/sessions/{session_id}/internet` with `{"internet_off": true}`; the next tool call follows it, and an unknown session is a **404**. Post it only once `/query` has answered, since the turn's own value replaces an earlier one; re-read the switch then and post it if it changed. |
271
+ | `changes_turn_id` | no | Contract ≥ 2.19. Needs `session_id`. The agent copies each file before changing it, under `<GAIA home>/snapshots/<session_id>/<changes_turn_id>/`, so the turn can be reviewed and undone; the copies are complete before the terminal event. Format: 14 digits of local time, `-`, 8 hex digits (`YYYYmmddHHMMSS-xxxxxxxx`); anything else is a **422**. Omit it to copy nothing. |
272
+ | `surface` | no | Contract ≥ 2.20. `"agent_ui"`, `"tui"` or `"cli"` — the front-end the turn comes from. With `"agent_ui"` the agent points recurring requests at the Agent UI's **Scheduled tasks** page. It also picks where a model-provider error tells the user to fix it; omitted (a headless caller), that is the model server the GAIA host uses (`LEMONADE_BASE_URL`). Read when a session is built. |
273
+ | `turns` | no | Contract ≥ 2.25. Earlier turns as the model saw them, oldest first: each is that turn's `final.model_messages`, or a `{user, assistant}` text pair for a turn without one. Send it instead of `context` (then `[]`, or the request is a **422**) so the agent sees its own earlier tool calls and their results; send `[]` on the first turn. It keeps the newest whole turns that fit its window. Sent, even as `[]`, it makes `final` carry `model_messages`; `[]` also clears a retained session's history. |
274
+ | `reasoning` | no | Contract ≥ 2.22. `true` streams the model's reasoning as `reasoning` events (`{ delta }`), apart from `status` lines and the answer's `token`s. Join the deltas as they come: each keeps its own spacing. Omit it and reasoning folds into `status` lines as before. |
262
275
  | `can_answer_questions` | no | Set `false` for one-shot / batch runs so the agent resolves ambiguity itself instead of parking on a question nobody can see. |
263
276
  | `model` | no | Overrides the model id. On a retained `session_id` a different model is **switched in place** (contract ≥ 2.14), keeping the conversation and any loaded skills; a switch that fails is a **409** and leaves the session on its previous model. |
264
277
  | `provider` | no | `"lemonade"` (the default backend) or `"claude"`, which sends the conversation to Anthropic's API instead of the local server. Anything else is a **400**. When you name it, `model` must agree: under `"claude"` it must be a `claude-*` id, under `"lemonade"` it must not be. A mismatch is a **400**, on a new session or an existing one. Omit it and the `model` decides instead — a `claude-*` id alone reaches Anthropic, any other id runs locally — so there is nothing to mismatch. On a retained `session_id`, naming a different provider switches the session to it in place, onto `model` or, without one, the provider's default model; omitting both leaves the session where it is. |
@@ -317,13 +330,14 @@ The canonical event shapes, as emitted:
317
330
 
318
331
  | Event | Shape |
319
332
  |---|---|
320
- | `status` | `{ type, message, phase?, words?, chars? }` — progress and reasoning narration. `phase` (`loading_model`, `downloading_model`, `reading`, `reasoning`, `tool_call`) names what the model is doing; show it as the live line, not as narration |
333
+ | `status` | `{ type, message, phase?, words?, chars?, queue? }` — progress and reasoning narration. `phase` (`loading_model`, `downloading_model`, `reading`, `reasoning`, `tool_call`, `model_queue`) names what the model is doing; show it as the live line, not as narration. `model_queue` means the turn is waiting for the model behind another request: it repeats every ~2 s with a `queue` object (`event`, `position`, `queue_length`, `holder`, `holder_model`, `waited_s`), so its steady arrival means queued, not stuck |
321
334
  | `token` | `{ type, delta }` — answer text to append |
322
- | `tool_call` | `{ type, tool, args }` |
323
- | `tool_result` | `{ type, tool, data, render? }` |
324
- | `needs_confirmation` | `{ type, run_id, action, summary, confirm_id?, always_scope? }` — answer it, or it ends the run; see §8 |
335
+ | `reasoning` | `{ type, delta }` — the model's reasoning, only when the request set `reasoning: true` (contract ≥ 2.22); join the deltas as they come and keep them apart from the answer |
336
+ | `tool_call` | `{ type, tool, args, mcp_server?, mcp_server_label?, mcp_tool? }` — an MCP tool names its connector (`mcp_server_label`, e.g. `"Git"`) and the server's own tool name (`mcp_tool`, e.g. `"git_log"`); label the call with those, not the prefixed `tool` |
337
+ | `tool_result` | `{ type, tool, data, render? }` — an MCP tool that returned text carries it in `data.mcp_output` (`{ text, truncated }`); a `search_web` result lists its hits in `data.sources` (`[{ title, url }]`) |
338
+ | `needs_confirmation` | `{ type, run_id, action, summary, confirm_id?, always_scope?, risk?, tool_label?, args?, args_truncated?, prompt_context? }` — answer it, or it ends the run; see §8. `summary` shortens long code; `args` is the call itself, exactly the arguments the model sent, each value capped at 64,000 characters (`args_truncated: true` when one was cut — don't auto-approve a call you can't fully see). What GAIA adds to the prompt is in `prompt_context`, never `args`: `working_directory` (where a shell call runs, when the model's arguments don't say), `risk_note` (why GAIA's checks don't cover the call) and `file_path` (the absolute path a write lands at, when the model gave a relative one) |
325
339
  | `needs_input` | `{ type, run_id, request_id, question, options[], allow_free_text, sensitive, respond_url, timeout_seconds? }` |
326
- | `final` | `{ type, answer, usage? }` — terminal |
340
+ | `final` | `{ type, answer, usage?, failed?, context?, context_error?, performance?, model_messages? }` — terminal. `failed: true`: the model failed mid-turn and `answer` says what was and wasn't done; show it as an error, not a result. `model_messages` (contract ≥ 2.25, only when the request sent `turns`) is the turn as the model saw it; store it with the reply and send it back in `turns`. `context` / `context_error` and `performance` (the turn's step timings) are optional; see SPEC §5.1 |
327
341
  | `error` | `{ type, detail, status }` — terminal, surface `detail` verbatim |
328
342
 
329
343
  Rules a client must respect:
@@ -342,7 +356,10 @@ Rules a client must respect:
342
356
  - **Cancel with `POST /v1/gaia/query/{run_id}/cancel`.** It returns
343
357
  `{ run_id, cancelled }` — an unknown id reports `cancelled: false` with a
344
358
  **200**, not a 404, because a cancel racing a normal completion is expected.
345
- Dropping the HTTP connection also cancels the run.
359
+ Dropping the HTTP connection also cancels the run. A run stopped mid-answer
360
+ ends with an empty `final`, and no `token` follows the cancel, so keep the
361
+ text the `token` events already delivered. Text not yet sent when the
362
+ cancel lands is dropped, not delivered late.
346
363
  - **Add to a running turn with `POST /v1/gaia/query/{run_id}/followup`**
347
364
  (contract ≥ 2.15, body `{ text }`). The run is not interrupted and no second
348
365
  turn starts: the agent folds the text in at its next step boundary and
@@ -440,6 +457,12 @@ not for a bound socket, so it stays on stdio: HTTP sessions never lift the shell
440
457
  gates, and the request body rejects unknown fields so a client cannot ask. See
441
458
  SPEC §5.5.
442
459
 
460
+ So a shell command past those gates (a delete, `git add`) is never run unasked
461
+ under bypass. On a run someone can answer (`can_answer_questions` not `false`,
462
+ with a `session_id`) it emits `needs_confirmation` and waits for
463
+ `/tool_decision`, exactly as with bypass off; commands that need no approval
464
+ still run unasked. A run nobody can answer refuses it, as below.
465
+
443
466
  **A run nobody can answer is still refused.** With `can_answer_questions: false`,
444
467
  or with no `session_id`, the server emits `needs_confirmation`, follows it
445
468
  immediately with a terminal `final` saying it stopped before running the action,
@@ -558,10 +581,12 @@ Two consequences an integrator needs to plan for:
558
581
  alongside `gaia-voice`'s 790.
559
582
  - **An embedding pass on the first semantic code search.** If the repo has no
560
583
  [code index](https://amd-gaia.ai/docs/guides/code-index), the first
561
- `search_code_index` builds it, then searches. On a large monorepo that one
562
- search takes minutes of local embedding; a task that never searches pays
563
- nothing. `GAIA_PROJECT_MAP_AUTO_INDEX=1` builds it in the background at task
564
- start instead.
584
+ `search_code_index` starts building it in the background. A small repo is
585
+ searched within that call; on a large monorepo the build takes many minutes
586
+ of local embedding, and each search until then reports its progress so the
587
+ agent greps meanwhile. A task that never searches pays nothing.
588
+ `GAIA_PROJECT_MAP_AUTO_INDEX=1` builds it in the background at task start
589
+ instead.
565
590
 
566
591
  The sidecar's CLI accepts only `--host` and `--port`, so pointing the map at a
567
592
  specific project means `GAIA_PROJECT_ROOT=/path/to/repo` in its environment, or
@@ -640,7 +665,7 @@ There is no silent null.
640
665
  temp dir) is dropped, and a **shared** bin directory is moved to the end
641
666
  instead of removed, so the `python3` / `lemonade-server` / real `gaia` beside
642
667
  it stay reachable. If the Python CLI isn't installed anywhere, the daemon never
643
- comes up. It must also be **0.25.0+**: an older core's daemon starts fine but
668
+ comes up. It must also be **0.25.3+**: an older core's daemon starts fine but
644
669
  cannot run this agent, which reads as a UI with a dead agent rather than as
645
670
  a version problem.
646
671
  - **The TUI is installed as `gaia-tui`, never `gaia`** — the terminal-hub artifact
@@ -671,7 +696,7 @@ Then, in another terminal:
671
696
 
672
697
  ```bash
673
698
  curl -s http://127.0.0.1:8141/health # {"status":"ok","service":"gaia-agent-gaia"}
674
- curl -s http://127.0.0.1:8141/version # {"apiVersion":"2.17","agentVersion":"0.1.1"}
699
+ curl -s http://127.0.0.1:8141/version # {"apiVersion":"2.25","agentVersion":"0.3.1"}
675
700
  curl -s http://127.0.0.1:8141/v1/gaia/init # 200 + "ready":true, or 503 + a "hint"
676
701
  curl -N -X POST http://127.0.0.1:8141/v1/gaia/query \
677
702
  -H 'content-type: application/json' \
package/SPEC.md CHANGED
@@ -135,6 +135,10 @@ failure anywhere: it is a 404 on a user's first run.
135
135
  | `linux-arm64` | **no** | yes |
136
136
  | `win32-arm64` | **no** | yes |
137
137
 
138
+ The `linux-x64` sidecar is built on a glibc 2.34 floor (#5545), so it needs
139
+ glibc 2.34 or newer at run time (Ubuntu 22.04, Debian 12, RHEL 9 or later).
140
+ On an older distro it fails at launch.
141
+
138
142
  `terminal-hub` publishes all six. The sidecar is a PyInstaller freeze, produced on
139
143
  the platform it targets, and there is no arm64 Linux or arm64 Windows freeze.
140
144
  Resolving `sidecar` on those two keys raises `PlatformError` naming the platform
@@ -254,7 +258,7 @@ the header.
254
258
  | ------------------ | --------------------------------------- |
255
259
  | Default port | `8141` (`DEFAULT_PORT` in `server.py`) |
256
260
  | Reserved port | `4001` — refused with a `RangeError` |
257
- | Contract version | `API_VERSION = "2.17"` |
261
+ | Contract version | `API_VERSION = "2.25"` |
258
262
  | Agent id / prefix | `gaia` → `/v1/gaia/...` |
259
263
 
260
264
  ### 5.1 Endpoints
@@ -271,7 +275,8 @@ the header.
271
275
  | `POST` | `/v1/gaia/query/{run_id}/cancel` | Cancel a run by its host-minted `run_id` |
272
276
  | `POST` | `/v1/gaia/query/{run_id}/respond`| Answer a mid-run question |
273
277
  | `POST` | `/v1/gaia/query/{run_id}/tool_decision` | Answer a confirmation-gated tool (≥ 2.14) |
274
- | `POST` | `/v1/gaia/sessions/{session_id}/bypass` | Run gated tools without asking, for one session (≥ 2.14) |
278
+ | `POST` | `/v1/gaia/sessions/{session_id}/bypass` | Stop the prompts for gated tools in one session (≥ 2.14); the shell gates stay on, so a gated shell command still answers `needs_confirmation` |
279
+ | `POST` | `/v1/gaia/sessions/{session_id}/internet` | Turn one session's internet on or off from its next tool call, including in a turn already running (≥ 2.23) |
275
280
  | `POST` | `/v1/gaia/query/{run_id}/followup`| Add to a run already in flight (contract ≥ 2.15) |
276
281
 
277
282
  #### `/query` request body
@@ -286,7 +291,15 @@ checked for drift in CI — the same pattern as the email agent's own
286
291
  missing required one is `422` before the agent runs. `run_id` must parse as a
287
292
  UUID (`uuid.UUID(v)`); a non-UUID string is also `422`. Everything else
288
293
  (`model`, `provider`, `max_steps`, `session_id`, `can_answer_questions`,
289
- `documents`) is optional.
294
+ `documents`, `allowed_paths`, `working_directory`, `memory`, `internet_off`,
295
+ `changes_turn_id`, `surface`) is optional.
296
+
297
+ `context` is the transcript you push. It replaces the agent's history for the
298
+ turn. If it doesn't fit beside the agent's prompt and tools in the model's
299
+ window, the agent drops the oldest whole turns until it does, and streams a
300
+ `status` event saying older messages are out of the model's view. A single turn
301
+ bigger than the whole budget is kept, not dropped: its longest messages are cut
302
+ with a note saying how many characters are missing, so its answer stays in view.
290
303
 
291
304
  `documents` (contract ≥ 2.16) carries the absolute paths of the files the user
292
305
  attached to the conversation. Send the whole set on every turn. Before the turn
@@ -297,6 +310,101 @@ a secret the path policy refuses is reported as not indexed in the turn's
297
310
  `index_documents` step, and the turn still runs. Pre-2.16 binaries `422` the
298
311
  field, so probe `/version` before sending it.
299
312
 
313
+ `allowed_paths` (contract ≥ 2.18) carries the conversation's file scope as the
314
+ caller computed it, so the agent works in the folders the caller's UI shows. A
315
+ new session is built with exactly that scope; a retained session gains any
316
+ entry it lacks and drops none until the session ends — neither what the user
317
+ granted mid-chat nor an entry the caller has since stopped sending. `GAIA_ALLOWED_PATHS`
318
+ still narrows it, and a path outside it is asked about, never silently
319
+ granted. Omit it for the agent's default scope. Pre-2.18 binaries `422` the
320
+ field. Whatever the scope, its existing folders are named in the system prompt
321
+ once, when the session is built, with GAIA's documents folder first; a folder
322
+ added to a retained session later is usable but not named. A caller that omits
323
+ the field, like the TUI, gets the default scope (the user's home folder) named.
324
+
325
+ `working_directory` (contract ≥ 2.24) is the absolute chat workspace used as the
326
+ base for relative file paths. It is separate from `allowed_paths`: the latter is
327
+ an access allowlist, while this field determines where a bare `notes.txt` lands.
328
+ New and retained sessions use it for relative reads, writes, edits and approval
329
+ prompts, so the path shown to the user matches the file that is actually changed.
330
+ A relative or empty value is a `422`; send it only when `/version` reports 2.24 or
331
+ later.
332
+
333
+ `memory` (contract ≥ 2.18) is `"on"`, `"private"` (a private chat) or `"off"`
334
+ (memory turned off in the caller's settings). Under `"private"` or `"off"` the
335
+ turn saves nothing to memory, including the errors and lessons the agent saves
336
+ on its own. A retained session keeps the last value, so send it every turn;
337
+ omit it to leave memory as the agent has it. `"on"` never turns memory on when
338
+ the agent has none (no store, or `GAIA_MEMORY_DISABLED=1`). Pre-2.18 binaries
339
+ `422` the field.
340
+
341
+ `internet_off` (contract ≥ 2.23) is `true` when the caller's chat has the
342
+ Internet switched off and `false` when it is on. With `true` the turn refuses
343
+ web search and page fetches, the browser, connector (MCP), mailbox and Agent
344
+ Hub tools and any call or shell command that contacts a host other than the agent's own machine, and
345
+ the refusal tells the model the internet is off for this chat. A retained
346
+ session keeps the last value, so send it every turn; omit it to leave the
347
+ agent as it is. Pre-2.23 binaries `422` the field, so a caller whose chat has
348
+ the internet off should check `/version` and refuse the turn on an older
349
+ binary rather than answer it online, and cancel an older binary's running
350
+ turn when the switch goes off.
351
+
352
+ `internet_off` is read when a turn starts. To change it for a turn already
353
+ running, `POST /v1/gaia/sessions/{session_id}/internet` with
354
+ `{ "internet_off": true }` (or `false`): the session's next tool call follows
355
+ it, and so do later turns until a `/query` sends another value. An unknown
356
+ session is a **404** rather than a new one.
357
+
358
+ Send it only once `/query` has answered. The response starts after the turn's
359
+ own `internet_off` is applied, so a switch posted earlier is replaced by the
360
+ body's value, and on a session's first turn it is a **404** because the
361
+ session does not exist yet. A caller whose user can flip the switch while a
362
+ turn starts re-reads it when `/query` answers and posts it if it changed.
363
+
364
+ `changes_turn_id` (contract ≥ 2.19) asks the agent to record the turn's file
365
+ changes for undo. Before a tool writes, edits, creates or deletes a file, the
366
+ agent saves a copy of it under `<GAIA home>/snapshots/<session_id>/<changes_turn_id>/`,
367
+ the store the Agent UI's Changes view, Outputs and Undo read. The recording is
368
+ complete before the terminal `final` or `error` event is sent, even while the
369
+ agent finishes post-answer work, so a caller may read or restore it as soon as
370
+ the stream ends. It needs `session_id` (`422` without one) and must look like
371
+ `file_snapshots.new_turn_id()` makes it — 14 digits of local time, a hyphen and
372
+ 8 hex digits — so a session's turns sort in the order they ran; anything else
373
+ is a `422`. Work the agent cannot copy, such as a shell command, is noted in
374
+ the turn as untracked. Omit the field to record nothing. Pre-2.19 binaries
375
+ `422` it.
376
+
377
+ `surface` (contract ≥ 2.20) is the front-end the turn comes from: `"agent_ui"`,
378
+ `"tui"` or `"cli"`. With `"agent_ui"` the agent points recurring requests at the
379
+ Agent UI's **Scheduled tasks** page instead of saying it cannot schedule. It is
380
+ read when a session is built and kept for that session. It also picks where a
381
+ model-provider error tells the user to fix it: each front-end gets its own
382
+ settings screen, and a headless caller, which omits it, is pointed at the model
383
+ server the GAIA host uses (`LEMONADE_BASE_URL`). A failed model request also
384
+ names the `host:port` it went through; on a base URL the user set, a local
385
+ model's failure reads "the model server at `host:port`", not "Lemonade on this
386
+ machine". Pre-2.20 binaries `422` the field and word every such error for the
387
+ TUI.
388
+
389
+ `turns` (contract ≥ 2.25) is the conversation so far as the model saw it,
390
+ oldest first. Each entry is one earlier turn's `final.model_messages`, which
391
+ holds the assistant's tool calls and the tool results, or a user/assistant text
392
+ pair for a turn that recorded none. The agent replays the newest whole turns
393
+ that fit its context window and says in a `status` event when older ones drop
394
+ out. Sent, it replaces `context`, which must then be `[]` (anything else is a
395
+ `422`); `turns: []` means no earlier turns, and clears a retained session's
396
+ history. A request that sent `turns`, even `[]`, gets `model_messages` on its
397
+ `final` so the caller can store it with the reply; a request without `turns`
398
+ never does, because the record holds every tool result in full. It is also
399
+ absent when the run had not finished 15 s after its answer, and that turn then
400
+ replays as text only. Pre-2.25 binaries `422` `turns`.
401
+
402
+ `reasoning` (contract ≥ 2.22), when `true`, streams the model's reasoning as
403
+ `reasoning` events, `{ "type": "reasoning", "delta": "…" }`, so a client can
404
+ show it apart from `status` lines and from the answer's `token`s. Each delta
405
+ keeps its own spacing: join them as they arrive. Without the field, reasoning
406
+ folds into `status` lines as before. Pre-2.22 binaries `422` it.
407
+
300
408
  ```json
301
409
  {
302
410
  "query": "What's in this directory?",
@@ -307,6 +415,22 @@ field, so probe `/version` before sending it.
307
415
  }
308
416
  ```
309
417
 
418
+ #### `needs_confirmation` arguments
419
+
420
+ A `needs_confirmation` event carries the gated call in `args`: exactly the
421
+ arguments the model sent, each value capped at 64,000 characters with a note
422
+ saying how much was cut, and `args_truncated: true` when any was. `summary`
423
+ shortens long code, so show `args` when the user should read all of it; a
424
+ policy client matches its rules on `args` and should not auto-approve a call
425
+ with `args_truncated`. The agent never adds a key to `args`. What it adds to
426
+ its own prompt is in `prompt_context`: `working_directory` (where a shell call
427
+ runs, when the model's arguments don't say it), `risk_note` (why the
428
+ agent's checks don't cover the call, which is then approved once) and
429
+ `file_path` (the absolute path a write lands at, when the model gave a relative
430
+ one). A prompt
431
+ the agent raises on its own, such as `allow_path_access`, has the path it asks
432
+ about in `args`.
433
+
310
434
  `/health` is liveness only. It says nothing about whether Lemonade is up or a
311
435
  model is loaded — `/v1/gaia/init` answers that.
312
436
 
@@ -319,13 +443,43 @@ daemon-transport counterpart of the stdio `MEMORY_DUMP_QUERY` sentinel; both
319
443
  paths call the same `build_memory_dump()` and return the identical shape.
320
444
 
321
445
  `GET /v1/gaia/skills` (contract ≥ 2.17) lists the skills the agent can load:
322
- `{ "skills": [{ "name", "description", "version", "tier", "tools", "path" }],
323
- "invalid": { "<folder>": "<why it failed to parse>" } }`. It reads the same
324
- roots the agent does — its bundled skills, `~/.gaia/skills`, Claude Code skill
325
- folders — so a host without the Python package (the bundled skills live inside
326
- the binary) can still show them. A skill root that cannot be read is a `500`
446
+ `{ "skills": [{ "name", "description", "version", "tier", "tools", "path",
447
+ "source" }], "invalid": { "<folder>": "<why it failed to parse>" } }`. It reads
448
+ the same roots the agent does — its bundled skills, `~/.gaia/skills`, Claude
449
+ Code skill folders — so a host without the Python package (the bundled skills
450
+ live inside the binary) can still show them. `source` names the root a skill
451
+ came from: `agent-bundled`, `user` or `claude-import`, or `null` for any other
452
+ root. An agent that predates `source` omits the field; a host must treat a list
453
+ where any skill lacks it as ungrouped and show it flat, not drop the skill. A skill root that cannot be read is a `500`
327
454
  naming it.
328
455
 
456
+ `/query` streams the answer as `token` events before its terminal `final`. A
457
+ run cancelled mid-answer (`/cancel`, or a dropped connection) ends with an
458
+ empty `final` and sends no `token` after the cancel, so a host keeps the text
459
+ the `token` events already delivered. Answer text the model produced but the
460
+ stream had not yet sent when the cancel landed is discarded with the
461
+ half-written tail.
462
+
463
+ `final` carries `context` (contract ≥ 2.21): what the next model call holds
464
+ if the caller pushes this turn back as history — the same payload the Agent
465
+ UI's Context panel shows. `window` is the model's context window in tokens,
466
+ `used` the tokens taken, and `system`, `tools`, `documents`, `history` split it
467
+ by kind; `categories` lists the panel's rows (their tokens add up to `used`),
468
+ `tool_loading` which tools ride every request, and `tokenizer.exact` whether
469
+ the counts came from the loaded model's own tokenizer or the shared estimate.
470
+ A turn that was stopped or ended on a model failure carries no `context`; a
471
+ measurement that failed carries `context_error`, one sentence saying why, and
472
+ the answer still stands. Older binaries send neither, so read `context` only
473
+ when `/version` reports 2.21 or later.
474
+
475
+ `final` also carries `performance`: the turn's step timings, as
476
+ `{ "timing_summary", "conversation" }`. `timing_summary` holds the run's model
477
+ and tool seconds and its model call count; `conversation` holds one
478
+ `{ "role": "system", "content": { "type": "stats", ... } }` entry per model
479
+ call, with that call's token counts and speed. The Agent UI turns it into the
480
+ Context panel's "Under the hood" figures. A run that timed nothing
481
+ carries none, and older binaries never send it, so read it only when present.
482
+
329
483
  ### 5.2 `session_id` and agent retention
330
484
 
331
485
  `POST /v1/gaia/query` accepts an optional `session_id` in the request body.
@@ -335,7 +489,8 @@ agent instead of a throwaway built fresh per call — indexed documents and
335
489
  `load_skill` state only survive between turns when the same `session_id`
336
490
  threads them together. A `session_id` must be 1–128 characters from
337
491
  `A-Z a-z 0-9 . _ -`, not only dots (a UUID works); any other value is a **400**
338
- naming the allowed characters, on `/query` and on `/sessions/{session_id}/bypass`.
492
+ naming the allowed characters, on `/query`, `/sessions/{session_id}/bypass` and
493
+ `/sessions/{session_id}/internet`.
339
494
  Omitting it is a valid, explicit one-shot: nothing persists past that single
340
495
  turn, and the agent is not told otherwise.
341
496
 
@@ -362,7 +517,8 @@ directory shape, entry points, installed commands and platform quirks, present
362
517
  whenever the agent's working directory resolves to a code repository (a VCS
363
518
  directory or a recognised manifest at its root). `GAIA_PROJECT_ROOT=<path>`
364
519
  picks the project when the working directory is not it. If that repository has
365
- no code index, the first `search_code_index` builds one;
520
+ no code index, the first `search_code_index` starts building one in the
521
+ background and reports its progress until it lands;
366
522
  `GAIA_PROJECT_MAP_AUTO_INDEX=1` builds it in a background thread at task start
367
523
  instead. The same repository check also
368
524
  makes `run_shell_command` always-on for the session instead of semantically
@@ -479,7 +635,12 @@ a JSON line carrying a `gaia_control` key: a back-channel that answers a
479
635
  confirmation prompt *while* a turn is in flight, toggles full access or
480
636
  "accept edits" (file edits inside the starting folder run unasked; commands still
481
637
  ask), and stops a turn (`cancel`) without ending the process — so loaded skills,
482
- "always" grants, history and the permission mode survive a cancel.
638
+ "always" grants, history and the permission mode survive a cancel. When stdin
639
+ closes mid-turn (the host has gone), the running turn is cancelled and saved in
640
+ the session history as interrupted: "This answer was interrupted when GAIA
641
+ closed." The question is saved when the turn starts, so it survives even if the
642
+ host kills the process before the turn ends. Another process sharing the
643
+ history file leaves a turn that is still running out of its model view.
483
644
 
484
645
  Contract 2.14 gave the HTTP surface the same three capabilities per run and per
485
646
  session — `/tool_decision`, `/sessions/{id}/bypass` (the route keeps its
@@ -515,7 +676,10 @@ arbitrary code execution in the working directory, which is why it exists only
515
676
  on this transport: one local parent process on a private pipe. It is **not**
516
677
  reachable over HTTP, and the request body cannot ask for it —
517
678
  `POST /v1/gaia/sessions/{id}/bypass` stops an HTTP session's approval prompts
518
- but never lifts these shell gates. `rm` is excluded
679
+ but never lifts these shell gates. A command those gates hold is never run
680
+ unasked there: on a run someone can answer it still emits `needs_confirmation`
681
+ and waits for `/tool_decision`, and on a run nobody can answer it is refused.
682
+ `rm` is excluded
519
683
  from the developer set — not a boundary, since anything in the set can delete a
520
684
  file, but a tripwire against an accidental recursive delete. Redirection has one
521
685
  exception: a command that is nothing but a skill-granted CLI runs argv-only
@@ -634,7 +798,7 @@ failed start never leaks a process.
634
798
  | ------- | ----------------------------------------------------------------------- |
635
799
  | `0` | Success |
636
800
  | `1` | A typed failure: `IntegrityError`, `PlatformError`, `HealthTimeoutError`, `VersionMismatchError`, `BinaryNotFoundError`, `PortInUseError`, `SidecarExitedError`, `MalformedResponseError`, or an unexpected error; also `serve` on Ctrl+C when the sidecar survives the forced kill (the message names its pid and the kill command) |
637
- | `2` | Usage error: unknown command, invalid `--port`, or a flag the command does not read (`run --port`, `serve --component`, `serve --cache-dir`, `run`/`serve` `--platform`), an unknown `--component`, or a non-https `--base-url` without `--allow-insecure-base-url` |
801
+ | `2` | Usage error: unknown command, an unknown option, invalid `--port`, or a flag the command does not read (`run --port`, `serve --component`, `serve --cache-dir`, `run`/`serve` `--platform`), an unknown `--component`, or a non-https `--base-url` without `--allow-insecure-base-url` |
638
802
  | *other* | From `run`: the TUI's own exit code, propagated verbatim |
639
803
 
640
804
  A TUI killed by a signal propagates as `128 + signum` (the shell convention), so a
@@ -1,34 +1,34 @@
1
1
  {
2
2
  "schemaVersion": "3.0",
3
- "agentVersion": "0.3.0",
3
+ "agentVersion": "0.3.3",
4
4
  "components": {
5
5
  "sidecar": {
6
- "componentVersion": "0.3.0",
7
- "baseUrl": "https://hub.amd-gaia.ai/agents/gaia/0.3.0",
6
+ "componentVersion": "0.3.3",
7
+ "baseUrl": "https://hub.amd-gaia.ai/agents/gaia/0.3.3",
8
8
  "platforms": {
9
9
  "win32-x64": {
10
10
  "filename": "gaia-agent-win32-x64.exe",
11
11
  "executable": "gaia-agent.exe",
12
- "sha256": "12e79e63e1169eafadbdd453e2d5e9114bc8c9c04e7ab95e9bd1c96249f50067",
13
- "size": 169416076
12
+ "sha256": "44892b0c48b6f41d25372045b9136dc259edd644196acc59592a8fc5d16ee7a4",
13
+ "size": 173039470
14
14
  },
15
15
  "darwin-arm64": {
16
16
  "filename": "gaia-agent-darwin-arm64",
17
17
  "executable": "gaia-agent",
18
- "sha256": "f431642fc5cd9c05110cb8f0ffd05fe36853fe1a579bbd0b5421a3c79adc5ddd",
19
- "size": 159366272
18
+ "sha256": "66f086d888ba8d42a18a38cc2a4f532200aa03ec36e22b8ca253e21698bbd01d",
19
+ "size": 162600800
20
20
  },
21
21
  "darwin-x64": {
22
22
  "filename": "gaia-agent-darwin-x64",
23
23
  "executable": "gaia-agent",
24
- "sha256": "c793fa760b42c1c1acea7aa411f7a6830ee3bd9f68f56c5b6fac686801883f3b",
25
- "size": 168077296
24
+ "sha256": "864155bcee93886d22f48f04c160329baf74c7900e6cb11693a1879eb2b5f18a",
25
+ "size": 173141904
26
26
  },
27
27
  "linux-x64": {
28
28
  "filename": "gaia-agent-linux-x64",
29
29
  "executable": "gaia-agent",
30
- "sha256": "b2b115d0ae84e5fbe9aebcf055b264b5b68ae2f1a4b436bdd80de6602ca51437",
31
- "size": 221414096
30
+ "sha256": "a35a9db438e4de0abd289283eaab7ac44abf2bc529d66deb5d2eada01c423e3d",
31
+ "size": 212086912
32
32
  }
33
33
  }
34
34
  },
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAGA;;;;;;;;;;;GAWG;AA8BH,UAAU,UAAU;IAClB,CAAC,EAAE,MAAM,EAAE,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,mEAAmE;IACnE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAaD,4EAA4E;AAC5E,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAExC,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAuCpD;AAsID;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,MAAM,GAAG,SAA+B,EACjD,KAAK,GAAE,MAAM,GAAG,SAA2B,GAC1C,MAAM,GAAG,SAAS,CAsBpB;AA+CD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAUnG;AAsLD,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1D"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAGA;;;;;;;;;;;GAWG;AA8BH,UAAU,UAAU;IAClB,CAAC,EAAE,MAAM,EAAE,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,mEAAmE;IACnE,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAaD,4EAA4E;AAC5E,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAExC,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAwCpD;AA8JD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,MAAM,GAAG,SAA+B,EACjD,KAAK,GAAE,MAAM,GAAG,SAA2B,GAC1C,MAAM,GAAG,SAAS,CAsBpB;AA+CD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAUnG;AAsLD,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1D"}