Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

feat: ChatGPT OAuth login and live agent session#9

Merged
Natfii merged 58 commits intomainfrom
feature/openai-oauth
Feb 28, 2026
Merged

feat: ChatGPT OAuth login and live agent session#9
Natfii merged 58 commits intomainfrom
feature/openai-oauth

Conversation

@Natfii
Copy link
Copy Markdown
Owner

@Natfii Natfii commented Feb 28, 2026

Summary

  • ChatGPT OAuth Login: Users can now log in with their ChatGPT account across all entry points (onboarding, connections, settings). Uses PKCE OAuth flow with loopback redirect server, automatic token refresh, and foreground service hold to prevent Android process freezing during 2FA.
  • Live Agent Session: The terminal REPL now acts as a personal chat interface with your ZeroClaw agent. Messages go through a full agent loop with persistent conversation history, streaming responses, tool execution with real-time transparency, auto-compaction, and memory context injection. Slash commands (/status, /cost, etc.) remain instant via Rhai.
  • ThinkingCard with Tool Activity: During agent turns, the UI shows a merged thinking + tool activity card with reasoning tokens, active tools (hourglass), and completed tools (check/cross with duration).
  • User Identity in System Prompt: The user's name, timezone, and communication style from onboarding are now injected into the agent's system prompt so the agent knows who it's talking to.

Test plan

  • 178 Rust tests passing (175 existing + 3 new identity extras tests)
  • Clippy clean, rustfmt clean
  • spotlessCheck + detekt clean
  • Gradle assembleDebug BUILD SUCCESSFUL
  • Manual testing on physical device (ChatGPT OAuth flow, agent chat, tool stripping)

🤖 Generated with Claude Code

Natfii and others added 30 commits February 27, 2026 16:34
Rhai-powered REPL with terminal aesthetic, slash commands mapped
to all 33 FFI functions, markdown rendering, and full accessibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers Rhai engine setup, function registration verified against
source, Kotlin terminal UI, slash commands, Room migration, state
refresh after mutations, and Console deprecation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add rhai 1.21 with sync feature to Cargo.toml. Derive serde::Serialize
on all 9 FFI record/enum types (FfiCostSummary, FfiBudgetStatus,
FfiComponentHealth, FfiHealthDetail, FfiCronJob, FfiSkill, FfiSkillTool,
FfiToolSpec, FfiMemoryEntry) so they can be serialized to JSON for the
terminal REPL output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bundle JetBrains Mono v2.304 Regular and Bold weights as Android font
resources and define TerminalTypography that overrides body, label, and
title styles with the monospace font family while preserving sp-based
sizes for Android 14+ nonlinear font scaling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rsistence

Add the Room persistence layer for terminal REPL history:
- TerminalEntry domain model with content, entryType, timestamp, imageUris
- TerminalEntryEntity Room entity (terminal_entries table)
- TerminalEntryDao with observeAll, insert, and deleteAll operations
- TerminalEntryRepository interface and RoomTerminalEntryRepository impl
- Entity mapper extensions in EntityMappers.kt
- Database migration 7->8 creating the terminal_entries table
- Wired into ZeroClawApplication for manual DI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r composables

- TerminalBlock: sealed interface with Input, Response, Structured,
  Error, and System variants for the REPL scrollback buffer
- BrailleSpinner: animated braille dot spinner matching Claude Code CLI
  with power-save fallback to static ellipsis
- TerminalOutputRenderer: composable functions that render each block
  variant with JSON pattern detection, accessibility semantics, and
  long-press copy support
- Fix detekt CognitiveComplexMethod suppression in CommandRegistry
- Apply spotless formatting to Type.kt and ZeroClawApplication.kt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and history

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…om bar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…inal REPL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ting violations

Rename internal MutableStateFlow fields that lack matching public
properties to drop the underscore prefix (loadingState,
pendingImagesState, processingImagesState). Apply spotless auto-format
to import ordering and expression wrapping across 4 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Defence-in-depth: set max operations (100K), expression depth (32/16),
string size (64KB), array size (1024), map size (256), and call stack
depth (16). Two new tests verify infinite loops and deep recursion are
terminated by the sandbox.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ommands

