Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
45d14b1
fix(cognitive): retry display name sync after index failures
69gg Oct 6, 2026
e4b6d16
fix(batching): finish completed speculative replies before new input
69gg Oct 6, 2026
7a8202d
fix(jm): honor session paths and isolate malformed inputs and chapters
69gg Oct 6, 2026
d70d4cb
fix(deploy): defer writes and map interpreter files for host Docker
69gg Oct 6, 2026
5f134c9
docs(development): document the CI coverage gate
69gg Oct 6, 2026
9743776
fix(chat): publish history readiness after session release
69gg Oct 6, 2026
3ffaaf4
fix(chat): align native pagination and preserve job recovery state
69gg Oct 6, 2026
8d6ca8d
fix(python-interpreter): clean directories created during cancellation
69gg Oct 6, 2026
33b83a1
fix(chat): ignore stale cancellation errors and stream statuses
69gg Oct 6, 2026
7d93d19
fix(douyin): stop fallback after uncertain delivery errors
69gg Oct 6, 2026
58dabe7
fix(chat): create attachment downloads without overwriting files
69gg Oct 6, 2026
c55e3a2
fix(automations): serialize snapshot persistence through cancellation
69gg Oct 6, 2026
2dfb9c3
fix(arxiv): settle inflight waiters when owner is cancelled
69gg Oct 6, 2026
9288097
fix(webui): preserve file charset when compressing downloads
69gg Oct 6, 2026
d4030f1
fix(onebot): reconnect without waiting for message handlers
69gg Oct 6, 2026
9a3803f
fix(ai): propagate cancellation to queued model calls
69gg Oct 6, 2026
67d61b4
fix(config): preserve valid settings and retry failed hot updates
69gg Oct 6, 2026
55a238a
fix(skills): preserve inflight stats across hot reload
69gg Oct 6, 2026
3ee4f02
fix(deploy): abort the wizard on keyboard interrupt
69gg Oct 6, 2026
9762adf
fix(file-analysis): confine archive extraction and cache cleanup
69gg Oct 6, 2026
af4b31e
fix(ai): share task progress across tool call rounds
69gg Oct 6, 2026
c0006d7
fix(webui): wait for authoritative history before releasing chat jobs
69gg Oct 6, 2026
21dfb0b
fix(knowledge): preserve source chunks until replacement succeeds
69gg Oct 6, 2026
913e075
fix(webui): allow remote chat images under image CSP
69gg Oct 6, 2026
a202c20
fix(automations): finish pending saves before propagating cancellation
69gg Oct 6, 2026
c5f6cd4
fix(scripts): keep reembedding dry runs read-only
69gg Oct 6, 2026
2f4a73e
fix(onebot): collect message tasks when reconnect is cancelled
69gg Oct 6, 2026
83036d2
fix(webui): apply validated settings without rereading config
69gg Oct 6, 2026
9dbf6f3
fix(api): use a typed application key for runtime context
69gg Oct 6, 2026
61a9d4b
fix(batcher): bind inflight replies to their batch token
69gg Oct 6, 2026
186f53a
fix(cognitive): confine profile paths to their storage directory
69gg Oct 6, 2026
0d72a78
fix(knowledge): skip cancelled queued model requests
69gg Oct 6, 2026
fafbe82
fix(scripts): synchronize restored profile search vectors
69gg Oct 6, 2026
7e4f808
fix(bilibili): settle download threads before cancelled cleanup
69gg Oct 6, 2026
55a8c96
fix(runtime): drain background workers before closing model queues
69gg Oct 6, 2026
f7b0789
fix(deploy): preserve distinct configuration backups
69gg Oct 6, 2026
bde8ee8
fix(chat): isolate runtime state and stale asynchronous responses
69gg Oct 6, 2026
36e2ab1
fix(scripts): retry incomplete version synchronization
69gg Oct 6, 2026
132852d
fix(weixin): flatten nested forward content before delivery
69gg Oct 6, 2026
cb48f92
docs(scripts): clarify cognitive storage configuration paths
69gg Oct 6, 2026
2530c2c
fix(cognitive): validate profile paths inside file IO threads
69gg Oct 6, 2026
4e3b173
fix(scripts): commit all managed version targets on retry
69gg Oct 6, 2026
842fd05
fix(chat): suspend requests and recover failed connection changes
69gg Oct 6, 2026
09665d1
fix(webui): ignore stale chat job poll completions
69gg Oct 6, 2026
5548370
fix(chat): preserve later edits when message sending fails
69gg Oct 6, 2026
0270b50
fix(webui): scope history pagination to its request
69gg Oct 6, 2026
d785f34
fix(jm): clean cancelled downloads after event loop shutdown
69gg Oct 6, 2026
6bc41fd
fix(chat): guard conversation history request ordering
69gg Oct 6, 2026
d38ecc0
fix(chat): render failed code highlighting as plain text
69gg Oct 6, 2026
0ebaac5
fix(webui): ignore stale active job recovery responses
69gg Oct 6, 2026
56a53b8
fix(api): close chat admission and roll back failed job creation
69gg Oct 6, 2026
19b84f0
fix(runtime): drain accepted work before closing transports
69gg Oct 6, 2026
217deba
fix(chat): retain blobs while images are displayed
69gg Oct 6, 2026
590790e
fix(console): preserve schemes and IPv6 in profile migration
69gg Oct 6, 2026
fa84cc4
fix(naga): preserve delivered results when usage persistence fails
69gg Oct 6, 2026
8181461
fix(webui): honor configurable graceful shutdown deadlines
69gg Oct 6, 2026
9f0d087
fix(webui): serialize config saves and preserve distinct backups
69gg Oct 6, 2026
733924a
fix(arxiv): scope send deduplication to physical delivery routes
69gg Oct 6, 2026
9f76316
fix(attachments): preserve CQ parameter values on delivery
69gg Oct 6, 2026
aaa71e7
fix(api): normalize memory time filters to comparable instants
69gg Oct 6, 2026
7e7f27f
fix(webui): preserve workflow edits across stale responses
69gg Oct 6, 2026
8133c35
fix(cognitive): repair profile indexes before confirming skipped merges
69gg Oct 6, 2026
06a53b6
fix(memes): guard reanalysis against concurrent record mutations
69gg Oct 6, 2026
beabf70
fix(webui): scope WeChat login work to its dialog
69gg Oct 6, 2026
e5aa5ea
fix(chat): split production chunks and preserve full highlighting
69gg Oct 6, 2026
c270e7a
fix(deploy): format IPv6 service URLs correctly
69gg Oct 7, 2026
2bb4bf7
fix(webui): serialize auth token refreshes by session
69gg Oct 7, 2026
ce9467a
fix(memes): reject stale attachment cache fills
69gg Oct 7, 2026
6857bab
fix(chat): serialize conversation deletion with job acceptance
69gg Oct 7, 2026
93a3f42
fix(chat): preserve selection when renaming conversations
69gg Oct 7, 2026
b42b54d
fix(memes): preserve delivery success and serialize usage indexing
69gg Oct 7, 2026
9997691
fix(download): validate compressed wire sizes separately
69gg Oct 7, 2026
ab8dfed
fix(ai): hash complete multimodal cache identities
69gg Oct 7, 2026
66b046e
fix(webui): honor shutdown policy and avoid watcher deadlocks
69gg Oct 7, 2026
ce9cb82
fix(memes): make pruning and failed deletion safely retryable
69gg Oct 7, 2026
864951d
fix(weixin): serialize account changes with client lifecycle
69gg Oct 7, 2026
afaa7d8
fix(webui): isolate log streams and responses by view
69gg Oct 7, 2026
92f0cbc
fix(memes): preserve cancellation during indexing failures
69gg Oct 7, 2026
a3b54e9
fix(chat): expose complete timelines in job snapshots
69gg Oct 7, 2026
352b88d
fix(webui): preserve meme selection and unsaved drafts
69gg Oct 7, 2026
eb64c53
fix(config): serialize replacements and preserve cancelled writes
69gg Oct 7, 2026
d6f531d
fix(chat): preserve snapshot and nested agent timelines
69gg Oct 7, 2026
fea67d5
fix(webui): keep live logs ahead of delayed snapshots
69gg Oct 7, 2026
ffba02a
fix(webui): retain meme drafts outside refreshed result pages
69gg Oct 7, 2026
fa4d2d7
fix(chat): replay legacy resumes and preserve history call IDs
69gg Oct 7, 2026
a8240b3
fix(weixin): mark each acknowledged delivery as sent
69gg Oct 7, 2026
0d0ecae
fix(automations): preserve zero loop counts and validate integers
69gg Oct 7, 2026
a77f5b2
fix(webui): bind chat sends to their original conversation
69gg Oct 7, 2026
b53e147
fix(memes): recover stale jobs during worker polling
69gg Oct 7, 2026
5b135df
fix(weixin): preserve uncertain delivery across business fallbacks
69gg Oct 7, 2026
acd78ca
fix(config): catch up saved changes when restarting watchers
69gg Oct 7, 2026
88a62a8
fix(chat): publish conversation changes after durable commits
69gg Oct 7, 2026
d4b7a9d
fix(memory): publish only durable ordered commits
69gg Oct 7, 2026
96da594
fix(attachments): decode local file URIs consistently
69gg Oct 7, 2026
d6572ed
fix(deploy): recreate lxmusic when generated config changes
69gg Oct 7, 2026
4e5c440
fix(queue): retain started LLM calls during group trimming
69gg Oct 7, 2026
37d1b1d
fix(arxiv): clean cancelled downloads after file workers settle
69gg Oct 7, 2026
f2877bb
docs(memory): clarify durable add failure semantics
69gg Oct 7, 2026
8759662
fix(chat): refresh completed conversations after reconnect
69gg Oct 7, 2026
b5c275e
fix(chat): preserve cache when legacy migration fails
69gg Oct 7, 2026
e7bdec5
fix(chat): settle pending uploads when saving settings
69gg Oct 7, 2026
5d6fe1d
fix(weixin): preserve acknowledged sends when history fails
69gg Oct 11, 2026
ba61847
fix(automations): preserve concurrent outputs across nested loops
69gg Oct 11, 2026
c857288
fix(chat): clean up late Android lifecycle subscriptions
69gg Oct 11, 2026
26f0d29
fix(weixin): prevent replay after partial message delivery
69gg Oct 11, 2026
b939561
fix(webui): isolate stale polls and history during sends
69gg Oct 11, 2026
fd04879
fix(config): serialize watcher restarts and retire stopped tasks
69gg Oct 11, 2026
f17f991
chore(release): prepare v3.18.5
69gg Oct 11, 2026
a8f9fb6
fix(automations): preserve legacy null loop counts
69gg Oct 11, 2026
8ca1a91
test(memory): verify persistence errors at tool boundaries
69gg Oct 11, 2026
b450278
fix(weixin): preserve errors for known unsent messages
69gg Oct 11, 2026
c5be1c8
fix(queue): cap new ordinary group LLM admissions
69gg Oct 11, 2026
b58b4fb
fix(webui): report failed configuration restores
69gg Oct 11, 2026
a43db99
fix(chat): preserve new jobs and retire stale stream starts
69gg Oct 11, 2026
fdc7bf2
docs(review): clarify v3.18.5 failure and recovery boundaries
69gg Oct 11, 2026
4960717
fix(weixin): preserve retryable HTTP rejections
69gg Oct 11, 2026
bc6533e
fix(onebot): reject new events while disconnecting
69gg Oct 11, 2026
377b867
test(review): cover wizard cancellation and clarify cleanup limits
69gg Oct 11, 2026
e815810
fix(knowledge): handle source removal during indexing
69gg Oct 11, 2026
8167ee6
fix(naga): protect UUID failure cleanup and shared waiters
69gg Oct 11, 2026
b383897
docs(concurrency): explain persistence and lifecycle lock boundaries
69gg Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Use `uv` for the root project:

