Commit Graph
1337 Commits
Author SHA1 Message Date
viettranx 95bdb23a36 docs(hooks): user guide + example configs + changelog + Make targets
- docs/agent-hooks.md: handler reference, lifecycle events, security model
- examples/hooks/: 5 runnable JSON configs (audit, lint, block-rm-rf,
  Discord notify, context injector)
- docs/17-changelog.md: Wave 0 entry
- Makefile: hooks-specific test targets
- CLAUDE.md: cross-reference
2026-04-16 14:17:47 +07:00
viettranx f90f11a9f9 test(hooks): integration coverage — e2e, rbac, tracing, chaos
Adds cross-cutting integration tests exercising hooks RPC surface:

- hooks_e2e_test: create → dispatcher fire → audit entry → history list
- hooks_rbac_test: tenant-scope guard, master-only global scope enforcement
- hooks_tracing_test: audit entries include handler timing + outcome
- hooks_chaos_test: handler timeout behavior, on_timeout block/allow paths
- hooks_pipeline_test: extends existing pipeline coverage
2026-04-16 14:17:47 +07:00
viettranx 727f345344 feat(web): hooks management UI — list, editor, history, test panel
Adds /hooks page with tenant-scoped list, create/edit dialog (per-handler
config forms for command/http/prompt), history table with diff viewer,
and dry-run test panel that calls hooks.test RPC. Sidebar entry gated by
edition/scope through existing auth store.

i18n: new hooks.json namespace + sidebar.json additions for en/vi/zh.
Routes + query-keys wired; no new runtime deps added.
2026-04-16 14:17:47 +07:00
viettranx 1f560e15d7 feat(gateway): config.defaults RPC exposing SSoT pruning values
Adds config.defaults WS method so UI can render pruning-knob placeholders
that mirror the server's internal fallback values. Exports
agent.DefaultPruningValues() as the single source of truth used by both
the resolver and this RPC — keeps UI in sync without a parallel table.
2026-04-16 14:17:47 +07:00
viettranx aa1b2f026a feat(hooks): WS RPC surface + tenant-scope guards + shared test runner
Exposes agent hooks over WebSocket (hooks.list/create/update/delete/
toggle/test/history) behind existing auth layer. Additions:

- Gateway wiring: buildHookHandlers() factory reused by dispatcher and
  hooks.test runner, so UI test panel exercises production code paths
- GetByID tenant-scope guard: non-master callers only see own tenant +
  global rows (matches List() behavior on both PG and SQLite)
- i18n keys + catalogs (en/vi/zh) for hooks error messages
- Protocol method constants for hooks.* and config.defaults
2026-04-16 14:17:47 +07:00
viettranx 553340b3a9 feat(hooks/prompt): LLM-driven prompt handler with per-turn cap
Adds PromptHandler that runs an LLM over matched hook events (user prompts,
tool calls) to produce decisions (allow/block/modify). Features:

- Budget integration: per-tenant token spend enforced via budget.Store
- Per-turn counter seeded in ContextStage and propagated through state.Ctx
  so PreToolUse fires under the same cap as UserPromptSubmit
- prompt_template required (non-empty) at validate time to prevent
  misconfigured hooks from silently no-oping
- Injection-hardened: tool_input/raw_input are quoted into system prompt
  rather than concatenated
- Resolver abstraction (RegistryResolver) picks model from provider registry
  with SystemConfigs fallback
