100 Commits
Author SHA1 Message Date
359198d1f8 Merge pull request #3165 from zhongjunqi7657/feat/mcp-retrieval-frontend
feat(mcp): show tool retrieval diagnostics in clients

Adapted while landing on current master:
- Ported SSE forwarding + rendering to the refactored web layer
  (channel/web/core/channel.py, channel/web/static/js/chat/*, core/i18n.js).
- Fallback (full-injection) is now log-only instead of a client-facing
  diagnostic step; only successful retrieved decisions are surfaced.

Co-authored-by: junqi.zhong <3249888615@qq.com>
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-18 12:25:29 +08:00
zhayujieandcowagent dc6393a6b3 fix: honor configured ASR model instead of hardcoding whisper-1
The voice bridge sent a fixed whisper-1 for every ASR request, ignoring
the user's picked engine. Now it forwards the configured voice_to_text_model
(empty means let the gateway pick its default). Also expose default/doubao/
whisper-1/baidu as ASR options and fix the empty 'default' option rendering
blank in both the web console and the desktop app.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-18 10:55:08 +08:00
zhayujieandcowagent ce3bbce2c0 fix: scope upload preview URLs to the selected agent
Uploads are stored per agent under <workspace>/tmp, but /upload returned a
preview_url without the agent, so /uploads/<name> resolved against the default
agent's tmp. Under any non-default agent the attachment 404'd and rendered as a
broken thumbnail.

Multipart uploads also ignored the agent entirely and always wrote to the
default workspace: the client sends it in the query string (a field present in
both query and body arrives as a list and breaks handlers), while the handler
read the body alone via rawinput("post").

Both upload routes now resolve the agent and name it in the URL they hand back,
matching what the voice ASR and TTS paths already did.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 17:03:49 +08:00
zhayujieandcowagent cc6d8c0feb docs(web): describe the backend layout; list the api/ modules for PyInstaller
README had a Frontend layout section and nothing for the server side. Adds
the counterpart: what web_channel.py is now, what core/ and api/ hold, and
the two things that bite when moving code between them -- patch targets
follow the handler's module, and __file__-derived paths are relative to it.

The desktop build resolves channel.web.web_channel as a hidden import and
would have to follow the new modules through static analysis. These are
namespace packages, which collect_submodules does not reliably walk, so the
spec reads the list off the directory instead.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:40:16 +08:00
zhayujieandcowagent f93420c9d1 refactor(web): move the last handlers out; web_channel.py is the URL table
Auth, the shell pages and their assets, the message routes, and everything
that moves bytes. web_channel.py is now 177 lines: the URL table, build_app,
and the imports that put every handler in the namespace build_app resolves
against.

Two latent NameErrors surfaced once the star import was no longer in the
way. VoiceTtsHandler read ReplyType, which 'from bridge.context import *'
never exported, so a TTS reply raised instead of being checked; it now
imports it from bridge.reply. WebChannel's local-file import read quote,
which stopped resolving when the class moved to core/channel.py earlier on
this branch.

AssetsHandler derives static/ from __file__ and sits a level deeper now,
so it walks up one more directory.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:30:00 +08:00
zhayujieandcowagent 9c2669c223 refactor(web): move the remaining view handlers into channel/web/api/
Skills, memory, knowledge, logs and the updater. Small modules, but they
are what is left of the page-shaped groups.

test_doc_edit's request helpers take the handler class, so they now read
the module off the class instead of naming one: moving a handler between
api/ modules no longer sends them patching an empty namespace.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:23:04 +08:00
zhayujieandcowagent 839107d7e3 refactor(web): move the workspace handlers into channel/web/api/
Browsing, reading and writing workspace and project files. _is_path_allowed
and the roots it checks against go to core instead: the file-serving and
preview routes gate on them too.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:19:16 +08:00
zhayujieandcowagent 48ae89660f refactor(web): move the session handlers into channel/web/api/
Listing and renaming sessions, replaying history, the per-session
permission mode and model, clearing and compacting context. The
step-rendering helpers come along: replaying history is the only thing
that reads stored steps back, since a live turn streams them as they
happen.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:15:13 +08:00
zhayujieandcowagent dd1c996f19 refactor(web): move the agents handlers into channel/web/api/
The roster, core files and avatars, plus the two things that make editing
an Agent more than a config write: _reload_agent_runtime rebuilds the
snapshot the running services hold, _bind_channel_instance re-points the
IM channels the Agent answers on. cloud_client.py imports the first of
those and now asks the module that owns it.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:12:31 +08:00
zhayujieandcowagent c0307ba78d refactor(web): move the scheduler handlers into channel/web/api/
Eleven handlers, one per route as web.py wants, plus the task-store
accessor they all go through and the instance-to-agent lookup only the
create path uses.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:10:30 +08:00
zhayujieandcowagent a9b99f2983 refactor(web): move the channels handlers into channel/web/api/
ChannelsHandler plus the two interactive sign-ins, the WeChat QR code and
Feishu's one-click registration. _live_channel_manager goes to core, not
with them: the agents code binds instances through it too.

The disconnect tests patched _require_auth and friends on web_channel,
which no longer reaches a handler reading them from its own module, and
the _channel_mgr source guard was reading a file the handler had left.
That one now reads web_backend_py(), so it guards the console's server
code rather than one file of it.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:07:25 +08:00
zhayujieandcowagent 73af516902 refactor(web): move ConfigHandler and ModelsHandler into channel/web/api/
ModelsHandler was 2.3k lines, a third of what was left in web_channel.py
and the largest handler in the console by a wide margin. Both are pure
moves; the modules take only the imports their code actually reaches for.

They are re-exported from web_channel.py because build_app() resolves the
URL table against that module's globals, which is also why the tests can
keep importing them from there. What could not stay is the patch targets:
patching channel.web.web_channel.conf no longer reaches a handler that
reads conf out of its own module, so those tests now name the module the
handler actually lives in.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 16:02:11 +08:00
zhayujieandcowagent 9234f114fb refactor(web): untangle ConfigHandler and ModelsHandler
The two handlers reached into each other: ModelsHandler read the vendor
catalogue and the key-masking helper off ConfigHandler, ConfigHandler
called ModelsHandler._legacy_custom_in_use. That is a cycle as soon as
they live in separate modules, which is where they are headed.

All three shared pieces describe a provider's configured values, so they
move to core/providers.py next to the catalogue itself, and both handlers
read them from there. The ConfigHandler.PROVIDER_MODELS alias goes with
them; nothing needed the indirection any more.

Imported by name rather than as a module: 'providers' is a common local
variable in these handlers, and a local binding shadows the module for
the whole function, including the lines above the assignment.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:55:34 +08:00
zhayujieandcowagent 3cbffb3565 refactor(web): move the OpenAI-compatible endpoint to channel/web/api/
Renamed to openai_compat.py: it is the OpenAI-compatible chat completions
endpoint, and a module called openai.py sitting in an import path is a
trap worth avoiding.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:49:44 +08:00
zhayujieandcowagent 776ed0f981 refactor(web): gather the backend modules under channel/web/core/
The split was dropping modules next to chat.html, static/ and templates/,
and the handler split still to come would put a dozen more there. Mirror
the frontend instead: core/ for the shared infrastructure, api/ for the
handlers, leaving web_channel.py as the only Python file at the top.

template.py and channel.py derive paths from __file__ and now sit one
level below the web root, so both walk up one more directory.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:48:06 +08:00
zhayujieandcowagent dc60baa60d refactor(web): move WebChannel into channel/web/channel.py
The channel and the request handlers were in one module because they shared
helpers; with those in _common there was one thing left holding them together,
a WebChannel method reading ConfigHandler.PROVIDER_MODELS, and that table is
now in providers. WebChannel needs nothing else from the handler module.

The URL table moves with the handlers rather than with the channel, to module
scope as URLS next to the build_app() that feeds it to web.py -- the names in
it are resolved out of the namespace they are defined in, so the table has to
live where the handlers do. WebChannel now asks for the app instead of
assembling one, and imports build_app inside startup(): web_channel.py imports
this module, and by the time startup() runs it is always already imported,
since channel_factory only ever reaches the class through it.

web_channel.py keeps re-exporting WebChannel and SERVING, which app.py and
channel_factory resolve by name through this module.

Two tests imported helpers straight out of web_channel and now take them from
_common, where they live.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:31:20 +08:00
zhayujieandcowagent 2d897f04ab refactor(web): lift the shared plumbing into channel/web/_common.py
Everything WebChannel reaches for, the handlers reach for too -- auth, the
addressed agent, upload and workspace paths, the small value types. There is
nothing only the channel needs. That shared surface was the reason the channel
and 76 handlers had to live in one module: a helper could only be shared by
being a neighbour.

_common knows about neither side, so both can import it without a cycle, which
is what makes the rest of the split possible. The names are imported back here
by name rather than as a module, so the handlers still resolve them out of this
module's globals -- the same place web.py resolves handler names from, and
where the tests already patch them.

Pure move: all 46 definitions are byte-identical to the originals.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:25:11 +08:00
zhayujieandcowagent 668f60c232 test(web): let backend source assertions survive the split
Seven test files read channel/web/web_channel.py as text and assert a route
or a helper is still wired up. Moving any of that code out of the file breaks
tests that have nothing to do with the move, which is what makes a large file
hard to split.

web_backend_py() is the backend counterpart of console_js(): it concatenates
every Python file under channel/web/, read off the directory so it cannot
fall behind a new one. The two tests that parse a specific structure -- the
URL table, RootHandler's body -- keep reading the single file they mean, so
they still fail loudly when that structure moves.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:16:55 +08:00
zhayujieandcowagent bebd58f955 refactor(web): move the vendor catalogue out of ConfigHandler
PROVIDER_MODELS is data, but it lived as a class attribute on ConfigHandler
while ModelsHandler read it fifteen times and WebChannel once, so two of the
three readers had to reach into a handler to get at it. It now sits in
channel/web/providers.py; ConfigHandler.PROVIDER_MODELS stays as an alias so
no call site changes.

Pure move: the extracted block is byte-identical to the original.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 15:12:36 +08:00
zhayujieandcowagent 408e98440a fix(feishu): don't respond to group messages that mention only other users
The _is_mention_bot fallback returned True when the bot identity was
unknown, so an app with the broad im:message scope (which receives every
group message) would treat any @-mention as directed at the bot. This let
the bot process and run tools on messages addressed to other people.

- Fail closed: return False (and warn) when neither bot open_id nor
  feishu_bot_name is available, instead of assuming the bot was mentioned.
- Lazily retry the open_id lookup (rate-limited) so a transient startup
  failure no longer leaves the bot blind for the whole process lifetime.
- Log non-200 responses in _fetch_bot_open_id and add a debug log when a
  group message mentioning others is ignored.

Fixes #3168

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 12:22:05 +08:00
zhayujieandcowagent 07c1c63100 Merge pull request #3167 from zkjqd/refactor/split-web-channel
The split itself already landed on master; the branch was force-pushed
afterwards, so this records its final head and takes its English README,
re-applying the master-side changes on top: mtime asset versioning, the
address-bar routing section, and the removal of the -old scaffolding.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 12:07:44 +08:00
zhayujieandcowagent d68644e5e4 fix(console): route the scheduler view at /scheduler, not /tasks
The backend has called this feature scheduler all along (/api/scheduler/...)
and the view only reads as "tasks" because that is its element id. Routing it
at /tasks also spent the obvious name for a task on the thing that schedules
them, and a task is a concept this will want separately.

The view id, the element ids and the switcher stay as they are; only the
path segment moves, so /scheduler and /scheduler/records are what the address
bar shows and /tasks is free again.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 11:41:46 +08:00
zhayujieandcowagent 370d96bdc8 feat(console): make the sidebar logo go home
The top-left mark is the way back to the start of an app everywhere else on
the web, and here it was inert: the only route to the chat view was its
sidebar entry, which is two groups down and scrolls away.

The image is the target, not the header row it sits in -- the name beside it
is a heading, and a click target that wide is one the user hits by accident.
It goes through navigateTo like every other destination, so the address bar
follows, the unsaved-edit guard still applies and the mobile drawer closes.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 11:41:36 +08:00
zhayujieandcowagent f090a28762 docs(web): update the console docs for path routing
The usage line still pointed at /chat, the vendor notes still pointed at
the deleted console.js, and a comment in the shell still described the
router as listening for hashchange. Also documents the two route naming
rules: default tabs stay out of the path, and a tab whose element id reads
as an internal name is routed under the name the UI uses.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 11:25:31 +08:00
zhayujieandcowagent 4816f7a282 fix(tests): keep the console tests independent of collection order
Two module-level caches survive a test and are reachable from one, so the
console tests passed or failed on what the run happened to collect first.

template caches each fragment under its mtime, which no test disturbs, so
a read taken while test_web_chat_content_type had builtins.open patched was
held for the rest of the session and every later render() returned that
test's stand-in markup. Nine modules install a stub web module, none of
which carries the ctx that handlers driven straight from a test read.

Clear the cache around each test and give the stub an empty ctx, which is
the case those handlers are already written for.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 11:25:31 +08:00
zhayujieandcowagent 1fb6b74bf9 fix(console): keep default tabs out of the route, name the evolution tab
Every tabbed view wrote its default tab into the path, so opening the
scheduler landed on /tasks/tasks and settings on /settings/basic. A segment
is there to say which of several tabs is showing, so the one you get by
default has nothing to say: it is now left out, and only /tasks/records,
/settings/models and friends carry one.

A bare path consequently means the default tab rather than "leave the tab
alone", or Back out of /tasks/records would keep showing records and the
settle at the end of routeApply would push the path straight back.

The self-evolution tab routed as /memory/dreams, which is its element id,
not the Self-Evolution it is labelled everywhere the user can see it. Paths
say what the UI says now; ROUTE_TAB_PATHS maps the two, so the element id,
the switcher argument and ROUTE_TABS are untouched, and a hand-written
/memory/dreams still opens the tab and normalises to /memory/evolution.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 11:25:21 +08:00
zhayujieandcowagent 2b93812111 chore(web): drop the -old comparison scaffolding and a dead route
The -old flag served a git-checked-out snapshot of the pre-split console
so the two could be compared side by side. That was worth having while the
split was under review; merged, it is broken by its own success, since
snapshot_legacy.py reads console.js out of master and the split deletes it.
Removing the flag also takes a branch out of ChatHandler.

The URL table gained '/chat' -> RootHandler without losing the '/chat' ->
ChatHandler below it. web.py takes the first match, so the second entry
never ran.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-17 11:25:11 +08:00
zhayujieandcowagent 5fd138cf26 feat: handle agent update and delete actions
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-16 16:55:48 +08:00
zhayujie e9aa9fe1a7 Merge branch 'feat-superagent-multiagent' 2026-09-16 16:21:15 +08:00
zhayujieandcowagent 54f5e02846 fix: strip leading mention when the conversation owner is addressed
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-16 16:19:31 +08:00
zhayujieandcowagent af0c3ec0a9 fix(models): treat qwen3.8-max/flash as hybrid thinking models
qwen3.8-max/flash were wrongly marked thinking_only, which forced the long
xhigh reasoning pass with no way to disable it — the cause of the reported
"first byte in 1.7s but output only after 90s". They are actually hybrid
thinking models, so honor the thinking toggle: enable_thinking=false now
truly disables thinking (and skips preserve_thinking/reasoning_effort).

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-16 12:33:53 +08:00
zhayujie b497921b42 feat: add multiagent team group session service 2026-09-16 12:20:33 +08:00
zhayujieandcowagent 630e89de0e feat: scope workspace requests to the addressed agent
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-15 20:06:45 +08:00
zhayujieandcowagent 723684699a fix: dedupe self-evolution bubble on web console history reload
The self-evolution summary is both pushed live (random evolution_ request id) and persisted to history (no request id), so the existing request-id dedupe couldn't match them and the same learning showed as two identical bubbles after a history reload. Tag evolution bubbles with a content key and skip a persisted copy when the live one is already on screen.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-15 18:15:51 +08:00
zhayujieandcowagent a12295d0b6 fix: resolve reserved id "default" to the default agent instead of falling back on unknown ids
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-15 17:52:41 +08:00
zhayujie 3bf0429055 Merge branch 'feat/web-console-one-click-update' 2026-09-15 10:34:05 +08:00
zhayujie 43edb84b97 feat: simplify web console updater to a compact sidebar menu 2026-09-15 10:33:55 +08:00
zhayujieandcowagent d0e44125f5 fix(desktop): stop backend logs being written to run.log three times
The backend's stdout was persisted to run.log by three writers at once:
Python's own FileHandler, python-manager mirroring stdout for the whole
run, and the console.* patch re-writing every emitted line with a
[MAIN][backend] prefix. Only mirror stdout before the backend is ready
(bootstrap-crash capture, which is why the mirror exists) and route the
per-line forward through process.stdout instead of the patched console,
so steady-state logs land in run.log exactly once.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-14 18:26:02 +08:00
zhayujieandcowagent 1f191022d5 fix: keep create-agent modal footer visible on short screens
Make the create-agent form body scroll when it outgrows the viewport (small laptops, narrow mobile widths) so the create/cancel buttons in the footer stay reachable. The body still overflows visibly while a dropdown is open so its menu isn't clipped.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-14 18:24:28 +08:00
zhayujie be1782cb39 feat: release v2.1.9 2026-09-14 14:36:24 +08:00
zhayujieandcowagent 81012a5c14 fix(desktop): keep net.fetch typing compatible with the Electron 22 Win7 build
net.fetch only exists in Electron 28+, so the Win7 legacy build (Electron 22)
failed tsc with "Property 'fetch' does not exist on type 'Net'". The runtime
guard and http fallback were already correct; access net.fetch through a
custom structural type so it no longer depends on Electron's Net typing (nor
the DOM Response type, absent from the main tsconfig).

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 21:39:52 +08:00
zhayujieandcowagent 0b2b001d21 feat(desktop): add a group-chat entry point on the Agents page
Adds a "Group chat" button next to "Create Agent" (shown when at least two
Agents exist) that navigates to the chat page with ?team=1, which pops the
group-chat picker over the conversation. The flag is cleared once consumed so
back/refresh doesn't reopen it.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 21:22:15 +08:00
zhayujieandcowagent 8b3d5f530e feat: clarify multi-agent picker with a main-Agent row and slimmer solo view
Group chats now list the host first as the "main Agent" (green badge, not
removable) so the owner is visible; solo chats drop the full switch roster and
show only the current Agent, since switching there silently forked a new
conversation. Renames the clashing "default" wording to "main Agent" and
removes the now-dead pickComposerAgent path on web.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 21:22:04 +08:00
zhayujieandcowagent ecf4bccb9b fix: delegate under the speaking guest's identity and let it reach the host
When a user addresses a teammate by name, routing overwrites the turn's
agent_id with the conversation host, so agent_delegate was running under the
host's identity and rejecting the guest handing work to the host as
"delegate to self". Resolve the delegation source from speaker_agent_id
(falling back to agent_id for nested turns) and include the host in the
delegable roster so it matches the prompt roster [host, *members].

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 21:21:26 +08:00
zhayujieandcowagent aa4e3fe1e9 fix: render agent-embedded images for non-default agents on web and desktop
Agent replies embed workspace-relative image refs (e.g. images/x.png) that
only resolved for the default agent. Backend now rewrites relative media refs
to absolute /api/file URLs on the SSE/polling and history paths, and the
desktop markdown renderer recognizes those URLs and prefixes them with the
backend base. Also add a native right-click Save/Copy image menu on desktop.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 20:56:12 +08:00
zhayujie 3fb38f409a Merge branch 'master' of https://github.com/zhayujie/CowAgent 2026-09-13 20:24:26 +08:00
zhayujieandcowagent 0771b752c2 fix(web): improve mobile layout of the console
Use dynamic viewport height (dvh) instead of 100vh so the full-height layout
no longer overflows below the mobile URL bar, let the logs toolbar wrap on
narrow screens, add a small-screen horizontal-overflow guard, and hide the
redundant header logout icon on phones. Desktop layout is unchanged.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 20:21:36 +08:00
zhayujieandcowagent c60436dd9a fix: prevent premature done by restricting run_in_background to long-lived processes
Clarify the bash run_in_background param to be used only for long-lived processes, and tell the image-generation skill to run synchronously.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 19:55:51 +08:00
zhayujieandcowagent 6807c99fb0 feat(desktop): sync the model catalog overlay editor to the desktop app
Bring the desktop Models tab in line with the web console's catalog editor:
a collapsible advanced section in the vendor and custom-provider modals that
edits a provider's model list with the same overlay semantics — prefill the
effective list (presets − removals + overrides), diff against the presets on
save, and persist only overrides + hidden so untouched presets keep following
the code-side metadata.

- New ModelCatalogEditor component with the overlay helpers (toDraftRows,
  normalizeRows, diffAgainstSeed); wired into both modals, widened to lg.
- types: ModelProvider gains hidden/effective, ModelsAction gains save_catalog.
- Save credentials then the catalog (custom providers use the returned id),
  skipping the write when the draft matches the stored overlay.
- Both platforms: the catalog section now always opens collapsed, and the
  redundant "no models yet" empty hint is removed.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 12:10:10 +08:00
zhayujieandcowagent ddfbc663a9 feat(models): make the provider model catalog an overlay on the presets
Rework the per-provider model catalog from a wholesale replacement into an
overlay stored at <workspace>/system/models.json, so it no longer bloats
config.json and untouched presets keep following the code-side metadata.

- Store only overrides + hidden tombstones; the editor prefills the full
  effective list (presets - removals + overrides) so adding one model can no
  longer wipe the rest, and only the diff from the presets is persisted.
- Migrate an early adopter's legacy config.json provider_model_catalog into
  the overlay store once, then strip the old key.
- Share the family budget table via resolve_family_spec so the editor shows
  the same context window / max output the runtime uses (e.g. gpt-6-astra).
- UI: widen the vendor modals, fix the vendor picker being clipped by the
  modal's scroll body, go straight to the custom-provider modal once every
  built-in is configured, and simplify the catalog hint copy.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 11:46:55 +08:00
zhayujieandcowagent 148c4f8e31 fix(desktop): use the round brand logo for the default Agent avatar
The default Agent face used the wide-margin square logo.png scaled to 72%,
so it looked small and, once cropped into the circle, read as an octagon.
Use the same round logo.jpg the login/chat screens and web console use,
filled edge-to-edge, so circle faces are clean discs and chat bubbles fill.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-13 11:35:52 +08:00
zhayujieandcowagent 74cd0feac9 feat: support multiple agents in one runtime with per-agent data isolation
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-12 20:43:01 +08:00
zhayujie bda0505302 Merge branch 'master' of https://github.com/zhayujie/CowAgent 2026-09-12 14:56:36 +08:00
zhayujieandcowagent c407bf6170 fix(qq): auto-reconnect on silently-dead websocket connection
Enable transport-level ping keepalive (ping_interval/ping_timeout) so a
half-open connection triggers the existing _on_close reconnect path, and
add a heartbeat-ACK watchdog that force-closes the socket when the gateway
stops acking. Extend the ws compat layer to strip ping_interval/ping_timeout
on older websocket-client versions so the keepalive knobs never break startup.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-12 14:41:47 +08:00
zhayujieandcowagent 3c6495fd72 fix(desktop): always show the built-in default Agent on the team page
The desktop team page could come up empty - not even the built-in CowAgent
card - so users thought their Agent was gone. The Agent was fine (its
persona lives in ~/cow/AGENT.md and it kept chatting); only the roster list
failed to render.

Two causes, both fixed:

1. Frontend (the actual trigger): on a cold start the local WSGI backend can
   be momentarily busy (an SSE stream or a concurrent poll holding threads),
   and a plain fetch then throws a bare TypeError. getAgents() went through
   the no-retry request() path (postFormData already retries the same hiccup
   for uploads), and agentStore.refresh() treated that failure as a valid
   empty roster and cleared the list. Creating an Agent still worked because
   that POST ran once the backend was idle and then refreshed - which is why
   "create works but the list is empty" looked contradictory.
   Now the roster fetch retries a few times on the transient network error
   (a real HTTP status like 401/404 stays final), and the page is never
   stranded on an empty roster: keep the last good roster, otherwise fall
   back to the same synthesized default the backend uses (id=default,
   name=CowAgent) so the built-in Agent - and its brand logo - is always
   shown.

2. Backend hardening: a config.json saved with a UTF-8 BOM (e.g. edited with
   Windows Notepad / PowerShell) made AgentAdminService._load() raise
   "Unexpected UTF-8 BOM", which surfaced as a failed /api/agents snapshot.
   commit a9dfc54c fixed the main config and web-console read paths but
   missed the roster paths; read config.json/team.json with utf-8-sig at
   agent/admin.py and agent/team.py (write-back stays plain utf-8 so the BOM
   is stripped on the next save).

Single- and multi-Agent installs and legacy backends without /api/agents are
unaffected.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-12 13:57:28 +08:00
zhayujie 7f9356d0e5 Merge branch 'fix/console-channel-start' 2026-09-12 13:31:35 +08:00
zhayujieandcowagent 61ee3bcbcd fix(web): restore app_module lookup in channel _do_stop
PR #3141 dropped the sys/app_module definition in the disconnect path's
_do_stop while switching to _live_channel_manager(), but the next line
still reads _clear_singleton_cache off app_module. That raised NameError
(swallowed by the except), so the singleton cache was never cleared on
disconnect. Restore the app_module lookup so cleanup runs again.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-12 13:31:12 +08:00
zhayujieandcowagent a9dfc54c4e fix: tolerate UTF-8 BOM in config.json and stop pre-login 401 polling
Read config.json with utf-8-sig so a BOM (e.g. saved by Windows Notepad)
no longer breaks config/model updates from the web console; write-back
stays plain utf-8 to strip the BOM on next save.

Gate the /poll and /api/scheduler/runs background pollers behind auth so
a password-protected console no longer spams "401 Unauthorized" before
the user logs in.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-11 10:41:18 +08:00
zhayujieandcowagent 9455e9b9a9 fix(desktop): always show Team entry and use brand logo for the default Agent
The Team nav entry was only shown when the roster already had more than one
enabled Agent, but that entry is the only way to add a second Agent — a
chicken-and-egg trap that left single-Agent installs unable to opt into a
team. Always surface it.

Also render the CowAgent brand logo for the default Agent's avatar instead of
a bare initial disc, so the built-in Agent's face matches the app.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-11 10:14:19 +08:00
zhayujie 6a6a8bc81c docs: update 2.1.8 release notes 2026-09-11 09:50:14 +08:00
zhayujie e3674f8948 docs: update 2.1.8 release notes 2026-09-10 20:35:47 +08:00
zhayujieandcowagent a171038eca feat: support DeepSeek V4.1 Flash (deepseek-flash) as default model
Add deepseek-flash (DeepSeek V4.1 Flash, natively multimodal) and make it the
global default across config templates, provider recommended lists, startup
scripts and docs. Recognize it for thinking mode / reasoning_effort, the large
context+output window, and native vision; keep deepseek-v4-flash / -pro for
backward compatibility.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-10 16:03:01 +08:00
zhayujieandcowagent 201ae31b38 docs: add empty base64 image fix entry (#3133) to v2.1.8
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-10 12:08:52 +08:00
zhayujie 82e6d0d94f Merge branch 'master' into pr-3133
# Conflicts:
#	skills/image-generation/scripts/generate.py
2026-09-10 12:05:36 +08:00
zhayujieandcowagent ead145a7ae feat: add gpt-6-astra model with Responses API tool calling
Route gpt-6-astra tool calls through the Responses API without depending on
the openai SDK, translating requests/streaming back to the chat-completion
shape consumed downstream. Also surface it in the console dropdown, README
and docs, and reorder the recommended list (gpt group ahead of gemini).

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-10 12:03:03 +08:00
zhayujieandcowagent f4dbae8f49 feat: add gemini-3.8-flash model
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-10 12:02:48 +08:00
zhayujieandcowagent a93adb8876 fix: clear engaged model fallback immediately when disabling chat fallback
Disabling chat_fallback in the UI only rewrote config; a model that had already switched to the backup kept its engaged fallback state on the long-lived AgentLLMModel, so the backup model kept answering until a run boundary reset it (or a restart). Now clear the engaged fallback across all live agents right when the fallback is turned off.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-10 11:40:38 +08:00
zhayujieandcowagent c696cc8235 fix: skip empty b64_json and fall back to url in image generation (#3131)
Some backends (e.g. Agnes) return an empty b64_json alongside a valid url in URL output mode, which decoded to a 0-byte file. Check value non-emptiness instead of key presence.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-10 10:54:56 +08:00
zhayujie c4652f837d Merge branch 'master' of https://github.com/zhayujie/CowAgent 2026-09-10 10:05:38 +08:00
zhayujieandcowagent 69e3313d66 docs: update v2.1.8 release notes
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 18:11:23 +08:00
zhayujieandcowagent 607d7b7759 docs: add streaming error fix entry (#3128) to v2.1.8
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 18:11:23 +08:00
zhayujieandcowagent 9ad944dd3d docs: bump v2.1.8 release date to 2026.09.10 in overview and READMEs
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 18:07:11 +08:00
zhayujieandcowagent 688927d29f docs: add streaming error fix entry (#3128) to v2.1.8 and bump release date
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 18:06:33 +08:00
zhayujieandcowagent abacbc6597 docs: point en architecture image to the new jpg asset
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 12:00:07 +08:00
zhayujieandcowagent 28ec2a0b15 docs: add multi-agent content across intro docs and sync translations
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 11:09:18 +08:00
zhayujieandcowagent 84b42f576e fix: resolve webUtils import breaking Win7 desktop build
The Win7 legacy pipeline pins Electron to 22, whose typings don't export
webUtils, so the static named import failed tsc with TS2305. Look up
webUtils off the runtime electron module instead, with an optional-chained
call and existing try/catch fallback.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-09 09:53:26 +08:00
zhayujie 37c0db204c docs: add multi-agent team section in README 2026-09-08 23:32:32 +08:00
zhayujieandcowagent d7bb9c68bd fix: show the @-an-Agent input hint only in team conversations
Solo chats can only @ files, so the composer placeholder now falls back to
the file-only hint and switches to the Agent-or-file hint when the session
has other members (web and desktop). Also relocalize the Agents workbench on
language switch and add the task-name placeholder i18n key.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 23:27:15 +08:00
zhayujieandcowagent dd95c950f7 docs: add v2.1.8 release notes and multi-agent team docs
Add the v2.1.8 release notes in zh/en/ja, plus Agent team, agent_delegate
and subagent tool pages. Translate the multi-agent docs to en/ja to match
the restructured zh version, update navigation and the changelog overview,
and point the docs images to the cowagent-assets CDN.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 23:27:08 +08:00
zhayujieandcowagent 8b513aa3b6 feat: add edit icon to skill cards and render skill frontmatter cleanly
Skill cards now show a pencil icon that opens the skill directly in edit
mode (clicking the card body still opens the read-only view). The skill
viewer parses YAML frontmatter into a titled header block instead of
feeding it to the markdown renderer, which turned the header into an
oversized bold heading. Applies to both web and desktop.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 22:05:56 +08:00
zhayujieandcowagent ef52cccf99 fix: clearer channel disconnect confirmation copy
Drop the misleading "config will be preserved" wording and state the actual
effect: the channel stops receiving messages. Applied to both the web console
(zh / zh-Hant / en) and the desktop app (zh / en).

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 20:56:54 +08:00
zhayujieandcowagent f15d40bc7f fix: keep team chat history in sync and clean up speaker labels
In a group chat every Agent cached its own message list and only restored
it on first init, so a teammate missed later turns spoken by someone else
(and the host missed guest replies). Reload the shared transcript before
each turn in a team conversation.

Replay a colleague's past reply as a `Name(@id):` user turn instead of an
`[Name]` assistant prefix, so the model has nothing to copy into its own
reply, and strip any speaker label it still echoes. Tell it in the prompt
not to prefix its reply or expose teammate ids to the user.

Also refresh team.mdx with real screenshots.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 20:53:08 +08:00
zhayujieandcowagent 09893c1e9f feat(desktop): confirm before disconnecting a channel
The desktop app disconnected a channel the instant the button was clicked,
with no confirmation — unlike the web console, which has always asked first.
Add a confirm dialog (via the shared askConfirm store) before disconnect, and
the channels_disconnect_confirm i18n string it needs.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 20:52:33 +08:00
zhayujieandcowagent 483f486c14 fix: make channel disconnect stick and put WeChat channels first
Disconnecting a channel had no effect on both the web console and the
desktop app. In multi-Agent mode a legacy channel (enabled via config.json
channel_type) is folded into channel_instances by bootstrap_legacy_instances,
which runs on every team.json write. Removing the instance record alone was
immediately undone: the next write re-materialized it straight from
channel_type, so the card came right back.

- Prune the type from channel_type when disconnecting its last instance, so
  the bootstrap has nothing to recreate.
- Route disconnect/rename without an instance_id to the legacy per-type path
  instead of rejecting it with "instance_id is required".
- Surface disconnect failures on both clients (web toast, desktop status)
  and refresh the desktop list silently so it no longer jumps to the top.
- Show WeChat channels first in the connected list (stable sort).
- Add regression tests covering the dispatch routing and the bootstrap
  re-materialization bug.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 20:50:41 +08:00
zhayujieandcowagent 9f025cbac6 fix: keep keenable.ai signup guidance in search config copy
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 18:19:14 +08:00
zhayujieandcowagent 1f4ebef106 docs: add Keenable to v2.1.8 search providers release note
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 18:17:19 +08:00
zhayujieandcowagent 42ec55161f fix: support keenable search config in desktop client
Add keenable to the dedicated-key providers, generalize the anonymous
opt-in flow (anysearch + keenable) and add the missing i18n title/desc
so the desktop search modal no longer shows raw i18n keys.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 18:14:32 +08:00
zhayujieandcowagent 750ef721c4 fix: simplify keenable search config copy in console
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 18:12:20 +08:00
zhayujieandcowagent 9c7e3d5fe8 Merge pull request #3122 from keenableai/feat/keenable-web-search
feat: add Keenable as an opt-in web_search provider

Thanks @ilya-bogin-keenable (#3122). Resolved conflicts against the
Tavily/SearXNG providers merged earlier; all nine backends coexist.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 18:09:39 +08:00
zhayujie b49d915d64 Merge branch 'master' into pr3122-keenable
# Conflicts:
#	agent/tools/web_search/web_search.py
#	channel/web/static/js/console.js
#	channel/web/web_channel.py
#	docs/ja/tools/web-search.mdx
#	docs/tools/web-search.mdx
#	docs/zh/tools/web-search.mdx
#	tests/test_web_search.py
2026-09-08 18:06:39 +08:00
zhayujie f04f3524e2 Merge branch 'feat/web-search-tavily-searxng' 2026-09-08 17:37:10 +08:00
zhayujieandcowagent a7be54fa8f docs: expand v2.1.8 search providers into a per-provider list
Add Tavily and SearXNG to the v2.1.8 release notes and split the search
provider entry into one bullet per backend.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 17:36:49 +08:00
zhayujieandcowagent 1b905e4b85 fix: shorten qianfan display name to 百度 in console
Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 17:36:49 +08:00
zhayujieandcowagent ffb789bbb7 feat: support tavily and searxng in desktop search settings
Extend the desktop search credential UI to cover the two new providers,
matching the web console: Tavily uses the shared API-key modal, and SearXNG
gets an instance-URL field (prefilled from url_masked, no masking). Adds the
needs_url/url_masked types and the tavily/searxng i18n strings.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 17:25:02 +08:00
zhayujieandcowagent 24c4105d93 feat: support anysearch and serply key config in desktop search settings
Align the desktop client's search credential UI with the web console: show all
dedicated-key providers (bocha/anysearch/serply) as editable chips plus an add
entry, support AnySearch anonymous mode, and generalize the key modal.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 16:40:05 +08:00
zhayujieandcowagent 8189861174 feat(scheduler): paginate and delete run records, richer run detail
Scheduler execution history improvements across web console and desktop:
- Paginate the run history list with a 'load more' button (30 per page),
  backed by an offset parameter on list_runs and the runs API.
- Delete a single run record via a trash button on each card with a
  confirm dialog, backed by a new delete_run store method and API.
- Keep the runs row a light index: output_preview stays a 1000-char
  snippet and now appends an ellipsis when truncated so a clipped preview
  never reads as the whole message.
- Raise the session-copy cap (the detail view's full body source) from
  2000 to 4000 chars, with an ellipsis on overflow.
- Render the run-detail body as markdown by scoping it to .msg-content so
  paragraphs, headings and lists show correctly; add a preview/raw toggle
  on desktop to match web.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 16:04:06 +08:00
zhayujie 134fe5d7ff Merge branch 'master' of https://github.com/zhayujie/CowAgent 2026-09-08 15:28:40 +08:00
zhayujieandcowagent d6a2ea7e22 feat(desktop): align scheduler UI, run records, and single-source notifications
- Tasks/Records tabbed view with an execution-records list + detail modal;
  run-detail resolves channel type/name and owning agent.
- Channel cards match the web console (instance friendly name as title with
  inline rename, type · id as subtitle).
- ConfirmDialog renders above the modal that spawned it (elevated z-index).
- Scheduler notifications come from a single global runs poll
  (useSchedulerNotifyPoll), deduped by run id, so a scheduled push into any
  session surfaces exactly once.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 15:27:03 +08:00
zhayujieandcowagent 6f9c776553 feat(scheduler): web-always-persist delivery, run preview/instance, newest-first ordering
- Web is always a valid delivery target and always persists to conversation
  history (an idle /poll client has no other surface after a restart), instead
  of gating on the in-memory session_queues.
- Record instance_id on each run and raise the output_preview cap to 500 so the
  history detail view has enough context.
- list_tasks orders enabled-first then newest-created, so a freshly added task
  surfaces at the top.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 15:26:54 +08:00
zhayujieandcowagent 082e19029f feat(web): scheduler run history, run-detail markdown toggle, and notification fixes
Web console additions and fixes for scheduled-task delivery:
- Execution-records tab + list + detail modal, backed by a new
  /api/scheduler/runs(+/detail) endpoint over the global runs ledger
  (list_runs gains agent_id/since filters; get_run_detail recovers the full
  delivered body from the receiver session).
- Run-detail output renders Markdown by default with a preview/text toggle.
- type=web shows channel type 'web' / name '网页'.
- Notifications: pop exactly once per run across all browser tabs
  (localStorage + BroadcastChannel dedup); route a manual run's notice back to
  the triggering tab; on click, broadcast open-session so whichever tab the
  browser foregrounds lands on the right conversation; pass agent_id so
  cross-agent notification clicks switch correctly.

Co-authored-by: cowagent <cow@cowagent.ai>
2026-09-08 15:26:44 +08:00