For the console app, run `cd apps/undefined-console && npm ci && npm run check`. Use `npm run dev` for the Vite shell and `npm run tauri:dev` for the desktop shell.

For the native chat client, run `cd apps/undefined-chat && npm ci && npm run check` (Biome + TypeScript + Vitest unit/e2e + `cargo fmt --check`/`cargo check`/`cargo test`). Use `npm run tauri:dev` for the desktop shell.
For the native chat client, run `cd apps/undefined-chat && npm ci && npm run check` (Biome + TypeScript + Vitest unit/e2e + Node production-build regression + `cargo fmt --check`/`cargo check`/`cargo test`). Use `npm run tauri:dev` for the desktop shell.

## Coding Style & Naming Conventions
Use 4-space indentation. Python code must be fully type-annotated and pass strict mypy checks. Disk I/O should go through `src/Undefined/utils/io.py` so writes stay async-safe and atomic. Follow `snake_case` for modules and functions, `PascalCase` for classes, and prefer extending existing services/helpers over introducing one-off abstractions. Skills handlers must not import repo-local modules outside `skills/` (only `Undefined.skills.*` and same-directory relative imports are allowed); pass dependencies through the execution context, share helpers via `src/Undefined/skills/shared.py`, and keep `tests/test_skills_import_boundary.py`'s ratchet baseline shrinking. WebUI JavaScript in `src/Undefined/webui/static/js/` and both native apps use Biome 2.5.10; keep the two package pins, lockfiles, and three configuration schemas in sync. Nested app configurations set `root: false` for checks launched from the repository root and use `extends: []` (explicit no-inherit) plus their own `formatter.indentStyle = "tab"`, so the root WebUI rules never leak into app checks. App changes must satisfy Biome, TypeScript, and Cargo checks.
Expand Down Expand Up @@ -63,7 +63,7 @@ Both main prompt variants and `res/IMPORTANT/each.md` guide replies to add usefu
`group_analysis.member_activity` distinguishes recency (`member_list`), retrieved window message counts (`history`), and weighted indicators (`hybrid`, default). History-mode timestamps must come from messages inside the selected window. History aggregation uses `try_parse_message_time` and skips invalid timestamps before counting or ranking; `parse_message_time` retains its current-time fallback for existing display callers. Missing/zero `last_sent_time` means unknown, not proof of inactivity or never speaking; last-message time does not indicate current online status. See [the toolset documentation](src/Undefined/skills/toolsets/group_analysis/README.md) for reporting limits.