When the terminal REPL executes a mutating command (cron add, skill
install, send, etc.), emit a RefreshCommand through a SharedFlow on
ZeroClawApplication so DaemonViewModel immediately re-fetches the
affected data instead of waiting for the 15-30 second poll cycle.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete ChatMessage model, entity, DAO, and repository (5 files).
Add Room migration v8→v9 to drop the chat_messages table.
Replace the daemon restart handler's chatMessageRepository.clear()
with terminalEntryRepository.clear().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded /c/Users/Natal paths with $HOME-relative or
env-var-with-fallback patterns in scripts. Gitignore .cargo/config.toml
(contains machine-specific NDK linker paths) and add a .template.
Delete terminal REPL plan/design docs (implementation complete).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Modern OpenAI keys use sk-proj-... prefix (up to 156 chars). The
startsWith("sk-") validation already accepts them correctly, but
the hint text was stale. Updated to "sk- (usually sk-proj-...)".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ProviderAuthType.API_KEY_OR_OAUTH enum value and oauthClientId
field to ProviderInfo for providers that support OAuth login alongside
API keys. Update OpenAI entry to use the new auth type with client ID
app_EMoamEEZ73f0CkXaXp7hrann, and refresh its suggested models to the
current GPT-5/o-series lineup (gpt-5.2, gpt-5-mini, gpt-5-nano, o3,
o4-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano). Also downgrade
androidx.browser from 1.9.0 to 1.8.0 for compileSdk 35 compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add the core PKCE OAuth logic for the OpenAI authorization code flow,
mirroring the upstream Rust implementation. Includes PKCE state generation,
authorize URL construction, and token exchange via HttpURLConnection.
Uses java.util.Base64 (not android.util.Base64) for JVM test compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements a lightweight HTTP server on localhost:1455 (with fallback to
1456) that captures the OAuth redirect callback from the browser. The
server extracts the authorization code and state from query params,
serves a success HTML page, and exposes the result via a coroutine
CompletableDeferred. Includes two unit tests: callback extraction and
timeout behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add provider parameter to refresh() with Anthropic default, provider-specific
refresh URLs (Anthropic/OpenAI), OpenAI client_id in request body, and
fallback when OpenAI omits refresh_token in response. Update ApiKeyRepository
call site to pass key.provider. Add unit tests for refreshUrlForProvider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…inator

Wire the PKCE OAuth flow into the onboarding wizard. Add isOAuthInProgress,
oauthEmail, oauthRefreshToken, and oauthExpiresAt fields to ProviderStepState.
Implement startOAuthLogin() and disconnectOAuth() methods in the coordinator,
and pass OAuth tokens through to ApiKey on completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add OAuth UI to ProviderSetupFlow: a "Login with ChatGPT" button with
progress indicator, "or use API key" divider, and OAuthConnectedChip
for the connected state. Wire the OAuth state and callbacks through
ProviderStep into the onboarding ProviderStepCollector.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add startOAuthLogin() to ApiKeysViewModel with full PKCE flow using
OAuthCallbackServer and OpenAiOAuthManager. In ApiKeysScreen, show
"ChatGPT Login" label for OAuth tokens instead of masked key text and
add a "Login with ChatGPT" button. Bump detekt TooManyFunctions
threshold to accommodate the new method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hide openai-codex as internal provider (not user-selectable), fix
port-aware redirect URIs for token exchange, clean up stale openai
keys on successful OAuth, migrate agents to openai-codex provider,
remove standalone OAuth button from API keys list, and add
AuthProfileWriter for auth-profiles.json persistence. All three
entry points (onboarding, connections, settings) now share identical
OAuth flow via ProviderSetupFlow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix reliability.api_keys type mismatch: emit flat Vec<String> array
  instead of dotted TOML table keys
- Align default values to upstream schema.rs: gateway port (42617),
  max_actions_per_hour (100), max_cost_per_day_cents (1000),
  provider_backoff_ms (500), max_cpu_time_seconds (60),
  max_subprocesses (10)
- Add coerceAtLeast(0) on all 24 integer fields that map to unsigned
  Rust types (u16/u32/u64/usize) to prevent TOML parse errors like
  "archive_after_days = -1"
