mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 04:18:44 +00:00
27 KiB
27 KiB
Project Changelog
Significant changes, features, and fixes in reverse chronological order.
2026-05-24
Google Workspace CLI runtime integration
Features
- Added
gwsas a preinstalled Google Workspace CLI in the full runtime image. - Added a SecureCLI
gwspreset with encrypted credential injection fields and guardrails for interactive auth/export commands. - Documented Drive, Gmail, and Calendar command patterns plus live credential smoke-test requirements.
Tests
- Added runtime binary discovery, SecureCLI preset, deny-pattern, and Dockerfile contract coverage for Google Workspace CLI.
Browser cookie sync and config UI
Features
- Added scoped browser cookie sync API, encrypted cookie store, browser runtime cookie application, dashboard browser settings, and a selected-cookie Chrome extension prototype.
Cron SecureCLI credential context
Fixes
- Fixed cron-triggered agent turns so credentialed CLI lookups preserve the explicit tenant user credential identity captured when the cron job is created.
- Kept cron
user_idownership unchanged for group-scoped list/remove behavior while storing credential lookup identity separately in cron payload metadata.
Tests
- Added regression coverage for cron payload credential metadata, legacy payload compatibility, cron scheduler context injection, and SQLite cron persistence parity.
2026-05-22
Messaging debounce hardening
Features
- Added per-agent inbound debounce override via
other_config.inbound_debounce_ms; unset inherits the global gateway setting. - Added Web Chat debounce for rapid text-only
chat.sendcalls usinggateway.inbound_debounce_ms. - Clarified shared inbound debounce behavior in docs and Web UI config help text.
Fixes
- Fixed inbound debounce semantics so
gateway.inbound_debounce_ms=0means no debounce and positive values set the wait window. - Fixed Slack
debounce_delay: 0so it disables per-thread batching instead of falling back to the default.
Tests
- Added regression coverage for channel inbound debounce, Web Chat debounce, media/cancel bypass handling, and Slack debounce config defaults.
CLI P6 backend API unblock
Features
- Added HTTP session branch and history follow endpoints for automation clients.
- Added read-only channel writer permission testing.
- Added split activity-log and runtime-log aggregate endpoints.
Tests
- Added focused HTTP, store, and runtime log coverage for branch/follow, writer test, activity aggregate, and LogTee ring aggregation.
CI/CD: zuey beta deploy
Features
- Added automatic zuey VPS deployment to the
Dev CI and Beta Releaseworkflow after beta prerelease assets are published. - The deploy job triggers the protected gateway upgrade endpoint with the generated
vX.Y.Z-beta.Ntag, waits for upgrade status, and verifies public/health. - Beta prereleases now upload
CHECKSUMS.sha256alongside binary assets.
Fixes
- Updated the host release-upgrade script to support beta asset filenames with a leading
v. - Added checksum fallback to GitHub release asset SHA256 digests when beta releases do not publish
CHECKSUMS.sha256. - Detached gateway-triggered upgrades into a transient
systemd-rununit so stoppinggoclawduring deploy no longer kills the upgrade job. - Allowed stale upgrade
runningstatus records to be superseded after timeout and made the zuey deploy wait loop tolerate transient 502s during restart.
Tests
- Added regression coverage for stale gateway upgrade status recovery.
2026-05-20
HTTP API contract hardening
Fixes
- Fixed memory API
{agentID}handling so agent keys are resolved before storage access and invalid IDs return structured client errors instead of leaking UUID parse failures as HTTP 500. - Allowed system/admin API-key automation to list agents and sessions without an extra
X-GoClaw-User-Idheader while preserving user filtering for non-admin callers. - Added structured
/v1/*not-found responses and a read-onlyGET /v1/sessionscompatibility endpoint for automation clients.
Tests
- Added regression coverage for memory agent-key resolution, invalid memory IDs, session list auth behavior, and structured API 404s.
2026-05-18
Packages: GitHub installer runtime path
Fixes
- Fixed GitHub Releases package installs on bare-metal gateways by defaulting the GitHub binary directory to
{runtimeDir}/bininstead of Docker-only/app/data/.runtime/bin. - The fix covers installs such as
github:nextlevelbuilder/goclaw-cli@v0.4.1on the VPS, where/appis not writable or present.
Tests
- Added default-path regression coverage and made Unix-socket apk helper tests skip cleanly on Windows environments that cannot bind Unix sockets.
Tools: built-in wait delay
Features
- Added a built-in
waittool with bounded millisecond delays, cancellation support, per-agent min/max settings, and runtime policy visibility. - Preserved same-response ordering by making
waita sequential tool-call barrier. - Added Web agent settings controls so per-agent wait limits are not dropped on save.
Tests
- Added focused wait validation, cancellation, policy, builtin seed, config parsing, and tool-stage ordering coverage.
Providers: ChatGPT OAuth GPT-5.5 default
Changed
- Updated ChatGPT Subscription (OAuth) default model, provider model catalog, reasoning metadata, and docs examples to prefer
gpt-5.5.
Tests
- Updated focused provider/model catalog, reasoning, registry, and token-window coverage for
gpt-5.5.
Deployment: Codex CLI service-user auth
Fixes
- Fixed agent-controlled Codex CLI auth on the VPS by ensuring the
goclawsystemd service user has the ChatGPT login auth file under/var/lib/goclaw/.codex/auth.json. - Documented the required service-user check:
sudo -u goclaw -H codex login status.
Packages: npm workspace protocol fallback
Fixes
- Fixed Node package installs for registry packages published with
workspace:dependency ranges, such as@agenttasks/cli. - GoClaw now retries npm
EUNSUPPORTEDPROTOCOL workspace:failures by packing the registry tarball, rewriting workspace dependency ranges to published package versions, and installing the sanitized package folder.
Tests
- Added focused coverage for workspace protocol detection and package.json dependency rewrite behavior.
2026-05-17
Skills: agent manage grants
Fixes
- Added explicit per-agent skill manage grants so agents can edit/delete skills they were authorized to maintain even when
owner_idno longer matches their current actor identity. - Auto-granted manage permission to the creating/publishing agent for new managed skills.
UI
- Show custom skill owner IDs in the Skills table.
- Added Skills page controls to grant agent skill access and edit/delete permission.
Tests
- Added PG/SQLite grant coverage and verified Go builds plus Web UI build.
Agents: provider switch save fix
Fixes
- Fixed agent detail save after switching provider/model when the UI clears stale ChatGPT OAuth routing; typed JSON nulls now coerce to
{}for NOT NULL agent JSON config columns in PostgreSQL and SQLite.
Tests
- Added regression coverage for typed
json.RawMessage(nil)/ JSONnullagent config updates.
Deployment: VPS hybrid GoClaw setup
Operations
- Deployed GoClaw to a VPS using bare-metal
systemdgateway plus Dockerized PostgreSQL 18 pgvector. - Restored the latest private PostgreSQL backup, then upgraded schema from
57to65. - Installed Node.js 22 and Codex CLI on the host; interactive
codex --loginremains manual. - Configured Cloudflare-proxied deployment domain and issued SSL through Certbot/Nginx.
- Added
goclaw-backup-r2.timerto dump PostgreSQL every 6 hours, upload to private Cloudflare R2 storage, and retain the latest 20 backups. - Added deployment runbook in
docs/deployment-guide.md.
Features
- Added a protected gateway upgrade HTTP API that triggers the fixed host-local upgrade script asynchronously.
- Added
scripts/goclaw-upgrade-release.shand installed the VPS copy at/usr/local/bin/goclaw-upgrade-release; dry-run verifies the latest stable server release asset and checksum before deploy.
CI/CD: dev branch beta automation
Features
- Added a
Dev CI and Beta ReleaseGitHub Actions workflow fordevpushes that runs Go and Web UI checks before publishing a beta prerelease. - Added semantic-release-style beta version calculation from Conventional Commits, creating
vX.Y.Z-beta.Ntags and prereleases automatically after tests pass. - Beta automation uploads Linux binaries and publishes
betaDocker image tags for the same release version, with Docker Hub publishing enabled when credentials are configured.
Fixes
- Made beta prerelease publishing independent of a local checkout by passing the repository explicitly to GitHub CLI release commands.
Agent Permissions: channel and workspace matrix
Features
- Added
config.permissions.checkso the UI can preview the effective allow/deny decision for an agent, scope, config type, and user. - Added Permissions UI support for
userId="*"to grant all members in a selected group scope. - Documented the cross-channel agent permission matrix, including Zalo group context writes and workspace/context file boundaries.
Security
- Protected group context file writes now require a real sender with
context_filesor legacyfile_writerpermission. - Group file/context/cron permission-store errors now fail closed instead of silently allowing mutation.
- Backend config permission RPCs validate config types and permission values before storing rules.
Tests
- Added focused store and context interceptor coverage for permission preview and protected group context writes.
CLI Credentials: per-agent env vars under Packages
Features
- Kept
CLI Credentialsas the Packages tab at/packages?tab=cli-credentialsand preserved the legacy/cli-credentialsredirect. - Removed the duplicate standalone
CLI Credentialsitem from the left sidebar. - Added focused coverage for grant env payload semantics and routing contracts.
Fixes
- Made agent-grant environment variable controls easier to find by labeling the row action and adding a visible Environment Variables header inside the grant form.
Security
- Nested agent-grant get/update/delete/reveal routes now verify the grant belongs to the binary ID in the URL.
- Grant creation now validates both the CLI binary and target agent exist in the authenticated tenant before inserting.
- Grant updates now validate env payloads before scalar writes, preventing partial state changes on 400 responses.
- Runtime env precedence is covered: per-user env overrides per-agent grant env for duplicate keys.
- Credentialed exec now fails closed if per-user env JSON is invalid.
- SQLite add-column migrations for replayed schema snapshots now skip already-present columns.
Tests
- Focused backend, store compile, UI unit, and web build validation pass.
- Live PostgreSQL validation skipped because
TEST_DATABASE_URLis not set.
2026-05-16
Agents: per-agent model fallback
Features
- Added per-agent
model_fallbackconfig with ordered provider/model candidates. - Agent advanced config UI now supports enabling fallback, adding backup provider/model pairs, and drag-and-drop ordering.
- Runtime wraps the resolved agent provider with fallback only for normal agent execution. Explicit provider/model overrides bypass the fallback chain.
Migrations
- PG:
000065_agent_model_fallbackaddsagents.model_fallback JSONB NOT NULL DEFAULT '{}'. - SQLite: schema v33 to v34 adds
agents.model_fallback TEXT NOT NULL DEFAULT '{}'.
Tests
- Focused provider, provider resolver, store tests pass. Main app builds in default and
sqliteonlymodes. Web production build passes.
v3.11.3 — 2026-04-26
Fixes
goclaw providers verify— empty body now triggers ping mode (provider registered/reachable check) and returns{valid:true}for registered providers. New--model <alias>flag for chat-verify against a specific model. CLI response parser switched from stale{success, models}to{valid, error}. Onboard auto-verify path fixed identically (was silently printing "FAILED" on every successful provider creation). (#1034)goclaw providers delete— succeeds when referenced byagent_heartbeats. FK changed toON DELETE SET NULL;DeleteProvider(PG + SQLite) now wraps in a transaction that also disables affected heartbeats so the next scheduler tick cannot fire stale config.slog.Warn("heartbeat.provider_cleared")emitted with the disabled count. (#1034)goclaw doctor— provider rows with emptydisplay_namenow render the canonicalnameinstead of a blank line. Query switched fromCOALESCE(display_name, name)toCOALESCE(NULLIF(display_name, ''), name). (#1034)
Migrations
- PG:
000057_heartbeat_provider_fk_set_null— defensive orphan cleanup, drop existing FK by lookup, re-add withON DELETE SET NULL. BriefACCESS EXCLUSIVElock onagent_heartbeatsduring ALTER (sub-second on small tables; heartbeat workers may pause briefly). - SQLite: schema v25 → v26 — full table rebuild for
agent_heartbeatswith new FK clause; explicit 25-column INSERT/SELECT preserves existing rows.idx_heartbeats_duerecreated.
Upgrade notes
- Docker users: MUST pull the new image (
ghcr.io/nextlevelbuilder/goclaw:v3.11.3) AND rungoclaw upgrade(orgoclaw migrate up). Stale images on v3.11.2 will fail boot withschema version mismatch: required 57, current 56after the migration runs. - Bare-metal users: rebuild and run
./goclaw upgrade.
OpenAPI
/v1/providers/{id}/verify—requestBody.required: false;modeldocumented as optional with ping-mode semantics.
2026-04-24
Tools: Config-driven shell deny-groups + read_audio routing fixes
Features
shellDenyGroupsruntime config:config.tools.shellDenyGroups(map[string]bool) allows operators to toggle shell deny-groups (e.g.package_install,env_dump) from the /config Web UI without restarting. Merged with per-agent overrides with per-key agent precedence; multi-tenant invariant preserved. Subscribed tobus.TopicConfigChangedfor live reload.
Fixes
- Credentialed CLI wording scope: "operation requires admin approval" error wording now scoped to
[CREDENTIALED EXEC]marker only — was over-applied to generic shell failures, causing unjustified LLM pre-refusals. - read_audio transcription routing: Fixed silent fallback on missing API credentials for transcription/gemini/openai paths — now hard-errors with clear message. Fixed openai_compat providers (e.g. DashScope) not reaching
/v1/audio/transcriptionsendpoint; moved transcription model check above provider type switch.
Tests
- 6 unit tests for shell deny-groups merge/defensive-copy semantics.
- 3 pub/sub dispatch tests for config reload lifecycle.
- 3 regression tests for read_audio fail-fast paths.
2026-04-22
Providers: Native image generation for Codex + OpenAI-compat
Features
- Codex native track:
CodexProvidernow attaches theimage_generationtool object toPOST /codex/responseswhen the agent permits it. Streamsresponse.image_generation_call.partial_imageintermediate frames +response.output_item.done(typeimage_generation_call) final images; non-stream path walksresponse.output[]. Deduped peritem_id, partial frames emitted asImageContent{Partial:true}for UI progressive render. - OpenAI-compat track:
tools[]serializer passes{type:"image_generation"}entries through natively; response parser readschoices[0].message.images[]/choices[0].delta.images[](data URLs) intoChatResponse.Images. - Media persistence:
internal/agent/media.gopersistAssistantImages()writes final images to{workspace}/media/{sha256}.{ext}, returnsMediaRefentries, clears inline base64. Idempotent on hash. Wired viapipeline.Deps.PersistAssistantImagescallback fromFinalizeStage. Partial frames skipped. - Capabilities + gate:
ProviderCapabilities.ImageGenerationflag, set true on Codex provider. Tri-level gate in agent loop: provider capability ANDAgentConfig.AllowImageGeneration(read fromother_config.allow_image_generation, default true) AND request not opted-out viax-goclaw-no-image-genheader. - Web UI: Composer "Images" toggle chip (visible only when provider supports image gen, per-agent persistence in localStorage). Streaming placeholder skeleton in
ActiveRunZonewhile partials arrive.MediaGalleryassignsgenerated-{timestamp}.pngfilename for assistant-generated PNGs.
Wire format
Implementation is evidence-backed against the native ChatGPT Responses API event shape, not the compat shim shape. Research notes in plans/reports/.
i18n
- 1 UI key (
imageGenDownloadName) inui/web/src/i18n/locales/{en,vi,zh}/chat.json— download filename for generated images.
Tests
- Unit tests across providers (Codex native + OpenAI-compat), agent media persistence, store config. Full test sweep: 2618 pass.
Internal docs
plans/260422-1349-goclaw-chatgpt-image-gen/— plan + phase files.plans/reports/researcher-260422-1414-codex-native-image-events.md— native event schema.
2026-04-20
Pipeline: accurate context token tracking + dynamic compaction
Features
- Session token display from metadata:
sessions.metadatanow carrieslast_prompt_tokensandlast_message_counton finalize. List query reads from metadata; fallback to octet/rune heuristic when absent. Fixes stale token display across session re-opens. - Tool-schema token accounting:
TokenCounter.CountToolSchemas(model, tools)new method counts tool definitions serialized as JSON. Tool-schema tokens included inOverheadTokensat ContextStage. - Dynamic compaction max_tokens: Compaction
max_tokensnow derived fromin/25with clamp[1024, 8192]. Applied to both summarization flow (loop_compact.go) and history sanitization (loop_history_sanitize.go). Replaces static 4096 limit — adapts budget to context size.
Code
internal/store/pg/sessions_list.go— read/writelast_prompt_tokensandlast_message_countin metadata.internal/store/sqlitestore/sessions*.go— parity SQLite store updates.internal/tokencount/token_counter.go—CountToolSchemasinterface method +tiktoken_counter.goimpl.internal/pipeline/context_stage.go— include tool overhead inOverheadTokens.internal/agent/loop_compact.go—dynamicSummaryMaxfunction; apply to compaction call.internal/agent/loop_history_sanitize.go— apply dynamic max to sanitization.
Tests
internal/tokencount/count_tool_schemas_test.go— tool schema token counting.internal/agent/loop_compact_dynamic_max_test.go— dynamic max_tokens clamping.internal/pipeline/context_stage_tool_overhead_test.go— tool overhead integration.internal/store/sqlitestore/sessions_display_tokens_integration_test.go— metadata round-trip.
TTS: timeout tenant-config + Gemini text-only 400 fix
Features & Fixes
- Tenant-config timeout: HTTP
/v1/tts/synthesizeand/v1/tts/test-connectionnow readtts.timeout_msfrom system_configs (default 120s, was hardcoded 15s/10s). Gemini client default bumped 30s→120s for end-to-end alignment. - Gemini text-only error recovery: Gemini preview models occasionally emit 400 "text generation" responses. Fixed by: (1) prepending inline prefix
"Speak naturally: "to single-voice synthesis (multi-speaker untouched), (2) 1-retry with stronger prefix"Read the following text aloud without translating, commenting, or modifying: ", (3) new sentinelgemini.ErrTextOnlyResponsepreserved through fallback chain viaerrors.Join. - Error UX: HTTP returns 422 with localized
MsgTtsGeminiTextOnlymessage. Agent TTS tool branches on sentinel to emit locale-translated ForLLM response. - Model default: Gemini default model bumped
gemini-2.5-flash-preview-tts→gemini-3.1-flash-tts-previewfor higher stability. - UI bounds: TTS timeout input now has
max=300000(5 min).
i18n
- New key
MsgTtsGeminiTextOnlyin EN/VI/ZH catalogs for HTTP 422 + agent-tool ForLLM mapping.
Code
internal/audio/tts.go— read tenant timeout in synthesize handlers.internal/audio/gemini/— inline prefix logic, retry budget, text-only sentinel.internal/tools/tts.go— agent-tool i18n branching on sentinel.internal/http/methods/tts.go— HTTP 422 error mapping.
Tools: send_file — explicit workspace file delivery
Features
send_filetool (internal/tools/send_file.go): dedicated tool for sending existing workspace files as chat attachments. Takespath(required) andcaption(optional). Replaces implicitmessage(MEDIA:path)convention for re-delivering already-created files. MarksDeliveredMediaon success to prevent duplicate delivery.DeliveredMediamark onmessage(MEDIA:)sends (internal/tools/message.go): patched to callIsDelivered/ mark after successful MEDIA upload — closes the cross-tool duplicate-delivery gap where a file sent viamessage(MEDIA:)was not tracked and could be re-sent bysend_file.- Registered as builtin tool in
cmd/gateway_tools_wiring.goand seeded incmd/gateway_builtin_tools.go.
2026-04-22
Codex OAuth pool routing strategy cleanup
Changes
- Removed
primary_firstfrom the public Codex OAuth routing strategy surface. The API, OpenAPI schema, and web UI now expose onlyround_robinandpriority_order. - Legacy
primary_firstandmanualrouting values now normalize topriority_orderon read in the backend store layer. - Activity endpoints now default empty/no-pool responses to
priority_orderinstead ofprimary_first. - Agent overrides that explicitly persist
extra_provider_names: []continue to behave as single-account-only routing after the migration.
Docs
- Updated
docs/02-providers.mdanddocs/18-http-api.mdto describe the two-strategy model and the compatibility migration.
2026-04-21
Webhook fixes (post-review security & idempotency hardening)
Fixes
- K1: Auth context isolation — Webhook auth middleware now resolves secret/HMAC signature before tenant injection (eliminating 401 due to tenant scope applied too early). Unscoped store methods
GetByHashUnscoped+GetByIDUnscopedadded to WebhookStore interface. - K7: IP allowlist enforcement — Inbound webhook calls now check
ip_allowlistfield (CIDR + exact IP) after bearer/HMAC auth. Empty list = allow all (back-compat). Rejected requests return HTTP 403 with logsecurity.webhook.ip_denied. - K8: HMAC replay protection — Per-process nonce cache (key =
sha256(tenant_id + "|" + signature_hex)) with 320s TTL rejects duplicate signatures within the skew window. Single-node caveat documented. Log:security.webhook.hmac_replay. - K2:
request_payloadcanonical shape — All webhook audit rows now store{"body_hash":"<hex64>","meta":{...}}JSON instead of raw bytes. Idempotency checker compares body hashes to detect replays with different payloads (409 Conflict). - K3: Body hash extraction —
extractBodyHash()now parses canonical audit payload structure (previously had parsing bugs leading to missed hash validation). - K9: Invariant test column fix — Webhook tenant isolation test now references correct schema columns (
encrypted_secret,lease_token). - K4: Worker slot drain — Fixed channel leak in webhook worker that prevented slot release on successful claims. Concurrency now scales properly under load.
- K5: Lease-token CAS on UpdateStatus — Stale webhook receivers can no longer overwrite delivery status. Status updates use optimistic concurrency on
lease_token(UUID), ensuring only the owning worker can mark the call done. Prevents duplicate delivery from slow receivers. - K6: HMAC signing key encryption — Raw secret (from which
hmac_signing_key = hex(SHA-256(secret))is derived) is now encrypted at rest via AES-256-GCM usingGOCLAW_ENCRYPTION_KEY. Database compromise no longer = HMAC key compromise. Clients receive plaintext secret once (create/rotate response) and must store securely. - K10: Shared rate limiter instance — Fixed duplicate
webhookLimiterinstantiation causing doubled RPM enforcement. Single limiter now shared across all webhook endpoints.
Migrations
- PostgreSQL: Migration
000057addslease_tokencolumn towebhook_calls. Migration000058addsencrypted_secretcolumn towebhooks. - SQLite: Schema v28 includes both new columns.
Docs
docs/webhooks.md: Section 3 clarified bearer/HMAC auth contract + IP allowlist behavior. New Section 14 explains encryption at rest, key contract, DB compromise boundary.docs/00-architecture-overview.md: Section 12 (Webhook Subsystem) updated to mention lease-token CAS semantics and secret encryption.
Environment
GOCLAW_ENCRYPTION_KEYis now required for webhook HMAC auth. Same key also encrypts LLM provider credentials.
2026-04-19
TTS: Gemini provider + ProviderCapabilities schema engine
Features
- Gemini TTS provider (
internal/audio/gemini/): supportsgemini-2.5-flash-preview-ttsandgemini-2.5-pro-preview-tts. 30 prebuilt voices, 70+ languages, multi-speaker mode (up to 2 simultaneous speakers with distinct voices), audio-tag styling, WAV output via PCM-to-WAV conversion. ProviderCapabilitiesschema (internal/audio/capabilities.go): dynamic per-provider param descriptor. Each provider exposesCapabilities()returning[]ParamSchema(type, range, default, dependsOn conditions, hidden flag) +CustomFeaturesflags. UI readsGET /v1/tts/capabilitiesand renders param editors without hard-coded field lists.- Dual-read TTS storage: tenant config read from both legacy flat keys (
tts.provider,tts.voice_id, …) and new params blob (tts.<provider>.paramsJSON). Blob wins on conflict. Allows gradual migration; no data loss on downgrade. VoiceListProviderinterface refactor: dynamic voice fetching (ElevenLabs, MiniMax) now viaListVoices(ctx, ListVoicesOptions)instead of per-provider ad-hoc methods. Unifiedaudio.Voicetype.POST /v1/tts/test-connection: ephemeral provider creation from request credentials + short synthesis smoke test. Returns{ success, latency_ms }. No provider registration; no config mutation. Operator role required.GET /v1/tts/capabilities: returnsProviderCapabilitiesJSON for all registered providers.
i18n
- Backend sentinel error keys (
MsgTtsGeminiInvalidVoice,MsgTtsGeminiInvalidModel,MsgTtsGeminiSpeakerLimit,MsgTtsParamOutOfRange,MsgTtsParamDependsOn,MsgTtsMiniMaxVoicesFailed) in all 3 catalogs (EN/VI/ZH). - HTTP 422 responses for Gemini sentinel errors now use
i18n.T(locale, key, args...)— locale fromAccept-Languageheader. - ~80 param
label/helpkeys across web + desktop locale files (EN/VI/ZH); parity enforced byui/web/src/__tests__/i18n-tts-key-parity.test.ts.
Security
- SSRF guard on
api_baseoverride for test-connection (validateProviderURL()) — blocks127.0.0.1/localhost/ RFC1918 ranges.
Docs
docs/tts-provider-capabilities.md— schema reference + per-provider param tables + storage format + "Adding a new provider" checklist.docs/codebase-summary.md— TTS subsystem section documenting manager, providers, storage, endpoints.