is_npm_package_installed() was running 'npx --yes camoufox-browser@0.1.0 --version'
which actually LOADS the package code. If we've removed the bundled
camoufox-js@0.9.3, this fails with 'Cannot open database' error, making
the check return false and triggering a re-install.
Fix: check for daemon.js on disk via npm root -g instead.
Also: install_npm_package() now removes the bundled camoufox-js after
install so the daemon always resolves to the global v0.11.0.
Bug 1 — writer.shutdown() kills Node socket before response:
Rust called shutdown() on the write half immediately after writing
the command. This sent EOF to the Node.js daemon, whose safeWrite()
checks socket.destroyed and silently bails. Fixed by dropping writer
instead.
Bug 2 — daemon has zero macro support:
The daemon's execute() never handles macro_name/query. Only url
matters for the navigate action. Fixed search() to resolve all 12
macros (@google_search, @youtube_search, etc.) to actual search URLs
in Rust before sending navigate with cmd.url set.
install_binary() now:
1. Automatically picks best location per platform:
- macOS: ~/.cargo/bin/ (no sudo, in PATH for Rust users)
- Linux: ~/.cargo/bin/ → /usr/local/bin → ~/.local/bin/
- Windows: /usr/local/bin (Git Bash/MSYS2)
2. Detects if old binary exists at a different PATH location
and warns the user: 'There is ANOTHER gyandoot at ...'
3. Verifies 'command -v gyandoot' resolves correctly after install
4. Shows shell profile instructions if PATH needs updating
Previous issue: always installed to /usr/local/bin with sudo,
overwriting the old binary from install.sh but not warning if
another copy existed elsewhere in PATH.
Root cause: camoufox-browser@0.1.0's CLI wrapper spawns the daemon
with stdio: 'ignore' and only waits 5 seconds. If the daemon crashes
(silent better-sqlite3 native binding error, etc.), the error is
swallowed and we see 'Failed to connect: No such file or directory'.
Fix:
1. find_daemon_js() — locates daemon.js in global node_modules via
'npm root -g' with fallbacks for common nvm/homebrew paths
2. trigger_daemon_start() — spawns daemon.js directly with 'node',
captures stderr to ~/.gyandoot/camoufox-daemon-stderr.log
3. Uses spawn() + std::mem::forget (background daemon) instead of
.output() (which would wait forever since daemon never exits)
4. kill_on_drop(false) — prevents killing daemon when child drops
5. Poll loop in wait_for_socket() still works as before (30s timeout)
Root cause: camoufox-js@0.11.0 fetch can exit 0 but silently fail
to download the browser binary when:
1. A partial/incomplete cache from a previous run exists
2. GitHub API rate-limits the unauthenticated release query
3. Network drops mid-download
Fix:
1. run_fetch() now DELETE incomplete cache (dir exists but no
version.json) before fetching — prevents 'already cached' no-op
2. After fetch, VERIFY version.json exists — if missing, RETRY once
3. After retry, verify Camoufox.app/Contents/MacOS/camoufox exists
4. Log stdout and stderr in INFO traces for debugging
Root cause: On Apple Silicon macOS, the Camoufox browser binary
inherits com.apple.quarantine from npm download. When camoufox-browser
launches it headlessly, Gatekeeper silently kills the process with
SIGKILL (exitCode=null, signal=SIGKILL) before it can run.
Fix:
1. remove_macos_quarantine() — runs 'xattr -dr com.apple.quarantine'
on the Camoufox cache directory after every fetch and on every
subsequent startup (in case cache was from a prior npx run)
2. check_known_errors() — detects 'sigkill' in daemon stderr and
shows the exact fix command to the user
3. Runs quarantine removal in TWO places:
- Immediately after camoufox-js fetch downloads the binary
- Always, even when binary already exists in cache
Root cause on macOS: camoufox-browser@0.1.0 downloads an alpha binary
(v150.0.2-alpha.26) from daijro/camoufox that has a broken macOS build
(missing libmozglue.dylib). Binary crashes with SIGABRT/SIGKILL.
Changes:
1. gyandoot-browser client now supports TWO transports:
- Transport::UnixSocket (existing — daemon IPC)
- Transport::Http (new — Docker REST API on port 9377)
2. init_browser() tries Docker HTTP first, falls back to npx daemon
3. HTTP transport maps browser actions to Docker REST endpoints
(/navigate, /tabs, /health, etc.) with correct userId params
4. check_known_errors() detects libmozglue.dylib + SIGKILL patterns
5. Browser cache check works for both macOS and Linux paths
6. Error messages correctly say 'Use Docker' on corrupted binary
7. Added reqwest dep to gyandoot-agent for Docker health check
Clean build: 0 errors
Root cause on macOS: 'camoufox-browser install' downloaded v150.0.2-alpha.26
instead of the stable browser binary. This alpha version is missing
libmozglue.dylib, causing dyld to crash immediately.
Changes:
1. detect_os() — returns Mac/Linux/Wsl/Windows enum for platform-aware logic
2. check_known_errors() — parses daemon stderr for known failures (missing
libmozglue.dylib on macOS, missing libgtk/libnss on Linux, code-signing)
3. platform_fix_instructions() — returns OS-specific recovery steps:
- macOS: rm -rf ~/Library/Caches/camoufox, reinstall, or Docker fallback
- WSL/Linux: correct apt-get install commands (with t64 package variants)
4. Browser cache detection now checks Camoufox.app on macOS, camoufox on Linux
5. All error messages use detect_os() — no more Linux apt-get on macOS
6. Corrupted download detected automatically, user told to clear cache + retry
Clean build: 0 errors, 0 warnings
- Add is_wsl() detection via /proc/version
- Add example_npx_path() returning platform-appropriate example path
- Replace all hardcoded macOS paths in error messages with dynamic ones
- Add WSL-specific hints: Node.js must be installed inside WSL, not Windows
- WSL hint includes nvm install instructions
- Clean build: 0 errors, 0 warnings
trigger_daemon_start now captures stdout/stderr and exit code instead
of silently redirecting to a log file. On failure it prints the actual
error, making it obvious what went wrong (npm install issue, node
version mismatch, etc.).
The setup wizard now auto-detects the npx/npx.cmd path via
`which npx` (Unix) or `where npx.cmd` (Windows) and writes it
to [browser].node_path in config.toml.
Changes:
- detect_npx_path(): cross-platform detection using which/where
- SerializedConfig now includes optional [browser] section
- Flat config parser (main.rs) handles [browser] section
- browser: BrowserSection threaded through AgentConfig everywhere
- init_browser() accepts node_path parameter with proper fallback
Adds [browser].node_path config option so camoufox-browser can use
an explicit npx path (essential for nvm/fnm/asdf users).
Resolution order (cross-platform):
1. config.browser.node_path — explicit user override
2. 'npx.cmd' on Windows, 'npx' on Linux/macOS — PATH lookup
Changes:
- BrowserSection config: added node_path: Option<String>
- AgentConfig: now carries browser section (was dropped in From<GyandootConfig>)
- browser_manager::init_browser(node_path): accepts optional path
- All call sites updated (run(), init_session(), subagents, tests)
- Error messages now hint at browser.node_path config
Two fixes for the TUI:
1. **Raw LLM responses** — was stateless, creating
fresh messages every turn. Added +
methods that accumulate conversation history, inject identity, load
past memory, run browser init, and compress context — identical to
the interactive loop but designed for external callers.
2. **Banner flash** — removed call that fired to stdout
*before* TUI entered alternate screen. TUI already shows the robot
head via as initial chat content.
Also added getter and field to AgentLoop.
Two bugs fixed:
1. Banner not shown: TUI default output was a plain welcome text
instead of the actual GyanDoot banner. Fixed by using
banner::banner_plain() as initial TUI output content.
2. No agent responses: TUI was a standalone shell with no connection
to the agent loop. Now uses mpsc channels:
- user_tx: TUI → agent (user sends messages)
- event_rx: agent → TUI (agent sends responses + telemetry)
- TuiEvent::Response / Status / Telemetry enum for structured
communication
- tokio::spawn runs agent in background processing messages
- poll_events() in render loop drains channel every frame tick
Also updated cmd_tui() in main to create channels + spawn agent.
Complete visual redesign of the TUI with a cyberpunk/holographic aesthetic:
Layout:
- Top header: GyanDoot logo (neon robot head) + 6 telemetry cards (3x2 grid)
- Middle: Chat interface area with message count
- Bottom: Mode-aware status bar (NORMAL/INPUT/CMD)
Telemetry Cards (live real-time dashboard):
1. PROVIDER — LLM provider name (neon cyan)
2. MODEL — Model name (neon green)
3. CLOCK — Live HH:MM:SS (neon amber)
4. TOKENS — Total/Max token count (neon magenta)
5. USAGE — Gauge bar with % (green→amber→red gradient)
6. MESSAGES — Total message count
Color Palette (holographic):
- BG: Deep violet-black (#0A0214)
- Cards: Neon cyan/magenta/green/amber/red
- Borders: Dim purple, rounded box-drawing
- Status bar: Glowing border per mode (cyan/green/amber)
Controls: / or : → command mode, i → input, q → quit
1. /quit and /help commands restored:
- /quit: exits the interactive loop
- /help: shows formatted help box with available commands & tips
- Unknown commands get a friendly error pointing to /help
- Slash commands intercepted before LLM processing (no token waste)
2. Zero warnings across entire workspace (13 files fixed):
- Fixed ambiguous glob re-exports in gyandoot-core
- Added #[allow(dead_code)] on intentional dead fields
- Fixed unused imports, variables, and macros
- Fixed unused assignments in subagents
- cleaned up service.rs, profile, kanban warnings
install.sh now produces a completely clean build with zero warnings.
Fixes the cramped terminal output. Two changes:
1. display_output: trailing \n after G> text for spacing
2. read_input: leading \n before » prompt + 30ms delay after spinner clear
to prevent \r overlap with prompt character
Before: G> response...» Hi (no gap, overlapping)
After: G> response...
(blank line)
» Hi (clean, separated)
Three UX improvements for interactive mode:
1. Input prompt: Shows '»' (bold cyan) on stdin before reading user input
2. Output prefix: Agent responses prefixed with 'G>' (bold green)
3. Thinking indicator: Animated spinner on stderr showing 'answering (N.Ns)'
with braille frame characters, auto-clears when response arrives
- Start/stop via tokio::sync::watch channel + tokio::spawn
- Only shows when interactive (atty detection)
- Does not affect gateway/sub-agent modes
- Create systemd + launchd service templates in resources/
- Add gyandoot-core/src/service.rs module with full lifecycle
- systemd: user service, daemon-reload, enable, start, stop, disable
- launchd: macOS plist, load/unload, start/stop (launchctl)
- Add 'gyandoot service install|remove|status' CLI subcommand
- Update setup wizard: prompts 'Install system service? [y/N]'
- Update install.sh: prompts user for service install after binary
- Browser auto-starts at runtime (browser_manager), no separate service needed
- Camoufox installed via npm, auto-started by GyanDoot agent loop
Full integration:
- install.sh now installs camoufox-browser via npm (preferred) or Docker
- install.sh creates systemd/launchd auto-start service on Linux/macOS
- New browser_manager.rs — health-check, auto-start, wait-for-ready
- AgentLoop::run() auto-inits Camoufox and registers web_search/web_browse
- System prompt tells LLM to use web_search/web_browse as PRIMARY tools
- Graceful fallback: if Camoufox unavailable, logs warning, uses bash tools
- ToolExecutor is now Arc<Mutex<>> for runtime tool registration
GyanDoot v2 now: install → Camoufox auto-starts → web tools available →
"search NSE India" uses Camoufox with anti-detection 🎯
Three fixes:
1. OllamaProvider now sends tools[] in native /api/chat requests
2. OllamaProvider parses tool_calls from response (OpenAI-compatible
format with function.name, function.arguments)
3. System prompt now lists available tools so LLM knows it can use
bash, curl, read/write files — stops saying "I cannot do that"
- Agent name now falls back to config.name if [identity] section is empty
- Agent role defaults to "AI Assistant built by Gyannetra"
- User name defaults to "the User" if not set
- Removes dependency on identity_setup being completed
- Every session gets identity+memory injected into system prompt
- Added [identity] config section (agent_name, agent_role, user_name,
user_role, setup_complete) to GyandootConfig and AgentConfig
- Agent loop now injects identity into system prompt on startup
- Agent loop injects past verbatim dialogue memory into system prompt
- Added dialogue_append() / dialogue_read() to MemPalace
- Word-by-word, line-by-line verbatim record in dialogue.md
- No compression, no truncation — every message preserved
- Agent loop writes to BOTH daily transcript AND verbatim dialogue
on every turn
- All config paths pass identity through (flat converter, subagents)
BUGFIXES:
- Banner now displays on 'gyandoot run' via gyandoot_tui::banner::print_banner()
- Config now loads from ~/.config/gyandoot/config.toml with fallback chain
(XDG -> ~/.gyandoot/config.yaml -> hardcoded defaults), fixing 401 errors
- gyandoot init now writes visible placeholder api_key for users to edit
NEW PROVIDERS (4 added, now 7 total matching setup wizard):
- Google Gemini: Native API with x-goog-api-key auth, streamingGenerateContent
- DeepSeek: OpenAI-compatible wrapper at api.deepseek.com/v1
- OpenRouter: With HTTP-Referer/X-Title headers, model routing
- Groq: OpenAI-compatible wrapper at api.groq.com/openai/v1
All compile with 0 errors.