luckytri 1.0.9 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/docs/en/CHANGELOG.md +14 -0
  4. package/docs/en/architecture.md +4 -0
  5. package/docs/en/cognition.md +34 -0
  6. package/docs/en/guide.md +5 -5
  7. package/docs/zh/CHANGELOG.md +14 -0
  8. package/docs/zh/architecture.md +4 -0
  9. package/docs/zh/cognition.md +34 -0
  10. package/docs/zh/guide.md +5 -5
  11. package/package.json +112 -112
  12. package/public/app/assets/ChatsPage-OQgvV8YR.js +3 -0
  13. package/public/app/assets/{Empty-C7yzOBzy.js → Empty-DjvoHpAi.js} +1 -1
  14. package/public/app/assets/Field-BECf8Ykc.js +1 -0
  15. package/public/app/assets/HeartPage-6s6prdTE.js +1 -0
  16. package/public/app/assets/LifePage-g-8hiKC6.js +1 -0
  17. package/public/app/assets/MemoryPage-efIhloIp.js +1 -0
  18. package/public/app/assets/NaturePage-cjgIe6wY.js +3 -0
  19. package/public/app/assets/PeoplePage-DQZ0uevM.js +1 -0
  20. package/public/app/assets/PluginsPage-DZQFEyxq.js +2 -0
  21. package/public/app/assets/SystemPage-BHHcZb1h.css +1 -0
  22. package/public/app/assets/SystemPage-DjYS33Sv.js +1 -0
  23. package/public/app/assets/TimePage-B3oncGqb.js +3 -0
  24. package/public/app/assets/index-CqRx6L9a.js +4 -0
  25. package/public/app/assets/{knowledge-BJOIyuJo.js → knowledge-Dj6hHClj.js} +1 -1
  26. package/public/app/assets/media-B1i_W5FC.js +1 -0
  27. package/public/app/assets/{mind-DtTVrW6Z.js → mind-BY8WSI7v.js} +1 -1
  28. package/public/app/index.html +3 -3
  29. package/public/guide.en.html +3 -3
  30. package/public/guide.html +3 -3
  31. package/server/core/backoff.js +123 -0
  32. package/server/core/context-builder.js +307 -307
  33. package/server/core/contribution-check.js +50 -50
  34. package/server/core/conversation-cues.js +377 -377
  35. package/server/core/conversation-grounding.js +547 -547
  36. package/server/core/dialogue-context.js +247 -247
  37. package/server/core/dialogue-contract.js +1 -1
  38. package/server/core/embedding.js +246 -0
  39. package/server/core/expression-strategy.js +90 -0
  40. package/server/core/initiative-context.js +88 -88
  41. package/server/core/model-json.js +223 -223
  42. package/server/core/model-manager.js +256 -54
  43. package/server/core/model-presets.js +1045 -1045
  44. package/server/core/orchestrator.js +41 -3
  45. package/server/core/persona-manager.js +364 -364
  46. package/server/core/reader-check.js +166 -166
  47. package/server/core/reply-delivery.js +688 -678
  48. package/server/core/repository.js +3 -0
  49. package/server/core/response-generator.js +101 -101
  50. package/server/core/retired-prompts.js +53 -51
  51. package/server/core/time-expressions.js +143 -0
  52. package/server/core/turn-processor.js +154 -6
  53. package/server/core/turn.js +41 -0
  54. package/server/core/understanding.js +85 -0
  55. package/server/core/working-memory.js +149 -0
  56. package/server/i18n/decisions.js +225 -225
  57. package/server/i18n/errors.js +12 -0
  58. package/server/i18n/models.js +120 -120
  59. package/server/knowledge/api.js +364 -342
  60. package/server/knowledge/embedding-profile.js +163 -114
  61. package/server/knowledge/manager.js +6 -1
  62. package/server/mind/api.js +68 -0
  63. package/server/mind/continuity.js +634 -562
  64. package/server/mind/day-planner.js +52 -3
  65. package/server/mind/episodes.js +242 -0
  66. package/server/mind/expression-grounding.js +24 -24
  67. package/server/mind/index.js +13 -2
  68. package/server/mind/initiative.js +356 -338
  69. package/server/mind/life/index.js +6 -0
  70. package/server/mind/meetings.js +1112 -1069
  71. package/server/mind/memory.js +1091 -888
  72. package/server/mind/recall/index.js +776 -0
  73. package/server/mind/recall/schema.js +132 -0
  74. package/server/mind/recall/sources.js +220 -0
  75. package/server/mind/recall/store.js +368 -0
  76. package/server/mind/salience.js +202 -201
  77. package/server/mind/schema.js +568 -567
  78. package/server/mind/self.js +537 -496
  79. package/server/mind/time/activity-recall.js +65 -65
  80. package/server/mind/time/tasks.js +28 -0
  81. package/server/mind/time/works.js +528 -528
  82. package/server/mind/view.js +54 -15
  83. package/server/storage/archive.js +1 -1
  84. package/public/app/assets/ChatsPage-CshdtTzV.js +0 -3
  85. package/public/app/assets/Field-BjFv4Rfn.js +0 -1
  86. package/public/app/assets/HeartPage-eqo6ru16.js +0 -1
  87. package/public/app/assets/LifePage-CN8GE5NK.js +0 -1
  88. package/public/app/assets/MemoryPage-CCpMN4Wg.js +0 -1
  89. package/public/app/assets/NaturePage-C0JqdBTR.js +0 -3
  90. package/public/app/assets/PeoplePage-BXGBbO4u.js +0 -1
  91. package/public/app/assets/PluginsPage-Dksp5AV5.js +0 -2
  92. package/public/app/assets/SystemPage-BenIyTvk.css +0 -1
  93. package/public/app/assets/SystemPage-Cd7aa8eE.js +0 -1
  94. package/public/app/assets/TimePage-Bs_zImb4.js +0 -3
  95. package/public/app/assets/index-CyRtYxP3.js +0 -4
  96. package/public/app/assets/media-Bwtx8blz.js +0 -1
