wezard 2.2.2 → 2.3.0
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.md +7 -4
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/daemon/approval.js +6 -4
- package/dist/daemon/approval.js.map +1 -1
- package/dist/daemon/chat-log.js +6 -1
- package/dist/daemon/chat-log.js.map +1 -1
- package/dist/daemon/danger.js +17 -0
- package/dist/daemon/danger.js.map +1 -1
- package/dist/daemon/detail.js +41 -0
- package/dist/daemon/detail.js.map +1 -1
- package/dist/daemon/handoff.js +8 -2
- package/dist/daemon/handoff.js.map +1 -1
- package/dist/daemon/inbound.js +9 -4
- package/dist/daemon/inbound.js.map +1 -1
- package/dist/daemon/index.js +744 -152
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/jobs.js +62 -23
- package/dist/daemon/jobs.js.map +1 -1
- package/dist/daemon/last-response.js +15 -0
- package/dist/daemon/last-response.js.map +1 -1
- package/dist/daemon/memory-steward.js +124 -68
- package/dist/daemon/memory-steward.js.map +1 -1
- package/dist/daemon/mirror-bridge.js +461 -91
- package/dist/daemon/mirror-bridge.js.map +1 -1
- package/dist/daemon/mirror-store.js.map +1 -1
- package/dist/daemon/model-select.js +99 -0
- package/dist/daemon/model-select.js.map +1 -1
- package/dist/daemon/notices.js +7 -5
- package/dist/daemon/notices.js.map +1 -1
- package/dist/daemon/peers.js +183 -60
- package/dist/daemon/peers.js.map +1 -1
- package/dist/daemon/receipts.js +391 -77
- package/dist/daemon/receipts.js.map +1 -1
- package/dist/daemon/route.js +63 -10
- package/dist/daemon/route.js.map +1 -1
- package/dist/daemon/spawn-tmux.js +24 -13
- package/dist/daemon/spawn-tmux.js.map +1 -1
- package/dist/daemon/task-registry.js +1 -1
- package/dist/daemon/task-registry.js.map +1 -1
- package/dist/daemon/tasks.js +12 -4
- package/dist/daemon/tasks.js.map +1 -1
- package/dist/daemon/usage.js +4 -11
- package/dist/daemon/usage.js.map +1 -1
- package/dist/daemon/wizard-memory.js +36 -4
- package/dist/daemon/wizard-memory.js.map +1 -1
- package/dist/daemon/wizard.js +102 -58
- package/dist/daemon/wizard.js.map +1 -1
- package/dist/mcp/server.js +138 -111
- package/dist/mcp/server.js.map +1 -1
- package/dist/shared/charter-view.js +35 -0
- package/dist/shared/charter-view.js.map +1 -0
- package/dist/shared/chat-http.js +133 -31
- package/dist/shared/chat-http.js.map +1 -1
- package/dist/shared/chat-render.js +6 -3
- package/dist/shared/chat-render.js.map +1 -1
- package/dist/shared/chat-view.js +17 -6
- package/dist/shared/chat-view.js.map +1 -1
- package/dist/shared/cli-backends.js +2 -0
- package/dist/shared/cli-backends.js.map +1 -1
- package/dist/shared/config.js +9 -5
- package/dist/shared/config.js.map +1 -1
- package/dist/shared/daemon-token.js +18 -0
- package/dist/shared/daemon-token.js.map +1 -0
- package/dist/shared/detail-render.js +140 -16
- package/dist/shared/detail-render.js.map +1 -1
- package/dist/shared/detail-store.js +33 -12
- package/dist/shared/detail-store.js.map +1 -1
- package/dist/shared/effort.js +11 -0
- package/dist/shared/effort.js.map +1 -0
- package/dist/shared/model-prices.js +258 -0
- package/dist/shared/model-prices.js.map +1 -0
- package/dist/shared/pricing.js +55 -0
- package/dist/shared/pricing.js.map +1 -0
- package/dist/shared/reminder.js +18 -4
- package/dist/shared/reminder.js.map +1 -1
- package/dist/shared/role-render.js +172 -8
- package/dist/shared/role-render.js.map +1 -1
- package/dist/shared/role-view.js +89 -25
- package/dist/shared/role-view.js.map +1 -1
- package/dist/shared/session-label.js +4 -0
- package/dist/shared/session-label.js.map +1 -1
- package/dist/shared/task-file.js +16 -1
- package/dist/shared/task-file.js.map +1 -1
- package/dist/shared/turn-state.js +24 -0
- package/dist/shared/turn-state.js.map +1 -0
- package/dist/shared/world.js +37 -3
- package/dist/shared/world.js.map +1 -1
- package/dist/svr/index.js +2 -1
- package/dist/svr/index.js.map +1 -1
- package/hooks/pre-tool-use.sh +16 -3
- package/package.json +1 -1
- package/web/chat.css +172 -40
- package/web/chat.js +725 -247
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// 出 该 wizard 这一轮的回复: 复用 renderTurnGroup (文本 + 工具细节), 去掉问句 ——
|
|
6
6
|
// 问句已经是上面那条入消息了; 本轮用量单独走 `meta`, 客户端写在名字那一行
|
|
7
7
|
// 断点 视角 role 自己的 /clear /new
|
|
8
|
-
import { renderCutMark, renderTurnGroup, splitReminders, escHtml, hashStr, tagSig } from "./detail-render.js";
|
|
8
|
+
import { renderCutMark, renderTurnGroup, splitReminders, escHtml, fmtTs, hashStr, tagSig, turnUsageChips } from "./detail-render.js";
|
|
9
9
|
import { isTurn, staleAt, turnDone } from "./chat-view.js";
|
|
10
10
|
import { isKeepaliveTurn } from "./keepalive.js";
|
|
11
|
-
import { teammateOf, unwrapMates } from "./role-view.js";
|
|
11
|
+
import { channelOf, injectTurnsOf, teammateOf, unwrapMates } from "./role-view.js";
|
|
12
12
|
// 入消息顶上的一行归因: 这句话不是人打的字时说清是谁派的。
|
|
13
13
|
// 没有「公开」标记 —— 公开与否是**会话窗口**这一层的事, 不是消息这一层的:
|
|
14
14
|
// send_peer 的 channel 就是 `isPublic ? channelOf(self) : ""` (daemon/index.ts),
|
|
@@ -17,35 +17,172 @@ import { teammateOf, unwrapMates } from "./role-view.js";
|
|
|
17
17
|
// 时刻不在这里 —— 它写在气泡外的 .mwho 上 (每条消息只报一次时间), 所以
|
|
18
18
|
// 没有归因要说时整行省掉, 免得留下一条空的内边距。
|
|
19
19
|
const mateChip = (mate) => mate === undefined ? "" : `<span class="mchip sys">agent team${mate ? ` · ${escHtml(mate)}` : ""}</span>`;
|
|
20
|
+
/** 工单 badge: 只写工单号, 标题与进度由客户端按此刻的账填 (账会变, 片段的 sig 不该跟着变)。 */
|
|
21
|
+
const jobChip = (r) => r.from?.kind === "peer" && r.from.job ? `<button class="mchip job" data-job="${escHtml(r.from.job)}"></button>` : "";
|
|
20
22
|
const inMeta = (r) => {
|
|
21
23
|
const bits = [
|
|
22
24
|
r.origin ? `<span class="mchip graph" title="graph ${escHtml(r.origin.runId)}">🕸 轮 ${r.origin.round}/${r.origin.rounds} · 步 ${r.origin.step}/${r.origin.steps}</span>` : "",
|
|
23
25
|
r.from?.kind === "task" ? `<span class="mchip task">⏰ 定时 ${escHtml(r.from.taskId ?? "")}</span>` : "",
|
|
24
26
|
mateChip(teammateOf(r.userQuery)),
|
|
25
|
-
|
|
27
|
+
jobChip(r),
|
|
26
28
|
].filter(Boolean);
|
|
27
29
|
return bits.length ? `<div class="mmeta">${bits.join("")}</div>` : "";
|
|
28
30
|
};
|
|
31
|
+
// 回执的定论 —— 标在交回结论的那一方 (答话方) 那一轮的出消息上: 那条消息才是回执;
|
|
32
|
+
// 收回执那一方的回执轮只是读到了它, 不再挂。
|
|
33
|
+
const RCPT = {
|
|
34
|
+
done: "已交", need: "反问", error: "报错", timeout: "超时", silent: "没答", dead: "失联", canceled: "撤回",
|
|
35
|
+
};
|
|
36
|
+
const turnsWhere = (records, pick) => records.filter(isTurn).filter(pick).sort((a, b) => a.createdAt - b.createdAt);
|
|
37
|
+
/** 派活的那些句: 开一轮的问话, 加上插进别人那一轮的插话 (各自一个轮次外形, 见 injectTurnsOf)。 */
|
|
38
|
+
const asksWhere = (records, pick) => records.filter(isTurn).flatMap((t) => [t, ...injectTurnsOf(t)]).filter(pick).sort((a, b) => a.createdAt - b.createdAt);
|
|
39
|
+
/** 一件活的派活那句 = 收信方接手的那一轮的入消息 (续问共用活号, 取最早那一轮)。 */
|
|
40
|
+
const dispatchOf = (records, turn) => asksWhere(records, (t) => t.from?.kind === "peer" && !t.from.receipt && t.from.turn === turn && !!t.userQuery?.trim())[0];
|
|
41
|
+
/** 跳到派活那句要的坐标。派活那句常常不在点击处这个会话窗口里 (私聊派的活, 回执落在群里),
|
|
42
|
+
* 所以带上它自己的消息 id / 时刻 / 频道 / 两端 —— 会话键随视角而变, 由客户端算 (片段与视角无关)。 */
|
|
43
|
+
const gotoAttrs = (turn, d) => ` data-goto="${escHtml(turn)}" data-gid="${escHtml(d.id)}:in" data-gts="${d.createdAt}" data-gch="${escHtml(channelOf(d))}"` +
|
|
44
|
+
` data-gfrom="${escHtml(d.from?.from ?? "")}" data-gto="${escHtml(d.target ?? "")}"`;
|
|
45
|
+
const squash = (s) => s.replace(/\s+/g, " ").trim();
|
|
46
|
+
/** 一轮的终句 (说不清 final 的后端退回最后一段话)。 */
|
|
47
|
+
const finalOf = (t) => {
|
|
48
|
+
const texts = t.items.filter((it) => it.t === "text");
|
|
49
|
+
return (texts.filter((it) => it.final).at(-1) ?? texts.at(-1))?.body ?? "";
|
|
50
|
+
};
|
|
51
|
+
/** 一份回执是答话方哪一轮交回来的。回执轮的那句话就是答话方那一轮的终句原样贴进来 ——
|
|
52
|
+
* 按正文认: 链式续回时终句在它后来的回执轮里, 活号对不上。认不出 (超时 / 失联这类合成的
|
|
53
|
+
* 定论没有正文) 就退回它接手这件活的最后一轮。 */
|
|
54
|
+
const answerOf = (rc, records) => {
|
|
55
|
+
const f = rc.from;
|
|
56
|
+
if (f?.kind !== "peer" || !f.receipt || !f.from)
|
|
57
|
+
return undefined;
|
|
58
|
+
const d = f.turn ? dispatchOf(records, f.turn) : undefined;
|
|
59
|
+
const mine = turnsWhere(records, (t) => t.target === f.from && !t.agent && t.createdAt <= rc.createdAt && t.createdAt >= (d?.createdAt ?? 0));
|
|
60
|
+
const said = squash(rc.userQuery ?? "");
|
|
61
|
+
const byText = mine.filter((t) => ((p) => p.length >= 4 && said.includes(p))(squash(finalOf(t)).slice(0, 60))).at(-1);
|
|
62
|
+
return byText ?? mine.filter((t) => t.from?.kind === "peer" && !t.from.receipt && t.from.turn === f.turn).at(-1);
|
|
63
|
+
};
|
|
64
|
+
/** 答话方的轮次 id → 交回它的那份回执 (同一轮被认领多次取最新)。按 records 快照记一次: 每条出消息都要查。 */
|
|
65
|
+
const RECEIPTS = new WeakMap();
|
|
66
|
+
const receiptsByAnswer = (records) => {
|
|
67
|
+
const hit = RECEIPTS.get(records);
|
|
68
|
+
if (hit)
|
|
69
|
+
return hit;
|
|
70
|
+
const m = turnsWhere(records, (t) => t.from?.kind === "peer" && !!t.from.receipt)
|
|
71
|
+
.reduce((acc, rc) => ((a) => (a ? acc.set(a.id, rc) : acc))(answerOf(rc, records)), new Map());
|
|
72
|
+
RECEIPTS.set(records, m);
|
|
73
|
+
return m;
|
|
74
|
+
};
|
|
75
|
+
/** 回执 chip: 挂在答话方交回结论的那一轮上 —— 定论 + 工单里第几份; 找得到派活那句才可点,
|
|
76
|
+
* 点它跳过去 (跨会话也行)。 */
|
|
77
|
+
const receiptChip = (r, records, dir) => {
|
|
78
|
+
const rc = receiptsByAnswer(records).get(r.id), f = rc?.from;
|
|
79
|
+
if (!rc || f?.kind !== "peer")
|
|
80
|
+
return "";
|
|
81
|
+
const st = f.status ?? "done";
|
|
82
|
+
const seq = f.job && f.total ? ` · ${escHtml(f.job)} 第 ${f.done ?? 0}/${f.total} 份` : "";
|
|
83
|
+
const d = f.turn ? dispatchOf(records, f.turn) : undefined;
|
|
84
|
+
const what = `.${dir.nameOf(r.target ?? "")} 把 ${f.turn ? `${f.turn} ` : ""}的结论交回给 .${dir.nameOf(rc.target ?? "")}`;
|
|
85
|
+
const [tag, attrs] = f.turn && d
|
|
86
|
+
? ["button", `${gotoAttrs(f.turn, d)} title="${escHtml(`${what} · 点击跳到派活那句`)}"`]
|
|
87
|
+
: ["span", ` title="${escHtml(`${what} · 派活那句已不在记录里`)}"`];
|
|
88
|
+
return `<${tag} class="mchip rcpt st-${escHtml(st)}"${attrs}>↩ 回执 · ${RCPT[st] ?? escHtml(st)}${seq}</${tag}>`;
|
|
89
|
+
};
|
|
90
|
+
/** 这一行移交落成的那句。同一个活号续问多次就有多句 (每次 tell_peer 一句): 按原文认 ——
|
|
91
|
+
* 落地的那句就是原文贴进去的 (外面裹着信封), 取首段比对; 认不出 (没原文) 退回最早那句。 */
|
|
92
|
+
const dispatchFor = (records, h) => {
|
|
93
|
+
const all = asksWhere(records, (t) => t.from?.kind === "peer" && !t.from.receipt && t.from.turn === h.turn && !!t.userQuery?.trim());
|
|
94
|
+
const head = squash(h.text).slice(0, 80);
|
|
95
|
+
return (head.length >= 4 ? all.find((t) => squash(t.userQuery ?? "").includes(head)) : undefined) ?? all[0];
|
|
96
|
+
};
|
|
97
|
+
/** 整行点了要开的那段往来: 移交双方 (发话方 = 这一轮的 role, 收信方) 在哪个频道 —— 派活那句在就以它为准
|
|
98
|
+
* 并带上落点; 还没接手就按这次调用公开与否推 (公开的落在发话方此刻的群)。会话键随视角, 由客户端算。 */
|
|
99
|
+
const pairAttrs = (r, to, h, d) => ` data-hfrom="${escHtml(r.target ?? "")}" data-hto="${escHtml(to)}" data-hch="${escHtml(d ? channelOf(d) : h.public ? channelOf(r) : "")}"` +
|
|
100
|
+
(d ? ` data-gid="${escHtml(d.id)}:in" data-gts="${d.createdAt}"` : "");
|
|
101
|
+
const FAIL = new Set(["timeout", "silent", "dead", "canceled"]);
|
|
102
|
+
/** 移交行的 rolepage 那一半: 对方是名录里的谁 (头像 + 名字), 整行点了开双方的那段往来并落到这一句,
|
|
103
|
+
* 这件活此刻到了哪一步 —— 这一句之后的第一份回执就是它的定论 (need 之后续问再交, 那是下一行的),
|
|
104
|
+
* 没落定就看它接手了没有。每次请求现算, 不进轮次缓存: 回执比派活那一轮晚到, 气泡的 sig 得跟着它变。 */
|
|
105
|
+
const handoffDeco = (r, records, dir, now) => {
|
|
106
|
+
const landed = (h) => (h.turn ? dispatchFor(records, { ...h, turn: h.turn }) : undefined);
|
|
107
|
+
return {
|
|
108
|
+
who: (h) => {
|
|
109
|
+
const id = dir.resolve(h.name);
|
|
110
|
+
return id
|
|
111
|
+
? `<span class="ho-who"><span class="av">${escHtml(dir.labelOf(id))}</span><span class="nm wizard">${escHtml(dir.nameOf(id))}</span></span>`
|
|
112
|
+
: `<span class="ho-nm">.${escHtml(h.name)}</span>`;
|
|
113
|
+
},
|
|
114
|
+
attrs: (h) => {
|
|
115
|
+
const id = dir.resolve(h.name);
|
|
116
|
+
return id ? `${pairAttrs(r, id, h, landed(h))} title="${escHtml(`打开 .${dir.nameOf(r.target ?? "")} 与 .${dir.nameOf(id)} 的往来, 定位到这一句`)}"` : "";
|
|
117
|
+
},
|
|
118
|
+
status: (h) => {
|
|
119
|
+
if (!h.turn || !h.receipt)
|
|
120
|
+
return { key: "plain", tip: h.kind === "fyi" ? "只是知会, 不等回执" : "不等回执" };
|
|
121
|
+
const d = landed(h);
|
|
122
|
+
const rc = turnsWhere(records, (t) => t.from?.kind === "peer" && !!t.from.receipt && t.from.turn === h.turn && t.createdAt >= (d?.createdAt ?? 0))[0];
|
|
123
|
+
if (!rc)
|
|
124
|
+
return { key: "run", tip: d ? "在办 · 等回执" : "等它接手" };
|
|
125
|
+
const st = rc.from?.status ?? "done";
|
|
126
|
+
const key = st === "done" || st === "need" || st === "error" ? st : FAIL.has(st) ? "fail" : "run";
|
|
127
|
+
return { key, tip: `回执 · ${RCPT[st] ?? st}` };
|
|
128
|
+
},
|
|
129
|
+
acct: (h) => ((d) => (d ? turnUsageChips(d, now) : ""))(((d) => d && hostOf(d, records))(landed(h))),
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
/** 派活 / 续问那句带上它的活号, 回执凭它定位回来。 */
|
|
133
|
+
const pturnAttr = (r) => r.from?.kind === "peer" && r.from.turn && !r.from.receipt ? ` data-pturn="${escHtml(r.from.turn)}"` : "";
|
|
29
134
|
const renderIn = (r) => {
|
|
30
135
|
const q = splitReminders(unwrapMates(r.userQuery ?? ""));
|
|
31
|
-
return tagSig(`<section class="bubble mq" data-key="${escHtml(r.id)}:in">${inMeta(r)}` +
|
|
136
|
+
return tagSig(`<section class="bubble mq" data-key="${escHtml(r.id)}:in"${pturnAttr(r)}>${inMeta(r)}` +
|
|
32
137
|
`<div class="md-body"></div><script type="text/plain" class="md-src">${escHtml(q.body)}</script>${q.html}</section>`);
|
|
33
138
|
};
|
|
34
139
|
/** 子 agent 的轮次内联进父轮 —— 与旧线程视图同一规则 (见 chat-view.threadEntries)。 */
|
|
35
|
-
const childrenOf = (records, turnId, now) => records.filter(isTurn)
|
|
140
|
+
const childrenOf = (records, turnId, now, deco) => records.filter(isTurn)
|
|
36
141
|
.filter((r) => r.agent?.parentTurnId === turnId)
|
|
37
142
|
.sort((a, b) => a.createdAt - b.createdAt)
|
|
38
|
-
.map((c) => renderTurnGroup(c, now));
|
|
143
|
+
.map((c) => renderTurnGroup(c, now, [], true, deco));
|
|
39
144
|
const base = (m, dir) => ({
|
|
40
145
|
from: m.from, to: m.to, channel: m.channel,
|
|
41
146
|
fromName: dir.nameOf(m.from), fromLabel: dir.labelOf(m.from),
|
|
42
147
|
toName: dir.nameOf(m.to), toLabel: dir.labelOf(m.to),
|
|
43
148
|
});
|
|
149
|
+
/** `who` 在 `ts` 这一刻正跑着的主会话轮次 —— 它这一刻说出去的话 (派活、插话、notify) 都记在这一轮的账上。 */
|
|
150
|
+
const turnAt = (records, who, ts) => records.filter(isTurn)
|
|
151
|
+
.filter((t) => t.target === who && !t.agent && t.createdAt <= ts)
|
|
152
|
+
.reduce((best, t) => (!best || t.createdAt > best.createdAt ? t : best), undefined);
|
|
153
|
+
/** 插话的轮次外形 (id `<轮>~<序号>`) 没有自己的账: 它落进的那一轮才是。 */
|
|
154
|
+
const hostOf = (t, records) => t.id.includes("~") ? records.filter(isTurn).find((x) => x.id === t.id.slice(0, t.id.indexOf("~"))) : t;
|
|
155
|
+
/** 同伴派来的那句是发话方某一轮里说出去的: 它的账 (模型 / ctx / 耗时) 就是这条入消息的描述 ——
|
|
156
|
+
* 与答话那一侧同一份 chips, 两方会话里同样只藏模型 / ctx。人说的、定时任务放的没有自己的账。 */
|
|
157
|
+
const senderTurn = (r, records) => r.from?.kind === "peer" && r.from.from ? turnAt(records, r.from.from, r.createdAt) : undefined;
|
|
158
|
+
/** notify 贴的那段话装在一个零时长的轮次外形里 (见 messageOfPost): 账记在发话 wizard 贴它时跑着的那一轮。 */
|
|
159
|
+
const POSTS = new WeakMap();
|
|
160
|
+
const isPostTurn = (r, records) => (POSTS.get(records) ?? ((s) => (POSTS.set(records, s), s))(new Set(records.filter((x) => x.kind === "post").map((x) => x.id)))).has(r.id);
|
|
161
|
+
/** 片段里读了 `r` 这一轮的账或结论的**别的**记录 —— `r` 变了它们得跟着重推:
|
|
162
|
+
* 记在 `r` 账上的入消息 / notify, `r` 接手 (或回执) 的那件活所在的移交行, 回执认领的答话轮。 */
|
|
163
|
+
export const dependentsOf = (r, records) => {
|
|
164
|
+
// 先按「r 之后、r 的主人说的」收窄, 再逐条认账 —— 每个脏轮都要算一遍, 不能对全表两两比。
|
|
165
|
+
const mine = (at) => at >= r.createdAt && turnAt(records, r.target ?? "", at)?.id === r.id;
|
|
166
|
+
const said = records.filter((x) => x.kind === "post" ? x.target === r.target && mine(x.createdAt)
|
|
167
|
+
: isTurn(x) && (x.from?.kind === "peer" && x.from.from === r.target && mine(x.createdAt) ||
|
|
168
|
+
// 插话那句的入消息渲染在它落进的那一轮里, 重推的是那一轮
|
|
169
|
+
(x.injects ?? []).some((i) => i.from?.kind === "peer" && i.from.from === r.target && mine(i.ts))));
|
|
170
|
+
const f = r.from?.kind === "peer" ? r.from : undefined;
|
|
171
|
+
const owner = f?.turn ? (f.receipt ? r.target : f.from) : undefined;
|
|
172
|
+
const handed = owner ? records.filter(isTurn).filter((t) => t.target === owner && t.items.some((it) => it.t === "tool_result" && it.body.includes(f.turn))) : [];
|
|
173
|
+
const answered = f?.receipt ? [answerOf(r, records)].filter((t) => !!t) : [];
|
|
174
|
+
return [...new Set([...said, ...handed, ...answered].map((x) => x.id))].filter((id) => id !== r.id);
|
|
175
|
+
};
|
|
44
176
|
export const renderMsg = (m, records, dir, now) => {
|
|
45
177
|
const r = m.turn;
|
|
178
|
+
const acct = (t) => (t ? turnUsageChips(t, now) : "");
|
|
179
|
+
const out = (g) => ({
|
|
180
|
+
html: g.html,
|
|
181
|
+
meta: receiptChip(r, records, dir) + jobChip(r) + (isPostTurn(r, records) ? acct(turnAt(records, r.target ?? "", r.createdAt)) : g.meta),
|
|
182
|
+
});
|
|
46
183
|
const { html, meta } = m.dir === "in"
|
|
47
|
-
? { html: renderIn(r), meta:
|
|
48
|
-
: renderTurnGroup(r, now, childrenOf(records, r.id, now), false);
|
|
184
|
+
? { html: renderIn(r), meta: acct(senderTurn(r, records)) }
|
|
185
|
+
: out(((deco) => renderTurnGroup(r, now, childrenOf(records, r.id, now, deco), false, deco))(handoffDeco(r, records, dir, now)));
|
|
49
186
|
const live = m.dir === "out" && !turnDone(r, now);
|
|
50
187
|
return {
|
|
51
188
|
id: m.id, turnId: r.id, dir: m.dir, ...base(m, dir), ts: m.ts,
|
|
@@ -61,4 +198,31 @@ export const renderMark = (mk, role, dir) => {
|
|
|
61
198
|
ts: mk.createdAt, ping: false, html: f.html, meta: "", sig: f.sig, staleAt: 0,
|
|
62
199
|
};
|
|
63
200
|
};
|
|
201
|
+
// 工单窗口的开工 / 收工两行: 不是哪一轮的消息, 是账本上的两个时刻。收工那行按成员列定论与交付物,
|
|
202
|
+
// 工单不再进群, 这两行就是收工结论唯一的落点; 还开着时开工那行列成员此刻的进度。
|
|
203
|
+
const OUTCOME = { ...RCPT, "": "在干" };
|
|
204
|
+
// 老 daemon 的快照不记成员定论: 收了工却没有定论的, 不说它「在干」。
|
|
205
|
+
const memberLine = (mm, dir, closed) => `<li><span class="jo st-${escHtml(mm.outcome ?? (closed ? "none" : "run"))}">${escHtml(mm.outcome ? OUTCOME[mm.outcome] ?? mm.outcome : closed ? "—" : OUTCOME[""])}</span>` +
|
|
206
|
+
(dir.isWizard(mm.target)
|
|
207
|
+
? `<button class="go" data-r="${escHtml(mm.target)}">${escHtml(dir.labelOf(mm.target))} .${escHtml(dir.nameOf(mm.target))}</button>`
|
|
208
|
+
: `<span>${escHtml(dir.labelOf(mm.target))} ${escHtml(dir.nameOf(mm.target))}</span>`) +
|
|
209
|
+
(mm.task ? `<em title="${escHtml(mm.task.trim())}">${escHtml(mm.task.trim())}</em>` : "") +
|
|
210
|
+
(mm.artifacts ?? []).map((a) => `<div class="ja">↳ ${escHtml(a.path)}${a.note ? ` — ${escHtml(a.note)}` : ""}</div>`).join("") + "</li>";
|
|
211
|
+
export const renderJobMarks = (j, dir) => {
|
|
212
|
+
const owner = { from: j.owner, to: j.owner, channel: j.base };
|
|
213
|
+
const members = j.members.length ? `<ul class="jmem">${j.members.map((mm) => memberLine(mm, dir, j.status === "closed")).join("")}</ul>` : "";
|
|
214
|
+
const row = (phase, ts, body) => {
|
|
215
|
+
const html = tagSig(`<div class="tg-job ${phase}" data-key="j:${escHtml(j.id)}:${phase}">${body}</div>`);
|
|
216
|
+
return { id: `j:${j.id}:${phase}`, turnId: j.id, dir: "mark", ...base(owner, dir), ts, ping: false, html, meta: "", sig: hashStr(html), staleAt: 0 };
|
|
217
|
+
};
|
|
218
|
+
// 计划 / 结论是人写的 markdown, 换行要留着: 交给客户端同一个 markdown 渲染 (breaks:true)。
|
|
219
|
+
const md = (cls, text) => text?.trim() ? `<div class="md-body ${cls}"></div><script type="text/plain" class="md-src">${escHtml(text.trim())}</script>` : "";
|
|
220
|
+
const head = (verb) => `<div class="jh">📋 <b>${escHtml(j.id)}</b> ${verb} · <span class="jt">${escHtml(j.title)}</span><span class="s">${escHtml(fmtTs(verb === "开工" ? j.openedAt : j.closedAt ?? 0))}</span></div>`;
|
|
221
|
+
return [
|
|
222
|
+
row("open", j.openedAt, head("开工") + md("jplan", j.plan) + (j.status === "open" ? members : "")),
|
|
223
|
+
...(j.status === "closed" && j.closedAt
|
|
224
|
+
? [row("close", j.closedAt, head("收工") + members + md("jsum", j.summary))]
|
|
225
|
+
: []),
|
|
226
|
+
];
|
|
227
|
+
};
|
|
64
228
|
//# sourceMappingURL=role-render.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-render.js","sourceRoot":"","sources":["../../shared/role-render.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"role-render.js","sourceRoot":"","sources":["../../shared/role-render.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,uCAAuC;AACvC,EAAE;AACF,6CAA6C;AAC7C,iEAAiE;AACjE,mDAAmD;AACnD,+BAA+B;AAC/B,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAqD,MAAM,oBAAoB,CAAC;AACxL,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAA4B,MAAM,gBAAgB,CAAC;AA0B7G,gCAAgC;AAChC,4CAA4C;AAC5C,8EAA8E;AAC9E,0DAA0D;AAC1D,uCAAuC;AACvC,6CAA6C;AAC7C,4BAA4B;AAC5B,MAAM,QAAQ,GAAG,CAAC,IAAwB,EAAU,EAAE,CACpD,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qCAAqC,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;AAE5G,6DAA6D;AAC7D,MAAM,OAAO,GAAG,CAAC,CAAmB,EAAU,EAAE,CAC9C,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,uCAAuC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;AAEvH,MAAM,MAAM,GAAG,CAAC,CAAmB,EAAU,EAAE;IAC7C,MAAM,IAAI,GAAG;QACX,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,0CAA0C,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE;QAC5K,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,iCAAiC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;QACrG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACjC,OAAO,CAAC,CAAC,CAAC;KACX,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,sBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;AACxE,CAAC,CAAC;AAEF,gDAAgD;AAChD,yBAAyB;AACzB,MAAM,IAAI,GAAqC;IAC7C,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI;CAC7F,CAAC;AACF,MAAM,UAAU,GAAG,CAAC,OAAgC,EAAE,IAAsC,EAAsB,EAAE,CAClH,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;AAEhF,gEAAgE;AAChE,MAAM,SAAS,GAAG,CAAC,OAAgC,EAAE,IAAsC,EAAsB,EAAE,CACjH,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;AAEzH,iDAAiD;AACjD,MAAM,UAAU,GAAG,CAAC,OAAgC,EAAE,IAAY,EAAgC,EAAE,CAClG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAE5H;iEACiE;AACjE,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,CAAmB,EAAU,EAAE,CAC9D,eAAe,OAAO,CAAC,IAAI,CAAC,eAAe,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,SAAS,eAAe,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG;IAC5H,gBAAgB,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,eAAe,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,CAAC;AAEvF,MAAM,MAAM,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,oCAAoC;AACpC,MAAM,OAAO,GAAG,CAAC,CAAmB,EAAU,EAAE;IAC9C,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAA2C,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;IAC/F,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;AAC7E,CAAC,CAAC;AAEF;;8BAE8B;AAC9B,MAAM,QAAQ,GAAG,CAAC,EAAoB,EAAE,OAAgC,EAAgC,EAAE;IACxG,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9I,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtH,OAAO,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACnH,CAAC,CAAC;AAEF,oEAAoE;AACpE,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA0D,CAAC;AACvF,MAAM,gBAAgB,GAAG,CAAC,OAAgC,EAAiC,EAAE;IAC3F,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;SAC9E,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,EAA4B,CAAC,CAAC;IAC3H,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACzB,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF;qBACqB;AACrB,MAAM,WAAW,GAAG,CAAC,CAAmB,EAAE,OAAgC,EAAE,GAAc,EAAU,EAAE;IACpG,MAAM,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC;IAC7D,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,KAAK,MAAM;QAAE,OAAO,EAAE,CAAC;IACzC,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;IAC9B,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;IACpH,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;QAC9B,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,OAAO,CAAC,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC;QAChF,CAAC,CAAC,CAAC,MAAM,EAAE,WAAW,OAAO,CAAC,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;IAC5D,OAAO,IAAI,GAAG,yBAAyB,OAAO,CAAC,EAAE,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,EAAE,CAAC,GAAG,GAAG,KAAK,GAAG,GAAG,CAAC;AACjH,CAAC,CAAC;AAEF;uDACuD;AACvD,MAAM,WAAW,GAAG,CAAC,OAAgC,EAAE,CAA6B,EAAgC,EAAE;IACpH,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrI,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;AAC9G,CAAC,CAAC;AAEF;2DAC2D;AAC3D,MAAM,SAAS,GAAG,CAAC,CAAmB,EAAE,EAAU,EAAE,CAAU,EAAE,CAA+B,EAAU,EAAE,CACzG,gBAAgB,OAAO,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,eAAe,OAAO,CAAC,EAAE,CAAC,eAAe,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG;IAC3I,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAEzE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAEhE;;6DAE6D;AAC7D,MAAM,WAAW,GAAG,CAAC,CAAmB,EAAE,OAAgC,EAAE,GAAc,EAAE,GAAW,EAAe,EAAE;IACtH,MAAM,MAAM,GAAG,CAAC,CAAU,EAAgC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACjI,OAAO;QACL,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,EAAE;gBACP,CAAC,CAAC,yCAAyC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,kCAAkC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,gBAAgB;gBAC5I,CAAC,CAAC,wBAAwB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;QACvD,CAAC;QACD,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE;YACX,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAChJ,CAAC;QACD,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAClG,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACpB,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtJ,IAAI,CAAC,EAAE;gBAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAC7D,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,IAAI,MAAM,CAAC;YACrC,MAAM,GAAG,GAAG,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YAClG,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC;QAChD,CAAC;QACD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;KACrG,CAAC;AACJ,CAAC,CAAC;AAEF,iCAAiC;AACjC,MAAM,SAAS,GAAG,CAAC,CAAmB,EAAU,EAAE,CAChD,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAE3G,MAAM,QAAQ,GAAG,CAAC,CAAmB,EAAU,EAAE;IAC/C,MAAM,CAAC,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC,wCAAwC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE;QACnG,uEAAuE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC;AAC1H,CAAC,CAAC;AAEF,kEAAkE;AAClE,MAAM,UAAU,GAAG,CAAC,OAAgC,EAAE,MAAc,EAAE,GAAW,EAAE,IAAiB,EAAkB,EAAE,CACtH,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;KACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC;KAC/C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;KACzC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEzD,MAAM,IAAI,GAAG,CAAC,CAAuC,EAAE,GAAc,EAAE,EAAE,CAAC,CAAC;IACzE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO;IAC1C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5D,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;CACrD,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,MAAM,GAAG,CAAC,OAAgC,EAAE,GAAW,EAAE,EAAU,EAAgC,EAAE,CACzG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;KACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;KAChE,MAAM,CAA+B,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;AAEtH,iDAAiD;AACjD,MAAM,MAAM,GAAG,CAAC,CAAmB,EAAE,OAAgC,EAAgC,EAAE,CACrG,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzG;2DAC2D;AAC3D,MAAM,UAAU,GAAG,CAAC,CAAmB,EAAE,OAAgC,EAAgC,EAAE,CACzG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAEjG,2EAA2E;AAC3E,MAAM,KAAK,GAAG,IAAI,OAAO,EAAwC,CAAC;AAClE,MAAM,UAAU,GAAG,CAAC,CAAmB,EAAE,OAAgC,EAAW,EAAE,CACpF,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAE5I;iEACiE;AACjE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAmB,EAAE,OAAgC,EAAY,EAAE;IAC9F,oDAAoD;IACpD,MAAM,IAAI,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;IAC5G,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAChC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5D,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;YACtF,+BAA+B;YAC/B,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACzG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,aAAa,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAE,CAAC,IAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnK,MAAM,QAAQ,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAyB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpG,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;AACtG,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAM,EAAE,OAAgC,EAAE,GAAc,EAAE,GAAW,EAAe,EAAE;IAC9G,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IACjB,MAAM,IAAI,GAAG,CAAC,CAA+B,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC;QAChC,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;KACzI,CAAC,CAAC;IACH,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,KAAK,IAAI;QACnC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE;QAC3D,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACnI,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE;QAC7D,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;QACxB,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;QACrC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC/B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EAAoB,EAAE,IAAY,EAAE,GAAc,EAAe,EAAE;IAC5F,MAAM,CAAC,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAC5B,OAAO;QACL,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC;QACjG,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC;KAC9E,CAAC;AACJ,CAAC,CAAC;AAEF,sDAAsD;AACtD,4CAA4C;AAC5C,MAAM,OAAO,GAAqC,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACxE,0CAA0C;AAC1C,MAAM,UAAU,GAAG,CAAC,EAAmC,EAAE,GAAc,EAAE,MAAe,EAAU,EAAE,CAClG,0BAA0B,OAAO,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAE,CAAC,SAAS;IAC7K,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC;QACtB,CAAC,CAAC,8BAA8B,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,WAAW;QACpI,CAAC,CAAC,SAAS,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC;IACxF,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;AAE3I,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAe,EAAE,GAAc,EAAiB,EAAE;IAC/E,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9D,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9I,MAAM,GAAG,GAAG,CAAC,KAAuB,EAAE,EAAU,EAAE,IAAY,EAAe,EAAE;QAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,sBAAsB,KAAK,iBAAiB,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,KAAK,KAAK,IAAI,QAAQ,CAAC,CAAC;QACzG,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,EAAE,IAAI,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACvJ,CAAC,CAAC;IACF,mEAAmE;IACnE,MAAM,EAAE,GAAG,CAAC,GAAW,EAAE,IAAa,EAAE,EAAE,CACxC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,uBAAuB,GAAG,oDAAoD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACpI,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAC5B,yBAAyB,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,IAAI,uBAAuB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,0BAA0B,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;IACjM,OAAO;QACL,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChG,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ;YACrC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1E,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/shared/role-view.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// 一轮 turn 在这里拆成两条消息:
|
|
8
8
|
// 入 发话方 → 该 wizard userQuery
|
|
9
9
|
// 出 该 wizard → 发话方 本轮的回复 (文本 + 工具细节)
|
|
10
|
-
// 发话方: 同伴 (from.kind=peer) / 定时任务 (task) / 人 (speaker;
|
|
11
|
-
//
|
|
10
|
+
// 发话方: 同伴 (from.kind=peer) / 定时任务 (task) / 人 (speaker; 认不出就是
|
|
11
|
+
// 「未知」)。消息落在哪个会话由 channel 决定 —— 公开频道 (群 / 与人的单聊) 按 base
|
|
12
12
|
// 归并, 私聊按对端 wizard 归并。
|
|
13
13
|
//
|
|
14
14
|
// role id: wizard 就是它的 target key; 人是 `human:<userid>` (`human:` = 不知道是谁);
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { baseOfKey, labelFor, stripSigil, tagOfKey } from "./session-label.js";
|
|
20
20
|
import { isGhostTurn, isMark, isPost, isTurn, staleAt, summarizeTag } from "./chat-view.js";
|
|
21
21
|
import { isKeepaliveTurn } from "./keepalive.js";
|
|
22
|
+
import { jobProgress } from "./world.js";
|
|
22
23
|
// ── 频道与发话方 ─────────────────────────────────────────────────────
|
|
23
24
|
/** 见 TurnDetailRecord.channel: 老记录按 from 推 —— peer 私聊, 其余在 home。 */
|
|
24
25
|
export const channelOf = (r) => r.channel !== undefined
|
|
@@ -61,8 +62,10 @@ export const senderOf = (r) => {
|
|
|
61
62
|
const convTurns = (records, now) => records.filter(isTurn).filter((r) => !!r.target && !r.agent && !isGhostTurn(r, now));
|
|
62
63
|
/** 保温 ping 的那一条 —— 进时间轴, 但不进会话列表的条数/预览, 也不算一条关系。 */
|
|
63
64
|
export const isPing = (m) => isKeepaliveTurn(m.turn);
|
|
64
|
-
/** 一个公开频道里听话的那一方:
|
|
65
|
-
|
|
65
|
+
/** 一个公开频道里听话的那一方: 与人的单聊是那个人; 群里是这条链的链头 (守护进程写 turn /
|
|
66
|
+
* post 时顺着派活链记下的, 见 Asker) —— 只在他正是在这个群里开的口时才算; 认不出就是
|
|
67
|
+
* `human:` (未知), 不拿群里别的开口的人去猜: 一个 wizard 可能同时在答好几个人。 */
|
|
68
|
+
export const audienceOf = (channel, asker) => channel.startsWith("user:") ? humanOf(channel) : asker?.chat === channel ? humanOf(asker.who) : "human:";
|
|
66
69
|
/** 回执轮: 守护进程把同伴那一轮的终句原样转进发话方的会话。 */
|
|
67
70
|
const isReceipt = (r) => r.from?.kind === "peer" && r.from.receipt === true;
|
|
68
71
|
/** 一轮 → 入/出两条消息。没问话的 (/clear 之后的续跑) 只有出; 还没产出也没在跑
|
|
@@ -70,12 +73,19 @@ const isReceipt = (r) => r.from?.kind === "peer" && r.from.receipt === true;
|
|
|
70
73
|
* 回执轮例外: 入那一句就是同伴自己那一轮的出 (同一段话, 已在同伴的出消息里), 再画
|
|
71
74
|
* 一遍就是重复, 所以没有入; 它的出是在频道里对那里的人说的 —— 公开频道答给人, 只有
|
|
72
75
|
* 私聊回执 ("" 频道, 回复不进任何群) 才仍是对那个同伴。 */
|
|
76
|
+
/** 一轮里的插话 → 各自一个只有问话的轮次外形 (id `<轮>~<序号>`): 出处、频道、发话人都是它自己的,
|
|
77
|
+
* 不随被插的那一轮。它没有自己的回复 —— 回复在被插的那一轮的出消息里。 */
|
|
78
|
+
export const injectTurnsOf = (r) => (r.injects ?? []).map((x, i) => ({
|
|
79
|
+
kind: "turn", id: `${r.id}~${i}`, createdAt: x.ts, updatedAt: x.ts, closed: true, items: [],
|
|
80
|
+
target: r.target, sessionId: r.sessionId, userQuery: x.body, channel: x.channel,
|
|
81
|
+
...(x.from ? { from: x.from } : {}), ...(x.speaker ? { speaker: x.speaker } : {}),
|
|
82
|
+
}));
|
|
73
83
|
export const messagesOfTurn = (r) => {
|
|
74
84
|
const w = r.target;
|
|
75
85
|
const who = senderOf(r);
|
|
76
86
|
const channel = channelOf(r);
|
|
77
87
|
const receipt = isReceipt(r);
|
|
78
|
-
const to = receipt && channel ? audienceOf(channel) : who;
|
|
88
|
+
const to = receipt && channel ? audienceOf(channel, r.from?.asker) : who;
|
|
79
89
|
const firstOut = r.items.reduce((m, it) => Math.min(m, it.ts), Infinity);
|
|
80
90
|
const inMsg = r.userQuery?.trim() && !receipt
|
|
81
91
|
? [{ id: `${r.id}:in`, turn: r, dir: "in", from: who, to: w, channel, ts: r.createdAt }]
|
|
@@ -83,7 +93,8 @@ export const messagesOfTurn = (r) => {
|
|
|
83
93
|
const outMsg = r.items.length > 0 || !r.closed
|
|
84
94
|
? [{ id: `${r.id}:out`, turn: r, dir: "out", from: w, to, channel, ts: Math.max(r.createdAt + 1, Number.isFinite(firstOut) ? firstOut : r.createdAt + 1) }]
|
|
85
95
|
: [];
|
|
86
|
-
|
|
96
|
+
const injected = injectTurnsOf(r).map((t) => ({ id: `${t.id}:in`, turn: t, dir: "in", from: senderOf(t), to: w, channel: t.channel ?? "", ts: t.createdAt }));
|
|
97
|
+
return [...inMsg, ...outMsg, ...injected];
|
|
87
98
|
};
|
|
88
99
|
/** notify 贴进群的一段话 → 发话 wizard 在那个频道里的一条出消息, 对着那里的人说。
|
|
89
100
|
* 装进一个已收口、只有一段终句的轮次外形, 渲染与未读水位都照出消息的老规矩走。 */
|
|
@@ -95,7 +106,7 @@ export const messageOfPost = (p) => ({
|
|
|
95
106
|
},
|
|
96
107
|
dir: "out",
|
|
97
108
|
from: p.target,
|
|
98
|
-
to: audienceOf(p.channel),
|
|
109
|
+
to: audienceOf(p.channel, p.asker),
|
|
99
110
|
channel: p.channel,
|
|
100
111
|
ts: p.createdAt,
|
|
101
112
|
});
|
|
@@ -115,7 +126,7 @@ export const makeDirectory = (records, facts) => {
|
|
|
115
126
|
if (wizards.has(id))
|
|
116
127
|
return (facts_.get(id)?.name ?? "").trim() || tagOfKey(id) || chatName(baseOfKey(id)) || id;
|
|
117
128
|
if (id === "human:")
|
|
118
|
-
return "
|
|
129
|
+
return "未知";
|
|
119
130
|
return id.startsWith("human:") ? id.slice(6) : id;
|
|
120
131
|
};
|
|
121
132
|
const labelOf = (id) => id.startsWith("task:") ? "⏰" : id === SYSTEM ? "🧙" : wizards.has(id) ? labelFor(nameOf(id)) : "👤";
|
|
@@ -141,7 +152,7 @@ export const makeDirectory = (records, facts) => {
|
|
|
141
152
|
return undefined;
|
|
142
153
|
const f = facts_.get(id);
|
|
143
154
|
const t = untilOf(id);
|
|
144
|
-
return { alive: f?.alive ?? false, busy: f?.busy ?? false, runningUntil: t > now ? t : 0 };
|
|
155
|
+
return { alive: f?.alive ?? false, busy: f?.busy ?? false, runningUntil: t > now ? t : 0, ...(f?.waiting?.length ? { waiting: f.waiting } : {}) };
|
|
145
156
|
};
|
|
146
157
|
return { nameOf, labelOf, resolve, fact: (id) => facts_.get(id), chatName, isWizard: (id) => wizards.has(id), status, wizards: () => [...wizards] };
|
|
147
158
|
};
|
|
@@ -157,12 +168,13 @@ export const talkOf = (msgs, role, peers = [], chat) => {
|
|
|
157
168
|
const withPeer = (m) => isPing(m) ? m.turn.target === role || pick.has(m.turn.target ?? "") : involves(m, role) && pick.has(other(m, role));
|
|
158
169
|
return msgs.filter((m) => (chat === undefined || m.channel === chat) && (pick.size ? withPeer(m) : involves(m, role)));
|
|
159
170
|
};
|
|
160
|
-
/** `a:<x>` = x 参与的全部对话; `a:<x>|<p1>,<p2>` = x 与这几个对端之间的 —— 不分会话, 按时间排开。
|
|
171
|
+
/** `a:<x>` = x 参与的全部对话; `a:<x>|<p1>,<p2>` = x 与这几个对端之间的 —— 不分会话, 按时间排开。
|
|
172
|
+
* 再带一段 `|<base>` 就只在那个频道里 (`a:<x>||<base>` = x 在那个群里的全部记录, 对端不限)。 */
|
|
161
173
|
export const parseTalkKey = (key) => {
|
|
162
174
|
if (!key.startsWith("a:"))
|
|
163
175
|
return undefined;
|
|
164
|
-
const [who = "", rest] = key.slice(2).split("|");
|
|
165
|
-
return { who, peers: rest ? rest.split(",").filter(Boolean) : [] };
|
|
176
|
+
const [who = "", rest, chat] = key.slice(2).split("|");
|
|
177
|
+
return { who, peers: rest ? rest.split(",").filter(Boolean) : [], ...(chat ? { chat } : {}) };
|
|
166
178
|
};
|
|
167
179
|
const stripMd = (s) => s.replace(/[`*_~|#>]/g, "").replace(/\s+/g, " ").trim();
|
|
168
180
|
/** 一条消息里最后那句话与它说出的时刻: 入 = 问话 (轮次开始); 出 = 最后一段 text ——
|
|
@@ -205,17 +217,19 @@ const saidAt = (role) => (m) => {
|
|
|
205
217
|
const t = [...m.turn.items].reverse().find((it) => it.t === "text" && (it.final === true || (it.final === undefined && !!m.turn.closed)));
|
|
206
218
|
return t?.ts;
|
|
207
219
|
};
|
|
220
|
+
const heardBy = (role) => (ms) => ms.flatMap((m) => {
|
|
221
|
+
const ts = saidAt(role)(m);
|
|
222
|
+
return ts === undefined ? [] : [[ts, m.from, m.to]];
|
|
223
|
+
}).sort((a, b) => a[0] - b[0]).slice(-HEARD_MAX);
|
|
224
|
+
/** 开口之后才进来的话才可能没读过: 回过话 = 读到了那里。 */
|
|
225
|
+
const spokeBy = (role) => (ms) => ms.filter((m) => m.from === role).reduce((t, m) => Math.max(t, saidOf(m).ts), 0);
|
|
208
226
|
/** 一个 role 参与的全部会话, 最近活动在前。群聊只列它**自己开过口**的 —— 住在里面
|
|
209
227
|
* (home) 或只是被人叫过一声却没答话的, 都不算参与; 私聊则有往来就在列。 */
|
|
210
228
|
export const convsOf = (all_, role, dir, now) => {
|
|
211
229
|
// 会话列表讲的是"谁跟谁说过什么", ping 不是话: 条数与预览都不该被它顶掉。
|
|
212
230
|
const msgs = all_.filter((m) => !isPing(m));
|
|
213
|
-
const heard = (
|
|
214
|
-
|
|
215
|
-
return ts === undefined ? [] : [[ts, involves(m, role) ? other(m, role) : ""]];
|
|
216
|
-
}).sort((a, b) => a[0] - b[0]).slice(-HEARD_MAX);
|
|
217
|
-
// 开口之后才进来的话才可能没读过: 回过话 = 读到了那里。
|
|
218
|
-
const spoke = (ms) => ms.filter((m) => m.from === role).reduce((t, m) => Math.max(t, saidOf(m).ts), 0);
|
|
231
|
+
const heard = heardBy(role);
|
|
232
|
+
const spoke = spokeBy(role);
|
|
219
233
|
const mine = talkOf(msgs, role);
|
|
220
234
|
const keys = [...new Set(mine.filter((m) => !m.channel || m.from === role).map((m) => convKeyOf(m, role)))];
|
|
221
235
|
return keys
|
|
@@ -226,40 +240,90 @@ export const convsOf = (all_, role, dir, now) => {
|
|
|
226
240
|
return {
|
|
227
241
|
key, kind: "wizard", name: dir.nameOf(peer), label: dir.labelOf(peer), base: "", peer,
|
|
228
242
|
status: dir.status(peer, now),
|
|
229
|
-
...glanceOr(ms, speakerPrefix(dir, role, [peer])), count: ms.length, heard: heard(ms), mine: spoke(ms), subs: [],
|
|
243
|
+
...glanceOr(ms, speakerPrefix(dir, role, [peer])), count: ms.length, heard: heard(ms), mine: spoke(ms), subs: [], ...withJobs(ms),
|
|
230
244
|
};
|
|
231
245
|
}
|
|
232
246
|
const base = key.slice(2);
|
|
233
247
|
const all = msgs.filter((m) => m.channel === base);
|
|
234
248
|
const others = [...new Set(all.flatMap((m) => [m.from, m.to]))].filter((r) => r !== role && !r.startsWith("task:") && r !== SYSTEM);
|
|
249
|
+
// 子项成对: 群里与视角有往来的每个 role 一项, 内容是「我与它」在这个群里的对话 (与点进去的窗口同一份 talkOf)。
|
|
250
|
+
// 没往来的 count 为 0, 默认不列; 「chat 内全部」下用 whole (它在群里的全部记录) 列出来。
|
|
251
|
+
// 两份 glance 都走 glanceOfTalk —— 关系图卡片 (/api/glance) 的同一个实现。
|
|
235
252
|
const subs = others
|
|
236
253
|
.map((r) => {
|
|
237
254
|
const pair = talkOf(all, role, [r]);
|
|
238
|
-
const seen = talkOf(all, r);
|
|
239
255
|
return {
|
|
240
256
|
role: r, name: dir.nameOf(r), label: dir.labelOf(r), count: pair.length, mine: spoke(pair),
|
|
241
|
-
...(
|
|
257
|
+
...glanceOfTalk(all, role, dir, role, [r], base), status: dir.status(r, now),
|
|
258
|
+
whole: { count: talkOf(all, r).length, ...glanceOfTalk(all, role, dir, r, [], base) }, ...withJobs(pair),
|
|
242
259
|
};
|
|
243
260
|
})
|
|
244
|
-
//
|
|
245
|
-
.sort((a, b) => Number(b.count > 0) - Number(a.count > 0) || b.lastTs - a.lastTs)
|
|
261
|
+
// 成对的排前 (默认只列它们), 其余按它们自己的记录排 —— 截断时先丢与我无往来的。
|
|
262
|
+
.sort((a, b) => Number(b.count > 0) - Number(a.count > 0) || (a.count ? b.lastTs - a.lastTs : b.whole.lastTs - a.whole.lastTs))
|
|
246
263
|
.slice(0, SUB_MAX);
|
|
247
264
|
return {
|
|
248
265
|
key, kind: "group", base,
|
|
249
266
|
name: dir.chatName(base) || (base.startsWith("user:") ? dir.nameOf(humanOf(base)) : base.replace(/^chat:/, "").slice(0, 10)),
|
|
250
267
|
label: "💬",
|
|
251
|
-
...glanceOr(all, speakerPrefix(dir, role)), count: all.length, heard: heard(all), mine: spoke(all), subs,
|
|
268
|
+
...glanceOr(all, speakerPrefix(dir, role)), count: all.length, heard: heard(all), mine: spoke(all), subs, ...withJobs(all),
|
|
252
269
|
};
|
|
253
270
|
})
|
|
254
271
|
.sort((a, b) => b.lastTs - a.lastTs);
|
|
255
272
|
};
|
|
273
|
+
// ── 工单 ─────────────────────────────────────────────────────────────
|
|
274
|
+
// `j:<id>` 是一张工单的全部往来: 归在它名下的派活、各成员的答话、送回开单者的回执 —— 不分
|
|
275
|
+
// 谁对谁, 也不按视角的 session 段裁 (成员各有各的 session)。开工 / 收工两行不是消息, 由
|
|
276
|
+
// chat-http 按 facts 现画。
|
|
277
|
+
export const jobOfKey = (key) => (key.startsWith("j:") ? key.slice(2) : undefined);
|
|
278
|
+
export const jobMessages = (msgs, id) => msgs.filter((m) => m.turn.from?.job === id);
|
|
279
|
+
/** 一组消息里出现过的工单号, 去重保序。 */
|
|
280
|
+
export const jobsIn = (msgs) => [...new Set(msgs.map((m) => m.turn.from?.job ?? "").filter(Boolean))];
|
|
281
|
+
const withJobs = (msgs) => ((js) => (js.length ? { jobs: js } : {}))(jobsIn(msgs));
|
|
282
|
+
/** 视角开的、或在里面的工单 —— 主区工单列表的一行, 数据与群 / 私聊同一套 (glance)。不进侧栏。 */
|
|
283
|
+
export const jobConvsOf = (all_, role, dir, jobs) => {
|
|
284
|
+
const msgs = all_.filter((m) => !isPing(m));
|
|
285
|
+
return jobs
|
|
286
|
+
.filter((j) => j.owner === role || j.members.some((mm) => mm.target === role))
|
|
287
|
+
.map((j) => {
|
|
288
|
+
const ms = jobMessages(msgs, j.id);
|
|
289
|
+
const g = glanceOr(ms, speakerPrefix(dir, role));
|
|
290
|
+
return {
|
|
291
|
+
key: `j:${j.id}`, kind: "job", name: j.title, label: "📋", base: j.base,
|
|
292
|
+
preview: g.preview, lastTs: Math.max(g.lastTs, j.closedAt ?? j.openedAt),
|
|
293
|
+
// 工单里的每句话同时也在某个群 / 私聊里, 未读只记在那一处 —— 两边各记一份, 读了一边另一边还亮着。
|
|
294
|
+
count: ms.length, heard: [], mine: 0, subs: [],
|
|
295
|
+
job: { id: j.id, owner: j.owner, status: j.status, ...jobProgress(j) },
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
/** 每个 role 对应的全部叶子项 —— 群里的成对子项 (它与某个 role 在那个群里有往来, 与 convsOf 的子项
|
|
300
|
+
* 同一口径) 与私聊项 (有往来就算)。侧栏名字旁的「+N」= 这份减去那一项自己。
|
|
301
|
+
* key 取绝对的 (`c:<base>|<a>|<b>` / `p:<a>|<b>`, 两端排序), 与视角无关。定时与系统不是聊天的一方。 */
|
|
302
|
+
export const chatKeysOf = (all_) => {
|
|
303
|
+
const party = (r) => !!r && !r.startsWith("task:") && r !== SYSTEM;
|
|
304
|
+
const index = all_.filter((m) => !isPing(m)).reduce((idx, m) => {
|
|
305
|
+
const add = (r, k) => idx.set(r, (idx.get(r) ?? new Set()).add(k));
|
|
306
|
+
if (party(m.from) && party(m.to)) {
|
|
307
|
+
const pair = [m.from, m.to].sort().join("|");
|
|
308
|
+
const k = m.channel ? `c:${m.channel}|${pair}` : `p:${pair}`;
|
|
309
|
+
add(m.from, k);
|
|
310
|
+
add(m.to, k);
|
|
311
|
+
}
|
|
312
|
+
return idx;
|
|
313
|
+
}, new Map());
|
|
314
|
+
return Object.fromEntries([...index].map(([r, ks]) => [r, [...ks]]));
|
|
315
|
+
};
|
|
256
316
|
/** 一个会话窗口里的消息。`withRole` 只对公开频道有意义: 当前 role 与它在这个频道里的往来。
|
|
257
317
|
* 整个公开频道 (不带 withRole) 是频道里所有人说的话, 不是某个 role 的往来 —— 唯一不走 talkOf 的。 */
|
|
258
318
|
export const convMessages = (msgs, role, key, withRole) => {
|
|
319
|
+
const job = jobOfKey(key);
|
|
320
|
+
if (job)
|
|
321
|
+
return jobMessages(msgs, job);
|
|
259
322
|
const t = talkArgs(role, key, withRole);
|
|
260
323
|
return t ? talkOf(msgs, t.who, t.peers, t.chat) : msgs.filter((m) => m.channel === key.slice(2));
|
|
261
324
|
};
|
|
262
|
-
/** 一个窗口交给 talkOf 的入参; 整个公开频道不是谁的往来,
|
|
325
|
+
/** 一个窗口交给 talkOf 的入参; 整个公开频道不是谁的往来, 没有。群里选中一个子项 (withRole)
|
|
326
|
+
* 看的是视角与它在这个群里的往来。 */
|
|
263
327
|
export const talkArgs = (role, key, withRole) => {
|
|
264
328
|
const t = parseTalkKey(key);
|
|
265
329
|
if (t)
|