## Testing Guidelines
Write tests as `tests/test_<feature>.py`. Async tests use `pytest-asyncio`. Add or update coverage for behavior changes in APIs, config loading/hot reload, cognitive memory, meme or knowledge flows, and WebUI/runtime routes. If you touch `apps/undefined-console/` or `src/Undefined/webui/static/js/`, run `npm run check` in `apps/undefined-console/` in addition to the Python checks; if you touch `apps/undefined-chat/`, run `npm run check` in `apps/undefined-chat/` (it bundles Vitest unit/e2e suites, so cover changed behavior there). No fixed coverage threshold is configured, so cover touched paths well.
Write tests as `tests/test_<feature>.py`. Async tests use `pytest-asyncio`. Add or update coverage for behavior changes in APIs, config loading/hot reload, cognitive memory, meme or knowledge flows, and WebUI/runtime routes. If you touch `apps/undefined-console/` or `src/Undefined/webui/static/js/`, run `npm run check` in `apps/undefined-console/` in addition to the Python checks; if you touch `apps/undefined-chat/`, run `npm run check` in `apps/undefined-chat/` (it bundles Vitest unit/e2e suites, so cover changed behavior there). CI runs `pytest --cov --cov-report=term` with the 65% minimum configured in `pyproject.toml`; cover touched paths well in addition to satisfying this overall gate.

