Clear targetAgentID in handleMessage so the gateway consumer's
resolveAgentRoute matches via cfg.Bindings instead of always
using the channel instance's agent_id.
Without this patch, all Discord channels route to the channel
instance's default agent, ignoring bindings in config.json.
Add slog.Info call for routing diagnostics.
Co-authored-by: Duy /zuey/ <duy@wearetopgroup.com>
Co-authored-by: Said <msaidf@users.noreply.github.com>
The sessions.reset RPC only reset the native session store, leaving the
Claude CLI-backed history (.jsonl + CLAUDE.md) in place. The /reset chat
command already clears it via providers.ResetCLISession, so RPC callers
got an incomplete reset for claude-cli agents — stale history was resumed
on the next turn.
Mirror the chat path by calling ResetCLISession from handleReset. It is a
no-op when the CLI provider is unused. The call is wired through an
overridable package var so the behavior can be regression-tested.
makeCronJobHandler builds the agent RunRequest with no provider/model, so cron
jobs always run on the agent default provider. High-frequency scheduled jobs
(e.g. triage) can't be routed to a cheaper model the way heartbeats already can
via agent_heartbeats.provider_id/model.
Add provider_id/model to cron_jobs (Postgres migration 000074; SQLite schema +
migration v42→43, both mirroring agent_heartbeats) and resolve them into
RunRequest.ProviderOverride/ModelOverride in the handler, reusing the existing
per-run override plumbing that heartbeats use. NULL/unset → agent default, so
existing jobs are unaffected.
Settable today via UpdateJob patch (CronJobPatch.ProviderID/Model) or directly
in the DB; tool/RPC/dashboard exposure left as a follow-up.
Verified: go build ./... and go build -tags sqlite ./... (exit 0), go vet,
gofmt, and cron unit tests on both pg and sqlite stores.
* 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
bridgeContextMiddleware injects the agent UUID (store.WithAgentID) but never the
agent key, so session tools (sessions_list/history/send/status) that resolve the
caller via tools.ToolAgentKeyFromCtx always get "" and fail with "agent context
required" when invoked over /mcp/bridge (e.g. by a claude-cli provider agent).
Session keys are namespaced by agent key (agent:<key>:...), not UUID, and the
bridge has no RunContext fallback.
Inject the key in the same block that already fetches the agent for shell-deny
overrides — symmetric with store.WithShellDenyGroups, no extra DB call, no new
imports.
#1094 makes the same fix at this site but bundles it into a large, stale
(CONFLICTING) ACP/i18n refactor; this is the minimal extraction of that slice.
It omits #1094's companion store.WithAgentKey write, which nothing on dev reads
(every session tool reads tools.ToolAgentKeyFromCtx).
Add internal/gateway/bridge_context_test.go: a regression guard asserting a
signed X-Agent-ID lands the agent key in tools.ToolAgentKeyFromCtx, plus a
no-store negative control.
Signed-off-by: Jaegeon Oh <zezaeoh@gmail.com>
Upstream returns HTTP 400 `thinking is enabled but reasoning_content
is missing in assistant tool call message at index N` when an
assistant message with tool_calls is replayed in history without a
reasoning_content field. Kimi has server-side thinking enabled by
default for kimi-k2-turbo-preview, so the field is required even when
goclaw doesn't have captured reasoning content to send (e.g. the model
emitted a tool_call without any thinking, or the stream chunk that
carried it was lost).
The existing branch already gates on
openAIWireAssistantReasoningContent(model) (kimi/deepseek/o-series)
and emits the field only when Thinking != "". Extend so kimi_coding
also emits an empty string when Thinking is unset — satisfies Kimi's
"must be present" check without inventing reasoning content. Other
providers in the allowlist keep today's behavior: omit when empty.
Three new tests:
- kimi_coding always carries reasoning_content on assistant
- kimi_coding preserves real Thinking content when set
- non-kimi providers (deepseek) do NOT inject empty reasoning_content
Reference: NousResearch/hermes-agent plugins/model-providers/kimi-coding
documents the same upstream behavior (thinking enabled by default,
reasoning_content roundtrip required).
Moonshot's Kimi Coding endpoint is OpenAI-compatible on the wire but
has two non-standard rules:
1. Every request must carry `User-Agent: claude-code/0.1.0` — without
it the upstream rejects the call outright.
2. `temperature` is locked to the server default; passing any other
value returns HTTP 400 `invalid temperature: only 1 is allowed for
this model`.
Rather than special-case either, this commit generalises both:
- WithExtraHeaders on OpenAIProvider — static headers attached to
every outgoing request. Reusable by any future provider that needs
pinned identity headers; mirrored in adapter_openai.ToRequest so
callers using the adapter path see the same shape.
- The existing skipTemp branch in openai_request.go gets a
provider_type check — kimi_coding joins o1/o3/o4/gpt-5-mini in
omitting `temperature` from the request body.
Provider wiring:
- store.ProviderKimiCoding constant + ValidProviderTypes entry +
KimiCoding{DefaultAPIBase,DefaultModel,RequiredUserAgent}.
- case store.ProviderKimiCoding in both registration switches
(cmd/gateway_providers.go and internal/http/providers.go).
- UI dropdown entry with the API base pre-filled.
5 unit tests cover: real outgoing header injection, adapter-path
header mirroring, empty-map WithExtraHeaders no-op, kimi_coding
strips temperature, and the negative control (other providers still
forward temperature).
Admin flow: Providers → Add → "Kimi Coding (Moonshot)" → paste API
key → save.
- Change outer container from items-center to items-start sm:items-center for mobile-first layout
- Add overflow-y-auto and max-h-dvh constraints to inner content div on mobile
- Desktop (sm breakpoint) retains centered layout with no scroll constraint
- Fixes issue where setup page content exceeds viewport on mobile without zoom
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>