- Write-time normalization via normalizeOllamaAPIBase() on create/update
- Read-time safety net in resolveAPIBase() for pre-existing DB records
- Covers both ProviderOllama and ProviderOllamaCloud
- Stop hardcoding +"/v1" in registerInMemory/startup registration
Closes#654
- Add `reasoning` JSON alias to openAIMessage and openAIStreamDelta structs
- Coalesce reasoning/reasoning_content in streaming and non-streaming parse paths
- reasoning_content remains canonical; reasoning is Ollama-specific fallback
Closes#655
- Ollama/OllamaCloud providers now use native /api/tags for richer metadata
- Display name built from family + parameter_size + quantization_level
- Strips /v1 suffix, DockerLocalhost support, graceful fallback on error
- 7 comprehensive tests covering all edge cases
Closes#656
Fireworks AI requires stream=true for max_tokens > 4096. Proactively
clamp non-streaming requests to avoid 400 errors (their error format
doesn't match the generic clampMaxTokensFromError retry regex).
Closes#660
When share_memory is enabled, memory queries now remove the user_id
filter entirely instead of filtering by user_id IS NULL. This matches
the existing KG shared behavior and allows all users to see all memory
documents (global + per-user from everyone).
sanitizeHistory dedup rewrites ToolCalls IDs but not RawAssistantContent.
Anthropic provider prefers RawAssistantContent → sends stale IDs → HTTP 400.
Fix: clear RawAssistantContent when dedup fires. Also merge consecutive
same-role messages (user↔user, assistant↔assistant) to fix role alternation
errors from session corruption.
- Add expires_at check to ApprovePairing SELECT (PG + SQLite) to close
race between prune DELETE and code lookup
- Add isValidSenderID regex validation to handleRequest, handleRevoke,
and handleBrowserPairingStatus (prevents log injection / bus poisoning)
- Add slog.Warn on decrypt fallback paths for downgrade detection
- Remove Slack compound senderID|displayName format; all channels now
pass plain senderID with displayName in metadata
Memory files (MEMORY.md, memory/*.md) are stored in PostgreSQL, not on
disk. When the model uses exec to run cat/ls/grep/find on memory paths,
short-circuit with a hint to use memory_search or memory_get instead.
Also append [Source: database, not filesystem] to read_file and
list_files results served from the memory interceptor.
Master tenant's base dir is the global data dir, which contains
the tenants/ cross-tenant isolation root. Without this fix, the
Storage page exposes tenant slugs, file names, and sizes.
- Extract topLevelPath() helper, remove dead code
- Add isHiddenPath() scoped to master tenant only
- Guard handleList, handleRead, handleSize with hidden path checks
- handleSize uses filepath.SkipDir to skip entire tenant subtree
- Add regression tests for list, read, size, subpath, and
non-master tenant isolation
Sandbox: add noexec/nosuid/nodev to tmpfs mounts, remove SETUID/SETGID/CHOWN
caps, add PidsLimit 256 default, keep no-new-privileges from base.
Auth: reject X-GoClaw-User-Id header spoofing in dev mode (no gateway token),
use full 32-byte HMAC for file tokens instead of truncated 16-byte.
Shell: add NFKC Unicode normalization + zero-width character stripping before
deny pattern matching, add 5 export-prefixed env var deny patterns, fix
exemption logic to check per-argument prefix instead of whole-command substring
(prevents bypass via comments while preserving skill store access).
Heavy MCP servers (FastMCP with 80+ tools, OAuth servers) take 3-5s to
start their stdin read loop. Without retries, connectAndDiscover sends
the initialize request immediately after fork/exec, gets EOF, and
permanently fails with "transport closed".
Add a retry loop (4 attempts, 2s/4s/8s backoff) for stdio transports
only. SSE/HTTP transports fail immediately since connection-refused is
definitive. The retry respects context cancellation to avoid blocking
shutdown.
Closes#385
* fix(secure-cli): resolve ambiguous column in LookupByBinary JOIN query (#641)
LookupByBinary uses LEFT JOIN with secure_cli_user_credentials but
SELECT columns lacked table alias prefix, causing PostgreSQL error:
"column reference 'id' is ambiguous (SQLSTATE 42702)"
This silently broke ALL credentialed CLI exec — commands fell through
to regular shell exec without injected env vars.
Fix: use b.-prefixed column names for JOIN queries.
Also add diagnostic logging to lookupCredentialedBinary for future debugging.
* fix(agent): defer warning messages after parallel tool results (#644)
When parallel tool calls trigger loop detection warnings, the warning
messages (role="user") were inserted between tool result messages
(role="tool"). This breaks the Anthropic API when routed through
OpenAI-compatible proxies (e.g. LiteLLM): the proxy groups consecutive
tool messages into a single user message with tool_result blocks, but
an intervening user warning splits the group, causing orphaned
tool_results and HTTP 400 "tool_use ids without tool_result blocks".
Fix: accumulate warning messages during parallel result processing and
append them after all tool results, preserving the consecutive grouping.
Closes#642
* fix(web): allow editing Brave Search API key when masked
---------
Co-authored-by: Viet Tran <viettranx@gmail.com>
Prompt compression (Issue #613):
- Truncate skill descriptions to 200 runes in inline XML (matches mcpToolDescMaxLen)
- Lower skill inline token threshold from 5000 to 3000
- Compact tool descriptions (risk-audited: preserve behavioral hints)
- Compact boilerplate sections: media hint, safety, tool call style, spawn,
self-evolve, skill creation, team workspace
- Fix token estimator to account for description truncation
- Restore safety anti-manipulation clauses dropped during compaction
Team context injection fix (found during prod audit):
- Add IsTeamLead field to LoopConfig/Loop, plumbed from resolver
- Gate team context (TEAM.md, workspace, members) on session type:
leader inbound → team context; member-only inbound → spawn section;
team dispatch → team context
- Filter TEAM.md from context files for member-only inbound chat
- Skip team member DB query when team context not needed
- Rename HasTeam → IsTeamContext for semantic clarity
- Add 8 table-driven tests for team context injection scenarios
Widen settings dialogs from sm:max-w-2xl (672px) to sm:max-w-3xl (768px)
to prevent cramped toggle groups. Move boolean field help text to a
separate line below the switch+label for cleaner readability.
When parallel tool calls trigger loop detection warnings, the warning
messages (role="user") were inserted between tool result messages
(role="tool"). This breaks the Anthropic API when routed through
OpenAI-compatible proxies (e.g. LiteLLM): the proxy groups consecutive
tool messages into a single user message with tool_result blocks, but
an intervening user warning splits the group, causing orphaned
tool_results and HTTP 400 "tool_use ids without tool_result blocks".
Fix: accumulate warning messages during parallel result processing and
append them after all tool results, preserving the consecutive grouping.
Closes#642
Both dialogs now show an amber callout explaining that chat users
(Telegram, Discord, etc.) must be merged into a tenant user via
Contacts page before they can have per-user credentials.
i18n: en/vi/zh for both cli-credentials and mcp namespaces.
Replace raw Combobox + useUserPicker with UserPickerCombobox
component (source="tenant_user") to match MCP user credentials
dialog. Both systems use the same runtime user ID from context.
Use existing useUserPicker hook + Combobox component to search
contacts + tenant_users instead of manual UUID entry.
Supports allowCustom for IDs not in the search results.
Backend:
- Add POST /v1/cli-credentials/check-binary to resolve binary via exec.LookPath
- Security: safeBinaryNameRe regex prevents filesystem probing
- Fix ambiguous column in LookupByBinary LEFT JOIN (secureCLISelectColsAliased)
- Add diagnostic logging to lookupCredentialedBinary
Frontend:
- Replace agent ID text input with Select dropdown (reuse useAgents hook)
- Add Check Binary button next to binary name (auto-fills resolved path)
- Add binary path hint explaining auto-detection from PATH
- Update i18n keys (en/vi/zh) for new UI elements
LookupByBinary uses LEFT JOIN with secure_cli_user_credentials but
SELECT columns lacked table alias prefix, causing PostgreSQL error:
"column reference 'id' is ambiguous (SQLSTATE 42702)"
This silently broke ALL credentialed CLI exec — commands fell through
to regular shell exec without injected env vars.
Fix: use b.-prefixed column names for JOIN queries.
Also add diagnostic logging to lookupCredentialedBinary for future debugging.
- Set dev as default branch, protect main (owner-only merge)
- Add CI trigger for PRs targeting dev
- Add PR template with checklist and branch targeting guide
- Add CONTRIBUTING.md with branch strategy and review criteria
- Update README clone command to use -b main for stable
Traces list Name column now displays agent display name, user label,
source type badge (Direct/Group/Cron/Team/Web), channel badge, and
truncated input preview — making it easy to identify which chat each
trace belongs to.
* feat(web): add Brave Search API key to tools config UI
Expose `web.brave.api_key` in the Web Search settings with password input,
strip masked values on save, and show the field only when Brave Search is
enabled. Add en/vi/zh strings for label, tooltip, placeholder, and managed key.
* refactor(web): extract isSecret to shared utility and fix mobile input font-size
DRY: move duplicated isSecret() from providers-section and tools-web-section
into lib/secret.ts. Add text-base md:text-sm to number inputs for iOS
Safari auto-zoom prevention.
---------
Co-authored-by: viettranx <viettranx@gmail.com>
Remap status reaction emojis for clarity (tool: 🔥→✍, error: 😱→💔)
and reduce overlap between statuses. Add /reactions command to show
emoji legend table to users.
fix(cli-credentials): show existing env keys on edit and support merge/remove
- Expose env_keys on secure CLI list/get and per-user credential list (values stay hidden)
- PUT merges env with stored secrets: empty value keeps existing, removed keys dropped
- Generic error messages to prevent env key enumeration
- Audit logging for user credential set/delete
- DRY: reuse envKeysFromDecryptedJSON helper
- Client-side env key name validation (en/vi/zh i18n)
- Filter Python stdlib modules at scan time to prevent false positives
when the runtime checker fails (e.g. pip:argparse, pip:sys)
- Install pip/npm packages one-by-one instead of batch so partial
success is preserved when one package fails
- Persist missing deps to DB after install via StoreMissingDeps() so
reload reflects actual state instead of stale data
- Use explicit master tenant context in handleInstallDeps for
consistency with rescanAndUpdate()
Previously, finishSummon() and RegenerateAgent() unconditionally overwrote
display_name with the LLM-extracted name from IDENTITY.md. Now if the user
already set a custom name, it is preserved and IDENTITY.md Name field is
synced to match — keeping UI label and agent self-identity consistent.
LLM @mentions are not necessarily Telegram usernames. Auto-wrapping
them in <a href="https://t.me/..."> caused unwanted profile cards.
Keep placeholder protection for italic conversion, restore as plain text.
Replace raw scope strings (e.g. group:nta7-goclaw:-5101523...) with
human-readable group names by fetching delivery targets from
channel_contacts via heartbeat.targets API. Scope labels are also
resolved in the permissions list display.
- Switch default Gemini video model to veo-3.1-lite-generate-preview (50% cheaper)
- Add image_path tool param for image-to-video generation (Gemini Veo only)
- Add resolution and generate_audio as per-provider chain params in UI
- Support dual response formats (Veo 3.1 generatedVideos + Veo 3.0 generatedSamples)
- Gracefully skip image-to-video on providers that don't support it (MiniMax, chat)
- Split create_video into per-provider files (gemini, minimax, chat) for maintainability
Post-merge cleanup for #634 channel health diagnostics:
Backend:
- Extract health types, ClassifyChannelError, mergeChannelHealth,
buildRemediation into internal/channels/health.go
- Fix case-true anti-pattern → default in Slack config
- Remove duplicate connection-refused case in ClassifyChannelError
- Remove unused _ bool param from BaseChannel.setHealth
- Fix snapshot.Enabled == false → !snapshot.Enabled
Web UI:
- Extract channel status utilities to channels-status-utils.ts
- Extract ChannelDiagnosticsCard from channel-detail-page.tsx
- Extract ChannelAttentionPanel from system-health-card.tsx
Desktop UI:
- Extract shared getChannelStatusDisplay() to utils/channel-status.ts
- Add explicit stopped state case for visual consistency with web UI
- Add missing vi/zh locale keys for new health status states
* feat: add channel health diagnostics
* fix(channels): harden startup failure reporting
- sanitize operator-facing failure details instead of exposing raw upstream errors
- keep registered channel instances failed during reload/startup errors and clean up pre-registration failure entries
- stop Telegram warning states from reporting running=true after polling or startup failures
* feat(channels-ui): surface actionable health diagnostics
- expand channel and overview views with richer health summaries, remediation hints, and failure history
- refine the channel dashboard layout so diagnostics are easier to scan instead of reading a wall of text
- keep shared web and desktop channel types aligned with the new health payload
* fix(web): harden channel health diagnostics states
- treat enabled channels without backend status as checking instead of stopped\n- align fallback remediation and overview attention synthesis with channel reality\n- localize diagnostics copy and reject invalid channel detail tabs\n\nRefs #628\nRefs #633
* docs(pr-assets): add channel diagnostics visual review pack
Remove blanket rejection of provider_type on update. Instead, when
provider_type changes, re-validate the existing api_base against the
new type to prevent SSRF via ACP→non-ACP type switch.
This enables provider type changes in onboarding/setup flows while
maintaining security: ACP providers skip URL validation, so switching
from ACP to another type now forces URL re-validation.
* fix(providers): recover Codex text from responses completion events
* fix(providers): honor Codex phase-aware response assembly
* refactor(providers): extract Codex stream state to separate file
Split codexMessageStreamState and related types/methods from codex.go
(457 lines) into codex_stream_state.go to comply with 200-line file
size guideline. Fix redundant nil check in registerMessageItem. Add
clarifying comment on reconcileCompleted edge case.
---------
Co-authored-by: viettranx <viettranx@gmail.com>