@konglx/rotom 2.21.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 (189) hide show
  1. package/README.md +417 -0
  2. package/bin/mesh-master.sh +439 -0
  3. package/bin/rotom +29 -0
  4. package/bin/rotom-link.sh +136 -0
  5. package/bin/rotom-send-with-status +57 -0
  6. package/bin/rotom-up.sh +428 -0
  7. package/dist/cli/ask.js +62 -0
  8. package/dist/cli/common.js +321 -0
  9. package/dist/cli/config.js +65 -0
  10. package/dist/cli/directory.js +17 -0
  11. package/dist/cli/executor.js +58 -0
  12. package/dist/cli/fed.js +91 -0
  13. package/dist/cli/group.js +273 -0
  14. package/dist/cli/identity.js +62 -0
  15. package/dist/cli/init.js +268 -0
  16. package/dist/cli/issue.js +202 -0
  17. package/dist/cli/join.js +170 -0
  18. package/dist/cli/link.js +47 -0
  19. package/dist/cli/master.js +51 -0
  20. package/dist/cli/memory.js +307 -0
  21. package/dist/cli/note.js +68 -0
  22. package/dist/cli/repo.js +77 -0
  23. package/dist/cli/rotom.js +277 -0
  24. package/dist/cli/routes.js +118 -0
  25. package/dist/cli/run.js +45 -0
  26. package/dist/cli/schedule.js +237 -0
  27. package/dist/cli/skill.js +173 -0
  28. package/dist/cli/team.js +106 -0
  29. package/dist/executor/claude-code-hook.cjs +80 -0
  30. package/dist/executor/cli-executor.js +8 -0
  31. package/dist/executor/executors/claude-code.js +780 -0
  32. package/dist/executor/executors/codex.js +719 -0
  33. package/dist/executor/executors/hermes-cli.js +855 -0
  34. package/dist/executor/executors/openclaw.js +467 -0
  35. package/dist/executor/executors/pi.js +514 -0
  36. package/dist/executor/index.js +269 -0
  37. package/dist/executor/jsonrpc-transport.js +125 -0
  38. package/dist/executor/process-runner.js +101 -0
  39. package/dist/executor/reasoning-status.js +83 -0
  40. package/dist/executor/repo-cache.js +502 -0
  41. package/dist/executor/session-store.js +188 -0
  42. package/dist/executor/worker-chat.js +257 -0
  43. package/dist/executor/worker-connection.js +89 -0
  44. package/dist/executor/worker-issue.js +264 -0
  45. package/dist/executor/worker.js +877 -0
  46. package/dist/link/pending-requests.js +72 -0
  47. package/dist/link/server.js +233 -0
  48. package/dist/link/visibility-store.js +58 -0
  49. package/dist/master/api/agents.js +333 -0
  50. package/dist/master/api/artifacts.js +271 -0
  51. package/dist/master/api/domains.js +64 -0
  52. package/dist/master/api/groups.js +635 -0
  53. package/dist/master/api/guidance-templates.js +147 -0
  54. package/dist/master/api/index.js +89 -0
  55. package/dist/master/api/issues-patrol.js +172 -0
  56. package/dist/master/api/issues.js +663 -0
  57. package/dist/master/api/links-patrol.js +168 -0
  58. package/dist/master/api/links.js +114 -0
  59. package/dist/master/api/memory.js +259 -0
  60. package/dist/master/api/messages.js +157 -0
  61. package/dist/master/api/notes.js +77 -0
  62. package/dist/master/api/schedule-patterns.js +133 -0
  63. package/dist/master/api/schedules.js +272 -0
  64. package/dist/master/api/sessions.js +158 -0
  65. package/dist/master/api/share.js +269 -0
  66. package/dist/master/api/skills.js +190 -0
  67. package/dist/master/api/teams.js +122 -0
  68. package/dist/master/api/uploads.js +245 -0
  69. package/dist/master/auth.js +134 -0
  70. package/dist/master/dashboard/animations/calico-dozing.apng +0 -0
  71. package/dist/master/dashboard/animations/calico-error.apng +0 -0
  72. package/dist/master/dashboard/animations/calico-happy.apng +0 -0
  73. package/dist/master/dashboard/animations/calico-notification.apng +0 -0
  74. package/dist/master/dashboard/animations/calico-sleeping.apng +0 -0
  75. package/dist/master/dashboard/animations/calico-thinking.apng +0 -0
  76. package/dist/master/dashboard/animations/calico-waking.apng +0 -0
  77. package/dist/master/dashboard/assets/ApprovalCard-C38VV6ko.css +1 -0
  78. package/dist/master/dashboard/assets/ApprovalCard-CHPh2dmE.js +17 -0
  79. package/dist/master/dashboard/assets/ArtifactPanel-P_2gAP7v.js +1 -0
  80. package/dist/master/dashboard/assets/ArtifactPanel-aGHySny5.css +1 -0
  81. package/dist/master/dashboard/assets/css.worker-DaIe3gwK.js +84 -0
  82. package/dist/master/dashboard/assets/editor.worker-BCzxt1at.js +12 -0
  83. package/dist/master/dashboard/assets/html.worker-CKrFyw_2.js +461 -0
  84. package/dist/master/dashboard/assets/index-CChrTn81.css +32 -0
  85. package/dist/master/dashboard/assets/index-Dhu4SN1z.js +181 -0
  86. package/dist/master/dashboard/assets/json.worker-B7c_PmGb.js +49 -0
  87. package/dist/master/dashboard/assets/markdown-CeN5IgdF.js +29 -0
  88. package/dist/master/dashboard/assets/monaco-core-DyX1CsEw.css +1 -0
  89. package/dist/master/dashboard/assets/monaco-core-oQiQUisy.js +833 -0
  90. package/dist/master/dashboard/assets/monaco-setup-CiOPQdmo.js +1 -0
  91. package/dist/master/dashboard/assets/react-vendor-C8IxlyCR.js +67 -0
  92. package/dist/master/dashboard/assets/ts.worker-BhkL8olL.js +51334 -0
  93. package/dist/master/dashboard/assets/useMonaco-ILb4vyPh.js +12 -0
  94. package/dist/master/dashboard/assets/vite-preload-CxJPbCTl.js +1 -0
  95. package/dist/master/dashboard/debug-auth.html +197 -0
  96. package/dist/master/dashboard/favicon.ico +0 -0
  97. package/dist/master/dashboard/index.html +20 -0
  98. package/dist/master/dashboard/rotom-avatar.png +0 -0
  99. package/dist/master/db/agent-sessions.js +60 -0
  100. package/dist/master/db/agent-visibility.js +64 -0
  101. package/dist/master/db/agents.js +119 -0
  102. package/dist/master/db/ask-bridges.js +157 -0
  103. package/dist/master/db/build-update.js +59 -0
  104. package/dist/master/db/core.js +82 -0
  105. package/dist/master/db/domains.js +80 -0
  106. package/dist/master/db/groups.js +316 -0
  107. package/dist/master/db/guidance-templates.js +58 -0
  108. package/dist/master/db/index.js +12 -0
  109. package/dist/master/db/internal.js +45 -0
  110. package/dist/master/db/issues-patrol.js +81 -0
  111. package/dist/master/db/issues.js +373 -0
  112. package/dist/master/db/links.js +221 -0
  113. package/dist/master/db/master-node.js +43 -0
  114. package/dist/master/db/memory.js +272 -0
  115. package/dist/master/db/messages.js +210 -0
  116. package/dist/master/db/notes.js +55 -0
  117. package/dist/master/db/schedule-patterns.js +56 -0
  118. package/dist/master/db/schedules.js +135 -0
  119. package/dist/master/db/skills.js +144 -0
  120. package/dist/master/db/team.js +88 -0
  121. package/dist/master/db/types.js +10 -0
  122. package/dist/master/db.js +12 -0
  123. package/dist/master/embedded.js +133 -0
  124. package/dist/master/federation/client.js +283 -0
  125. package/dist/master/federation/identity.js +133 -0
  126. package/dist/master/federation/manager.js +267 -0
  127. package/dist/master/federation/publisher.js +87 -0
  128. package/dist/master/federation/self-publisher.js +69 -0
  129. package/dist/master/federation/server.js +487 -0
  130. package/dist/master/group-paths.js +208 -0
  131. package/dist/master/offline-queue.js +38 -0
  132. package/dist/master/opc-bootstrap.js +245 -0
  133. package/dist/master/patrol-terminal.js +275 -0
  134. package/dist/master/repo-scan.js +188 -0
  135. package/dist/master/router.js +214 -0
  136. package/dist/master/scheduler-handlers.js +510 -0
  137. package/dist/master/scheduler.js +201 -0
  138. package/dist/master/server.js +203 -0
  139. package/dist/master/services/link-collector.js +82 -0
  140. package/dist/master/services/link-patrol-bootstrap.js +50 -0
  141. package/dist/master/services/memory-extract-prompt.js +34 -0
  142. package/dist/master/services/patrol-bootstrap.js +63 -0
  143. package/dist/master/share-tokens.js +56 -0
  144. package/dist/master/terminal-hub.js +300 -0
  145. package/dist/master/uploads.js +108 -0
  146. package/dist/master/util/fs.js +100 -0
  147. package/dist/master/util/paths.js +50 -0
  148. package/dist/master/util/persona.js +10 -0
  149. package/dist/master/ws-hub/connection.js +928 -0
  150. package/dist/master/ws-hub/conversation.js +290 -0
  151. package/dist/master/ws-hub/directory.js +70 -0
  152. package/dist/master/ws-hub/dispatch-enrich.js +34 -0
  153. package/dist/master/ws-hub/hub.js +136 -0
  154. package/dist/master/ws-hub/index.js +9 -0
  155. package/dist/master/ws-hub/internal.js +35 -0
  156. package/dist/master/ws-hub/routing.js +295 -0
  157. package/dist/master/ws-hub/sessions.js +130 -0
  158. package/dist/master/ws-hub.js +11 -0
  159. package/dist/shared/agent-profile.js +44 -0
  160. package/dist/shared/constants.js +55 -0
  161. package/dist/shared/dedup.js +33 -0
  162. package/dist/shared/group-context.js +62 -0
  163. package/dist/shared/json-codec.js +33 -0
  164. package/dist/shared/logger.js +136 -0
  165. package/dist/shared/mention.js +22 -0
  166. package/dist/shared/network.js +40 -0
  167. package/dist/shared/parse.js +18 -0
  168. package/dist/shared/prompt-composer.js +171 -0
  169. package/dist/shared/protocol/client-messages.js +8 -0
  170. package/dist/shared/protocol/enums.js +6 -0
  171. package/dist/shared/protocol/federation.js +62 -0
  172. package/dist/shared/protocol/guards.js +87 -0
  173. package/dist/shared/protocol/server-messages.js +8 -0
  174. package/dist/shared/protocol/types.js +8 -0
  175. package/dist/shared/protocol.js +19 -0
  176. package/dist/shared/readonly-allowlist.js +122 -0
  177. package/dist/shared/rotom-cli-prompt.js +23 -0
  178. package/dist/shared/skill-context.js +19 -0
  179. package/dist/shared/skill-md.js +43 -0
  180. package/dist/shared/slash-commands.js +50 -0
  181. package/dist/shared/time.js +80 -0
  182. package/dist/shared/title.js +46 -0
  183. package/dist/shared/url-extractor.js +99 -0
  184. package/migrations/001-schema.sql +942 -0
  185. package/package.json +68 -0
  186. package/scripts/fix-node-pty-perms.mjs +46 -0
  187. package/skill/rotom-a2a-communicate/SKILL.md +257 -0
  188. package/skill/rotom-bus-host/SKILL.md +78 -0
  189. package/skill/rotom-bus-host/scripts/poll-replies.sh +148 -0
