evolclaw 3.1.4 → 3.1.5
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/CHANGELOG.md +10 -0
- package/dist/agents/claude-runner.js +348 -156
- package/dist/agents/kit-renderer.js +176 -21
- package/dist/aun/aid/agentmd.js +68 -103
- package/dist/aun/aid/client.js +1 -29
- package/dist/aun/aid/identity.js +105 -64
- package/dist/aun/aid/index.js +2 -1
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/msg/p2p.js +26 -2
- package/dist/aun/rpc/connection.js +23 -30
- package/dist/channels/aun.js +77 -88
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +270 -190
- package/dist/channels/qqbot.js +1 -0
- package/dist/channels/wechat.js +1 -0
- package/dist/channels/wecom.js +1 -0
- package/dist/cli/agent.js +11 -5
- package/dist/cli/bench.js +40 -23
- package/dist/cli/index.js +170 -44
- package/dist/cli/init-channel.js +5 -1
- package/dist/cli/model.js +324 -0
- package/dist/cli/net-check.js +133 -50
- package/dist/cli/watch-msg.js +7 -7
- package/dist/cli/watch-web/debug-log.js +18 -0
- package/dist/cli/watch-web/server.js +306 -0
- package/dist/cli/watch-web/sources/aid.js +63 -0
- package/dist/cli/watch-web/sources/msg.js +70 -0
- package/dist/cli/watch-web/sources/session.js +638 -0
- package/dist/cli/watch-web/sources/types.js +10 -0
- package/dist/cli/watch-web/static/app.js +546 -0
- package/dist/cli/watch-web/static/index.html +54 -0
- package/dist/cli/watch-web/static/style.css +247 -0
- package/dist/core/channel-loader.js +7 -4
- package/dist/core/command-handler.js +81 -86
- package/dist/core/evolagent-registry.js +1 -1
- package/dist/core/evolagent.js +4 -4
- package/dist/core/interaction-router.js +59 -0
- package/dist/core/message/message-bridge.js +6 -6
- package/dist/core/message/message-log.js +2 -2
- package/dist/core/message/message-processor.js +86 -101
- package/dist/core/message/stream-idle-monitor.js +21 -0
- package/dist/core/model/model-catalog.js +215 -0
- package/dist/core/model/model-scope.js +250 -0
- package/dist/core/relation/peer-identity.js +40 -49
- package/dist/core/relation/peer-key.js +16 -0
- package/dist/core/session/session-fs-store.js +34 -55
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +308 -251
- package/dist/core/session/session-mapper.js +9 -4
- package/dist/core/trigger/manager.js +3 -3
- package/dist/core/trigger/scheduler.js +2 -1
- package/dist/index.js +6 -2
- package/dist/ipc.js +22 -0
- package/kits/docs/GUIDE.md +2 -2
- package/kits/docs/INDEX.md +11 -7
- package/kits/docs/channels/aun.md +56 -17
- package/kits/docs/channels/feishu.md +41 -12
- package/kits/docs/context-assembly.md +181 -0
- package/kits/docs/evolclaw/agent.md +49 -0
- package/kits/docs/evolclaw/aid.md +49 -0
- package/kits/docs/evolclaw/ctl.md +46 -0
- package/kits/docs/evolclaw/group.md +82 -0
- package/kits/docs/evolclaw/msg.md +86 -0
- package/kits/docs/evolclaw/rpc.md +35 -0
- package/kits/docs/evolclaw/storage.md +49 -0
- package/kits/docs/venues/aun-group.md +10 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +11 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.json +72 -36
- package/kits/rules/01-overview.md +20 -10
- package/kits/rules/06-channel.md +30 -27
- package/kits/templates/system-fragments/session.md +10 -3
- package/kits/templates/system-fragments/venue.md +9 -0
- package/package.json +11 -6
- package/dist/aun/aid/lifecycle-log.js +0 -33
- package/dist/utils/aid-lifecycle-log.js +0 -33
- package/kits/docs/evolclaw/AGENT_CMD.md +0 -31
- package/kits/docs/evolclaw/MSG_GROUP.md +0 -30
- package/kits/docs/evolclaw/MSG_PRIVATE.md +0 -72
- package/kits/docs/evolclaw/tools.md +0 -25
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: #0d1117;
|
|
3
|
+
--bg2: #161b22;
|
|
4
|
+
--bg3: #21262d;
|
|
5
|
+
--border: #30363d;
|
|
6
|
+
--fg: #e6edf3;
|
|
7
|
+
--dim: #8b949e;
|
|
8
|
+
--accent: #58a6ff;
|
|
9
|
+
--green: #3fb950;
|
|
10
|
+
--red: #f85149;
|
|
11
|
+
--orange: #db8e3c;
|
|
12
|
+
--blue: #58a6ff;
|
|
13
|
+
--magenta: #bc8cff;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", monospace;
|
|
20
|
+
background: var(--bg);
|
|
21
|
+
color: var(--fg);
|
|
22
|
+
font-size: 13px;
|
|
23
|
+
height: 100vh;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* ── 配对页 ── */
|
|
28
|
+
.pair-page {
|
|
29
|
+
display: flex; align-items: center; justify-content: center;
|
|
30
|
+
height: 100vh;
|
|
31
|
+
}
|
|
32
|
+
.pair-box {
|
|
33
|
+
background: var(--bg2);
|
|
34
|
+
border: 1px solid var(--border);
|
|
35
|
+
border-radius: 12px;
|
|
36
|
+
padding: 40px 48px;
|
|
37
|
+
text-align: center;
|
|
38
|
+
min-width: 320px;
|
|
39
|
+
}
|
|
40
|
+
.pair-box h1 { font-size: 22px; margin-bottom: 8px; }
|
|
41
|
+
.pair-hint { color: var(--dim); margin-bottom: 24px; }
|
|
42
|
+
.pair-box input {
|
|
43
|
+
width: 100%;
|
|
44
|
+
font-size: 28px;
|
|
45
|
+
letter-spacing: 12px;
|
|
46
|
+
text-align: center;
|
|
47
|
+
padding: 12px;
|
|
48
|
+
background: var(--bg);
|
|
49
|
+
border: 1px solid var(--border);
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
color: var(--fg);
|
|
52
|
+
margin-bottom: 16px;
|
|
53
|
+
}
|
|
54
|
+
.pair-box input:focus { outline: none; border-color: var(--accent); }
|
|
55
|
+
.pair-box button {
|
|
56
|
+
width: 100%;
|
|
57
|
+
padding: 12px;
|
|
58
|
+
font-size: 15px;
|
|
59
|
+
background: var(--accent);
|
|
60
|
+
color: #fff;
|
|
61
|
+
border: none;
|
|
62
|
+
border-radius: 8px;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
}
|
|
65
|
+
.pair-box button:hover { opacity: 0.9; }
|
|
66
|
+
.pair-error { color: var(--red); margin-top: 12px; min-height: 18px; }
|
|
67
|
+
|
|
68
|
+
/* ── 主面板布局 ── */
|
|
69
|
+
.app { display: flex; flex-direction: column; height: 100vh; }
|
|
70
|
+
.topbar {
|
|
71
|
+
display: flex; align-items: center; gap: 24px;
|
|
72
|
+
padding: 0 16px; height: 44px;
|
|
73
|
+
background: var(--bg2);
|
|
74
|
+
border-bottom: 1px solid var(--border);
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
}
|
|
77
|
+
.brand { font-weight: 600; }
|
|
78
|
+
.tabs { display: flex; gap: 4px; }
|
|
79
|
+
.tab {
|
|
80
|
+
background: none; border: none; color: var(--dim);
|
|
81
|
+
padding: 6px 14px; cursor: pointer; border-radius: 6px;
|
|
82
|
+
font-size: 13px; font-family: inherit;
|
|
83
|
+
}
|
|
84
|
+
.tab:hover { color: var(--fg); background: var(--bg3); }
|
|
85
|
+
.tab.active { color: var(--accent); background: var(--bg3); }
|
|
86
|
+
.conn-status { margin-left: auto; color: var(--dim); font-size: 12px; }
|
|
87
|
+
.conn-status.ok { color: var(--green); }
|
|
88
|
+
.conn-status.err { color: var(--red); }
|
|
89
|
+
|
|
90
|
+
.content { flex: 1; overflow: hidden; position: relative; }
|
|
91
|
+
.view { display: none; height: 100%; overflow: auto; }
|
|
92
|
+
.view.active { display: block; }
|
|
93
|
+
|
|
94
|
+
/* ── AID 表格 ── */
|
|
95
|
+
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
96
|
+
th, td { text-align: left; padding: 7px 12px; border-bottom: 1px solid var(--border); white-space: nowrap; }
|
|
97
|
+
th { color: var(--dim); font-weight: 500; position: sticky; top: 0; background: var(--bg2); }
|
|
98
|
+
tr:hover td { background: var(--bg2); }
|
|
99
|
+
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
|
|
100
|
+
.dot.on { background: var(--green); }
|
|
101
|
+
.dot.off { background: var(--red); }
|
|
102
|
+
.dot.idle { background: var(--orange); }
|
|
103
|
+
.preview { color: var(--dim); max-width: 360px; overflow: hidden; text-overflow: ellipsis; }
|
|
104
|
+
.banner { padding: 10px 16px; background: #3d2a1a; color: var(--orange); border-bottom: 1px solid var(--border); }
|
|
105
|
+
.empty { padding: 40px; text-align: center; color: var(--dim); }
|
|
106
|
+
|
|
107
|
+
/* ── Messages 三栏 ── */
|
|
108
|
+
.msg-layout, .sess-layout { display: flex; height: 100%; }
|
|
109
|
+
.msg-col { overflow-y: auto; height: 100%; }
|
|
110
|
+
.msg-aids { width: 220px; border-right: 1px solid var(--border); flex-shrink: 0; }
|
|
111
|
+
.msg-peers { width: 240px; border-right: 1px solid var(--border); flex-shrink: 0; }
|
|
112
|
+
.msg-stream { flex: 1; padding: 12px; }
|
|
113
|
+
.list-item {
|
|
114
|
+
padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border);
|
|
115
|
+
}
|
|
116
|
+
.list-item:hover { background: var(--bg2); }
|
|
117
|
+
.list-item.sel { background: var(--bg3); border-left: 2px solid var(--accent); }
|
|
118
|
+
.list-item .name { font-weight: 500; }
|
|
119
|
+
.list-item .sub { color: var(--dim); font-size: 11px; margin-top: 2px; }
|
|
120
|
+
.col-title { padding: 8px 12px; color: var(--dim); font-size: 11px; text-transform: uppercase; position: sticky; top: 0; background: var(--bg); border-bottom: 1px solid var(--border); }
|
|
121
|
+
|
|
122
|
+
.bubble { margin-bottom: 12px; max-width: 80%; }
|
|
123
|
+
.bubble.in { margin-right: auto; }
|
|
124
|
+
.bubble.out { margin-left: auto; }
|
|
125
|
+
.bubble .meta { font-size: 11px; color: var(--dim); margin-bottom: 3px; }
|
|
126
|
+
.bubble .body {
|
|
127
|
+
padding: 8px 10px; border-radius: 8px; white-space: pre-wrap; word-break: break-word;
|
|
128
|
+
background: var(--bg2); border: 1px solid var(--border);
|
|
129
|
+
}
|
|
130
|
+
.bubble.in .body { border-left: 2px solid var(--green); }
|
|
131
|
+
.bubble.out .body { border-left: 2px solid var(--blue); }
|
|
132
|
+
.tag { display: inline-block; padding: 0 5px; border-radius: 3px; font-size: 10px; margin-left: 4px; background: var(--bg3); color: var(--magenta); }
|
|
133
|
+
|
|
134
|
+
/* ── Sessions ── */
|
|
135
|
+
.sess-list { width: 360px; border-right: 1px solid var(--border); flex-shrink: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
136
|
+
.sess-filter { padding: 10px; border-bottom: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px; flex-shrink: 0; }
|
|
137
|
+
.sess-filter select, .sess-filter input {
|
|
138
|
+
width: 100%; padding: 6px 8px; background: var(--bg); color: var(--fg);
|
|
139
|
+
border: 1px solid var(--border); border-radius: 6px; font-size: 12px; font-family: inherit;
|
|
140
|
+
}
|
|
141
|
+
.sess-filter select:focus, .sess-filter input:focus { outline: none; border-color: var(--accent); }
|
|
142
|
+
.sess-count { font-size: 11px; color: var(--dim); }
|
|
143
|
+
.sess-items { flex: 1; overflow-y: auto; }
|
|
144
|
+
|
|
145
|
+
.sess-items .list-item .name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
146
|
+
.sess-items .list-item .sub { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
|
147
|
+
.bind-badge { display: inline-flex; align-items: center; gap: 3px; padding: 0 5px; border-radius: 3px; background: var(--bg3); color: var(--accent); font-size: 10px; }
|
|
148
|
+
.bind-badge .dot { width: 6px; height: 6px; margin: 0; }
|
|
149
|
+
.msg-count { color: var(--dim); }
|
|
150
|
+
|
|
151
|
+
.sess-detail { flex: 1; padding: 0; overflow-y: auto; }
|
|
152
|
+
|
|
153
|
+
/* 会话头 */
|
|
154
|
+
.sess-header { position: sticky; top: 0; background: var(--bg2); border-bottom: 1px solid var(--border); padding: 12px 20px; z-index: 1; }
|
|
155
|
+
.sh-title { font-size: 15px; font-weight: 600; margin-bottom: 8px; }
|
|
156
|
+
.sh-stats { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: var(--dim); }
|
|
157
|
+
.sh-stat { display: inline-flex; align-items: center; gap: 4px; }
|
|
158
|
+
.sh-stat .dot { width: 7px; height: 7px; margin: 0 2px; }
|
|
159
|
+
.sh-path { font-size: 11px; color: var(--dim); margin-top: 6px; font-family: ui-monospace, Consolas, monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
160
|
+
|
|
161
|
+
/* 顶部 4 类分类条 */
|
|
162
|
+
.sh-cats { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
|
|
163
|
+
.cat-chip { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 10px; font-size: 11px; background: var(--bg3); }
|
|
164
|
+
.cat-swatch { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
165
|
+
.cat-chip.cat-user_input .cat-swatch { background: var(--green); }
|
|
166
|
+
.cat-chip.cat-model_output .cat-swatch { background: var(--blue); }
|
|
167
|
+
.cat-chip.cat-tool_call .cat-swatch { background: var(--magenta); }
|
|
168
|
+
.cat-chip.cat-tool_result .cat-swatch { background: var(--orange); }
|
|
169
|
+
.cat-chip.cat-msg_send .cat-swatch { background: var(--cyan, #22d3a8); }
|
|
170
|
+
|
|
171
|
+
/* 视图切换工具条 */
|
|
172
|
+
.sess-toolbar { display: flex; align-items: center; gap: 12px; padding: 8px 20px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--bg); z-index: 1; }
|
|
173
|
+
.view-toggle { padding: 4px 12px; border-radius: 14px; border: 1px solid var(--border); background: var(--bg3); color: var(--fg); cursor: pointer; font-size: 12px; font-family: inherit; }
|
|
174
|
+
.view-toggle.active { background: var(--accent); border-color: var(--accent); color: #fff; }
|
|
175
|
+
.toolbar-hint { font-size: 11px; color: var(--dim); }
|
|
176
|
+
.turn-list { padding-top: 8px; }
|
|
177
|
+
|
|
178
|
+
/* 对话视图:仿微信气泡 */
|
|
179
|
+
.chat-row { display: flex; flex-direction: column; margin: 10px 20px; max-width: 72%; }
|
|
180
|
+
.chat-row.in { align-items: flex-start; margin-right: auto; }
|
|
181
|
+
.chat-row.out { align-items: flex-end; margin-left: auto; }
|
|
182
|
+
.chat-bubble { padding: 9px 13px; border-radius: 12px; white-space: pre-wrap; word-break: break-word; line-height: 1.5; font-size: 13px; }
|
|
183
|
+
.chat-row.in .chat-bubble { background: var(--bg3); border-bottom-left-radius: 3px; }
|
|
184
|
+
.chat-row.out .chat-bubble { background: #2a6b3f; color: #eafff0; border-bottom-right-radius: 3px; }
|
|
185
|
+
.chat-time { font-size: 10px; color: var(--dim); margin-top: 3px; }
|
|
186
|
+
|
|
187
|
+
/* 处理过程折叠组 */
|
|
188
|
+
.proc-group { margin: 8px 20px; }
|
|
189
|
+
.proc-group > summary { cursor: pointer; color: var(--dim); font-size: 11px; padding: 4px 0; text-align: center; list-style: none; }
|
|
190
|
+
.proc-group > summary::-webkit-details-marker { display: none; }
|
|
191
|
+
.proc-group > summary:hover { color: var(--fg); }
|
|
192
|
+
.proc-body { border-left: 2px dashed var(--border); margin-left: 8px; padding-left: 4px; }
|
|
193
|
+
.proc-body .turn { margin: 8px 12px; }
|
|
194
|
+
|
|
195
|
+
/* 轮次:按 4 类着色(左色条 + 角色标签色)*/
|
|
196
|
+
.turn { margin: 0 20px 18px; border-left: 3px solid var(--border); padding-left: 12px; }
|
|
197
|
+
.turn:first-of-type { margin-top: 16px; }
|
|
198
|
+
.turn.cat-user_input { border-left-color: var(--green); }
|
|
199
|
+
.turn.cat-model_output { border-left-color: var(--blue); }
|
|
200
|
+
.turn.cat-tool_call { border-left-color: var(--magenta); }
|
|
201
|
+
.turn.cat-tool_result { border-left-color: var(--orange); }
|
|
202
|
+
.turn.cat-system { border-left-color: var(--dim); }
|
|
203
|
+
|
|
204
|
+
.turn-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; }
|
|
205
|
+
.turn-role { font-weight: 600; font-size: 12px; }
|
|
206
|
+
.turn.cat-user_input .turn-role { color: var(--green); }
|
|
207
|
+
.turn.cat-model_output .turn-role { color: var(--blue); }
|
|
208
|
+
.turn.cat-tool_call .turn-role { color: var(--magenta); }
|
|
209
|
+
.turn.cat-tool_result .turn-role { color: var(--orange); }
|
|
210
|
+
.turn.cat-system .turn-role { color: var(--dim); }
|
|
211
|
+
.turn-time { font-size: 11px; color: var(--dim); }
|
|
212
|
+
.turn-usage { font-size: 11px; color: var(--dim); margin-left: auto; }
|
|
213
|
+
.turn-blocks { display: flex; flex-direction: column; gap: 8px; }
|
|
214
|
+
|
|
215
|
+
/* 普通文本块 */
|
|
216
|
+
.blk-text { white-space: pre-wrap; word-break: break-word; line-height: 1.55; }
|
|
217
|
+
|
|
218
|
+
/* 思考块(折叠) */
|
|
219
|
+
.blk-thinking { font-size: 12px; }
|
|
220
|
+
.blk-thinking summary { cursor: pointer; color: var(--magenta); opacity: 0.8; }
|
|
221
|
+
.blk-thinking-body { white-space: pre-wrap; word-break: break-word; color: var(--dim); margin-top: 6px; padding-left: 12px; border-left: 2px solid var(--border); font-style: italic; }
|
|
222
|
+
|
|
223
|
+
/* 工具调用块 */
|
|
224
|
+
.blk-tool { background: var(--bg2); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; }
|
|
225
|
+
.tool-head { font-size: 12px; margin-bottom: 4px; }
|
|
226
|
+
.tool-name { font-weight: 600; color: var(--accent); font-family: ui-monospace, "SF Mono", Consolas, monospace; }
|
|
227
|
+
.tool-param { display: flex; gap: 8px; margin-top: 3px; font-size: 12px; align-items: baseline; }
|
|
228
|
+
.tool-param .pk { color: var(--dim); flex-shrink: 0; min-width: 64px; }
|
|
229
|
+
.tool-param .pv {
|
|
230
|
+
font-family: ui-monospace, "SF Mono", Consolas, monospace; color: var(--fg);
|
|
231
|
+
background: var(--bg); padding: 1px 6px; border-radius: 4px; word-break: break-all;
|
|
232
|
+
white-space: pre-wrap; display: block; flex: 1;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* 工具结果块(折叠,默认收起) */
|
|
236
|
+
.blk-result { font-size: 12px; }
|
|
237
|
+
.blk-result summary { cursor: pointer; color: var(--dim); }
|
|
238
|
+
.blk-result.err summary { color: var(--red); }
|
|
239
|
+
.result-body {
|
|
240
|
+
margin-top: 6px; padding: 8px 10px; background: var(--bg); border: 1px solid var(--border);
|
|
241
|
+
border-radius: 6px; white-space: pre-wrap; word-break: break-word; max-height: 280px; overflow-y: auto;
|
|
242
|
+
font-family: ui-monospace, "SF Mono", Consolas, monospace; font-size: 11.5px; line-height: 1.5;
|
|
243
|
+
}
|
|
244
|
+
.blk-result.err .result-body { border-color: var(--red); }
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
@@ -128,18 +128,21 @@ export class ChannelLoader {
|
|
|
128
128
|
}
|
|
129
129
|
const SEP = '#';
|
|
130
130
|
export function formatChannelKey(k) {
|
|
131
|
-
|
|
131
|
+
if (k.selfAID.includes(SEP)) {
|
|
132
|
+
throw new Error(`Invalid selfAID (contains '#'): ${k.selfAID}`);
|
|
133
|
+
}
|
|
134
|
+
return `${k.type}${SEP}${k.selfAID}${SEP}${k.name}`;
|
|
132
135
|
}
|
|
133
136
|
export function parseChannelKey(key) {
|
|
134
137
|
const parts = key.split(SEP);
|
|
135
138
|
if (parts.length !== 3) {
|
|
136
139
|
throw new Error(`Invalid channel key (expected 3 segments separated by '#'): ${key}`);
|
|
137
140
|
}
|
|
138
|
-
const [type,
|
|
139
|
-
if (!type || !
|
|
141
|
+
const [type, selfAID, name] = parts;
|
|
142
|
+
if (!type || !selfAID || !name) {
|
|
140
143
|
throw new Error(`Invalid channel key (empty segment): ${key}`);
|
|
141
144
|
}
|
|
142
|
-
return { type,
|
|
145
|
+
return { type, selfAID, name };
|
|
143
146
|
}
|
|
144
147
|
export function tryParseChannelKey(key) {
|
|
145
148
|
try {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_PERMISSION_MODE } from '../types.js';
|
|
2
2
|
import { hasModelSwitcher, hasPermissionController } from '../agents/claude-runner.js';
|
|
3
3
|
import { getCodexEfforts } from '../agents/codex-runner.js';
|
|
4
|
-
import { resolveAnthropicConfig, resolveOpenaiConfig } from '../agents/resolve.js';
|
|
5
4
|
import { renderCommandCardAsText } from './interaction-router.js';
|
|
6
5
|
import { buildEnvelope, sendInteractionPayload } from './message/message-processor.js';
|
|
7
6
|
import { resolvePaths, getPackageRoot } from '../paths.js';
|
|
@@ -27,33 +26,13 @@ function getAvailableEfforts(agent, model) {
|
|
|
27
26
|
function formatModelUsage(_agent, _model) {
|
|
28
27
|
return '用法: /model <模型>';
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch { }
|
|
39
|
-
return {
|
|
40
|
-
apiBaseUrl: resolved.baseUrl,
|
|
41
|
-
apiKey: resolved.apiKey,
|
|
42
|
-
fallbackModels: agent.listModels?.() || [],
|
|
43
|
-
owner: 'openai',
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
let resolved = {};
|
|
47
|
-
try {
|
|
48
|
-
resolved = resolveAnthropicConfig({ agents: { claude: claudeConfig } }, claudeConfig);
|
|
49
|
-
}
|
|
50
|
-
catch { }
|
|
51
|
-
return {
|
|
52
|
-
apiBaseUrl: resolved.baseUrl,
|
|
53
|
-
apiKey: resolved.apiKey,
|
|
54
|
-
fallbackModels: ['claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6'],
|
|
55
|
-
owner: 'anthropic',
|
|
56
|
-
};
|
|
29
|
+
/**
|
|
30
|
+
* 模型展示标签:短别名 + 实际完整 ID(如 "opus (claude-opus-4-8)")。
|
|
31
|
+
* 仅用于展示;命令值/持久化仍使用短别名。完整 ID 不可用或与短名相同时只显示短名。
|
|
32
|
+
*/
|
|
33
|
+
function modelDisplayLabel(agent, model) {
|
|
34
|
+
const full = agent.resolveModelId?.(model);
|
|
35
|
+
return full && full !== model ? `${model} (${full})` : model;
|
|
57
36
|
}
|
|
58
37
|
/**
|
|
59
38
|
* 写入用户级 ~/.claude/settings.json(与 Claude CLI 行为一致)
|
|
@@ -636,7 +615,7 @@ export class CommandHandler {
|
|
|
636
615
|
const models = await agent.listModels() ?? [];
|
|
637
616
|
const currentModel = agent.getModel();
|
|
638
617
|
if (models.length > 0)
|
|
639
|
-
return models.map((m) => ({ value: m, label: m, selected: m === currentModel }));
|
|
618
|
+
return models.map((m) => ({ value: m, label: modelDisplayLabel(agent, m), selected: m === currentModel }));
|
|
640
619
|
}
|
|
641
620
|
return null;
|
|
642
621
|
}
|
|
@@ -1044,7 +1023,7 @@ export class CommandHandler {
|
|
|
1044
1023
|
env: { ...process.env, EVOLCLAW_HOME: resolvePaths().root }
|
|
1045
1024
|
}).unref();
|
|
1046
1025
|
this.eventBus.publish({ type: 'system:restart', channel, channelId });
|
|
1047
|
-
setTimeout(() => { process.kill(process.pid, 'SIGTERM'); },
|
|
1026
|
+
setTimeout(() => { process.kill(process.pid, 'SIGTERM'); }, 1000);
|
|
1048
1027
|
return { data: { action: 'restart', success: true } };
|
|
1049
1028
|
}
|
|
1050
1029
|
if (action === 'check') {
|
|
@@ -1161,12 +1140,12 @@ export class CommandHandler {
|
|
|
1161
1140
|
}
|
|
1162
1141
|
// 空闲检查:某些命令需要等待当前会话空闲
|
|
1163
1142
|
// 原则:仅对"写/破坏性"形态拦截,纯读/用法提示的无参形态始终放行
|
|
1164
|
-
// - 始终需要 idle(无参即写):/
|
|
1143
|
+
// - 始终需要 idle(无参即写):/clear /compact /repair /fork /new
|
|
1165
1144
|
// - 仅带参时需要 idle(无参是列表/用法):/session /baseagent /rewind
|
|
1166
1145
|
// - /chatmode:在 handler 内部自行做写操作的 idle 检查
|
|
1167
1146
|
// - /dispatch:在 handler 内部自行做写操作的 idle 检查
|
|
1168
1147
|
// - /safe:已禁用 no-op,不再要求 idle
|
|
1169
|
-
const idleAlways = ['/
|
|
1148
|
+
const idleAlways = ['/clear', '/compact', '/repair', '/fork', '/new'];
|
|
1170
1149
|
const idleWhenArg = ['/session', '/baseagent', '/rewind'];
|
|
1171
1150
|
const needsIdle = idleAlways.some(cmd => normalizedContent === cmd || normalizedContent.startsWith(cmd + ' ')) ||
|
|
1172
1151
|
idleWhenArg.some(cmd => normalizedContent.startsWith(cmd + ' '));
|
|
@@ -1176,13 +1155,19 @@ export class CommandHandler {
|
|
|
1176
1155
|
const threadSession = await this.sessionManager.getThreadSession(channel, channelId, threadId);
|
|
1177
1156
|
if (threadSession) {
|
|
1178
1157
|
const threadAgent = this.getAgent(channel, threadSession.agentId);
|
|
1179
|
-
|
|
1158
|
+
const isBusy = threadAgent.hasActiveStream(threadSession.id) ||
|
|
1159
|
+
this.messageQueue?.isProcessing(threadSession.id);
|
|
1160
|
+
if (isBusy) {
|
|
1180
1161
|
return { kind: 'command.error', text: '⚠️ 当前正在处理消息,请稍后再试\n使用 /stop 中断当前任务后重试' };
|
|
1181
1162
|
}
|
|
1182
1163
|
}
|
|
1183
1164
|
}
|
|
1184
|
-
else if (activeSession
|
|
1185
|
-
|
|
1165
|
+
else if (activeSession) {
|
|
1166
|
+
const isBusy = agent.hasActiveStream(activeSession.id) ||
|
|
1167
|
+
this.messageQueue?.isProcessing(activeSession.id);
|
|
1168
|
+
if (isBusy) {
|
|
1169
|
+
return { kind: 'command.error', text: '⚠️ 当前正在处理消息,请稍后再试\n使用 /stop 中断当前任务后重试' };
|
|
1170
|
+
}
|
|
1186
1171
|
}
|
|
1187
1172
|
}
|
|
1188
1173
|
// 检查是否以 / 开头(可能是命令)
|
|
@@ -1627,37 +1612,12 @@ export class CommandHandler {
|
|
|
1627
1612
|
const currentModel = hasModelSwitcher(setmodelAgent) ? setmodelAgent.getModel() : setmodelAgent.name;
|
|
1628
1613
|
const efforts = getAvailableEfforts(setmodelAgent, currentModel);
|
|
1629
1614
|
const currentEffort = setmodelAgent.getEffort?.() || 'auto';
|
|
1630
|
-
const
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
1637
|
-
const resp = await fetch(modelsUrl, {
|
|
1638
|
-
signal: controller.signal,
|
|
1639
|
-
headers: { 'Authorization': `Bearer ${modelListSource.apiKey || ''}` },
|
|
1640
|
-
});
|
|
1641
|
-
clearTimeout(timeout);
|
|
1642
|
-
if (resp.ok) {
|
|
1643
|
-
modelListData = await resp.json();
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
catch { }
|
|
1647
|
-
}
|
|
1648
|
-
// 兜底模型列表
|
|
1649
|
-
if (!modelListData || !modelListData.data || modelListData.data.length === 0) {
|
|
1650
|
-
const now = Math.floor(Date.now() / 1000);
|
|
1651
|
-
modelListData = {
|
|
1652
|
-
object: 'list',
|
|
1653
|
-
data: modelListSource.fallbackModels.map(id => ({
|
|
1654
|
-
id,
|
|
1655
|
-
object: 'model',
|
|
1656
|
-
created: now,
|
|
1657
|
-
owned_by: modelListSource.owner,
|
|
1658
|
-
})),
|
|
1659
|
-
};
|
|
1660
|
-
}
|
|
1615
|
+
const now = Math.floor(Date.now() / 1000);
|
|
1616
|
+
const modelIds = hasModelSwitcher(setmodelAgent) ? setmodelAgent.listModels() : [];
|
|
1617
|
+
const modelListData = {
|
|
1618
|
+
object: 'list',
|
|
1619
|
+
data: modelIds.map(id => ({ id, object: 'model', created: now, owned_by: setmodelAgent.name === 'codex' ? 'openai' : 'anthropic' })),
|
|
1620
|
+
};
|
|
1661
1621
|
return { kind: 'command.result', text: JSON.stringify({
|
|
1662
1622
|
current_model: currentModel,
|
|
1663
1623
|
current_effort: currentEffort,
|
|
@@ -1690,12 +1650,15 @@ export class CommandHandler {
|
|
|
1690
1650
|
kind: {
|
|
1691
1651
|
kind: 'command-card',
|
|
1692
1652
|
title: '🤖 切换模型',
|
|
1693
|
-
buttons: models.map((m) =>
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1653
|
+
buttons: models.map((m) => {
|
|
1654
|
+
const display = modelDisplayLabel(modelAgent, m);
|
|
1655
|
+
return {
|
|
1656
|
+
label: m === currentModel ? `✓ ${display}` : display,
|
|
1657
|
+
command: `/model ${m}`,
|
|
1658
|
+
style: (m === currentModel ? 'primary' : 'default'),
|
|
1659
|
+
disabled: m === currentModel,
|
|
1660
|
+
};
|
|
1661
|
+
}),
|
|
1699
1662
|
},
|
|
1700
1663
|
};
|
|
1701
1664
|
const replyCtx = this.getReplyContext(modelSession);
|
|
@@ -1705,14 +1668,14 @@ export class CommandHandler {
|
|
|
1705
1668
|
return { kind: 'command.result', text: cardResult };
|
|
1706
1669
|
}
|
|
1707
1670
|
// 降级:文本
|
|
1708
|
-
const modelList = models.map((m) => ` ${m === currentModel ? '✓' : ' '} ${m}`).join('\n');
|
|
1671
|
+
const modelList = models.map((m) => ` ${m === currentModel ? '✓' : ' '} ${modelDisplayLabel(modelAgent, m)}`).join('\n');
|
|
1709
1672
|
const effortHint = efforts.length > 0
|
|
1710
1673
|
? `\n推理强度: ${currentEffort === 'auto' ? 'auto (SDK默认)' : currentEffort} (使用 /effort 调整)`
|
|
1711
1674
|
: '';
|
|
1712
1675
|
if (isAdmin) {
|
|
1713
|
-
return { kind: 'command.result', text: `当前模型: ${currentModel}${effortHint}\n\n可用模型:\n${modelList}\n\n用法: /model <模型>` };
|
|
1676
|
+
return { kind: 'command.result', text: `当前模型: ${modelDisplayLabel(modelAgent, currentModel)}${effortHint}\n\n可用模型:\n${modelList}\n\n用法: /model <模型>` };
|
|
1714
1677
|
}
|
|
1715
|
-
return { kind: 'command.result', text: `当前模型: ${currentModel}${effortHint}` };
|
|
1678
|
+
return { kind: 'command.result', text: `当前模型: ${modelDisplayLabel(modelAgent, currentModel)}${effortHint}` };
|
|
1716
1679
|
}
|
|
1717
1680
|
// 带参(切换/调整)需 admin+;无参查询已在上方返回
|
|
1718
1681
|
if (!isAdmin)
|
|
@@ -1732,20 +1695,29 @@ export class CommandHandler {
|
|
|
1732
1695
|
else if (allEfforts.includes(arg)) {
|
|
1733
1696
|
return { kind: 'command.error', text: `⚠️ 请使用 /effort ${arg} 调整推理强度` };
|
|
1734
1697
|
}
|
|
1735
|
-
else if (models.includes(arg)) {
|
|
1736
|
-
newModel = arg;
|
|
1737
|
-
}
|
|
1738
1698
|
else {
|
|
1739
|
-
const
|
|
1740
|
-
|
|
1741
|
-
|
|
1699
|
+
const resolvedArg = hasModelSwitcher(modelAgent) ? (modelAgent.resolveModelId?.(arg) ?? arg) : arg;
|
|
1700
|
+
if (models.includes(resolvedArg)) {
|
|
1701
|
+
newModel = resolvedArg;
|
|
1702
|
+
}
|
|
1703
|
+
else if (models.includes(arg)) {
|
|
1704
|
+
newModel = arg;
|
|
1705
|
+
}
|
|
1706
|
+
else {
|
|
1707
|
+
const modelList = models.map((m) => ` ${m === currentModel ? '✓' : ' '} ${modelDisplayLabel(modelAgent, m)}`).join('\n');
|
|
1708
|
+
const effortHint = efforts.length > 0 ? `\n\n推理强度请使用 /effort 命令` : '';
|
|
1709
|
+
return { kind: 'command.error', text: `❌ 无效参数: ${arg}\n\n可用模型:\n${modelList}${effortHint}` };
|
|
1710
|
+
}
|
|
1742
1711
|
}
|
|
1743
1712
|
}
|
|
1744
1713
|
else {
|
|
1745
1714
|
// 双参数:model effort
|
|
1746
|
-
const [
|
|
1715
|
+
const [modelArgRaw, effortArg] = parts;
|
|
1716
|
+
const modelArg = hasModelSwitcher(modelAgent)
|
|
1717
|
+
? (models.includes(modelArgRaw) ? modelArgRaw : (modelAgent.resolveModelId?.(modelArgRaw) ?? modelArgRaw))
|
|
1718
|
+
: modelArgRaw;
|
|
1747
1719
|
if (!models.includes(modelArg)) {
|
|
1748
|
-
return { kind: 'command.error', text: `❌ 无效的模型ID: ${
|
|
1720
|
+
return { kind: 'command.error', text: `❌ 无效的模型ID: ${modelArgRaw}` };
|
|
1749
1721
|
}
|
|
1750
1722
|
const targetEfforts = getAvailableEfforts(modelAgent, modelArg);
|
|
1751
1723
|
if (targetEfforts.length === 0) {
|
|
@@ -2016,12 +1988,12 @@ export class CommandHandler {
|
|
|
2016
1988
|
const threadSession = await this.sessionManager.getThreadSession(channel, channelId, threadId);
|
|
2017
1989
|
if (threadSession) {
|
|
2018
1990
|
const threadAgent = this.getAgent(channel, threadSession.agentId);
|
|
2019
|
-
if (threadAgent.hasActiveStream(threadSession.id)) {
|
|
1991
|
+
if (threadAgent.hasActiveStream(threadSession.id) || this.messageQueue?.isProcessing(threadSession.id)) {
|
|
2020
1992
|
return { kind: 'command.error', text: '⚠️ 当前正在处理消息,请稍后再试\n使用 /stop 中断当前任务后重试' };
|
|
2021
1993
|
}
|
|
2022
1994
|
}
|
|
2023
1995
|
}
|
|
2024
|
-
else if (agent.hasActiveStream(chatmodeSession.id)) {
|
|
1996
|
+
else if (agent.hasActiveStream(chatmodeSession.id) || this.messageQueue?.isProcessing(chatmodeSession.id)) {
|
|
2025
1997
|
return { kind: 'command.error', text: '⚠️ 当前正在处理消息,请稍后再试\n使用 /stop 中断当前任务后重试' };
|
|
2026
1998
|
}
|
|
2027
1999
|
await this.sessionManager.updateSession(chatmodeSession.id, { sessionMode: arg });
|
|
@@ -2417,6 +2389,27 @@ export class CommandHandler {
|
|
|
2417
2389
|
env: { ...process.env, EVOLCLAW_HOME: resolvePaths().root }
|
|
2418
2390
|
}).unref();
|
|
2419
2391
|
this.eventBus.publish({ type: 'system:restart', channel, channelId });
|
|
2392
|
+
// 先发送重启反馈消息,等待发送完成后再 kill 进程
|
|
2393
|
+
// 避免消息还没发出去进程就退出了
|
|
2394
|
+
const adapter = this.adapters.get(channel);
|
|
2395
|
+
if (adapter) {
|
|
2396
|
+
try {
|
|
2397
|
+
const envelope = buildEnvelope({
|
|
2398
|
+
taskId: `restart-${Date.now()}`,
|
|
2399
|
+
channel,
|
|
2400
|
+
channelId,
|
|
2401
|
+
agentName: 'system',
|
|
2402
|
+
chatmode: 'interactive',
|
|
2403
|
+
replyContext,
|
|
2404
|
+
});
|
|
2405
|
+
await adapter.send(envelope, { kind: 'command.result', text: '🔄 服务正在重启,请稍候...(约 5 秒后恢复)' });
|
|
2406
|
+
// 等待消息发送完成后再延迟 kill
|
|
2407
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
2408
|
+
}
|
|
2409
|
+
catch (err) {
|
|
2410
|
+
logger.error('[System] Failed to send restart notification:', err);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2420
2413
|
// 发 SIGTERM 而非直接 process.exit(0),让 index.ts 的 shutdown() 先
|
|
2421
2414
|
// 正常关闭所有 channel(包括 Feishu WebSocket close frame),
|
|
2422
2415
|
// 避免 Feishu 服务端因连接异常断开而重推未 ack 的消息给新进程。
|
|
@@ -2447,7 +2440,8 @@ export class CommandHandler {
|
|
|
2447
2440
|
}
|
|
2448
2441
|
}
|
|
2449
2442
|
await executeRestart();
|
|
2450
|
-
|
|
2443
|
+
// executeRestart 内部已经发送了反馈消息,这里返回 null 避免重复发送
|
|
2444
|
+
return null;
|
|
2451
2445
|
}
|
|
2452
2446
|
// /upgrade 命令:检查版本更新,提示用户手动重启
|
|
2453
2447
|
if (normalizedContent === '/upgrade') {
|
|
@@ -3166,6 +3160,7 @@ export class CommandHandler {
|
|
|
3166
3160
|
nextFireAt,
|
|
3167
3161
|
targetChannel: parsed.targetChannel ?? channel,
|
|
3168
3162
|
targetChannelId: parsed.targetChannelId ?? channelId,
|
|
3163
|
+
targetChannelType: this.resolveChannelType(parsed.targetChannel ?? channel),
|
|
3169
3164
|
targetThreadId: parsed.targetThreadId,
|
|
3170
3165
|
targetSessionStrategy: parsed.targetSessionStrategy,
|
|
3171
3166
|
agentId: parsed.agentId,
|
|
@@ -51,7 +51,7 @@ export function detectDuplicates(agents) {
|
|
|
51
51
|
export class EvolAgentRegistry {
|
|
52
52
|
_agentsDir;
|
|
53
53
|
agents = new Map();
|
|
54
|
-
/** channel key (`<type>#<
|
|
54
|
+
/** channel key (`<type>#<selfAID>#<name>`) → agent aid */
|
|
55
55
|
channelIndex = new Map();
|
|
56
56
|
/** 启动期被 ConfigStore 跳过的目录(命名非法 / 缺 config.json / 校验失败等) */
|
|
57
57
|
skipped = [];
|
package/dist/core/evolagent.js
CHANGED
|
@@ -62,11 +62,11 @@ export class EvolAgent {
|
|
|
62
62
|
}
|
|
63
63
|
// ── Channels ──────────────────────────────────────────────────────────
|
|
64
64
|
/**
|
|
65
|
-
* effective channel key:`<type>#<
|
|
66
|
-
* AUN channel 的
|
|
65
|
+
* effective channel key:`<type>#<selfAID>#<name>`。
|
|
66
|
+
* AUN channel 的 selfAID 是 agent.aid,name 固定为 'main'。
|
|
67
67
|
*/
|
|
68
68
|
effectiveChannelName(type, rawName) {
|
|
69
|
-
return formatChannelKey({ type,
|
|
69
|
+
return formatChannelKey({ type, selfAID: this.aid, name: rawName });
|
|
70
70
|
}
|
|
71
71
|
channelInstanceNames() {
|
|
72
72
|
// AUN channel 隐式存在(从 agent.aid 派生),不需要在 channels[] 里声明
|
|
@@ -97,7 +97,7 @@ export class EvolAgent {
|
|
|
97
97
|
*/
|
|
98
98
|
isAunChannelKey(channelKey) {
|
|
99
99
|
const parsed = tryParseChannelKey(channelKey);
|
|
100
|
-
return parsed?.type === 'aun' && parsed.
|
|
100
|
+
return parsed?.type === 'aun' && parsed.selfAID === this.aid;
|
|
101
101
|
}
|
|
102
102
|
getOwner(channelKey) {
|
|
103
103
|
if (this.isAunChannelKey(channelKey)) {
|