Skip to content

fix(ui): stop silently accepting unimplemented agent_mode 'autonomous'#2257

Merged
kovtcharov merged 1 commit into
mainfrom
fix/agent-mode-honest-2005
Jul 18, 2026
Merged

fix(ui): stop silently accepting unimplemented agent_mode 'autonomous'#2257
kovtcharov merged 1 commit into
mainfrom
fix/agent-mode-honest-2005

Conversation

@kovtcharov

Copy link
Copy Markdown
Collaborator

Fixes #2005 (also covers the duplicate medium finding flagged in #2103).

Why this matters

Before: the documented default agent_mode="autonomous" was a no-op label — the loop's tick handler has no observation cycle, so it behaved byte-identically to goal_driven while the spec and API docs promised "observe, infer, and execute own goals". After: the default is honestly goal_driven, selecting autonomous fails loudly with a 400 that says exactly why and what to use instead, and legacy DBs that already stored autonomous keep working (normalized to goal_driven with a once-per-process warning). Zero behavior change for any existing user — only the labels stop lying.

Scoped fix, not the full feature. The real observation cycle (spec §6.7) is a large open-ended build: six sensors (email/git/error-log/conversation/memory/system-state), a lightweight-model inference step, confidence-gated auto-approval against FAISS permission grants. That needs design decisions (sensor plumbing through connectors, auto-approval safety semantics, observe-model provisioning), so this PR makes the mode selection honest rather than shipping a half-baked cycle. The spec doc now carries an implementation-status note.

Follow-up needed for the full autonomous mode

  • Implement the §6.7 observation cycle behind the goal-check in AgentLoop._run_step (sensors → lightweight-model inference → GoalStore entries with source="agent_inferred")
  • Decide auto-approval semantics (confidence ≥ 0.8 + safe_to_auto_execute vs. FAISS permission grants)
  • Re-admit "autonomous" to _VALID_AGENT_MODES, flip the default back, and drop the legacy normalization in resolve_agent_mode()

Test plan

  • pytest tests/unit/chat/ui/test_server.py tests/unit/chat/ui/test_agent_loop.py tests/unit/test_goal_store.py — 182 passed
  • New API tests: default is goal_driven; PUT autonomous → 400 with actionable detail (nothing persisted); PUT manual/goal_driven round-trip; legacy stored autonomous reported as goal_driven on GET
  • New loop tests: _run_step with legacy autonomous returns a directive identical to goal_driven (proves no phantom observation cycle); manual pauses; normalization warns exactly once
  • python util/lint.py --all green

@github-actions github-actions Bot added documentation Documentation changes tests Test changes labels Jul 18, 2026
@kovtcharov
kovtcharov enabled auto-merge July 18, 2026 19:30
@github-actions

Copy link
Copy Markdown
Contributor

Verdict: Approve

This makes the autonomous agent mode honest: since its observation cycle was never built, the mode behaved byte-identically to goal_driven while the docs promised more. Now the default is goal_driven, selecting autonomous fails loudly with an actionable 400, and legacy databases that already stored autonomous keep working (normalized to goal_driven with a one-time warning). No behavior change for existing users — only the labels stop lying.

The fix is fail-loudly-compliant, the docs are updated in lockstep (spec + API model + endpoint docstrings), and the tests are thorough — including an equivalence test proving there's no phantom observation cycle. One minor consistency note below; nothing blocking.

🔍 Technical details

🟢 Minor

Frontend AgentMode type still lists 'autonomous' (src/gaia/apps/webui/src/services/memoryApi.ts:295)

The TS type is now wider than the API accepts. No current selector sends agent_mode, so nothing regresses today — but if a Settings dropdown is ever wired from this union it would emit a value the backend 400s. Worth trimming to match the server contract:

export type AgentMode = 'manual' | 'goal_driven';

Non-blocking — only flag it so the type and the API don't drift.

Strengths

  • Fail-loudly done right. Rejects the unshipped mode with a 400 that names what failed, what to use instead, and where to look (§6.7, Default agent_mode 'autonomous' never runs its observation cycle — it behaves identically to goal_driven #2005), and the legacy-normalization warning fires once per process rather than silently swapping behavior — exactly the CLAUDE.md "no silent fallbacks" pattern.
  • Docs updated together. Spec note, SettingsResponse/SettingsUpdateRequest descriptions, and both endpoint docstrings all move in one change — no self-contradicting surface left behind.
  • Tests earn their keep. test_legacy_autonomous_identical_to_goal_driven compares the full directive tuple to prove the removed autonomous branch was a no-op, and the API tests assert nothing is persisted on the rejected write — both catch the exact class of regression this PR is guarding against.

#2005)

The default agent_mode 'autonomous' never ran its defining observation
cycle — the tick handler idles when no goals exist, so the mode behaved
byte-identically to 'goal_driven' while docs promised 'observe, infer,
and execute own goals'. Users selecting the default got a no-op label.

Until the observation cycle (docs/spec/autonomous-agent-mode.md §6.7)
ships, be honest about what runs:

- default agent_mode is now 'goal_driven' (identical behavior, truthful
  name)
- PUT /api/settings rejects 'autonomous' with an actionable 400 pointing
  at 'goal_driven' and issue #2005 instead of silently accepting it
- legacy DBs with 'autonomous' stored are normalized to 'goal_driven'
  via resolve_agent_mode() with a once-per-process warning, and GET
  /api/settings reports the effective mode
- spec doc gains an implementation-status note so it no longer claims
  the mode is shipped and default

Tests pin the settings API contract and prove the loop's legacy
'autonomous' path is identical to 'goal_driven' (no phantom observation
cycle).
@kovtcharov
kovtcharov force-pushed the fix/agent-mode-honest-2005 branch from adff75e to aa2f81d Compare July 18, 2026 20:06
@kovtcharov
kovtcharov added this pull request to the merge queue Jul 18, 2026
Merged via the queue into main with commit f8f309e Jul 18, 2026
37 checks passed
@kovtcharov
kovtcharov deleted the fix/agent-mode-honest-2005 branch July 18, 2026 20:11
@itomek itomek mentioned this pull request Jul 22, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Documentation changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default agent_mode 'autonomous' never runs its observation cycle — it behaves identically to goal_driven

2 participants