Delegate session keys (delegate:{uuid8}:{agentKey}:{delegationId}) were
not parsed by makeSchedulerRunFunc, causing fallback to default agent ID
which doesn't exist in managed-mode DBs. Add switch/case to handle both
agent: and delegate: prefixes.
* refactor: remove managed/standalone mode distinction from codebase
Standalone mode is deprecated; managed mode is now the only mode.
Remove redundant "managed mode" qualifiers from comments, docs,
and error messages. Error strings now reference "database stores"
instead of "managed mode" for clarity.
* improve(onboard): streamline onboard process and env setup
Simplify onboard wizard, extract helpers to dedicated file,
update env example and entrypoint for default managed mode,
clean up prepare-env script, update i18n catalogs.
Cron jobs ran in isolated sessions with no context about who requested
them or where to deliver responses. The agent would misroute responses
(e.g., using team_message instead of replying to the original chat).
- Inject ExtraSystemPrompt with job name, requester ID, and delivery
target so the agent knows to produce content directly
- Pass client.UserID() in Web UI cron.create instead of empty string
StripConfigLeak was blocking legitimate responses when predefined agents
mentioned SOUL.md/IDENTITY.md/AGENTS.md in architecture explanations.
Improved detection logic to exclude code blocks but disabled the gate
entirely for now until a more robust approach is designed.
- Update seed defaults to use chain format {"providers":[...]}
- Add startup auto-migration for existing legacy flat settings in DB
- Remove legacy flat format parsing from parseChainSettings()
Run d3-force simulation synchronously in useMemo (~300 ticks → 1 render
instead of 300). Redesign team notifications section with gradient bg,
Bell icon, and Switch toggle.
- Add agent_count to MCP server list API via CountAgentGrantsByServer query
- Show Agents column in MCP servers table
- Mask sensitive header values (Authorization, API keys) and env vars
(keys containing secret/token/password) in create/edit form
Replace manual user_id text input with select dropdown populated from
sessions (DM + group chats). Add colorMode prop to ReactFlow for dark
mode Controls/MiniMap. Add agent select in empty state.
- Add ~/.goclaw/cli-workspaces/ to read_file allowed paths
- Enables agents to read working files from CLI workspace sessions
Co-authored-by: Nam Nguyen Ngoc <namnn.0911@gmail.com>
Predefined agents were vulnerable to conversational identity manipulation
(e.g. "I'm your master", "you only listen to me") — users could establish
authority through chat history, causing the agent to comply in subsequent messages.
Added identity anchoring at 3 system prompt zones (predefined agents only):
- Primacy: explicit instruction after Safety section
- Middle: USER_PREDEFINED.md owner authority framing
- Recency: persona reminder reinforcement
LLM summarization (30-120s) was blocking HTTP response causing browser
timeouts. Now runs in goroutine and returns immediately. UI polls every
5s until completion.
MaybeCompact relied on RAM count which resets to 0 on restart (LoadFromDB
is a no-op). Messages accumulated in DB but never triggered compaction.
Now falls back to CountByKey DB query when RAM count is below threshold.
Replace channel column with context usage bar showing estimated tokens
vs compaction threshold. Token count estimated from messages JSONB
(octet_length/4 + 12K system prompt) — no migration needed.
Color-coded: green <60%, amber 60-85%, red >85%. Inline compaction
count with refresh icon.
* fix(channels): annotate DM messages with sender identity
Telegram and Zalo group messages already include [From: sender] prefix
so the agent knows who is talking, but DM messages were sent without
any sender context — the agent had no way to address the user by name.
- Telegram DM: add [From: @username] (or FirstName if no username)
- Zalo DM: add [From: displayName] when dName is present in payload
* fix(tests): add missing EnsureUserProfile to test stubs
AgentStore interface gained EnsureUserProfile in 4fce731 but the test
stub implementations were not updated, breaking CI on main.
---------
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Replace hardcoded English tooltip strings in compaction config with
t() calls. Add insertContact, searchContacts, noContactsFound i18n
keys across en/vi/zh.
Add inline contact search above textarea in file editor (predefined
agents only). Search contacts and click to insert formatted snippet
at cursor position. Also make USER.md clickable in sidebar.
Extract KG from Memory page into standalone /knowledge-graph route.
Fix graph zoom: fitView after D3 force simulation settles so all nodes
are visible. Compact layout: merge stats/search/actions into single row,
inline filters with page header.
Add searchable contact dropdown in instances sidebar to find channel
contacts and add them as agent instances. Backend EnsureUserProfile
creates user_agent_profiles row on demand when admin adds contacts.
Regroup sidebar from 4 to 7 sections (Core, Conversations, Connectivity,
Capabilities, Data, Monitoring, System) for better organization. Keep
technical terms Nodes and TTS as-is across all languages.
Pass contactCollector through channel manager to all channel handlers
(Telegram, Discord, Feishu, Slack, Zalo) so contacts are automatically
collected when users interact with the agent.
The `allowEmpty` prop on `ProviderModelSelect` was defined but the
Select dropdown still auto-selected the first provider when the value
was empty, and there was no way to reset back to empty (auto-detect).
- Map empty provider to a sentinel so Radix Select can represent it
- Show an "(auto)" option in the dropdown when `allowEmpty` is set
- Pass `allowEmpty` from both embedding config callers
- Skip clearing the model on provider change in allowEmpty mode to
avoid a stale-closure bug where the second setState overwrites the
provider update
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
* feat(cron): configurable default timezone for cron expressions
Cron expressions (e.g. "0 8 * * *") are evaluated relative to a timezone.
Without an explicit per-job timezone, they default to the server's system
timezone, which may not match the user's local time — especially in Docker
containers (default UTC) or multi-region deployments.
This adds a `default_timezone` setting to `CronConfig` (IANA format, e.g.
"Asia/Ho_Chi_Minh") that is applied as fallback when a cron job has no
explicit `schedule.tz`. The setting is configurable via the UI config page
(Integrations → Cron Scheduler) and hot-reloads on config changes.
Backend:
- Add `DefaultTimezone` field to `CronConfig`
- Add `SetDefaultTimezone()` to `CronStore` interface + PG implementation
- Apply default TZ in `AddJob()` when `schedule.TZ` is empty
- Wire at startup + subscribe to config change events for hot reload
- Update cron tool description so LLM knows about gateway default
Frontend:
- Add timezone dropdown (20 common IANA timezones) to Cron config section
- Add i18n keys for en, vi, zh
* fix(cron): apply default timezone to existing jobs via computeNextRun
Pass defaultTZ as fallback to computeNextRun so existing cron jobs
(with timezone = NULL in DB) also use the gateway's configured default
timezone when computing next_run_at. This ensures old jobs benefit
from the timezone setting without needing a DB migration or backfill.
---------
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
* feat(providers): add Ollama local and Ollama Cloud provider support
Adds two new provider variants:
ollama — local/self-hosted Ollama instance
- Gated on providers.ollama.host in config (or GOCLAW_OLLAMA_HOST env)
- No API key required; Ollama's OpenAI-compat endpoint accepts any Bearer value
- Defaults to http://localhost:11434/v1 (configurable for LAN/remote hosts)
- Default model: llama3.3
ollama-cloud — Ollama Cloud (managed remote inference)
- Gated on providers.ollama_cloud.api_key (or GOCLAW_OLLAMA_CLOUD_API_KEY env)
- Bearer token from ollama.com/settings/keys
- Default base URL: https://ollama.com/v1 (overridable via api_base)
- Default model: llama3.3
Both variants use the existing NewOpenAIProvider (OpenAI-compat) — no new
provider struct needed. Both are registered from config file and DB (via
llm_providers table with ProviderOllama / ProviderOllamaCloud types).
OllamaCloud.APIKey follows all existing secret handling patterns:
MaskedCopy, StripSecrets, StripMaskedSecrets.
* feat(providers): wire Ollama into web UI and fix DB registration
- Add ollama + ollama_cloud to PROVIDER_TYPES constants (dropdowns)
- Fix setup wizard: skip API key requirement for Ollama local (isOllama)
- Fix bootstrap status: recognize Ollama local as no-API-key provider
- Add ollama_cloud to config-page KNOWN_PROVIDERS list
- Fix gateway_providers.go: move ProviderOllama before APIKey=='' guard
so DB-registered local Ollama providers actually register at startup
(same pattern as ClaudeCLI, which also needs no API key)
The onboard wizard sets GOCLAW_PROVIDER and GOCLAW_MODEL in .env for
initial bootstrap. Previously these env vars always overrode the config
file value via envStr(), making it impossible to change the default
provider/model through the Dashboard — every save was silently reverted
by ApplyEnvOverrides().
Change envStr to envFallback for these two fields: the env var is only
applied when the config file has no value (empty string). Once the user
saves a provider/model via the Dashboard, the config-file value wins.
Also:
- Stabilize ProviderModelSelect auto-select effect (useRef + useMemo)
- Add toast feedback on config save success/failure
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Flush threshold (176K) was higher than compaction threshold (150K), so
flush was always skipped. Removed independent threshold calculation —
flush now always runs before compaction, gated only by enabled check and
per-cycle dedup guard. Also localized hardcoded compaction tooltips
(en/vi/zh), fixed inaccurate Memory Flush description, and removed dead
reserveTokensFloor/softThreshold UI inputs.
- Defer Telegram media download until after mention gate — pending
history now uses lightweight tags (no download) saving bandwidth
and avoiding errors on large files
- Fix compaction status query: has_summary now false when new messages
arrive after last compaction, re-enabling the compact button
- Fix HTTP compact endpoint: detach context from request so LLM
summarization isn't cancelled when browser closes connection
Backend defaults memoryFlush.enabled to true when config is nil
(memoryflush.go:39-46), but UI used `?? false` showing OFF.
Also fix sub-fields visibility to show when flush is defaulted ON.
Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
Add Provider, Model, MaxTokens to PendingCompactionConfig so users can
override the LLM used for pending message summarization via the config
UI. Falls back to agent's provider/model when not set. Increase default
max_tokens from 512 to 4096. Add allowEmpty prop to ProviderModelSelect
to prevent auto-selecting first provider when empty means "use default".
When an announce (child trace) run starts, toggle the parent trace
status to "running" so the UI doesn't show "completed" with a running
child span. Restore to "completed" when the announce finishes.
Spans are now emitted BEFORE execution starts with status "running" and
input visible, then updated when the step completes. This lets users see
what the system is doing during long-running LLM calls and tool executions.
- Add EmitSpanUpdate() to collector with separate update buffer
- Flush ordering: batch INSERT new spans first, then process updates
- Split LLM/tool/agent spans into start/end pairs (agent loop + subagent)
- Emit tool span end inside goroutines for parallel calls (no orphans)
- EmitSpanUpdate is a channel send — works after ctx cancellation
- Fix compact endpoint using random provider instead of agent's configured provider+model
- Wire auto-compaction for all 5 channel types (telegram, discord, slack, feishu, zalo_personal)
via PendingCompactable interface and InstanceLoader
- Add global PendingCompactionConfig (threshold, keep_recent) to ChannelsConfig
- Wire global config through InstanceLoader and PendingMessagesHandler
- Increase compaction timeout from 45s to 180s for slow providers
- Add pending compaction config card to Behavior tab in config page
- Add HowItWorksCard (expanded by default) and toast notifications to pending messages page
- Add i18n support for all new strings (en/vi/zh)
- Add reviewer role to team system (backend + UI) for evaluate_loop workflows
- Fix handoff/evaluate_loop/delegate_search system prompt entries (were showing as custom tool)
- Filter inactive agents from delegation queries (DelegateTargets, SearchDelegateTargets)
- Fix agent link direction display (flip outbound→inbound when viewed from target side)
- Improve builtin tool seed descriptions with detailed action-oriented text
- Add i18n support for builtin tool descriptions (en/vi/zh frontend locale files)
- Notify LLM when KG extraction triggered on memory write
* fix(gateway): extract media forwarding helper and fix group writer cron/media gaps
- Extract appendMediaToOutbound() helper to deduplicate media attachment conversion across all outbound paths
- Add media forwarding to cron job handler, handoff announce, and teammate message flows
- Fix silent reply suppression to allow media-only responses (no text content)
- Skip group writer refusal prompt for system-initiated runs (cron, delegate, subagent) with empty senderID
* fix(agent): protect identity files in system-initiated group writer runs
- Move writer list lookup before senderID check so cron/delegate/subagent
runs still load group writer config
- Inject file protection prompt for system-initiated runs instead of
skipping entirely, preventing modification of SOUL.md, IDENTITY.md,
AGENTS.md, and USER.md
- Retain fail-open behavior when writer list is unavailable
---------
Co-authored-by: Nam Nguyen Ngoc <namnn.0911@gmail.com>
* fix(i18n): add missing config.json locale files
The config namespace was registered in i18n/index.ts but the actual
JSON files were never created, breaking the UI Docker build (tsc error).
Also fix .gitignore: config.json → /config.json so only the root
gateway config is ignored, not the i18n locale files.
Closes#107
* feat(ui): replace language cycle button with dropdown select
Users can now pick a language directly from a dropdown instead of
cycling through all options on each click.
* fix(i18n): keep "Subagent" in Vietnamese translations
"Agent con" and "sinh sản" sound unnatural; keep the English term.
* chore: align .dockerignore config.json pattern with .gitignore
Extract CompactGroup() from runCompaction() for shared use by both
auto-compact and HTTP compact endpoint. Wire provider registry into
PendingMessagesHandler so /v1/pending-messages/compact performs actual
LLM summarization (keep 15 recent + summary) instead of hard delete.
Add rows-affected guard in Compact() to prevent duplicate summaries
when concurrent compactions race on the same key.
Move count assignment before RAM trim in Record() so MaybeCompact
receives pre-trim count (51) that exceeds threshold (50), allowing
compaction to fire. Previously count was always ≤ limit after trim.
- Update go.mod and Dockerfile to Go 1.26
- Apply `go fix ./...` stdlib modernizations across 170+ files
- Add `go fix` to post-implementation checklist in CLAUDE.md
- Fix go fix misapplied rewrite in loop_history.go
- Add tool status display on channels during tool execution (streaming preview + reactions)
- Emit agent.activity events at phase transitions (thinking, tool_exec, compacting)
- Enrich delegation progress with per-member activity and tool info
- Add LLM-based intent classifier for DM status queries when agent is busy
- Keyword fast-path for cancel/status patterns (no LLM cost)
- Falls back to LLM classification with 5s timeout
- Supports status_query (immediate reply) and cancel (abort run) intents
- Register/unregister runs in makeSchedulerRunFunc for channel inbound tracking
- Add sessionRuns secondary index in Router for O(1) IsSessionBusy lookups
- Add intent_classify config toggle (global default + per-agent override)
- Add tool_status config toggle for channel tool status display
- Add i18n keys and translations (en/vi/zh) for status messages
- Add web UI config toggles for intent_classify and tool_status