Commit Graph
1030 Commits
Author SHA1 Message Date
viettranx 1dd5635f2b docs: document CI/CD pipelines, release flow, and v2.66.0 changelog
- CLAUDE.md: add CI/CD & Releases section with workflow table, tag
  patterns, Docker variants, beta/desktop release commands
- CONTRIBUTING.md: expand Releases section with standard (auto),
  beta (manual tag), and desktop release workflows
- docs/17-changelog.md: add v2.66.0 entry covering IDOR fix, BytePlus
  provider, per-agent grants, beta pipeline, and CI fixes
2026-04-05 11:22:30 +07:00
viettranx 56155ce53e Merge remote-tracking branch 'origin/main' into dev 2026-04-05 11:18:17 +07:00
viettranx c083622f9b fix(ci): skip CI condition in semantic-release for main branch
go-semantic-release auto-detects the default branch from GitHub API
(which is dev), but releases are cut from main. The CI condition
rejects runs on non-default branches. Use --no-ci to bypass this
check since the workflow already gates on push to main.
2026-04-05 11:12:32 +07:00
viettranx af3a5e0297 fix(ci): skip CI condition in semantic-release for main branch
go-semantic-release auto-detects the default branch from GitHub API
(which is dev), but releases are cut from main. The CI condition
rejects runs on non-default branches. Use --no-ci to bypass this
check since the workflow already gates on push to main.
2026-04-05 11:12:05 +07:00
Viet Tran f663fd05fa Merge pull request #696 from nextlevelbuilder/dev
release: merge dev into main
2026-04-05 11:03:22 +07:00
viettranx 52cb4fc3c6 fix(ci): prevent scheduler test hang + add beta release workflow
Scheduler tests hung on CI when t.Fatal fired before close(blockCh) —
defer sched.Stop() called wg.Wait() on goroutines still blocked on
blockCh. Fix: defer close(blockCh) after defer Stop() (LIFO order
ensures blockCh closes first).

Add release-beta.yaml for dev branch beta releases:
- Triggers on v*-beta* and v*-rc* tags
- Builds Linux binaries + Docker images (latest, full variants)
- Creates GitHub prerelease
- Docker tags: version-specific + "beta" rolling tag
- No overlap with release.yaml (branch-triggered, clean semver)