package/README.en.md CHANGED
@@ -94,7 +94,7 @@ The full design is in [Her life](docs/en/her-life.md) and [Architecture and exte
94
94
 
95
95
  Recent work focuses on continuity and clearer conversation. Account-based recognition links public encounters across groups and private chats. Relationships, forms of address, her own views and private promises retain their sources and visibility boundaries. An ordinary greeting need not bring back unfinished tasks or demonstrate how much she remembers.
96
96
 
97
- **1.0.9 update:** Add a separate Volcengine Ark Agent Plan category with its auto-router and all current text-model presets, using the dedicated endpoint and Responses API. Remove the unavailable OpenCode Zen Space Bunny Free and Step 5 Free presets, and clear their saved profiles on upgrade. Conversation fixes continue to prioritize the current speaker's question, follow-ups and quotations, with plain explanations when someone cannot follow. See the [changelog](docs/en/CHANGELOG.md). These changes still need testing in everyday interaction; automated scores are not a guarantee of conversation quality.
97
+ **1.1.1 update:** The embedding service takes a request address and a key; the model name can be left empty. The key is no longer a password field, so the browser does not keep asking to sign in again. See the [changelog](docs/en/CHANGELOG.md).
98
98
 
99
99
  | Area | What it does today |
100
100
  | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
package/README.md CHANGED
@@ -94,7 +94,7 @@ flowchart LR
94
94
 
95
95
  近期更新集中在让相处更连贯、说话更清楚。她按账号认出同一个人,在不同群与私聊间衔接公开经历;关系、称呼、自己的看法和私人约定分别保留来源与可见范围。普通问候不需要带出旧任务,每次开口也不需要证明自己记住了多少事。
96
96
 
97
- **1.0.9 更新:** 新增独立的火山方舟 Agent Plan 模型分类,提供自动路由及全部当前文本模型预设,使用专属接口和 Responses API;移除已不可用的 OpenCode Zen Space Bunny Free 与 Step 5 Free 预设,并在升级时清理已有档案。对话修复继续优先理解当前说话人的问题、连续补充与引用;对方说没听懂,就换成能直接理解的话。完整变化见 [版本记录](docs/zh/CHANGELOG.md)。这些修复仍需真实相处检验,项目不把自动评分当作回复质量的保证。
97
+ **1.1.1 更新:** 向量服务填写请求地址和密钥;模型名可留空。密钥不再占用密码框,避免浏览器反复要求重新登录。完整变化见 [版本记录](docs/zh/CHANGELOG.md)。
98
98
 
99
99
  | 方面 | 现在能做什么 |
100
100
  | ------------ | --------------------------------------------------------------------------------------------------------- |
@@ -2,6 +2,20 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## 1.1.1 — 2026-10-11
6
+
7
+ - The embedding service asks for its request address and key. The model name can be left empty; a Volcengine Ark address uses doubao-embedding-vision.
8
+ - The key is no longer a password field, so the browser does not treat it as a studio sign-in and ask to log in again. A 401 from the vector service is no longer treated as a signed-out studio.
9
+
10
+ ## 1.1.0 — 2026-10-11
11
+
12
+ - She is the same person in every room. What was just said elsewhere comes into the current turn, and she knows it. She does not usually bring another place up here. When the person in front of her was part of it, she may continue or leave it unsaid. Private wording, secrets and another account's history still stay out.
13
+ - Every turn carries her own state: awake or asleep, energy, mood, what she is living for, and the people she is close to. That is for her, not a report to recite.
14
+ - Memory uses one rebuildable index. Facts, meetings, episodes, self, thoughts, diaries and promises can be found by meaning, words and who is present. Without an embedding model, keyword recall and recent context still answer, and conversation continues.
15
+ - The embedding service needs only an API key. Address, model and 1024 dimensions are chosen from the chat service. Those requests use their own lane and do not block replies.
16
+ - An affair is an episode thread from its start to its outcome. A follow-up that comes due is only a candidate and still passes the existing initiative gates. A lesson must cite lived experience, the same experience is used once, and two independent pieces establish it.
17
+ - Background failures no longer retry in a loop. Conversation stays ahead of upkeep, a permanent model error pauses that model, and a schedule clash takes the nearest free slot. The database schema moves to version 5 and copies the whole file before upgrading.
18
+
5
19
  ## 1.0.9 — 2026-10-11
6
20
 
7
21
  - In follow-ups, answer the new detail without repeating limitations already explained; answer a question about source or feelings directly.
@@ -150,3 +150,7 @@ Nothing above the core changes. The connection option in settings and the readin
150
150
  - The built studio is committed in `public/app/`; run `npm run build:ui` after changing the frontend.
151
151
  - The guide pages are generated by `npm run docs:build` from `docs/zh/guide.md` and `docs/en/guide.md`.
152
152
  - Runtime data, backups, reports and secrets stay on the machine and never enter the repository.
153
+
154
+ ## Cognition and memory
155
+
156
+ How understanding, recall, episodes, lessons and follow-ups fit together is in [Cognition](cognition.md). The recall index is derived. Episodes and self threads stay append-only.
@@ -0,0 +1,34 @@
1
+ <p align="right"><a href="../zh/cognition.md">中文</a> · <b>English</b></p>
2
+
3
+ # Cognition and memory
4
+
5
+ She is still one turn call, not one model call per layer. What was added is a hint while a message is waiting, one recall path, episode threads, working memory, and lessons written in solitude from experience.
6
+
7
+ ## How a message moves
8
+
9
+ 1. It is stored in `core_events`. The speaker's query vector is computed while the batch is waiting.
10
+ 2. Messages in one session become one turn. `understanding.js` joins one person's consecutive lines and notes tone, stance and time expressions. Those are hints.
11
+ 3. `mind/recall` takes vector, full-text and structured candidates together, fuses them by reciprocal rank, then reranks by person, age and source. Permission is applied before ranking.
12
+ 4. Working memory keeps the current stretch verbatim. Older stretches are a summary plus semantic recall. Open episodes appear on a relationship as `goingOn`.
13
+ 5. One turn call. A model sometimes nests `choice` inside `understanding` or `contribution`. Normalization puts the fields back where the contract says they belong.
14
+ 6. The reply is checked and sent. Background consolidation writes facts and episodes. An episode whose follow-up time has arrived, or a promise that is due, is only a candidate during solitude. It still passes the existing initiative gates.
15
+
16
+ When the embedding service is down, recall falls back to full text, structured lookup and recent context. The turn still runs.
17
+
18
+ ## Storage
19
+
20
+ `DATABASE_VERSION` is 5. `mind_index`, `mind_index_fts` and `mind_vectors` are derived and can be rebuilt. `mind_episodes` is append-only: a status change is a new row in the same `thread_id`. `mind_self` gained a `lesson` kind and a `subject`. A lesson must cite experience that did not come from the model. The same experience is used once. Two independent pieces move it from `emerging` to `active`. A counterexample revises it downward.
21
+
22
+ A fact can be archived. Archiving only removes it from ordinary recall. When a person asks for deletion, the source, the index row and the vector are removed together.
23
+
24
+ ## Embeddings
25
+
26
+ The configured embedding model uses the OpenAI-compatible `/embeddings` endpoint, in batches, 2048 dimensions by default. Asking for 1024 works and is similar. The Ark multimodal endpoint takes one item at a time. Cosine between a short chat line and a stored fact is low, so a hit counts when it stands above the median of the nearest batch, with different floors for the speaker, people present, and everyone else. Queries have their own request lane and do not queue behind conversation. Background backfill is paced and slows down on HTTP 429.
27
+
28
+ On a backup copy, with 1024 dimensions and about 40 paraphrased real facts: lexical Recall@5 was 0.475; hybrid recall with feature rerank was 0.950, and the average number returned fell from 12 to about 3.9. There is no separate model reranker.
29
+
30
+ ## What evaluation showed
31
+
32
+ Before these changes, a recent day of live turns had a median of about 35 seconds, and about 8.4 seconds before the first model call. Memory consolidation succeeded 13 times out of about 230. Priority lanes, persisted backoff, picking a free slot, and skipping a knowledge embedding when the library has no vectors, are there to stop failed retries and the queue in front of a turn.
33
+
34
+ On the same 24 historical turns, the baseline passed 24/24. After tone fields were added, one run failed because JSON fields were nested in the wrong object. Normalization lifts a misplaced `choice`, `tone` or `stance`. Expression hints appear only when openings repeat, questions keep ending the line, or replies keep getting longer.
package/docs/en/guide.md CHANGED
@@ -202,11 +202,11 @@ The server removes the export copy after download. Cancelled, expired and interr
202
202
  | Too much motion | "Reduce motion" at the bottom left; when the system asks for reduced motion, particles and morphing switch off automatically |
203
203
 
204
204
  Never post API keys, `.env` or private chat text anywhere public.
205
-
206
- ### Thoughts about places and update checks
207
-
208
- People → Thoughts about this place keeps her own impressions, wishes and questions, selectively updated from real encounters. Details show reasons, sources and history; copied group personas remain history only.
209
-
205
+
206
+ ### Thoughts about places and update checks
207
+
208
+ People → Thoughts about this place keeps her own impressions, wishes and questions, selectively updated from real encounters. Details show reasons, sources and history; copied group personas remain history only.
209
+
210
210
  People → Relationships binds a person or another robot by account, with reciprocal roles, an optional name and reason, and visibility. The relationship informs conversation, solitude, diaries and action choices. Trust and closeness still come from real encounters; a binding creates no shared history. Changes and removal retain history and memories. Private relationships are used only in private chats with that account. Equal names never merge accounts.
211
211
 
212
212
  An absent person can still be recalled by name or relationship role. Current facts, actual encounters and her own feelings inform that recognition together. Earlier opinions remain history rather than overriding current facts, and relevant replies are checked before delivery. She still chooses how to interact and address people without reciting management fields.
@@ -2,6 +2,20 @@
2
2
 
3
3
  # 版本记录
4
4
 
5
+ ## 1.1.1 — 2026-10-11
6
+
7
+ - 向量服务要填写请求地址和密钥。模型名可以留空,火山方舟地址会用 doubao-embedding-vision。
8
+ - 密钥不再使用密码框,避免浏览器把它当成管理台登录而反复要求重新登录。向量服务自己返回的 401 也不会再被当成管理台掉线。
9
+
10
+ ## 1.1.0 — 2026-10-11
11
+
12
+ - 她在每个房间都是同一个人。最近在别处说过的话会进入当前这一轮,她知道;一般不主动向这里的人提起别的地方的事。眼前就是当时那个人时,可以接着,也可以不提,由她决定。私下原话、保密内容和另一个账号的经历仍然不带过来。
13
+ - 每一轮都带着她自己的状态:醒着或困着、精力、心情、正在过的愿望,以及亲近的人。这是给她自己的,不是向眼前的人汇报的清单。
14
+ - 记忆改成一份可重建的检索索引:事实、相遇、情景、自我、念头、日记和约定可以按意思、关键词和在场的人一起找。没有向量模型时仍用关键词和近期上下文,对话照常进行。
15
+ - 向量服务只需要密钥。地址、模型和 1024 维按对话服务自动选择,请求走单独的车道,不挡回复。
16
+ - 一件事从发生到结果记成情景线程。到了跟进时间只作为候选,仍经过原有的主动发言关卡。教训必须来自外部经历,同一段经历只用一次,两条独立经历才算确立。
17
+ - 后台失败不再连环重试:对话优先于整理,永久错误会暂停该模型,日程冲突改选最近的空档。数据库结构升到第 5 版,升级前会先复制整份数据。
18
+
5
19
  ## 1.0.9 — 2026-10-11
6
20
 
7
21
  - 连续追问只回答新问的细节,不反复重讲已经交代的限制;问来源就说来源,问感受就说感受。
@@ -150,3 +150,7 @@ public/ 构建后的工作室(app/)与教程页
150
150
  - 构建后的工作室提交在 `public/app/`,改动前端后运行 `npm run build:ui`。
151
151
  - 教程页由 `npm run docs:build` 从 `docs/zh/guide.md` 与 `docs/en/guide.md` 生成。
152
152
  - 运行时数据、备份、报告和密钥永远只在本机,不进入仓库。
153
+
154
+ ## 认知与记忆
155
+
156
+ 理解、回忆、情景、教训和主动跟进的数据流写在 [认知](cognition.md)。检索索引是派生数据,情景和自我线索仍然只追加。
@@ -0,0 +1,34 @@
1
+ <p align="right"><b>中文</b> · <a href="../en/cognition.md">English</a></p>
2
+
3
+ # 认知与记忆
4
+
5
+ 她仍然是一次回合调用,不是按层各调一次模型。新增的部分是:消息到达后的理解提示、统一检索、情景线程、工作记忆,以及独处时根据经历写下的教训。
6
+
7
+ ## 一条消息怎么走
8
+
9
+ 1. 接收并写入 `core_events`。发言人的查询向量在聚合等待期间异步算好。
10
+ 2. 按会话聚合成一轮。`understanding.js` 合并同一人的连续消息,给出语气、立场和时间表达,这些只是提示。
11
+ 3. `mind/recall` 并行取向量、全文和结构化候选,用倒数排名融合,再按人物、时效和来源重排。权限过滤在排序之前。
12
+ 4. 工作记忆保留当前这一段的原文,更早的内容用摘要和语义召回代替。未结束的情景写进关系里的 `goingOn`。
13
+ 5. 一次 turn 调用。模型有时会把 `choice` 放进 `understanding` 或 `contribution`;归一化时按约定归位。
14
+ 6. 校验后发送。后台整理写出事实和情景。到了跟进时间的情景、到期的约定,只作为独处时的候选,仍要经过现有的主动发言关卡。
15
+
16
+ 嵌入服务不可用时,检索退回全文加结构化加近期上下文,回合照常进行。
17
+
18
+ ## 存储
19
+
20
+ `DATABASE_VERSION` 为 5。`mind_index`、`mind_index_fts` 和 `mind_vectors` 是派生索引,可以重建。`mind_episodes` 只追加:状态变化写成同一 `thread_id` 的新行。`mind_self` 增加 `lesson` 类型和 `subject`。教训必须引用模型之外的经历,同一段经历只用一次,至少两条独立经历才从 `emerging` 变成 `active`,反例用修订降低强度。
21
+
22
+ 人物事实可以归档。归档只是从普通检索里拿掉,记录还在。用户要求删除时,源记录、索引和向量一起删除。
23
+
24
+ ## 嵌入
25
+
26
+ 已配置的向量模型走 OpenAI 兼容的 `/embeddings`,可批量,默认 2048 维,指定 1024 维可用且相近。方舟多模态接口一次一条。短对话和事实之间的余弦不高,所以召回看一条命中比最近一批的中位数高出多少,再按说话人、在场的人和其他人使用不同的下限。查询有自己的请求车道,不和对话排队。后台补建限速,遇到 429 会放慢。
27
+
28
+ 在备份副本上,1024 维、约 40 条改写过的真实事实:词面检索 Recall@5 为 0.475,混合加特征重排为 0.950,平均返回条数从 12 降到约 3.9。不引入单独的模型重排。
29
+
30
+ ## 评测里看到的变化
31
+
32
+ 优化前,线上最近一天的回合中位耗时约 35 秒,第一次模型调用前约 8.4 秒;记忆整理大约 230 次里成功 13 次。优先级车道、持久化退避、空档安排和跳过没有向量的知识库嵌入,是为了切断失败重试和回合前的排队。
33
+
34
+ 同一批 24 条历史回合:基线 24/24 通过;加上语气字段后有一轮因 JSON 字段嵌套失败。归一化会把放错层级的 `choice`、`tone` 和 `stance` 归位。表达上的统计提示只在出现重复开头、问句结尾或越说越长时给出。
package/docs/zh/guide.md CHANGED
@@ -202,11 +202,11 @@ TA 的一切都存在这一个数据库里,所以服务运行时会自动备
202
202
  | 界面动得太多 | 左下角「减少动画」;系统开启了减少动态效果时会自动关闭粒子和形变 |
203
203
 
204
204
  不要把 API 密钥、`.env` 或私聊原文发到公开位置。
205
-
206
- ### 群里的独特想法与检查更新
207
-
208
- 「人际 → 在这里的想法」保存她在各处自己想留下的印象、愿望或疑问。由她根据实际相处选择更新,详情显示理由、来源和历史;旧群人格只留在历史里。
209
-
205
+
206
+ ### 群里的独特想法与检查更新
207
+
208
+ 「人际 → 在这里的想法」保存她在各处自己想留下的印象、愿望或疑问。由她根据实际相处选择更新,详情显示理由、来源和历史;旧群人格只留在历史里。
209
+
210
210
  「人际 → 彼此的关系」可按账号绑定人或另一位机器人,设置妹妹、姐姐、朋友等双向称呼、名字、缘由和公开范围。她在交流、独处、日记与行动选择中知道这份关系;信任和亲近仍依据实际相处,绑定不会制造共同经历。修改和解除保留历史及原有记忆。私下关系只在与对方的私聊中使用,不按同名合并账号。
211
211
 
212
212
  对方不在当前会话,也可以因名字或关系称呼被想起。当前关系、实际相处与自己的感受共同参与理解;关系更新前的看法作为历史保留,不再直接代表当前事实。相关回复发送前会再次核对。她仍选择如何相处和称呼,不机械播报关系字段。
package/package.json CHANGED
@@ -1,112 +1,112 @@
1
- {
2
- "name": "luckytri",
3
- "version": "1.0.9",
4
- "description": "让由技术诞生的她,在时间、记忆、相遇与选择中逐渐形成自己的方向",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/TodayYueC/LuckyTri.git"
9
- },
10
- "bugs": {
11
- "url": "https://github.com/TodayYueC/LuckyTri/issues"
12
- },
13
- "homepage": "https://github.com/TodayYueC/LuckyTri",
14
- "type": "module",
15
- "bin": {
16
- "luckytri": "bin/luckytri.js"
17
- },
18
- "files": [
19
- "bin/luckytri.js",
20
- "server/**/*.js",
21
- "server/plugins/runner/*.mjs",
22
- "scripts/launch.js",
23
- "scripts/start-server.js",
24
- "scripts/service-supervisor.js",
25
- "scripts/stop.js",
26
- "scripts/reset-password.js",
27
- "scripts/setup.js",
28
- "scripts/runtime.js",
29
- "scripts/service.js",
30
- "scripts/browser.js",
31
- "scripts/proxy-config.js",
32
- "scripts/backup.js",
33
- "scripts/recover.js",
34
- "scripts/import-data.js",
35
- "scripts/plugin.js",
36
- "public/app/index.html",
37
- "public/app/assets/*.js",
38
- "public/app/assets/*.css",
39
- "public/app/assets/*.png",
40
- "public/plugin-kit/v1/bridge.js",
41
- "public/plugin-kit/v1/kit.css",
42
- "public/guide.html",
43
- "public/guide.en.html",
44
- "public/tutorial.md",
45
- "public/tutorial.en.md",
46
- "plugins/weather/index.js",
47
- "plugins/weather/luckytri-plugin.json",
48
- "plugins/sdk/index.d.ts",
49
- "plugins/sdk/testing.js",
50
- "docs/zh/*.md",
51
- "docs/en/*.md",
52
- "docs/brand/*.png",
53
- ".github/SECURITY.md",
54
- "README.en.md",
55
- "LICENSE",
56
- "scripts/update-instance.js"
57
- ],
58
- "publishConfig": {
59
- "access": "public",
60
- "registry": "https://registry.npmjs.org/"
61
- },
62
- "keywords": [
63
- "qq",
64
- "onebot",
65
- "chatbot",
66
- "ai",
67
- "memory",
68
- "webui"
69
- ],
70
- "engines": {
71
- "node": ">=24.5"
72
- },
73
- "scripts": {
74
- "launch": "node scripts/launch.js",
75
- "start": "node scripts/start-server.js",
76
- "dev:ui": "vite --config studio-web/vite.config.ts",
77
- "build:ui": "vite build --config studio-web/vite.config.ts",
78
- "build": "npm run build:ui && npm run docs:build",
79
- "test": "node --import ./tests/helpers/awake.mjs --test tests/*.test.js",
80
- "test:clock": "node scripts/dev/test-clock.js",
81
- "audit:sent": "node --env-file-if-exists=.env scripts/dev/audit-sent.js",
82
- "test:ui": "node tests/ui/access.mjs && node tests/ui/smoke.js && node tests/ui/ta.mjs && node tests/ui/navigation.mjs && node tests/ui/time.mjs && node tests/ui/time-arrangement.mjs && node tests/ui/performance.mjs && node tests/ui/search.mjs && node tests/ui/storage.mjs && node tests/ui/data-transfer.mjs && node tests/ui/place-notes-updates.mjs && node tests/ui/i18n.mjs && node tests/ui/relationships-update.mjs",
83
- "setup": "node scripts/setup.js",
84
- "stop": "node scripts/stop.js",
85
- "backup": "node scripts/backup.js",
86
- "recovery": "node scripts/recover.js",
87
- "docs:build": "node scripts/build-guide.js",
88
- "plugin": "node scripts/plugin.js",
89
- "pack:check": "node scripts/pack-check.js",
90
- "test:package": "node scripts/test-package.js",
91
- "test:registry": "node scripts/test-package.js --registry",
92
- "prepack": "npm run build && npm run pack:check",
93
- "prepublishOnly": "npm run build && npm test && npm run test:package",
94
- "release:check": "npm run build && npm test && npm run test:ui && npm run test:package",
95
- "format": "prettier --write bin server studio-web scripts tests plugins package.json",
96
- "format:check": "prettier --check bin server studio-web scripts tests plugins package.json"
97
- },
98
- "dependencies": {
99
- "express": "^5.1.0",
100
- "sharp": "^0.35.5",
101
- "ws": "^8.18.3"
102
- },
103
- "devDependencies": {
104
- "@playwright/test": "^1.55.0",
105
- "@vitejs/plugin-vue": "^6.0.9",
106
- "marked": "^18.0.13",
107
- "prettier": "^3.9.8",
108
- "typescript": "^7.0.2",
109
- "vite": "^8.3.0",
110
- "vue": "^3.5.43"
111
- }
112
- }
1
+ {
2
+ "name": "luckytri",
3
+ "version": "1.1.1",
4
+ "description": "让由技术诞生的她,在时间、记忆、相遇与选择中逐渐形成自己的方向",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/TodayYueC/LuckyTri.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/TodayYueC/LuckyTri/issues"
12
+ },
13
+ "homepage": "https://github.com/TodayYueC/LuckyTri",
14
+ "type": "module",
15
+ "bin": {
16
+ "luckytri": "bin/luckytri.js"
17
+ },
18
+ "files": [
19
+ "bin/luckytri.js",
20
+ "server/**/*.js",
21
+ "server/plugins/runner/*.mjs",
22
+ "scripts/launch.js",
23
+ "scripts/start-server.js",
24
+ "scripts/service-supervisor.js",
25
+ "scripts/stop.js",
26
+ "scripts/reset-password.js",
27
+ "scripts/setup.js",
28
+ "scripts/runtime.js",
29
+ "scripts/service.js",
30
+ "scripts/browser.js",
31
+ "scripts/proxy-config.js",
32
+ "scripts/backup.js",
33
+ "scripts/recover.js",
34
+ "scripts/import-data.js",
35
+ "scripts/plugin.js",
36
+ "public/app/index.html",
37
+ "public/app/assets/*.js",
38
+ "public/app/assets/*.css",
39
+ "public/app/assets/*.png",
40
+ "public/plugin-kit/v1/bridge.js",
41
+ "public/plugin-kit/v1/kit.css",
42
+ "public/guide.html",
43
+ "public/guide.en.html",
44
+ "public/tutorial.md",
45
+ "public/tutorial.en.md",
46
+ "plugins/weather/index.js",
47
+ "plugins/weather/luckytri-plugin.json",
48
+ "plugins/sdk/index.d.ts",
49
+ "plugins/sdk/testing.js",
50
+ "docs/zh/*.md",
51
+ "docs/en/*.md",
52
+ "docs/brand/*.png",
53
+ ".github/SECURITY.md",
54
+ "README.en.md",
55
+ "LICENSE",
56
+ "scripts/update-instance.js"
57
+ ],
58
+ "publishConfig": {
59
+ "access": "public",
60
+ "registry": "https://registry.npmjs.org/"
61
+ },
62
+ "keywords": [
63
+ "qq",
64
+ "onebot",
65
+ "chatbot",
66
+ "ai",
67
+ "memory",
68
+ "webui"
69
+ ],
70
+ "engines": {
71
+ "node": ">=24.5"
72
+ },
73
+ "scripts": {
74
+ "launch": "node scripts/launch.js",
75
+ "start": "node scripts/start-server.js",
76
+ "dev:ui": "vite --config studio-web/vite.config.ts",
77
+ "build:ui": "vite build --config studio-web/vite.config.ts",
78
+ "build": "npm run build:ui && npm run docs:build",
79
+ "test": "node --import ./tests/helpers/awake.mjs --test tests/*.test.js",
80
+ "test:clock": "node scripts/dev/test-clock.js",
81
+ "audit:sent": "node --env-file-if-exists=.env scripts/dev/audit-sent.js",
82
+ "test:ui": "node tests/ui/access.mjs && node tests/ui/smoke.js && node tests/ui/ta.mjs && node tests/ui/navigation.mjs && node tests/ui/time.mjs && node tests/ui/time-arrangement.mjs && node tests/ui/performance.mjs && node tests/ui/search.mjs && node tests/ui/storage.mjs && node tests/ui/data-transfer.mjs && node tests/ui/place-notes-updates.mjs && node tests/ui/i18n.mjs && node tests/ui/relationships-update.mjs",
83
+ "setup": "node scripts/setup.js",
84
+ "stop": "node scripts/stop.js",
85
+ "backup": "node scripts/backup.js",
86
+ "recovery": "node scripts/recover.js",
87
+ "docs:build": "node scripts/build-guide.js",
88
+ "plugin": "node scripts/plugin.js",
89
+ "pack:check": "node scripts/pack-check.js",
90
+ "test:package": "node scripts/test-package.js",
91
+ "test:registry": "node scripts/test-package.js --registry",
92
+ "prepack": "npm run build && npm run pack:check",
93
+ "prepublishOnly": "npm run build && npm test && npm run test:package",
94
+ "release:check": "npm run build && npm test && npm run test:ui && npm run test:package",
95
+ "format": "prettier --write bin server studio-web scripts tests plugins package.json",
96
+ "format:check": "prettier --check bin server studio-web scripts tests plugins package.json"
97
+ },
98
+ "dependencies": {
99
+ "express": "^5.1.0",
100
+ "sharp": "^0.35.5",
101
+ "ws": "^8.18.3"
102
+ },
103
+ "devDependencies": {
104
+ "@playwright/test": "^1.55.0",
105
+ "@vitejs/plugin-vue": "^6.0.9",
106
+ "marked": "^18.0.13",
107
+ "prettier": "^3.9.8",
108
+ "typescript": "^7.0.2",
109
+ "vite": "^8.3.0",
110
+ "vue": "^3.5.43"
111
+ }
112
+ }
@@ -0,0 +1,3 @@
1
+ import{Dt as e,E as t,Et as n,G as r,H as i,I as a,J as o,M as s,P as c,Q as l,St as u,Tt as d,U as f,W as p,X as m,Y as h,Z as g,_t as _,a as v,at as y,bt as b,dt as x,et as S,f as C,ft as w,gt as T,it as E,j as D,lt as O,m as k,n as A,o as j,rt as M,s as N,t as P,tt as F,ut as ee,v as I,wt as L,yt as R,z}from"./Empty-DjvoHpAi.js";import{g as te,y as B}from"./mind-BY8WSI7v.js";import{c as V,f as H,h as U,m as W,n as G,o as ne,p as K,u as q,y as re}from"./index-CqRx6L9a.js";import{t as ie}from"./media-B1i_W5FC.js";import{t as J}from"./Field-BECf8Ykc.js";async function ae(e,t){let n=new URLSearchParams({session:e});return t!==void 0&&n.set(`after`,String(t)),k(`/core/events?`+n.toString())}async function oe(e,t={}){let n=new URLSearchParams({session:e});return t.limit&&n.set(`limit`,String(t.limit)),t.compact&&n.set(`compact`,`1`),k(`/core/traces?`+n.toString())}async function Y(e){return k(`/core/traces/`+e)}async function se(e){return k(`/simulate`,`POST`,e)}async function ce(e,t){return k(`/decisions/`+e+`/feedback`,`POST`,{tag:t})}async function X(e){return k(`/core/replay`,`POST`,e)}async function le(e){return k(`/core/sessions`,`POST`,e)}async function ue(e,t){return k(`/core/sessions/`+encodeURIComponent(e),`PATCH`,{enabled:t})}async function de(e,t){return k(`/core/sessions/`+encodeURIComponent(e)+`/archive`,`PATCH`,{archived:t})}async function fe(e){return k(`/core/sessions/`+encodeURIComponent(e),`DELETE`,{})}async function pe(e,t){return k(`/core/sessions/`+encodeURIComponent(e),`PUT`,t)}async function me(e){return k(`/core/sessions/`+encodeURIComponent(e)+`/summaries`)}async function he(e){return k(`/core/sessions/`+encodeURIComponent(e)+`/context`,`DELETE`,{})}var ge={class:`session-list`},_e={class:`list-head`},Z=[`placeholder`,`aria-label`],ve={class:`rows scroll-pane`},Q=[`data-pick`,`aria-current`,`onClick`],ye={class:`who`},be=[`aria-label`],xe=[`title`],Se=[`checked`,`aria-label`,`onChange`],Ce={key:0,class:`muted empty-line`},we={key:1,class:`archive-list`},Te={class:`row`},Ee=[`data-restore-session`,`onClick`],De=[`data-delete-session`,`onClick`],Oe=A(M({__name:`SessionList`,props:{sessions:{},selected:{},unread:{}},emits:[`select`,`add`,`toggle`,`restore`,`remove`],setup(t,{emit:s}){let c=t,f=s,p=u(``),m=o(()=>c.sessions.filter(e=>!e.archived&&`${e.name} ${e.id}`.toLowerCase().includes(p.value.toLowerCase()))),_=o(()=>c.sessions.filter(e=>e.archived));return(o,s)=>(x(),l(`div`,ge,[h(`div`,_e,[h(`h2`,null,[S(e(L(a)(`会话`))+` `,1),h(`small`,null,e(m.value.length),1)]),h(`button`,{class:`small primary`,onClick:s[0]||=e=>f(`add`)},e(L(a)(`+ 添加会话`)),1)]),b(h(`input`,{"onUpdate:modelValue":s[1]||=e=>p.value=e,type:`search`,placeholder:L(a)(`搜索名称或号码`),"aria-label":L(a)(`搜索会话`)},null,8,Z),[[i,p.value]]),h(`div`,ve,[(x(!0),l(r,null,w(m.value,r=>(x(),l(`div`,{key:r.id,class:d([`session-row`,{selected:r.id===t.selected,paused:!r.enabled}])},[h(`button`,{class:`row-main`,"data-pick":r.id,"aria-current":r.id===t.selected?`true`:void 0,onClick:e=>f(`select`,r.id)},[h(`span`,{class:`badge`,style:n({"--hue":L(H)(r.id)})},e(r.kind===`private`?L(a)(`私`):L(a)(`群`)),5),h(`span`,ye,[h(`b`,null,e(L(U)(r)),1),h(`small`,null,e(r.enabled?L(a)(`参与中`):L(a)(`已暂停`)),1)]),t.unread[r.id]?(x(),l(`span`,{key:0,class:`unread`,"aria-label":L(a)(`{v} 条没细看`,{v:t.unread[r.id]})},e(t.unread[r.id]),9,be)):g(``,!0)],8,Q),h(`label`,{class:`mini-switch`,title:r.enabled?L(a)(`暂停参与`):L(a)(`开启参与`)},[h(`input`,{type:`checkbox`,checked:r.enabled,"aria-label":L(a)(`参与:{v}`,{v:L(U)(r)}),onChange:e=>f(`toggle`,r)},null,40,Se)],8,xe)],2))),128)),m.value.length?g(``,!0):(x(),l(`p`,Ce,e(L(a)(`暂无匹配的会话。`)),1)),_.value.length?(x(),l(`details`,we,[h(`summary`,null,e(L(a)(`已归档 · {length}`,{length:_.value.length})),1),(x(!0),l(r,null,w(_.value,t=>(x(),l(`article`,{key:t.id},[h(`b`,null,e(L(U)(t)),1),h(`div`,Te,[h(`button`,{class:`small`,"data-restore-session":t.id,onClick:e=>f(`restore`,t.id)},e(L(a)(`恢复`)),9,Ee),h(`button`,{class:`small danger`,"data-delete-session":t.id,onClick:e=>f(`remove`,t.id)},e(L(a)(`永久删除`)),9,De)])]))),128))])):g(``,!0)])]))}}),[[`__scopeId`,`data-v-9b8dcbac`]]),ke={class:`muted`},Ae={key:0,class:`notice`,role:`status`},je=[`pattern`,`inputmode`],Me=[`disabled`],Ne=M({__name:`AddSession`,props:{open:{type:Boolean},channel:{},ready:{type:Boolean}},emits:[`close`,`add`],setup(t,{emit:n}){let r=t,i=u(`group`),s=n,c=o(()=>r.channel===`qqbot`),d=o(()=>c.value?`[0-9A-Za-z_\\-]{8,64}`:`\\d{4,20}`);function f(e){let t=e.target;s(`add`,{...Object.fromEntries(new FormData(t)),channel:r.channel})}return(n,r)=>(x(),m(ne,{open:t.open,title:L(a)(`添加群聊 / 私聊`),eyebrow:L(a)(`NEW PLACE`),width:`440px`,onClose:r[1]||=e=>s(`close`)},{default:R(()=>[h(`form`,{id:`addSession`,class:`stack`,onSubmit:p(f,[`prevent`])},[h(`p`,ke,e(L(a)(`添加之后再决定要不要让 TA 参与;开不开口仍由 TA 自己决定。`)),1),c.value&&!t.ready?(x(),l(`p`,Ae,e(L(a)(`请先在「连接 QQ」里填写机器人的 AppID`)),1)):g(``,!0),F(J,{label:L(a)(`类型`)},{default:R(()=>[F(V,{modelValue:i.value,"onUpdate:modelValue":r[0]||=e=>i.value=e,name:`kind`,"aria-label":L(a)(`类型`),options:[{value:`group`,label:L(a)(`群聊`)},{value:`private`,label:L(a)(`私聊`)}]},null,8,[`modelValue`,`aria-label`,`options`])]),_:1},8,[`label`]),F(J,{label:c.value?L(a)(`群 openid / 用户 openid`):L(a)(`群号 / QQ 号`),hint:c.value?L(a)(`8–64 位字母、数字、下划线或短横线。官方平台的会话通常会在有人 @ 她或私聊她时自动出现,这里只用于提前登记。`):L(a)(`4–20 位数字`)},{default:R(()=>[h(`input`,{name:`id`,required:``,pattern:d.value,inputmode:c.value?`text`:`numeric`,autocomplete:`off`},null,8,je)]),_:1},8,[`label`,`hint`]),F(J,{label:L(a)(`显示名称`)},{default:R(()=>[...r[2]||=[h(`input`,{name:`name`,required:``,maxlength:`100`,autocomplete:`off`},null,-1)]]),_:1},8,[`label`]),h(`button`,{class:`primary`,disabled:c.value&&!t.ready},e(L(a)(`添加会话`)),9,Me)],32)]),_:1},8,[`open`,`title`,`eyebrow`]))}}),Pe={class:`transcript`},Fe={class:`t-head`},Ie=[`aria-label`,`title`],Le={class:`t-title`},Re={id:`liveStatus`,class:`faint`},ze={class:`t-tools`},Be=[`disabled`],Ve={id:`liveMessages`,class:`chat-messages scroll-pane`},He={key:0,class:`day-label`},Ue=[`data-status`],We={key:0},Ge={class:`body`},Ke={class:`message-meta`},qe={key:0,class:`chip`,"data-tone":`warn`},Je={class:`text`},Ye=[`aria-expanded`,`onClick`],Xe={key:0,class:`why`},Ze={key:0,class:`muted`},Qe={key:0},$e={key:1},et={key:2,class:`fb`},tt={class:`composer-note`},nt={class:`chip`,"data-tone":`warn`},rt={class:`faint`},it={class:`composer-row`},at={class:`uid`},ot={class:`sr-only`},st=[`title`],ct=[`placeholder`,`onKeydown`],lt={class:`check mention`},ut=[`disabled`],dt={key:1,class:`composer off`},ft={class:`faint`},pt=A(M({__name:`Transcript`,props:{session:{},sessionId:{},events:{},traces:{},decisions:{},status:{},demo:{type:Boolean},compact:{type:Boolean},sending:{type:Boolean}},emits:[`simulate`,`feedback`,`openList`,`openReplay`],setup(t,{expose:_,emit:v}){let C=t,T=v,E=c({glanced:D(`TA 扫了一眼`),silent:D(`TA 看了,没出声`),deferred:D(`TA 睡着了,醒来再看`)}),O=u(`10001`),k=u(``),A=u(!0),j=u(),M=u({}),ee=()=>B(s(),{month:`long`,day:`numeric`,weekday:`short`}),I=o(()=>{let e=C.events.map(e=>({type:`message`,key:`m`+e.seq,time:e.time,event:e})),t=C.events[0]?.time??2**53-1;for(let n of C.traces)E[n.status]&&n.time>=t&&e.push({type:`divider`,key:`t`+n.id,time:n.time,status:n.status,reasons:[n.reason].filter(Boolean),count:1});e.sort((e,t)=>e.time-t.time);let n=[],r=``;for(let t of e){let e=ee().format(t.time);e!==r&&(r=e,n.push({type:`day`,key:`d`+t.time,time:t.time,label:e}));let i=n[n.length-1];if(t.type===`divider`&&i?.type===`divider`&&i.status===t.status){i.count+=1,t.reasons[0]&&!i.reasons.includes(t.reasons[0])&&i.reasons.push(t.reasons[0]);continue}n.push(t)}return n});function R(e){let t=String(e.payload.text||``);return C.decisions.filter(n=>n.session_id===e.session_id&&n.reply&&n.reply.includes(t)&&Math.abs(n.time-e.time)<6e5).sort((t,n)=>Math.abs(t.time-e.time)-Math.abs(n.time-e.time))[0]}function te(e){return C.traces.filter(t=>t.status===`sent`&&t.time<=e.time+1e3).sort((e,t)=>t.time-e.time)[0]}async function U(e){if(M.value[e.seq]){let{[e.seq]:t,...n}=M.value;M.value=n;return}M.value={...M.value,[e.seq]:{loading:!0,detail:null}};let t=te(e),n=null;try{n=t?await Y(t.id):null}catch{n=null}M.value[e.seq]&&(M.value={...M.value,[e.seq]:{loading:!1,detail:n}})}async function W(){await y(),j.value?.focus()}function G(){k.value.trim()&&!C.sending&&(T(`simulate`,{userId:O.value,text:k.value,mentioned:A.value}),k.value=``)}return _({focusComposer:W}),(o,s)=>(x(),l(`section`,Pe,[h(`header`,Fe,[t.compact?(x(),l(`button`,{key:0,class:`icon-button`,"aria-label":L(a)(`全部会话`),title:L(a)(`全部会话`),onClick:s[0]||=e=>T(`openList`)},` ☰ `,8,Ie)):g(``,!0),t.session?(x(),l(`span`,{key:1,class:`badge`,style:n({"--hue":L(H)(t.session.id)})},e(t.session.kind===`private`?L(a)(`私`):L(a)(`群`)),5)):g(``,!0),h(`div`,Le,[h(`h2`,null,e(t.session?.name||L(a)(`选择一段对话`)),1),h(`span`,Re,e(t.status),1)]),h(`div`,ze,[h(`button`,{class:`small sim-button`,disabled:!t.sessionId,onClick:W},e(L(a)(`模拟消息`)),9,Be),h(`button`,{class:`small`,onClick:s[1]||=e=>T(`openReplay`)},e(L(a)(`回到那一刻`)),1)])]),h(`div`,Ve,[(x(!0),l(r,null,w(I.value,t=>(x(),l(r,{key:t.key},[t.type===`day`?(x(),l(`div`,He,[h(`span`,null,e(t.label),1)])):t.type===`divider`?(x(),l(`div`,{key:1,class:`divider`,"data-status":t.status},[h(`span`,null,e(L(E)[t.status])+e(t.count>1?` × ${t.count}`:``),1),t.reasons.length?(x(),l(`small`,We,e(t.reasons.slice(0,2).join(`;`)),1)):g(``,!0)],8,Ue)):(x(),l(`article`,{key:2,class:d([`message`,{bot:t.event.role===`assistant`,simulated:t.event.payload.simulated}])},[t.event.role===`assistant`?g(``,!0):(x(),l(`span`,{key:0,class:`avatar`,style:n({"--hue":L(H)(String(t.event.payload.userId||t.event.payload.name))}),"aria-hidden":`true`},e(L(K)(t.event.payload.name)),5)),h(`div`,Ge,[h(`div`,Ke,[h(`b`,null,e(t.event.payload.name),1),h(`time`,null,e(L(q)(t.event.time)),1),t.event.payload.simulated?(x(),l(`span`,qe,e(L(a)(`模拟`)),1)):g(``,!0)]),h(`p`,Je,e(t.event.payload.text),1),t.event.role===`assistant`?(x(),l(r,{key:0},[h(`button`,{class:`text-button why-toggle`,"aria-expanded":!!M.value[t.event.seq],onClick:e=>U(t.event)},e(M.value[t.event.seq]?L(a)(`收起`):L(a)(`为什么这么说`)),9,Ye),M.value[t.event.seq]?(x(),l(`div`,Xe,[M.value[t.event.seq].loading?(x(),l(`p`,Ze,e(L(a)(`正在翻当时的记录…`)),1)):(x(),l(r,{key:1},[M.value[t.event.seq].detail?.data?.decision?.appraisal?(x(),l(`p`,Qe,[h(`b`,null,e(L(a)(`TA 当时的理解:`)),1),S(e(M.value[t.event.seq].detail.data.decision.appraisal),1)])):g(``,!0),h(`p`,null,[h(`b`,null,e(L(a)(`理由:`)),1),S(e(M.value[t.event.seq].detail?.data?.reason||M.value[t.event.seq].detail?.data?.decision?.reason||R(t.event)?.reason||L(a)(`没有留下理由。`)),1)]),M.value[t.event.seq].detail?.data?.snapshot?.knowledge?.length?(x(),l(`p`,$e,[h(`b`,null,e(L(a)(`引用的资料:`)),1),S(e(M.value[t.event.seq].detail.data.snapshot.knowledge.map(e=>e.title).join(`、`)),1)])):g(``,!0),R(t.event)?(x(),l(`label`,et,[S(e(L(a)(`这句怎么样?`))+` `,1),F(V,{"model-value":R(t.event).feedback||``,"aria-label":L(a)(`这句怎么样?`),options:[{value:``,label:L(a)(`选择评价`)},...Object.entries(L(N).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>T(`feedback`,R(t.event).id,e)},null,8,[`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])):g(``,!0)],64))])):g(``,!0)],64)):g(``,!0)])],2))],64))),128)),t.events.length?g(``,!0):(x(),m(P,{key:0,title:t.sessionId?L(a)(`等待新的消息`):L(a)(`选择一段对话`),text:L(a)(`群聊与私聊消息会在这里实时出现;TA 扫一眼、没出声也会留下一条分隔线。`)},null,8,[`title`,`text`]))]),t.demo?(x(),l(`form`,{key:0,id:`simulate`,class:`composer`,onSubmit:p(G,[`prevent`])},[h(`div`,tt,[h(`span`,nt,e(L(a)(`模拟消息`)),1),h(`span`,rt,e(L(a)(`作为一条模拟消息走完整流程,写进模拟会话;不会发到 QQ,也不进入真实记忆。`)),1)]),h(`div`,it,[h(`label`,at,[h(`span`,ot,e(L(a)(`体验用户 ID`)),1),b(h(`input`,{"onUpdate:modelValue":s[2]||=e=>O.value=e,name:`userId`,pattern:`\\d{4,20}`,required:``,title:L(a)(`体验用户 ID`)},null,8,st),[[i,O.value]])]),b(h(`textarea`,{ref_key:`composer`,ref:j,"onUpdate:modelValue":s[3]||=e=>k.value=e,name:`text`,rows:`1`,placeholder:L(a)(`以这位群友的身份说一句…`),required:``,onKeydown:f(p(G,[`exact`,`prevent`]),[`enter`])},null,40,ct),[[i,k.value]]),h(`label`,lt,[b(h(`input`,{"onUpdate:modelValue":s[4]||=e=>A.value=e,type:`checkbox`},null,512),[[z,A.value]]),S(e(L(a)(`视为 @`)),1)]),h(`button`,{class:`primary`,disabled:t.sending||!t.sessionId},e(t.sending?L(a)(`TA 在想…`):L(a)(`发送模拟消息`)),9,ut)])],32)):(x(),l(`div`,dt,[h(`span`,ft,e(L(a)(`真实模式下不能模拟消息。想看看 TA 会怎么回,可以「和 TA 聊聊」——那是试聊,什么都不写入。`)),1),h(`button`,{class:`small`,onClick:s[5]||=e=>L(N).chatOpen=!0},e(L(a)(`和 TA 聊聊`)),1)]))]))}}),[[`__scopeId`,`data-v-849aeb0f`]]),mt={class:`panel`},ht={key:0,class:`panel-body`},gt={key:1,class:`panel-body scroll-pane`},_t={class:`here`},vt={class:`facts`},yt={class:`eyebrow`},bt={key:1,class:`muted`},xt={key:2,class:`faint`},St={class:`eyebrow`},Ct={class:`faint`},wt={class:`row actions`},Tt=[`data-toggle`],Et=[`data-archive`],Dt=[`open`],Ot=[`data-session`],kt={class:`faint`},At=[`value`],jt={class:`faint`},Mt={class:`check`},Nt={class:`advanced-policy`},Pt={class:`stack tight`},Ft={class:`check`},It={class:`check`},Lt={class:`check`},Rt={class:`save-bar`},zt={class:`primary`,type:`submit`},Bt={class:`faint`},Vt={class:`fold summary-list`},Ht={key:0,class:`muted`},Ut={class:`faint`},Wt={key:0},Gt={key:2,class:`panel-body feedback-pane`},Kt={class:`faint`},qt={id:`feedbackList`,class:`scroll-pane`},Jt={class:`faint`},Yt={class:`reply-excerpt`},Xt={key:0,class:`muted`},Zt={class:`pagination`},Qt=[`disabled`],$t=[`disabled`],$=6,en=A(M({__name:`SessionPanel`,props:E({session:{},group:{},summaries:{},decisions:{}},{settingsOpen:{type:Boolean,default:!1},settingsOpenModifiers:{},tab:{default:`here`},tabModifiers:{}}),emits:E([`toggle`,`archive`,`clear`,`save`,`feedback`],[`update:settingsOpen`,`update:tab`]),setup(t,{emit:n}){let s=t,f=T(t,`settingsOpen`),m=T(t,`tab`),v=n,y=c([D(`近期细摘要`),D(`中期摘要`),D(`较早摘要`),D(`远期摘要`)]),C=u(0),E=o(()=>s.decisions.filter(e=>e.session_id===s.session?.id&&e.reply)),O=o(()=>Math.max(1,Math.ceil(E.value.length/$))),k=u(!1),A=u({});_(()=>s.session?.policy,e=>{A.value={...e||{}}},{immediate:!0,deep:!0}),_(()=>s.session?.id,()=>{C.value=0,k.value=!1});function j(e){let t=e?.face;return t?[t.role,t.tone,t.content].filter(Boolean).join(`
2
+
3
+ `):``}function M(e){let t=e.target,n=Object.fromEntries(new FormData(t));for(let e of[`aggregateMs`,`maxWaitMs`,`contextMessages`,`maxReply`])n[e]=Number(n[e]);for(let e of[`memory`,`selectiveVision`,`deepCheck`,`compaction`])n[e]=t.elements.namedItem(e).checked;v(`save`,n)}return(n,o)=>(x(),l(`section`,mt,[F(G,{modelValue:m.value,"onUpdate:modelValue":o[0]||=e=>m.value=e,label:L(a)(`这个会话`),items:[{key:`here`,label:L(a)(`这个会话`)},{key:`feedback`,label:L(a)(`反馈`)}]},null,8,[`modelValue`,`label`,`items`]),t.session?m.value===`here`?(x(),l(`div`,gt,[h(`div`,_t,[h(`div`,vt,[h(`div`,null,[h(`span`,yt,e(L(a)(`TA 在这里的样子`)),1),j(t.group)?(x(),l(r,{key:0},[h(`p`,{class:d([`face-body`,k.value?`open`:`clamp`])},e(j(t.group)),3),j(t.group).length>96?(x(),l(`button`,{key:0,type:`button`,class:`text-button`,onClick:o[1]||=e=>k.value=!k.value},e(k.value?L(a)(`收起`):L(a)(`展开完整内容`)),1)):g(``,!0)],64)):(x(),l(`p`,bt,e(L(a)(`还没有形成在这里的样子。`)),1)),t.group?.face?.aspiration?(x(),l(`small`,xt,e(L(a)(`想成为:{aspiration}`,{aspiration:t.group.face.aspiration})),1)):g(``,!0)]),h(`div`,null,[h(`span`,St,e(t.session.kind===`private`?L(a)(`这段私聊给 TA 的感觉`):L(a)(`这个群给 TA 的感觉`)),1),h(`p`,null,e(t.group?.bond?.feel||L(a)(`刚来这里不久`)),1)])]),h(`p`,Ct,e(L(a)(`在这里开不开口由 TA 自己决定:没有参与概率和冷却。TA 对这里每个人的感觉在「人际」里看。`)),1),h(`div`,wt,[h(`button`,{id:`toggleSession`,"data-toggle":t.session.id,class:d({primary:!t.session.enabled}),onClick:o[2]||=e=>v(`toggle`,t.session)},e(t.session.enabled?L(a)(`暂停参与`):L(a)(`开启参与`)),11,Tt),h(`button`,{"data-archive":t.session.id,onClick:o[3]||=e=>v(`archive`,t.session.id)},e(L(a)(`归档`)),9,Et),h(`button`,{id:`clearLiveContext`,onClick:o[4]||=e=>v(`clear`)},e(L(a)(`清空上下文`)),1)]),h(`details`,{class:`fold session-settings`,open:f.value,onToggle:o[19]||=e=>f.value=e.target.open},[h(`summary`,null,e(L(a)(`会话设置`)),1),(x(),l(`form`,{key:t.session.id,"data-session":t.session.id,class:`stack tight`,onSubmit:p(M,[`prevent`]),onInput:o[17]||=e=>L(N).dirty=!0,onChange:o[18]||=e=>L(N).dirty=!0},[h(`p`,kt,e(L(a)(`所有会话和独处都用模型库里的默认模型;其余已启用的模型按列表顺序做备用。她在这个聊天窗里用什么样子,由她自己从经历里决定。`)),1),h(`label`,null,[S(e(L(a)(`会话名称`))+` `,1),h(`input`,{name:`name`,maxlength:`100`,autocomplete:`off`,value:t.session.name},null,8,At),h(`small`,jt,e(L(a)(`官方机器人平台不提供群名称,可以在这里自己起一个`)),1)]),h(`label`,Mt,[b(h(`input`,{name:`selectiveVision`,type:`checkbox`,"onUpdate:modelValue":o[5]||=e=>A.value.selectiveVision=e,onInput:o[6]||=p(()=>{},[`stop`])},null,544),[[z,A.value.selectiveVision]]),S(e(L(a)(`节能看图:只在被 @ 或明确要求时,看这一条和附近的图`)),1)]),h(`details`,Nt,[h(`summary`,null,e(L(a)(`高级策略`)),1),h(`div`,Pt,[h(`label`,null,[S(e(L(a)(`聚合窗口 ms`)),1),b(h(`input`,{name:`aggregateMs`,type:`number`,"onUpdate:modelValue":o[7]||=e=>A.value.aggregateMs=e},null,512),[[i,A.value.aggregateMs,void 0,{number:!0}]])]),h(`label`,null,[S(e(L(a)(`最长等待 ms`)),1),b(h(`input`,{name:`maxWaitMs`,type:`number`,"onUpdate:modelValue":o[8]||=e=>A.value.maxWaitMs=e},null,512),[[i,A.value.maxWaitMs,void 0,{number:!0}]])]),h(`label`,null,[S(e(L(a)(`近期原文条数(10–500)`)),1),b(h(`input`,{name:`contextMessages`,type:`number`,min:`10`,max:`500`,"onUpdate:modelValue":o[9]||=e=>A.value.contextMessages=e},null,512),[[i,A.value.contextMessages,void 0,{number:!0}]])]),h(`label`,null,[S(e(L(a)(`一轮总字数`)),1),b(h(`input`,{name:`maxReply`,type:`number`,"onUpdate:modelValue":o[10]||=e=>A.value.maxReply=e},null,512),[[i,A.value.maxReply,void 0,{number:!0}]])]),h(`label`,Ft,[b(h(`input`,{name:`compaction`,type:`checkbox`,"onUpdate:modelValue":o[11]||=e=>A.value.compaction=e,onInput:o[12]||=p(()=>{},[`stop`])},null,544),[[z,A.value.compaction]]),S(e(L(a)(`上下文压缩:更早的聊天定期整理成分层摘要,越近越详细`)),1)]),h(`label`,It,[b(h(`input`,{name:`memory`,type:`checkbox`,"onUpdate:modelValue":o[13]||=e=>A.value.memory=e,onInput:o[14]||=p(()=>{},[`stop`])},null,544),[[z,A.value.memory]]),S(e(L(a)(`长期记忆`)),1)]),h(`label`,Lt,[b(h(`input`,{name:`deepCheck`,type:`checkbox`,"onUpdate:modelValue":o[15]||=e=>A.value.deepCheck=e,onInput:o[16]||=p(()=>{},[`stop`])},null,544),[[z,A.value.deepCheck]]),S(e(L(a)(`倾诉、纠正和危机时额外复审一次回复`)),1)])])]),h(`div`,Rt,[h(`button`,zt,e(L(a)(`保存会话设置`)),1),h(`small`,Bt,e(L(N).dirty?L(a)(`有未保存的修改`):L(a)(`保存后下一轮生效`)),1)])],40,Ot))],40,Dt),h(`details`,Vt,[h(`summary`,null,e(L(a)(`语境摘要 · {length} 段`,{length:t.summaries.length})),1),t.summaries.length?g(``,!0):(x(),l(`p`,Ht,e(L(a)(`还没有摘要。聊天超过近期原文条数后,更早的内容会自动整理到这里。`)),1)),(x(!0),l(r,null,w(t.summaries,t=>(x(),l(`article`,{key:t.id,class:`summary`},[h(`header`,null,[h(`b`,null,e(L(y)[t.level]||L(a)(`摘要`)),1),h(`small`,Ut,e(L(a)(`{period} · {messages} 条消息`,{period:t.period,messages:t.messages})),1)]),h(`p`,null,e(t.summary),1),t.keyPoints?.length?(x(),l(`ul`,Wt,[(x(!0),l(r,null,w(t.keyPoints,(t,n)=>(x(),l(`li`,{key:n},e(t.open?L(a)(`未完:`):``)+e(t.text),1))),128))])):g(``,!0)]))),128))])])])):(x(),l(`div`,Gt,[h(`p`,Kt,e(L(a)(`针对具体回复评价口吻;这是 TA 在这个会话里听到的话,不会改写天性。`)),1),h(`div`,qt,[(x(!0),l(r,null,w(E.value.slice(C.value*$,C.value*$+$),t=>(x(),l(`article`,{key:t.id,class:`feedback-item`},[h(`time`,Jt,e(L(q)(t.time)),1),h(`p`,Yt,e(t.reply),1),h(`label`,null,[S(e(L(a)(`这句回复怎么样?`))+` `,1),F(V,{"data-feedback":t.id,"model-value":t.feedback||``,"aria-label":L(a)(`这句回复怎么样?`),options:[{value:``,label:L(a)(`选择评价`)},...Object.entries(L(N).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>v(`feedback`,t.id,e)},null,8,[`data-feedback`,`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])]))),128)),E.value.length?g(``,!0):(x(),l(`p`,Xt,e(L(a)(`还没有可以评价的回复。`)),1))]),h(`div`,Zt,[h(`button`,{class:`small`,disabled:C.value===0,onClick:o[20]||=e=>C.value--},`←`,8,Qt),h(`span`,null,e(C.value+1)+` / `+e(O.value),1),h(`button`,{class:`small`,disabled:C.value+1>=O.value,onClick:o[21]||=e=>C.value++},` → `,8,$t)])])):(x(),l(`div`,ht,[F(P,{title:L(a)(`还没有选中会话`),text:L(a)(`从左边选一个群聊或私聊。`)},null,8,[`title`,`text`])]))]))}}),[[`__scopeId`,`data-v-96fbad3d`]]),tn={class:`replay`},nn={class:`col source`},rn={class:`col-head`},an={id:`events`,class:`scroll-pane`},on={key:0},sn={key:1,class:`muted`},cn=[`disabled`],ln={class:`faint`},un={class:`col`},dn={class:`col-head`},fn={id:`traces`,class:`scroll-pane`},pn=[`data-trace`,`onClick`],mn=[`data-tone`],hn={key:0,class:`muted`},gn={class:`col`},_n={class:`col-head`},vn={class:`scroll-pane inspect`},yn={key:0,class:`metrics`},bn={id:`traceTokens`},xn={key:1,class:`notice`},Sn={id:`traceDetail`},Cn=A(M({__name:`ReplaySheet`,props:{open:{type:Boolean},sessionId:{},events:{}},emits:[`close`,`load`],setup(t,{emit:n}){let o=t,s=n,f=c({sent:D(`开口了`),silent:D(`没出声`),glanced:D(`扫了一眼`),deferred:D(`睡着了`),running:D(`处理中`),error:D(`失败`),complete:D(`完成`),stale:D(`旧稿作废`),cancelled:D(`已取消`),interrupted:D(`已中断`)}),v=c({live:D(`真实聊天`),demo:D(`模拟预览`),replay:D(`测试回放`),memory:D(`记忆整理`),summary:D(`语境压缩`)}),y=u([]),C=u(null),T=u(``),E=u(``),O=u(!1),k=u(``);async function A(){o.sessionId&&(y.value=await oe(o.sessionId))}function j(){let e=o.events.map(e=>Number(e.seq));e.length&&(T.value||=String(Math.min(...e)),E.value||=String(Math.max(...e)))}async function M(){if(!O.value){O.value=!0;try{I(a(`回放正在运行`));let e=await X({session:o.sessionId,from:Number(T.value),to:Number(E.value)});k.value=JSON.stringify(e,null,2),I(a(`回放结束`)),await A()}catch(e){I(e.message,!0)}finally{O.value=!1}}}async function N(e){C.value=await Y(e),k.value=JSON.stringify(C.value,null,2)}function P(e){return e?a(`输入 {v}(缓存读 {v2} / 写 {v3})· 输出 {v4}`,{v:W(e.input),v2:W(e.cachedRead),v3:W(e.cacheWrite),v4:W(e.output)}):`—`}return _(()=>[o.open,o.sessionId],()=>{o.open&&(T.value=``,E.value=``,C.value=null,j(),A())},{immediate:!0}),_(()=>o.events.length,j),(n,o)=>(x(),m(ne,{open:t.open,title:L(a)(`回到那一刻`),eyebrow:`REPLAY`,width:`1180px`,onClose:o[3]||=e=>s(`close`)},{default:R(()=>[h(`div`,tn,[h(`section`,nn,[h(`div`,rn,[h(`h3`,null,e(L(a)(`历史消息`)),1),h(`button`,{id:`loadMessages`,class:`small`,onClick:o[0]||=e=>{s(`load`),A()}},e(L(a)(`↻ 加载`)),1)]),h(`div`,an,[t.events.length?(x(),l(`table`,on,[h(`thead`,null,[h(`tr`,null,[h(`th`,null,e(L(a)(`序号`)),1),h(`th`,null,e(L(a)(`发送者`)),1),h(`th`,null,e(L(a)(`消息`)),1)])]),h(`tbody`,null,[(x(!0),l(r,null,w([...t.events].reverse(),t=>(x(),l(`tr`,{key:t.seq},[h(`td`,null,e(t.seq),1),h(`td`,null,e(t.payload.name),1),h(`td`,null,e(t.payload.text),1)]))),128))])])):(x(),l(`p`,sn,e(L(a)(`当前会话暂无历史消息。`)),1))]),h(`form`,{class:`range`,onSubmit:p(M,[`prevent`])},[h(`label`,null,[S(e(L(a)(`起始序号`)),1),b(h(`input`,{"onUpdate:modelValue":o[1]||=e=>T.value=e,name:`from`,type:`number`,required:``},null,512),[[i,T.value]])]),h(`label`,null,[S(e(L(a)(`截止序号`)),1),b(h(`input`,{"onUpdate:modelValue":o[2]||=e=>E.value=e,name:`to`,type:`number`,required:``},null,512),[[i,E.value]])]),h(`button`,{id:`replay`,class:`primary`,disabled:O.value||!t.events.length},e(O.value?L(a)(`正在回放…`):L(a)(`回放这段对话`)),9,cn),h(`p`,ln,e(L(a)(`隔离回放:只测试回复,不发送 QQ,不修改真实记忆和 TA 的心智。`)),1)],32)]),h(`section`,un,[h(`div`,dn,[h(`h3`,null,e(L(a)(`处理记录`)),1)]),h(`div`,fn,[(x(!0),l(r,null,w(y.value,t=>(x(),l(`button`,{key:t.id,"data-trace":t.id,class:d([`trace-row`,{selected:C.value?.id===t.id}]),onClick:e=>N(t.id)},[h(`span`,null,[h(`time`,null,e(L(q)(t.time)),1),h(`small`,{class:`chip`,"data-tone":t.status===`error`?`danger`:`quiet`},e(L(f)[t.status]||t.status),9,mn)]),h(`p`,null,e(t.reason||t.error||L(a)(`查看处理详情`)),1)],10,pn))),128)),y.value.length?g(``,!0):(x(),l(`p`,hn,e(L(a)(`有新的消息处理记录后会显示在这里。`)),1))])]),h(`section`,gn,[h(`div`,_n,[h(`h3`,null,e(L(a)(`运行详情`)),1)]),h(`div`,vn,[C.value?(x(),l(`div`,yn,[h(`span`,null,[S(e(L(a)(`模式`)),1),h(`b`,null,e(L(v)[C.value.mode]||C.value.mode),1)]),h(`span`,null,[S(e(L(a)(`耗时`)),1),h(`b`,null,e(C.value.data?.elapsed??`—`)+` ms`,1)]),h(`span`,null,[S(e(L(a)(`模型调用`)),1),h(`b`,null,e(L(a)(`{v} 次`,{v:C.value.data?.calls?.length??0})),1)]),h(`span`,bn,[o[4]||=S(`Token`,-1),h(`b`,null,e(P(C.value.data?.tokens)),1)])])):g(``,!0),C.value?(x(),l(`p`,xn,e(C.value.reason||C.value.data?.decision?.reason||L(a)(`展开下方查看上下文、模型输入与原始输出。`)),1)):g(``,!0),h(`pre`,Sn,e(k.value||L(a)(`选择一条处理记录,查看最终上下文、Prompt、原始输出、Token 与耗时。`)),1)])])])]),_:1},8,[`open`,`title`]))}}),[[`__scopeId`,`data-v-839b3181`]]),wn={key:0,class:`card col list-col`},Tn={class:`card col talk-col`},En={class:`card col panel-col`},Dn={class:`sheet-list`},On=A(M({__name:`ChatsPage`,setup(e){let n=ie(`(max-width: 1100px)`),r=o(()=>N.core?.sessions||[]),i=o(()=>r.value.filter(e=>!e.archived)),c=sessionStorage.activeSession,f=u(i.value.some(e=>e.id===c)?c:i.value[0]?.id||``),p=o(()=>i.value.find(e=>e.id===f.value)||null),b=u([]),S=u([]),w=u([]),T=u([]),E=u({}),D=u(``),k=u(!1),A=u(!1),M=u(!1),P=u(N.sub===`settings`),z=u(N.sub===`replay`),B=u(`here`),V=o(()=>N.health?.decisions||[]),H=o(()=>T.value.find(e=>e.session===f.value)||null),U=!1,W=!1,G=!1,K=``,q=0,J=null,Y=0;async function X(e=!1){if(!f.value)return;if(U){W=!0,G||=e;return}let t=f.value,n=e||K!==t;U=!0;try{let[e,r]=await Promise.all([ae(t,n?void 0:q),oe(t,{limit:30,compact:!0})]);if(f.value!==t)return;let i=document.getElementById(`liveMessages`),o=K!==t||!i||i.scrollHeight-i.scrollTop-i.clientHeight<80;n?(b.value=e.reverse(),q=0):e.length&&(b.value=[...b.value,...e]);for(let t of e)q=Math.max(q,Number(t.seq)||0);!n&&e.length===100&&(W=!0),S.value=r,K=t,await y();let c=document.getElementById(`liveMessages`);c&&o&&(c.scrollTop=c.scrollHeight),D.value=a(`实时同步 · `)+new Date().toLocaleTimeString(s())}catch(e){D.value=a(`同步失败:`)+e.message}finally{if(U=!1,f.value!==t)W=!1,X();else if(W){let e=G;W=!1,G=!1,X(e)}}}async function ge(){try{let[e,t]=await Promise.all([te.bonds(),te.overview()]);T.value=e.groups,E.value=Object.fromEntries(t.attention.map(e=>[e.session,e.unread]))}catch{}}_(f,e=>{if(J?.abort(),e&&(sessionStorage.activeSession=e),b.value=[],S.value=[],w.value=[],K=``,q=0,W=!1,G=!1,M.value=!1,X(),!e)return;me(e).then(t=>{f.value===e&&(w.value=[...t].reverse())}).catch(()=>{});let t=new AbortController;J=t;let n=``;re(e=>{let t=Number(e?.messages);Number.isSafeInteger(t)&&t<q&&(K=``,q=0,b.value=[]);let r=JSON.stringify(e),i=n!==``&&r!==n;n=r,i&&X()},{session:e,signal:t.signal,reconnectMs:1e3})},{immediate:!0}),_(i,e=>{e.some(e=>e.id===f.value)||(f.value=e[0]?.id||``)}),_(B,async e=>{if(e===`feedback`)try{N.health=await C()}catch{}}),_(z,e=>j(e?`replay`:P.value?`settings`:``)),_(()=>N.sub,e=>{e===`replay`&&(z.value=!0),e===`settings`&&(P.value=!0)}),_(()=>N.pulse,ge),O(()=>{ge(),Y=window.setInterval(()=>X(!0),15e3)}),ee(()=>{clearInterval(Y),J?.abort()});async function _e(e){try{let t=await le(e);await v(),f.value=t?.sessionId||`${e.kind}:${e.id}`,P.value=!0,B.value=`here`,A.value=!1,I(a(`会话已添加`))}catch(e){I(e.message,!0)}}async function Z(e){await ue(e.id,!e.enabled),await v()}async function ve(e){await de(e,!0),await v()}async function Q(e){await de(e,!1),await v()}async function ye(e){await t(a(`永久删除这个会话?它的消息和设置都会消失。`),{title:a(`永久删除`),confirmText:a(`删除`),danger:!0})&&(await fe(e),await v())}async function be(e){if(p.value)try{await pe(p.value.id,{...e,name:String(e.name||``).trim()||p.value.name}),N.dirty=!1,I(a(`会话配置已保存`)),await v()}catch(e){I(e.message,!0)}}async function xe(){f.value&&await t(a(`确定清空这个会话的消息上下文吗?长期记忆不会删除。`),{title:a(`清空上下文`),confirmText:a(`清空`),danger:!0})&&(await he(f.value),b.value=[],S.value=[],K=``,q=0,await X(!0))}async function Se(e){if(!k.value&&f.value){k.value=!0;try{let t=await se({sessionId:f.value,...e});I(t.reason||a(`已发送`)),N.health=await C(),await X()}catch(e){I(e.message,!0)}finally{k.value=!1}}}async function Ce(e,t){try{await ce(e,t),I(a(`反馈已保存`)),N.health=await C()}catch(e){I(e.message,!0)}}return(e,t)=>(x(),l(`div`,{class:d([`page chats`,{compact:L(n)}])},[L(n)?g(``,!0):(x(),l(`aside`,wn,[F(Oe,{sessions:r.value,selected:f.value,unread:E.value,onSelect:t[0]||=e=>f.value=e,onAdd:t[1]||=e=>A.value=!0,onToggle:Z,onRestore:Q,onRemove:ye},null,8,[`sessions`,`selected`,`unread`])])),h(`div`,Tn,[F(pt,{"session-id":f.value,session:p.value,events:b.value,traces:S.value,decisions:V.value,status:D.value||L(a)(`请选择会话`),demo:!!L(N).health.settings.demo,compact:L(n),sending:k.value,onSimulate:Se,onFeedback:Ce,onOpenList:t[2]||=e=>M.value=!0,onOpenReplay:t[3]||=e=>z.value=!0},null,8,[`session-id`,`session`,`events`,`traces`,`decisions`,`status`,`demo`,`compact`,`sending`])]),h(`aside`,En,[F(en,{"settings-open":P.value,"onUpdate:settingsOpen":t[4]||=e=>P.value=e,tab:B.value,"onUpdate:tab":t[5]||=e=>B.value=e,session:p.value,group:H.value,summaries:w.value,decisions:V.value,onToggle:Z,onArchive:ve,onClear:xe,onSave:be,onFeedback:Ce},null,8,[`settings-open`,`tab`,`session`,`group`,`summaries`,`decisions`])]),L(n)?(x(),m(ne,{key:1,open:M.value,title:L(a)(`全部会话`),eyebrow:`CHATS`,width:`400px`,onClose:t[8]||=e=>M.value=!1},{default:R(()=>[h(`div`,Dn,[F(Oe,{sessions:r.value,selected:f.value,unread:E.value,onSelect:t[6]||=e=>f.value=e,onAdd:t[7]||=e=>A.value=!0,onToggle:Z,onRestore:Q,onRemove:ye},null,8,[`sessions`,`selected`,`unread`])])]),_:1},8,[`open`,`title`])):g(``,!0),F(Ne,{open:A.value,channel:L(N).health?.connection?.channel===`qqbot`?`qqbot`:`onebot`,ready:!!L(N).health?.connection?.qqbot?.appId,onClose:t[9]||=e=>A.value=!1,onAdd:_e},null,8,[`open`,`channel`,`ready`]),F(Cn,{open:z.value,"session-id":f.value,events:b.value,onClose:t[10]||=e=>z.value=!1,onLoad:t[11]||=e=>X(!0)},null,8,[`open`,`session-id`,`events`])],2))}}),[[`__scopeId`,`data-v-c328f9fe`]]);export{On as default};