- Update docs/00-architecture-overview.md with TokenCounter and sessions.metadata details
- Update docs/codebase-summary.md with overhead accounting and compaction logic
- Update docs/project-changelog.md with v3.11.0 context-tokens accuracy fixes
After stripBotMention removes the bot's own @mention from content, the LLM
still lacks knowledge of its own platform handle. In multi-bot groups (e.g.
"@botA @botB do X") the other bot's mention remains in the content and the
agent incorrectly treats it as the intended target, replying NO_REPLY.
Capture the bot's first_name from GetMe during channel Start and expose it
alongside the username via a new MetaChannelSelfIdentity metadata key. The
consumer appends the formatted hint ("You are @{username} ({display_name})
on this Telegram channel.") to the agent's extraSystemPrompt so the LLM can
reliably identify itself across single- and multi-bot scenarios.
Falls back to "You are @{username} on this Telegram channel." when the
display name is not available, and no-ops when the username has not been
resolved yet (startup race).
Agents previously saw their own Telegram handle (e.g. "@viet_super_bot")
in user messages and mistook it for a different bot, replying NO_REPLY.
The username was only used for the mention gate, never removed from the
content passed to the LLM.
Slack and Feishu already strip their own bot mentions (handlers_mention.go
stripBotMention, bot_parse.go resolveMentions); Telegram was the odd one out.
Implementation:
- Add stripBotMention helper with leading/trailing word-boundary anchors so
inline matches inside words (e.g. contact@viet_super_bot.com) are not
falsely stripped.
- Apply in handleMessage right after the mention gate, before pairing/media
processing, so history recording for unmentioned messages keeps raw text.
- Restore "[empty message]" placeholder when a message consisting only of
"@botname" becomes empty after stripping.
* refactor(providers): migrate ToolDefinition.Function to pointer + add image response fields
ToolDefinition.Function becomes *ToolFunctionSchema with omitempty so native tool types (image_generation, web_search, etc.) can be declared without a function body. All 9 internal construction sites updated. CleanToolSchemas refactored — function-shape cleaning extracted into cleanFunctionSchema helper, outer pass-through handles native tools.
Added image response fields needed by the next commits: ChatResponse.Images, StreamChunk.Images, ImageContent.Partial (distinguishes partial frames from final images).
* feat(providers): native image_generation for Codex + OpenAI-compat tracks
Codex native (POST /codex/responses): emit image_generation tool object in request tools[] (type, action, model, output_format, partial_images). Handle SSE events response.image_generation_call.partial_image + response.output_item.done (type image_generation_call) + response.completed output[] walk for non-stream. Dedup per item_id. Extend codexSSEEvent/codexItem with output_format, result, partial_image_b64, partial_image_index.
OpenAI-compat (/v1/chat/completions): serialize ToolDefinition{Type:'image_generation'} as {type:'image_generation'} pass-through. Parse choices[0].message.images[] + delta.images[] (data URLs) via new parseDataURL helper; append to ChatResponse.Images.
ProviderCapabilities.ImageGeneration flag; Codex provider + adapter set true. Other providers default false.
* feat(agent,http,store): persist assistant images + tri-level image_generation gate
Agent loop tri-level gate: (provider capability) AND (AgentConfig.AllowImageGeneration, default true, stored in other_config.allow_image_generation) AND (request lacks x-goclaw-no-image-gen header). Gate in loop_tool_filter.go appends ToolDefinition{Type:'image_generation'} only when all three pass. Per-request opt-out parsed in chat_completions.go and propagated via RunRequest.NoImageGen.
Media persistence: persistAssistantImages writes final images (Partial:false) to {workspace}/media/{sha256}.{ext}, returns MediaRef entries, clears inline Images[] from the message. Idempotent on hash, traversal-safe, symlink-guarded. Invoked from pipeline.FinalizeStage via new Deps.PersistAssistantImages callback — covers both stream-final and non-stream paths.
Agent store reads AllowImageGeneration from other_config JSONB with absent/nil = true default (matches V3Flags pattern). No DB migration — code-only default.
* feat(ui/web): image_generation toggle + streaming placeholder + download filename
Composer chip 'Images' visible only when active agent's provider has ImageGeneration capability. Per-agent localStorage persistence via useImageGenToggle hook. When off, sends noImageGen:true to WS chat method (maps to x-goclaw-no-image-gen on upstream HTTP call path).
ActiveRunZone renders a skeleton placeholder while streaming partial_image frames arrive. MediaGallery assigns generated-YYYYMMDD-HHmmss.png as the download filename for hex/UUID PNGs.
i18n keys added to en/vi/zh chat.json: imageGenToggle, imageGenGenerating, imageGenDownloadName. 8 vitest tests for the toggle hook.
* docs: add Image Generation section to codebase-summary + changelog entry
Documents the new native image_generation pipeline across providers layer (Codex + OpenAI-compat), agent gate, media persistence, and web UI surface.
* fix(ui/web): match Codex-routed providers for image_generation toggle
Image-gen toggle visibility was hard-coded to provider id 'chatgpt_oauth' but real Codex-routed agents in production use provider ids like 'cliproxy-codex'. The toggle never rendered.
Replace the Set-has check with a small helper that accepts the literal ids plus any provider string containing 'codex' (case-insensitive). Same logic applied in both chat-input.tsx (composer chip) and chat-page.tsx (streaming placeholder gate).
Verified against a live Codex-routed agent: toggle now renders, noImageGen:true propagates on toggle-off.
* docs(pr-1002): targeted-mode UX evidence report
Captures the UI integration surface for native image_generation against a live Codex-routed agent on the remote dev backend.
Includes: composer toggle chip (rendered), streaming skeleton placeholder, and honest failure-path capture showing the legacy create_image builtin fallback. Self-contained HTML report in .github/pr-assets/1002/index.html.
* fix(permissions): classify sessions.compact as write method
CI RBAC-drift test (TestMethodRole_DriftCoverage_AllProtocolMethodsClassified) was failing because the new sessions.compact method added upstream was not classified in any of isPublicMethod / isAdminMethod / isWriteMethod / isReadMethod.
Sessions compaction mutates session history (compacts messages into summaries), so it belongs with the other sessions.* write methods.
* fix(tests): remove duplicate contains() in integration package
Both tts_gemini_live_test.go and mcp_grant_revoke_test.go declared a file-local func contains(s, substr string) bool with identical bodies, causing 'contains redeclared in this block' at compile time in the integration job.
Replace all call sites with strings.Contains (same semantics, stdlib) and drop the duplicates. No behavior change.
* feat(providers): NativeImageProvider interface + Codex implementation
Defines a provider-level contract (NativeImageProvider.GenerateImage) that OAuth-backed providers can implement to serve image generation without exposing static API credentials. Re-uses the PR's Track A native wire format (POST /codex/responses with image_generation tool, item.result decoding, SSE fallback).
CodexProvider + CodexAdapter implement it. Also adds MediaRef.Prompt field so downstream layers can propagate the generating prompt alongside the asset.
* feat(tools): route create_image through NativeImageProvider for OAuth providers
create_image.callProvider now checks for a NativeImageProvider implementation before the credentialProvider interface. When the provider chain points at a Codex-family provider (no static API key), the tool delegates to the provider's GenerateImage which executes the native ChatGPT Responses API image_generation flow.
Resolves 'provider X does not expose API credentials required for image generation' errors for openai-codex / cliproxy-codex chains. On success the tool embeds the user's prompt as a PNG tEXt 'Description' chunk (file-local pngEmbedPrompt helper to avoid tools→agent import cycle), writes the image to /tmp, and threads the prompt through result.MediaPrompts for downstream MediaRef propagation.
* feat(agent,pipeline): propagate image prompt through MediaRef + PNG tEXt embed helper
Adds EmbedPNGPrompt public helper in internal/agent/png_metadata.go that inserts a tEXt 'Description' chunk (plus 'Software: goclaw') into PNG byte streams before the IEND chunk. Non-PNG inputs are passed through unchanged — the helper never errors on unknown formats.
FinalizeStage wires MediaResult.Prompt (from create_image tool output) onto MediaRef.Prompt so the UI can render the generating prompt alongside the image. Per-image prompt list threaded via pipeline RunState.
* feat(ui/web): show generating prompt as caption under each image in MediaGallery
When a MediaRef carries a prompt, MediaGallery renders it as a muted italic caption (line-clamp-2) beneath the image with the full text in the title tooltip. Caption is hidden when the prompt is absent so non-assistant images (user uploads, legacy data) look unchanged.
MediaItem + session media_refs types extended with an optional prompt field; the chat-message adapter threads ref.prompt through when converting WS payloads to UI state.
* fix(providers/codex): stream:true + instructions for native image_generation
The ChatGPT Responses API on /codex/responses rejects two things hard:
- stream:false → HTTP 400 "Stream must be set to true"
- missing instructions → HTTP 400 "Instructions are required"
buildNativeImageRequestBody now sets stream:true and a purpose-specific instructions string ("Generate an image matching the user's description using the image_generation tool. Return only the image; do not describe it in text."). The existing parseNativeImageSSE path was already in place for stream parsing; routing changed from the non-stream branch to the SSE branch.
Regression assertions added to TestCodexGenerateImage_BuildsNativeRequest so these two fields can't silently regress.
* feat(providers,tools,ui): image_model whitelist (gpt-image-2 default, gpt-image-1.5 legacy)
Replaces the hardcoded "gpt-image-2" literal in buildNativeImageRequestBody with a user-configurable field threaded through NativeImageRequest.ImageModel. The whitelist is enforced by ValidateImageModel which rejects anything outside {gpt-image-2, gpt-image-1.5} with a clear error — prevents silent upstream 400s from model names the Responses API would reject.
create_image.callProvider reads params.image_model from the chain entry and threads it through. Empty / absent falls back to DefaultImageModel (gpt-image-2).
UI: added an 'Image model' select inside the existing openai-codex Settings panel on the Create Image Provider Chain dialog. Options: Default · gpt-image-2 (recommended) and Legacy · gpt-image-1.5. i18n keys in en/vi/zh tools.json under builtin.mediaChain.
Tests: TestCodexGenerateImage covers default/legacy/rejected model cases; TestCreateImageTool_ThreadsImageModel covers params→request threading with empty/legacy/explicit sub-cases.
* fix(tools): raise media chain default timeout to 600s/1 retry for image gen
gpt-image-2 on complex prompts (dense Vietnamese text, infographic layouts) legitimately takes 4–8 minutes to complete. The previous default of 120s × 2 retries routinely died mid-generation with 'context deadline exceeded' — the upstream run was still producing bytes when our ctx cancelled.
Default is now Timeout: 600 / MaxRetries: 1. Retries dropped to 1 because image generation is stateful per upstream run: a mid-flight timeout leaves orphan server work, and retrying a fresh generation doubles cost for no gain. Surface the failure fast so operators can widen the timeout instead.
Operators can still set a tighter value explicitly via the Chain dialog.
* refactor: remove user-facing Images toggle, keep admin-level AllowImageGeneration
The per-request opt-out toggle (composer chip + streaming placeholder + noImageGen header plumbing) was a support footgun — users toggle OFF, forget, then can't generate images and think it's broken. Removed in full.
Kept: AgentConfig.AllowImageGeneration (admin kill-switch, stored in other_config.allow_image_generation, default true). Tri-level gate simplifies to two tiers: provider capability AND agent config allows.
Removed: useImageGenToggle hook, IMAGE_GEN_PROVIDER_IDS set in chat-input, supportsImageGenProvider helper, agentProvider/agentKey props on ChatInput, showImageGenPlaceholder prop on MessageBubble/ActiveRunZone/ChatThread, noImageGen param on use-chat-send, parseNoImageGen in chat_completions.go, NoImageGen on RunRequest, no_image_gen_header_test.go, imageGenToggle/imageGenGenerating i18n keys. Kept imageGenDownloadName — used by MediaGallery for generated-\*.png filename resolution.
* docs(pr-1002): refreshed UX trace + updated codebase notes
Replaces the earlier stealth-state evidence with a clean three-capture trace from a real successful run: inline image + prompt caption, MediaGallery lightbox expansion, Chain dialog with the new Image model dropdown open. Skill-routing rows scrubbed from the capture — they reflect per-agent skill setup, not anything this PR introduces.
codebase-summary + changelog: reflect final state (toggle removed, image_model selector, 600s default chain timeout, gpt-image-2 as quality baseline).
* fix(pipeline): preserve mid-loop image_generation output across iterations
FinalizeStage previously read state.Think.LastResponse.Images, which holds
only the last iteration's response. If the LLM emitted image_generation_call
in iteration N alongside a function_call, then responded text-only in N+1,
the image from N was silently dropped on finalize.
Accumulate final (non-partial) images into state.Observe.AssistantImages
across every iteration via ObserveStage, and source FinalizeStage from the
accumulator instead of LastResponse. Partial streaming frames are filtered
defensively; response.Images is cleared on drain to prevent double-counting
on re-exec.
* test(pipeline): regression coverage for mid-loop image accumulation
Six ObserveStage cases covering image accumulation semantics:
- single-iter image-only, image+tool_call same iter, mid-loop image
surviving text-only final iter, multiple images across iters, partial
frame filtering, nil-response safety.
Two FinalizeStage cases verifying accumulator is the source of truth:
- PersistsFromObserveAccumulator: image in Observe + empty LastResponse
must still be persisted via PersistAssistantImages.
- NoPersistWhenAccumulatorEmpty: no call when no images were emitted.
---------
Co-authored-by: viettranx <viettranx@gmail.com>
MiniMax /v1/get_voice does not return explicit gender/language fields,
but the system-voice naming convention exposes them:
- voice_id prefix `male-*` / `female-*` (legacy Chinese voices)
- voice_name suffix `*_Man` / `*_Boy` / `*_Lord` / `*_Speaker` → male
- voice_name suffix `*_Lady` / `*_Girl` / `*_Belle` / `*_Lass` → female
- voice_name prefix `English_*` / `Chinese_*` / `Japanese_*` /
`Korean_*` / `French_*` / etc. → language
`parseMinimaxLabels` is best-effort — returns nil for unparseable IDs
(e.g. `moss_audio_<uuid>` cloned voices), so the picker shows no badge
rather than a wrong one. The picker already renders `voice.labels` as
badges; nothing to wire on the FE side.
8-case unit test covers the matrix.
Google does not publish gender for the 30 prebuilt Gemini voices —
inclusive design — but does publish a one-word style descriptor
(Bright, Firm, Smooth, Mature, Lively, etc.). Surface it so users can
pick by character.
- `audio.VoiceOption` gains `Labels map[string]string` (provider-
specific descriptors). ElevenLabs already returns labels via API;
Gemini now hardcodes `{"style": "..."}` per voice from the official
catalog.
- Web + desktop `mapCapVoiceToPortal` passes labels through. Both
pickers' `LABEL_KEYS` now include `"style"`, so the existing badge
rendering picks it up — first 1–2 entries shown next to the name.
- TS `VoiceOption` interface (web + desktop) gains optional `labels`.
No gender field is invented for Gemini voices.
Card primitive uses `flex flex-col gap-6` which adds 24px between
header and content regardless of `pb-3` on CardHeader. Override the
flex-gap to `gap-3` on each TTS section so the section number/title
visually attaches to its body.
Affects: provider-setup, credentials-section, voice/model card on
tts-page, test-playground, behavior-section.
Post-review cleanup of Phase 4. Closes Finding #9 properly and corrects
the Finding #13 documentation lie surfaced in the code-review report.
Capability schema:
- Replace `AgentOverridable bool` with `AgentOverridableAs string` on
ParamSchema. Empty string = not overridable; non-empty = the generic
key alias (`"speed"`, `"emotion"`, `"style"`).
- Each provider declaration now carries the alias inline, so the
generic↔native mapping has a single TS-readable source.
Frontend:
- Web `tts-override-block.tsx` drops the inline `GENERIC_TO_NATIVE`
literal and derives the bidirectional adapter from the filtered
capability params (each param self-describes its alias). Adapter
tests rewritten around the new shape.
- Desktop `AgentDetailPanel.tsx` drops the 45-line inline IIFE in
favour of a new `<TtsOverrideFineTune>` component that uses the
same alias-based mapping.
Backend:
- Move `AgentTTSParamsAllowedKeys` + `ValidateAgentTTSParams` to
`internal/audio/agent_params_adapter.go`. HTTP `validate.go` and WS
`gateway/methods/agents_update.go` both delegate, eliminating the
duplicated `{speed, emotion, style}` literal.
Cleanup:
- Delete orphan i18n keys `MsgTtsParamInvalidJSON` and
`MsgTtsParamDependsOn` from `keys.go` + en/vi/zh catalogs (no
in-code references; DependsOn is FE-only, JSON parse failures
already surface via slog).
Documentation:
- `prompt-settings-section.tsx` Finding #13 comment rewritten to
honestly describe the best-effort merge into a fresh local copy of
the cached `otherConfig` prop. Concurrent-tab clobber remains
possible — server-side JSON-merge-patch endpoint planned for v2.
Tests: 9 backend suites (race), web 217/217, desktop build clean,
both Go build tags pass.
Phase 4 — final phase of the TTS params/layout/agent-override plan.
Adds a 3-key allow-list (`speed`, `emotion`, `style`) per agent stored
in `agents.other_config.tts_params`. Backend resolves and merges into
`opts.Params` PER ATTEMPT inside the fallback loop so each provider
sees its own native shape — never the primary's keys when fallback
runs (Finding #1 critical).
Backend:
- `AgentOverridable bool` on `audio.ParamSchema`. UI filter reads this
flag from /v1/tts/capabilities; no separate TS literal mirror —
capabilities API is the single source of truth (Finding #9).
- `audio.AdaptAgentParams(generic, provider)` maps the 3 generic keys
to provider-native paths (e.g. `speed` → `voice_settings.speed` for
ElevenLabs, flat `speed` for OpenAI/MiniMax, dropped for Edge/Gemini).
- `Manager.SynthesizeWithFallbackAdapted` adapts inside the loop so
fallback providers receive correctly-shaped params.
- `manager_auto.go` and `tools/tts.go` Execute do per-attempt adaptation
on the tenant + direct + fallback call sites.
- Drop log bumped to `slog.Info("tts.agent.params.dropped", ...)` for
audit trail when a generic key isn't supported by the active provider.
- Cross-check test asserts every adapter switch case has at least one
capability ParamSchema with `AgentOverridable: true`, and vice versa.
Security (red-team findings):
- Allow-list ENFORCED at write path: `validateAgentTTSParams` in HTTP
`handleUpdate` AND WS `agents_update` rejects any `tts_params` key
outside `{speed, emotion, style}` (Finding #5).
- 64KB body cap on agent PUT via `http.MaxBytesReader` (Finding #6).
- Explicit tenant-scope guard after `agents.GetByID` (Finding #12).
- Concurrent-tab clobber: handleSave merges `tts_params` into a fresh
copy of `otherConfig` rather than reusing stale state (Finding #13).
- Rate-limit verified — RoleAdmin gate sufficient for v1 (Finding #15).
Frontend (web + desktop):
- `TtsOverrideBlock` rewritten: filters capability params to
`agent_overridable === true`, renders via `DynamicParamForm`. Hides
entirely for providers with no overridable params (Edge, Gemini).
- Bidirectional adapter (generic ↔ capability-native form state) so
agent storage stays in generic keys while UI works in native paths.
25 round-trip tests cover all 5 providers.
- Desktop `AgentDetailPanel` gains an inline fine-tune section gated
on `globalProvider`, reusing the desktop `DynamicParamForm`.
i18n: `tts.override.params.title` ("Fine-tune") added to web + desktop
en/vi/zh.
Tests: all 9 backend suites green (race), web 214/214, desktop build
clean, both Go build tags pass.
Phase 3 of the TTS params/layout/agent-override plan.
Reads the `Group` field shipped in Phase 1 to partition each provider's
params into Basic (always visible) and Advanced (collapsed by default,
chevron toggle, count badge). Edge providers with no advanced params
hide the toggle entirely.
- `partitionSchema(schema)` splits params; unknown group values fall
into Basic (forward-compat).
- `evaluateDependsOn` runs against the full shared form state, so
cross-section dependencies (e.g. MiniMax `audio.bitrate` depends on
basic-section `audio.format`) work without scoping.
- Count badge uses `t("tts.advanced.count", {count})` with i18next
pluralization (`_one` for English).
- Desktop mirror with inline SVG chevron (no lucide-react dep).
i18n (web + desktop):
- New `tts.advanced.count` + `tts.advanced.count_one` keys in en/vi/zh.
- `tts.advanced.title` already present; verified.
TS contract: `ParamSchema.group?: string` added to web + desktop
`tts-capabilities.ts`. Plain TS interface — no Zod strict-validator
guard needed (verified via grep).
10 new unit tests cover partition order, unknown-group fallback, count
badge respecting DependsOn, and the cross-group MiniMax case.
189/189 web tests pass.
Phase 2 of the TTS params/layout/agent-override plan.
Web (`ui/web/src/components/voice-picker.tsx`):
- Extract `PortalVoicePicker` (search + VoiceRow + portal dropdown)
from the `DynamicVoicePicker` inner body.
- Introduce `PortalVoice` shape + `mapCapVoiceToPortal` adapter so
capability `VoiceOption` and `useVoices()` results both feed in.
- Dispatcher gains a Gemini route: static voices + provider==="gemini"
→ PortalVoicePicker with the static catalog. Other static providers
(OpenAI) continue to use `StaticVoicePicker` (Radix Select).
- `DynamicVoicePicker` reduced to a thin fetch wrapper around
`useVoices()` + `useRefreshVoices()`.
Desktop mirror (`ui/desktop/frontend/src/components/agents/voice-picker.tsx`):
- Same extraction + Gemini route. OpenAI keeps the Combobox-driven
static path to preserve native UX.
Tests: 9 new cases covering Gemini→Portal dispatch, mapping shape,
absent `preview_url`/`labels`, plus the unchanged ElevenLabs/MiniMax/
OpenAI paths. 179/179 web tests pass.
No backend change. No new i18n keys (existing voice_picker.* reused).
- Change default mode from "off" to "cache-ttl" (enabled by default)
- Increase softTrimRatio from 0.25 to 0.3 to match TS defaults
- Update tests to reflect new default-enabled behavior
- Fix prose body citation in 15-core-skills-system (removed :410 line ref)
- Normalize File Reference schema in 08-scheduling-cron and 10-tracing-observability to Module/Path/Purpose format
- Add missing hint line to 03-tools-system File Reference section
- Clean Go method-call symbols from 01-agent-loop Mermaid diagrams (Router + Resolver)
- 01-agent-loop: replace Go function names in sanitize step details and
mermaid labels with behavioral descriptions; clean resolver resolved
properties, router cache/run-tracking, and team workspace context
variables of symbol references
- 11-agent-teams: compress mailbox 3-action table + Use Cases into
3 narrative paragraphs; replace WorkspaceDir Go code block with
prose description
Replace trailing File Reference sections in 7 docs with 3-4 row
module-level tables. Column schema: Module | Path | Purpose.
Adds 1-line grep hint at end of each section. Also fixes one
body .go:line citation in 05-channels-messaging.md.
Rewrote docs/03-tools-system.md from 987 lines to 519 lines.
Removed Go type/const definitions, function citations, 47-file
path listing, and static regex patterns. Replaced with concept
tables, complete tool inventory cross-checked against live
internal/tools/ registrations, and 3-row module table.
Root CHANGELOG.md is the user-facing single source of truth. docs/17-changelog.md
was an internal verbose log duplicating git history.
Also removed docs/multi-tenant-architecture.bk.md (gitignored, pre-v3 backup
superseded by 23-multi-tenant-architecture.md).
Phase 01 of plans/260419-1344-docs-audit-and-condense.
* fix(vault): prevent vault_read id-namespace collision
vault_search was leaking KG/episodic entity ids into result sets even when
narrow `types` were requested, and callers then passed those ids to
vault_read which returned a generic "document not found". The cause was
threefold:
1. `types` filter was only applied to the vault fan-out; KG and episodic
ran unconditionally. Now gated by shouldFanout(types, key).
2. vault_search output lacked a per-source tool hint. Each result now ends
with " → use <tool>" naming the correct follow-up (vault_read,
knowledge_graph_search, or memory_search).
3. vault_read miss returned "document not found" without checking whether
the id belonged to a foreign namespace. It now probes KG then episodic
and returns a namespace-specific redirect error. Stores are injected
via SetKGStore/SetEpisodicStore, nil-safe, tenant-scoped.
Adds red→green characterization tests plus an end-to-end integration
scenario seeding a vault doc + KG entity with identical basenames.
* test(agent): bump none-mode prompt size budget to 3100
vault_read wiring (#948) added ~95 chars to read_file tool summary,
pushing none-mode prompt from <3000 to 3075 chars. Bump budget to
3100 (~775 tokens) to match the intentional addition.
* test(integration): ensure data_migrations table exists in reset helper
The reset helper runs before RunPendingHooks, but RunPendingHooks is
what normally creates data_migrations. On a fresh CI database the
DELETE fails with 'relation does not exist'. Create the table
defensively so reset works regardless of execution order.
* refactor(vault): per-source id fields + wire episodic into search
Align vault_search output fields with downstream tool input params:
doc_id (vault_read), entity_id (knowledge_graph_search), episodic_id
(memory_expand). Prevents LLMs from pattern-matching a generic `id:`
and misrouting a foreign-namespace uuid into vault_read. Fallback
redirect in vault_read now quotes id + names the correct param so
the LLM can self-correct in one turn.
Also wire stores.Episodic into VaultSearchService (stale comment
claimed pending-impl; PGEpisodicStore has existed and been in use
since v3). Unifies search fan-out with vault_read namespace probe.
---------
Co-authored-by: viettranx <viettranx@gmail.com>
- aria-haspopup/aria-expanded/aria-controls on trigger; role="listbox"
+ aria-label on dropdown; role="option" + aria-selected on rows. Listbox
id from useId() so multiple pickers don't collide.
- Clicking the trigger while open now closes it (toggle), not a no-op.
- Close on window scroll/resize: the fixed-position dropdown doesn't
reflow with the viewport, so pinning it in the wrong place is worse
than closing.
- shell.go: remove commented-out git conflict markers; restore load-bearing
rationale for plain exec.Command vs CommandContext (process-group kill).
- credentialed_exec.go: pass through SYSTEMROOT, TEMP, APPDATA and other
Windows runtime vars that native CLIs (gh, az, aws, npm) require. Missing
SYSTEMROOT breaks networking/registry APIs in most Win32 programs. Switch
to USERNAME (Windows) vs USER (POSIX) per platform convention.
* 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.
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.
* Fix: Destop Lite Windows edition exec failure
The executing of local commands use sh wrongly.
Models occasionally emit NO_REPLY with trailing underscores, quotes,
markdown emphasis, or short explanations (e.g. `NO_REPLY_`,
`**NO_REPLY**`, `NO_REPLY: offline`). Previous word-boundary check
treated `_` as a word char so `NO_REPLY_` slipped through and was
delivered to end users.
Rewrite IsSilentReply to strip decorative wrappers, then prefix-match
case-insensitively. Token is only rejected when glued to another
word character (e.g. `NO_REPLYING`). Intentional divergence from
upstream #19537 — documented in the doc comment.
Remove ToolsWebSection from config page and clean up i18n keys for web
search provider configuration (duckduckgo, exa, tavily, brave). Web search
now configured entirely via builtin_tool_tenant_configs REST API.
Delete tools-web-section.tsx component and update locale catalogs (en/vi/zh).