wezard 0.3.3
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/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +14 -0
- package/CLAUDE.md +83 -0
- package/LICENSE +21 -0
- package/README.md +410 -0
- package/cli/weclaude.sh +236 -0
- package/commands/audit.md +7 -0
- package/commands/wrc.md +7 -0
- package/config.example.jsonc +102 -0
- package/dist/cli/audit.js +156 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/init.js +311 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/sync.js +175 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/daemon/approval.js +1301 -0
- package/dist/daemon/approval.js.map +1 -0
- package/dist/daemon/ask.js +101 -0
- package/dist/daemon/ask.js.map +1 -0
- package/dist/daemon/audit.js +168 -0
- package/dist/daemon/audit.js.map +1 -0
- package/dist/daemon/cc-bridge.js +204 -0
- package/dist/daemon/cc-bridge.js.map +1 -0
- package/dist/daemon/cfg-sync.js +240 -0
- package/dist/daemon/cfg-sync.js.map +1 -0
- package/dist/daemon/claim.js +95 -0
- package/dist/daemon/claim.js.map +1 -0
- package/dist/daemon/detail.js +153 -0
- package/dist/daemon/detail.js.map +1 -0
- package/dist/daemon/graph.js +176 -0
- package/dist/daemon/graph.js.map +1 -0
- package/dist/daemon/http.js +83 -0
- package/dist/daemon/http.js.map +1 -0
- package/dist/daemon/inbound.js +919 -0
- package/dist/daemon/inbound.js.map +1 -0
- package/dist/daemon/index.js +569 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/last-response.js +59 -0
- package/dist/daemon/last-response.js.map +1 -0
- package/dist/daemon/mirror-bridge.js +3512 -0
- package/dist/daemon/mirror-bridge.js.map +1 -0
- package/dist/daemon/mirror-store.js +39 -0
- package/dist/daemon/mirror-store.js.map +1 -0
- package/dist/daemon/outbound.js +42 -0
- package/dist/daemon/outbound.js.map +1 -0
- package/dist/daemon/peers.js +146 -0
- package/dist/daemon/peers.js.map +1 -0
- package/dist/daemon/pending.js +90 -0
- package/dist/daemon/pending.js.map +1 -0
- package/dist/daemon/quota.js +164 -0
- package/dist/daemon/quota.js.map +1 -0
- package/dist/daemon/redact.js +27 -0
- package/dist/daemon/redact.js.map +1 -0
- package/dist/daemon/session-cache.js +149 -0
- package/dist/daemon/session-cache.js.map +1 -0
- package/dist/daemon/session-scan.js +273 -0
- package/dist/daemon/session-scan.js.map +1 -0
- package/dist/daemon/sessions.js +35 -0
- package/dist/daemon/sessions.js.map +1 -0
- package/dist/daemon/spawn-tmux.js +213 -0
- package/dist/daemon/spawn-tmux.js.map +1 -0
- package/dist/daemon/tips.js +25 -0
- package/dist/daemon/tips.js.map +1 -0
- package/dist/daemon/topics.js +197 -0
- package/dist/daemon/topics.js.map +1 -0
- package/dist/daemon/usage.js +358 -0
- package/dist/daemon/usage.js.map +1 -0
- package/dist/daemon/wedoc.js +210 -0
- package/dist/daemon/wedoc.js.map +1 -0
- package/dist/daemon/ws.js +67 -0
- package/dist/daemon/ws.js.map +1 -0
- package/dist/mcp/server.js +385 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/shared/ansi.js +134 -0
- package/dist/shared/ansi.js.map +1 -0
- package/dist/shared/chat-http.js +195 -0
- package/dist/shared/chat-http.js.map +1 -0
- package/dist/shared/chat-render.js +53 -0
- package/dist/shared/chat-render.js.map +1 -0
- package/dist/shared/chat-view.js +116 -0
- package/dist/shared/chat-view.js.map +1 -0
- package/dist/shared/cli-backends.js +304 -0
- package/dist/shared/cli-backends.js.map +1 -0
- package/dist/shared/config-writer.js +39 -0
- package/dist/shared/config-writer.js.map +1 -0
- package/dist/shared/config.js +278 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/detail-render.js +836 -0
- package/dist/shared/detail-render.js.map +1 -0
- package/dist/shared/detail-store.js +205 -0
- package/dist/shared/detail-store.js.map +1 -0
- package/dist/shared/highlight.js +154 -0
- package/dist/shared/highlight.js.map +1 -0
- package/dist/shared/lan-ip.js +35 -0
- package/dist/shared/lan-ip.js.map +1 -0
- package/dist/shared/log.js +18 -0
- package/dist/shared/log.js.map +1 -0
- package/dist/shared/md-chunk.js +99 -0
- package/dist/shared/md-chunk.js.map +1 -0
- package/dist/shared/paths.js +12 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/session-label.js +67 -0
- package/dist/shared/session-label.js.map +1 -0
- package/dist/shared/web-assets.js +51 -0
- package/dist/shared/web-assets.js.map +1 -0
- package/dist/svr/index.js +246 -0
- package/dist/svr/index.js.map +1 -0
- package/hooks/hooks.json +16 -0
- package/hooks/pre-tool-use.sh +167 -0
- package/launchd/com.weclaude.daemon.plist.template +45 -0
- package/package.json +97 -0
- package/scripts/install.sh +73 -0
- package/scripts/postinstall.sh +12 -0
- package/scripts/uninstall.sh +44 -0
- package/systemd/weclaude.service.template +17 -0
- package/web/chat.css +171 -0
- package/web/chat.js +367 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { baseOfKey } from "./session-label.js";
|
|
2
|
+
import { chatSummary, isTurn, threadOf, turnDone } from "./chat-view.js";
|
|
3
|
+
import { renderTurnGroup } from "./detail-render.js";
|
|
4
|
+
import { chatScript, chatStyles, renderChatPage } from "./chat-render.js";
|
|
5
|
+
const DEFAULT_LIMIT = 20;
|
|
6
|
+
const FLUSH_MS = 300;
|
|
7
|
+
const PING_MS = 25_000;
|
|
8
|
+
const json = (res, status, body) => {
|
|
9
|
+
res.statusCode = status;
|
|
10
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
11
|
+
res.setHeader("cache-control", "no-store");
|
|
12
|
+
res.end(JSON.stringify(body));
|
|
13
|
+
};
|
|
14
|
+
/** Chat a record belongs to. Turns/tools carry `target` directly; an approval
|
|
15
|
+
* only knows its sessionId, so borrow the base from a turn of that session. */
|
|
16
|
+
const baseFromRecord = (store, rec) => {
|
|
17
|
+
const direct = rec.target;
|
|
18
|
+
if (direct)
|
|
19
|
+
return baseOfKey(direct);
|
|
20
|
+
const sid = rec.sessionId;
|
|
21
|
+
if (!sid)
|
|
22
|
+
return "";
|
|
23
|
+
const owner = store.list()
|
|
24
|
+
.filter(isTurn)
|
|
25
|
+
.filter((r) => r.sessionId === sid && r.target)
|
|
26
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)[0];
|
|
27
|
+
return owner?.target ? baseOfKey(owner.target) : "";
|
|
28
|
+
};
|
|
29
|
+
const resolveScope = (store, url) => {
|
|
30
|
+
const id = url.searchParams.get("id") ?? "";
|
|
31
|
+
if (!id)
|
|
32
|
+
return undefined;
|
|
33
|
+
const rec = store.get(id);
|
|
34
|
+
if (!rec)
|
|
35
|
+
return undefined;
|
|
36
|
+
const base = baseFromRecord(store, rec);
|
|
37
|
+
if (!base)
|
|
38
|
+
return undefined;
|
|
39
|
+
return { base, selfTarget: rec.target ?? "" };
|
|
40
|
+
};
|
|
41
|
+
/** A `?target=` is only honoured when it lives in the same chat as the token. */
|
|
42
|
+
const authorizedTarget = (scope, raw) => {
|
|
43
|
+
const t = (raw ?? "").trim();
|
|
44
|
+
if (!t)
|
|
45
|
+
return scope.selfTarget;
|
|
46
|
+
return baseOfKey(t) === scope.base ? t : scope.selfTarget;
|
|
47
|
+
};
|
|
48
|
+
export const createChatRoutes = (store) => {
|
|
49
|
+
const summary = (base) => chatSummary(store.list(), base, Date.now());
|
|
50
|
+
const page = (_req, res) => {
|
|
51
|
+
res.statusCode = 200;
|
|
52
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
53
|
+
res.setHeader("cache-control", "no-store");
|
|
54
|
+
res.end(renderChatPage());
|
|
55
|
+
};
|
|
56
|
+
// 前端资源: ETag 跟着文件 mtime 走 —— 改一次 web/chat.js 浏览器就重新取一次,
|
|
57
|
+
// 没改则 304, 而 shell 本身永远 no-store, 不会把旧版本钉死在缓存里。
|
|
58
|
+
const asset = (make) => (req, res) => {
|
|
59
|
+
const a = make();
|
|
60
|
+
res.setHeader("cache-control", "no-cache");
|
|
61
|
+
res.setHeader("etag", a.etag);
|
|
62
|
+
if (req.headers["if-none-match"] === a.etag) {
|
|
63
|
+
res.statusCode = 304;
|
|
64
|
+
res.end();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
res.statusCode = 200;
|
|
68
|
+
res.setHeader("content-type", a.type);
|
|
69
|
+
res.end(a.body);
|
|
70
|
+
};
|
|
71
|
+
const chat = (_req, res, url) => {
|
|
72
|
+
const scope = resolveScope(store, url);
|
|
73
|
+
if (!scope) {
|
|
74
|
+
json(res, 404, { ok: false, error: "未找到该会话 (链接可能已过期)" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const s = summary(scope.base);
|
|
78
|
+
json(res, 200, { ok: true, ...s, self: { target: scope.selfTarget } });
|
|
79
|
+
};
|
|
80
|
+
const thread = (_req, res, url) => {
|
|
81
|
+
const scope = resolveScope(store, url);
|
|
82
|
+
if (!scope) {
|
|
83
|
+
json(res, 404, { ok: false, error: "未找到该会话 (链接可能已过期)" });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const target = authorizedTarget(scope, url.searchParams.get("target"));
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const all = threadOf(store.list(), target, now);
|
|
89
|
+
// 注意 Number(null) === 0 —— 缺省必须先判 null, 否则默认就成了"全量"。
|
|
90
|
+
const raw = url.searchParams.get("limit");
|
|
91
|
+
const n = raw === null ? Number.NaN : Number(raw);
|
|
92
|
+
const limit = Number.isFinite(n) && n >= 0 ? n : DEFAULT_LIMIT;
|
|
93
|
+
// 0 = 全量; 否则只回最近 N 轮 (页面默认贴底, 更早的按需再拉)。
|
|
94
|
+
const shown = limit === 0 ? all : all.slice(-limit);
|
|
95
|
+
json(res, 200, {
|
|
96
|
+
ok: true,
|
|
97
|
+
target,
|
|
98
|
+
at: now,
|
|
99
|
+
total: all.length,
|
|
100
|
+
truncated: shown.length < all.length,
|
|
101
|
+
running: all.some((r) => !turnDone(r, now)),
|
|
102
|
+
turns: shown.map((r) => renderTurnGroup(r, now)),
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
// SSE — 一条连接同时喂两种事件: chat (侧栏 + 页脚总账) 与 turn (当前 tag 的增量)。
|
|
106
|
+
// store.subscribe 的回调在写入路径上, 所以这里只打标记, 由 FLUSH_MS 定时器合并推送:
|
|
107
|
+
// 一次工具结果会连带更新 turn 记录多次, 逐条推会把同一段 HTML 重复渲染。
|
|
108
|
+
const events = (req, res, url) => {
|
|
109
|
+
const scope = resolveScope(store, url);
|
|
110
|
+
if (!scope) {
|
|
111
|
+
json(res, 404, { ok: false, error: "not found" });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const target = authorizedTarget(scope, url.searchParams.get("target"));
|
|
115
|
+
res.writeHead(200, {
|
|
116
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
117
|
+
"cache-control": "no-store, no-transform",
|
|
118
|
+
connection: "keep-alive",
|
|
119
|
+
"x-accel-buffering": "no", // nginx 反代下不缓冲, 否则事件会被攒住
|
|
120
|
+
});
|
|
121
|
+
res.write(": open\n\n");
|
|
122
|
+
const send = (event, data) => {
|
|
123
|
+
try {
|
|
124
|
+
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
125
|
+
}
|
|
126
|
+
catch { /* closed */ }
|
|
127
|
+
};
|
|
128
|
+
const pushChat = () => send("chat", { ...summary(scope.base), self: { target: scope.selfTarget } });
|
|
129
|
+
// 一个 turn 的 HTML 可以是几十 KB, 而 usage 累加这类改动并不改变正文 —— 按 sig
|
|
130
|
+
// 去重, 内容没变就不重发。
|
|
131
|
+
const sentSig = new Map();
|
|
132
|
+
const pushTurn = (id) => {
|
|
133
|
+
const r = store.get(id);
|
|
134
|
+
if (!r || !isTurn(r))
|
|
135
|
+
return;
|
|
136
|
+
const frag = renderTurnGroup(r, Date.now());
|
|
137
|
+
if (sentSig.get(id) === frag.sig)
|
|
138
|
+
return;
|
|
139
|
+
sentSig.set(id, frag.sig);
|
|
140
|
+
send("turn", frag);
|
|
141
|
+
};
|
|
142
|
+
pushChat();
|
|
143
|
+
let chatDirty = false;
|
|
144
|
+
const turnDirty = new Set();
|
|
145
|
+
const flush = setInterval(() => {
|
|
146
|
+
if (chatDirty) {
|
|
147
|
+
chatDirty = false;
|
|
148
|
+
pushChat();
|
|
149
|
+
}
|
|
150
|
+
for (const id of turnDirty)
|
|
151
|
+
pushTurn(id);
|
|
152
|
+
turnDirty.clear();
|
|
153
|
+
}, FLUSH_MS);
|
|
154
|
+
const ping = setInterval(() => { try {
|
|
155
|
+
res.write(": ping\n\n");
|
|
156
|
+
}
|
|
157
|
+
catch { /* closed */ } }, PING_MS);
|
|
158
|
+
const unsub = store.subscribe((rec) => {
|
|
159
|
+
if (!isTurn(rec) || !rec.target)
|
|
160
|
+
return;
|
|
161
|
+
if (baseOfKey(rec.target) !== scope.base)
|
|
162
|
+
return;
|
|
163
|
+
chatDirty = true;
|
|
164
|
+
if (rec.target === target)
|
|
165
|
+
turnDirty.add(rec.id);
|
|
166
|
+
});
|
|
167
|
+
const close = () => {
|
|
168
|
+
unsub();
|
|
169
|
+
clearInterval(flush);
|
|
170
|
+
clearInterval(ping);
|
|
171
|
+
try {
|
|
172
|
+
res.end();
|
|
173
|
+
}
|
|
174
|
+
catch { /* ignore */ }
|
|
175
|
+
};
|
|
176
|
+
req.on("close", close);
|
|
177
|
+
req.on("error", close);
|
|
178
|
+
};
|
|
179
|
+
return { page, styles: asset(chatStyles), script: asset(chatScript), chat, thread, events };
|
|
180
|
+
};
|
|
181
|
+
/** Path → handler map; the daemon registers each, svr dispatches through it. */
|
|
182
|
+
export const chatRouteTable = (routes) => ({
|
|
183
|
+
"GET /chat": routes.page,
|
|
184
|
+
"GET /chat/app.css": routes.styles,
|
|
185
|
+
"GET /chat/app.js": routes.script,
|
|
186
|
+
"GET /api/chat": routes.chat,
|
|
187
|
+
"GET /api/thread": routes.thread,
|
|
188
|
+
"GET /api/events": routes.events,
|
|
189
|
+
});
|
|
190
|
+
/** Route keys, single-sourced so the daemon's registration can't drift. */
|
|
191
|
+
export const CHAT_ROUTE_KEYS = [
|
|
192
|
+
"GET /chat", "GET /chat/app.css", "GET /chat/app.js",
|
|
193
|
+
"GET /api/chat", "GET /api/thread", "GET /api/events",
|
|
194
|
+
];
|
|
195
|
+
//# sourceMappingURL=chat-http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-http.js","sourceRoot":"","sources":["../../shared/chat-http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Chat 详情视图的服务端外壳 —— 只剩一张静态 HTML 骨架。
|
|
2
|
+
//
|
|
3
|
+
// 样式与脚本已经搬到 web/chat.css / web/chat.js, 由 /chat/app.css、/chat/app.js
|
|
4
|
+
// 独立提供 (见 ./chat-http)。好处不只是"代码在真文件里": 页面 shell 常驻不变,
|
|
5
|
+
// 资源可被浏览器按 ETag 缓存, 而 turn 正文继续由服务端渲染 (diff / ANSI / 语法
|
|
6
|
+
// 高亮只在 shared/detail-render 实现一次, 不必在客户端重写一遍)。
|
|
7
|
+
//
|
|
8
|
+
// 资源 URL 一律用相对路径 —— svr 常挂在反代子路径下 (如 /wc/chat), 绝对路径会 404。
|
|
9
|
+
import { SHARED_CSS, TURN_CSS } from "./detail-render.js";
|
|
10
|
+
import { readAsset } from "./web-assets.js";
|
|
11
|
+
/** /chat/app.css = 详情页共用样式 + 本视图外壳样式。 */
|
|
12
|
+
export const chatStyles = () => {
|
|
13
|
+
const own = readAsset("chat.css");
|
|
14
|
+
return {
|
|
15
|
+
body: `${SHARED_CSS}${TURN_CSS}${own?.body ?? ""}`,
|
|
16
|
+
type: "text/css; charset=utf-8",
|
|
17
|
+
etag: own?.etag ?? 'W/"nocss"',
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export const chatScript = () => readAsset("chat.js") ?? { body: "", type: "text/javascript; charset=utf-8", etag: 'W/"nojs"' };
|
|
21
|
+
export const renderChatPage = () => `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">
|
|
22
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
23
|
+
<title>Chat Details</title>
|
|
24
|
+
<link rel="stylesheet" href="https://unpkg.com/highlight.js@11/styles/github.min.css">
|
|
25
|
+
<link rel="stylesheet" href="chat/app.css">
|
|
26
|
+
<script src="https://unpkg.com/markdown-it@14/dist/markdown-it.min.js"></script>
|
|
27
|
+
<script src="https://unpkg.com/@highlightjs/cdn-assets@11/highlight.min.js"></script>
|
|
28
|
+
</head><body>
|
|
29
|
+
<div class="app">
|
|
30
|
+
<aside class="side">
|
|
31
|
+
<div class="side-head">
|
|
32
|
+
<div class="l1"><span class="t">会话列表</span><span class="n" id="side-n"></span></div>
|
|
33
|
+
<div class="s" id="side-sub"></div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="tags" id="tags"></div>
|
|
36
|
+
</aside>
|
|
37
|
+
<main class="main">
|
|
38
|
+
<div class="topbar">
|
|
39
|
+
<span class="back" id="tb-back">‹</span>
|
|
40
|
+
<span class="em" id="tb-em">💬</span>
|
|
41
|
+
<span class="h" id="tb-h"></span>
|
|
42
|
+
<span class="sub" id="tb-sub"></span>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="thread" id="thread"><div class="thread-in" id="thread-in"></div></div>
|
|
45
|
+
<div class="statusbar">
|
|
46
|
+
<span id="sb" style="display:contents"></span>
|
|
47
|
+
<span class="conn" id="conn"><span class="d"></span></span>
|
|
48
|
+
</div>
|
|
49
|
+
</main>
|
|
50
|
+
</div>
|
|
51
|
+
<script src="chat/app.js" defer></script>
|
|
52
|
+
</body></html>`;
|
|
53
|
+
//# sourceMappingURL=chat-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-render.js","sourceRoot":"","sources":["../../shared/chat-render.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,EAAE;AACF,qEAAqE;AACrE,sDAAsD;AACtD,wDAAwD;AACxD,+CAA+C;AAC/C,EAAE;AACF,2DAA2D;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AAExD,yCAAyC;AACzC,MAAM,CAAC,MAAM,UAAU,GAAG,GAAU,EAAE;IACpC,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAClC,OAAO;QACL,IAAI,EAAE,GAAG,UAAU,GAAG,QAAQ,GAAG,GAAG,EAAE,IAAI,IAAI,EAAE,EAAE;QAClD,IAAI,EAAE,yBAAyB;QAC/B,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,WAAW;KAC/B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAU,EAAE,CACpC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,gCAAgC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAEjG,MAAM,CAAC,MAAM,cAAc,GAAG,GAAW,EAAE,CACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA+Ba,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Chat-level derivations over the detail store — pure, no IO.
|
|
2
|
+
//
|
|
3
|
+
// A WeCom chat hosts one default session plus any number of `#tag` siblings
|
|
4
|
+
// (see session-label). The store already stamps every turn with its `target`
|
|
5
|
+
// (`chat:xxx#fix`), so "what does this chat look like right now" is a fold over
|
|
6
|
+
// the turn records sharing a base principal: group by target → a chat list,
|
|
7
|
+
// order one group by time → a thread, sum the usages → a status bar.
|
|
8
|
+
//
|
|
9
|
+
// Nothing here touches tmux or the mirror bridge, so the standalone svr derives
|
|
10
|
+
// exactly the same view from the records that were POSTed to it.
|
|
11
|
+
import { baseOfKey, labelFor, tagOfKey } from "./session-label.js";
|
|
12
|
+
const ZERO = {
|
|
13
|
+
input: 0, output: 0, cacheRead: 0, cacheWrite: 0, calls: 0,
|
|
14
|
+
ctxPeak: 0, durationMs: 0, turns: 0, tools: 0,
|
|
15
|
+
};
|
|
16
|
+
// ── turn 结束判定 ─────────────────────────────────────────────────────
|
|
17
|
+
// 三种证据, 由强到弱: closed (收口信号) → final text (closeTurn 可能滞后, 但
|
|
18
|
+
// final 就是最后一条) → 静默超时。第三条是唯一的兜底: daemon 重启 / 漏收
|
|
19
|
+
// turn_end 会把 turn 永久留在 open 态, 侧栏于是永远「运行中」, 而页脚耗时
|
|
20
|
+
// (open turn 的 span 按 now 算) 会一路累加到几十小时。
|
|
21
|
+
//
|
|
22
|
+
// 静默阈值分两档, 因为"没有新 item"有两种截然不同的含义:
|
|
23
|
+
// • 尾项是已闭合的文本/工具结果 → agent 真的停了, 2 分钟足够;
|
|
24
|
+
// • 尾项是未回的 tool_use 或未决的 approval → 它在**等**, 一次 build 或一次
|
|
25
|
+
// 人工点按可以耗掉很久, 按短档判死会让页面在任务跑到一半时变「已完成」。
|
|
26
|
+
const IDLE_MS = 120_000;
|
|
27
|
+
const IDLE_AWAIT_MS = 3600_000;
|
|
28
|
+
const lastItemOf = (r) => r.items.reduce((m, it) => (!m || it.ts >= m.ts ? it : m), undefined);
|
|
29
|
+
/** 尾项还挂着未回的工具 / 未决的审批 —— 静默是「在等」, 不是「结束了」。 */
|
|
30
|
+
const awaitingReply = (r) => {
|
|
31
|
+
const last = lastItemOf(r);
|
|
32
|
+
if (!last)
|
|
33
|
+
return false;
|
|
34
|
+
if (last.t === "approval")
|
|
35
|
+
return last.decision === undefined;
|
|
36
|
+
if (last.t === "tool_use") {
|
|
37
|
+
return !r.items.some((it) => it.t === "tool_result" && it.toolUseId === last.toolUseId);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
};
|
|
41
|
+
/** 该 turn 若再无写入, 到这个时刻就算结束。0 = 已经结束。 */
|
|
42
|
+
export const staleAt = (r) => r.closed || r.items.some((it) => it.t === "text" && it.final === true)
|
|
43
|
+
? 0
|
|
44
|
+
: r.updatedAt + (awaitingReply(r) ? IDLE_AWAIT_MS : IDLE_MS);
|
|
45
|
+
export const turnDone = (r, now) => {
|
|
46
|
+
const until = staleAt(r);
|
|
47
|
+
return until === 0 || now > until;
|
|
48
|
+
};
|
|
49
|
+
/** 空壳 turn: 记录建了, 但一条 item、一次 usage 都没落地 —— ack 之后队列被强关 /
|
|
50
|
+
* daemon 重启 / 注入失败都会留下它。刚建的几秒内是正常的 ack 态, 过了静默期就是
|
|
51
|
+
* 垃圾: 计进轮数与耗时只会污染统计, 渲染出来是个空的 turn 分组。 */
|
|
52
|
+
export const isGhostTurn = (r, now) => r.items.length === 0 && !r.usage && now - r.updatedAt > IDLE_MS;
|
|
53
|
+
const turnSpan = (r, now) => (turnDone(r, now) ? r.updatedAt : now) - r.createdAt;
|
|
54
|
+
const addUsage = (a, r, now) => {
|
|
55
|
+
const u = r.usage;
|
|
56
|
+
const ctx = u ? (u.ctxPeak ?? u.input + u.cacheRead + u.cacheWrite) : 0;
|
|
57
|
+
return {
|
|
58
|
+
input: a.input + (u?.input ?? 0),
|
|
59
|
+
output: a.output + (u?.output ?? 0),
|
|
60
|
+
cacheRead: a.cacheRead + (u?.cacheRead ?? 0),
|
|
61
|
+
cacheWrite: a.cacheWrite + (u?.cacheWrite ?? 0),
|
|
62
|
+
calls: a.calls + (u?.calls ?? 0),
|
|
63
|
+
serviceTier: a.serviceTier ?? u?.serviceTier,
|
|
64
|
+
ctxPeak: Math.max(a.ctxPeak ?? 0, ctx),
|
|
65
|
+
durationMs: a.durationMs + turnSpan(r, now),
|
|
66
|
+
turns: a.turns + 1,
|
|
67
|
+
tools: a.tools + r.items.filter((it) => it.t === "tool_use").length,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export const aggregate = (turns, now) => turns.reduce((a, r) => addUsage(a, r, now), ZERO);
|
|
71
|
+
const stripMd = (s) => s.replace(/[`*_~|#>]/g, "").replace(/\s+/g, " ").trim();
|
|
72
|
+
/** Last assistant prose of a turn, else the query that opened it. */
|
|
73
|
+
const previewOf = (r) => {
|
|
74
|
+
const texts = r.items.filter((it) => it.t === "text");
|
|
75
|
+
const last = texts[texts.length - 1]?.body ?? "";
|
|
76
|
+
const src = last || r.userQuery || "";
|
|
77
|
+
return stripMd(src).slice(0, 120);
|
|
78
|
+
};
|
|
79
|
+
export const isTurn = (r) => r.kind === "turn";
|
|
80
|
+
/** Real turns of one chat — every ghost dropped exactly once, up front, so the
|
|
81
|
+
* list / thread / status bar can never disagree about what counts. */
|
|
82
|
+
const liveTurns = (records, now) => records.filter(isTurn).filter((r) => !isGhostTurn(r, now));
|
|
83
|
+
/** Turns of one session key, oldest first — the thread order. */
|
|
84
|
+
export const threadOf = (records, target, now) => liveTurns(records, now).filter((r) => r.target === target).sort((a, b) => a.createdAt - b.createdAt);
|
|
85
|
+
const summarizeTag = (target, turns, now) => {
|
|
86
|
+
const last = turns[turns.length - 1];
|
|
87
|
+
const tag = tagOfKey(target);
|
|
88
|
+
const until = turns.reduce((m, r) => Math.max(m, staleAt(r)), 0);
|
|
89
|
+
return {
|
|
90
|
+
target,
|
|
91
|
+
tag,
|
|
92
|
+
label: labelFor(tag || target),
|
|
93
|
+
sessionId: last?.sessionId,
|
|
94
|
+
model: [...turns].reverse().find((r) => r.model)?.model,
|
|
95
|
+
turns: turns.length,
|
|
96
|
+
lastTs: turns.reduce((m, r) => Math.max(m, r.updatedAt), 0),
|
|
97
|
+
running: until > now,
|
|
98
|
+
runningUntil: until > now ? until : 0,
|
|
99
|
+
preview: last ? previewOf(last) : "",
|
|
100
|
+
usage: aggregate(turns, now),
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
/** Group every turn of one chat by session key → the chat list. Most recently
|
|
104
|
+
* active tag first; the running ones naturally float up. */
|
|
105
|
+
export const chatSummary = (records, base, now) => {
|
|
106
|
+
const mine = liveTurns(records, now).filter((r) => r.target && baseOfKey(r.target) === base);
|
|
107
|
+
const byTarget = mine.reduce((m, r) => {
|
|
108
|
+
const k = r.target;
|
|
109
|
+
return m.set(k, [...(m.get(k) ?? []), r]);
|
|
110
|
+
}, new Map());
|
|
111
|
+
const tags = [...byTarget.entries()]
|
|
112
|
+
.map(([target, turns]) => summarizeTag(target, [...turns].sort((a, b) => a.createdAt - b.createdAt), now))
|
|
113
|
+
.sort((a, b) => b.lastTs - a.lastTs);
|
|
114
|
+
return { base, at: now, tags, usage: aggregate(mine, now) };
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=chat-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-view.js","sourceRoot":"","sources":["../../shared/chat-view.ts"],"names":[],"mappings":"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"}
|