ACP providers use api_base to store an executable command/path, not a URL.
A previous SSRF-hardening refactor incorrectly classified ACP as a local-URL
provider type, causing create/update to reject valid ACP configs with
"provider URL must use http or https scheme".
Changes:
- Remove ProviderACP from localURLProviderTypes.
- Add validateACPExecutablePath mirroring the runtime registration logic in
cmd/gateway_providers.go (allows empty, claude/codex/gemini, or absolute path;
rejects URLs and relative paths).
- Route ACP through the new validator inside validateProviderURL.
- Add unit tests for validateACPExecutablePath.
- Update existing local-type tests that assumed ACP was URL-based.
- Add create/update HTTP handler regression tests for ACP binary, empty binary,
and URL rejection.
Bug-first verification: without the fix, ACP api_base values like "gemini" or
absolute paths fail URL validation, while URLs are incorrectly accepted; with
the fix the behavior is reversed to match the intended executable-path model.
Fixesnextlevelbuilder/goclaw#1481.
* feat(providers): explicit prompt cache for DashScope/Qwen
Extend OpenAI-compat path with Anthropic-style cache_control:ephemeral
inline blocks for Alibaba DashScope endpoints (provider_type=bailian or
URL contains "dashscope"). Verified live: qwen3.6-plus / qwen3.5-plus /
qwen3-coder-plus all return 99.7% cache hit rate on 6K-token prefix,
yielding ~89% token cost reduction on cached prefix (90% Alibaba discount).
- isDashScope(): 3-source detection (URL + providerType + name) handles
reverse-proxied endpoints; covers both "dashscope" and "bailian"
- buildRequestBody wraps system content with SplitSystemPromptForCache
(exported from anthropic_request.go) using <!-- GOCLAW_CACHE_BOUNDARY -->
- Tool prefix cache: cache_control on last tool definition, with 4-marker
budget guard
- Parse cache_creation_input_tokens from prompt_tokens_details into
Usage.CacheCreationTokens; propagates via existing span metadata writers
- Runtime escape hatch: GOCLAW_DISABLE_DASHSCOPE_CACHE=true
- Live integration smoke test (build tag integration, env-gated)
* chore(providers): polish per PR #1127 review
- Use strings.Repeat instead of custom repeat() helper in smoke test
- Clarify BuildRequestBodyForTest is test-only, not public API
* feat(providers): enable thinking for Qwen 3.7/3.6, observe cache in smoke test
Qwen3.7-plus and qwen3.6-plus support deep thinking but were missing from
dashscopeThinkingModels, so enable_thinking/thinking_budget was silently
skipped. Add both to the whitelist and test.
Add qwen3.7-plus to the cache smoke test as cache-optional: Alibaba's
context-cache doc does not yet list 3.7/3.6 for explicit cache, and the
cache wrap is a safe no-op when unsupported, so a no-cache result is logged
rather than failed to avoid a flaky live assertion.
Claude-Session: https://claude.ai/code/session_01X3jkrc7N3ar8ZGzUJWyNS5
* fix(providers): preserve DashScope detection for proxy routes
* fix(sandbox): avoid shell in FsBridge writes
Replace sh -c with interpolated path by shell-free 'tee -- <path>' argv form,
piping content via stdin. Prevents command injection through filenames
containing shell metacharacters inside the sandbox container.
Co-authored-by: evgyur <evgyur@gmail.com>
* fix(security): fail-closed on pairing DB errors across channels
On IsPaired lookup error, deny instead of granting access. Covers the shared
CheckDMPolicy/CheckGroupPolicy helpers (Slack/Discord/Feishu/WhatsApp/Zalo) and
the four inline Telegram pairing checks.
Co-authored-by: Srini <srinis.k@gmail.com>
* fix(security): harden provider URL validation against SSRF
Enforce scheme check for all provider types; restrict local types (ollama,
claude_cli, acp) to an explicit localhost allowlist instead of skipping checks;
resolve remote hostnames and reject any IP in a private/reserved range via the
shared security.IsBlocked CIDR list (covers loopback, link-local, metadata,
multicast, and unspecified 0.0.0.0/::). Closes the wildcard-DNS bypass and the
local-type escape hatch. Operator opt-in via GOCLAW_ALLOW_PRIVATE_PROVIDER_URLS.
Exports security.IsBlocked as the single source of truth for blocked ranges.
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
* feat(pipeline): add fail-closed tool call authorization gate
Gate tool execution against the server-side AllowedTools allowlist built from the
RBAC/tenant-aware filtered tool set. Resolve the tool-call prefix before the
allowlist lookup so prefixed agents are not wrongly blocked, re-check deny on lazy
MCP activation, and expand IsDenied to cover aliased tool names.
Co-authored-by: Huy Doan <tui@pm.me>
* fix(security): expand file-serve deny-list defense-in-depth
Add absolute-path deny prefixes (/home, /Users, /srv, /var/lib, /var/www, /opt)
and an explicit fail-closed log when no file-serving boundary is configured.
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
* fix(providers): allow claude cli executable paths
Refs: #1185
---------
Co-authored-by: evgyur <evgyur@gmail.com>
Co-authored-by: Srini <srinis.k@gmail.com>
Co-authored-by: Linh Vo Van <linh.vo@e-cq.net>
Co-authored-by: Huy Doan <tui@pm.me>
Follow-up to #942. The HTTP onboarding path (registerInMemory) still diverged
from the startup path in two ways that #942 deliberately deferred:
1. Anthropic: startup wires WithAnthropicRegistry(modelReg) (cmd/gateway_providers.go:349),
HTTP skipped it — forward-compat model resolution + token counting used static
defaults for HTTP-onboarded providers.
2. Claude CLI: startup validates filepath.IsAbs + exec.LookPath before registering
(cmd/gateway_providers.go:290-297), HTTP skipped both — DB could be poisoned
with relative paths (resolves against CWD) or non-existent binaries that would
pass verify but crash on first spawn.
Changes:
- Add ProvidersHandler.modelReg field + SetModelRegistry setter
- Thread modelReg through wireHTTP in cmd/gateway_http_handlers.go
- Anthropic branch: append WithAnthropicRegistry when modelReg non-nil
- Claude CLI branch: mirror startup validation (reject non-absolute, skip if LookPath fails)
Tests added:
- TestProvidersHandlerRegisterInMemorySkipsClaudeCLIWhenBinaryMissing — asserts
provider NOT registered when binary path cannot be resolved
- TestProvidersHandlerRegisterInMemoryAnthropicUsesModelRegistry — reflection-probe
verifies registry wiring (no public getter on AnthropicProvider)
- Updated existing CLI test to use a real temp binary so exec.LookPath succeeds
regardless of whether \`claude\` is installed in the runner's PATH
* fix(providers): use DB name for Anthropic/ClaudeCLI in HTTP onboarding
Commit 7fcf0327 added WithAnthropicName/WithClaudeCLIName on the startup
registration path (cmd/gateway_providers.go) but missed the HTTP onboarding
path (internal/http/providers.go). Providers created via POST /v1/providers
were registered in-memory with the hardcoded default name ("anthropic",
"claude-cli") instead of the user's chosen DB name.
Symptom: user onboards an Anthropic provider named e.g. "my-anthropic",
picks a model, hits verify — handleVerifyProvider looks up via
GetForTenant(tenantID, p.Name) and fails with "provider not registered:
my-anthropic" because the registry key was "anthropic".
Fix: mirror the startup path by passing WithAnthropicName(p.Name) and
WithClaudeCLIName(p.Name) in registerInMemory. Adds two regression tests
asserting custom-named providers are locatable by their DB name and NOT
by the hardcoded default.
Closes incomplete-refactor gap in #742.
* test(providers): add negative assertion for ClaudeCLI default name
Mirrors the Anthropic test's negative guard: when a user picks a custom
provider name, the hardcoded "claude-cli" default must NOT be present in
the registry. Per Claude review on PR #942 — symmetric test coverage.
- Reject incompatible explicit provider embedding dimensions on create/update
- Ignore previously saved incompatible dimensions at runtime, fall back to 1536
- Share RequiredMemoryEmbeddingDimensions constant from store package
- Remove dimensions input from provider UI (always 1536 per pgvector schema)
- Add HTTP, runtime, and validation unit tests
Closes#548
Supersedes #410
* feat(auth): support named chatgpt oauth providers
- add provider-scoped ChatGPT OAuth routes and CLI support
- persist refresh tokens per provider and reject provider-type collisions
- wire provider OAuth setup flows in the dashboard and setup UI
Refs #448
* feat(agent): add chatgpt oauth account routing
- add agent other_config routing for manual and round-robin selection
- reuse routed provider resolution across resolver and pending loaders
- add router, parser, and agent advanced dialog coverage for multi-account use
Refs #448
* docs(api): describe chatgpt oauth routing
- document named-provider ChatGPT OAuth auth routes
- describe agent-side account routing and round-robin behavior
- update OpenAPI agent config schema and provider type enum
Refs #448
* fix(store): add missing agent key context helpers
* feat(ui): clarify chatgpt oauth account setup and routing
* docs(providers): align chatgpt oauth alias examples
* feat(agent): add codex pool activity dashboard
* fix(providers): harden codex oauth alias setup
* feat(codex-pool): improve routing dashboard UX
- redesign the Codex/OpenAI pool page around saved-pool checkpoints and live evidence
- add clearer selection, attention, and recent-proof states for pool members
- make the lower panels fill the remaining desktop viewport while staying responsive
* fix(store): resolve context helper merge duplication
* feat(oauth): add codex pool quota and observation APIs
- add quota inspection and observation endpoints for ChatGPT Subscription (OAuth) providers
- teach codex routing to surface pool activity, observation metadata, and quota-aware readiness
- extend tests and HTTP docs/OpenAPI for the new pool monitoring flows
* feat(web): add codex pool quota monitor and controls
- add provider quota fetching, readiness badges, and live routing evidence on the account pool page
- redesign pool setup and activity panels for multi-account management with localized copy updates
- keep the live monitor internally scrollable and compact the account cards for better viewport fit
* fix(web): clarify pool routing labels
- rename the recent request badge from Direct to Selected
- restore compact quota bars in the live pool cards
* feat(codex-pool): add runtime health dashboard
- derive per-provider success and failure health from routed Codex traces
- surface routing, quota, and recent request evidence in the pool UI
- align provider alias guidance and owner access with the dashboard role model
* docs(auth): document tenant scoping and key roles
* fix(auth): harden tenant and codex pool access control
* fix(providers): align codex pool runtime defaults
* feat(ui): tighten codex pool responsive layout
* feat(chatgpt-oauth): refine codex pool management UX
* feat(chatgpt-oauth): surface quota bars on provider pages
- add compact quota bars to Codex provider rows and provider detail
- fetch quota only for ready visible provider rows and ready detail aliases
- fix managed-member detail visibility and tighten provider locale copy