luckytri 1.0.3 → 1.0.4
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.
- package/bin/luckytri.js +5 -1
- package/docs/en/CHANGELOG.md +7 -0
- package/docs/en/guide.md +3 -1
- package/docs/en/webui.md +3 -1
- package/docs/zh/CHANGELOG.md +7 -0
- package/docs/zh/guide.md +3 -1
- package/docs/zh/webui.md +3 -1
- package/package.json +4 -3
- package/public/app/assets/{ChatsPage-B83Evc79.js → ChatsPage-CshdtTzV.js} +1 -1
- package/public/app/assets/{Empty-ddEfu0w8.js → Empty-C7yzOBzy.js} +1 -1
- package/public/app/assets/{Field-mIhV5YBA.js → Field-BjFv4Rfn.js} +1 -1
- package/public/app/assets/{HeartPage-BGEn24rc.js → HeartPage-eqo6ru16.js} +1 -1
- package/public/app/assets/{LifePage-BleqPj4O.js → LifePage-CN8GE5NK.js} +1 -1
- package/public/app/assets/{MemoryPage-BM3-0_lF.js → MemoryPage-CCpMN4Wg.js} +1 -1
- package/public/app/assets/{NaturePage-CxxMnQIc.js → NaturePage-C0JqdBTR.js} +2 -2
- package/public/app/assets/{PeoplePage-DDDHESQp.css → PeoplePage-BTU0H1pM.css} +1 -1
- package/public/app/assets/PeoplePage-BXGBbO4u.js +1 -0
- package/public/app/assets/{PluginsPage-DE_-Q3ra.js → PluginsPage-Dksp5AV5.js} +1 -1
- package/public/app/assets/{SystemPage-Be1a6UDP.css → SystemPage-BenIyTvk.css} +1 -1
- package/public/app/assets/{SystemPage-BWQKEQRU.js → SystemPage-Cd7aa8eE.js} +1 -1
- package/public/app/assets/{TimePage-Dzt8t8kL.js → TimePage-Bs_zImb4.js} +1 -1
- package/public/app/assets/{index-DY4e15Kh.css → index-BHZcHUMh.css} +1 -1
- package/public/app/assets/{index-oLIXWxsn.js → index-CyRtYxP3.js} +3 -3
- package/public/app/assets/{knowledge-Dvtmpd45.js → knowledge-BJOIyuJo.js} +1 -1
- package/public/app/assets/{media-keE52cIX.js → media-Bwtx8blz.js} +1 -1
- package/public/app/assets/mind-DtTVrW6Z.js +1 -0
- package/public/app/index.html +4 -4
- package/public/guide.en.html +23 -8
- package/public/guide.html +11 -5
- package/public/tutorial.en.md +3 -1
- package/public/tutorial.md +3 -1
- package/scripts/launch.js +11 -4
- package/scripts/service.js +57 -7
- package/scripts/start-server.js +4 -1
- package/scripts/update-instance.js +201 -0
- package/server/app.js +1 -1
- package/server/core/context-builder.js +2 -2
- package/server/core/context-compactor.js +2 -2
- package/server/core/orchestrator.js +1 -0
- package/server/core/persona-manager.js +10 -0
- package/server/core/reply-delivery.js +10 -0
- package/server/core/response-validator.js +20 -0
- package/server/i18n/errors.js +25 -0
- package/server/index.js +45 -2
- package/server/mind/api.js +25 -0
- package/server/mind/bonds.js +9 -2
- package/server/mind/index.js +2 -0
- package/server/mind/initiative.js +6 -1
- package/server/mind/life/context.js +14 -3
- package/server/mind/life/growth.js +2 -1
- package/server/mind/nature.js +23 -2
- package/server/mind/relationships.js +279 -0
- package/server/mind/schema.js +2 -0
- package/server/mind/time/tasks.js +2 -1
- package/server/mind/view.js +2 -0
- package/server/runtime/installation.js +116 -0
- package/server/runtime/update-execution.js +127 -0
- package/server/runtime/update-manager.js +140 -0
- package/server/startup-lock.js +12 -12
- package/server/storage/archive.js +10 -2
- package/server/storage/backup-lock.js +23 -5
- package/server/storage/data-transfer.js +2 -1
- package/server/studio/display-names.js +1 -0
- package/server/studio/settings.js +1 -0
- package/server/studio/updates.js +14 -3
- package/public/app/assets/PeoplePage-D12cvuy7.js +0 -1
- package/public/app/assets/mind-DYRfFMwh.js +0 -1
package/bin/luckytri.js
CHANGED
|
@@ -3,6 +3,7 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { runtimePaths } from "../server/paths.js";
|
|
5
5
|
import { VERSION } from "../server/version.js";
|
|
6
|
+
import { resolveInstallation } from "../server/runtime/installation.js";
|
|
6
7
|
|
|
7
8
|
const help = `LuckyTri ${VERSION}
|
|
8
9
|
|
|
@@ -44,7 +45,10 @@ try {
|
|
|
44
45
|
if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
|
|
45
46
|
console.log(help);
|
|
46
47
|
} else if (args.includes("--version") || args.includes("-v")) {
|
|
47
|
-
console.log(
|
|
48
|
+
console.log(
|
|
49
|
+
resolveInstallation(runtimePaths().home, runtimePaths().package, VERSION)
|
|
50
|
+
.version,
|
|
51
|
+
);
|
|
48
52
|
} else {
|
|
49
53
|
const command =
|
|
50
54
|
!args.length || args[0] === "--no-browser" ? "launch" : args.shift();
|
package/docs/en/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## 1.0.4 — 2026-10-06
|
|
6
|
+
|
|
7
|
+
- Account-based relationships for people and other robots, with reciprocal family/friend/custom roles, name, reason, visibility and history. Integrated into recognition, conversation, solitude, diaries, initiative and task choices without inventing trust or shared experiences.
|
|
8
|
+
- Removed the fixed LuckyTri identity from built-in nature and reflection prompts. Renamed instances use their configured identity; the exact old shipped rule is repaired without spending an edit. Other robots remain separate accounts.
|
|
9
|
+
- Update and restart stages and validates the official release before stopping, saves a verified data restore point, and switches programs. Failed startup attempts restoration of the previous code and data. Source/npm installs and instance configuration remain intact; repeated clicks coalesce and CLI launches retain managed updates.
|
|
10
|
+
- Added privacy, same-name identity, recovery, real installed-package switching and viewport regression checks. Archive checks verify bytes before parsing SQLite, so damaged files do not expose raw schema errors.
|
|
11
|
+
|
|
5
12
|
|
|
6
13
|
## 1.0.3 — 2026-10-06
|
|
7
14
|
|
package/docs/en/guide.md
CHANGED
|
@@ -205,4 +205,6 @@ Never post API keys, `.env` or private chat text anywhere public.
|
|
|
205
205
|
|
|
206
206
|
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.
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
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.
|
|
209
|
+
|
|
210
|
+
System → Runtime → Version and updates checks npm and links release notes. Update and restart downloads and validates the new program while the instance keeps running, then saves a verified backup and switches. Failed startup attempts recovery of the previous program and data. Managed programs live under the instance's `runtime/releases/`, leaving source and global npm installations untouched; launch commands select the updated program. A manually installed equal or newer source/global package takes precedence. The Nature page uses the server's remaining edit allowance; locally granted extra opportunities are recorded without clearing history.
|
package/docs/en/webui.md
CHANGED
|
@@ -72,4 +72,6 @@ Live messages follow new arrivals while you are at the bottom and keep their pla
|
|
|
72
72
|
- `node tests/ui/ta.mjs --serve`: opens an example world that has lived three days, handy for looking at the interface directly.
|
|
73
73
|
- Screenshots go only into the ignored `workspace/` folder; real chat screenshots and configuration must never be committed.
|
|
74
74
|
|
|
75
|
-
System → Runtime
|
|
75
|
+
System → Runtime checks npm manually, with the current version, last successful check and release notes. Results are cached for five minutes; failures retain the last known information. Update and restart explicitly downloads and checks the program, saves a backup and switches instances. Progress survives restart, the page waits for reconnection, and repeated clicks do not launch a second install.
|
|
76
|
+
|
|
77
|
+
People → Relationships binds people or other robots by account, using themed choices for type, reciprocal roles, name and visibility. Person details show change history; ending a relationship does not delete encounters. Summaries load on demand alongside existing chat caching.
|
package/docs/zh/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
# 版本记录
|
|
4
4
|
|
|
5
|
+
## 1.0.4 — 2026-10-06
|
|
6
|
+
|
|
7
|
+
- 新增按账号绑定的人际关系,支持人与机器人、妹妹姐姐等双向称呼、自定义关系、名字、缘由、公开范围与变化历史。接入人物识别、回复、独处、日记、主动联系和待办选择;不凭空增加信任或相处经历。
|
|
8
|
+
- 修复内置身份规则与回顾提示写死 LuckyTri 的问题。实例使用天性中的名字,旧默认规则自动修正且不消耗修改次数;另一位机器人按独立账号识别。
|
|
9
|
+
- 检查更新新增「更新并重启」:先安装校验,再保留完整数据恢复点并切换;启动失败尝试恢复旧程序和数据。源码、npm 安装、配置与生活数据保留,重复点击合并,启动命令接续已更新程序。
|
|
10
|
+
- 补充关系权限、同名对象、身份、失败恢复、真实安装切换与不同屏幕回归。校验清单先检查文件哈希,再解析数据库,损坏文件不会返回原始 SQLite 结构错误。
|
|
11
|
+
|
|
5
12
|
|
|
6
13
|
## 1.0.3 — 2026-10-06
|
|
7
14
|
|
package/docs/zh/guide.md
CHANGED
|
@@ -205,4 +205,6 @@ TA 的一切都存在这一个数据库里,所以服务运行时会自动备
|
|
|
205
205
|
|
|
206
206
|
「人际 → 在这里的想法」保存她在各处自己想留下的印象、愿望或疑问。由她根据实际相处选择更新,详情显示理由、来源和历史;旧群人格只留在历史里。
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
「人际 → 彼此的关系」可按账号绑定人或另一位机器人,设置妹妹、姐姐、朋友等双向称呼、名字、缘由和公开范围。她在交流、独处、日记与行动选择中知道这份关系;信任和亲近仍依据实际相处,绑定不会制造共同经历。修改和解除保留历史及原有记忆。私下关系只在与对方的私聊中使用,不按同名合并账号。
|
|
209
|
+
|
|
210
|
+
「系统 → 运行开关 → 版本与更新」可检查 npm 最新版本、查看发布说明,随后点击「更新并重启」。下载检查期间继续运行,切换前保留校验备份,启动失败时尝试恢复原版本和数据。新程序装在实例的 `runtime/releases/`,源码和全局 npm 安装保持原样;启动命令会选择已更新的程序。安装了同版本或更新版本的全局包或源码后,以该安装为准。天性页的剩余修改次数以服务端额度为准;本地维护额外授予的机会有独立记录,不清空原版本。
|
package/docs/zh/webui.md
CHANGED
|
@@ -72,4 +72,6 @@ studio-web/src/
|
|
|
72
72
|
- `node tests/ui/ta.mjs --serve`:打开一个过了三天的示例世界,方便直接看界面。
|
|
73
73
|
- 截图只放在被忽略的 `workspace/` 目录,真实聊天截图和配置不得提交。
|
|
74
74
|
|
|
75
|
-
系统 → 运行开关中的「版本与更新」支持手动检查 npm
|
|
75
|
+
系统 → 运行开关中的「版本与更新」支持手动检查 npm 最新版本,显示当前版本、上次成功检查与发布说明。五分钟内复用结果,断网时保留上次信息并显示失败。点击「更新并重启」才下载校验、留备份并切换实例,页面显示阶段,重启期间等待连接恢复;重复点击不产生第二次安装。
|
|
76
|
+
|
|
77
|
+
人际页的「彼此的关系」按账号绑定人或其他机器人,使用主题下拉框设置对象类型、双向称呼、名字与分寸;人物详情显示关系历史。解除关系不删除相处记录。列表摘要按需读取,正文与原有聊天缓存不受影响。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "luckytri",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "让由技术诞生的她,在时间、记忆、相遇与选择中逐渐形成自己的方向",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"docs/brand/*.png",
|
|
52
52
|
".github/SECURITY.md",
|
|
53
53
|
"README.en.md",
|
|
54
|
-
"LICENSE"
|
|
54
|
+
"LICENSE",
|
|
55
|
+
"scripts/update-instance.js"
|
|
55
56
|
],
|
|
56
57
|
"publishConfig": {
|
|
57
58
|
"access": "public",
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"test": "node --import ./tests/helpers/awake.mjs --test tests/*.test.js",
|
|
78
79
|
"test:clock": "node scripts/dev/test-clock.js",
|
|
79
80
|
"audit:sent": "node --env-file-if-exists=.env scripts/dev/audit-sent.js",
|
|
80
|
-
"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",
|
|
81
|
+
"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",
|
|
81
82
|
"setup": "node scripts/setup.js",
|
|
82
83
|
"stop": "node scripts/stop.js",
|
|
83
84
|
"backup": "node scripts/backup.js",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{$ as e,Ct as t,E as n,Et as r,H as i,I as a,J as o,M as s,P as c,Tt as l,U as u,V as d,W as f,X as p,Y as m,Z as h,a as g,ct as _,dt as v,et as y,f as b,gt as x,ht as S,it as C,j as w,lt as T,m as E,n as D,nt as O,o as k,q as A,rt as j,s as M,t as N,ut as P,v as F,vt as I,wt as L,xt as R,yt as z,z as B}from"./Empty-ddEfu0w8.js";import{h as ee,v as V}from"./mind-DYRfFMwh.js";import{c as H,f as U,h as W,m as G,n as K,o as te,p as q,u as J,y as ne}from"./index-oLIXWxsn.js";import{t as re}from"./media-keE52cIX.js";import{t as Y}from"./Field-mIhV5YBA.js";async function ie(e,t){let n=new URLSearchParams({session:e});return t!==void 0&&n.set(`after`,String(t)),E(`/core/events?`+n.toString())}async function ae(e,t={}){let n=new URLSearchParams({session:e});return t.limit&&n.set(`limit`,String(t.limit)),t.compact&&n.set(`compact`,`1`),E(`/core/traces?`+n.toString())}async function X(e){return E(`/core/traces/`+e)}async function oe(e){return E(`/simulate`,`POST`,e)}async function se(e,t){return E(`/decisions/`+e+`/feedback`,`POST`,{tag:t})}async function Z(e){return E(`/core/replay`,`POST`,e)}async function ce(e){return E(`/core/sessions`,`POST`,e)}async function le(e,t){return E(`/core/sessions/`+encodeURIComponent(e),`PATCH`,{enabled:t})}async function ue(e,t){return E(`/core/sessions/`+encodeURIComponent(e)+`/archive`,`PATCH`,{archived:t})}async function de(e){return E(`/core/sessions/`+encodeURIComponent(e),`DELETE`,{})}async function fe(e,t){return E(`/core/sessions/`+encodeURIComponent(e),`PUT`,t)}async function pe(e){return E(`/core/sessions/`+encodeURIComponent(e)+`/summaries`)}async function me(e){return E(`/core/sessions/`+encodeURIComponent(e)+`/context`,`DELETE`,{})}var he={class:`session-list`},ge={class:`list-head`},Q=[`placeholder`,`aria-label`],_e={class:`rows scroll-pane`},ve=[`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=D(O({__name:`SessionList`,props:{sessions:{},selected:{},unread:{}},emits:[`select`,`add`,`toggle`,`restore`,`remove`],setup(n,{emit:i}){let s=n,c=i,u=R(``),m=A(()=>s.sessions.filter(e=>!e.archived&&`${e.name} ${e.id}`.toLowerCase().includes(u.value.toLowerCase()))),g=A(()=>s.sessions.filter(e=>e.archived));return(i,s)=>(P(),h(`div`,he,[o(`div`,ge,[o(`h2`,null,[e(r(t(a)(`会话`))+` `,1),o(`small`,null,r(m.value.length),1)]),o(`button`,{class:`small primary`,onClick:s[0]||=e=>c(`add`)},r(t(a)(`+ 添加会话`)),1)]),z(o(`input`,{"onUpdate:modelValue":s[1]||=e=>u.value=e,type:`search`,placeholder:t(a)(`搜索名称或号码`),"aria-label":t(a)(`搜索会话`)},null,8,Q),[[d,u.value]]),o(`div`,_e,[(P(!0),h(f,null,v(m.value,e=>(P(),h(`div`,{key:e.id,class:L([`session-row`,{selected:e.id===n.selected,paused:!e.enabled}])},[o(`button`,{class:`row-main`,"data-pick":e.id,"aria-current":e.id===n.selected?`true`:void 0,onClick:t=>c(`select`,e.id)},[o(`span`,{class:`badge`,style:l({"--hue":t(U)(e.id)})},r(e.kind===`private`?t(a)(`私`):t(a)(`群`)),5),o(`span`,ye,[o(`b`,null,r(t(W)(e)),1),o(`small`,null,r(e.enabled?t(a)(`参与中`):t(a)(`已暂停`)),1)]),n.unread[e.id]?(P(),h(`span`,{key:0,class:`unread`,"aria-label":t(a)(`{v} 条没细看`,{v:n.unread[e.id]})},r(n.unread[e.id]),9,be)):p(``,!0)],8,ve),o(`label`,{class:`mini-switch`,title:e.enabled?t(a)(`暂停参与`):t(a)(`开启参与`)},[o(`input`,{type:`checkbox`,checked:e.enabled,"aria-label":t(a)(`参与:{v}`,{v:t(W)(e)}),onChange:t=>c(`toggle`,e)},null,40,Se)],8,xe)],2))),128)),m.value.length?p(``,!0):(P(),h(`p`,Ce,r(t(a)(`暂无匹配的会话。`)),1)),g.value.length?(P(),h(`details`,we,[o(`summary`,null,r(t(a)(`已归档 · {length}`,{length:g.value.length})),1),(P(!0),h(f,null,v(g.value,e=>(P(),h(`article`,{key:e.id},[o(`b`,null,r(t(W)(e)),1),o(`div`,Te,[o(`button`,{class:`small`,"data-restore-session":e.id,onClick:t=>c(`restore`,e.id)},r(t(a)(`恢复`)),9,Ee),o(`button`,{class:`small danger`,"data-delete-session":e.id,onClick:t=>c(`remove`,e.id)},r(t(a)(`永久删除`)),9,De)])]))),128))])):p(``,!0)])]))}}),[[`__scopeId`,`data-v-9b8dcbac`]]),ke={class:`muted`},Ae={key:0,class:`notice`,role:`status`},je=[`pattern`,`inputmode`],Me=[`disabled`],Ne=O({__name:`AddSession`,props:{open:{type:Boolean},channel:{},ready:{type:Boolean}},emits:[`close`,`add`],setup(e,{emit:n}){let i=e,s=R(`group`),c=n,l=A(()=>i.channel===`qqbot`),d=A(()=>l.value?`[0-9A-Za-z_\\-]{8,64}`:`\\d{4,20}`);function f(e){let t=e.target;c(`add`,{...Object.fromEntries(new FormData(t)),channel:i.channel})}return(n,i)=>(P(),m(te,{open:e.open,title:t(a)(`添加群聊 / 私聊`),eyebrow:t(a)(`NEW PLACE`),width:`440px`,onClose:i[1]||=e=>c(`close`)},{default:I(()=>[o(`form`,{id:`addSession`,class:`stack`,onSubmit:u(f,[`prevent`])},[o(`p`,ke,r(t(a)(`添加之后再决定要不要让 TA 参与;开不开口仍由 TA 自己决定。`)),1),l.value&&!e.ready?(P(),h(`p`,Ae,r(t(a)(`请先在「连接 QQ」里填写机器人的 AppID`)),1)):p(``,!0),y(Y,{label:t(a)(`类型`)},{default:I(()=>[y(H,{modelValue:s.value,"onUpdate:modelValue":i[0]||=e=>s.value=e,name:`kind`,"aria-label":t(a)(`类型`),options:[{value:`group`,label:t(a)(`群聊`)},{value:`private`,label:t(a)(`私聊`)}]},null,8,[`modelValue`,`aria-label`,`options`])]),_:1},8,[`label`]),y(Y,{label:l.value?t(a)(`群 openid / 用户 openid`):t(a)(`群号 / QQ 号`),hint:l.value?t(a)(`8–64 位字母、数字、下划线或短横线。官方平台的会话通常会在有人 @ 她或私聊她时自动出现,这里只用于提前登记。`):t(a)(`4–20 位数字`)},{default:I(()=>[o(`input`,{name:`id`,required:``,pattern:d.value,inputmode:l.value?`text`:`numeric`,autocomplete:`off`},null,8,je)]),_:1},8,[`label`,`hint`]),y(Y,{label:t(a)(`显示名称`)},{default:I(()=>[...i[2]||=[o(`input`,{name:`name`,required:``,maxlength:`100`,autocomplete:`off`},null,-1)]]),_:1},8,[`label`]),o(`button`,{class:`primary`,disabled:l.value&&!e.ready},r(t(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=D(O({__name:`Transcript`,props:{session:{},sessionId:{},events:{},traces:{},decisions:{},status:{},demo:{type:Boolean},compact:{type:Boolean},sending:{type:Boolean}},emits:[`simulate`,`feedback`,`openList`,`openReplay`],setup(n,{expose:g,emit:_}){let b=n,x=_,S=c({glanced:w(`TA 扫了一眼`),silent:w(`TA 看了,没出声`),deferred:w(`TA 睡着了,醒来再看`)}),T=R(`10001`),E=R(``),D=R(!0),O=R(),k=R({}),j=()=>V(s(),{month:`long`,day:`numeric`,weekday:`short`}),F=A(()=>{let e=b.events.map(e=>({type:`message`,key:`m`+e.seq,time:e.time,event:e})),t=b.events[0]?.time??2**53-1;for(let n of b.traces)S[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=j().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 I(e){let t=String(e.payload.text||``);return b.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 ee(e){return b.traces.filter(t=>t.status===`sent`&&t.time<=e.time+1e3).sort((e,t)=>t.time-e.time)[0]}async function W(e){if(k.value[e.seq]){let{[e.seq]:t,...n}=k.value;k.value=n;return}k.value={...k.value,[e.seq]:{loading:!0,detail:null}};let t=ee(e),n=null;try{n=t?await X(t.id):null}catch{n=null}k.value[e.seq]&&(k.value={...k.value,[e.seq]:{loading:!1,detail:n}})}async function G(){await C(),O.value?.focus()}function K(){E.value.trim()&&!b.sending&&(x(`simulate`,{userId:T.value,text:E.value,mentioned:D.value}),E.value=``)}return g({focusComposer:G}),(s,c)=>(P(),h(`section`,Pe,[o(`header`,Fe,[n.compact?(P(),h(`button`,{key:0,class:`icon-button`,"aria-label":t(a)(`全部会话`),title:t(a)(`全部会话`),onClick:c[0]||=e=>x(`openList`)},` ☰ `,8,Ie)):p(``,!0),n.session?(P(),h(`span`,{key:1,class:`badge`,style:l({"--hue":t(U)(n.session.id)})},r(n.session.kind===`private`?t(a)(`私`):t(a)(`群`)),5)):p(``,!0),o(`div`,Le,[o(`h2`,null,r(n.session?.name||t(a)(`选择一段对话`)),1),o(`span`,Re,r(n.status),1)]),o(`div`,ze,[o(`button`,{class:`small sim-button`,disabled:!n.sessionId,onClick:G},r(t(a)(`模拟消息`)),9,Be),o(`button`,{class:`small`,onClick:c[1]||=e=>x(`openReplay`)},r(t(a)(`回到那一刻`)),1)])]),o(`div`,Ve,[(P(!0),h(f,null,v(F.value,n=>(P(),h(f,{key:n.key},[n.type===`day`?(P(),h(`div`,He,[o(`span`,null,r(n.label),1)])):n.type===`divider`?(P(),h(`div`,{key:1,class:`divider`,"data-status":n.status},[o(`span`,null,r(t(S)[n.status])+r(n.count>1?` × ${n.count}`:``),1),n.reasons.length?(P(),h(`small`,We,r(n.reasons.slice(0,2).join(`;`)),1)):p(``,!0)],8,Ue)):(P(),h(`article`,{key:2,class:L([`message`,{bot:n.event.role===`assistant`,simulated:n.event.payload.simulated}])},[n.event.role===`assistant`?p(``,!0):(P(),h(`span`,{key:0,class:`avatar`,style:l({"--hue":t(U)(String(n.event.payload.userId||n.event.payload.name))}),"aria-hidden":`true`},r(t(q)(n.event.payload.name)),5)),o(`div`,Ge,[o(`div`,Ke,[o(`b`,null,r(n.event.payload.name),1),o(`time`,null,r(t(J)(n.event.time)),1),n.event.payload.simulated?(P(),h(`span`,qe,r(t(a)(`模拟`)),1)):p(``,!0)]),o(`p`,Je,r(n.event.payload.text),1),n.event.role===`assistant`?(P(),h(f,{key:0},[o(`button`,{class:`text-button why-toggle`,"aria-expanded":!!k.value[n.event.seq],onClick:e=>W(n.event)},r(k.value[n.event.seq]?t(a)(`收起`):t(a)(`为什么这么说`)),9,Ye),k.value[n.event.seq]?(P(),h(`div`,Xe,[k.value[n.event.seq].loading?(P(),h(`p`,Ze,r(t(a)(`正在翻当时的记录…`)),1)):(P(),h(f,{key:1},[k.value[n.event.seq].detail?.data?.decision?.appraisal?(P(),h(`p`,Qe,[o(`b`,null,r(t(a)(`TA 当时的理解:`)),1),e(r(k.value[n.event.seq].detail.data.decision.appraisal),1)])):p(``,!0),o(`p`,null,[o(`b`,null,r(t(a)(`理由:`)),1),e(r(k.value[n.event.seq].detail?.data?.reason||k.value[n.event.seq].detail?.data?.decision?.reason||I(n.event)?.reason||t(a)(`没有留下理由。`)),1)]),k.value[n.event.seq].detail?.data?.snapshot?.knowledge?.length?(P(),h(`p`,$e,[o(`b`,null,r(t(a)(`引用的资料:`)),1),e(r(k.value[n.event.seq].detail.data.snapshot.knowledge.map(e=>e.title).join(`、`)),1)])):p(``,!0),I(n.event)?(P(),h(`label`,et,[e(r(t(a)(`这句怎么样?`))+` `,1),y(H,{"model-value":I(n.event).feedback||``,"aria-label":t(a)(`这句怎么样?`),options:[{value:``,label:t(a)(`选择评价`)},...Object.entries(t(M).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>x(`feedback`,I(n.event).id,e)},null,8,[`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])):p(``,!0)],64))])):p(``,!0)],64)):p(``,!0)])],2))],64))),128)),n.events.length?p(``,!0):(P(),m(N,{key:0,title:n.sessionId?t(a)(`等待新的消息`):t(a)(`选择一段对话`),text:t(a)(`群聊与私聊消息会在这里实时出现;TA 扫一眼、没出声也会留下一条分隔线。`)},null,8,[`title`,`text`]))]),n.demo?(P(),h(`form`,{key:0,id:`simulate`,class:`composer`,onSubmit:u(K,[`prevent`])},[o(`div`,tt,[o(`span`,nt,r(t(a)(`模拟消息`)),1),o(`span`,rt,r(t(a)(`作为一条模拟消息走完整流程,写进模拟会话;不会发到 QQ,也不进入真实记忆。`)),1)]),o(`div`,it,[o(`label`,at,[o(`span`,ot,r(t(a)(`体验用户 ID`)),1),z(o(`input`,{"onUpdate:modelValue":c[2]||=e=>T.value=e,name:`userId`,pattern:`\\d{4,20}`,required:``,title:t(a)(`体验用户 ID`)},null,8,st),[[d,T.value]])]),z(o(`textarea`,{ref_key:`composer`,ref:O,"onUpdate:modelValue":c[3]||=e=>E.value=e,name:`text`,rows:`1`,placeholder:t(a)(`以这位群友的身份说一句…`),required:``,onKeydown:i(u(K,[`exact`,`prevent`]),[`enter`])},null,40,ct),[[d,E.value]]),o(`label`,lt,[z(o(`input`,{"onUpdate:modelValue":c[4]||=e=>D.value=e,type:`checkbox`},null,512),[[B,D.value]]),e(r(t(a)(`视为 @`)),1)]),o(`button`,{class:`primary`,disabled:n.sending||!n.sessionId},r(n.sending?t(a)(`TA 在想…`):t(a)(`发送模拟消息`)),9,ut)])],32)):(P(),h(`div`,dt,[o(`span`,ft,r(t(a)(`真实模式下不能模拟消息。想看看 TA 会怎么回,可以「和 TA 聊聊」——那是试聊,什么都不写入。`)),1),o(`button`,{class:`small`,onClick:c[5]||=e=>t(M).chatOpen=!0},r(t(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=D(O({__name:`SessionPanel`,props:j({session:{},group:{},summaries:{},decisions:{}},{settingsOpen:{type:Boolean,default:!1},settingsOpenModifiers:{},tab:{default:`here`},tabModifiers:{}}),emits:j([`toggle`,`archive`,`clear`,`save`,`feedback`],[`update:settingsOpen`,`update:tab`]),setup(n,{emit:i}){let s=n,l=S(n,`settingsOpen`),m=S(n,`tab`),g=i,_=c([w(`近期细摘要`),w(`中期摘要`),w(`较早摘要`),w(`远期摘要`)]),b=R(0),C=A(()=>s.decisions.filter(e=>e.session_id===s.session?.id&&e.reply)),T=A(()=>Math.max(1,Math.ceil(C.value.length/$))),E=R(!1),D=R({});x(()=>s.session?.policy,e=>{D.value={...e||{}}},{immediate:!0,deep:!0}),x(()=>s.session?.id,()=>{b.value=0,E.value=!1});function O(e){let t=e?.face;return t?[t.role,t.tone,t.content].filter(Boolean).join(`
|
|
1
|
+
import{$ as e,Ct as t,E as n,Et as r,H as i,I as a,J as o,M as s,P as c,Tt as l,U as u,V as d,W as f,X as p,Y as m,Z as h,a as g,ct as _,dt as v,et as y,f as b,gt as x,ht as S,it as C,j as w,lt as T,m as E,n as D,nt as O,o as k,q as A,rt as j,s as M,t as N,ut as P,v as F,vt as I,wt as L,xt as R,yt as z,z as B}from"./Empty-C7yzOBzy.js";import{g as ee,y as V}from"./mind-DtTVrW6Z.js";import{c as H,f as U,h as W,m as G,n as K,o as te,p as q,u as J,y as ne}from"./index-CyRtYxP3.js";import{t as re}from"./media-Bwtx8blz.js";import{t as Y}from"./Field-BjFv4Rfn.js";async function ie(e,t){let n=new URLSearchParams({session:e});return t!==void 0&&n.set(`after`,String(t)),E(`/core/events?`+n.toString())}async function ae(e,t={}){let n=new URLSearchParams({session:e});return t.limit&&n.set(`limit`,String(t.limit)),t.compact&&n.set(`compact`,`1`),E(`/core/traces?`+n.toString())}async function X(e){return E(`/core/traces/`+e)}async function oe(e){return E(`/simulate`,`POST`,e)}async function se(e,t){return E(`/decisions/`+e+`/feedback`,`POST`,{tag:t})}async function Z(e){return E(`/core/replay`,`POST`,e)}async function ce(e){return E(`/core/sessions`,`POST`,e)}async function le(e,t){return E(`/core/sessions/`+encodeURIComponent(e),`PATCH`,{enabled:t})}async function ue(e,t){return E(`/core/sessions/`+encodeURIComponent(e)+`/archive`,`PATCH`,{archived:t})}async function de(e){return E(`/core/sessions/`+encodeURIComponent(e),`DELETE`,{})}async function fe(e,t){return E(`/core/sessions/`+encodeURIComponent(e),`PUT`,t)}async function pe(e){return E(`/core/sessions/`+encodeURIComponent(e)+`/summaries`)}async function me(e){return E(`/core/sessions/`+encodeURIComponent(e)+`/context`,`DELETE`,{})}var he={class:`session-list`},ge={class:`list-head`},Q=[`placeholder`,`aria-label`],_e={class:`rows scroll-pane`},ve=[`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=D(O({__name:`SessionList`,props:{sessions:{},selected:{},unread:{}},emits:[`select`,`add`,`toggle`,`restore`,`remove`],setup(n,{emit:i}){let s=n,c=i,u=R(``),m=A(()=>s.sessions.filter(e=>!e.archived&&`${e.name} ${e.id}`.toLowerCase().includes(u.value.toLowerCase()))),g=A(()=>s.sessions.filter(e=>e.archived));return(i,s)=>(P(),h(`div`,he,[o(`div`,ge,[o(`h2`,null,[e(r(t(a)(`会话`))+` `,1),o(`small`,null,r(m.value.length),1)]),o(`button`,{class:`small primary`,onClick:s[0]||=e=>c(`add`)},r(t(a)(`+ 添加会话`)),1)]),z(o(`input`,{"onUpdate:modelValue":s[1]||=e=>u.value=e,type:`search`,placeholder:t(a)(`搜索名称或号码`),"aria-label":t(a)(`搜索会话`)},null,8,Q),[[d,u.value]]),o(`div`,_e,[(P(!0),h(f,null,v(m.value,e=>(P(),h(`div`,{key:e.id,class:L([`session-row`,{selected:e.id===n.selected,paused:!e.enabled}])},[o(`button`,{class:`row-main`,"data-pick":e.id,"aria-current":e.id===n.selected?`true`:void 0,onClick:t=>c(`select`,e.id)},[o(`span`,{class:`badge`,style:l({"--hue":t(U)(e.id)})},r(e.kind===`private`?t(a)(`私`):t(a)(`群`)),5),o(`span`,ye,[o(`b`,null,r(t(W)(e)),1),o(`small`,null,r(e.enabled?t(a)(`参与中`):t(a)(`已暂停`)),1)]),n.unread[e.id]?(P(),h(`span`,{key:0,class:`unread`,"aria-label":t(a)(`{v} 条没细看`,{v:n.unread[e.id]})},r(n.unread[e.id]),9,be)):p(``,!0)],8,ve),o(`label`,{class:`mini-switch`,title:e.enabled?t(a)(`暂停参与`):t(a)(`开启参与`)},[o(`input`,{type:`checkbox`,checked:e.enabled,"aria-label":t(a)(`参与:{v}`,{v:t(W)(e)}),onChange:t=>c(`toggle`,e)},null,40,Se)],8,xe)],2))),128)),m.value.length?p(``,!0):(P(),h(`p`,Ce,r(t(a)(`暂无匹配的会话。`)),1)),g.value.length?(P(),h(`details`,we,[o(`summary`,null,r(t(a)(`已归档 · {length}`,{length:g.value.length})),1),(P(!0),h(f,null,v(g.value,e=>(P(),h(`article`,{key:e.id},[o(`b`,null,r(t(W)(e)),1),o(`div`,Te,[o(`button`,{class:`small`,"data-restore-session":e.id,onClick:t=>c(`restore`,e.id)},r(t(a)(`恢复`)),9,Ee),o(`button`,{class:`small danger`,"data-delete-session":e.id,onClick:t=>c(`remove`,e.id)},r(t(a)(`永久删除`)),9,De)])]))),128))])):p(``,!0)])]))}}),[[`__scopeId`,`data-v-9b8dcbac`]]),ke={class:`muted`},Ae={key:0,class:`notice`,role:`status`},je=[`pattern`,`inputmode`],Me=[`disabled`],Ne=O({__name:`AddSession`,props:{open:{type:Boolean},channel:{},ready:{type:Boolean}},emits:[`close`,`add`],setup(e,{emit:n}){let i=e,s=R(`group`),c=n,l=A(()=>i.channel===`qqbot`),d=A(()=>l.value?`[0-9A-Za-z_\\-]{8,64}`:`\\d{4,20}`);function f(e){let t=e.target;c(`add`,{...Object.fromEntries(new FormData(t)),channel:i.channel})}return(n,i)=>(P(),m(te,{open:e.open,title:t(a)(`添加群聊 / 私聊`),eyebrow:t(a)(`NEW PLACE`),width:`440px`,onClose:i[1]||=e=>c(`close`)},{default:I(()=>[o(`form`,{id:`addSession`,class:`stack`,onSubmit:u(f,[`prevent`])},[o(`p`,ke,r(t(a)(`添加之后再决定要不要让 TA 参与;开不开口仍由 TA 自己决定。`)),1),l.value&&!e.ready?(P(),h(`p`,Ae,r(t(a)(`请先在「连接 QQ」里填写机器人的 AppID`)),1)):p(``,!0),y(Y,{label:t(a)(`类型`)},{default:I(()=>[y(H,{modelValue:s.value,"onUpdate:modelValue":i[0]||=e=>s.value=e,name:`kind`,"aria-label":t(a)(`类型`),options:[{value:`group`,label:t(a)(`群聊`)},{value:`private`,label:t(a)(`私聊`)}]},null,8,[`modelValue`,`aria-label`,`options`])]),_:1},8,[`label`]),y(Y,{label:l.value?t(a)(`群 openid / 用户 openid`):t(a)(`群号 / QQ 号`),hint:l.value?t(a)(`8–64 位字母、数字、下划线或短横线。官方平台的会话通常会在有人 @ 她或私聊她时自动出现,这里只用于提前登记。`):t(a)(`4–20 位数字`)},{default:I(()=>[o(`input`,{name:`id`,required:``,pattern:d.value,inputmode:l.value?`text`:`numeric`,autocomplete:`off`},null,8,je)]),_:1},8,[`label`,`hint`]),y(Y,{label:t(a)(`显示名称`)},{default:I(()=>[...i[2]||=[o(`input`,{name:`name`,required:``,maxlength:`100`,autocomplete:`off`},null,-1)]]),_:1},8,[`label`]),o(`button`,{class:`primary`,disabled:l.value&&!e.ready},r(t(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=D(O({__name:`Transcript`,props:{session:{},sessionId:{},events:{},traces:{},decisions:{},status:{},demo:{type:Boolean},compact:{type:Boolean},sending:{type:Boolean}},emits:[`simulate`,`feedback`,`openList`,`openReplay`],setup(n,{expose:g,emit:_}){let b=n,x=_,S=c({glanced:w(`TA 扫了一眼`),silent:w(`TA 看了,没出声`),deferred:w(`TA 睡着了,醒来再看`)}),T=R(`10001`),E=R(``),D=R(!0),O=R(),k=R({}),j=()=>V(s(),{month:`long`,day:`numeric`,weekday:`short`}),F=A(()=>{let e=b.events.map(e=>({type:`message`,key:`m`+e.seq,time:e.time,event:e})),t=b.events[0]?.time??2**53-1;for(let n of b.traces)S[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=j().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 I(e){let t=String(e.payload.text||``);return b.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 ee(e){return b.traces.filter(t=>t.status===`sent`&&t.time<=e.time+1e3).sort((e,t)=>t.time-e.time)[0]}async function W(e){if(k.value[e.seq]){let{[e.seq]:t,...n}=k.value;k.value=n;return}k.value={...k.value,[e.seq]:{loading:!0,detail:null}};let t=ee(e),n=null;try{n=t?await X(t.id):null}catch{n=null}k.value[e.seq]&&(k.value={...k.value,[e.seq]:{loading:!1,detail:n}})}async function G(){await C(),O.value?.focus()}function K(){E.value.trim()&&!b.sending&&(x(`simulate`,{userId:T.value,text:E.value,mentioned:D.value}),E.value=``)}return g({focusComposer:G}),(s,c)=>(P(),h(`section`,Pe,[o(`header`,Fe,[n.compact?(P(),h(`button`,{key:0,class:`icon-button`,"aria-label":t(a)(`全部会话`),title:t(a)(`全部会话`),onClick:c[0]||=e=>x(`openList`)},` ☰ `,8,Ie)):p(``,!0),n.session?(P(),h(`span`,{key:1,class:`badge`,style:l({"--hue":t(U)(n.session.id)})},r(n.session.kind===`private`?t(a)(`私`):t(a)(`群`)),5)):p(``,!0),o(`div`,Le,[o(`h2`,null,r(n.session?.name||t(a)(`选择一段对话`)),1),o(`span`,Re,r(n.status),1)]),o(`div`,ze,[o(`button`,{class:`small sim-button`,disabled:!n.sessionId,onClick:G},r(t(a)(`模拟消息`)),9,Be),o(`button`,{class:`small`,onClick:c[1]||=e=>x(`openReplay`)},r(t(a)(`回到那一刻`)),1)])]),o(`div`,Ve,[(P(!0),h(f,null,v(F.value,n=>(P(),h(f,{key:n.key},[n.type===`day`?(P(),h(`div`,He,[o(`span`,null,r(n.label),1)])):n.type===`divider`?(P(),h(`div`,{key:1,class:`divider`,"data-status":n.status},[o(`span`,null,r(t(S)[n.status])+r(n.count>1?` × ${n.count}`:``),1),n.reasons.length?(P(),h(`small`,We,r(n.reasons.slice(0,2).join(`;`)),1)):p(``,!0)],8,Ue)):(P(),h(`article`,{key:2,class:L([`message`,{bot:n.event.role===`assistant`,simulated:n.event.payload.simulated}])},[n.event.role===`assistant`?p(``,!0):(P(),h(`span`,{key:0,class:`avatar`,style:l({"--hue":t(U)(String(n.event.payload.userId||n.event.payload.name))}),"aria-hidden":`true`},r(t(q)(n.event.payload.name)),5)),o(`div`,Ge,[o(`div`,Ke,[o(`b`,null,r(n.event.payload.name),1),o(`time`,null,r(t(J)(n.event.time)),1),n.event.payload.simulated?(P(),h(`span`,qe,r(t(a)(`模拟`)),1)):p(``,!0)]),o(`p`,Je,r(n.event.payload.text),1),n.event.role===`assistant`?(P(),h(f,{key:0},[o(`button`,{class:`text-button why-toggle`,"aria-expanded":!!k.value[n.event.seq],onClick:e=>W(n.event)},r(k.value[n.event.seq]?t(a)(`收起`):t(a)(`为什么这么说`)),9,Ye),k.value[n.event.seq]?(P(),h(`div`,Xe,[k.value[n.event.seq].loading?(P(),h(`p`,Ze,r(t(a)(`正在翻当时的记录…`)),1)):(P(),h(f,{key:1},[k.value[n.event.seq].detail?.data?.decision?.appraisal?(P(),h(`p`,Qe,[o(`b`,null,r(t(a)(`TA 当时的理解:`)),1),e(r(k.value[n.event.seq].detail.data.decision.appraisal),1)])):p(``,!0),o(`p`,null,[o(`b`,null,r(t(a)(`理由:`)),1),e(r(k.value[n.event.seq].detail?.data?.reason||k.value[n.event.seq].detail?.data?.decision?.reason||I(n.event)?.reason||t(a)(`没有留下理由。`)),1)]),k.value[n.event.seq].detail?.data?.snapshot?.knowledge?.length?(P(),h(`p`,$e,[o(`b`,null,r(t(a)(`引用的资料:`)),1),e(r(k.value[n.event.seq].detail.data.snapshot.knowledge.map(e=>e.title).join(`、`)),1)])):p(``,!0),I(n.event)?(P(),h(`label`,et,[e(r(t(a)(`这句怎么样?`))+` `,1),y(H,{"model-value":I(n.event).feedback||``,"aria-label":t(a)(`这句怎么样?`),options:[{value:``,label:t(a)(`选择评价`)},...Object.entries(t(M).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>x(`feedback`,I(n.event).id,e)},null,8,[`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])):p(``,!0)],64))])):p(``,!0)],64)):p(``,!0)])],2))],64))),128)),n.events.length?p(``,!0):(P(),m(N,{key:0,title:n.sessionId?t(a)(`等待新的消息`):t(a)(`选择一段对话`),text:t(a)(`群聊与私聊消息会在这里实时出现;TA 扫一眼、没出声也会留下一条分隔线。`)},null,8,[`title`,`text`]))]),n.demo?(P(),h(`form`,{key:0,id:`simulate`,class:`composer`,onSubmit:u(K,[`prevent`])},[o(`div`,tt,[o(`span`,nt,r(t(a)(`模拟消息`)),1),o(`span`,rt,r(t(a)(`作为一条模拟消息走完整流程,写进模拟会话;不会发到 QQ,也不进入真实记忆。`)),1)]),o(`div`,it,[o(`label`,at,[o(`span`,ot,r(t(a)(`体验用户 ID`)),1),z(o(`input`,{"onUpdate:modelValue":c[2]||=e=>T.value=e,name:`userId`,pattern:`\\d{4,20}`,required:``,title:t(a)(`体验用户 ID`)},null,8,st),[[d,T.value]])]),z(o(`textarea`,{ref_key:`composer`,ref:O,"onUpdate:modelValue":c[3]||=e=>E.value=e,name:`text`,rows:`1`,placeholder:t(a)(`以这位群友的身份说一句…`),required:``,onKeydown:i(u(K,[`exact`,`prevent`]),[`enter`])},null,40,ct),[[d,E.value]]),o(`label`,lt,[z(o(`input`,{"onUpdate:modelValue":c[4]||=e=>D.value=e,type:`checkbox`},null,512),[[B,D.value]]),e(r(t(a)(`视为 @`)),1)]),o(`button`,{class:`primary`,disabled:n.sending||!n.sessionId},r(n.sending?t(a)(`TA 在想…`):t(a)(`发送模拟消息`)),9,ut)])],32)):(P(),h(`div`,dt,[o(`span`,ft,r(t(a)(`真实模式下不能模拟消息。想看看 TA 会怎么回,可以「和 TA 聊聊」——那是试聊,什么都不写入。`)),1),o(`button`,{class:`small`,onClick:c[5]||=e=>t(M).chatOpen=!0},r(t(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=D(O({__name:`SessionPanel`,props:j({session:{},group:{},summaries:{},decisions:{}},{settingsOpen:{type:Boolean,default:!1},settingsOpenModifiers:{},tab:{default:`here`},tabModifiers:{}}),emits:j([`toggle`,`archive`,`clear`,`save`,`feedback`],[`update:settingsOpen`,`update:tab`]),setup(n,{emit:i}){let s=n,l=S(n,`settingsOpen`),m=S(n,`tab`),g=i,_=c([w(`近期细摘要`),w(`中期摘要`),w(`较早摘要`),w(`远期摘要`)]),b=R(0),C=A(()=>s.decisions.filter(e=>e.session_id===s.session?.id&&e.reply)),T=A(()=>Math.max(1,Math.ceil(C.value.length/$))),E=R(!1),D=R({});x(()=>s.session?.policy,e=>{D.value={...e||{}}},{immediate:!0,deep:!0}),x(()=>s.session?.id,()=>{b.value=0,E.value=!1});function O(e){let t=e?.face;return t?[t.role,t.tone,t.content].filter(Boolean).join(`
|
|
2
2
|
|
|
3
3
|
`):``}function k(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;g(`save`,n)}return(i,s)=>(P(),h(`section`,mt,[y(K,{modelValue:m.value,"onUpdate:modelValue":s[0]||=e=>m.value=e,label:t(a)(`这个会话`),items:[{key:`here`,label:t(a)(`这个会话`)},{key:`feedback`,label:t(a)(`反馈`)}]},null,8,[`modelValue`,`label`,`items`]),n.session?m.value===`here`?(P(),h(`div`,gt,[o(`div`,_t,[o(`div`,vt,[o(`div`,null,[o(`span`,yt,r(t(a)(`TA 在这里的样子`)),1),O(n.group)?(P(),h(f,{key:0},[o(`p`,{class:L([`face-body`,E.value?`open`:`clamp`])},r(O(n.group)),3),O(n.group).length>96?(P(),h(`button`,{key:0,type:`button`,class:`text-button`,onClick:s[1]||=e=>E.value=!E.value},r(E.value?t(a)(`收起`):t(a)(`展开完整内容`)),1)):p(``,!0)],64)):(P(),h(`p`,bt,r(t(a)(`还没有形成在这里的样子。`)),1)),n.group?.face?.aspiration?(P(),h(`small`,xt,r(t(a)(`想成为:{aspiration}`,{aspiration:n.group.face.aspiration})),1)):p(``,!0)]),o(`div`,null,[o(`span`,St,r(n.session.kind===`private`?t(a)(`这段私聊给 TA 的感觉`):t(a)(`这个群给 TA 的感觉`)),1),o(`p`,null,r(n.group?.bond?.feel||t(a)(`刚来这里不久`)),1)])]),o(`p`,Ct,r(t(a)(`在这里开不开口由 TA 自己决定:没有参与概率和冷却。TA 对这里每个人的感觉在「人际」里看。`)),1),o(`div`,wt,[o(`button`,{id:`toggleSession`,"data-toggle":n.session.id,class:L({primary:!n.session.enabled}),onClick:s[2]||=e=>g(`toggle`,n.session)},r(n.session.enabled?t(a)(`暂停参与`):t(a)(`开启参与`)),11,Tt),o(`button`,{"data-archive":n.session.id,onClick:s[3]||=e=>g(`archive`,n.session.id)},r(t(a)(`归档`)),9,Et),o(`button`,{id:`clearLiveContext`,onClick:s[4]||=e=>g(`clear`)},r(t(a)(`清空上下文`)),1)]),o(`details`,{class:`fold session-settings`,open:l.value,onToggle:s[19]||=e=>l.value=e.target.open},[o(`summary`,null,r(t(a)(`会话设置`)),1),(P(),h(`form`,{key:n.session.id,"data-session":n.session.id,class:`stack tight`,onSubmit:u(k,[`prevent`]),onInput:s[17]||=e=>t(M).dirty=!0,onChange:s[18]||=e=>t(M).dirty=!0},[o(`p`,kt,r(t(a)(`所有会话和独处都用模型库里的默认模型;其余已启用的模型按列表顺序做备用。她在这个聊天窗里用什么样子,由她自己从经历里决定。`)),1),o(`label`,null,[e(r(t(a)(`会话名称`))+` `,1),o(`input`,{name:`name`,maxlength:`100`,autocomplete:`off`,value:n.session.name},null,8,At),o(`small`,jt,r(t(a)(`官方机器人平台不提供群名称,可以在这里自己起一个`)),1)]),o(`label`,Mt,[z(o(`input`,{name:`selectiveVision`,type:`checkbox`,"onUpdate:modelValue":s[5]||=e=>D.value.selectiveVision=e,onInput:s[6]||=u(()=>{},[`stop`])},null,544),[[B,D.value.selectiveVision]]),e(r(t(a)(`节能看图:只在被 @ 或明确要求时,看这一条和附近的图`)),1)]),o(`details`,Nt,[o(`summary`,null,r(t(a)(`高级策略`)),1),o(`div`,Pt,[o(`label`,null,[e(r(t(a)(`聚合窗口 ms`)),1),z(o(`input`,{name:`aggregateMs`,type:`number`,"onUpdate:modelValue":s[7]||=e=>D.value.aggregateMs=e},null,512),[[d,D.value.aggregateMs,void 0,{number:!0}]])]),o(`label`,null,[e(r(t(a)(`最长等待 ms`)),1),z(o(`input`,{name:`maxWaitMs`,type:`number`,"onUpdate:modelValue":s[8]||=e=>D.value.maxWaitMs=e},null,512),[[d,D.value.maxWaitMs,void 0,{number:!0}]])]),o(`label`,null,[e(r(t(a)(`近期原文条数(10–500)`)),1),z(o(`input`,{name:`contextMessages`,type:`number`,min:`10`,max:`500`,"onUpdate:modelValue":s[9]||=e=>D.value.contextMessages=e},null,512),[[d,D.value.contextMessages,void 0,{number:!0}]])]),o(`label`,null,[e(r(t(a)(`一轮总字数`)),1),z(o(`input`,{name:`maxReply`,type:`number`,"onUpdate:modelValue":s[10]||=e=>D.value.maxReply=e},null,512),[[d,D.value.maxReply,void 0,{number:!0}]])]),o(`label`,Ft,[z(o(`input`,{name:`compaction`,type:`checkbox`,"onUpdate:modelValue":s[11]||=e=>D.value.compaction=e,onInput:s[12]||=u(()=>{},[`stop`])},null,544),[[B,D.value.compaction]]),e(r(t(a)(`上下文压缩:更早的聊天定期整理成分层摘要,越近越详细`)),1)]),o(`label`,It,[z(o(`input`,{name:`memory`,type:`checkbox`,"onUpdate:modelValue":s[13]||=e=>D.value.memory=e,onInput:s[14]||=u(()=>{},[`stop`])},null,544),[[B,D.value.memory]]),e(r(t(a)(`长期记忆`)),1)]),o(`label`,Lt,[z(o(`input`,{name:`deepCheck`,type:`checkbox`,"onUpdate:modelValue":s[15]||=e=>D.value.deepCheck=e,onInput:s[16]||=u(()=>{},[`stop`])},null,544),[[B,D.value.deepCheck]]),e(r(t(a)(`倾诉、纠正和危机时额外复审一次回复`)),1)])])]),o(`div`,Rt,[o(`button`,zt,r(t(a)(`保存会话设置`)),1),o(`small`,Bt,r(t(M).dirty?t(a)(`有未保存的修改`):t(a)(`保存后下一轮生效`)),1)])],40,Ot))],40,Dt),o(`details`,Vt,[o(`summary`,null,r(t(a)(`语境摘要 · {length} 段`,{length:n.summaries.length})),1),n.summaries.length?p(``,!0):(P(),h(`p`,Ht,r(t(a)(`还没有摘要。聊天超过近期原文条数后,更早的内容会自动整理到这里。`)),1)),(P(!0),h(f,null,v(n.summaries,e=>(P(),h(`article`,{key:e.id,class:`summary`},[o(`header`,null,[o(`b`,null,r(t(_)[e.level]||t(a)(`摘要`)),1),o(`small`,Ut,r(t(a)(`{period} · {messages} 条消息`,{period:e.period,messages:e.messages})),1)]),o(`p`,null,r(e.summary),1),e.keyPoints?.length?(P(),h(`ul`,Wt,[(P(!0),h(f,null,v(e.keyPoints,(e,n)=>(P(),h(`li`,{key:n},r(e.open?t(a)(`未完:`):``)+r(e.text),1))),128))])):p(``,!0)]))),128))])])])):(P(),h(`div`,Gt,[o(`p`,Kt,r(t(a)(`针对具体回复评价口吻;这是 TA 在这个会话里听到的话,不会改写天性。`)),1),o(`div`,qt,[(P(!0),h(f,null,v(C.value.slice(b.value*$,b.value*$+$),n=>(P(),h(`article`,{key:n.id,class:`feedback-item`},[o(`time`,Jt,r(t(J)(n.time)),1),o(`p`,Yt,r(n.reply),1),o(`label`,null,[e(r(t(a)(`这句回复怎么样?`))+` `,1),y(H,{"data-feedback":n.id,"model-value":n.feedback||``,"aria-label":t(a)(`这句回复怎么样?`),options:[{value:``,label:t(a)(`选择评价`)},...Object.entries(t(M).health.feedbackLabels||{}).map(([e,t])=>({value:e,label:String(t)}))],"onUpdate:modelValue":e=>g(`feedback`,n.id,e)},null,8,[`data-feedback`,`model-value`,`aria-label`,`options`,`onUpdate:modelValue`])])]))),128)),C.value.length?p(``,!0):(P(),h(`p`,Xt,r(t(a)(`还没有可以评价的回复。`)),1))]),o(`div`,Zt,[o(`button`,{class:`small`,disabled:b.value===0,onClick:s[20]||=e=>b.value--},`←`,8,Qt),o(`span`,null,r(b.value+1)+` / `+r(T.value),1),o(`button`,{class:`small`,disabled:b.value+1>=T.value,onClick:s[21]||=e=>b.value++},` → `,8,$t)])])):(P(),h(`div`,ht,[y(N,{title:t(a)(`还没有选中会话`),text:t(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=D(O({__name:`ReplaySheet`,props:{open:{type:Boolean},sessionId:{},events:{}},emits:[`close`,`load`],setup(n,{emit:i}){let s=n,l=i,g=c({sent:w(`开口了`),silent:w(`没出声`),glanced:w(`扫了一眼`),deferred:w(`睡着了`),running:w(`处理中`),error:w(`失败`),complete:w(`完成`),stale:w(`旧稿作废`),cancelled:w(`已取消`),interrupted:w(`已中断`)}),_=c({live:w(`真实聊天`),demo:w(`模拟预览`),replay:w(`测试回放`),memory:w(`记忆整理`),summary:w(`语境压缩`)}),y=R([]),b=R(null),S=R(``),C=R(``),T=R(!1),E=R(``);async function D(){s.sessionId&&(y.value=await ae(s.sessionId))}function O(){let e=s.events.map(e=>Number(e.seq));e.length&&(S.value||=String(Math.min(...e)),C.value||=String(Math.max(...e)))}async function k(){if(!T.value){T.value=!0;try{F(a(`回放正在运行`));let e=await Z({session:s.sessionId,from:Number(S.value),to:Number(C.value)});E.value=JSON.stringify(e,null,2),F(a(`回放结束`)),await D()}catch(e){F(e.message,!0)}finally{T.value=!1}}}async function A(e){b.value=await X(e),E.value=JSON.stringify(b.value,null,2)}function j(e){return e?a(`输入 {v}(缓存读 {v2} / 写 {v3})· 输出 {v4}`,{v:G(e.input),v2:G(e.cachedRead),v3:G(e.cacheWrite),v4:G(e.output)}):`—`}return x(()=>[s.open,s.sessionId],()=>{s.open&&(S.value=``,C.value=``,b.value=null,O(),D())},{immediate:!0}),x(()=>s.events.length,O),(i,s)=>(P(),m(te,{open:n.open,title:t(a)(`回到那一刻`),eyebrow:`REPLAY`,width:`1180px`,onClose:s[3]||=e=>l(`close`)},{default:I(()=>[o(`div`,tn,[o(`section`,nn,[o(`div`,rn,[o(`h3`,null,r(t(a)(`历史消息`)),1),o(`button`,{id:`loadMessages`,class:`small`,onClick:s[0]||=e=>{l(`load`),D()}},r(t(a)(`↻ 加载`)),1)]),o(`div`,an,[n.events.length?(P(),h(`table`,on,[o(`thead`,null,[o(`tr`,null,[o(`th`,null,r(t(a)(`序号`)),1),o(`th`,null,r(t(a)(`发送者`)),1),o(`th`,null,r(t(a)(`消息`)),1)])]),o(`tbody`,null,[(P(!0),h(f,null,v([...n.events].reverse(),e=>(P(),h(`tr`,{key:e.seq},[o(`td`,null,r(e.seq),1),o(`td`,null,r(e.payload.name),1),o(`td`,null,r(e.payload.text),1)]))),128))])])):(P(),h(`p`,sn,r(t(a)(`当前会话暂无历史消息。`)),1))]),o(`form`,{class:`range`,onSubmit:u(k,[`prevent`])},[o(`label`,null,[e(r(t(a)(`起始序号`)),1),z(o(`input`,{"onUpdate:modelValue":s[1]||=e=>S.value=e,name:`from`,type:`number`,required:``},null,512),[[d,S.value]])]),o(`label`,null,[e(r(t(a)(`截止序号`)),1),z(o(`input`,{"onUpdate:modelValue":s[2]||=e=>C.value=e,name:`to`,type:`number`,required:``},null,512),[[d,C.value]])]),o(`button`,{id:`replay`,class:`primary`,disabled:T.value||!n.events.length},r(T.value?t(a)(`正在回放…`):t(a)(`回放这段对话`)),9,cn),o(`p`,ln,r(t(a)(`隔离回放:只测试回复,不发送 QQ,不修改真实记忆和 TA 的心智。`)),1)],32)]),o(`section`,un,[o(`div`,dn,[o(`h3`,null,r(t(a)(`处理记录`)),1)]),o(`div`,fn,[(P(!0),h(f,null,v(y.value,e=>(P(),h(`button`,{key:e.id,"data-trace":e.id,class:L([`trace-row`,{selected:b.value?.id===e.id}]),onClick:t=>A(e.id)},[o(`span`,null,[o(`time`,null,r(t(J)(e.time)),1),o(`small`,{class:`chip`,"data-tone":e.status===`error`?`danger`:`quiet`},r(t(g)[e.status]||e.status),9,mn)]),o(`p`,null,r(e.reason||e.error||t(a)(`查看处理详情`)),1)],10,pn))),128)),y.value.length?p(``,!0):(P(),h(`p`,hn,r(t(a)(`有新的消息处理记录后会显示在这里。`)),1))])]),o(`section`,gn,[o(`div`,_n,[o(`h3`,null,r(t(a)(`运行详情`)),1)]),o(`div`,vn,[b.value?(P(),h(`div`,yn,[o(`span`,null,[e(r(t(a)(`模式`)),1),o(`b`,null,r(t(_)[b.value.mode]||b.value.mode),1)]),o(`span`,null,[e(r(t(a)(`耗时`)),1),o(`b`,null,r(b.value.data?.elapsed??`—`)+` ms`,1)]),o(`span`,null,[e(r(t(a)(`模型调用`)),1),o(`b`,null,r(t(a)(`{v} 次`,{v:b.value.data?.calls?.length??0})),1)]),o(`span`,bn,[s[4]||=e(`Token`,-1),o(`b`,null,r(j(b.value.data?.tokens)),1)])])):p(``,!0),b.value?(P(),h(`p`,xn,r(b.value.reason||b.value.data?.decision?.reason||t(a)(`展开下方查看上下文、模型输入与原始输出。`)),1)):p(``,!0),o(`pre`,Sn,r(E.value||t(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=D(O({__name:`ChatsPage`,setup(e){let r=re(`(max-width: 1100px)`),i=A(()=>M.core?.sessions||[]),c=A(()=>i.value.filter(e=>!e.archived)),l=sessionStorage.activeSession,u=R(c.value.some(e=>e.id===l)?l:c.value[0]?.id||``),d=A(()=>c.value.find(e=>e.id===u.value)||null),f=R([]),v=R([]),S=R([]),w=R([]),E=R({}),D=R(``),O=R(!1),j=R(!1),N=R(!1),z=R(M.sub===`settings`),B=R(M.sub===`replay`),V=R(`here`),H=A(()=>M.health?.decisions||[]),U=A(()=>w.value.find(e=>e.session===u.value)||null),W=!1,G=!1,K=!1,q=``,J=0,Y=null,X=0;async function Z(e=!1){if(!u.value)return;if(W){G=!0,K||=e;return}let t=u.value,n=e||q!==t;W=!0;try{let[e,r]=await Promise.all([ie(t,n?void 0:J),ae(t,{limit:30,compact:!0})]);if(u.value!==t)return;let i=document.getElementById(`liveMessages`),o=q!==t||!i||i.scrollHeight-i.scrollTop-i.clientHeight<80;n?(f.value=e.reverse(),J=0):e.length&&(f.value=[...f.value,...e]);for(let t of e)J=Math.max(J,Number(t.seq)||0);!n&&e.length===100&&(G=!0),v.value=r,q=t,await C();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(W=!1,u.value!==t)G=!1,Z();else if(G){let e=K;G=!1,K=!1,Z(e)}}}async function he(){try{let[e,t]=await Promise.all([ee.bonds(),ee.overview()]);w.value=e.groups,E.value=Object.fromEntries(t.attention.map(e=>[e.session,e.unread]))}catch{}}x(u,e=>{if(Y?.abort(),e&&(sessionStorage.activeSession=e),f.value=[],v.value=[],S.value=[],q=``,J=0,G=!1,K=!1,N.value=!1,Z(),!e)return;pe(e).then(t=>{u.value===e&&(S.value=[...t].reverse())}).catch(()=>{});let t=new AbortController;Y=t;let n=``;ne(e=>{let t=Number(e?.messages);Number.isSafeInteger(t)&&t<J&&(q=``,J=0,f.value=[]);let r=JSON.stringify(e),i=n!==``&&r!==n;n=r,i&&Z()},{session:e,signal:t.signal,reconnectMs:1e3})},{immediate:!0}),x(c,e=>{e.some(e=>e.id===u.value)||(u.value=e[0]?.id||``)}),x(V,async e=>{if(e===`feedback`)try{M.health=await b()}catch{}}),x(B,e=>k(e?`replay`:z.value?`settings`:``)),x(()=>M.sub,e=>{e===`replay`&&(B.value=!0),e===`settings`&&(z.value=!0)}),x(()=>M.pulse,he),_(()=>{he(),X=window.setInterval(()=>Z(!0),15e3)}),T(()=>{clearInterval(X),Y?.abort()});async function ge(e){try{let t=await ce(e);await g(),u.value=t?.sessionId||`${e.kind}:${e.id}`,z.value=!0,V.value=`here`,j.value=!1,F(a(`会话已添加`))}catch(e){F(e.message,!0)}}async function Q(e){await le(e.id,!e.enabled),await g()}async function _e(e){await ue(e,!0),await g()}async function ve(e){await ue(e,!1),await g()}async function ye(e){await n(a(`永久删除这个会话?它的消息和设置都会消失。`),{title:a(`永久删除`),confirmText:a(`删除`),danger:!0})&&(await de(e),await g())}async function be(e){if(d.value)try{await fe(d.value.id,{...e,name:String(e.name||``).trim()||d.value.name}),M.dirty=!1,F(a(`会话配置已保存`)),await g()}catch(e){F(e.message,!0)}}async function xe(){u.value&&await n(a(`确定清空这个会话的消息上下文吗?长期记忆不会删除。`),{title:a(`清空上下文`),confirmText:a(`清空`),danger:!0})&&(await me(u.value),f.value=[],v.value=[],q=``,J=0,await Z(!0))}async function Se(e){if(!O.value&&u.value){O.value=!0;try{let t=await oe({sessionId:u.value,...e});F(t.reason||a(`已发送`)),M.health=await b(),await Z()}catch(e){F(e.message,!0)}finally{O.value=!1}}}async function Ce(e,t){try{await se(e,t),F(a(`反馈已保存`)),M.health=await b()}catch(e){F(e.message,!0)}}return(e,n)=>(P(),h(`div`,{class:L([`page chats`,{compact:t(r)}])},[t(r)?p(``,!0):(P(),h(`aside`,wn,[y(Oe,{sessions:i.value,selected:u.value,unread:E.value,onSelect:n[0]||=e=>u.value=e,onAdd:n[1]||=e=>j.value=!0,onToggle:Q,onRestore:ve,onRemove:ye},null,8,[`sessions`,`selected`,`unread`])])),o(`div`,Tn,[y(pt,{"session-id":u.value,session:d.value,events:f.value,traces:v.value,decisions:H.value,status:D.value||t(a)(`请选择会话`),demo:!!t(M).health.settings.demo,compact:t(r),sending:O.value,onSimulate:Se,onFeedback:Ce,onOpenList:n[2]||=e=>N.value=!0,onOpenReplay:n[3]||=e=>B.value=!0},null,8,[`session-id`,`session`,`events`,`traces`,`decisions`,`status`,`demo`,`compact`,`sending`])]),o(`aside`,En,[y(en,{"settings-open":z.value,"onUpdate:settingsOpen":n[4]||=e=>z.value=e,tab:V.value,"onUpdate:tab":n[5]||=e=>V.value=e,session:d.value,group:U.value,summaries:S.value,decisions:H.value,onToggle:Q,onArchive:_e,onClear:xe,onSave:be,onFeedback:Ce},null,8,[`settings-open`,`tab`,`session`,`group`,`summaries`,`decisions`])]),t(r)?(P(),m(te,{key:1,open:N.value,title:t(a)(`全部会话`),eyebrow:`CHATS`,width:`400px`,onClose:n[8]||=e=>N.value=!1},{default:I(()=>[o(`div`,Dn,[y(Oe,{sessions:i.value,selected:u.value,unread:E.value,onSelect:n[6]||=e=>u.value=e,onAdd:n[7]||=e=>j.value=!0,onToggle:Q,onRestore:ve,onRemove:ye},null,8,[`sessions`,`selected`,`unread`])])]),_:1},8,[`open`,`title`])):p(``,!0),y(Ne,{open:j.value,channel:t(M).health?.connection?.channel===`qqbot`?`qqbot`:`onebot`,ready:!!t(M).health?.connection?.qqbot?.appId,onClose:n[9]||=e=>j.value=!1,onAdd:ge},null,8,[`open`,`channel`,`ready`]),y(Cn,{open:B.value,"session-id":u.value,events:f.value,onClose:n[10]||=e=>B.value=!1,onLoad:n[11]||=e=>Z(!0)},null,8,[`open`,`session-id`,`events`])],2))}}),[[`__scopeId`,`data-v-c328f9fe`]]);export{On as default};
|