Root cause: AvailableModels() with a warm persistent cache calls
startPersistentModelRefresh(), which spawns a background goroutine that
writes the refreshed model list into the test's TempDir. When the test
function returns, t.TempDir() runs RemoveAll before that goroutine
finishes its last write — manifesting as:
TempDir RemoveAll cleanup: unlinkat /tmp/.../001: directory not empty
The race window is too small to hit reliably without -cover; coverage
instrumentation slows the goroutine enough to make it reproducible.
Fix (two-part):
1. Add refreshWg sync.WaitGroup to Agent and track every background
refresh goroutine with Add(1)/Done() in startPersistentModelRefresh.
This is a pure bookkeeping addition with zero production behaviour
change.
2. In TestAvailableModels_PrefersPersistentCacheOverDiscoveredModels,
register t.Cleanup(func() { a.refreshWg.Wait() }) immediately after
creating the agent (before AvailableModels is called). Cleanup
functions run LIFO: our Wait fires before t.TempDir's RemoveAll,
guaranteeing the goroutine has finished writing before the directory
is cleaned up.
Verified: go test -count=1 -cover ./agent/opencode/... run 10 times
consecutively — 10/10 PASS, 0 failures.
Co-authored-by: root <root@UYQQVGRAEKQKNQP>
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds optional 'agent' config option under [projects.agent.options] for
opencode. When set, the value is passed as --agent to every 'opencode run'
invocation, enabling plugin-defined agents (e.g. oh-my-openagent's
'Sisyphus - Ultraworker') to work correctly without falling back to the
default agent.
Co-authored-by: Cursor <cursoragent@cursor.com>
ListSessions reads a.cmd and a.workDir without holding a.mu, while
SetWorkDir / SetSessionEnv / StartSession all touch those fields
under the lock. ProjectMemoryFile likewise reads a.workDir directly.
The sibling DeleteSession already uses the canonical
"capture under RLock" pattern; ListSessions and ProjectMemoryFile
were the inconsistent ones.
SetWorkDir mutates a.workDir under a.mu, so /workspace landing on
one goroutine while /list or a memory-file lookup runs on another
races on the string field. Strings carry a (ptr, len) pair, and a
torn read of a different-length string can produce a frankenstring
whose pointer doesn't match its length, leading to memory-safety
failures or empty/garbled paths.
Capture cmd and workDir inside the locked section in ListSessions;
capture workDir in ProjectMemoryFile. No public API or behaviour
change.
Regression: TestOpencodeAgent_WorkDirRaceFreeReaders spawns
concurrent SetWorkDir writers and ListSessions / ProjectMemoryFile
readers under -race. Without the production fix the test trips
"DATA RACE detected"; with the fix the detector stays quiet.
* fix(feishu): prevent card action callback timeout on slow operations
When navigating to /list or similar card actions that call agent.ListSessions(), the synchronous cardNavHandler can exceed Feishu's 3-second callback timeout. This wraps the handler in a goroutine with a 2.5s deadline: fast responses return the card as before; slow responses return a loading toast immediately, then async-refresh the card via the Patch API once complete.
* fix: show session name in /current command with fallback to agent summary
* fix: show session name in /current via SessionTitleProvider fallback to DB query
* fix(opencode): sqlite3 CLI does not support ? placeholders, use inline query
* chore(opencode): use production opencode.db path for SessionTitleProvider
* fix(feishu): fix flaky TestCardAction_NavSlow_ReturnsToastThenRefreshes test
Use channel-based sync (refreshDone) instead of time.Sleep to wait for
async RefreshCard goroutine, eliminating race condition in test.
---------
Co-authored-by: vibecoder <16832299+vibecoder@user.noreply.gitee.com>
Add DeleteSession to the opencode agent by calling `opencode session delete <id>`
via the CLI, enabling single, batch, and range deletion from any connected platform.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
AvailableModels now runs `<cmd> models` first and falls back to
provider-configured models and the built-in list only when the CLI
command fails or produces no output. Results are deduplicated and
sorted for stable /model output.
Co-authored-by: q107580018 <107580018@qq.com>
Made-with: Cursor
* feat(web): add web admin dashboard (React + Tailwind + TypeScript)
Full-featured management UI for CC-Connect instances:
- Token-based auth, dark/light/system theme, i18n (EN/ZH/ZH-TW/JA/ES)
- Dashboard with system status, project overview
- Project management with providers, heartbeat, settings tabs
- Session list and chat interface with Markdown rendering
- Cron job management (create/delete)
- Bridge adapter viewer
- System config viewer, logs, restart/reload controls
- All endpoints aligned with docs/management-api.md
Made-with: Cursor
* feat(web): improve session UI, markdown rendering, and API richness
- Enrich session list/detail API with live status, last_message preview,
agent_type, platform, user metadata, and timestamps
- Add SessionKeyMap() helper to core/session.go for ID-to-key mapping
- Redesign SessionList as responsive grid layout with project filtering,
time-ago display, and last message preview
- Upgrade markdown rendering: rehype-highlight for syntax highlighting,
@tailwindcss/typography for proper prose styling, copy-to-clipboard on
code blocks, GitHub light/dark themes
- Show live/offline session status; disable message input for non-live sessions
- Update management-api.md to document new session fields
- Remove node_modules from git tracking and add to .gitignore
Made-with: Cursor
* chore: add .vite/ to .gitignore
Made-with: Cursor
* fix(opencode): add warn logs when sqlite3 CLI is missing or query fails
The /list command silently showed 0 messages when sqlite3 was not installed
or the DB query failed. Now logs a warning so operators can diagnose the
issue. Fixes#318.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(core): share provider model lookup and stabilize tests
* fix(tests): address PR review follow-ups
---------
Co-authored-by: Deeka Wong <8337659+huangdijia@users.noreply.github.com>
OpenCode (`opencode run --format json`) streams NDJSON events,
similar to the Gemini agent. This wires it up so users can drive
OpenCode from Telegram / Discord / Slack / etc.
- agent/opencode/opencode.go — agent struct, factory, model/mode/provider switching
- agent/opencode/session.go — subprocess lifecycle, NDJSON event → core.Event mapping
- cmd/cc-connect/main.go — register the "opencode" agent