- Extract appendReliabilityApiKeys helper to fix detekt complexity
- Clamp memory hygiene inputs in MemoryAdvancedScreen UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Natfii and others added 13 commits February 28, 2026 10:59
Add SetupRoute data object for the post-onboarding daemon setup screen
and a configuredChannelNames() suspend bridge method wrapping the new
get_configured_channel_names FFI function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all daemonBridge.markRestartRequired() calls with
triggerHotReload() which shows a SetupBottomSheet progress overlay
when the daemon is running. Falls back to markRestartRequired() when
the daemon is stopped. Adds config building helpers that mirror
SetupViewModel/ZeroClawDaemonService patterns for constructing the
TOML before calling SetupOrchestrator.runHotReload().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix detekt LongMethod: extract pollChannelHealth() and
  markChannelsTimedOut() from stepAwaitChannels in SetupOrchestrator
- Fix detekt TooManyFunctions: bump class threshold from 23 to 24
- Fix detekt LargeClass: suppress pre-existing violation on ApiKeysViewModel
- Fix detekt UnusedParameter: remove unused expectedChannels param
- Fix spotless: reformat splitCsv single-expression function
- Fix build: add futures-util dependency for streaming module
- Fix build: add ACTION_OAUTH_HOLD constant and handleOAuthHold() to
  ZeroClawDaemonService (was referenced but never committed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SetupState.kt (SetupStepStatus/SetupProgress) and SetupViewModel.kt
(secure ByteArray key handling with zero-fill) that were missing from
earlier commits. Also harden security posture:

- Sanitize streaming errors through LogSanitizer before Log.e
- Mark terminal clipboard copies as IS_SENSITIVE (Android 13+)
- Gate OAuth debug logs behind BuildConfig.DEBUG

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The onboarding OAuth flow was missing the Android 12+ process-freezing
mitigation that the settings flow already had. When the user switched to
a 2FA app during OAuth, Android's cached-app freezer would freeze the
process and the NanoHTTPD callback server, causing the redirect to hang
indefinitely.

Add holdForegroundForOAuth(), releaseOAuthHold(), and
bringAppToForeground() mirroring the pattern in ApiKeysViewModel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aemon

Two bugs fixed:

1. Fresh install skipped SetupScreen: markComplete() in completeInternal()
   triggered AppShell recomposition before navigation to SetupRoute fired,
   causing the NavHost to reinitialize with DashboardRoute as start
   destination. Fix: move markComplete() to SetupViewModel.startSetup()
   so onboarding is only marked complete after the setup pipeline runs.

2. Re-run wizard failed with "daemon already running": runFullSetup()
   called daemonBridge.start() unconditionally. Fix: add
   stopDaemonIfRunning() check before stepStartDaemon() that gracefully
   stops any existing daemon instance first.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix extractFromIdentity() to navigate nested AIEOS JSON structure
  instead of flat root-level lookup (agent name under identity.names.first,
  other keys under identity object)
- Fix identity key constants to match actual JSON keys (user_name,
  communication_style)
- Move markComplete() to after setup pipeline completes
- Replace broken OpenAI CDN icon URLs with Google Favicon API
- Add ChatGPT favicon to OAuth login button in ProviderSetupFlow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Intercept chat messages in TerminalViewModel when no provider is
configured, showing a helpful system message instead of a cryptic
auth error. Admin/scripting commands remain unaffected. Welcome
banner now indicates "Admin Console" mode when unconfigured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Defines the architecture for turning the blocking REPL into a live
agent session with streaming responses, tool execution transparency,
auto-compaction, and session seeding from Room persistence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Natfii Natfii changed the title Feature/ Built in REPL with live agent chatting and terminal access, no more extranal chat bots required & OpenAI oauth Feature/ Built in REPL with live agent chatting and terminal access, no more external chat bots required & OpenAI oauth Feb 28, 2026
Natfii and others added 12 commits February 28, 2026 13:25
13-task implementation plan covering Rust FFI session module,
on_delta parser, Kotlin streaming state, ThinkingCard tool
activity, and dual-path dispatch in TerminalViewModel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add tokio-util 0.7 with the rt feature to support CancellationToken
for cooperative cancellation in the upcoming session send loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce the session module with:
- Session struct and global singleton for single-session lifecycle
- SessionMessage UniFFI record for FFI-safe conversation messages
- FfiSessionListener callback interface with 10 event methods
- dispatch_delta() parser that converts upstream emoji-prefixed
  progress strings into typed listener callbacks
- parse_tool_completion() helper for extracting tool name and duration
- 11 unit tests covering all delta dispatch paths and edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add clone_daemon_config() and clone_daemon_memory() runtime helpers,
and implement session_start_inner() which mirrors upstream agent::run()
setup: resolves provider/model/temperature from daemon config, builds
Android-appropriate tool descriptions, queries native tool support via
a temporary provider, constructs the full system prompt via
build_system_prompt_with_mode, and stores the Session singleton.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add the core agent loop (session_send_inner) with provider chat integration,
memory context building, history compaction, and cooperative cancellation via
CancellationToken. Implement all lifecycle functions: session_seed_inner,
session_cancel_inner, session_clear_inner, session_history_inner, and
session_destroy_inner. Tool specs are passed for provider awareness but
execution is deferred (SecurityPolicy is pub(crate) upstream). All 175 tests
pass, clippy clean with -D warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SecurityPolicy is pub(crate) in upstream zeroclaw, blocking
all_tools_with_runtime() from the FFI crate. This commit adds
FFI-specific tool wrappers for MemoryStoreTool and MemoryForgetTool
that bypass SecurityPolicy (unnecessary on Android where user
initiates all actions). Uses upstream MemoryRecallTool, CronListTool,
CronRunsTool, and WebSearchTool directly (no SecurityPolicy needed).

The agent loop now executes real tools instead of returning
"Tool execution is not available" stubs:
- memory_store, memory_recall, memory_forget (full CRUD)
- cron_list, cron_runs (scheduling visibility)
- web_search (when configured)
- Unknown tools get a graceful "not available" fallback

Also adds async-trait dependency for Tool trait implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exports session_start, session_seed, session_send, session_cancel,
session_clear, session_history, and session_destroy through UniFFI
for Kotlin binding generation. All wrapped in catch_unwind with
FfiError return types following the existing pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds TOOL_EXECUTING and COMPACTING phases to StreamingPhase enum.
Introduces ToolProgress and ToolResultEntry data classes for tracking
in-flight and completed tool executions.

ThinkingCard now shows a tool activity footer below thinking text:
active tools with hourglass indicators, completed tools with
success/failure icons and duration. Follows Material 3, accessibility,
and battery-conscious rendering conventions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add KotlinSessionListener implementing FfiSessionListener with
MutableStateFlow.update{} for thread-safe streaming state transitions.
Dual-path dispatch: slash commands route to Rhai, chat messages to
agent session via sessionSend(). Wire TerminalScreen to display
ThinkingCard during agent turns, StreamingResponseBlock for progressive
response rendering, and disable input during active streaming.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update TOOL_CALL_TAG_REGEX to match self-closing format
(<tool_call name="..." args="..."/>) leaked by some models.
Add stripToolCallTags to KotlinSessionListener.onComplete so
agent session responses get the same tag stripping as Rhai responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upstream AIEOS renderer only outputs agent identity fields (name, bio,
personality). Android onboarding also stores user_name, timezone, and
communication_style in the identity JSON, but these are silently
dropped by serde since the upstream IdentitySection struct lacks them.

Add append_android_identity_extras() to parse the raw aieos_inline
JSON and append a "## User Context" section with the user's name,
timezone, and preferred communication style. Includes 3 unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Natfii Natfii changed the title Feature/ Built in REPL with live agent chatting and terminal access, no more external chat bots required & OpenAI oauth feat: ChatGPT OAuth login and live agent session Feb 28, 2026
# Conflicts:
#	scripts/lint-all.sh
#	scripts/release.sh
#	scripts/test-local-all.sh
@Natfii Natfii merged commit a3baed2 into main Feb 28, 2026
2 of 4 checks passed
@Natfii Natfii deleted the feature/openai-oauth branch March 1, 2026 07:22
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant