Implement first-class Z.AI credential storage and authentication:
- New auth provider: src/auth/zai.ts for Z.AI API key management
- New CLI command: flynn zai-auth to store Z.AI API keys
- New TUI command: /login zai for interactive credential entry
- Modified src/auth/index.ts to register zai provider
- Modified src/cli/index.ts to register zai-auth command
- Modified src/cli/setup/providers.ts to include Z.AI in setup wizard
- Modified src/daemon/models.ts to support zhipuai use_oauth flag
- Modified src/daemon/clientFactory.test.ts to add Z.AI tests
- Modified src/frontends/tui/commands.ts to add login command
- Modified src/frontends/tui/minimal.ts to support credential prompts
This allows users to authenticate with Z.AI (GLM models) without
embedding secrets in config files. Credentials are stored securely in
~/.config/flynn/auth.json and resolved at runtime.
Updated state.json with new feature entry documenting the integration.
Replace manual process management with systemctl --user commands.
Uses ollama.service and llama-server.service units for proper lifecycle
management, VRAM cleanup, and integration with system services.
Track PIDs of backends started by /backend command and only kill those
specific PIDs. Previous implementation used pkill which would kill all
Ollama/llama-server processes including those started by the user or
systemd services. Now we only terminate processes we started.
- Add local_providers with ollama and llamacpp configurations
- /backend command now stops current daemon before starting new one
- Start backends as detached processes to avoid blocking TUI
- Wait 500ms for daemon to initialize before switching
- Add curly braces to all if/else/for/while statements
- Fix indentation and trailing spaces
- Auto-fixed 372 linting errors using eslint --fix
- Remaining issues are warnings only (non-null assertions, explicit any types)
Previously, switching to zhipuai/openrouter/xai via /model would throw a
confusing 'OPENAI_API_KEY missing' error from the OpenAI SDK. Now
createClientFromConfig validates API keys before constructing the client,
throwing errors that name the correct env var (e.g. ZHIPUAI_API_KEY).
Also fixes the misleading 'as anthropic' type cast in the /model handler
to validate against MODEL_PROVIDERS and use the ModelProvider type.
Extract MODEL_PROVIDERS const from config schema as single source of truth
for provider names. PROVIDER_NAMES in TUI commands now imports from schema
instead of maintaining a hardcoded list. Adds tests verifying sync.
Updates README TUI Commands section with /model hot-swap documentation,
supported providers, and runtime model switching examples.
ModelRouter now supports multiple tier-change listeners via addOnTierChange(),
SessionBridge subscribes to tier changes and propagates them to all WebChat
agents (both existing and newly created), and the fullscreen TUI now also
updates the agent's tier when switching models (matching minimal TUI behavior).
Use agent.reset() instead of session.clear() when agent is available,
ensuring tool fingerprint, usage counters, and agent history are all
cleared properly alongside the session.
Replace process.stdout.write-based onToolUse callback (which corrupts
Ink rendering) with a React state-driven approach that shows tool names,
args, and completion status in the streaming content area.
Fullscreen TUI was calling modelClient directly, bypassing the NativeAgent
tool loop entirely. Pass the agent through FullscreenTuiConfig → App and
use agent.process() for message handling, which enables the full tool
registry and executor.
Persist /model tier choice to ~/.local/share/flynn/preferences.json so
it survives restarts. Decode HTML entities (e.g. ') in markdown
renderer output. Suppress noisy logger.info and punycode deprecation
warnings in TUI startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Block-level renderer methods (paragraph, heading, blockquote, list) were
using raw token.text instead of this.parser.parseInline(tokens), causing
bold, italic, and inline code to never render. Add table renderer with
aligned columns and box-drawing separators. Remove unused marked-terminal
dependency (incompatible with marked v17).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clean up the once('close') listener on the readline Interface when
rl.question() resolves normally. Previously, each prompt loop iteration
accumulated a close listener that was never removed, triggering
MaxListenersExceededWarning after 11 prompts.
Five additive features with no breaking changes:
- Tool groups: group:fs, group:runtime, group:web, group:memory syntactic
sugar for allow/deny lists in tool policy config
- Typing indicators: Discord sendTyping() and WhatsApp sendStateTyping()
on message receipt for better UX feedback
- Session pruning: TTL-based auto-cleanup via sessions.ttl config with
hourly daemon timer and SQLite GROUP BY pruning
- /verbose command: TUI command parser toggle for raw streaming display
- !!think prefix: per-message extended thinking mode wired through
Anthropic (budget_tokens), OpenAI/GitHub (reasoning_effort), and
Gemini (thinkingConfig) providers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new 'github' model provider backed by the Copilot API
(api.githubcopilot.com), with OAuth device flow for authentication.
- New src/auth/github.ts: device flow login, token storage at
~/.config/flynn/auth.json with 0600 permissions
- New src/models/github.ts: OpenAI-compatible client with streaming,
tool calling, and Copilot-specific headers
- Add 'github' to provider enum in config schema
- Register provider in daemon factory and TUI client factory
- Refactor TUI to use provider-agnostic client factory (was hardcoded
to AnthropicClient for all tiers)
- Add /login command to TUI for interactive OAuth authorization
- Add Copilot model cost tracking entries
Widen Message.content from string to string | MessageContentPart[] to support
multimodal content. Add Attachment type to channel layer, media conversion
utilities, and image extraction to all channel adapters (Telegram, Discord,
Slack, WhatsApp). Update all model clients (Anthropic, OpenAI, Gemini, Bedrock)
to convert structured content to provider-specific formats. Fix downstream
consumers (tokens, compaction, TUI, local models) to handle the widened type
via getMessageText() helper.
The /model command was only updating the router's currentTier but not
the agent's currentTier. Since NativeAgent.chatWithRouter() passes its
own tier to router.chat(), switching to 'local' still sent requests
through the default (Anthropic) client first.
- Extract createClientFromConfig() to dispatch on provider field instead
of hardcoding all tiers as AnthropicClient
- Add fallback/fallbackReason metadata to ChatResponse and ChatStreamEvent
so callers know when a fallback model was used
- Enhance doctor check to report full model stack and warn on missing
API keys for cloud providers
- Log fallback warnings in NativeAgent and display them in TUI
- Support tier names and local_providers entries in fallback_chain
- Add 8 tests for createClientFromConfig covering all provider types
Telegram bot now shows tool status during execution:
- Sends status message when tool starts (tool name + args snippet)
- Edits status message with result on completion
- Keeps typing indicator active during tool execution
- Adds setOnToolUse() to NativeAgent for per-message callback control