wezard 1.3.22 → 1.4.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.
Files changed (43) hide show
  1. package/CLAUDE.md +4 -2
  2. package/README.md +43 -37
  3. package/dist/daemon/chat-name.js +65 -2
  4. package/dist/daemon/chat-name.js.map +1 -1
  5. package/dist/daemon/detail.js +7 -0
  6. package/dist/daemon/detail.js.map +1 -1
  7. package/dist/daemon/graph.js +25 -3
  8. package/dist/daemon/graph.js.map +1 -1
  9. package/dist/daemon/inbound.js +156 -89
  10. package/dist/daemon/inbound.js.map +1 -1
  11. package/dist/daemon/index.js +773 -125
  12. package/dist/daemon/index.js.map +1 -1
  13. package/dist/daemon/jobs.js +91 -0
  14. package/dist/daemon/jobs.js.map +1 -0
  15. package/dist/daemon/mirror-bridge.js +194 -14
  16. package/dist/daemon/mirror-bridge.js.map +1 -1
  17. package/dist/daemon/mirror-store.js.map +1 -1
  18. package/dist/daemon/notices.js +70 -0
  19. package/dist/daemon/notices.js.map +1 -0
  20. package/dist/daemon/peers.js +93 -61
  21. package/dist/daemon/peers.js.map +1 -1
  22. package/dist/daemon/spawn-tmux.js +46 -4
  23. package/dist/daemon/spawn-tmux.js.map +1 -1
  24. package/dist/daemon/tasks.js +90 -0
  25. package/dist/daemon/tasks.js.map +1 -0
  26. package/dist/daemon/wizard.js +151 -0
  27. package/dist/daemon/wizard.js.map +1 -0
  28. package/dist/mcp/server.js +236 -102
  29. package/dist/mcp/server.js.map +1 -1
  30. package/dist/shared/cli-backends.js +10 -0
  31. package/dist/shared/cli-backends.js.map +1 -1
  32. package/dist/shared/config.js +54 -14
  33. package/dist/shared/config.js.map +1 -1
  34. package/dist/shared/detail-render.js +3 -2
  35. package/dist/shared/detail-render.js.map +1 -1
  36. package/dist/shared/detail-store.js.map +1 -1
  37. package/dist/shared/schedule-spec.js +240 -0
  38. package/dist/shared/schedule-spec.js.map +1 -0
  39. package/dist/shared/session-label.js +8 -4
  40. package/dist/shared/session-label.js.map +1 -1
  41. package/package.json +1 -1
  42. package/dist/daemon/topics.js +0 -106
  43. package/dist/daemon/topics.js.map +0 -1