## Commit & Pull Request Guidelines
Recent history follows Conventional Commits with optional scopes, for example `fix(webui): refine launcher return flow` and `feat(commands): add /version (/v) slash command`. Keep commit subjects imperative and concise. Keep `code/NagaAgent/` syncs separate from local feature work when possible. If you are bumping release versions, prefer `uv run python scripts/bump_version.py <version>` so `pyproject.toml`, `src/Undefined/__init__.py`, and the `package.json` + Tauri config of both `apps/undefined-console/` and `apps/undefined-chat/` stay in sync. For pull requests, include a short impact summary, linked issues, and the commands you ran; attach screenshots for WebUI or Tauri UI changes.
2 changes: 2 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,8 @@ description: 从 PDF 文件中提取文本和表格,填写表单。当用户

微信接入由主进程独立管理,不依赖 OneBot 是否在线。`WeixinService` 先校验帐号与 peer,再把已绑定来源映射为逻辑 QQ 私聊交给 `MessageHandler`;未知来源在写历史或调用 AI 之前进入隔离存储。`DeliveryAddress` 将逻辑身份与物理通道分离:`qq:<id>` 和 `wechat:<id>` 可共享同一个用户历史、认知记忆与权限,但发送器、消息合并 scope 和 transport 元数据保持通道隔离。自动化同样持久化规范 `address`,旧 `target_type + target_id` 在加载时转换为 `group:` 或 `qq:` 地址。