2026-04-16 14:17:47 +07:00
viettranx ab50e571c5 feat(hooks/budget): per-tenant token spend tracking subsystem
Atomic budget store for prompt-handler LLM spend, with PG and SQLite
implementations. Enforces per-hook token caps to prevent runaway cost
from matcher-bypass scenarios.
2026-04-16 14:17:47 +07:00
Uğur Tafralı 12cfcec59a fix(zalo): correct json field mappings (#924)
Fixes #922 - Zalo OA bot was silently dropping all incoming messages due to JSON field mapping mismatches.

- Align zaloBotInfo/zaloFrom/zaloChat JSON tags with live API (display_name, chat_type)
- Change getUpdates to decode single-object response (API returns one update, not an array)
- Add dedicated pollClient with Timeout=0 + per-call context timeout for long-polling
- Update tests accordingly
2026-04-16 14:17:07 +07:00
Plateau Nguyen 8873548102 feat(pancake): add comment auto-react (like) on Facebook (#919)
Opt-in feature (features.auto_react: true) that automatically likes
a user's Facebook comment via Pancake /likes endpoint when the comment
webhook is received, as an engagement signal.

- Add AutoReact bool to Features config struct
- Add ReactComment() to APIClient (multipart POST to /likes, auth via api_key)
- Add reactCommentAsync() to comment handler; fires before keyword filter,
  independent of comment_reply feature
- Bound with 10-slot semaphore + 5s stopCtx timeout per goroutine
- Guard: only fires on platform=facebook with non-empty convID + msgID
- Startup warning when auto_react=true without webhook_secret (F9)
- Update feature gate: exit only when BOTH auto_react AND comment_reply disabled
- Add 8 tests (ReactComment contract, error body, invalid IDs, handler variants)
2026-04-16 10:14:43 +07:00
JinwooHyunandClaude Opus 4.6 a8c01dbc49 fix(skills): add missing AS alias for COALESCE in embedding search query (#896)
SearchByEmbedding query uses COALESCE(description, '') without an AS
alias, causing PostgreSQL to name the column 'coalesce'. The sqlx scan
struct expects db:"description", so the scan fails with:

  missing destination name coalesce in *[]pg.skillEmbeddingSearchRow

The BackfillEmbeddings query on line 84 already has the correct
AS description alias — this fix makes the search query consistent.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 23:00:11 +07:00
Viet Tran 60ca45ec97 fix(pancake): demote hot-path webhook log to Debug; guard commentID before echo (#916)
- webhook_handler.go: demote per-event "page_id resolution" log from Info to
  Debug. Firing on every webhook was the exact anti-pattern called out in the
  routing-metadata refactor review.
- pancake.go: move reply_to_comment_id guard above rememberOutboundEcho so a
  missing-metadata error does not stamp phantom echoes that would pollute
  inbound echo dedup.
- docs/journals: drop three Pancake journals (not needed in repo).
2026-04-15 22:30:23 +07:00
Plateau Nguyen 85e6f216b5 feat(channels): Pancake comment reply fix + platform select + UI improvements (#904)
* feat(channels): make Pancake platform a required select with 11 options

- Add mandatory platform select field (11 options) to Pancake channel schema
- Add config required validation on channel instance form (create-only)
- Add i18n fieldOptions/fieldConfig for platform in en/vi/zh locales
- Add TDD tests for Pancake config schema (channel-schemas.test.ts)
- Add backend test TestFactoryExplicitPlatformPreserved
- Add slog.Debug for auto-detect path in pancake.go
- Update Platform field comment in types.go for clarity
- Update plan status to completed; add changelog entry

* feat(channels): hide comment_reply for non-social Pancake platforms

Extend showWhen to accept string | string[] values. Apply it to
features.comment_reply so the field only appears for platforms that
support public posts/comments (facebook, instagram, threads, tiktok,
youtube). E-commerce platforms (shopee, lazada, tokopedia) and
messaging-only platforms (line, google, chat_plugin) no longer show
the irrelevant Comment Reply toggle.

Also guard depValue before String() coercion to avoid the "undefined"
literal matching hazard.

* feat(channels): Pancake comment reply fix + platform select + UI improvements

- Fix comment reply: pass message_id (reply_to_comment_id) to Pancake API
  ReplyComment now requires messageID param; guard added for empty ID
- Add SendMessageRequest.MessageID field (omitempty) for reply_comment action
- Platform select: required field with 11 options, showWhen gate for comment_reply
- Webhook Page ID moved to Advanced collapsible section in channel form
  (auto-expands when existing value is configured)
- routing_metadata.go: centralize routing metadata key constants
- config-flatten.ts: flatten/unflatten nested config for form state

Closes: Pancake comment reply returns 'Missing required field: message_id'
2026-04-15 22:26:13 +07:00
viettranx c189160f91 docs: remove journals folder 2026-04-15 21:12:36 +07:00
viettranx 17b0bc5365 test(sqlitestore): regression test for agent Update null→default coercion
Covers PR #897 fix path — verifies Update() coerces null values to schema
defaults for NOT NULL columns (other_config, tools_config, self_evolve,
skill_evolve, is_default, etc.), preventing the "NOT NULL constraint failed"
crash on fresh SQLite DBs when UI sends null for disabled/unset fields.
2026-04-15 21:12:01 +07:00
viettranx fb3552f7e5 fix(hooks/security): add SSRF-safe http client missed by phase 2 commit
Phase 2 (a587231b) advertised SSRF hardening for the http hook handler
but the supporting `internal/security` package was never created, so the
production HTTPHandler fell back to a bare http.Client and admin-config
webhooks could probe loopback / link-local / RFC1918 / cloud-metadata.

- internal/security/ssrf.go: Validate(rawURL) parses + resolves once,
  rejects loopback/link-local/private/multicast/unspecified + 169.254.169.254;
  NewSafeClient(timeout) returns an http.Client whose DialContext pins the
  resolved IP from context (defense-in-depth re-checks the dialed IP) and
  refuses redirects (CheckRedirect = ErrUseLastResponse)
- internal/hooks/handlers/http_handler.go: call Validate before each request,
  attach pinned IP via security.WithPinnedIP(ctx, ip)
- cmd/gateway_managed.go: construct HTTPHandler with
  Client: security.NewSafeClient(10*time.Second)
- tests: 13 new ssrf_test.go cases (every block category + redirect refused
  + dial pinned); existing http_test.go retrofitted with
  security.SetAllowLoopbackForTest helper for httptest.NewServer
- plan: phase-02 Step 2a marked done with reference to this commit

Refs: GitHub Issue #875
2026-04-15 21:12:01 +07:00
viettranx 97f9784443 feat(hooks): phase 2 — handlers, pipeline wiring, ssrf-safe http client
- add command + http handlers (internal/hooks/handlers) with edition gating
  and Authorization-header decryption via crypto.Decrypt
- add SSRF-safe dialer (internal/security/ssrf.go): DNS-once + pinned IP,
  blocks loopback / link-local / private ranges
- wire dispatcher into pipeline stages: ContextStage fires SessionStart
  (async) + UserPromptSubmit (sync); ToolStage fires PreToolUse (sync, COW
  staging for updatedInput) + PostToolUse (async); FinalizeStage fires
  Stop (async)
- bridge delegate events to dispatcher (SubagentStart / SubagentStop)
- construct dispatcher in cmd/gateway_managed.go with both handlers and
  thread it through agent.ResolverDeps + delegateTool.SetHookDispatcher
- unit tests for both handlers + integration tests covering HTTP allow with
  audit row, HTTP block, command Lite-only gate, delegate bridge subscribe
- defer worker pool optimization (Phase 2 Step 3) to Phase 4

Refs: GitHub Issue #875
2026-04-15 21:12:01 +07:00
Duy /zuey/ 20e34deb6a feat(teams): inline rename for team name + description (#571) (#906)
* feat(teams): add inline rename for team name and description (#571)

- Add reusable InlineEditText component (click-to-edit, Enter/Esc/blur)
- Wire inline edit on team detail header (name) and info dialog (description)
- Refactor useTeams hook: updateTeamSettings → updateTeam(teamId, patch)
- Backend: fix teams.update handler to accept *map[string]any for Settings
  so partial updates (name-only / description-only) do not wipe existing
  settings (would silently downgrade v2 → v1)
- Add rename.* i18n keys for en/vi/zh
- Follow mobile rules: text-base md:text-sm, touch target ≥44px on coarse pointers
- Stale-value guard + unmount safety + snapshot-based no-op detection

Closes #571

* fix: remove obsolete team_key reference in agent-identity-conventions

* docs: add journal on rename Agent Teams feature (#571)

Captures the critical data-loss bug caught by code review — backend
teams.update handler silently wiping v2 team settings on partial patches
before the fix (Settings *map[string]any).
2026-04-15 21:11:35 +07:00
248a39ba0c This fixes a data integrity issue affecting fresh installations of Desktop Lite and any environment where agent.other_config and serveral columns are not pre-initialized. This will stop Desktop Lite edtion from saving any agent setting. (#897)
The root cause is missing default initialization of JSON field in update flow, leading to NOT NULL violations in both SQLite and Postgres.

Fix ensures consistent behavior across Lite and Standard deployments.

Co-authored-by: Viet Tran <viettranx@gmail.com>
Co-authored-by: Chunning Ha <cn@ha.td>
2026-04-15 20:49:19 +07:00
viettranx 5040fe4cbe fix(channels): suppress agent-error text on public-facing channels
Rework of #911 against dev with correctness fixes.

Public-facing channels (Facebook, Telegram, Discord, Feishu, WhatsApp,
Zalo OA, Zalo Personal, Pancake, Slack) no longer receive raw internal
error text when an agent run fails — an empty outbound is published
instead so channels still clean up placeholders / typing indicators.
Errors continue to be logged server-side at Error level.

Applied on both hot paths:
  - cmd/gateway_consumer_normal.go   (agent run failure)
  - cmd/gateway_subagent_announce_queue.go (announce lead run failure)

Fixes vs #911:
  - Whitelist uses channels.Type* constants (compile-time safe) instead
    of string literals. The original PR's literals ("zalo", "line") did
    not match any real channel type, so Zalo OA/Personal and Pancake
    leaked errors while "line" was a dead branch.
  - Added TypePancake and TypeSlack which were missing.
  - Empty Content in Facebook.Send and Pancake.Send now short-circuits
    (matching Telegram/Discord/Slack). Previously, suppressed errors
    flowed to Graph/Pancake APIs as empty payloads, triggering 400s and
    channel health degradation.
  - Added table-driven test for isExternalChannel covering all real
    channel types + empty + unknown + legacy "zalo" short form.

Closes #911
2026-04-15 20:40:25 +07:00
badgerbeesandViet Tran 770af6b1c0 Vault: handle empty delete responses (#913)
Co-authored-by: Viet Tran <viettranx@gmail.com>
2026-04-15 17:11:32 +07:00
viettranx ce620c87ef fix(channels): bound Reload Start with timeout; preserve caller ctx for long-lived goroutines
A single hung ch.Start() (e.g. Telegram GetMe stalled on DNS/TCP) blocks
InstanceLoader.mu and wedges every subsequent Reload(), leaving new
channels stuck in "checking" forever.

Wrap Start() in a goroutine bounded by reloadStartTimeout (90s) so Reload
can move on to the next instance. On timeout, Stop the channel in a
bounded window, record a typed failure for the UI, and asynchronously
drain the late-returning Start to detect channels that ignore context
cancellation.

Critically, pass the caller's ctx — not a timeout-wrapped ctx — into
ch.Start. Channels routinely derive long-running goroutines from the
ctx they receive (Telegram's pollCtx is WithCancel(ctx)). A timeout-
wrapped ctx would silently kill polling 90s after a successful Reload.

Add regression tests covering both the hang-then-timeout path and the
caller-context preservation invariant.

Closes #914
2026-04-15 15:27:18 +07:00
viettranx 3fe5ae0b50 feat(hooks): introduce agent lifecycle hooks foundation with fail-closed blocking semantics
Phase 1 of agent-hooks-system establishes:
- Hook types, matchers, and CEL evaluation engine for event-driven extensibility
- Edition-gated command handlers (Lite disabled) with dedup_key-indexed audit log
- Dual-DB store layer (PostgreSQL + SQLite) with transaction boundary enforcement
- Blocking-event semantics: hook execution failures propagate to agent loop, graceful retry
- Tenant-isolated audit trail with per-hook execution context tracing
- Integration tests verifying store correctness and hook dispatch atomicity

Lays groundwork for post-v3.0 phase-02 (per-tenant onboarding hooks).
2026-04-15 14:25:59 +07:00
viettranx 86d564967f fix(agent/title): disable thinking and raise max_tokens for Gemini
Gemini 2.5/3 default to high thinking via OpenAI-compat, consuming the
entire max_tokens budget and truncating titles to a single word (and
adding latency). Title generation is a trivial task that does not
benefit from reasoning, so set OptThinkingLevel="off" and bump
max_tokens from 50 to 256 for headroom across reasoning-capable
providers.

Also update mapGeminiReasoningEffort to forward "off" as "low" (the
minimum effort all Gemini models accept via OpenAI-compat), since not
forwarding causes the server to fall back to "high".
2026-04-15 13:37:37 +07:00
viettranx 5590b94994 fix(providers/openai): gate tool-msg name field to Gemini routes only
Red-team feedback: the new `name` field on role=tool messages was
being sent to ALL OpenAI-compat hosts including Together, Groq, vLLM.
Those hosts typically reject unknown fields with HTTP 400 — Gemini
is the only backend that requires `name` (via FunctionResponse.name
in Google's OpenAI-compat shim). Scope the field to Gemini-only via
supportsThoughtSignature, matching how we already gate
thought_signature forwarding.

Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd
2026-04-15 12:46:48 +07:00
viettranx 666bfbaf3c fix(agent/systemprompt): soften predefined bootstrap to forbid empty-args tool calls
The previous MANDATORY phrasing ("MUST ALSO call write_file for
USER.md and BOOTSTRAP.md before your response ends") was making
small models emit write_file({}) when they had no real user info
to persist, tripping HTTP 400 on Google's Gemini OpenAI-compat shim.

Rewrite the predefined+BOOTSTRAP.md branch with softer "get to know
the user" copy plus explicit hard rules forbidding empty/placeholder
arguments and session-identifier content in USER.md. Safety net is
preserved: the USER PROFILE INCOMPLETE branch nudges the model every
subsequent turn until USER.md is populated.

Sync BOOTSTRAP_PREDEFINED.md template to remove the contradictory
MANDATORY block and replace with matching hard rules.

Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd
2026-04-15 12:42:32 +07:00
viettranx 5689d168e6 test: failing tests for softened predefined bootstrap prompt 2026-04-15 12:41:00 +07:00
viettranx 6544999779 fix(pipeline): treat gemini tool_calls+empty-args as truncation retry
Gemini returns finish_reason="tool_calls" (not "length") even when the
thinking budget exhausted max_tokens before tool arguments could be
emitted. Extend the existing truncation retry condition to detect this
case via an allowlist of mutating tools (write_file, edit, exec,
create_image, read_file) that virtually never legitimately call with
empty args. Nullary tools (datetime, heartbeat) pass through unchanged.

Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd
2026-04-15 12:39:56 +07:00
viettranx 5c229bed59 test: failing tests for scoped truncation retry on mutating tools 2026-04-15 12:38:55 +07:00
viettranx 2fcdbec22c fix(providers/openai): forward thinking_level to Gemini via reasoning_effort
Gemini 3 defaults to "high" thinking budget internally when no
reasoning_effort is set, consuming the full max_tokens budget and
leaving no tokens for tool call arguments on small models. Map
OptThinkingLevel to reasoning_effort on Gemini routes (native endpoint
or proxy-by-model), with "medium" downgraded to "high" per Gemini 3
Preview constraint. Non-Gemini OpenAI-compat hosts unchanged.

Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd
2026-04-15 12:37:43 +07:00
viettranx a9b97fad2d test: failing tests for gemini thinking_level forwarding 2026-04-15 12:36:45 +07:00
viettranx 498abb27d9 fix(providers/openai): include name on tool role messages (Gemini 400 fix)
Google Gemini's OpenAI-compat shim maps role=tool messages to native
FunctionResponse{name, response}; an empty name trips HTTP 400
("Name cannot be empty"). Build raw-ID → tool-name index at serialize
time and populate the name field on role=tool wire messages.

Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd
2026-04-15 12:35:47 +07:00
viettranx 7773fa01b8 test(providers/openai): failing tests for tool msg name field (Gemini 400 repro)
Reproduces trace 019d8f33-2de1-7ab2-9a32-9df92cd610dd where Google's
OpenAI-compat shim returns 400 'Name cannot be empty' on FunctionResponse
because role=tool wire messages omit the 'name' field.

- TestBuildRequestBody_ToolMessageIncludesName: expects 'name' on tool msg
- TestBuildRequestBody_ToolMessageNameLookupUsesRawID: long ToolCallID still resolves
- TestBuildRequestBody_ToolMessageWithoutMatchingCallOmitsName: orphan stays nameless
2026-04-15 12:33:08 +07:00
viettranx 48335d7797 feat(pruning)!: faithful port of TS context pruning + backfill migration
Port goclaw context pruning to match upstream TS design in
openclaw/src/agents/pi-hooks/context-pruning/:

- Opt-in default: prune only when mode="cache-ttl" (was opt-out)
- Remove Pass 0 per-result 30% guard (duplicated Pass 1 with different
  suffix, caused wobble)
- Dedupe double prune call per iteration: PruneStage owns the single
  entry point; loop_history only runs limitHistoryTurns + sanitizeHistory
- Add cache-TTL gate for Anthropic prompt cache: skip prune while cache
  is live, scoped per-session via sync.Map
- Add context.pruned event emission for observability
- Configurable TTL as Go duration string ("5m", "30s")

BREAKING CHANGE: context pruning now opt-in. Add
contextPruning.mode: "cache-ttl" to config.agents.defaults to restore.

Migration 51 / SQLite v19 backfills mode="cache-ttl" for agents with
existing custom context_pruning config missing the mode field, so
previously-configured agents keep pruning after the opt-in flip.
NULL configs stay NULL (new opt-in default applies).

Web UI adds Cache TTL input + toggle wiring mode to cache-ttl/off.
2026-04-15 11:24:57 +07:00
viettranx 8d9a7c127a feat(i18n): desktop tts namespace + STT form keys
Add tts namespace to desktop i18n (en/vi/zh). Extend tools.json with STT form keys including WhatsApp privacy banner. Mirrors web ui/web locale structure.
2026-04-15 11:24:57 +07:00
viettranx 033638533d feat(desktop): port voice picker + STT form from web
Port web voice picker and STT provider form to desktop frontend. Singleton audio preview (hide when preview_url null), whatsapp_enabled toggle with privacy banner. Reuses desktop's custom Combobox primitive. Voice id persists via other_config.tts_voice_id merge in AgentDetailPanel. ToolSettingsDialog routes stt tool to new SttProviderForm.
2026-04-15 11:24:57 +07:00
viettranx 4cd79eacd7 chore(desktop): add Vitest + testing-library
Install Vitest, @testing-library/react, jest-dom, jsdom. Add test + test:watch scripts. Mirrors ui/web vitest config.
2026-04-15 11:24:57 +07:00
viettranx 3b4004946e chore: apply go fix syntax upgrades (min/max, switch patterns)
Apply Go 1.21+ min/max builtins and improved switch pattern matching
across test files and utilities. No functional changes.
2026-04-15 11:24:57 +07:00
viettranx 77dfb97d89 docs: phase 5 channel STT migration
Document unified audio.Manager.Transcribe integration across Telegram,
Discord, Feishu, and optional WhatsApp STT. Update changelog with Phase 5
completion and audio manager consolidation notes.
2026-04-15 11:24:57 +07:00
viettranx 21801e2682 feat(i18n): add voice message fallback key
Add MsgVoiceMessageFallback key for unified voice message handling
across channels (Telegram, Discord, Feishu, WhatsApp). Translations:
EN "[Voice message]", VI "[Tin nhắn thoại]", ZH "[语音消息]".
2026-04-15 11:24:57 +07:00
viettranx 5e0a60da36 feat(channels): migrate STT to unified audio.Manager
Migrate Telegram, Discord, Feishu channels to audio.Manager.Transcribe
for consistent voice message handling. WhatsApp STT opt-in (default off;
Decision 6 — E2E encryption trade-off). Fix tenant-scoped context in
Discord factory. Remove per-channel STT implementations.
2026-04-15 11:24:57 +07:00
viettranx 4db33dc822 docs: sync changelog for STT Phase 04 completion 2026-04-15 11:24:57 +07:00
viettranx 5681ca284d feat(i18n): STT form labels (en/vi/zh)
Add STT provider form i18n translations for en, vi, zh locales:
- stt_provider_form.title
- stt_provider_form.model_label
- stt_provider_form.language_label
- stt_provider_form.api_key_label
- stt_provider_form.test_button

Locales synced across all 3 catalogs.
2026-04-15 11:24:57 +07:00
viettranx 6c229e71c5 feat(ui): STT admin form mirror of TTS form
Add stt-provider-form.tsx React component with form fields for model, language, and API key configuration. Mirror TTS provider form structure for consistency. Integrate into builtin-tool-settings-dialog.tsx. Includes comprehensive unit tests for form validation, submission, and error handling.
2026-04-15 11:24:57 +07:00
viettranx b7002a67d1 feat(audio): wire Scribe STT + legacy bridge in gateway setup
Register ElevenLabs Scribe and proxy STT providers in gateway initialization. Auto-wire legacy STT bridge from existing channel STTProxyURL configs. Implement setupAudioExtras() with RegisterSTT and SetSTTChain calls. Maintains backward compat with per-channel configs.
2026-04-15 11:24:57 +07:00
viettranx 0977952110 feat(db): seed builtin_tools[stt] (PG + SQLite)
Add STT builtin tools seeding:
- PG: migration 000050 (49→50) with stt_scribe and stt_proxy entries
- SQLite: schema version 17→18 with inline seed
- Both: register in gateway_builtin_tools.go with encrypted API key support

Migrations support rollback. Version bumps gated to prevent schema drift.
2026-04-15 11:24:57 +07:00
viettranx e2075dc231 feat(i18n): STT message keys
Add 3 new i18n message keys for STT error handling:
- stt.error.audio_too_large: Audio exceeds 20MB limit
- stt.error.network: Network error during transcription
- stt.error.api: STT provider API error

Includes translations for en, vi, zh catalogs.
2026-04-15 11:24:57 +07:00
viettranx 371dcd3acd feat(audio): legacy STT bridge for per-channel STTProxyURL
Implement legacy STT bridge adapter to support existing per-channel STTProxyURL database configuration. Wraps arbitrary proxy endpoints and auto-registers them via Manager at boot. Includes integration tests for URL validation, authentication, and error handling.
2026-04-15 11:24:57 +07:00
viettranx 8c8849365f feat(audio): Manager STT dispatch with channel override
Add Transcribe() method to Manager with STT provider dispatch and channel-specific overrides. Implements channelSTTOverrides map, WithChannel context propagation, RegisterChannelSTT registration, and resolveSTTChain fallback logic. Includes manager-level unit tests.
2026-04-15 11:24:57 +07:00
viettranx 9f88f12c2a feat(audio): proxy STT wrapper
Implement STT proxy provider that wraps arbitrary HTTP-based STT services. Ported 12 test cases from legacy Telegram STT integration to validate multipart/form-data submission, language/model overrides, and API error handling.
2026-04-15 11:24:57 +07:00
viettranx 37224937c6 feat(audio): ElevenLabs Scribe STT provider
Implement native ElevenLabs Scribe STT provider with POST /v1/speech-to-text endpoint. Supports 20MB audio cap, multipart form submission, configurable model and language. Includes comprehensive unit tests for happy path, audio size validation, API errors, and edge cases.
2026-04-15 11:24:57 +07:00