@@ -0,0 +1,90 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { patchJsonc } from "../shared/config-writer.js";
3
+ import { baseOfKey, withTagHeader } from "../shared/session-label.js";
4
+ import { describeWhen, isDue, nextFire } from "../shared/schedule-spec.js";
5
+ const stripPrefix = (s) => {
6
+ const i = s.indexOf(":");
7
+ return i >= 0 ? s.slice(i + 1) : s;
8
+ };
9
+ const persist = (cfg, sourcePath) => {
10
+ patchJsonc(sourcePath, [{ path: ["schedules"], value: cfg.schedules }]);
11
+ };
12
+ /** 新增一条定时。id 由这里发, 返回值里带着它 —— 取消只认 id。 */
13
+ export const addSchedule = (cfg, sourcePath, s) => {
14
+ const rec = {
15
+ ...s,
16
+ id: s.id ?? randomUUID().slice(0, 8),
17
+ createdAt: s.createdAt ?? Date.now(),
18
+ note: s.note ?? "",
19
+ };
20
+ cfg.schedules = [...cfg.schedules, rec];
21
+ persist(cfg, sourcePath);
22
+ return rec;
23
+ };
24
+ export const removeScheduleById = (cfg, sourcePath, id) => {
25
+ const hit = cfg.schedules.find((s) => s.id === id);
26
+ if (!hit)
27
+ return undefined;
28
+ cfg.schedules = cfg.schedules.filter((s) => s !== hit);
29
+ persist(cfg, sourcePath);
30
+ return hit;
31
+ };
32
+ /** 给出 `target` 就只列排给那个 wizard 的。 */
33
+ export const listSchedules = (cfg, target) => target ? cfg.schedules.filter((s) => s.target === target) : cfg.schedules;
34
+ /** 列表回显: 规格与下次触发都摊成人话, 调用方直接念给用户听。 */
35
+ export const renderSchedule = (s, now = new Date()) => ({
36
+ id: s.id,
37
+ when: describeWhen(s.when),
38
+ next: new Date(nextFire(s.when, s.lastFired ?? s.createdAt, now)).toLocaleString("zh-CN", { hour12: false }),
39
+ lastFired: s.lastFired ? new Date(s.lastFired).toLocaleString("zh-CN", { hour12: false }) : "",
40
+ note: s.note,
41
+ target: s.target,
42
+ prompt: s.prompt,
43
+ });
44
+ const announce = async (client, target, markdown) => {
45
+ await client.sendMessage(stripPrefix(baseOfKey(target)), {
46
+ msgtype: "markdown",
47
+ markdown: { content: withTagHeader(target, markdown) },
48
+ });
49
+ };
50
+ export const startScheduler = ({ client, cfg, sourcePath, log, inject }) => {
51
+ const fire = async (s) => {
52
+ if (!inject) {
53
+ log.warn({ id: s.id, target: s.target }, "scheduled task skipped: not in mirror mode");
54
+ return;
55
+ }
56
+ // 先 announce 后注入: 被唤醒的 wizard 接下来在群里说的话才有出处, 否则群里凭空
57
+ // 冒出一轮对话, 没人知道是谁点的火。
58
+ await announce(client, s.target, `⏰ **定时任务** · ${describeWhen(s.when)}\n> ${s.prompt.split("\n")[0].slice(0, 120)}`)
59
+ .catch((e) => log.warn({ id: s.id, err: e.message }, "task announce failed"));
60
+ const r = await inject(s.target, s.prompt);
61
+ log.info({ id: s.id, target: s.target, ok: r.ok, reason: r.reason }, "scheduled task fired");
62
+ if (!r.ok) {
63
+ await announce(client, s.target, `⏰ 定时任务注入失败:${r.reason ?? "unknown"}`).catch(() => { });
64
+ }
65
+ };
66
+ const tick = async () => {
67
+ const now = new Date();
68
+ const due = cfg.schedules.filter((s) => isDue(s.when, now, s.lastFired ?? s.createdAt));
69
+ if (!due.length)
70
+ return;
71
+ const firedAt = now.getTime();
72
+ const fired = new Set(due.map((s) => s.id));
73
+ // once 放完这一枪就没有下一枪了, 留在表里只会让列表越来越脏。
74
+ cfg.schedules = cfg.schedules
75
+ .map((s) => (fired.has(s.id) ? { ...s, lastFired: firedAt } : s))
76
+ .filter((s) => !(s.when.kind === "once" && s.lastFired));
77
+ persist(cfg, sourcePath);
78
+ for (const s of due) {
79
+ try {
80
+ await fire(s);
81
+ }
82
+ catch (e) {
83
+ log.error({ id: s.id, err: e.message }, "scheduled fire failed");
84
+ }
85
+ }
86
+ };
87
+ const timer = setInterval(() => { void tick(); }, 20_000);
88
+ return { stop: () => clearInterval(timer) };
89
+ };
90
+ //# sourceMappingURL=tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../daemon/tasks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ // Wizard: 给「绑定到聊天的会话」一个身份。
2
+ //
3
+ // 在这之前, 一个会话只是 `chat:wrkS…#tag` 这么一个路由 key —— 它不知道自己叫
4
+ // 什么、为什么存在、谁生的它、群里还有谁。于是每次协作都要人在提示词里重讲一遍。
5
+ // wizard 把这些沉到一条记录里, 并在 spawn 时以 `--append-system-prompt` 的形式
6
+ // 写进那个进程的上下文: 身份不是聊天历史里的一句话 (会被 /clear 抹掉、会被挤出
7
+ // 窗口), 而是随进程终身存在的前缀。
8
+ //
9
+ // wizard = target(一个聊天里的一个会话) + 名字 + 职责 + 记忆 + 家谱
10
+ // clone = parent 非空的 wizard; fork 了父亲的会话, 开局就带着父亲读过的东西
11
+ // 记忆 = 自己写下的短句, 每次 (重)spawn 都重新进上下文 —— 跨 /clear 的东西
12
+ //
13
+ // 本模块只有两类东西: 一个写盘的注册表 (唯一副作用), 和一堆把记录渲染成文字的
14
+ // 纯函数。真正的动作 (spawn / inject / kill) 全在 mirror-bridge, 这里一个都不做。
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
+ import { dirname } from "node:path";
17
+ import { expandHome } from "../shared/paths.js";
18
+ import { tagOfKey } from "../shared/session-label.js";
19
+ const MEMORY_MAX = 60;
20
+ const NOTE_MAX = 600;
21
+ const blank = (target) => ({ target, name: "", description: "", bornAt: Date.now(), memory: [] });
22
+ /** 写穿式单文件存储, 与 mirror-store 同款 —— 这是运行时状态, 不是用户手写配置,
23
+ * 所以躺在 stateDir 而不是 config.jsonc。 */
24
+ export const loadWizardStore = (filePath) => {
25
+ const abs = expandHome(filePath);
26
+ let map = {};
27
+ if (existsSync(abs)) {
28
+ try {
29
+ map = JSON.parse(readFileSync(abs, "utf8"));
30
+ }
31
+ catch {
32
+ map = {};
33
+ }
34
+ }
35
+ else {
36
+ mkdirSync(dirname(abs), { recursive: true });
37
+ }
38
+ const persist = () => {
39
+ try {
40
+ writeFileSync(abs, JSON.stringify(map, null, 2), "utf8");
41
+ }
42
+ catch { /* 记忆丢了也不该拖垮会话 */ }
43
+ };
44
+ return {
45
+ get: (t) => map[t],
46
+ upsert: (t, patch) => {
47
+ const next = { ...(map[t] ?? blank(t)), ...patch, target: t };
48
+ next.memory = next.memory.slice(-MEMORY_MAX).map((m) => m.slice(0, NOTE_MAX));
49
+ map[t] = next;
50
+ persist();
51
+ return next;
52
+ },
53
+ drop: (t) => { delete map[t]; persist(); },
54
+ all: () => Object.values(map),
55
+ };
56
+ };
57
+ // 进程内唯一的注册表。daemon 启动时绑一次, 之后任何模块都读得到 —— IM 侧的
58
+ // `/peers` 要把名字和职责印在每一行上, 为此给 installInboundRouter 再加一个参数
59
+ // 不值当 (同 bindCliBackends 的取舍)。headless 模式不绑, 读到 undefined 即退化。
60
+ let bound;
61
+ export const bindWizardStore = (store) => (bound = store);
62
+ export const wizardStore = () => bound;
63
+ // ── 纯推导 ────────────────────────────────────────────────────────────
64
+ /** 名字: 自己起的 > 聊天名 (+`#tag`) > 裸 tag > ""。
65
+ * 需求是「wizard 的名字就是 chat 的名字」—— 一个聊天里的多个会话靠 `#tag` 区分,
66
+ * 所以带 tag 的分身名字是 `chatName#tag`, 读起来正好就是它的地址。 */
67
+ export const wizardName = (rec, chatName, target) => {
68
+ const explicit = (rec?.name ?? "").trim();
69
+ if (explicit)
70
+ return explicit;
71
+ const tag = tagOfKey(target);
72
+ if (chatName)
73
+ return tag ? `${chatName}#${tag}` : chatName;
74
+ return tag;
75
+ };
76
+ /** 直系分身。 */
77
+ export const childrenOf = (all, target) => all.filter((w) => w.parent === target);
78
+ /** 祖先链, 从父亲到最老的那个 (环路自动截断 —— 记录是外部可编辑的 json)。 */
79
+ export const ancestorsOf = (all, target) => {
80
+ const byTarget = new Map(all.map((w) => [w.target, w]));
81
+ const walk = (t, seen) => !t || seen.includes(t) ? [...seen] : walk(byTarget.get(t)?.parent, [...seen, t]);
82
+ return walk(byTarget.get(target)?.parent, []);
83
+ };
84
+ const bullet = (xs) => xs.map((x) => `- ${x}`).join("\n");
85
+ const nameLine = (b) => `${b.name || b.address || "(无名)"} · 地址 \`${b.address || "(默认会话)"}\`${b.description ? ` · ${b.description}` : ""}${b.cwd ? ` · ${b.cwd}` : ""}`;
86
+ /** 开局宪章 —— spawn 时作为 `--append-system-prompt` 压进进程。
87
+ * 它回答四件事, 每一件都是"会话自己没法从对话里知道"的:
88
+ * 我是谁 / 我在哪个群、群里还有谁 / 我有哪些能力 / 我们当着人说话该怎么说。 */
89
+ export const renderCharter = (a) => {
90
+ const parts = [
91
+ "# 你是一个 wizard",
92
+ "",
93
+ "你不是一次性的助手进程。你是 wezard 架在企业微信与本机 agent CLI 之间的一个**常驻角色**:",
94
+ "一个聊天会话绑定一个工作区, 有名字、有职责、有记忆, 能生分身、能和别的 wizard 直接对话。",
95
+ "下面是你的身份, 它随你这个进程终身有效 —— 即使 `/clear` 清空了对话, 这段话仍在。",
96
+ "",
97
+ "## 我是谁",
98
+ bullet([
99
+ `名字: **${a.self.name || "(还没有名字 —— 需要时用 wizard_identity 给自己起一个)"}**`,
100
+ `地址: \`${a.self.address || "(本聊天的默认会话)"}\` —— 别的 wizard 用它找你`,
101
+ `所在聊天: ${a.chat ? `**${a.chat}**` : "(还没有名字 —— 它会在第一次被用到时按工作区自动补上; 想要个更好的名字就 name_chat)"} \`${a.principal}\``,
102
+ `工作区: \`${a.self.cwd || "(未设置)"}\``,
103
+ `职责: ${a.self.description || "(未写 —— 用 wizard_identity 写一句, 别人靠它决定该不该找你)"}`,
104
+ a.parent
105
+ ? `出身: 由 **${a.parent.name || a.parent.address}** 分身而来${a.inherited ? ", **你继承了它当时的全部上下文**(它读过的文件/文档你开局就有)" : ", 空白起步"}`
106
+ : "出身: 本聊天的原生 wizard",
107
+ ]),
108
+ "",
109
+ ];
110
+ if (a.memory.length > 0) {
111
+ parts.push("## 我的记忆", "这些是你自己写下的、要跨会话活下来的东西:", bullet(a.memory), "");
112
+ }
113
+ if (a.siblings.length > 0) {
114
+ parts.push("## 出生时群里已有的 wizard", bullet(a.siblings.map(nameLine)), "(这只是出生那一刻的快照。此后群里谁来了、谁收工了、谁改了职责, 会以一行 system-reminder", "挂在下一条进到你这儿的消息尾巴上 —— 不必去问。要当下完整的名册仍然是 `wizard_roster`。)", "");
115
+ }
116
+ parts.push("## 我能做什么 (MCP `wezard`)", bullet([
117
+ "`wizard_whoami` 我是谁、上下文用了多少、我的分身有哪些",
118
+ "`wizard_identity` 给自己起名字 / 写职责",
119
+ "`wizard_roster` 全部 wizard 与 clone: 名字、聊天、工作区、职责、模型、忙闲、家谱",
120
+ "`spawn_clone` 生一个分身 —— `inherit:true` 让它继承我此刻的上下文, `inherit:false` 给它一张白纸; `model` 给它挑模型 (跑腿的活给 haiku, 要判断的给 opus); 分身还能再生分身",
121
+ "`stop_wizard` 打断或终结一个分身/wizard (活干完了就收掉它)",
122
+ "`open_job` / `close_job` / `list_jobs` 一次要派出两个以上分身时的**工单**: 群里只出开工/收工两条气泡, 收工时整批回收临时分身",
123
+ "`wizard_remember` 写一条跨会话的记忆",
124
+ "`wizard_handoff_self` 上下文快满时自己原地交接重开",
125
+ "`send_peer` 跟别的 wizard 说话 (派活给一个正在忙的同伴用 `when:\"idle\"`, 别让两段话挤进同一轮) · `peek_peer` 看它在干嘛 · `list_peers` 看同群有谁",
126
+ "`wait_peer` 等它干完 —— 派了一**批**活就用 `tags` 一次等一组 (`need` 决定满几个就返回), 别一个一个等",
127
+ "`notify` 把一段话贴进某个聊天给**人**看 (省略 `to` 就是自己这个群) —— 和 send_peer 相反, 它不驱动任何 agent",
128
+ "`schedule_task` 给自己或别的 wizard 排一个到点自动执行的活 (「每个工作日晚上9:30 …」) · `list_tasks` / `cancel_task`",
129
+ "`set_workspace` 换工作区 · `name_chat` 给聊天起名 · `list_chats` 看别的聊天",
130
+ "`run_agent_graph` 把多个 wizard 串成一条会循环的流水线",
131
+ ]), "", "## 怎么干活: 编排", "遇到一组「共享同一批上下文」的任务, 不要自己一件件做完, 也不要让每个分身各读一遍材料。", "**控制流在你手里** —— 没有别的调度器替你跑这件事: 你自己分路、自己派、自己等、自己汇总。", bullet([
132
+ "先在自己这里把**公共材料**读进上下文 (规范、目录结构、关键文件)",
133
+ "要派出两个以上的分身就先 `open_job(标题, 计划)` 拿一个工单 id —— 之后每个 `spawn_clone` / `send_peer` 都带上 `job`",
134
+ "`spawn_clone({inherit:true, task})` 出需要的分身: 它们开局就带着这些材料, 只需告诉它各自那一份差异; 活写进 `task` 省一次往返",
135
+ "派活时要求它**把结论收口成一行** `RESULT: …` (交付物写进文件就回传路径) —— `wait_peer` 会把这一行单独摘进 `result`, 免得你从八百字散文里找结论",
136
+ "一次 `wait_peer({tags:[…]})` 把它们**一起**等回来。它们本来就在并行干活: 一个一个等, 墙钟是所有人之和; 一起等只花最慢那一个的时间 (`need` 可以让你先处理最先完事的那几个)",
137
+ "汇总完 `close_job(summary)` —— 结论发进群, 为这个工单生出来的分身整批回收。要反复迭代到收敛则用 `run_agent_graph`",
138
+ ]), "分身是有成本的 (一个 tmux pane + 一份上下文), 而且名下同时活着的分身有上限 ——", "任务少于两三件时自己做完更快; 干完了就收, 别让上一批堵住下一批。", "", "## 怎么说话: 人在群里看着", "你们之间的**关键节点**会以 `发起方 → 接收方` 的气泡出现 (分身出生、派活、收尾、跨聊天的", "结论), 中间的来回只落在 chat 详情页。一条气泡只落在**收信那一方**的群里: 同群互派就显示", "在你们共处的群; 你把活派去别的聊天, 它显示在**那边**, 头写成 `源聊天#源wizard → 目标", "wizard`, 你自己的群里不会再复述一遍。也就是说: 人看得见你派了什么活、谁给了什么结论,", "但看不见你们每一次催促。所以:", bullet([
139
+ "**分清对象**: 对人说话 = 你的正常回复; 对 wizard 说话 = `send_peer`。别把派给分身的指令写进给人的回复里。",
140
+ "**不要复述**: 你派出去的话已经以气泡显示给该看见的人了, 同群伙伴的回复也会以它自己的气泡出现。收到消息不必回「收到」, 拿到结果不必把对方原话再念一遍 —— 只说你据此做了什么、结论是什么。",
141
+ "**对 wizard 直说**: 不用寒暄、不用引用原文、不用客套。要什么、给什么、结论是什么, 一句话讲完。",
142
+ "**不要替别人开口**: 分身该做的事派给它、等它, 不要自己猜它会说什么然后替它答。",
143
+ "**对人要收口**: 人关心的是进展和结论, 不是你们之间的每一次往返。",
144
+ ]), "", "## 自我管理", bullet([
145
+ "上下文快满 (`wizard_whoami` 的 contextTokens) → 自己调 `wizard_handoff_self`, 把工作压成简报原地重开。",
146
+ "要换项目目录 → 自己调 `set_workspace`, 不必让人去敲命令。",
147
+ "没名字 / 职责为空 → 自己调 `wizard_identity` 补上; 名字就是别人喊你的那个词。",
148
+ ]));
149
+ return parts.filter((p) => p !== undefined).join("\n");
150
+ };
151
+ //# sourceMappingURL=wizard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wizard.js","sourceRoot":"","sources":["../../daemon/wizard.ts"],"names":[],"mappings":"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"}