@amd-gaia/gaia 0.2.0 → 0.3.2

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/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.12"` |
261
+ | Contract version | `API_VERSION = "2.24"` |
258
262
  | Agent id / prefix | `gaia` → `/v1/gaia/...` |
259
263
 
260
264
  ### 5.1 Endpoints
@@ -265,13 +269,204 @@ the header.
265
269
  | `GET` | `/version` | Contract probe. `{ "apiVersion", "agentVersion" }` |
266
270
  | `GET` | `/v1/gaia/version` | The TUI's negotiation probe |
267
271
  | `GET` | `/v1/gaia/init` | Readiness detail (Lemonade, model, connectors) |
272
+ | `GET` | `/v1/gaia/memory` | The `/memory` snapshot (contract ≥ 2.13) |
273
+ | `GET` | `/v1/gaia/skills` | The skills the agent can load (contract ≥ 2.17) |
268
274
  | `POST` | `/v1/gaia/query` | The streaming surface (`text/event-stream`) |
269
275
  | `POST` | `/v1/gaia/query/{run_id}/cancel` | Cancel a run by its host-minted `run_id` |
270
276
  | `POST` | `/v1/gaia/query/{run_id}/respond`| Answer a mid-run question |
277
+ | `POST` | `/v1/gaia/query/{run_id}/tool_decision` | Answer a confirmation-gated tool (≥ 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) |
280
+ | `POST` | `/v1/gaia/query/{run_id}/followup`| Add to a run already in flight (contract ≥ 2.15) |
281
+
282
+ #### `/query` request body
283
+
284
+ The full, machine-readable contract — every field, type, and which are
285
+ required — is the committed
286
+ [`openapi.gaia.json`](https://github.com/amd/gaia/blob/main/hub/agents/gaia/python/openapi.gaia.json),
287
+ regenerated from the live routes by `python -m gaia_agent.export_openapi` and
288
+ checked for drift in CI — the same pattern as the email agent's own
289
+ `openapi.email.json`. Required: **`query`**, **`run_id`**,
290
+ **`context`** — `additionalProperties: false`, so an unrecognized field or a
291
+ missing required one is `422` before the agent runs. `run_id` must parse as a
292
+ UUID (`uuid.UUID(v)`); a non-UUID string is also `422`. Everything else
293
+ (`model`, `provider`, `max_steps`, `session_id`, `can_answer_questions`,
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.
303
+
304
+ `documents` (contract ≥ 2.16) carries the absolute paths of the files the user
305
+ attached to the conversation. Send the whole set on every turn. Before the turn
306
+ runs, the agent indexes the ones it has not indexed yet, so a file attached
307
+ mid-conversation is read on the next turn. Each path is granted as that exact
308
+ file, never its folder. A relative path is a `422`. A missing file, a folder, or
309
+ a secret the path policy refuses is reported as not indexed in the turn's
310
+ `index_documents` step, and the turn still runs. Pre-2.16 binaries `422` the
311
+ field, so probe `/version` before sending it.
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
+ `reasoning` (contract ≥ 2.22), when `true`, streams the model's reasoning as
390
+ `reasoning` events, `{ "type": "reasoning", "delta": "…" }`, so a client can
391
+ show it apart from `status` lines and from the answer's `token`s. Each delta
392
+ keeps its own spacing: join them as they arrive. Without the field, reasoning
393
+ folds into `status` lines as before. Pre-2.22 binaries `422` it.
394
+
395
+ ```json
396
+ {
397
+ "query": "What's in this directory?",
398
+ "run_id": "5b1f6e2a-7c34-4b4d-9b3e-2b6d9a7a1c3e",
399
+ "context": [],
400
+ "session_id": "my-session-1",
401
+ "can_answer_questions": true
402
+ }
403
+ ```
404
+
405
+ #### `needs_confirmation` arguments
406
+
407
+ A `needs_confirmation` event carries the gated call in `args`: exactly the
408
+ arguments the model sent, each value capped at 64,000 characters with a note
409
+ saying how much was cut, and `args_truncated: true` when any was. `summary`
410
+ shortens long code, so show `args` when the user should read all of it; a
411
+ policy client matches its rules on `args` and should not auto-approve a call
412
+ with `args_truncated`. The agent never adds a key to `args`. What it adds to
413
+ its own prompt is in `prompt_context`: `working_directory` (where a shell call
414
+ runs, when the model's arguments don't say it), `risk_note` (why the
415
+ agent's checks don't cover the call, which is then approved once) and
416
+ `file_path` (the absolute path a write lands at, when the model gave a relative
417
+ one). A prompt
418
+ the agent raises on its own, such as `allow_path_access`, has the path it asks
419
+ about in `args`.
271
420
 
272
421
  `/health` is liveness only. It says nothing about whether Lemonade is up or a
273
422
  model is loaded — `/v1/gaia/init` answers that.
274
423
 
424
+ `GET /v1/gaia/memory` returns the read-only snapshot behind the TUI's
425
+ `/memory` view: `{ "available", "reason", "stats", "contexts", "shown",
426
+ "total", "items" }`. `available: false` means the session has no live memory
427
+ store (Lemonade down, embedding model not pulled, disabled via env) — `reason`
428
+ names why, so an outage never renders as "you have no memories". This is the
429
+ daemon-transport counterpart of the stdio `MEMORY_DUMP_QUERY` sentinel; both
430
+ paths call the same `build_memory_dump()` and return the identical shape.
431
+
432
+ `GET /v1/gaia/skills` (contract ≥ 2.17) lists the skills the agent can load:
433
+ `{ "skills": [{ "name", "description", "version", "tier", "tools", "path",
434
+ "source" }], "invalid": { "<folder>": "<why it failed to parse>" } }`. It reads
435
+ the same roots the agent does — its bundled skills, `~/.gaia/skills`, Claude
436
+ Code skill folders — so a host without the Python package (the bundled skills
437
+ live inside the binary) can still show them. `source` names the root a skill
438
+ came from: `agent-bundled`, `user` or `claude-import`, or `null` for any other
439
+ root. An agent that predates `source` omits the field; a host must treat a list
440
+ 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`
441
+ naming it.
442
+
443
+ `/query` streams the answer as `token` events before its terminal `final`. A
444
+ run cancelled mid-answer (`/cancel`, or a dropped connection) ends with an
445
+ empty `final` and sends no `token` after the cancel, so a host keeps the text
446
+ the `token` events already delivered. Answer text the model produced but the
447
+ stream had not yet sent when the cancel landed is discarded with the
448
+ half-written tail.
449
+
450
+ `final` carries `context` (contract ≥ 2.21): what the next model call holds
451
+ if the caller pushes this turn back as history — the same payload the Agent
452
+ UI's Context panel shows. `window` is the model's context window in tokens,
453
+ `used` the tokens taken, and `system`, `tools`, `documents`, `history` split it
454
+ by kind; `categories` lists the panel's rows (their tokens add up to `used`),
455
+ `tool_loading` which tools ride every request, and `tokenizer.exact` whether
456
+ the counts came from the loaded model's own tokenizer or the shared estimate.
457
+ A turn that was stopped or ended on a model failure carries no `context`; a
458
+ measurement that failed carries `context_error`, one sentence saying why, and
459
+ the answer still stands. Older binaries send neither, so read `context` only
460
+ when `/version` reports 2.21 or later.
461
+
462
+ `final` also carries `performance`: the turn's step timings, as
463
+ `{ "timing_summary", "conversation" }`. `timing_summary` holds the run's model
464
+ and tool seconds and its model call count; `conversation` holds one
465
+ `{ "role": "system", "content": { "type": "stats", ... } }` entry per model
466
+ call, with that call's token counts and speed. The Agent UI turns it into the
467
+ Context panel's "Under the hood" figures. A run that timed nothing
468
+ carries none, and older binaries never send it, so read it only when present.
469
+
275
470
  ### 5.2 `session_id` and agent retention
276
471
 
277
472
  `POST /v1/gaia/query` accepts an optional `session_id` in the request body.
@@ -279,27 +474,48 @@ model is loaded — `/v1/gaia/init` answers that.
279
474
  whole conversation.** Contract ≥ 2.12 resolves `session_id` to a *retained*
280
475
  agent instead of a throwaway built fresh per call — indexed documents and
281
476
  `load_skill` state only survive between turns when the same `session_id`
282
- threads them together.
477
+ threads them together. A `session_id` must be 1–128 characters from
478
+ `A-Z a-z 0-9 . _ -`, not only dots (a UUID works); any other value is a **400**
479
+ naming the allowed characters, on `/query`, `/sessions/{session_id}/bypass` and
480
+ `/sessions/{session_id}/internet`.
481
+ Omitting it is a valid, explicit one-shot: nothing persists past that single
482
+ turn, and the agent is not told otherwise.
483
+
484
+ Internal explicit deletion follows the same idle-only rule as eviction: it returns
485
+ `False` for an absent or busy session and preserves a running agent. Successful
486
+ deletion claims the turn lock before removal and closes outside the registry lock.
487
+
488
+ A skill **captured** in-conversation (the `capture_skill` tool — itself
489
+ confirmation-gated, so over `/query` it needs a session that can answer) loads
490
+ **instruction-only** until a human runs `gaia skill promote <name>` in a
491
+ terminal: `load_skill` injects its body but defers registering any tools it
492
+ declares, and reports the deferral in its result. Integrators must not expect
493
+ a captured skill's `<skill>/<tool>` names to exist before that promote.
283
494
 
284
495
  A retained skill stays *loaded* but its body is not necessarily in the prompt
285
496
  every turn: the agent selects per turn which loaded bodies match the query and
286
497
  collapses the rest to a one-line menu entry (re-activated by calling
287
498
  `load_skill` again). `GAIA_DYNAMIC_SKILLS=0` disables the selection;
288
499
  `GAIA_DYNAMIC_SKILLS_TAU=<float>` overrides its threshold; an embedder outage
289
- disables it for the session and every body renders. Omitting it is a valid, explicit one-shot: nothing
290
- persists past that single turn, and the agent is not told otherwise.
500
+ disables it for the session and every body renders.
291
501
 
292
502
  A retained session also carries a **project map** — up to 600 prompt tokens of
293
503
  directory shape, entry points, installed commands and platform quirks, present
294
504
  whenever the agent's working directory resolves to a code repository (a VCS
295
505
  directory or a recognised manifest at its root). `GAIA_PROJECT_ROOT=<path>`
296
506
  picks the project when the working directory is not it. If that repository has
297
- no code index, the first turn starts one in a background thread;
298
- `GAIA_PROJECT_MAP_AUTO_INDEX=0` disables that. Neither affects the wire
299
- contract — they change what the agent knows and what the first turn costs.
507
+ no code index, the first `search_code_index` builds one;
508
+ `GAIA_PROJECT_MAP_AUTO_INDEX=1` builds it in a background thread at task start
509
+ instead. The same repository check also
510
+ makes `run_shell_command` always-on for the session instead of semantically
511
+ selected. Neither affects the wire contract — they change what the agent
512
+ knows, what tools it is offered, and what the first turn costs.
300
513
 
301
514
  A second `/query` for a `session_id` that already has a turn in flight gets
302
- `409 Conflict` — cancel the running turn or wait for it, then retry. A
515
+ `409 Conflict` — cancel the running turn or wait for it, then retry. Once that
516
+ turn has been cancelled, the new `/query` is held until it finishes its
517
+ in-flight model call (up to **240 s**) and then runs, instead of getting `409`;
518
+ only a cancelled turn that outlasts the wait still gets `409`. A
303
519
  `/query` that needs a **new** session while every retained slot is busy and
304
520
  none is idle enough to evict gets `503` with the reason in `detail` — a
305
521
  temporary, retryable condition, not a bug: wait for a turn to finish (or
@@ -317,10 +533,24 @@ not survive and should be reloaded.
317
533
  A second `/query` reusing a `run_id` that is still in flight gets `409` —
318
534
  `run_id` is caller-minted, so mint a fresh UUID per request; reusing one would
319
535
  leave the earlier run with no way to be cancelled. A `/query` supplying a `model`
320
- that differs from the one its `session_id` was built with also gets `409`: only
321
- agent construction reads a model, so the request cannot be honoured on the
322
- retained agent. Omit `model` to continue on the session's current one, or start a
323
- new `session_id` to switch.
536
+ that differs from the one its `session_id` was built with **switches the retained
537
+ agent in place** (≥ 2.14), so the conversation and any loaded skills survive the
538
+ change; the same machinery the stdio transport's `/model` uses. A switch that
539
+ fails — a missing Claude credential, an unknown local model — is a `409` naming
540
+ the reason, and leaves the session on its previous model. The session records its
541
+ `provider` too: a turn naming a different `provider` switches it the same way,
542
+ onto that turn's `model` or, if it names none, the provider's default model. A
543
+ `model` from the other provider than the one named (a `claude-*` id with
544
+ `"lemonade"`, or a non-Claude id with `"claude"`) is a `400` on a new session and
545
+ an existing one alike, before anything is built or switched.
546
+
547
+ `provider` is optional, and an omitted one is **not** read as `"lemonade"` — it
548
+ means "whatever the `model` implies", so a `claude-*` id alone reaches Anthropic
549
+ and any other id runs locally. The `400` above therefore fires only when a
550
+ request names a `provider` that disagrees with its `model`; there is no pair to
551
+ disagree when only one is given. On a retained session, omitting both leaves the
552
+ session on the provider it is already using, so an ordinary follow-up turn never
553
+ moves a Claude conversation back to Lemonade.
324
554
 
325
555
  ### 5.3 Version gate
326
556
 
@@ -367,6 +597,15 @@ mints a per-session token and passes it as `GAIA_GAIA_SIDECAR_TOKEN_FILE`
367
597
  (`gaia.daemon.sidecars.spec` mirrors both names as plain strings so core never
368
598
  imports this wheel).
369
599
 
600
+ **Child processes.** The programs the agent starts — shell commands, MCP
601
+ servers, CLI installs and sign-ins, native hub agents, media tools, the Lemonade
602
+ server — never inherit the sidecar token or GAIA's other internal credentials
603
+ (the model-broker token, custody secret and engineering pairing token): their
604
+ environment comes from `gaia.env.child_env`, which also drops any name listed
605
+ in `GAIA_CHILD_ENV_DENY` (comma or space separated). User credentials such as `GH_TOKEN` pass through unchanged.
606
+ `GAIA_NO_DOTENV=1` in the sidecar's launch environment stops it loading `.env`
607
+ files; a `.env` file cannot set that switch itself.
608
+
370
609
  ### 5.5 Other transports
371
610
 
372
611
  The HTTP server above is the surface this package drives, and everything in
@@ -378,18 +617,30 @@ the hub, which is what this package delivers, is supervised by the daemon over
378
617
  the HTTP surface above instead (§6.1).
379
618
 
380
619
  It emits the identical canonical event vocabulary, but its input channel accepts
381
- a JSON line carrying a `gaia_control` key, which gives it something HTTP does
382
- not have: a back-channel that can answer a confirmation prompt *while* a turn is
383
- in flight, and stop that turn (`cancel`) without ending the process — so loaded
384
- skills, "always" grants, history and the bypass mode survive a cancel. It also
385
- takes `--bypass-permissions` (start with gating off) and
386
- `--use-claude` / `--claude-model` (route chat to the Anthropic API instead of
387
- local Lemonade; embeddings stay on Lemonade either way). None of that is
388
- reachable over `/v1/gaia/query`.
620
+ a JSON line carrying a `gaia_control` key: a back-channel that answers a
621
+ confirmation prompt *while* a turn is in flight, toggles full access or
622
+ "accept edits" (file edits inside the starting folder run unasked; commands still
623
+ ask), and stops a turn (`cancel`) without ending the process — so loaded skills,
624
+ "always" grants, history and the permission mode survive a cancel. When stdin
625
+ closes mid-turn (the host has gone), the running turn is cancelled and saved in
626
+ the session history as interrupted: "This answer was interrupted when GAIA
627
+ closed." The question is saved when the turn starts, so it survives even if the
628
+ host kills the process before the turn ends. Another process sharing the
629
+ history file leaves a turn that is still running out of its model view.
630
+
631
+ Contract 2.14 gave the HTTP surface the same three capabilities per run and per
632
+ session — `/tool_decision`, `/sessions/{id}/bypass` (the route keeps its
633
+ original spelling), and `provider: "claude"`. What remains stdio-only is the
634
+ *launch* form of those switches: `--full-access` starts a process with gating
635
+ already off, `--accept-edits` starts it in accept-edits mode, and `--use-claude` / `--claude-model` pin the backend for the life
636
+ of the process (embeddings stay on Lemonade either way).
389
637
 
390
638
  The stdio TUI also supports Local, Fireworks AI, and AMD LLM Gateway through
391
- Lemonade. `/model` lists downloaded local and discovered cloud chat models;
639
+ Lemonade. `/model` lists downloaded local and discovered cloud chat models,
640
+ grouped by provider, with `(default)` beside the configured default model;
392
641
  `/model <id>` switches the live client while preserving conversation and skills.
642
+ The switch reply is one line naming the model, where it runs, and, for a cloud
643
+ model, that messages and anything read from files or memory are sent there.
393
644
  Cloud IDs (`fireworks.*`, `amd.*`) route through Lemonade without local model
394
645
  loading. Model-state status events identify the provider and remote inference.
395
646
  The TUI's `/provider` panel configures credentials directly with local Lemonade;
@@ -397,8 +648,82 @@ keys never travel through stdio queries. These controls are not exposed over
397
648
  `/v1/gaia/query`. Lemonade may independently be configured to route a model
398
649
  remotely, so a local server URL alone does not establish local inference.
399
650
 
651
+ `--bypass-permissions` turns off more than the prompt. It also lifts the shell
652
+ tool's own guardrails for the session: the shell-only operators — redirection
653
+ (`>`, `>>`, `<`), backgrounding (`&`), substitution (`` ` ``, `$()`) and the
654
+ newline — parse and run, chaining (`&&`, `||`, `;`, `|`) already ran by
655
+ default, the read-only binary allowlist is replaced by a developer set
656
+ (`node`, `npm`, `make`, `cmake`, `go`, `cargo`, `sed`, `awk`, `curl`, `sleep`,
657
+ `timeout`, `export`, `cp`, `mv`, plus `python`/`python3`/`pytest`/`gh`/`git`,
658
+ which already had their own paths), and the shell rate limit is dropped. `git`
659
+ being in that set means its policy's outright refusals — push, reset, rebase —
660
+ also stop applying under bypass. That is
661
+ arbitrary code execution in the working directory, which is why it exists only
662
+ on this transport: one local parent process on a private pipe. It is **not**
663
+ reachable over HTTP, and the request body cannot ask for it —
664
+ `POST /v1/gaia/sessions/{id}/bypass` stops an HTTP session's approval prompts
665
+ but never lifts these shell gates. A command those gates hold is never run
666
+ unasked there: on a run someone can answer it still emits `needs_confirmation`
667
+ and waits for `/tool_decision`, and on a run nobody can answer it is refused.
668
+ `rm` is excluded
669
+ from the developer set — not a boundary, since anything in the set can delete a
670
+ file, but a tripwire against an accidental recursive delete. Redirection has one
671
+ exception: a command that is nothing but a skill-granted CLI runs argv-only
672
+ rather than through a shell, so a `>` there is refused with an explanation
673
+ instead of reaching the binary as a literal argument.
674
+
675
+ Every shell command run under bypass is recorded with its full arguments and its
676
+ per-segment breakdown in `~/.gaia/cache/file_audit.log` — skipping the prompt
677
+ does not skip the record. Treat that file as sensitive; arguments are verbatim.
678
+ The host can toggle bypass mid-session over `gaia_control`, and the shell gates
679
+ follow on the very next command.
680
+
681
+ `--use-claude` is the one with a reach beyond the machine, and it cannot be
682
+ turned on for what this package delivers: the terminal UI **refuses** the launch
683
+ flag for a daemon-transport agent, with an error saying so, because it pins the
684
+ backend for the life of the process and the daemon relay has no such lever.
685
+
686
+ That is a limit on the *launch flag*, not on the agent. Since contract 2.14 a
687
+ caller can ask for Anthropic per request with `/query`'s `provider` field (§5.2),
688
+ so local inference is this package's **default**, not a property of the transport
689
+ that nobody can change. Anything reaching Anthropic is named by the request that
690
+ asked for it.
691
+
400
692
  ---
401
693
 
694
+ ### 5.6 Adding to a turn already running
695
+
696
+ `POST /v1/gaia/query/{run_id}/followup` with `{ "text": "…" }` hands a live run
697
+ something the user typed after it started. Contract ≥ 2.15.
698
+
699
+ It is not a second turn and not an interrupt. The run keeps going on its
700
+ existing SSE stream; the agent folds the text into that turn's context at its
701
+ next agent-loop step boundary, labelled as arriving mid-task, and answers it
702
+ alongside the work already in progress. A five-minute turn can therefore be
703
+ corrected ("actually, only the unread ones") while it is still running, instead
704
+ of the correction waiting out the turn it was meant to change.
705
+
706
+ Two refusals, both loud, because the caller has already taken the message from
707
+ the user and owes them a truthful answer about where it went:
708
+
709
+ | Status | Meaning |
710
+ | ------ | ------------------------------------------------------------ |
711
+ | `404` | No such run in flight — it finished or was cancelled. Send it as a new `/query`. |
712
+ | `409` | The run's agent is not accepting mid-turn input. |
713
+
714
+ A turn that has already formed its answer takes one more step to address a
715
+ follow-up, so a turn answering in a single step is covered too. The one
716
+ exception is a turn already at its step limit: the message stays queued rather
717
+ than being consumed into a context no model call will read.
718
+
719
+ Because `/query` is stateless (§2.4) the host still owns the transcript: record
720
+ a delivered follow-up in the `context` you push on the **next** turn, between
721
+ that turn's question and its answer, or the conversation loses words the agent
722
+ demonstrably saw.
723
+
724
+ Clients that predate 2.15 get a `404` on the path itself. Probe `/version`
725
+ before sending rather than reading a 404 as "the run ended".
726
+
402
727
  ## 6. Process ownership
403
728
 
404
729
  Two distinct paths, deliberately:
@@ -458,8 +783,8 @@ failed start never leaks a process.
458
783
  | Code | Meaning |
459
784
  | ------- | ----------------------------------------------------------------------- |
460
785
  | `0` | Success |
461
- | `1` | A typed failure: `IntegrityError`, `PlatformError`, `HealthTimeoutError`, `VersionMismatchError`, `BinaryNotFoundError`, `PortInUseError`, `SidecarExitedError`, `MalformedResponseError`, or an unexpected error |
462
- | `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` |
786
+ | `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) |
787
+ | `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` |
463
788
  | *other* | From `run`: the TUI's own exit code, propagated verbatim |
464
789
 
465
790
  A TUI killed by a signal propagates as `128 + signum` (the shell convention), so a
@@ -535,3 +860,24 @@ yourself, which no lock describes, and `{ lock }` to reuse an already-loaded loc
535
860
  `DEBUG=gaia` (or `DEBUG=*`) enables debug output. **Everything goes to stderr** —
536
861
  stdout belongs to the TUI once it is exec'd, and to machine-readable JSON for
537
862
  `fetch` / `version`.
863
+
864
+ ## Developer engineering mode
865
+
866
+ - **Opt-in.** The agent enables it with `--developer-mode` or `GAIA_DEVELOPER_MODE=1`
867
+ in the host process. `gaia engineering` and the engineering MCP server accept only
868
+ the explicit `--developer-mode` flag. It is separate from diagnostic `--dev`.
869
+ - **Surface.** Only in that mode does the agent load the `gaia-harness-engineering`
870
+ skill and the `share_engineering_context`, `append_engineering_context`,
871
+ `approve_engineering_code`, `engineering_status`, `open_engineering_app` and
872
+ `revoke_engineering_context` tools. Sessions without it have none of them.
873
+ - **Consent.** Sharing, appending and code approval each ask for a fresh approval of
874
+ the exact content or scope shown. None of them can be remembered or auto-approved.
875
+ Snapshots are recipient-bound and expire; revoking stops future reads.
876
+ - **Pairing.** Configure the coding app's MCP connection from a Python GAIA install
877
+ with the `[mcp]` extra. The frozen binary this package ships can reuse that
878
+ configuration but cannot launch the Python MCP server itself.
879
+ - **Code.** Fixes happen in the coding app, in worktrees from GAIA's managed source
880
+ cache. Preview results are reported by that app, not verified by GAIA. Nothing
881
+ self-assesses or updates the installed GAIA.
882
+
883
+ See the [usage guide](https://amd-gaia.ai/docs/guides/harness-engineering).
@@ -1,76 +1,76 @@
1
1
  {
2
2
  "schemaVersion": "3.0",
3
- "agentVersion": "0.2.0",
3
+ "agentVersion": "0.3.2",
4
4
  "components": {
5
5
  "sidecar": {
6
- "componentVersion": "0.2.0",
7
- "baseUrl": "https://hub.amd-gaia.ai/agents/gaia/0.2.0",
6
+ "componentVersion": "0.3.2",
7
+ "baseUrl": "https://hub.amd-gaia.ai/agents/gaia/0.3.2",
8
8
  "platforms": {
9
9
  "win32-x64": {
10
10
  "filename": "gaia-agent-win32-x64.exe",
11
11
  "executable": "gaia-agent.exe",
12
- "sha256": "5d2ea1a5fd40e2399ba2bf50aec80b44d89c7e7bfdbad846c050f683fc8bf842",
13
- "size": 103531825
12
+ "sha256": "b107ed0740408b3ea50ab2556503549f173bcea13756e0cab0a5d0c98c867eab",
13
+ "size": 172894251
14
14
  },
15
15
  "darwin-arm64": {
16
16
  "filename": "gaia-agent-darwin-arm64",
17
17
  "executable": "gaia-agent",
18
- "sha256": "fbe526e7eb044a2ce633fbd487a1ae75001683eff190d8359eabf9ccc7196069",
19
- "size": 88311472
18
+ "sha256": "2a339e225396df8f1623ff32051dd92800bf2224c76ba482cd25ae7ba30ebe30",
19
+ "size": 162463472
20
20
  },
21
21
  "darwin-x64": {
22
22
  "filename": "gaia-agent-darwin-x64",
23
23
  "executable": "gaia-agent",
24
- "sha256": "b02731c786d79d7f5e644662238a64d1761eb6a23d421821df46e6a8282b585d",
25
- "size": 94294544
24
+ "sha256": "a796c734a72f43489062faa7d011399bcfdbd97280992e0ff8fea9734702aa28",
25
+ "size": 173074704
26
26
  },
27
27
  "linux-x64": {
28
28
  "filename": "gaia-agent-linux-x64",
29
29
  "executable": "gaia-agent",
30
- "sha256": "6c68a22c25954ea3c8d2a0a541bc04eba2401281d2a2b6448007983acd050a00",
31
- "size": 140015368
30
+ "sha256": "815817da63df38ae08dc96df226b758f53c54e56b59588b79a5e605c27c2ab64",
31
+ "size": 211939056
32
32
  }
33
33
  }
34
34
  },
35
35
  "tui": {
36
- "componentVersion": "0.24.1",
37
- "baseUrl": "https://hub.amd-gaia.ai/agents/terminal-hub/0.24.1",
36
+ "componentVersion": "0.25.0",
37
+ "baseUrl": "https://hub.amd-gaia.ai/agents/terminal-hub/0.25.0",
38
38
  "platforms": {
39
39
  "win32-x64": {
40
40
  "filename": "gaia-win-x64.exe",
41
41
  "executable": "gaia-tui.exe",
42
- "sha256": "ce07269674de6773fd304d67eef3ff19cd578f447fc90a13f0309a61ae5ef504",
43
- "size": 19213824
42
+ "sha256": "9b1c467b04b267f172a6024589f1ba0ddc882729337913d8afa7dcd43a7a7967",
43
+ "size": 20604416
44
44
  },
45
45
  "win32-arm64": {
46
46
  "filename": "gaia-win-arm64.exe",
47
47
  "executable": "gaia-tui.exe",
48
- "sha256": "41f2a25b2bf15bea6f70626bb519496aeb822cc2629dd3529e325319b7abb3ca",
49
- "size": 17744384
48
+ "sha256": "332068c5588e5639213225d63e4a1827d9258928e4053b7d6602668dc7eda60c",
49
+ "size": 19030016
50
50
  },
51
51
  "darwin-arm64": {
52
52
  "filename": "gaia-darwin-arm64",
53
53
  "executable": "gaia-tui",
54
- "sha256": "dc334e89cebefaa2cc5117942ccbc4dc391034c3013ebf02d2bebf87f5e99a5a",
55
- "size": 17499282
54
+ "sha256": "bb5ae28ae99ad25c095b3f57c4abf4e2ff67fa560122dc0a99036aedf9184802",
55
+ "size": 18801330
56
56
  },
57
57
  "darwin-x64": {
58
58
  "filename": "gaia-darwin-x64",
59
59
  "executable": "gaia-tui",
60
- "sha256": "9af8c1fefeff4814bae6bbe9698ffaac3b59f248a422525e9f9b50812f5435da",
61
- "size": 18629664
60
+ "sha256": "5259c82dc195297130e16377586ed6e0e668c5ec7f8b27a76ee029d67d776b52",
61
+ "size": 20015840
62
62
  },
63
63
  "linux-x64": {
64
64
  "filename": "gaia-linux-x64",
65
65
  "executable": "gaia-tui",
66
- "sha256": "ef80198f3c4f6c2a4e92993fe19257c67cf009e29632398642549274ef8c2ffa",
67
- "size": 18292898
66
+ "sha256": "07f1cc448f8056b5915eff2869f9f3eec3a5d04485d93e448ccad00d186d23c5",
67
+ "size": 19669154
68
68
  },
69
69
  "linux-arm64": {
70
70
  "filename": "gaia-linux-arm64",
71
71
  "executable": "gaia-tui",
72
- "sha256": "31d9e1fee15e4a21e677ca7abb707fcf08257d5e7f50f1baa71bc7159335c170",
73
- "size": 17105058
72
+ "sha256": "c324100d2de989823e816a6aae7eb4e1224019096b9a46ef1a6983ca6b98598f",
73
+ "size": 18350242
74
74
  }
75
75
  }
76
76
  }
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;AAuKD,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"}