Commit Graph
34 Commits
Author SHA1 Message Date
yiqi-017 554586fa82 fix: 持久化 llm_history 修复重启后模型幻觉工具调用 2026-06-06 16:09:22 +08:00
dd3xp 5ade9841d2 feat(desktop): bridge auto-starts non-IM services on boot (L4 cron)
The reflect/scheduler.py service drives the L4 archive cron every 12h,

but was only registered in the catalog and never auto-started. The UI

panel also wasn't surfacing it (WS snapshot filters to IM only), so

there was effectively no user-facing way to turn L4 archiving on.

Net effect: every GA deployment (local Tauri, Squishy, kw, etc.) had

L4_raw_sessions/ permanently empty. compress_session.py never ran.

Fix: on bridge startup, autostart everything in _catalog that isn't an

IM service. Currently that's just reflect/scheduler.py. IM services stay

manual because they need mykey.py config + explicit user opt-in.

After this lands:

  - bridge boots -> scheduler subprocess spawned

  - ~2 min later -> first L4 archive runs (since _l4_t starts at 0)

  - every 12h thereafter -> next L4 archive cycle

  - users get L4 with zero config
2026-06-05 22:37:43 +08:00
yiqi-017 8e37028370 [update]:修改文件上传大小至500MB 2026-06-05 18:30:29 +08:00
lyx 8f88506e9a 优化plan card视觉 2026-06-05 01:46:27 +08:00
lyx 1b88a2eac3 fix(desktop): Plan 按会话绑定 plan_path,修复完成态与跨会话串扰 2026-06-05 00:59:56 +08:00
lyx 6942c8bdbd fix(desktop): Plan 条稳定显示,对齐 TUI grace 与按会话 cwd 解析 2026-06-05 00:48:34 +08:00
lyx 6b77d8a5e0 fix(desktop): Plan 与 TUI 对齐,按会话绑定且不因预设文案误激活 2026-06-05 00:22:18 +08:00
lyx 8aab1fa49f 1. 聊天框和Conductor对齐
2. 尝试修复聊天渲染卡顿问题
2026-06-05 00:01:31 +08:00
lyx c62bcc5f9d feat(desktop): Plan 模式 todo 卡片初版
poll 响应附带 plan 事实数据(plan_state + bridge 两行接入),composer 上方渲染进度与任务行;完成/读空 grace 在前端处理。plan_state 原 API 与 TUI 完全兼容,Desktop 仅新增 *_for_desktop 辅助函数。
2026-06-04 15:14:00 +08:00
dd3xp 492caa610e fix(desktop-bridge): apply mykey edits to live agents without restart
Editing a model key / adding a profile wrote mykey.py but the running session
agents kept using the LLM sessions built at startup (old key), so the change
only took effect after a restart.

Root cause: llmcore tracks "changed since last load" via a single global mtime
flag, and load_llm_sessions() skips rebuilding when changed=False. Whichever
agent reloads first consumes that flag — e.g. the throwaway GenericAgent the UI
spins up to render the model list — so the real persistent session agent later
sees changed=False and keeps its stale sessions.

After saving a profile, force every live session agent to rebuild: reset
llmcore._mykey_mtime to None before each agent.load_llm_sessions() so
reload_mykeys() reports changed=True. Bridge-only (same trick as the existing
_invalidate_mykey_cache); no core changes to agentmain/llmcore; the rebuild
preserves conversation history via agentmain's oldhistory hand-off.
2026-05-28 01:22:07 +08:00
dd3xp bab5dec66f feat(desktop): group uploads by session id; purge on session delete
Uploads now land in temp/desktop_uploads/<sid>/ instead of a flat dir, so a
session's attachments can be cleaned up when the session is deleted.

- upload_handler reads `sid` from the body and saves under desktop_uploads/<sid>/
  (sanitized; missing sid -> _misc bucket).
- delete_session purges the session's upload subdir.
- _sweep_stale_uploads recurses into sid subdirs (rglob) and drops empty dirs.
- Frontend: uploadOne sends the active session's bridgeSessionId; addFiles
  ensures a session exists (reusing newSession/ensureBridgeSession) before
  uploading, so a sid is available — the empty-app case creates one first.