OneBot 消息处理任务由客户端跨连接跟踪。网络断线只结束接收循环、通知未完成的 API 请求连接失败,不等待媒体下载等处理任务即可重连;已有处理任务继续运行。主动断开、停止或取消连接任务时,会取消并等待这些处理任务收尾,连接建立或重连等待阶段收到取消也会执行同样的清理,避免后台任务遗留。

### "车站-列车" 队列模型

针对高并发消息处理,Undefined 实现了全新的 **ModelQueue** 调度机制:
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## v3.18.5 会话恢复、消息投递与并发持久化修复

本版本集中修复聊天续接、消息发送和后台任务在切换会话、取消、断线、重启及写入失败时的状态错乱:保留已经完成的投递与提交,避免重复回复、旧响应覆盖新操作,以及界面显示成功但数据尚未保存的问题。

- 聊天恢复与历史一致性:WebChat 只有在任务收尾、历史落盘并释放会话占用后才确认完成,创建任务失败会撤销占用,删除会话与接收新任务按顺序处理。完整任务快照保留正文、工具调用与嵌套 Agent 时间线,Chat 客户端修复断线续接、旧版事件重放和分页恢复;离线期间已经完成的会话也会刷新,旧连接、旧会话和旧请求的迟到结果不会覆盖当前状态。
- WebUI 与客户端交互:修复切换会话后发送归属错误、旧轮询重复追加正文、迟到历史清空刚发送的消息,以及发送失败覆盖后续草稿的问题。保存连接配置时暂停请求,将未完成上传标记失败并忽略迟到结果,旧缓存迁移失败保留原数据;日志、工作流、表情包编辑和微信登录弹窗隔离各自请求,刷新与翻页保留未保存的编辑。恢复配置失败会保留表单并显示错误,不再误报恢复成功;登录凭据刷新串行执行,避免并发刷新相互失效。
- 消息投递不再被后续故障误判:微信按每次服务端确认及时记录已发送状态,支持展开嵌套转发;历史、附件登记或使用统计失败不会把已送达内容当作未发送。分片或批量发送已有部分成功、或投递结果无法确认时,微信、抖音、JM 等业务降级不再重发整条内容;仍保留对明确未投递内容的降级。令牌失效、会话暂停、配置错误及明确的连接前失败保留原异常,不再误标为可能送达;认证问题不触发拆分消息的结构降级。微信帐号变更与客户端启停串行,OneBot 重连不再等待消息处理任务阻塞;arXiv 去重按实际平台与投递地址隔离。
- 配置保存与热更新:整体保存、增量修改、恢复、模板同步和密码更新共用原子写入顺序,同一时刻的备份不会互相覆盖;取消请求会等已开始的写入与必要更新完成。热更新失败可以重试,监听器重启会补读停止窗口内的修改,连续调整轮询间隔不会留下多个监听任务或继续使用旧间隔。内存构建配置不再意外读取本地文件,损坏的 TOML 即使使用 `strict=False` 也会明确报错。
- 停止与取消:停止 Bot 先关闭新任务入口,等待已接收的回复、后台任务和持久化收尾,再关闭模型队列与消息通道。新增 `[webui].stop_timeout_seconds`(环境变量 `WEBUI_STOP_TIMEOUT_SECONDS`),**默认 `0` 表示等待自然结束,正整数才会在对应秒数后强制终止**;停止按钮、关闭 WebUI 和更新前停止共用此规则。取消模型请求会同步清理排队及重试任务,普通群聊队列裁剪保留已启动调用所依赖的子请求。新增 `[core].group_llm_queue_max_pending`(默认 `128`,支持热更新),每模型普通群聊 LLM 等待队列满时明确拒绝新请求并清理其等待状态,保留已接收任务与重试;这是排队准入限制,不限制在途调用或总内存,过载时普通自动回复可能结束而不发送消息。
- 连续消息与工具状态:已经回复完成的投机批次及时结束,后续消息开启新批次,避免把旧输入再次回复;批次取消只作用于对应任务,不会误伤独立的 @ 或拍一拍回复。`task_progress` 在多轮工具调用间共享,技能热重载保留在途统计;多模态分析缓存使用完整媒体来源、类型与补充指令,避免长链接或 data URL 前缀相同而串用结果。
- 记忆与知识库:长期记忆和会话数据在落盘成功后才发布新状态,取消会等待已开始的提交,写入失败不会让内存与磁盘各自保留不同版本。知识文件替换先完成新内容读取、嵌入和写入,再清理旧索引;读取、嵌入或新块写入失败时保留旧索引供重试。侧写昵称刷新与史官跳过合并时也会补齐检索索引,索引失败不再提前确认完成;记忆时间筛选统一比较含时区与不含时区的时间。
- 表情包生命周期:重分析不会覆盖期间的人工编辑、恢复已删除内容或影响同 UID 重新入库的记录;停用、删除与附件缓存失效保持一致,搜索不会返回已停用条目。使用统计失败保留发送成功结果,容量清理跳过刚置顶或修改的候选,删除失败保留可重试记录;异常退出遗留的处理任务会在后续轮询中恢复,正在执行的任务不会被重复调度。
- 自动化流程:持久化按顺序完成,取消不会让旧快照覆盖新状态;嵌套并行循环只合并本轮修改的变量,避免覆盖同级节点刚产生的输出。`loop.times.count=0` 正确跳过循环,旧存档的 `count:null` 兼容默认次数;新建、更新与启用任务仍严格拒绝负数、布尔值、字符串及 `null`。
- 文件与附件:统一解析本地 `file://` URI,保留中文、空格及原始路径中的百分号;CQ 参数转义与解码保留链接里的逗号、方括号和实体文本。HTTP 压缩响应按传输字节校验完整性、按解压后大小执行容量限制。JM、Bilibili、arXiv 下载及 Python 执行目录在取消后等待后台文件操作结束再清理;JM 配置的会话目录生效,畸形输入和单章异常不再拖垮整次处理。侧写路径与归档解压目标限制在各自目录内,缓存清理拒绝符号链接目录,TAR 解压启用安全过滤。
- 原生显示与连接:附件下载以独占方式创建文件,避免覆盖同名文件;图片仍显示时保留其 Blob,代码高亮失败回落纯文本,生产构建拆分体积并保留完整语言支持。Console 迁移旧连接时保留协议与 IPv6 地址,Android 生命周期监听即使延迟注册也会在退出后释放。恢复应用时保留刷新途中刚获确认的新任务,同一任务不会重复建立在途 SSE 订阅;迟到旧句柄会关闭,避免覆盖当前订阅或在取消后泄漏。
- 部署与维护:部署向导中断不再继续写配置,备份名称不冲突,IPv6 服务地址正确加括号;容器模式为 Python 解释器映射宿主 Docker 可见的共享目录,LxMusic 配置变化会触发容器重建。`restore_profile.py restore` 现在同时恢复侧写并同步检索正文与向量,失败后可用 `sync` 补齐,**执行前须停止机器人及其他写入进程**。重嵌入的 `--dry-run` 在临时副本上读取,不修改源向量库,但仍会调用嵌入 API;侧写恢复的 `--dry-run` 不写盘、不调用模型。版本脚本支持失败后重跑,补齐全部版本文件与锁文件,并将此前已修改的版本目标纳入提交。

