wezard 2.0.0 → 2.0.2
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CLAUDE.md +7 -8
- package/README.md +20 -32
- package/cli/wezard.sh +0 -62
- package/config.example.jsonc +3 -9
- package/dist/cli/init.js +12 -21
- package/dist/cli/init.js.map +1 -1
- package/dist/daemon/approval.js +2 -4
- package/dist/daemon/approval.js.map +1 -1
- package/dist/daemon/cc-session.js +136 -0
- package/dist/daemon/cc-session.js.map +1 -0
- package/dist/daemon/detail.js +2 -3
- package/dist/daemon/detail.js.map +1 -1
- package/dist/daemon/inbound.js +90 -110
- package/dist/daemon/inbound.js.map +1 -1
- package/dist/daemon/index.js +130 -111
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/memory-steward.js +82 -0
- package/dist/daemon/memory-steward.js.map +1 -0
- package/dist/daemon/mirror-bridge.js +201 -62
- package/dist/daemon/mirror-bridge.js.map +1 -1
- package/dist/daemon/session-name.js +4 -27
- package/dist/daemon/session-name.js.map +1 -1
- package/dist/daemon/spawn-tmux.js +35 -13
- package/dist/daemon/spawn-tmux.js.map +1 -1
- package/dist/daemon/wizard-memory.js +21 -26
- package/dist/daemon/wizard-memory.js.map +1 -1
- package/dist/daemon/wizard.js +54 -11
- package/dist/daemon/wizard.js.map +1 -1
- package/dist/mcp/server.js +63 -153
- package/dist/mcp/server.js.map +1 -1
- package/dist/shared/chat-http.js +44 -16
- package/dist/shared/chat-http.js.map +1 -1
- package/dist/shared/chat-render.js +10 -8
- package/dist/shared/chat-render.js.map +1 -1
- package/dist/shared/cli-backends.js +2 -7
- package/dist/shared/cli-backends.js.map +1 -1
- package/dist/shared/config.js +10 -12
- package/dist/shared/config.js.map +1 -1
- package/dist/shared/detail-render.js +35 -16
- package/dist/shared/detail-render.js.map +1 -1
- package/dist/shared/detail-store.js +14 -3
- package/dist/shared/detail-store.js.map +1 -1
- package/dist/shared/role-render.js +4 -1
- package/dist/shared/role-render.js.map +1 -1
- package/dist/shared/role-view.js +49 -11
- package/dist/shared/role-view.js.map +1 -1
- package/dist/shared/world.js +11 -1
- package/dist/shared/world.js.map +1 -1
- package/hooks/pre-tool-use.sh +4 -4
- package/package.json +1 -2
- package/web/chat.css +103 -58
- package/web/chat.js +153 -82
- package/commands/wrc.md +0 -7
- package/dist/daemon/cc-bridge.js +0 -183
- package/dist/daemon/cc-bridge.js.map +0 -1
- package/dist/daemon/sessions.js +0 -4
- package/dist/daemon/sessions.js.map +0 -1
package/dist/daemon/detail.js
CHANGED
|
@@ -16,7 +16,7 @@ let remoteBase = "";
|
|
|
16
16
|
let remoteToken = "";
|
|
17
17
|
// 关系视图的注册表侧数据源。路由在 boot 早期就注册好了, 而 wizard/job/schedule
|
|
18
18
|
// 注册表要到 mirror 块才建得起来 —— 所以这里存的是 provider 而不是数据, 由那边
|
|
19
|
-
// 装上 (同 bindWizardStore 的取舍)
|
|
19
|
+
// 装上 (同 bindWizardStore 的取舍)。装上之前关系图退化成只画观测边。
|
|
20
20
|
let worldFacts;
|
|
21
21
|
export const setWorldFactsProvider = (fn) => { worldFacts = fn; startFactsForward(); };
|
|
22
22
|
// 注册表侧的事实同样要过河。turn 记录是 POST /d 一条条推的, 但身份 / 家谱 / 工单 /
|
|
@@ -126,8 +126,7 @@ export const recordTurnStart = (rec) => {
|
|
|
126
126
|
export const recordTurnItem = (id, item) => {
|
|
127
127
|
if (!store)
|
|
128
128
|
return;
|
|
129
|
-
store.appendTurnItem(id, item);
|
|
130
|
-
const full = store.get(id);
|
|
129
|
+
const full = store.get(store.appendTurnItem(id, item) ?? id);
|
|
131
130
|
if (full)
|
|
132
131
|
forwardToRemote(full);
|
|
133
132
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detail.js","sourceRoot":"","sources":["../../daemon/detail.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,sCAAsC;AACtC,0DAA0D;AAC1D,8EAA8E;AAC9E,sDAAsD;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAInC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EACL,iBAAiB,GAWlB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAA2B,MAAM,wBAAwB,CAAC;AACpH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,IAAI,KAAK,GAAuB,IAAI,CAAC;AACrC,IAAI,UAAU,GAAG,EAAE,CAAC;AACpB,IAAI,WAAW,GAAG,EAAE,CAAC;AACrB,uDAAuD;AACvD,qDAAqD;AACrD,
|
|
1
|
+
{"version":3,"file":"detail.js","sourceRoot":"","sources":["../../daemon/detail.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/inbound.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// Inbound text router. Hands the message off to
|
|
2
|
-
// (mode=headless) or the mirror bridge (mode=mirror).
|
|
1
|
+
// Inbound text router. Hands the message off to the mirror bridge.
|
|
3
2
|
import { mkdirSync, writeFileSync, statSync } from "node:fs";
|
|
4
3
|
import { join } from "node:path";
|
|
5
4
|
import { tailTurnsWithTools, keepalivePingSigs, renderPeerMentionHint } from "./peers.js";
|
|
@@ -13,8 +12,8 @@ import { computeAuditReport } from "./audit.js";
|
|
|
13
12
|
import { syncProjectConfig, renderSyncReport } from "./cfg-sync.js";
|
|
14
13
|
import { captureQuota, renderQuotaReport } from "./quota.js";
|
|
15
14
|
import { tagOfKey, baseOfKey, keyOf, withTagHeader, parseTagHeader, nameTokenRe, allNames, normalizeTag, uniqueTag, displayName, labelFor, tagLink } from "../shared/session-label.js";
|
|
16
|
-
import { chatNameOf, clearChatName,
|
|
17
|
-
import { wizardStore } from "./wizard.js";
|
|
15
|
+
import { chatNameOf, clearChatName, peerAddress, setChatName } from "./chat-name.js";
|
|
16
|
+
import { evictStaleName, reclaimChatName, wizardStore } from "./wizard.js";
|
|
18
17
|
import { truncate } from "../shared/std.js";
|
|
19
18
|
/** 判定"引用内容是否已在目标会话上下文里"时回看的轮数 —— 引用的通常是最近几轮
|
|
20
19
|
* 里的某条气泡,再往前用户多半是真想把老内容重新拎出来说事。 */
|
|
@@ -71,8 +70,7 @@ const authPrincipals = (msg) => {
|
|
|
71
70
|
};
|
|
72
71
|
// "会话id" = chat-binding (session/mirror key); "权限id" = either the group
|
|
73
72
|
// OR the sender — allowFrom passes if any one of them is whitelisted.
|
|
74
|
-
// Also surfaces per-id
|
|
75
|
-
// so users can copy-paste straight into a terminal to bind a Claude session.
|
|
73
|
+
// Also surfaces per-id 授权状态。
|
|
76
74
|
const renderIds = (msg, cfg) => {
|
|
77
75
|
const allowed = new Set(cfg.wrc.allowFrom.map((e) => sanitizeId(e)));
|
|
78
76
|
const mark = (id) => allowed.has("all") || allowed.has(id) ? "✅ 已授权" : "❌ 未授权";
|
|
@@ -83,13 +81,9 @@ const renderIds = (msg, cfg) => {
|
|
|
83
81
|
`群: \`${chat}\` ${mark(chat)}`,
|
|
84
82
|
`发送者: \`${sender}\` ${mark(sender)}`,
|
|
85
83
|
`(allowFrom 任一通过即可)`,
|
|
86
|
-
`在已有 Agent 会话中绑定本群聊: \`/wezard:wrc chat:${msg.chatid}\``,
|
|
87
84
|
].join("\n");
|
|
88
85
|
}
|
|
89
|
-
return
|
|
90
|
-
`会话id: \`${sender}\` ${mark(sender)}`,
|
|
91
|
-
`在已有 Agent 会话中绑定本单聊: \`/wezard:wrc user:${msg.from.userid}\``,
|
|
92
|
-
].join("\n");
|
|
86
|
+
return `会话id: \`${sender}\` ${mark(sender)}`;
|
|
93
87
|
};
|
|
94
88
|
const isIdCommand = (text) => text.trim() === "/id";
|
|
95
89
|
const isPwdCommand = (text) => text.trim() === "/pwd";
|
|
@@ -493,9 +487,9 @@ const isAllowed = (cfg, principals) => {
|
|
|
493
487
|
return principals.some((p) => allowed.has(p));
|
|
494
488
|
};
|
|
495
489
|
// Mirror mode grants implicit talkback: any chat that's currently a mirror
|
|
496
|
-
// target can post back without being in `allowFrom`.
|
|
497
|
-
//
|
|
498
|
-
const isMirrorTarget = (bridge, who) =>
|
|
490
|
+
// target can post back without being in `allowFrom`. A session bound to that
|
|
491
|
+
// chat is the authorization signal.
|
|
492
|
+
const isMirrorTarget = (bridge, who) => bridge.hasMirrorTarget(who);
|
|
499
493
|
// Sniff extension from magic bytes; falls back to .bin. WeCom doesn't always
|
|
500
494
|
// give us a filename for images, and we want claude's Read tool to recognize
|
|
501
495
|
// the file (it dispatches on extension).
|
|
@@ -533,26 +527,21 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
533
527
|
// 已知的机器人名只随配置变; 下面所有入站路径共用这一个剥离器。
|
|
534
528
|
const botNames = [...BUILTIN_BOT_NAMES, ...cfg.wrc.botNames.filter(Boolean)];
|
|
535
529
|
const stripAt = (msg, text) => maybeStripMentions(botNames, msg, text);
|
|
536
|
-
// Render /pwd output
|
|
537
|
-
// store via bridge.getCwd; headless mode has no per-chat cwd, so it just
|
|
538
|
-
// shows cfg.wrc.cwd as the global default.
|
|
530
|
+
// Render /pwd output from the live attachment + persisted store (bridge.getCwd).
|
|
539
531
|
const renderPwd = (who) => {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
lines.push(`下次切换: \`${pendingCwd}\` (使用 /new 或 /clear 生效)`);
|
|
545
|
-
}
|
|
546
|
-
if (runningCwd !== defaultCwd)
|
|
547
|
-
lines.push(`(默认: \`${defaultCwd}\`)`);
|
|
548
|
-
if (bridge.cwdUnconfirmed(who))
|
|
549
|
-
lines.push("⚠️ 这是默认兜底目录, 还没人确认过 —— 要换就说「切到 /path/to/proj」");
|
|
550
|
-
lines.push("> 切换其他项目: 对 AI 说「切到 /path/to/proj」(`set_workspace` 工具直接换目录重开)");
|
|
551
|
-
return lines.join("\n");
|
|
532
|
+
const { runningCwd, pendingCwd, defaultCwd } = bridge.getCwd(who);
|
|
533
|
+
const lines = [`[wezard] 📂 当前项目: \`${runningCwd}\``];
|
|
534
|
+
if (pendingCwd && pendingCwd !== runningCwd) {
|
|
535
|
+
lines.push(`下次切换: \`${pendingCwd}\` (使用 /new 或 /clear 生效)`);
|
|
552
536
|
}
|
|
553
|
-
|
|
537
|
+
if (runningCwd !== defaultCwd)
|
|
538
|
+
lines.push(`(默认: \`${defaultCwd}\`)`);
|
|
539
|
+
if (bridge.cwdUnconfirmed(who))
|
|
540
|
+
lines.push("⚠️ 这是默认兜底目录, 还没人确认过 —— 要换就说「切到 /path/to/proj」");
|
|
541
|
+
lines.push("> 切换其他项目: 对 AI 说「切到 /path/to/proj」(`set_workspace` 工具直接换目录重开)");
|
|
542
|
+
return lines.join("\n");
|
|
554
543
|
};
|
|
555
|
-
//
|
|
544
|
+
// Auto-spawn / /new helper. Routes through bridge.newSession
|
|
556
545
|
// which kills the old pane, spawns fresh in pendingCwd ?? runningCwd ??
|
|
557
546
|
// default, attaches, and pushes "📂 当前项目" info to the chat. Returns
|
|
558
547
|
// the user-facing one-line ack. The wizard's global name doubles as the
|
|
@@ -561,8 +550,6 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
561
550
|
// On success there is NO reply: newSession already pushed the single
|
|
562
551
|
// "created + cwd" bubble. Only failures produce user-facing text.
|
|
563
552
|
const spawnSession = async (who, cli, silent, model) => {
|
|
564
|
-
if (!("newSession" in bridge))
|
|
565
|
-
return { err: "[wezard] /new only available in mirror mode" };
|
|
566
553
|
const r = await bridge.newSession(who, displayName(who) || tagOf(who) || who, cli, { silent, model });
|
|
567
554
|
return r.ok ? {} : { err: `[wezard] /new failed: ${r.reason ?? "unknown"}` };
|
|
568
555
|
};
|
|
@@ -579,10 +566,37 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
579
566
|
return next;
|
|
580
567
|
};
|
|
581
568
|
// 显式 /new:排队但仍强制重开(用户就是要换一个)。
|
|
582
|
-
|
|
569
|
+
// 默认会话先把名字换回聊天名, 再 spawn —— charter 在 spawn 时按名字渲染。
|
|
570
|
+
// 聊天名若被一个静默超过一天的 wizard 占着, 先顶掉它 —— 否则默认会话只能退避成 `-N`。
|
|
571
|
+
const staleDeps = {
|
|
572
|
+
lastActivity: (t) => bridge.lastActivity(t),
|
|
573
|
+
retire: async (t) => { await bridge.killPane(t); },
|
|
574
|
+
};
|
|
575
|
+
// 新开的 slot 先按人写的名字落名: slot 只是内部 id, 被一个改过名的冷 wizard 占着
|
|
576
|
+
// 时会挪成 `foo-N` —— 名字不该跟着它退避。已有名字的 wizard 不受影响 (claim 不改)。
|
|
577
|
+
const claimAsked = (who, asked) => {
|
|
578
|
+
if (asked)
|
|
579
|
+
wizardStore()?.claim(who, asked);
|
|
580
|
+
};
|
|
581
|
+
const autoSpawnAndAttach = (who, asked, cli, model) => serializeSpawn(who, async () => {
|
|
582
|
+
claimAsked(who, asked);
|
|
583
|
+
const chat = chatNameOf(cfg, who);
|
|
584
|
+
if (chat && !tagOf(who)) {
|
|
585
|
+
const gone = await evictStaleName(wizardStore(), chat, [who], staleDeps);
|
|
586
|
+
if (gone)
|
|
587
|
+
log.info({ target: gone.target, name: gone.name }, "stale name evicted by /new");
|
|
588
|
+
}
|
|
589
|
+
reclaimChatName(wizardStore(), chat, who);
|
|
590
|
+
return spawnSession(who, cli, false, model);
|
|
591
|
+
});
|
|
583
592
|
// 隐式建会话(新 `.name` 的第一条消息):轮到自己时若前一条已经把会话建好,直接
|
|
584
593
|
// 复用,不再 respawn —— 否则先到的消息会被注入进一个刚被杀掉的 pane。
|
|
585
|
-
const ensureSession = (who) => serializeSpawn(who, async () =>
|
|
594
|
+
const ensureSession = (who, asked) => serializeSpawn(who, async () => {
|
|
595
|
+
if (bridge.hasMirrorTarget(who))
|
|
596
|
+
return {};
|
|
597
|
+
claimAsked(who, asked);
|
|
598
|
+
return spawnSession(who, undefined, true);
|
|
599
|
+
});
|
|
586
600
|
// Prefix user-visible daemon replies with `<emoji> .name`, so a chat hosting
|
|
587
601
|
// several wizards stays visually disambiguated. Emoji is derived from the
|
|
588
602
|
// name (not sessionId) so it stays stable across /clear cycles.
|
|
@@ -595,7 +609,7 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
595
609
|
};
|
|
596
610
|
// Common gating: claim bootstrap + allowFrom check. Returns true if the
|
|
597
611
|
// caller should stop (claim consumed or message rejected).
|
|
598
|
-
const gate = async (frame, msg, text, who) => {
|
|
612
|
+
const gate = async (frame, msg, text, who, asked = "") => {
|
|
599
613
|
const auths = authPrincipals(msg);
|
|
600
614
|
// Bootstrap / allowFrom operations are chat-scoped, not session-scoped;
|
|
601
615
|
// use the chat principal, not the routed wizard — a first-time user typing
|
|
@@ -643,9 +657,7 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
643
657
|
// Untagged form falls back to the caller's own mirror binding.
|
|
644
658
|
const audit = parseAuditCommand(text);
|
|
645
659
|
if (audit) {
|
|
646
|
-
const mirror =
|
|
647
|
-
? resolveAuditMirror(bridge.status().mirrors, audit.tag, who, chatPrincipal(msg))
|
|
648
|
-
: undefined;
|
|
660
|
+
const mirror = resolveAuditMirror(bridge.status().mirrors, audit.tag, who, chatPrincipal(msg));
|
|
649
661
|
let body;
|
|
650
662
|
if (!mirror) {
|
|
651
663
|
body = audit.tag
|
|
@@ -735,7 +747,7 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
735
747
|
// picks which wizard (re)spawns; the name is also the tmux window name.
|
|
736
748
|
const nu = parseNewCommand(text);
|
|
737
749
|
if (nu) {
|
|
738
|
-
const { err } = await autoSpawnAndAttach(who, nu.cli, nu.model);
|
|
750
|
+
const { err } = await autoSpawnAndAttach(who, asked, nu.cli, nu.model);
|
|
739
751
|
if (err) {
|
|
740
752
|
await replyText(frame, msg, who, err);
|
|
741
753
|
return { stop: true };
|
|
@@ -747,22 +759,20 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
747
759
|
return { stop: true };
|
|
748
760
|
}
|
|
749
761
|
// Authorized `/stop` — Esc the live pane to interrupt whatever Claude is
|
|
750
|
-
// currently doing.
|
|
762
|
+
// currently doing. Bails cleanly when no attachment.
|
|
751
763
|
if (isStopCommand(text)) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
await replyText(frame, msg, who, `⚠️ Esc 未送达(${r.escReason ?? "unknown"})${torndown} · 保活已暂停`);
|
|
765
|
-
}
|
|
764
|
+
// teardown: /stop is the user's "shut this up" button, so it must also
|
|
765
|
+
// close hanging bubbles and free the inject queue — not just press Esc.
|
|
766
|
+
// Stay silent on a clean stop (the pane going quiet IS the receipt);
|
|
767
|
+
// only speak up when a half actually failed — a live pane that refuses
|
|
768
|
+
// Esc is a very different situation from a chat merely stuck on a bubble.
|
|
769
|
+
const r = await bridge.interruptPane(who, { teardown: true });
|
|
770
|
+
if (!r.ok) {
|
|
771
|
+
await replyText(frame, msg, who, `[wezard] /stop failed: ${r.reason ?? "unknown"}`);
|
|
772
|
+
}
|
|
773
|
+
else if (!r.escOk) {
|
|
774
|
+
const torndown = r.torndown ? ` · 已收口 ${r.torndown} 个挂起气泡` : "";
|
|
775
|
+
await replyText(frame, msg, who, `⚠️ Esc 未送达(${r.escReason ?? "unknown"})${torndown} · 保活已暂停`);
|
|
766
776
|
}
|
|
767
777
|
return { stop: true };
|
|
768
778
|
}
|
|
@@ -770,39 +780,24 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
770
780
|
// and drop the binding (no `--resume` resurrection). Routed by `.name` like
|
|
771
781
|
// /stop, so `/kill #docs` only takes down that sibling.
|
|
772
782
|
if (isKillCommand(text)) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
else {
|
|
777
|
-
const r = await bridge.killPane(who);
|
|
778
|
-
await replyText(frame, msg, who, r.ok ? "🗑️ 会话已结束,pane 已移除" : `[wezard] /kill failed: ${r.reason ?? "unknown"}`);
|
|
779
|
-
}
|
|
783
|
+
const r = await bridge.killPane(who);
|
|
784
|
+
await replyText(frame, msg, who, r.ok ? "🗑️ 会话已结束,pane 已移除" : `[wezard] /kill failed: ${r.reason ?? "unknown"}`);
|
|
780
785
|
return { stop: true };
|
|
781
786
|
}
|
|
782
787
|
// Authorized `/n` — send a bare Enter to the live pane. Confirms a prompt /
|
|
783
788
|
// dismisses a "press enter to continue", or submits whatever's already in
|
|
784
|
-
// the input box.
|
|
789
|
+
// the input box. Bails cleanly when no attachment.
|
|
785
790
|
if (isEnterCommand(text)) {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
else {
|
|
790
|
-
const r = await bridge.submitPane(who);
|
|
791
|
-
await replyText(frame, msg, who, r.ok ? "Enter sent" : `[wezard] /n failed: ${r.reason ?? "unknown"}`);
|
|
792
|
-
}
|
|
791
|
+
const r = await bridge.submitPane(who);
|
|
792
|
+
await replyText(frame, msg, who, r.ok ? "Enter sent" : `[wezard] /n failed: ${r.reason ?? "unknown"}`);
|
|
793
793
|
return { stop: true };
|
|
794
794
|
}
|
|
795
795
|
// Authorized `/reveal` — switch the attached tmux client to this session's
|
|
796
|
-
// pane so the user lands in the terminal showing the live TUI.
|
|
797
|
-
//
|
|
796
|
+
// pane so the user lands in the terminal showing the live TUI. Routed by
|
|
797
|
+
// `.name` like any other session command.
|
|
798
798
|
if (isRevealCommand(text)) {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
}
|
|
802
|
-
else {
|
|
803
|
-
const r = await bridge.revealPane(who);
|
|
804
|
-
await replyText(frame, msg, who, r.ok ? "✅ 已切到本会话的 tmux 窗口" : `[wezard] /reveal failed: ${r.reason ?? "unknown"}`);
|
|
805
|
-
}
|
|
799
|
+
const r = await bridge.revealPane(who);
|
|
800
|
+
await replyText(frame, msg, who, r.ok ? "✅ 已切到本会话的 tmux 窗口" : `[wezard] /reveal failed: ${r.reason ?? "unknown"}`);
|
|
806
801
|
return { stop: true };
|
|
807
802
|
}
|
|
808
803
|
// /cfgsync [apply] — 3-way merge of the bound project's per-CLI config
|
|
@@ -810,7 +805,7 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
810
805
|
// .codebuddy/...). Writes files, so it sits AFTER the allowFrom gate.
|
|
811
806
|
const cs = parseCfgSyncCommand(text);
|
|
812
807
|
if (cs) {
|
|
813
|
-
const cwd =
|
|
808
|
+
const cwd = bridge.getCwd(who).runningCwd;
|
|
814
809
|
let body;
|
|
815
810
|
try {
|
|
816
811
|
body = renderSyncReport(await syncProjectConfig(cwd, cs.apply));
|
|
@@ -847,20 +842,12 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
847
842
|
}
|
|
848
843
|
// /chats — 跨聊天目录:谁有名字、各自跑着哪些会话。Read-only。
|
|
849
844
|
if (isChatsCommand(text)) {
|
|
850
|
-
if (!("chatRoster" in bridge)) {
|
|
851
|
-
await replyText(frame, msg, who, `[wezard] headless 模式无会话目录。已命名的聊天:${listChatNames(cfg).map((c) => c.name).join(", ") || "(无)"}`);
|
|
852
|
-
return { stop: true };
|
|
853
|
-
}
|
|
854
845
|
await replyText(frame, msg, who, renderChats(bridge.chatRoster(who)));
|
|
855
846
|
return { stop: true };
|
|
856
847
|
}
|
|
857
848
|
// /peers — this chat's own wizard roster (default + siblings), with
|
|
858
|
-
// live busy state. Read-only
|
|
849
|
+
// live busy state. Read-only.
|
|
859
850
|
if (isPeersCommand(text)) {
|
|
860
|
-
if (!("peers" in bridge)) {
|
|
861
|
-
await replyText(frame, msg, who, "[wezard] /peers only available in mirror mode");
|
|
862
|
-
return { stop: true };
|
|
863
|
-
}
|
|
864
851
|
let body;
|
|
865
852
|
try {
|
|
866
853
|
body = renderPeers(await bridge.peers(who), chatNameOf(cfg, who));
|
|
@@ -877,10 +864,6 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
877
864
|
// path as the /sessions/switch route so IM and MCP behave identically.
|
|
878
865
|
const sc = parseSessionsCommand(text);
|
|
879
866
|
if (sc) {
|
|
880
|
-
if (!("attach" in bridge)) {
|
|
881
|
-
await replyText(frame, msg, who, "[wezard] /sessions only available in mirror mode");
|
|
882
|
-
return { stop: true };
|
|
883
|
-
}
|
|
884
867
|
let sessions = [];
|
|
885
868
|
try {
|
|
886
869
|
sessions = await scanClaudeSessions();
|
|
@@ -910,12 +893,12 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
910
893
|
: `[wezard] 切换失败: ${att.reason ?? "unknown"}`);
|
|
911
894
|
return { stop: true };
|
|
912
895
|
}
|
|
913
|
-
//
|
|
896
|
+
// No Claude session attached for this chat yet. Since the
|
|
914
897
|
// sender is already in allowFrom, we treat that authorization as license
|
|
915
898
|
// to auto-spawn: this inbound becomes both the binding signal and the
|
|
916
899
|
// first prompt — attach, then fall through to dispatch.
|
|
917
|
-
if (
|
|
918
|
-
const { err } = await ensureSession(who);
|
|
900
|
+
if (!bridge.hasMirrorTarget(who)) {
|
|
901
|
+
const { err } = await ensureSession(who, asked);
|
|
919
902
|
if (err) {
|
|
920
903
|
await replyText(frame, msg, who, err);
|
|
921
904
|
return { stop: true };
|
|
@@ -925,9 +908,9 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
925
908
|
return { stop: false };
|
|
926
909
|
};
|
|
927
910
|
// 「引用内容是否已经在目标会话的 context 里」。两级:先查刚发出去的最后一条
|
|
928
|
-
// 气泡(
|
|
929
|
-
// —— 引用的往往是几轮之前的气泡,只比对最后一条会漏。目标未挂载(尚未 attach
|
|
930
|
-
//
|
|
911
|
+
// 气泡(内存);miss 再读目标会话 transcript 的尾部若干轮
|
|
912
|
+
// —— 引用的往往是几轮之前的气泡,只比对最后一条会漏。目标未挂载(尚未 attach)
|
|
913
|
+
// 时读不到 transcript,退化成"保留引用",宁可多给上下文。
|
|
931
914
|
const quoteInContext = (target, quoted) => {
|
|
932
915
|
if (isLastResponseQuote(baseOfKey(target), quoted)) {
|
|
933
916
|
log.info({ target, reason: "lastResponse" }, "quoteInContext: hit");
|
|
@@ -935,12 +918,11 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
935
918
|
}
|
|
936
919
|
// 源会话的 last stream 还没收口 ⇒ 引用的是实时中间态 (URL + 最新 CoT/工具行),
|
|
937
920
|
// 只保留路由 tag, 不把瞬态内容贴回 prompt。
|
|
938
|
-
if (
|
|
921
|
+
if (bridge.isOpenBubbleQuote(target, quoted)) {
|
|
939
922
|
log.info({ target, reason: "openBubble" }, "quoteInContext: hit");
|
|
940
923
|
return true;
|
|
941
924
|
}
|
|
942
|
-
const mirrors = bridge
|
|
943
|
-
.status?.().mirrors ?? [];
|
|
925
|
+
const mirrors = bridge.status().mirrors;
|
|
944
926
|
const jsonl = mirrors.find((m) => m.target === target)?.jsonlPath;
|
|
945
927
|
if (!jsonl) {
|
|
946
928
|
log.info({ target, mirrorCount: mirrors.length, mirrorTargets: mirrors.map((m) => m.target) }, "quoteInContext: no jsonl for target");
|
|
@@ -958,8 +940,6 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
958
940
|
// 同一套(全局名册),所以标注出来的地址一定是 peek_peer/send_peer 打得中的;
|
|
959
941
|
// 解析不到的 `.foo` 与自指静默略过。
|
|
960
942
|
const peerMentions = (who, text) => {
|
|
961
|
-
if (!("resolvePeerTag" in bridge))
|
|
962
|
-
return [];
|
|
963
943
|
const mb = bridge;
|
|
964
944
|
const seen = new Set();
|
|
965
945
|
return allNames(text).flatMap((name) => {
|
|
@@ -980,7 +960,7 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
980
960
|
});
|
|
981
961
|
};
|
|
982
962
|
// 名字解析的两个问题: 这个名字认不认识 (决定正文里哪个 `.x` 是路由)、它指向谁。
|
|
983
|
-
const live = (t) =>
|
|
963
|
+
const live = (t) => bridge.hasMirrorTarget(t);
|
|
984
964
|
const addrFor = (base) => ({
|
|
985
965
|
known: (name) => !!wizardStore()?.byName(name) || live(keyOf(base, normalizeTag(name))),
|
|
986
966
|
resolve: (b, name) => resolveName(b, name, live),
|
|
@@ -1013,7 +993,7 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
1013
993
|
return;
|
|
1014
994
|
const { text, tag, promoted, who } = route(msg, stripAt(msg, msg.text?.content ?? ""));
|
|
1015
995
|
log.info({ msgid: msg.msgid, len: text.length, tag, hasQuote: !!msg.quote, promoted }, "rx text");
|
|
1016
|
-
const { stop } = await gate(frame, msg, text, who);
|
|
996
|
+
const { stop } = await gate(frame, msg, text, who, tag);
|
|
1017
997
|
if (stop)
|
|
1018
998
|
return;
|
|
1019
999
|
await send(frame, msg, who, text);
|
|
@@ -1025,8 +1005,8 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
1025
1005
|
log.info({ msgid: msg.msgid, hasQuote: !!msg.quote }, "rx image");
|
|
1026
1006
|
// Images carry no text of their own — the quote (if any) is the only
|
|
1027
1007
|
// routing signal; without it they land on the chat's default session.
|
|
1028
|
-
const { text, who } = route(msg, "");
|
|
1029
|
-
const { stop } = await gate(frame, msg, "", who);
|
|
1008
|
+
const { text, tag, who } = route(msg, "");
|
|
1009
|
+
const { stop } = await gate(frame, msg, "", who, tag);
|
|
1030
1010
|
if (stop)
|
|
1031
1011
|
return;
|
|
1032
1012
|
const path = await downloadToInbox({ client, log, inboxDir }, msg.image.url, msg.image.aeskey, msg.msgid, 0);
|
|
@@ -1054,8 +1034,8 @@ export const installInboundRouter = (client, cfg, log, bridge, sourcePath) => {
|
|
|
1054
1034
|
.filter((it) => it.msgtype === "text")
|
|
1055
1035
|
.map((it) => it.text?.content ?? "")
|
|
1056
1036
|
.join("\n");
|
|
1057
|
-
const { who } = route(msg, stripAt(msg, rawText));
|
|
1058
|
-
const { stop } = await gate(frame, msg, "", who);
|
|
1037
|
+
const { tag, who } = route(msg, stripAt(msg, rawText));
|
|
1038
|
+
const { stop } = await gate(frame, msg, "", who, tag);
|
|
1059
1039
|
if (stop)
|
|
1060
1040
|
return;
|
|
1061
1041
|
const texts = [];
|