@@ -0,0 +1,136 @@
1
+ import { nowBeijing } from "./time.js";
2
+ /**
3
+ * Digital Employee Mesh — Logger
4
+ *
5
+ * Lightweight structured logger with timestamps. No external dependencies.
6
+ * Supports optional daily-rotated file output with auto-cleanup.
7
+ *
8
+ * Usage:
9
+ * const log = createLogger("mesh-master");
10
+ * log.info("started"); // → stdout only
11
+ *
12
+ * enableFileLogging("~/.rotom/logs"); // enable file output
13
+ * log.info("with file"); // → stdout + logs/mesh-master-2026-03-23.log
14
+ *
15
+ * Stream selection:
16
+ * - `createLogger("mesh-master")` → default: info/log to stdout, warn/error to stderr
17
+ * - `createLogger("mesh-executor", { stream: "stderr" })` → all levels to stderr
18
+ * (use this in executor so log lines never pollute stdout, which keeps
19
+ * stdout free for any machine-parsed output the master might consume)
20
+ *
21
+ * The file-name prefix is derived from the module name passed to
22
+ * createLogger — `createLogger("mesh-executor")` writes to
23
+ * `mesh-executor-YYYY-MM-DD.log` (not `mesh-master-...`).
24
+ */
25
+ import fs from "node:fs";
26
+ import path from "node:path";
27
+ import { format } from "node:util";
28
+ // ---------------------------------------------------------------------------
29
+ // File logging state (shared across all loggers in this process)
30
+ // ---------------------------------------------------------------------------
31
+ let logDir = null;
32
+ let currentDate = "";
33
+ let currentFd = null;
34
+ const LOG_RETAIN_DAYS = 30;
35
+ function todayStr() {
36
+ return nowBeijing().slice(0, 10); // "2026-03-23"
37
+ }
38
+ function ensureFd() {
39
+ if (!logDir)
40
+ return null;
41
+ const today = todayStr();
42
+ if (today !== currentDate) {
43
+ // Day changed — rotate
44
+ if (currentFd !== null) {
45
+ try {
46
+ fs.closeSync(currentFd);
47
+ }
48
+ catch { /* ignore */ }
49
+ }
50
+ currentDate = today;
51
+ const filePath = path.join(logDir, `mesh-master-${today}.log`);
52
+ currentFd = fs.openSync(filePath, "a");
53
+ }
54
+ return currentFd;
55
+ }
56
+ function writeToFile(line) {
57
+ const fd = ensureFd();
58
+ if (fd !== null) {
59
+ try {
60
+ fs.writeSync(fd, line + "\n");
61
+ }
62
+ catch { /* non-fatal */ }
63
+ }
64
+ }
65
+ function cleanupOldLogs() {
66
+ if (!logDir)
67
+ return;
68
+ try {
69
+ const cutoff = Date.now() - LOG_RETAIN_DAYS * 86_400_000;
70
+ for (const name of fs.readdirSync(logDir)) {
71
+ if (!name.startsWith("mesh-") || !name.endsWith(".log"))
72
+ continue;
73
+ const filePath = path.join(logDir, name);
74
+ const stat = fs.statSync(filePath);
75
+ if (stat.mtimeMs < cutoff) {
76
+ fs.unlinkSync(filePath);
77
+ }
78
+ }
79
+ }
80
+ catch { /* non-fatal */ }
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Public API
84
+ // ---------------------------------------------------------------------------
85
+ /**
86
+ * Enable daily-rotated file logging. All loggers created via createLogger()
87
+ * will also write to `{dir}/{module}-YYYY-MM-DD.log`.
88
+ * Old log files (>30 days) are cleaned up on enable.
89
+ */
90
+ export function enableFileLogging(dir) {
91
+ const resolved = dir.startsWith("~")
92
+ ? dir.replace("~", process.env.HOME || "")
93
+ : dir;
94
+ fs.mkdirSync(resolved, { recursive: true });
95
+ logDir = resolved;
96
+ currentDate = ""; // force re-open
97
+ currentFd = null;
98
+ cleanupOldLogs();
99
+ }
100
+ /** Flush and close file logging (for graceful shutdown). */
101
+ export function closeFileLogging() {
102
+ if (currentFd !== null) {
103
+ try {
104
+ fs.closeSync(currentFd);
105
+ }
106
+ catch { /* ignore */ }
107
+ currentFd = null;
108
+ }
109
+ logDir = null;
110
+ }
111
+ function ts() {
112
+ return nowBeijing();
113
+ }
114
+ /** Create a logger with a module prefix, e.g. createLogger("mesh-master") */
115
+ export function createLogger(module, opts) {
116
+ const tag = `[${module}]`;
117
+ const stream = opts?.stream ?? "default";
118
+ function emit(level, consoleFn, args) {
119
+ const timestamp = ts();
120
+ const line = `${timestamp} ${level} ${tag} ${format(...args)}`;
121
+ consoleFn(timestamp, level, tag, ...args);
122
+ writeToFile(line);
123
+ }
124
+ // For "stderr" mode, every level writes to process.stderr; otherwise
125
+ // the default mapping is info/log → console.log (stdout), warn → console.warn
126
+ // (stderr), error → console.error (stderr).
127
+ const outFn = stream === "stderr" ? console.error : console.log;
128
+ const warnFn = stream === "stderr" ? console.error : console.warn;
129
+ const errFn = console.error;
130
+ return {
131
+ log: (...args) => emit("INFO", outFn, args),
132
+ info: (...args) => emit("INFO", outFn, args),
133
+ warn: (...args) => emit("WARN", warnFn, args),
134
+ error: (...args) => emit("ERROR", errFn, args),
135
+ };
136
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @-mention extraction.
3
+ *
4
+ * The pattern `/@([\w一-鿿][\w.一-鿿-]*)/g` matches a leading @ followed by an
5
+ * identifier that may contain ASCII word chars, CJK (一-鿿), dots, and hyphens.
6
+ * Extracted to `shared/mention.ts` so the 8+ call sites across master/ws-hub
7
+ * and master/api keep the same matching rules and slicing convention.
8
+ *
9
+ * `extractMentions` returns the names without the leading @. Returns an empty
10
+ * array for falsy / no-match input. No dedup — callers historically consumed
11
+ * the raw match list, so the helper preserves that.
12
+ */
13
+ const MENTION_RE = /@([\w一-鿿][\w.一-鿿-]*)/g;
14
+ export function extractMentions(content) {
15
+ if (!content)
16
+ return [];
17
+ const out = [];
18
+ for (const m of content.matchAll(MENTION_RE)) {
19
+ out.push(m[1]);
20
+ }
21
+ return out;
22
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 判断 socket 远端地址是否本机(loopback)或局域网(可信内网)。
3
+ *
4
+ * 免 token 本机/局域网认证:本机进程或局域网内"我的设备"(iPad / Mac / 开发盒)
5
+ * 的请求/连接被信任,自动绑定本机 agent,跳过 mesh_token 校验。
6
+ *
7
+ * Node 在 IPv4-only 监听上会给出 `127.0.0.1`;
8
+ * 双栈监听上 IPv4 连接会给出 IPv4-mapped 形式 `::ffff:127.0.0.1`。
9
+ * 局域网段:192.168.0.0/16 / 10.0.0.0/8 / 172.16.0.0/12(含 IPv4-mapped 形式)。
10
+ */
11
+ export function isLoopback(addr) {
12
+ if (!addr)
13
+ return false;
14
+ return (addr === "127.0.0.1" ||
15
+ addr === "::1" ||
16
+ addr === "::ffff:127.0.0.1");
17
+ }
18
+ /** 局域网 IP 段(可信内网,与 loopback 一起走免 token 信任) */
19
+ export function isLocalNetwork(addr) {
20
+ if (!addr)
21
+ return false;
22
+ if (isLoopback(addr))
23
+ return true;
24
+ // 去掉 IPv4-mapped 前缀 `::ffff:` 便于匹配
25
+ const v4 = addr.replace(/^::ffff:/, "");
26
+ // 192.168.x.x
27
+ if (/^192\.168\.\d{1,3}\.\d{1,3}$/.test(v4))
28
+ return true;
29
+ // 10.x.x.x
30
+ if (/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v4))
31
+ return true;
32
+ // 172.16-31.x.x
33
+ const m172 = /^172\.(\d{1,3})\.\d{1,3}\.\d{1,3}$/.exec(v4);
34
+ if (m172) {
35
+ const second = parseInt(m172[1], 10);
36
+ if (second >= 16 && second <= 31)
37
+ return true;
38
+ }
39
+ return false;
40
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Parse helpers for untrusted JSON strings.
3
+ *
4
+ * Use `safeJsonParse(s, fallback)` whenever the caller's contract is
5
+ * "parse if valid, otherwise use a default" — replaces the
6
+ * try { x = JSON.parse(s || "{}"); } catch { /* fall back *\/ }
7
+ * boilerplate that appears across the codebase.
8
+ */
9
+ export function safeJsonParse(s, fallback) {
10
+ if (s == null || s === "")
11
+ return fallback;
12
+ try {
13
+ return JSON.parse(s);
14
+ }
15
+ catch {
16
+ return fallback;
17
+ }
18
+ }
@@ -0,0 +1,171 @@
1
+ import { nowBeijing } from "./time.js";
2
+ /**
3
+ * Prompt 组合器 —— 把"喂给 CLI agent 的 prompt"分层组装,每层标数据源。
4
+ *
5
+ * 拼接顺序:rotom-cli → agent-role → group-basic → cwd → task
6
+ * - rotom-cli: rotom CLI 使用规则(`ROTOM_CLI_PROMPT`),所有 agent 一致
7
+ * - agent-role: 来自 `agents.profile` JSON,per-agent
8
+ * - group-basic: 群上下文 + 活跃 issue 列表,群内所有 agent 一致
9
+ * - cwd: 工作目录只读头(如有 cwd)
10
+ * - task: chat 模式 = 用户原消息;issue 模式 = title + description
11
+ *
12
+ * 设计:这是**纯函数**,无 IO。worker 调它,拿到 ComposedPrompt 后:
13
+ * 1. `prompt = composed.final` 喂给 executor
14
+ * 2. `composed.layers` 经由 `a2a_reply_end` / `issue_update` 透传给 master 入库
15
+ * 3. 前端点击消息 → 直接读库渲染,无需重算
16
+ */
17
+ import { ROTOM_CLI_PROMPT, ROTOM_CLI_PROMPT_VERSION } from "./rotom-cli-prompt.js";
18
+ // ── Layer builders ──────────────────────────────────────────────────────
19
+ const SOURCE_ROTOM_CLI = "src/shared/rotom-cli-prompt.ts (constant)";
20
+ const SOURCE_AGENT_PROFILE = "agents.profile JSON (edit via Dashboard 员工介绍)";
21
+ const SOURCE_GROUP_BASIC = "groups + active_issues (runtime, from master enrichGroupConversation)";
22
+ const SOURCE_GROUP_GUIDANCE = "groups.guidance_prompt (edit via Dashboard 群设置 / PATCH /groups/:id)";
23
+ const SOURCE_CWD = "<worker.executor>.resolveIssueCwd(groupId)";
24
+ function buildRotomCliLayer() {
25
+ return { layer: "rotom-cli", content: ROTOM_CLI_PROMPT, source: SOURCE_ROTOM_CLI };
26
+ }
27
+ function buildAgentRoleLayer(profile) {
28
+ if (!profile)
29
+ return null;
30
+ const fields = [
31
+ ["category", profile.category],
32
+ ["position", profile.position],
33
+ ["bio", profile.bio],
34
+ ];
35
+ const present = fields.filter(([, v]) => typeof v === "string" && v.length > 0);
36
+ if (present.length === 0)
37
+ return null;
38
+ const lines = ["[Agent 角色]", ...present.map(([k, v]) => `${k}: ${v}`)];
39
+ return { layer: "agent-role", content: lines.join("\n") + "\n", source: SOURCE_AGENT_PROFILE };
40
+ }
41
+ function buildGroupBasicLayer(group, selfName) {
42
+ if (!group)
43
+ return null;
44
+ const header = `[群消息 context: groupId=${group.id}, groupName="${group.name}", 你自己是="${selfName}"]\n`;
45
+ const issuesBlock = renderActiveIssues(group.activeIssues);
46
+ return {
47
+ layer: "group-basic",
48
+ content: header + issuesBlock,
49
+ source: SOURCE_GROUP_BASIC,
50
+ };
51
+ }
52
+ /** 群级别指导 prompt;空/null 不渲染。在 group-basic 之后、cwd 之前插入。 */
53
+ function buildGroupGuidanceLayer(group) {
54
+ if (!group)
55
+ return null;
56
+ const prompt = group.guidancePrompt;
57
+ if (!prompt || !prompt.trim())
58
+ return null;
59
+ return {
60
+ layer: "group-guidance",
61
+ content: `[群指导] ${prompt.trim()}\n`,
62
+ source: SOURCE_GROUP_GUIDANCE,
63
+ };
64
+ }
65
+ function renderActiveIssues(issues) {
66
+ const n = issues?.length ?? 0;
67
+ const status = n === 0 ? "无" : `${n} 个进行中`;
68
+ return `[当前群活跃 issue] ${status}\n`;
69
+ }
70
+ function buildCwdLayer(cwd, mode, approvalPolicy) {
71
+ if (!cwd)
72
+ return null;
73
+ // 写盘策略单行(详细话术见 SKILL.md 锚点):
74
+ // chat → 只读,仅 Read/Grep/Glob/Bash(只读)
75
+ // rw_allow → 可写,Write/Edit/写 Bash 自动放行,无需 dashboard
76
+ // r_allow → 可写,但写盘需 dashboard Accept/Deny;要免审批用 --approval-policy rw_allow
77
+ const effectivePolicy = approvalPolicy ?? "rw_allow";
78
+ const writability = mode === "chat"
79
+ ? `模式: chat(只读)。仅可 Read/Grep/Glob/Bash(只读);要写盘见 SKILL.md#写盘兜底话术。\n`
80
+ : effectivePolicy === "rw_allow"
81
+ ? `模式: ${mode},可写(rw_allow)。Write/Edit/写 Bash 自动放行,无需 dashboard 确认;只写本任务相关产出。\n`
82
+ : `模式: ${mode},可写(r_allow)。写盘类(Write/Edit/写 Bash)会被挂起等 dashboard Accept/Deny;只读 Bash(ls/cat/grep/git status/git diff/git log/rotom status 等)自动放行不打扰;要完全免审批用 --approval-policy rw_allow。\n`;
83
+ return {
84
+ layer: "cwd",
85
+ content: `[artifacts目录] ${cwd}\n` +
86
+ `相对路径基于此目录解析;Read/Grep/Glob 用相对路径即可,不要 \`cd\` 切到其他目录。\n` +
87
+ writability,
88
+ source: SOURCE_CWD,
89
+ };
90
+ }
91
+ function buildTaskLayer(body, mode, fromName) {
92
+ let source;
93
+ let content;
94
+ switch (mode) {
95
+ case "chat":
96
+ source = "user message (GroupChatView.handleSendMessage → ws a2a_send.payload.message)";
97
+ content = fromName ? `[from=${fromName}]\n${body}` : body;
98
+ break;
99
+ case "issue":
100
+ source = "issues.title + issues.description (db.ts:executeIssue)";
101
+ content = body;
102
+ break;
103
+ }
104
+ return { layer: "task", content, source };
105
+ }
106
+ /** 记忆极简指针层:只一行,告诉 agent 有多少记忆可查 + 怎么查。不展开索引,不干扰核心任务。 */
107
+ function buildMemoryPointerLayer(counts) {
108
+ const total = counts.group + counts.global;
109
+ if (total === 0)
110
+ return null;
111
+ return {
112
+ layer: "memory-pointer",
113
+ content: `[可用记忆] 群 ${counts.group} 条 / 全局 ${counts.global} 条。` +
114
+ `需要时用 \`rotom memory search <关键词>\` 查询(支持 key/value/summary/tags),` +
115
+ `\`rotom memory get <id>\` 看详情;无关记忆忽略,不要硬套。\n`,
116
+ source: "agent_memory count (runtime, agent_visible=1, active, non-pending)",
117
+ };
118
+ }
119
+ /** skill 极简指针层:只一行,告诉 agent 有多少可用 skill + 怎么查。count=0 不注入。 */
120
+ function buildSkillPointerLayer(count, groupId) {
121
+ if (count === 0)
122
+ return null;
123
+ const gidHint = groupId ? ` ${groupId}` : "";
124
+ return {
125
+ layer: "skill-pointer",
126
+ content: `[可用技能] ${count} 个。用 \`rotom skill mine${gidHint}\` 查列表,` +
127
+ `\`rotom skill get <name>\` 看详情;无关技能忽略,不要硬套。\n`,
128
+ source: "agent_skill_bindings count (runtime, group+agent scoped)",
129
+ };
130
+ }
131
+ // ── Public API ──────────────────────────────────────────────────────────
132
+ export function composePrompt(ctx) {
133
+ const layers = [];
134
+ // Issue 模式的 prompt 不含 rotom CLI 使用规则:agent 直接执行任务描述,
135
+ // 不需要 rotom CLI 语法心智负担。该规则对群聊(chat)仍保留。
136
+ if (ctx.mode !== "issue") {
137
+ layers.push(buildRotomCliLayer());
138
+ }
139
+ const role = buildAgentRoleLayer(ctx.agentProfile);
140
+ if (role)
141
+ layers.push(role);
142
+ const group = buildGroupBasicLayer(ctx.group, ctx.agentName);
143
+ if (group)
144
+ layers.push(group);
145
+ const guidance = buildGroupGuidanceLayer(ctx.group);
146
+ if (guidance)
147
+ layers.push(guidance);
148
+ const cwd = buildCwdLayer(ctx.cwd, ctx.mode, ctx.approvalPolicy);
149
+ if (cwd)
150
+ layers.push(cwd);
151
+ // chat 模式下,发信人 fromName 注入到 task 层头部(只有 chat 才有 fromName 语义)。
152
+ layers.push(buildTaskLayer(ctx.body, ctx.mode, ctx.mode === "chat" ? ctx.fromName : null));
153
+ // 记忆极简指针层(末尾,优先级低,不干扰核心任务)
154
+ if (ctx.group?.memoryCounts) {
155
+ const ptr = buildMemoryPointerLayer(ctx.group.memoryCounts);
156
+ if (ptr)
157
+ layers.push(ptr);
158
+ }
159
+ // skill 极简指针层(最末尾,优先级最低)
160
+ if (ctx.group?.skillCount !== undefined) {
161
+ const ptr = buildSkillPointerLayer(ctx.group.skillCount, ctx.group?.id);
162
+ if (ptr)
163
+ layers.push(ptr);
164
+ }
165
+ return {
166
+ layers,
167
+ final: layers.map((l) => l.content).join("\n"),
168
+ generatedAt: nowBeijing(),
169
+ promptVersion: ROTOM_CLI_PROMPT_VERSION,
170
+ };
171
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Client → Master message definitions.
3
+ *
4
+ * Includes the `ClientMessage` union plus every concrete ClientXxxMessage
5
+ * shape. Consumers can import just this module to type incoming WS frames
6
+ * without pulling in server-side definitions.
7
+ */
8
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Real-person enum: human team members who can own issues / approve.
3
+ * Split from the protocol god-file so consumers needing only the enum don't
4
+ * pull in the full message union.
5
+ */
6
+ export const REAL_PERSONS = ["孔令飞"];
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Federation 协议 —— master 与 master 之间的通信。
3
+ *
4
+ * 拓扑(Phase 2):星型,所有跨 master 消息经协调 master 中转。
5
+ * member ←→ coordination ←→ member
6
+ *
7
+ * 传输:WebSocket(ws://coord-host:port/federation),文本帧 JSON,最大 1MB。
8
+ * 认证:MVP 免认证,假设内网可信;但握手强制声明 masterId/hostname,
9
+ * 协调侧持久化来源便于审计 audit_log。
10
+ *
11
+ * 路由键:masterId(8 字符 base36,持久化在 ~/.rotom/master.json,永远稳定)。
12
+ * 显示键:hostname(可改,仅作展示,形如 alice@hostA)。
13
+ *
14
+ * 消息类型(8 个):
15
+ * ─ 握手 ─
16
+ * FedHandshake member → coord,声明身份
17
+ * FedHandshakeAck coord → member,确认接入 / 报错(HOSTNAME_CONFLICT 等)
18
+ * ─ 可见性发布 ─
19
+ * FedAgentPublish member → coord,增量发布本地 agent 状态
20
+ * FedAgentUnpublish member → coord,撤销发布(离开部门时)
21
+ * FedDirectorySync coord → member,全量/增量同步 agent_visibility
22
+ * ─ 消息路由 ─
23
+ * FedRouteMessage member → coord,请求跨机投递
24
+ * FedRouteDeliver coord → member(目标侧),投递消息
25
+ * FedRouteReply member → coord → 来源 member,回复消息
26
+ */
27
+ /** masterId 校验:8 字符 base36 小写 */
28
+ export const MASTER_ID_PATTERN = /^[0-9a-z]{8}$/;
29
+ /** hostname 校验:非空、非 IP(详见 src/master/federation/identity.ts isValidHostname) */
30
+ export const HOSTNAME_PATTERN = /^[^\s:]+$/;
31
+ /** 协议版本 — bump when fed wire format changes */
32
+ export const FED_PROTOCOL_VERSION = 1;
33
+ const FED_MESSAGE_TYPES = new Set([
34
+ "fed_handshake",
35
+ "fed_handshake_ack",
36
+ "fed_agent_publish",
37
+ "fed_agent_unpublish",
38
+ "fed_directory_sync",
39
+ "fed_route",
40
+ "fed_deliver",
41
+ "fed_reply",
42
+ "fed_route_failed",
43
+ ]);
44
+ export function isFedMessage(x) {
45
+ if (!x || typeof x !== "object")
46
+ return false;
47
+ const msg = x;
48
+ if (typeof msg.type !== "string")
49
+ return false;
50
+ return FED_MESSAGE_TYPES.has(msg.type);
51
+ }
52
+ /** 解析 "alice@hostA" 形式;无 @ 则只返回 name */
53
+ export function parseAgentRef(ref) {
54
+ const at = ref.lastIndexOf("@");
55
+ if (at <= 0)
56
+ return { name: ref };
57
+ return { name: ref.slice(0, at), hostname: ref.slice(at + 1) };
58
+ }
59
+ /** 组装 "alice@hostA" 形式 */
60
+ export function formatAgentRef(name, hostname) {
61
+ return `${name}@${hostname}`;
62
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Runtime type guards for the mesh protocol.
3
+ *
4
+ * `isClientMessage` validates external WS input at the auth boundary (master
5
+ * connection handler). `isServerMessage` is provided for symmetry — workers
6
+ * that want to validate incoming master frames can use it.
7
+ */
8
+ export function isClientMessage(x) {
9
+ if (!x || typeof x !== "object")
10
+ return false;
11
+ const msg = x;
12
+ if (typeof msg.type !== "string")
13
+ return false;
14
+ switch (msg.type) {
15
+ case "auth":
16
+ // token 在 OPC 本机模式下可空(master 端 isLoopback 信任直通);
17
+ // 跨机连接远程 master 时由 authenticate() 校验。
18
+ return typeof msg.name === "string"
19
+ && (msg.token === undefined || msg.token === null || typeof msg.token === "string");
20
+ case "heartbeat":
21
+ return true;
22
+ case "a2a_send":
23
+ return typeof msg.requestId === "string" && !!msg.payload;
24
+ case "a2a_reply":
25
+ return typeof msg.requestId === "string" && !!msg.payload;
26
+ case "a2a_reply_chunk":
27
+ return typeof msg.requestId === "string" && typeof msg.delta === "string";
28
+ case "a2a_reply_end":
29
+ return typeof msg.requestId === "string" && !!msg.payload;
30
+ case "update_info":
31
+ return true;
32
+ case "disconnect":
33
+ return true;
34
+ case "issue_update":
35
+ return typeof msg.issueId === "string" && typeof msg.status === "string";
36
+ case "issue_todos_update":
37
+ return typeof msg.issueId === "string" && Array.isArray(msg.todos);
38
+ case "issue_usage_progress":
39
+ return typeof msg.issueId === "string" && !!msg.usage;
40
+ case "subscribe_issue_detail":
41
+ return typeof msg.issueId === "string";
42
+ case "unsubscribe_issue_detail":
43
+ return typeof msg.issueId === "string";
44
+ case "issue_approval_request":
45
+ return typeof msg.issueId === "string"
46
+ && typeof msg.approvalId === "string"
47
+ && (msg.kind === "exec" || msg.kind === "file_change" || msg.kind === "plan" || msg.kind === "ask")
48
+ && typeof msg.summary === "string";
49
+ case "session_view_response":
50
+ return typeof msg.requestId === "string"
51
+ && typeof msg.groupId === "string"
52
+ && typeof msg.sessionId === "string"
53
+ && (msg.format === "jsonl" || msg.format === "text" || msg.format === "raw")
54
+ && typeof msg.content === "string";
55
+ case "session_delete_response":
56
+ return typeof msg.requestId === "string"
57
+ && typeof msg.groupId === "string"
58
+ && typeof msg.sessionId === "string"
59
+ && typeof msg.ok === "boolean";
60
+ case "session_snapshot":
61
+ return Array.isArray(msg.entries);
62
+ case "session_invalidated":
63
+ return typeof msg.cliTool === "string"
64
+ && typeof msg.groupId === "string"
65
+ && typeof msg.sessionId === "string";
66
+ default:
67
+ return false;
68
+ }
69
+ }
70
+ const SERVER_MESSAGE_TYPES = new Set([
71
+ "auth_ok", "auth_fail", "heartbeat_ack",
72
+ "a2a_message", "route_result", "directory_update", "offline_messages",
73
+ "update_info_ack", "config_update", "a2a_stream_chunk", "a2a_stream_end",
74
+ "issue_created", "issue_assigned", "issue_update_ack",
75
+ "issue_approval_response", "issue_cancelled", "chat_cancelled",
76
+ "issue_changed", "issue_continue", "issue_append", "issue_interrupt",
77
+ "issue_usage_progress",
78
+ "session_view_request", "session_delete_request", "session_sync_push",
79
+ ]);
80
+ export function isServerMessage(x) {
81
+ if (!x || typeof x !== "object")
82
+ return false;
83
+ const msg = x;
84
+ if (typeof msg.type !== "string")
85
+ return false;
86
+ return SERVER_MESSAGE_TYPES.has(msg.type);
87
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Master → Client message definitions.
3
+ *
4
+ * Includes the `ServerMessage` union plus every concrete ServerXxxMessage
5
+ * shape. Consumers can import just this module to type outgoing WS frames
6
+ * without pulling in client-side definitions.
7
+ */
8
+ export {};
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Base value interfaces used across the mesh protocol — agent info, file
3
+ * references, conversation context, message payload, todos, token usage,
4
+ * session entries. Pulled out of the protocol god-file so master DB rows,
5
+ * dashboard types, and executor code can share these without dragging in
6
+ * the full ClientMessage/ServerMessage unions.
7
+ */
8
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Digital Employee Mesh — Protocol definitions
3
+ *
4
+ * All WebSocket message types between Agent and Master.
5
+ *
6
+ * This file is a barrel re-exporting from `./protocol/` submodules so
7
+ * existing `import { ... } from "../shared/protocol.js"` call sites keep
8
+ * working. New code may import from the specific sub-path:
9
+ * - "../shared/protocol/enums.js" — REAL_PERSONS, RealPerson
10
+ * - "../shared/protocol/types.js" — base value interfaces
11
+ * - "../shared/protocol/client-messages.js" — ClientMessage union + shapes
12
+ * - "../shared/protocol/server-messages.js" — ServerMessage union + shapes
13
+ * - "../shared/protocol/guards.js" — isClientMessage / isServerMessage
14
+ */
15
+ export * from "./protocol/enums.js";
16
+ export * from "./protocol/types.js";
17
+ export * from "./protocol/client-messages.js";
18
+ export * from "./protocol/server-messages.js";
19
+ export { isClientMessage, isServerMessage } from "./protocol/guards.js";