---

## v3.18.4 添加宣传片链接

在 README.md 中添加了宣传片链接。
Expand Down
18 changes: 12 additions & 6 deletions apps/undefined-chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ Undefined Chat 是 `apps/undefined-chat/` 下的 Tauri v2 + React 19 原生聊
## 当前能力

- 会话管理:创建、删除、重命名、切换会话。当前没有置顶字段/API。
- 消息历史:初始加载、cursor-based 加载更早消息、滚动锚点保持和长历史窗口化渲染。
- 消息渲染:Markdown、表格、任务列表、引用块、代码高亮、代码块复制;正文 HTML 经 sanitize 内联渲染,HTML 代码块可在独立预览窗口中运行。
- 消息历史:初始加载、cursor-based 加载更早消息、滚动锚点保持和长历史窗口化渲染。同一会话的重载与分页共用请求顺序保护;发送新消息、完成任务刷新或删除会话后,迟到的旧响应不会覆盖正文、游标或加载状态。
- 消息渲染:Markdown、表格、任务列表、引用块、代码高亮、代码块复制;未知语言或高亮失败时保留为纯文本代码,代码中的 HTML 不会插入正文。正文 HTML 经 sanitize 内联渲染,HTML 代码块可在独立预览窗口中运行。
- 附件:系统文件选择器、上传状态队列、Tauri 流式上传、下载和预览。当前显示上传中/成功/失败状态,不显示百分比进度。
- 图片:正文 `<attachment uid/>` 内联图、附件缩略图、blob 缓存、全屏查看、缩放、旋转和可滚动查看区域。
- 保存连接配置会把尚未完成的附件上传转为失败状态,恢复连接后可移除并重新选择文件;旧上传结果不能恢复已移除条目。同一 Runtime 已上传成功的附件保留,切换 Runtime 清理旧附件。
- 图片:正文 `<attachment uid/>` 内联图、附件缩略图、blob 缓存、全屏查看、缩放、旋转和可滚动查看区域。LRU 目标缓存 60 张;挂载中的图片和已打开的大图持有 blob 引用,超过目标时只淘汰无人使用的项,关闭或卸载消费者后再释放多余缓存。
- 命令:斜杠命令、子命令提示、方向键导航、Tab/Enter 补全;空态反馈(加载中/无匹配/不可用);窗口聚焦按 TTL 刷新命令列表。
- 引用:引用 bot 消息、划词引用选中文本、发送引用、引用芯片跳转当前已加载源消息;源消息不在当前页时会自动加载一页更早历史,仍未命中需手动继续加载更早历史。
- 工具调用块:层级展示、状态、运行中实时计时、阶段明细(stage detail)、与 WebUI 一致的输入/输出预览(JSON/Python 风格结构化、Markdown 输出、附件图片预览);历史回放支持 timeline/calls/events 多级回退。
- 自动滚动:智能跟随底部,可在设置面板开关,偏好持久化到 localStorage。
- 事件:SSE 优先,断开时 JSON fallback(指数退避重连,活跃任务持续重试)。
- 重命名只更新目标会话标题,保留当前会话、输入草稿与移动端面板;请求期间切换会话也不会被迟到响应切回。
- 事件:SSE 优先,断开时 JSON fallback(指数退避重连,活跃任务持续重试);完整任务快照恢复消息与已结束调用,并按快照游标续接,旧 Runtime 的正文前缀也会保留。
- 恢复连接时,已从 active 列表消失的本地任务会补刷所属会话的权威历史,包括非当前会话;仍在历史落盘的终态任务继续保留发送锁,已删除会话不再请求。刷新期间刚获发送确认的新任务不会被较早取得的空列表删除,也会继续订阅事件。
- SSE 订阅按任务去重在途启动;恢复或结束任务后,迟到的旧订阅会被关闭,旧启动失败不会覆盖当前订阅或启动额外轮询。
- 快捷键:Ctrl/Cmd+N 新会话,Ctrl/Cmd+K 聚焦输入并打开命令模式,Ctrl/Cmd+/ 切换侧栏,Ctrl/Cmd+, 打开设置,Escape 关闭当前弹层/抽屉。
- i18n:中英双语运行时切换,提供语言切换 UI,并按系统语言自动检测默认语言。