Update release-desktop.yaml:
- Auto-detect prerelease from tag name (beta/rc → prerelease: true)
2026-04-05 11:00:03 +07:00
viettranx 0c320be7b1 merge: resolve main into dev (favor dev for conflicts)
Conflicts in Dockerfile (pnpm install flags) and secure_cli.go
(duplicated aliased column constant) resolved keeping dev versions.
2026-04-05 09:28:28 +07:00
viettranx 8a042ed201 ci: remove redundant docker-publish workflow
release.yaml already builds and pushes Docker images (4 backend variants
+ web) to GHCR and Docker Hub when semantic-release creates a new
version. docker-publish.yaml triggered on v*.*.* tags, causing
duplicate Docker builds on every release.
2026-04-05 09:26:22 +07:00
Duc Nguyen 3039ce93e6 fix(scheduler): drain queued runs in DropOldPolicy test to prevent CI hang (#678)
The test closed blockCh then immediately exited, racing defer sched.Stop()
against scheduleNext(). Under -race on CI, Stop() could call wg.Wait()
while a goroutine was still being submitted, causing a 600s timeout hang.

Fix: wait for queued runs to complete before allowing Stop() to run.

Closes #677
2026-04-05 09:00:30 +07:00
Kai (Tam Nhu) Tran 29afd18e67 fix(ui): prevent first message disappearing in new chat session (#695)
When starting a new chat, loadHistory() races with chat.send — if
history is fetched before the server persists the user message, it
returns empty results and overwrites the optimistic message.

Skip loadHistory() on the "" → newKey session transition since the
optimistic user message is already in state. All other session
transitions (switch, refresh) continue loading history normally.

Closes #694
2026-04-05 08:50:38 +07:00
viettranx b65b23b212 fix(pool): strip stale member names from settings on save
When a pool member provider is deleted/disabled, its name persists in
the owner's extra_provider_names config. Validation already skips stale
refs, but the names accumulated as garbage in the DB.

Now stripStalePoolMembers() runs after validation passes, removing
member names that don't exist in the active provider set before the
settings are written to the database.
2026-04-05 08:47:10 +07:00
e9733e08c4 fix(pool): improve pool management UX — clickable affordance, stale ref handling, managed-by banner (#671)
* 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(docker): resolve @rollup/rollup-linux-arm64-musl missing on Alpine (#647)

Added ui/web/.npmrc with supportedArchitectures for musl+glibc/arm64+x64.
Updated Dockerfile to use --no-frozen-lockfile so pnpm fetches native rollup
binding compatible with Alpine's musl libc. Lockfile still pinned by copy order.

* docs(README): add history stars (#462)

* fix(pool): skip stale pool member references during validation

Unknown pool member references (deleted or disabled providers) now
continue instead of returning an error. Prevents stale data from
blocking provider saves.

Closes #670

* fix(ui): redesign pool member selector and add managed-by banner

Pool member selector:
- Replace invisible outline button with custom element using dashed
  primary border, + icon badge, and "Click to add" hint text
- Visible in both light and dark themes; hover transitions to solid
  border with shadow; active press scales down for tactile feedback

Managed-by banner:
- Show "Pool Defaults" section on pool members with info banner
  explaining which provider owns the pool, plus a Link navigation
- Previously this section was completely hidden with no explanation

i18n: add poolManagedByDescription and clickToAdd keys (en/vi/zh)

* docs: add before/after UI evidence for PR #671

Annotated screenshots with red callout borders marking review areas.
Self-contained HTML comparison report with dark/light theme toggle.

* feat(ui): add pool discovery badges and setup wizard

Replace verbose info banner with per-card "Pool available" badge on
unpooled ChatGPT OAuth providers. Clicking the badge opens a new
pool setup wizard dialog where users select owner, members, and
strategy in one step.

* docs: update UI evidence with pool discovery before/after

* fix(ui): hide pool members from provider selector in agent forms

Pool member providers are managed via the pool owner's routing config.
Showing them as standalone options in the agent Provider dropdown is
confusing — users may select a member directly instead of the owner,
bypassing pool routing entirely.

Filter out providers that exist in ownerByMember from the enabled
providers list in ProviderModelSelect.

* fix(ui): hide pool members from provider selector and add Pool badge

Pool member providers are filtered out of the agent Provider dropdown
in both the Create Agent dialog and the shared ProviderModelSelect
component. Pool owners display a "Pool" badge so users know the
provider routes to multiple accounts automatically.

* docs: add provider selector before/after evidence

* fix: revert stale merge in secure_cli.go and fix hardcoded i18n strings

- Revert secureCLISelectColsAliased: b.agent_id → b.is_global
  (agent_id was dropped in migration 36, stale merge conflict artifact)
- Replace hardcoded "Pool" badge text with t("providers:list.poolBadge")
  in provider-model-select and agent-identity-and-model-fields
- Replace hardcoded "Disabled" with t("common:disabled") in pool wizard
- Add list.poolBadge key to en/vi/zh locale files

---------

Co-authored-by: Viet Tran <viettranx@gmail.com>
Co-authored-by: Plateau Nguyen <nguyennlt.ncc@gmail.com>
Co-authored-by: DNT <ducconit@gmail.com>
2026-04-05 08:43:24 +07:00
viettranx 8aa351b02a fix(ui): refresh providers on dialog open and unblock agent creation
- Refresh provider list when AgentCreateDialog, HeartbeatConfigDialog,
  and AgentAdvancedDialog open (fixes stale cache from 60s staleTime)
- Remove verify-blocking from agent creation flow; verify remains as
  optional manual check but no longer gates the Create button
- Aligns with existing pattern in TeamCreateDialog
2026-04-05 08:29:57 +07:00
viettranx 489572ab63 fix(gateway): add session ownership check to chat.send
chat.send accepted any sessionKey without verifying the caller owns the
session. A non-admin user could write messages into — or inject into an
active run of — another user's session by supplying their sessionKey.

Add the same canSeeAll + UserID guard used by chat.history/inject/abort.
New sessions (Get returns nil) are allowed through so first-message
creation is not blocked.

Closes the last IDOR vector identified during #676 review.
2026-04-05 07:29:01 +07:00
Duc Nguyen e85545dc1b fix(gateway): add session ownership checks to chat.* WS methods (#676)
chat.history, chat.inject, chat.abort, and chat.session.status accepted
any sessionKey without verifying the caller owns the session. A non-admin
user could read, write, or disrupt another user's conversations by
supplying their sessionKey.

Apply the same requireSessionOwner() guard already used by sessions.*
methods: canSeeAll() bypass for admin/owner, sess.UserID match for
regular users. Extracted shared helper to access.go to reduce duplication.

Also fixes: handleSessionStatus i18n compliance (was hardcoded English),
and closes runId-only abort gap (non-admin must provide sessionKey).
2026-04-05 07:25:55 +07:00
Kai (Tam Nhu) Tran 3d9c71a8a2 fix(ui): restore tenant selection state from localStorage on reload (#693)
The Zustand persist migration excluded tenantSelected from the
partialize whitelist, causing it to reset to false on page reload.
This created a redirect loop: require-auth saw the intermediate
state (connected + tenants loaded + tenantSelected=false) and
redirected to /select-tenant before the WS auto-select callback
could fire.

Restore the original localStorage-derived initialization so
tenantSelected is true immediately on rehydration when a tenant
scope was previously saved.

Closes #692
2026-04-05 07:19:11 +07:00
viettranx aa5158d4a2 feat(providers): add BytePlus ModelArk provider with Seedream/Seedance media gen
Add BytePlus ModelArk as a new OpenAI-compatible provider for Seed 2.0
models (chat, vision). Two provider types: standard API and Coding Plan
(separate base URLs, same auth).

Integrate Seedream image generation (sync API) and Seedance video
generation (async polling) into the builtin media tool chain, following
the established DashScope/Gemini patterns.

- Add WithAuthPrefix option to OpenAIProvider for future non-standard auth
- Add ProviderBytePlus/ProviderBytePlusCoding store constants and config
- Register provider from config.json and llm_providers DB table
- Add BytePlus to media chain routing, priority lists, and dispatch
- Create create_image_byteplus.go (Seedream, sync response)
- Create create_video_byteplus.go (Seedance, async poll with 5min timeout)
- Add BytePlus to web and desktop UI provider type dropdowns
- Update provider docs with BytePlus entries

Closes #686
2026-04-04 23:07:42 +07:00
viettranx 7a266aee36 fix(openai): Together-compatible requests with reasoning/stream/vision gating
Port PR #685 fixes for HTTP 400 on Together AI and strict OpenAI-compat hosts,
with additional improvements:

- Gate reasoning_content on assistant history to allowlisted models only
  (OpenAI o-series/GPT-5, DeepSeek, Kimi) — prevents HTTP 400 on Together/Qwen
- Gate reasoning_effort to models that support it (OpenAI reasoning family)
- Skip stream_options for Together endpoints (causes HTTP 400)
- Scope DashScope enable_thinking/thinking_budget to DashScope providers only
- Reorder multimodal parts: text before images (Together/Qwen preferred order)
- Add redacted_thinking tag to sanitization patterns with early-exit guard fix
- Upgrade Together detection from URL-only to URL + providerType + name fallback
  (mirrors dashScopePassthroughKeys pattern for reverse-proxy compatibility)
- Add comprehensive tests for all new behavior
2026-04-04 21:17:47 +07:00
viettranx 156b2dd96c feat(secure-cli): per-agent grants with setting overrides
Replace agent_id column on secure_cli_binaries with is_global flag
and new secure_cli_agent_grants table for per-agent access control
with optional deny_args, deny_verbose, timeout_seconds, tips overrides.

- Migration 000036: create grants table, migrate agent-specific rows,
  dedup binaries, drop agent_id, add is_global
- Store layer: SecureCLIAgentGrantStore interface + PG implementation,
  LookupByBinary with LEFT JOIN grant merge, ListForAgent
- HTTP API: CRUD endpoints at /v1/cli-credentials/{id}/agent-grants
- Agent loop: buildCredentialCLIContext uses ListForAgent for scoped
  system prompt (agents only see authorized CLIs)
- Web UI: grants dialog with card list + inline form, is_global toggle
  replaces agent dropdown, i18n for en/vi/zh
2026-04-04 13:18:57 +07:00
viettranx 29f06cc24b fix(desktop): replace MarkdownRenderer with plain pre in trace span preview
TraceContentPreview was rendering JSON through CodeBlock with oneDark theme,
causing black background spans in light mode. Use plain <pre> with JSON
pretty-print instead — matches the original trace detail rendering.
2026-04-04 08:02:37 +07:00
viettranx 46b93a6b5f fix(desktop): guard slugify against undefined input
watch() can return undefined before form reset() runs, causing
'undefined is not an object (evaluating input.normalize)' crash.
2026-04-04 07:58:53 +07:00
viettranx 3dd3162c26 chore: add language matching instruction to CLAUDE.md, promote go.mod direct deps 2026-04-04 07:57:01 +07:00
viettranx 3466df726c fix(desktop): address code review findings — deduplicate slugify, restore moveFile guard, fix name collision
- H1: rename McpTestResult component import to McpTestResultDisplay (avoid type collision)
- H2: replace local slugify with shared lib/slug import in McpFormDialog
- H3: restore same-path no-op guard in storageService.moveFile
- H4: deduplicate StorageFileContent interface (use service as source of truth)
- M1: replace inline import() syntax with standard import in use-storage
2026-04-04 07:54:25 +07:00
viettranx 6be88f4178 refactor(desktop): react-arch audit — schemas, RHF forms, file splits, services, store cleanup, dir restructure
- Phase 1: add zod, react-hook-form, @hookform/resolvers; create 8 Zod schemas
- Phase 2: migrate 6 form dialogs from useState to useForm+zodResolver
- Phase 3: split 24 files >200L into ~40 focused modules (0 files >300L remaining)
- Phase 4: create services/ layer (8 service files), eliminate direct ws.call in components
- Phase 5: split chat-store into chat-message-store + chat-activity-store
- Phase 6: flatten settings/ directory — move 9 feature subdirs to top-level components/
2026-04-04 07:47:02 +07:00
Tuan-TC 4d9c286aae fix(openai): normalize Mistral tool call IDs (#669)
Fix Mistral tool call ID formatting on the outbound OpenAI-compatible payload.

For provider `mistral` only, normalizes both `tool_calls[].id` and `tool`
message `tool_call_id` to a 9-character hex string (SHA-256 based) before
sending the request. Other providers keep the existing `truncateToolCallID`
behavior unchanged.

- Hash full ID via SHA-256 to avoid prefix-dependent collisions
- Detect Mistral via both provider name and providerType (DB-loaded support)
- Add collision uniqueness and DB provider detection tests
2026-04-04 07:26:04 +07:00
Duc Nguyen 2b1180f59d fix(http): allow localhost URLs for local provider types (Ollama, Claude CLI) (#674)
SSRF validation in validateProviderURL() blocked all localhost/loopback
addresses, preventing local providers like Ollama from being configured
with http://localhost:11434/v1. Introduce localProviderTypes map to skip
SSRF checks for inherently local provider types (ollama, claude_cli, acp).

Closes #673
2026-04-04 07:06:34 +07:00
Songlin Yang 5e680a7f25 fix(188): didn't call Stop when tsnet Server Listen failed (#226) 2026-04-04 06:58:15 +07:00
viettranx bed309a364 fix(ui,kg): fix graph canvas sizing on agent selection
ForceGraph2D was rendering at wrong default dimensions when the
KG page first loaded after selecting an agent. Root cause: early
return for empty state removed the container div from DOM, so
useLayoutEffect couldn't read dimensions on first mount.

Fix: always render container div (flex-1 gives it height from
parent), show empty state inside it. Use useLayoutEffect for
synchronous initial measurement + ResizeObserver for ongoing
changes. Gate ForceGraph2D render behind `ready` flag.

Also extracted memory-documents-table from memory-page (gap closure).
2026-04-03 23:42:43 +07:00
viettranx 4b6538839f refactor(ui): gap closure Phase 2 — extract memory-documents-table
Split memory-page.tsx (314 → 244) by extracting the documents table
into memory-documents-table.tsx.

Remaining 14 files >300 lines assessed — no clean extraction
boundaries found (dense forms, tab routers, DnD contexts).
2026-04-03 23:32:16 +07:00
viettranx f6bca7003a refactor(ui): gap closure — extract form field groups from bloated RHF dialogs
Split 4 RHF form dialogs that grew after Phase 3 Zod refactoring:
- channel-instance-form-dialog: 445 → 292 (+ form-step 196)
- cli-credential-form-dialog: 417 → 277 (+ binary-fields 151, scope-fields 72)
- mcp-form-dialog: 359 → 209 (+ connection-fields 100, settings-fields 95)
- agent-create-dialog: 351 → 165 (+ identity-model-fields 166, description 90)

Skipped: heartbeat-config (already decomposed), provider-form (no 50+ line boundary).

Files >300 lines: 20 → 16. Build passes.
2026-04-03 23:26:26 +07:00
viettranx e962a8ec7a refactor(ui): Phase 5 — Zustand persist middleware + constants split
Stores:
- use-auth-store: manual localStorage → persist() with partialize
  (token, userId, senderID)
- use-ui-store: manual localStorage → persist() with partialize
  (theme, language, timezone, sidebarCollapsed)
- use-team-event-store: manual persist helpers → persist() with
  partialize (last 20 events), onRehydrateStorage seeds counter

Constants:
- Split lib/constants.ts (144 lines) into:
  - lib/routes.ts (46 lines) — route paths
  - lib/timezone-utils.ts (74 lines) — IANA timezone data
  - lib/constants.ts (30 lines) — barrel re-export

ForwardRef: skipped — all UI primitives already use React 19
ref-as-prop pattern.

No behavior changes. Build passes.
2026-04-03 23:08:34 +07:00
viettranx 2ed810b854 refactor(ui): Phase 4 complete — provider-pool adapter eliminates DRY violation
Create adapters/provider-pool.adapter.ts with 6 pure functions that
replace duplicated CodexPoolEntry construction in 3 files:
- provider-overview.tsx (inline poolEntries useMemo)
- agent-codex-pool-page.tsx (35-line buildEntries callback)
- provider-pool-activity-section.tsx (30-line entries useMemo)

Also centralizes resolveProviderAvailability (was copied in 3 places).
2026-04-03 22:59:25 +07:00
viettranx 1313f02cf4 refactor(ui): Phase 4 — adapter layer for data transformations
Create src/adapters/ with 3 adapter files:
- kg-graph.adapter.ts: KGEntity/KGRelation → GraphData (nodes, links,
  degree computation, color mapping, entity limiter)
- chat-message.adapter.ts: moved from chat/hooks/chat-message-transformer.ts
- trace.adapter.ts: buildSpanTree extracted from trace-span-tree-node

Skipped team-task (already in board-utils) and provider-pool (<10 lines,
single call site).

Inline transforms in kg-graph-view.tsx and trace-detail-dialog.tsx
replaced with adapter calls. No behavior changes.
2026-04-03 22:54:40 +07:00
viettranx 7e62623123 refactor(ui): Phase 3 complete — 5 more forms to RHF+Zod (11 total)
Add 5 more Zod schemas: mcp, mcp-credentials, memory,
cron-advanced, team-settings.

Refactor 5 more form dialogs:
- mcp-form-dialog: 15 → 4 useState
- mcp-user-credentials-dialog: 11 → 7 useState
- memory-create-dialog: 10 → 2 useState
- cron-advanced-dialog: 10 → 2 useState
- team-settings-tab: 22 → 1 useState

agent-advanced-dialog skipped (complex typed objects, no field
validation needed — RHF adds indirection with zero benefit).

Total Phase 3: 11 schemas, 11 forms refactored, ~120 useState removed.
2026-04-03 22:50:04 +07:00
viettranx 7a6a944622 refactor(ui): Phase 3 — Zod schemas + React Hook Form for 6 form dialogs
Add zod@4.3.6, react-hook-form@7.72.1, @hookform/resolvers@5.2.2.

Create src/schemas/ with 6 validation schemas:
- agent.schema.ts, provider.schema.ts, cron.schema.ts
- channel.schema.ts, credential.schema.ts, heartbeat.schema.ts

Refactor 6 form dialogs from manual useState chains to RHF + Zod:
- agent-create-dialog: 12 → 2 useState
- provider-form-dialog: 8 → 0 useState
- cron-form-dialog: 7 → 0 useState
- channel-instance-form-dialog: 11 → 6 useState
- cli-credential-form-dialog: 18 → 9 useState
- heartbeat-config-dialog: 19 → 4 useState

Total: 75 useState removed, replaced with type-safe form state.
Async side-effects kept as hooks. No behavior changes.
2026-04-03 22:35:06 +07:00
viettranx 1187146626 refactor(ui): modularize web UI components — Phase 1 & 2
Split 19 large files (300-874 lines) into 37 focused modules.
All files target ≤200 lines for optimal maintainability.

Phase 1 (>400 lines): provider-overview, use-chat-messages,
system-settings-modal, trace-detail-dialog, media-provider-chain-form,
cli-credential-form-dialog, team-settings-tab, skills-page,
heartbeat-config-dialog, mcp-grants-dialog.

Phase 2 (200-400 lines): builtin-tools-page, quota-section,
file-tree, rich-content, session-detail-page, ai-defaults-section,
markdown-renderer, file-viewers, channel-managers-tab.

Also fixed DRY violations: exported shared types (VerifyResult,
ReasoningCapability, ManualEnvEntry), resolved circular import
in media-provider-chain files.

No behavior changes — pure refactoring. Build passes.
2026-04-03 22:12:10 +07:00
viettranx b2fda38e90 feat(kg): node limit selector, remove auto-zoom, graph as default view
- Add node limit selector (100/200/300/500) in graph stats bar
- Graph API fetches 500 (server max), frontend clips by degree centrality
- Remove auto zoom-to-fit on engine stop (was overriding user zoom/pan)
- Keep manual zoom-to-fit button in stats bar
- Default view mode changed from table to graph
2026-04-03 20:09:24 +07:00
viettranx e00bfbc887 feat(kg): increase graph limit to 200 nodes (Canvas handles it) 2026-04-03 19:45:38 +07:00
viettranx 7850ccea01 feat(kg): replace ReactFlow SVG with react-force-graph-2d Canvas renderer
- Replace ReactFlow (SVG, lags at 80+ nodes) with react-force-graph-2d
  (Canvas/WebGL, handles 500+ nodes at 60fps)
- Custom nodeCanvasObject: colored circle by entity type + name label
- Single click: highlight node + neighbors + connected edges with
  directional particles; dim unconnected nodes to 20% opacity
- Double click (timer-based 280ms): open entity detail modal
- Edge labels shown only for selected node's connections (background pill)
- Increase GRAPH_LIMIT from 80 to 150 (Canvas renderer handles it)
- ResizeObserver for responsive container sizing
- Zoom-to-fit button in stats bar
- Auto zoom-to-fit on engine stop
- Dark/light theme support via isDark conditional colors
- Remove ReactFlow + D3 force imports (packages kept in package.json)
2026-04-03 19:41:44 +07:00
Viet Tran cfc69f6f70 fix(tracing): use per-request model/provider overrides in trace spans (#667) (#668)
Trace spans and cost calculations were recording the agent's default
model/provider instead of the effective overridden values from heartbeat
requests. Introduced functional options pattern (spanOption) so all span
emitters resolve the correct model/provider.
2026-04-03 19:38:51 +07:00
viettranx 9e037bca8a fix(kg): fix filter reload, graph perf, stats layout, double-click interaction
- Fix filter not reloading: remove placeholderData from entity list and
  graph queries so data clears immediately on filter change; add key prop
  to force remount on filter change
- Fix graph lag/crash: reduce GRAPH_LIMIT to 80, increase Barnes-Hut theta
  for faster approximation, increase alphaDecay for faster convergence
- Move entity type stats to separate line below toolbar (better readability)
- Change node click: single-click highlights edges, double-click opens detail
- Increase entity list limit to 200 (table view only, graph stays at 80)
2026-04-03 18:42:05 +07:00
viettranx c2bd5349e5 fix(ui,kg): fix credential user picker, KG filters, graph limits, and traversal depth
- Fix MCP/CLI credential dialogs: separate search text from selected user ID
  to prevent keystroke-as-selection bug (use onSelect pattern)
- Fix Combobox: suppress value-sync after selection to prevent label flicker
- Fix MCP dialog: prevent full-page spinner flash when switching users
- Fix KG page filter: replace session-based scope with stats-based user_ids
  from KG entities (more reliable user list)
- Increase KG graph visualization limit from 50 to 200 nodes with optimized
  D3 force layout breakpoints for 100+/150+ nodes
- Cap graph API server-side limit at 500 to prevent unbounded queries
- Increase KG traversal max_depth from 3 to 5 (HTTP API + agent tool)
- Add dedicated Zustand store for KG detail dialog (isolated from main graph)
- Add depth selector UI (2-5 hops) in entity detail dialog
2026-04-03 16:45:00 +07:00
Thieu Nguyen 929dcd20c2 fix(docs): correct team tools reference in README (#651)
- Replace `team_message` with `spawn` for subagent orchestration
- Update team communication description to reflect current implementation
- Align docs with actual tool capabilities

Related: Team coordination updates for agent orchestration
2026-04-03 16:05:04 +07:00
Huy Doan 72025698bb fix(sandbox): support write_file append in FsBridge (#650)
- Add appendMode bool param to FsBridge.WriteFile (>> vs >)
- Pass appendMode through sandbox path in WriteFileTool
- EditTool always uses overwrite mode (false)

Closes #650
2026-04-03 16:03:57 +07:00
viettranx 80c2c0e0ee fix(providers): normalize Ollama api_base URL to include /v1 suffix
- 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
2026-04-03 15:44:39 +07:00
Kai (Tam Nhu) Tran 9050426840 fix(providers): add reasoning field alias for Ollama thinking models (#658)
- 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
2026-04-03 15:31:46 +07:00
viettranx 78a2152053 refactor(http): split provider_models.go into focused modules
- provider_models.go (154L): types, handler, reasoning helpers
- provider_models_fetch.go (191L): remote API fetchers (Anthropic, Gemini, OpenAI, Ollama)
- provider_models_catalog.go (103L): hardcoded model lists

All files under 200-line guideline. No logic changes.
2026-04-03 15:18:54 +07:00
Kai (Tam Nhu) Tran e8579f5e1c feat(providers): add rich Ollama model listing via /api/tags (#659)
- 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
2026-04-03 15:17:08 +07:00
viettranx bde95b70c5 fix(providers): clamp max_tokens to 4096 for Fireworks non-streaming requests
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
2026-04-03 15:04:45 +07:00
viettranx 3206c266bf fix(docker): bump claude-code pin to ^2.1.91
Previous pin ^1.0.47 was stale — latest is 2.x series.
2026-04-03 14:55:44 +07:00