Whitelist checks (upload_raw / upload_delete) already validate "under root",
so the deeper subdir paths pass unchanged.
2026-05-26 00:41:40 +08:00
dd3xp 4cb073ee05 refactor(desktop): drop dead image-upload path; images & files share one dir
The 05-20 image mechanism (_save_image_data -> desktop_image_uploads, images
sent as dataUrl in the prompt) was superseded on 05-21 by the placeholder-path
flow: the frontend uploads every attachment via /upload to desktop_uploads/ and
inlines the file path into the prompt text, sending only path metadata
(files/imageMetas) — never dataUrl. So _save_image_data was never called and
desktop_image_uploads stayed empty (dead code).

- Remove _save_image_data and the _UPLOAD_DIR (desktop_image_uploads) dir.
- normalize_prompt now only flattens prompt text; image handling dropped
  (images arg kept for backward compat, ignored).
- _sweep_stale_uploads now sweeps only desktop_uploads.

Net: images and files share one dir (desktop_uploads), no duplicate storage.
2026-05-26 00:16:14 +08:00
dd3xp 36424089e9 fix(desktop): persist uploads in GA temp dir with 30-day retention
Uploaded files were stored in the OS temp dir, and the attachment dir was
wiped wholesale on every bridge startup (shutil.rmtree) — so attachments in
past sessions became dead links after any restart (e.g. "File not found:
...\ga_web_uploads\...__thesis.pdf").

- Move both _UPLOAD_DIR (images) and _WEB_UPLOAD_DIR (attachments) under
  ga_root/temp/ (gitignored): desktop_image_uploads/ and desktop_uploads/.
- Drop the rmtree-on-startup; uploads now survive bridge restarts.
- Add _sweep_stale_uploads(): on startup, delete only files older than
  UPLOAD_RETENTION_DAYS (30) by mtime, so temp storage can't grow unbounded.
- Remove now-unused tempfile/shutil imports.
2026-05-25 23:24:32 +08:00
dd3xp 56b688cc28 feat(desktop): file attachments survive refresh + remote download + fix Windows open
Three fixes for the chat file-attachment flow:

1. Attachments vanished on refresh — sendPrompt now sends files/imageMetas
   (name+path) to the bridge, submit_prompt persists them on the user message,
   and msgNode renders images via GET /upload/raw URL instead of the in-memory
   dataUrl. So after a page refresh (bridge still running) file cards and image
   thumbnails come back. (Hard limit: restarting the bridge rmtree's
   ga_web_uploads, so a restart still drops the files.)

2. Remote can't open files — opening a file runs os.startfile/xdg-open on the
   BRIDGE machine, useless when the browser is on another box. openUploadFile
   now detects non-localhost and downloads the file to the client instead
   (GET /upload/raw?download=1). Remote image preview works too (lightbox uses
   the inline /upload/raw URL).

3. Windows opened pdf/docx in Notepad — PR #12's open path used
   _open_path_in_editor ('edit' verb), which falls back to Notepad for types
   with no edit verb (pdf, etc.). Uploads now use _open_path_default (default
   'open' verb → registered default app). mykey still uses the editor path.

New bridge endpoint: GET /upload/raw?path=&download= — whitelisted to
_WEB_UPLOAD_DIR, inline preview or attachment download, restores original
filename. Serves both the refresh-preview and remote-download cases.
2026-05-25 01:18:38 +08:00
顾祎炜andYiwei Gu 4c8ec489e2 feat(chat): file preview routing — open vs reveal-in-finder (#12)
Click on a file attachment now picks behavior by extension: previewable
types (PDF, images, text, code, Office docs, audio/video) open in the
system default app; everything else (zip, binaries, unknown) reveals the
file in Finder/Explorer instead of opening it directly — avoids garbled
output for non-textual formats.

- frontend: PREVIEWABLE_EXTS whitelist + isPreviewableByName()
- bridge: /path/open accepts mode='reveal', uses 'open -R' (macOS),
  'explorer /select,' (Windows), parent-dir xdg-open (Linux fallback)
- pending thumb-strip chips also pass data-name for the same routing

Co-authored-by: Yiwei Gu <yiwei.gu@sap.com>
2026-05-24 17:09:54 +08:00
yiqi-017 913ec672a5 @
feat(desktop): persist token usage history to local file instead of localStorage

Token stats (history + snapshot) now stored in temp/desktop_token_history.json
via GET/POST /token-history endpoints, ensuring cross-browser consistency.
@
2026-05-24 15:38:59 +08:00
yiqi-017 a045524f84 feat(desktop):
>>   move session persistence from localStorage to backend API
>>
>>   Sessions are now loaded from GET /sessions and metadata updates (pin,
>>   title) go through PATCH /session/{sid}, eliminating cross-browser
>>   isolation issues between Chrome and Safari.
2026-05-24 15:12:38 +08:00
顾祎炜andYiwei Gu 8783914077 feat(chat): file attachment cards in user message + click-to-open (#10)
Non-image attachments now render as Gemini/ChatGPT-style file cards above
the user text bubble (icon disc + bold filename + kind subtitle). Click on
a file card opens it via the system default app — bridge `/path/open` gains
a `kind=upload` mode that whitelists \`_WEB_UPLOAD_DIR\` to prevent the
frontend from opening arbitrary local paths.

Composer thumb-strip uses the same card style for non-image pending files,
also click-to-open. Image thumbs (48×48) unchanged.

Co-authored-by: Yiwei Gu <yiwei.gu@sap.com>
2026-05-23 19:42:58 +08:00
lyxanddd3xp 1a54f1788a feat(desktop): chat font-size control + dark-mode Markdown readability (#9)
Settings gains a 10–20px chat font stepper (default 14, slider with aria),
applied only to chat bubbles + composer via --chat-font; pre/code/fold switch
to em-relative sizing so they scale with it. Dark mode swaps highlight.js to
the github-dark theme and adds link/code/pre/katex/table/blockquote styling so
markdown is legible on dark backgrounds.

Persistence rides the same ui-prefs mechanism as #7 (ui.fontSize in
~/.ga_desktop_settings.json via /config), so this PR supersedes #7 — it
contains the full server-first persistence layer (bootUiFromDom / persistUiPrefs
/ syncBootCache) plus fontSize. Keeps #5's interrupt-on-resend behavior intact.

Closes #9

Co-Authored-By: dd3xp <zhangjingming131@gmail.com>
2026-05-23 19:03:52 +08:00
dd3xp e2a67f0803 fix(upload): surface payload-too-large errors, reject empty files, use toast not chat-log
Root cause of "uploads silently land as 0-byte __file": aiohttp default body
limit is 1 MiB; PDFs almost always exceed it, request.json() raises
HTTPRequestEntityTooLarge, the generic read_json() swallows it as bare
Exception, upload_handler then sees an empty dict and happily writes an
empty file with name='file', returning ok=true. Front-end thinks the
upload worked.

Bridge:
- Set web.Application(client_max_size=1MB) explicitly so the limit is
  visible at the call site (kept at 1MB intentionally for now; bump later
  if larger uploads are needed)
- upload_handler reads body itself instead of read_json so it can catch
  HTTPRequestEntityTooLarge and HTTPBadRequest and return a real error
  to the client instead of silently writing 0 bytes
- Reject blob == b'' as a second line of defence

Frontend:
- addFiles skips files with size === 0 (handles drag-folder ghost File
  and edge-case clipboard items) with a dedicated 'upload.empty' toast
- 4x showSystem(...) in addFiles -> showChanToast(title, detail, 'err').
  Upload failures should be transient toasts, not permanent system
  messages glued into the chat transcript.
2026-05-21 23:39:39 +08:00
dd3xp e970ec5172 fix(desktop): persist stopped output, decouple user display from LLM prompt, anti-flicker boot
- Bridge cancel() saves partial assistant content as a stopped message so the truncated output survives a refresh
- submit_prompt accepts a separate display field; UI now shows the original user input while the bridge stores the composed Plan/Auto SOP + expanded file paths for LLM/restore_context
- Boot-time chat-start is hidden via early data-boot-has-sessions attribute when localStorage has sessions, removing the welcome-page flash on refresh
- Conv-item click on non-chat pages now jumps back to the chat page with that session selected
- Remove dead refresh button from topbar (no handler, no id)
2026-05-21 17:12:42 +08:00
顾祎炜andYiwei Gu 7b83ec0ee2 feat(chat): chat UI — Plan/Auto, presets, image/file upload, model dropdown (#3)
* i18n: add empty keys for chat-page button text (chip/upload/model)

* html: bind chat composer chips to i18n (plan/auto/upload/model)

* feat(chat): add Plan/Auto sticky toggle state with localStorage hydration

* style(chat): chip.on toggle, dropdown menu, image thumb strip

* style(chat): anchor .composer for .ga-menu dropdown

* feat(chat): image upload via hidden file input + thumb strip

* feat(chat): sendPrompt prepends Plan/Auto SOP, passes pending images

* fix(chat): drop hardcoded '[image]' session-title fallback

* feat(chat): model chip dropdown menu (replaces click-to-cycle)

* i18n: fill chat composer + Plan/Auto preset prompt values

* feat(chat): drag-drop + paste image upload (chat panel overlay)

* feat(chat): image lightbox + custom/built-in preset management

- Image lightbox: click composer thumbnails or sent message images to preview full-screen
- Custom presets: add via modal, delete via hover ×, persisted in localStorage (ga_custom_presets)
- Built-in preset hide: × on builtin cards hides them (ga_hidden_builtins); "restore defaults" button in preset-modal head brings them back
- Plan/Auto mutex: clicking one turns off the other
- Unified preset rendering: makeCardEl + renderAllPresets covers both grids (chat-start and preset-modal)

* feat(upload): tuiapp_v2-style file upload (any file, path placeholders)

Bridge:
- POST /upload: save dataUrl → <tempdir>/ga_web_uploads/<uuid>__<name>, return abs path
- DELETE /upload: remove uploaded file (path-traversal guarded)
- Wipe ga_web_uploads/ on bridge startup (no orphan accumulation)

Frontend:
- Pick / drag / paste any file → POST /upload → insert [Image #N] or [File #N] placeholder in textarea
- Attachment strip shows image thumbnail or filename chip with × to remove
- × also fires DELETE /upload + strips placeholder from textarea
- sendPrompt expands placeholders to bare paths in-place; no longer sends images field
- Limits: 50 MB/file, 10 files/batch
- i18n: upload.* keys reworded for "file" (was "image"); added upload.failed

* fix(chat): drop dead langSel block, hook renderAllPresets into selectLang

Post-merge cleanup: feature/chat-ui's old langSel <select> handler was
left behind after origin/main switched to the lang-list radio control,
causing 'langSel is not defined' at top level which aborted app.js
before applyI18n() could run (entire UI rendered blank).

Also wire renderAllPresets() into selectLang() so dynamically-built
preset cards refresh on language change.

* fix(chat): drop fake 'auto' model menu item, align llmNo with profile id

The model dropdown injected a synthetic 'auto-select' row at data-llmno=0
and offset real profiles by +1 (no = i+1). Backend list_llms() / next_llm
both index from 0, so the labels were off by one — clicking 'auto'
selected the first real profile, clicking the first real label selected
the second, and clicking the last wrapped via modulo back to the first.

Render real profiles only, using each profile's id directly as data-llmno.
Menu click now routes through selectModel() so the choice persists to
localStorage and bridge config.

---------

Co-authored-by: Yiwei Gu <yiwei.gu@sap.com>
2026-05-21 15:59:46 +08:00
yiqi-017 46fc1faaa7 feat(desktop): session persistence + token stats fixes + model pricing tooltip
- Sessions persist across refresh (localStorage metadata + bridge disk storage)
- Bridge restores LLM context on restart via /session/{sid}/restore endpoint
- Sort sessions: pinned first, then by most recent activity
- Fix token stats: remove double .json() parse, handle negative deltas after restart
- Add model pricing tooltip on hover in token statistics page
- localStorage only stores session metadata (not messages) to prevent quota overflow
- Remove duplicate tokPollBridge() call in pollSession
2026-05-21 15:05:37 +08:00
weaselkkkandCursor ea69be0c73 Merge branch 'feat/message_channel' into main
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 14:42:31 +08:00
weaselkkk 7ce4778903 fix: update setting board in message turnnel page 2026-05-21 14:32:49 +08:00
dd3xp 4f18d6e9e7 fix(bridge): protocol now required for add-model (no apibase auto-guess); UX: segmented control + flag SVGs
- _next_native_var now requires explicit protocol ('oai' or 'claude'); raises if missing. Removes the silent 'native_claude_config' default that wrongly routed all OAI-compatible relays through Anthropic protocol.
- Add-model form: replace native <select> with a segmented control (2 radio buttons styled as pills) since browsers can't style native dropdown panels. Required validation kept.
- Language list: inline SVG flags before labels (China / USA, per request), uses official flag colors. Reliable on Windows where flag emojis render inconsistently.
- i18n: model.protocol/protocolPick/protocolOai/protocolClaude in zh+en.
- Edit mode now backfills protocol radio from varName.
2026-05-21 12:59:19 +08:00
weaselkkk 3fcf12e707 feat: complete status panel page 2026-05-21 12:29:07 +08:00
lyx 1a83c28ccf refactor: 压缩 bridge 与主题 CSS 行数
合并 mykey 块改写 helper,--blue 改由 JS 设置,去掉重复 CSS 映射规则。
2026-05-21 03:29:37 +08:00
lyx 852ab63d43 feat(desktop): 设置页 — 弹窗与模型 CRUD
设置弹窗、模型列表增删改、表单必填/选填提示;bridge 提供 model-profiles CRUD 与 llmNo 传递。
2026-05-21 03:29:37 +08:00
weaselkkk e6379b7f0c feat: complete message channel page 2026-05-21 00:35:50 +08:00
yiqi-017 c2a7707d1d feat: token stats page with per-prompt tracking, time filter & cost estimation
- Add /token-stats bridge endpoint exposing cost_tracker data + model name
- Frontend localStorage persistence for per-prompt token records (WS-triggered)
- Time range filter (datetime-local), pagination, expandable session details
- Model-based cost estimation (Claude/GPT/DeepSeek price table with cache rates)
- All UI text via i18n, all colors via CSS variables
2026-05-20 18:38:57 +08:00
yiqi-017 94b83d4226 Revert "feat: token stats with persistent history, time filter, pagination & model-based cost"
This reverts commit 011824860d.
2026-05-20 15:40:42 +08:00
yiqi-017 5180eca7d6 feat: token stats with persistent history, time filter, pagination & model-based cost
- Persist per-prompt token usage to ~/.GenericAgent/token_history.json
- Add GET /token-stats API endpoint with since/until filtering
- Token page: real data, expandable per-prompt details with model & cost
- Model price table for accurate cost estimation (GPT/Claude/DeepSeek/etc)
- Time range filter (datetime-local) with reset button
- Pagination (15 sessions per page)
- Stat cards (total/today/cost) follow time filter, today always absolute
- Migrate Rust settings path to ~/.GenericAgent/ga_desktop_settings.json
- Fix cost_tracker Python 3.9 compat (from __future__ import annotations)
2026-05-20 15:14:50 +08:00
dd3xp 5032fe7091 GenericAgent Desktop: mockup-based client wired to bridge
Initial snapshot. Desktop frontend (frontends/desktop/static/) rebuilt as the served client: 3-card UI, sidebar nav, preset functions, sessions, streaming via HTTP poll + WS notify, markdown, modals (preset/add-model/settings), theme-variable styling. mykey.py excluded (gitignored).
2026-05-20 00:29:22 +08:00