Expand All @@ -30,7 +34,7 @@ Undefined Chat 是 `apps/undefined-chat/` 下的 Tauri v2 + React 19 原生聊
Android:

- 文件选择器返回的 `content://` URI 通过 Tauri fs plugin 打开,上传链路仍需要真机/模拟器 smoke 覆盖。
- 生命周期监听使用 Tauri `tauri://suspended` / `tauri://resumed` 事件;恢复前台时重新 bootstrap 并恢复 Runtime 订阅。逐 job 主动补齐仍依赖 store 的 SSE 断线 fallback 行为。
- 生命周期监听使用 Tauri `tauri://suspended` / `tauri://resumed` 事件;恢复前台时重新 bootstrap,接受 Runtime 的完整任务时间线与对应游标后恢复订阅;快照内已有事件不会重复追加。卸载或重新注册时会清理尚在异步注册中的监听,已排队的旧恢复回调不会再发起请求。
- Android 通过生成工程注入的 `SecretPlugin` 使用 Android Keystore + AES-GCM 保存 API Key:密钥在 AndroidKeyStore 中生成且不可导出,加密后的密文存放在 `MODE_PRIVATE` 的 SharedPreferences 中。Android 生成插件已接入并有脚本/Rust 测试覆盖,真实设备仍需 smoke 验证;只有平台安全存储不可用且用户显式确认时,才允许不安全文件降级。

