fix(launcher): detect and display externally-managed gateway as running#1811
Closed
securityguy wants to merge 21 commits intosipeed:mainfrom
Closed
fix(launcher): detect and display externally-managed gateway as running#1811securityguy wants to merge 21 commits intosipeed:mainfrom
securityguy wants to merge 21 commits intosipeed:mainfrom
Conversation
Some providers (via OpenRouter) reject assistant messages with "content": "" alongside tool_calls. The OpenAI spec permits content to be absent when tool_calls is set. Switch openaiMessage.Content from string to *string with omitempty and introduce msgContent() to return nil when content is empty and tool calls are present. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ields Some OpenAI-compatible providers (e.g. OpenRouter routing to strict backends) reject non-standard fields in the request body such as reasoning_content in messages and extra_content / thought_signature in tool calls. Add a per-model strict_compat: true config option that strips these fields before serialization. Implementation: - Add StrictCompat bool to config.ModelConfig - Add WithStrictCompat option to openai_compat.Provider - Refactor HTTPProvider constructors into a single NewHTTPProviderWithOptions using variadic openai_compat.Option, eliminating the growing list of named constructors - Thread StrictCompat through CreateProviderFromConfig via composed options Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the claude CLI exits with a non-zero status, the previous error handler only checked stderr. However, the CLI writes its output (including error details) to stdout, especially when invoked with --output-format json. This left the caller with only "exit status 1" and no actionable information. Now includes both stderr and stdout in the error message so the actual failure reason is visible in logs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add claude-cli and codex-cli to the supported vendors table and include vendor-specific configuration examples explaining: - No API key is required (uses existing CLI subscription) - The claude-code sentinel model ID skips --model flag so the CLI uses its own configured default model Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add channels.telegram_bots config allowing multiple Telegram bot tokens to be configured, each mapped to a separate channel (e.g. telegram-amber, telegram-karen). Each channel can be independently bound to an agent via the bindings config, enabling distinct AI personas behind separate bots. Backward compatibility is preserved: the existing channels.telegram single-entry config continues to work unchanged. On load it is normalized into telegram_bots as an entry with id "default", which produces the channel name "telegram" so all existing bindings remain valid. Key changes: - config: add TelegramBotConfig struct with ChannelName/AsTelegramConfig helpers; add TelegramBots field to ChannelsConfig; normalize legacy single entry into list on load - telegram: add NewTelegramChannelFromConfig constructor accepting TelegramConfig + explicit channel name (avoids import cycle) - channels: add TelegramBotFactory registry; add injectChannelDependencies helper to eliminate injection code duplication; add duplicate channel name guard in initTelegramBot; update initChannels to iterate over TelegramBots; add prefix-based rate limit fallback for named bots Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…le.json Add two disabled example bots (alice, bob) under channels.telegram_bots and corresponding top-level bindings to illustrate how multiple Telegram bots map to separate named channels and agents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds GeminiCliProvider that wraps the Gemini CLI as a subprocess,
following the same pattern as the existing claude-cli and codex-cli
providers.
The provider invokes:
gemini --yolo --output-format json --prompt ""
with the prompt sent via stdin. The --prompt "" flag enables
non-interactive (headless) mode, reading the full prompt from stdin.
Key details:
- Model sentinel: "gemini-cli" skips --model flag (uses CLI default)
- Explicit model: "gemini-cli/gemini-2.5-pro" passes --model gemini-2.5-pro
- System messages prepended to stdin (no --system-prompt flag in gemini)
- Parses JSON response format: {"response": "...", "stats": {"models": {...}}}
- Token usage summed across all models in stats.models (gemini uses
multiple internal models per request)
- Tool calls extracted from response text using shared extractToolCallsFromText
- New protocol: "gemini-cli" / alias "geminicli"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add PR sipeed#1633 (gemini-cli provider) to contributions table - Add configuration guide section covering: - claude-cli, codex-cli, and gemini-cli providers with model_list examples - Multiple Telegram bots with bindings and per-agent config - Agent workspace and personality file notes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds GeminiCliProvider (PR sipeed#1633 against sipeed/picoclaw). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace Amber/Karen with Alice/Bob in all README examples for consistency. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously all agents shared a single LLMProvider instance created from agents.defaults.model_name. Per-agent model config (agents.list[].model) only changed the model string passed to Chat() — it never changed which provider binary was invoked. This caused cross-provider fallback chains (e.g. gemini-cli falling back to claude-cli) to fail, and made it impossible to assign different CLI providers to different agents. Introduces ProviderDispatcher which lazily creates and caches provider instances keyed by "protocol/modelID". The fallback chain's run closure now resolves the correct provider via the dispatcher before falling back to agent.Provider for backward compatibility. References sipeed#1634 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brings in ProviderDispatcher fix (PR sipeed#1637 against sipeed/picoclaw). References sipeed#1634. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
requiresRuntimeProbe and probeLocalModelAvailability handled claude-cli and codex-cli but not gemini-cli, causing the launcher to report "default model has no credentials configured" and skip auto-start when gemini-cli was set as the default model. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TryAutoStartGateway only checked gateway.cmd, which tracks processes the
launcher itself spawned. A gateway managed externally (e.g. via systemd)
was invisible to this check, causing two problems:
1. The launcher started a duplicate gateway instance on every launch.
2. The WebUI showed "Gateway Not Running" even when it was healthy.
Fix: probe the gateway health endpoint in two places:
- TryAutoStartGateway: skip auto-start if the health endpoint responds.
- gatewayStatusData: report "running" when the launcher has no owned
process but the health endpoint is responding. Launcher-owned
transition states (restarting/error) take precedence over the probe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
securityguy
added a commit
to securityguy/picoclaw
that referenced
this pull request
Mar 19, 2026
Brings in launcher external gateway detection fix (PR sipeed#1811 against sipeed/picoclaw). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
securityguy
added a commit
to securityguy/picoclaw
that referenced
this pull request
Mar 19, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This was referenced Mar 20, 2026
|
@securityguy Hi! The external gateway detection fix has been quiet for over 2 weeks. I'm closing it for now. Reopen if you'd like to continue working on this! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the picoclaw gateway is managed externally (e.g. via systemd), the launcher had two issues:
Duplicate start:
TryAutoStartGatewayonly checkedgateway.cmd, which tracks processes the launcher itself spawned. An external gateway was invisible, so the launcher started a second instance on every launch.WebUI showed "Gateway Not Running":
gatewayStatusDataonly probed the health endpoint when it owned the process. Withgateway.cmd == nil, it returned "stopped" immediately without checking whether the gateway was actually reachable.Fix
Adds
isGatewayHealthy()which probes the configured gateway health endpoint with a 1-second timeout.TryAutoStartGateway: callsisGatewayHealthy()before starting. If the gateway responds, logs "already running externally" and returns.gatewayStatusData: when the launcher has no owned process and its internal state is "stopped", probes the health endpoint. If healthy, reports "running". Launcher-owned transition states (restarting/error) take precedence over the probe.🤖 Generated with Claude Code