iOS:
Expand All @@ -45,7 +49,9 @@ npm run check
npm run tauri:dev
```

`npm run check` 当前包含 Biome、TypeScript、unit/jsdom integration tests、cargo fmt、cargo check 和 cargo test。
`npm run check` 当前包含 Biome、TypeScript、unit/jsdom integration tests、独立 Node production build 回归、cargo fmt、cargo check 和 cargo test。

`npm run build` 将应用、React、其余依赖与代码高亮模块分别打包。highlight.js 的全部语言定义按文件名区间拆成四组,仍同步注册并保留自动检测;分包控制单个 chunk 的体积并细化缓存粒度,全部语言支持保持一致。总下载体积基本不变,独立 gzip 压缩会因减少字典复用而增加压缩体积。`npm run test:build` 在临时目录构建,检查默认 500 kB 限制、chunk 导入图,并执行生产高亮模块,核对完整语言列表、各语言高亮和自动检测结果。

Biome 固定为 **2.5.10**,与 Console 和根目录 WebUI 配置保持一致;使用 `npm ci` 安装锁定版本。App 的 `biome.json` 使用独立嵌套配置(`root: false`),不继承 WebUI 格式规则,并显式启用 React 的 `noArrayIndexKey` 检查,避免依赖自动检测造成漏检;升级约定见 [构建文档](../../docs/build.md#6-git-hook-集成)。

Expand Down
1 change: 1 addition & 0 deletions apps/undefined-chat/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"includes": [
"**/src/**/*.ts",
"**/src/**/*.tsx",
"**/tests/**/*.mjs",
"**/package.json",
"**/tsconfig.json",
"**/vite.config.ts",
Expand Down
4 changes: 2 additions & 2 deletions apps/undefined-chat/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading