evolcore 0.0.32 → 0.0.33
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 +24 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +23 -19
- package/dist/agents/ecagent-runner.js +5 -13
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/channels/aun.js +28 -2
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +365 -29
- package/dist/cli/contact.js +8 -2
- package/dist/cli/daemon-commands.js +81 -12
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +8 -1
- package/dist/cli/queue-command.js +9 -4
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +78 -2
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +620 -0
- package/dist/cli/trigger-command.js +4 -2
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/auth/agent-delegation.js +25 -1
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +33 -0
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/command-handler.js +60 -24
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +139 -2
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +349 -67
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +126 -7
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +8 -5
- package/dist/ipc.js +89 -10
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/07-memory-and-thread.md +202 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +66 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skill-slash.ts — `/skill` 斜杠命令:技能查看与索引刷新
|
|
3
|
+
*
|
|
4
|
+
* 权限:
|
|
5
|
+
* - 只读(list / show / where):私聊所有角色可看;群聊仅 admin+ 可看
|
|
6
|
+
* - refresh:私聊仅 admin+;群聊一律拒绝
|
|
7
|
+
*
|
|
8
|
+
* 本文件刻意**只做只读 + refresh**:写操作(install / remove / enable / disable)
|
|
9
|
+
* 的权限门禁在 `kits/skills/ec-skill/scripts/skill.mjs` 的 `assertPermission()` 里,
|
|
10
|
+
* 那套判据(群聊拒绝、agent 级需本 Agent owner、本机级需 daemonowner)依赖运行时上下文,
|
|
11
|
+
* 在 slash 层重实现一份必然漂移。写操作请走 `ec skill` 或 ec-skill 技能。
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import { agentDir, getPackageRoot, resolveRoot } from '../../paths.js';
|
|
16
|
+
import { resolveAllSkillStates } from '../skill/switch-resolver.js';
|
|
17
|
+
import { readSkillDescription } from '../skill/index-generator.js';
|
|
18
|
+
import { evaluateWhen, loadManifest, resolvePathWithDiag, } from '../../eck/manifest-engine.js';
|
|
19
|
+
import { DEFAULT_MANIFEST_FILE } from '../../eck/kit-renderer.js';
|
|
20
|
+
import { resolveAgentLifecycle } from '../../config/lifecycle.js';
|
|
21
|
+
const USAGE = [
|
|
22
|
+
'用法:',
|
|
23
|
+
' /skill 技能概览(启用数 / 总数)',
|
|
24
|
+
' /skill list [all] 技能列表(默认仅启用;all 显示未启用的)',
|
|
25
|
+
' /skill index 查看技能索引加载情况(哪些索引文件被加载、时间、技能数)',
|
|
26
|
+
' /skill index builtin 查看内置技能索引正文',
|
|
27
|
+
' /skill index local 查看本机技能索引正文',
|
|
28
|
+
' /skill show <name> 单个技能详情:来源、状态、生效依据、描述',
|
|
29
|
+
' /skill where 打印三级技能根目录',
|
|
30
|
+
' /skill refresh 重建当前 Agent 的技能索引',
|
|
31
|
+
'',
|
|
32
|
+
'写操作(install / remove / enable / disable)请用 ec skill 或 ec-skill 技能。',
|
|
33
|
+
].join('\n');
|
|
34
|
+
const result = (text) => ({ kind: 'command.result', text });
|
|
35
|
+
const error = (text) => ({ kind: 'command.error', text: `❌ ${text}` });
|
|
36
|
+
/** 只读子命令:私聊所有角色;群聊仅 admin+。 */
|
|
37
|
+
export function canReadSkillSlash(ctx) {
|
|
38
|
+
return ctx.chatType === 'group' ? ctx.isAdmin : true;
|
|
39
|
+
}
|
|
40
|
+
/** refresh:私聊仅 admin+;群聊一律拒绝。 */
|
|
41
|
+
export function canRefreshSkillSlash(ctx) {
|
|
42
|
+
return ctx.chatType === 'group' ? false : ctx.isAdmin;
|
|
43
|
+
}
|
|
44
|
+
export function canUseSkillSlash(ctx) {
|
|
45
|
+
return canReadSkillSlash(ctx);
|
|
46
|
+
}
|
|
47
|
+
/** evolhelp「技能管理」条目。可见范围与 canReadSkillSlash / canRefreshSkillSlash 一致。 */
|
|
48
|
+
export function skillHelpEntries(ctx) {
|
|
49
|
+
if (!canUseSkillSlash(ctx))
|
|
50
|
+
return [];
|
|
51
|
+
const category = '技能管理';
|
|
52
|
+
const roles = [ctx.role];
|
|
53
|
+
const out = [
|
|
54
|
+
{ command: '/skill list', description: '已启用的技能列表', category, roles },
|
|
55
|
+
{ command: '/skill list all', description: '全部技能列表(含未启用)', category, roles },
|
|
56
|
+
{ command: '/skill index', description: '技能索引加载情况(文件、时间、技能数)', category, roles },
|
|
57
|
+
{ command: '/skill index builtin', description: '查看内置技能索引正文', category, roles },
|
|
58
|
+
{ command: '/skill index local', description: '查看本机技能索引正文', category, roles },
|
|
59
|
+
{ command: '/skill where', description: '三级技能根目录', category, roles },
|
|
60
|
+
];
|
|
61
|
+
if (canRefreshSkillSlash(ctx)) {
|
|
62
|
+
out.push({ command: '/skill refresh', description: '重建当前 Agent 的技能索引', category, roles });
|
|
63
|
+
}
|
|
64
|
+
out.push({ command: '/skill show', description: '单个技能详情', args: '<name>', category, roles });
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
/** 技能三级根目录;与 ec-skill 脚本的 skillRoots() 保持一致。 */
|
|
68
|
+
function skillRoots(selfAid) {
|
|
69
|
+
return {
|
|
70
|
+
builtin: path.join(getPackageRoot(), 'kits', 'skills'),
|
|
71
|
+
local: path.join(resolveRoot(), 'skills'),
|
|
72
|
+
agent: selfAid ? path.join(agentDir(selfAid), 'skills') : '(未确定 agent)',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** 目录本身是 agent 级 skills/,resolveAllSkillStates 要的是 agent 根目录。 */
|
|
76
|
+
function agentRootFor(selfAid) {
|
|
77
|
+
return selfAid ? agentDir(selfAid) : undefined;
|
|
78
|
+
}
|
|
79
|
+
function sourceLabel(source) {
|
|
80
|
+
return source === 'builtin' ? '内置' : '本机';
|
|
81
|
+
}
|
|
82
|
+
function stateLabel(s) {
|
|
83
|
+
return s.enabled ? '开' : '关';
|
|
84
|
+
}
|
|
85
|
+
function overviewLine(states) {
|
|
86
|
+
const enabled = states.filter(s => s.enabled).length;
|
|
87
|
+
return `${states.length} 个技能,${enabled} 个启用`;
|
|
88
|
+
}
|
|
89
|
+
function formatList(states, showAll) {
|
|
90
|
+
const shown = showAll ? states : states.filter(s => s.enabled);
|
|
91
|
+
if (shown.length === 0) {
|
|
92
|
+
return showAll ? '(没有任何技能)' : '(没有启用的技能;用 /skill list all 查看全部)';
|
|
93
|
+
}
|
|
94
|
+
const nameW = Math.max(20, ...shown.map(s => s.name.length + 2));
|
|
95
|
+
const lines = [`${overviewLine(states)}${showAll ? '' : '(仅列启用;/skill list all 查看全部)'}`, ''];
|
|
96
|
+
for (const s of shown) {
|
|
97
|
+
const lock = s.locked ? ' [locked]' : '';
|
|
98
|
+
lines.push(` ${s.name.padEnd(nameW)}${sourceLabel(s.source).padEnd(6)}${stateLabel(s)}${lock}`);
|
|
99
|
+
}
|
|
100
|
+
return lines.join('\n');
|
|
101
|
+
}
|
|
102
|
+
function formatInfo(s) {
|
|
103
|
+
const desc = readSkillDescription(s.skillDir);
|
|
104
|
+
const lines = [
|
|
105
|
+
`技能: ${s.name}`,
|
|
106
|
+
`来源: ${sourceLabel(s.source)}`,
|
|
107
|
+
`路径: ${s.skillDir}`,
|
|
108
|
+
`状态: ${stateLabel(s)}(由 ${s.decidedBy} 决定)`,
|
|
109
|
+
];
|
|
110
|
+
if (s.locked)
|
|
111
|
+
lines.push('锁定: 内置级 locked,任何级别都关不掉');
|
|
112
|
+
lines.push('', `描述: ${desc || '(无)'}`);
|
|
113
|
+
return lines.join('\n');
|
|
114
|
+
}
|
|
115
|
+
function formatWhere(selfAid) {
|
|
116
|
+
const roots = skillRoots(selfAid);
|
|
117
|
+
return [
|
|
118
|
+
'三级技能根目录:',
|
|
119
|
+
` 内置级: ${roots.builtin}`,
|
|
120
|
+
` 本机级: ${roots.local}`,
|
|
121
|
+
` Agent 级: ${roots.agent}`,
|
|
122
|
+
'',
|
|
123
|
+
'Agent 级只放 .switches.json(开关),不放 SKILL.md。',
|
|
124
|
+
].join('\n');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 读**生效的 manifest**(含 ECK override),挑出指向技能索引文件的段,
|
|
128
|
+
* 再逐段判定实际是否会被注入。
|
|
129
|
+
*
|
|
130
|
+
* 为什么不直接扫磁盘:`optional: true` 的文件缺失会被静默跳过,`when` 不满足或
|
|
131
|
+
* 总闸超限也会让段不加载。只有走 manifest 判定,`/skill index` 才能反映"实际加载"。
|
|
132
|
+
*/
|
|
133
|
+
function inspectSkillIndexes(agentRoot, selfAid, lifecycle) {
|
|
134
|
+
const manifestFile = DEFAULT_MANIFEST_FILE;
|
|
135
|
+
const sections = loadManifest(manifestFile);
|
|
136
|
+
// 只关心指向 <agentRoot>/skills/.index-*.md 的段;override 可能加段或改路径,
|
|
137
|
+
// 所以按解析后的路径判断,而不是按段 id 匹配。
|
|
138
|
+
const skillsDir = path.join(agentRoot, 'skills');
|
|
139
|
+
// lifecycle 与渲染器同源解析:显式非法值按 null(fail-closed),缺省按 active 兼容。
|
|
140
|
+
const resolvedLifecycle = resolveAgentLifecycle({ lifecycle });
|
|
141
|
+
const vars = {
|
|
142
|
+
selfAid,
|
|
143
|
+
EVOLCORE_HOME: resolveRoot(),
|
|
144
|
+
AGENT_DIR: agentRoot,
|
|
145
|
+
...(resolvedLifecycle === null ? {} : { lifecycle: resolvedLifecycle }),
|
|
146
|
+
};
|
|
147
|
+
const out = [];
|
|
148
|
+
for (const section of sections) {
|
|
149
|
+
if (section.type !== 'file' || !section.file)
|
|
150
|
+
continue;
|
|
151
|
+
// 与 kit-renderer 共用同一个解析器:变量替换 + path.normalize,不做任何空白裁剪。
|
|
152
|
+
// 变量缺失时 status !== 'ok',resolved 可能为 null —— 段会被跳过,而不是拼出一个
|
|
153
|
+
// 看起来合理的错误路径(原先的 stripBlankLines 版本会静默拼错,还吃掉路径里的空格)。
|
|
154
|
+
const r = resolvePathWithDiag(section.file, vars);
|
|
155
|
+
if (r.status !== 'ok' || !r.resolved)
|
|
156
|
+
continue;
|
|
157
|
+
const resolved = r.resolved;
|
|
158
|
+
if (!isSkillIndexPath(resolved, skillsDir))
|
|
159
|
+
continue;
|
|
160
|
+
const enabled = section.enabled !== false;
|
|
161
|
+
const whenPassed = evaluateWhen(section.when ?? 'always', vars);
|
|
162
|
+
const fileExists = fs.existsSync(resolved);
|
|
163
|
+
let reason;
|
|
164
|
+
if (!enabled)
|
|
165
|
+
reason = 'manifest 中该段 enabled: false';
|
|
166
|
+
else if (!whenPassed)
|
|
167
|
+
reason = `manifest 的 when 条件不满足(当前 lifecycle=${resolvedLifecycle ?? '值非法'})`;
|
|
168
|
+
else if (!fileExists)
|
|
169
|
+
reason = section.optional ? '文件不存在(optional,静默跳过)' : '文件不存在';
|
|
170
|
+
const diag = {
|
|
171
|
+
id: section.id,
|
|
172
|
+
rawPath: section.file,
|
|
173
|
+
resolvedPath: resolved,
|
|
174
|
+
loaded: enabled && whenPassed && fileExists,
|
|
175
|
+
...(reason ? { reason } : {}),
|
|
176
|
+
fileExists,
|
|
177
|
+
};
|
|
178
|
+
if (fileExists) {
|
|
179
|
+
try {
|
|
180
|
+
const st = fs.statSync(resolved);
|
|
181
|
+
diag.sizeBytes = st.size;
|
|
182
|
+
diag.mtimeMs = st.mtimeMs;
|
|
183
|
+
diag.skillCount = countSkillsInIndex(fs.readFileSync(resolved, 'utf-8'));
|
|
184
|
+
}
|
|
185
|
+
catch { /* 读失败保持字段缺省 */ }
|
|
186
|
+
}
|
|
187
|
+
out.push(diag);
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
/** 判断解析后的路径是否落在本 agent 的 skills 索引目录里,且文件名形如 .index-*.md */
|
|
192
|
+
function isSkillIndexPath(resolved, skillsDir) {
|
|
193
|
+
const dir = path.dirname(resolved);
|
|
194
|
+
if (path.normalize(dir).toLowerCase() !== path.normalize(skillsDir).toLowerCase())
|
|
195
|
+
return false;
|
|
196
|
+
return /^\.index-.*\.md$/i.test(path.basename(resolved));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* 从索引正文里数技能条目的数量。
|
|
200
|
+
*
|
|
201
|
+
* 索引由 index-generator 生成,段头形如 `本 Agent 已启用 N 个内置技能。`;
|
|
202
|
+
* 拿不到就退化为数 `- ` 开头的条目行。
|
|
203
|
+
*/
|
|
204
|
+
function countSkillsInIndex(content) {
|
|
205
|
+
const headerMatch = content.match(/已启用\s*(\d+)\s*个/);
|
|
206
|
+
if (headerMatch)
|
|
207
|
+
return Number(headerMatch[1]);
|
|
208
|
+
const items = content.split('\n').filter(l => /^-\s+\S/.test(l.trim()));
|
|
209
|
+
return items.length > 0 ? items.length : undefined;
|
|
210
|
+
}
|
|
211
|
+
function formatIndexOverview(sections, selfAid) {
|
|
212
|
+
if (sections.length === 0) {
|
|
213
|
+
return '当前生效的 manifest 里没有引用任何技能索引文件。\n'
|
|
214
|
+
+ '(技能索引由 kits/eck_manifest.json 的 skills-builtin / skills-local 段注入)';
|
|
215
|
+
}
|
|
216
|
+
const lines = [`技能索引加载情况(agent: ${selfAid})`, ''];
|
|
217
|
+
for (const s of sections) {
|
|
218
|
+
const state = s.loaded ? '已加载' : '未加载';
|
|
219
|
+
lines.push(`${state} ${path.basename(s.resolvedPath)}`);
|
|
220
|
+
lines.push(` manifest 段: ${s.id}`);
|
|
221
|
+
if (s.loaded) {
|
|
222
|
+
lines.push(` 技能数: ${s.skillCount ?? '(解析不到)'}`);
|
|
223
|
+
lines.push(` 文件时间: ${s.mtimeMs ? new Date(s.mtimeMs).toLocaleString('zh-CN') : '(未知)'}`);
|
|
224
|
+
lines.push(` 大小: ${s.sizeBytes ?? '?'} 字节`);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
lines.push(` 原因: ${s.reason ?? '未知'}`);
|
|
228
|
+
}
|
|
229
|
+
lines.push(` 路径: ${s.resolvedPath}`);
|
|
230
|
+
lines.push('');
|
|
231
|
+
}
|
|
232
|
+
const loaded = sections.filter(s => s.loaded);
|
|
233
|
+
const total = loaded.reduce((n, s) => n + (s.skillCount ?? 0), 0);
|
|
234
|
+
lines.push(`合计:${loaded.length}/${sections.length} 个索引文件被加载,共 ${total} 个技能`);
|
|
235
|
+
lines.push('查看正文: /skill index builtin 或 /skill index local');
|
|
236
|
+
return lines.join('\n');
|
|
237
|
+
}
|
|
238
|
+
/** 读某个索引文件正文;kind 由段 id 或路径推断。 */
|
|
239
|
+
function readIndexFile(selfAid, kind, lifecycle) {
|
|
240
|
+
const root = agentRootFor(selfAid);
|
|
241
|
+
if (!root)
|
|
242
|
+
return '❌ 无法确定当前 agent 目录';
|
|
243
|
+
const file = path.join(root, 'skills', kind === 'builtin' ? '.index-builtin.md' : '.index-local.md');
|
|
244
|
+
const sections = inspectSkillIndexes(root, selfAid, lifecycle);
|
|
245
|
+
const section = sections.find(s => path.basename(s.resolvedPath).toLowerCase() === path.basename(file).toLowerCase());
|
|
246
|
+
if (section && !section.loaded) {
|
|
247
|
+
return `⚠️ ${path.basename(file)} 当前未加载(${section.reason ?? '未知原因'})\n`
|
|
248
|
+
+ `路径: ${file}`;
|
|
249
|
+
}
|
|
250
|
+
if (!fs.existsSync(file)) {
|
|
251
|
+
return `(${path.basename(file)} 不存在——该来源当前没有启用任何技能,索引生成时会被删除)`;
|
|
252
|
+
}
|
|
253
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
254
|
+
const count = countSkillsInIndex(content);
|
|
255
|
+
const header = `${path.basename(file)}(${count ?? '?'} 个技能,${new Date(fs.statSync(file).mtimeMs).toLocaleString('zh-CN')})`;
|
|
256
|
+
return `${header}\n\n${content}`;
|
|
257
|
+
}
|
|
258
|
+
export async function handleSkillSlash(ctx) {
|
|
259
|
+
const args = ctx.content.trim().split(/\s+/).slice(1);
|
|
260
|
+
const sub = args[0];
|
|
261
|
+
if (!sub || sub === 'help') {
|
|
262
|
+
if (sub === 'help')
|
|
263
|
+
return result(USAGE);
|
|
264
|
+
if (!canReadSkillSlash(ctx))
|
|
265
|
+
return error('群聊中技能列表仅限管理员查看');
|
|
266
|
+
const root = agentRootFor(ctx.selfAid);
|
|
267
|
+
if (!root)
|
|
268
|
+
return error('无法确定当前 agent 目录');
|
|
269
|
+
return result(`${overviewLine(resolveAllSkillStates(root))}\n\n输入 /skill help 查看全部子命令`);
|
|
270
|
+
}
|
|
271
|
+
if (!canReadSkillSlash(ctx))
|
|
272
|
+
return error('群聊中技能查看仅限管理员');
|
|
273
|
+
const root = agentRootFor(ctx.selfAid);
|
|
274
|
+
if (!root)
|
|
275
|
+
return error('无法确定当前 agent 目录');
|
|
276
|
+
try {
|
|
277
|
+
if (sub === 'list') {
|
|
278
|
+
const showAll = args[1] === 'all';
|
|
279
|
+
return result(formatList(resolveAllSkillStates(root), showAll));
|
|
280
|
+
}
|
|
281
|
+
if (sub === 'show' || sub === 'info') {
|
|
282
|
+
const name = args[1];
|
|
283
|
+
if (!name)
|
|
284
|
+
return error('用法: /skill show <name>');
|
|
285
|
+
const found = resolveAllSkillStates(root).find(s => s.name === name);
|
|
286
|
+
if (!found)
|
|
287
|
+
return error(`技能不存在: ${name}\n用 /skill list all 查看全部技能`);
|
|
288
|
+
return result(formatInfo(found));
|
|
289
|
+
}
|
|
290
|
+
if (sub === 'index') {
|
|
291
|
+
const kind = args[1];
|
|
292
|
+
if (kind === 'builtin' || kind === 'local') {
|
|
293
|
+
return result(readIndexFile(ctx.selfAid, kind, ctx.lifecycle));
|
|
294
|
+
}
|
|
295
|
+
if (kind !== undefined) {
|
|
296
|
+
return error(`未知索引: ${kind}\n用法: /skill index [builtin|local]`);
|
|
297
|
+
}
|
|
298
|
+
return result(formatIndexOverview(inspectSkillIndexes(root, ctx.selfAid, ctx.lifecycle), ctx.selfAid));
|
|
299
|
+
}
|
|
300
|
+
if (sub === 'where')
|
|
301
|
+
return result(formatWhere(ctx.selfAid));
|
|
302
|
+
if (sub === 'refresh') {
|
|
303
|
+
if (!canRefreshSkillSlash(ctx)) {
|
|
304
|
+
return error(ctx.chatType === 'group'
|
|
305
|
+
? '群聊中不支持重建技能索引,请私聊或由管理员在终端执行'
|
|
306
|
+
: '无权限:重建技能索引仅限管理员');
|
|
307
|
+
}
|
|
308
|
+
if (!ctx.selfAid)
|
|
309
|
+
return error('无法确定当前 agent AID');
|
|
310
|
+
if (!ctx.refresher)
|
|
311
|
+
return error('daemon 未注入索引刷新入口');
|
|
312
|
+
const r = await ctx.refresher(ctx.selfAid);
|
|
313
|
+
if (!r.ok)
|
|
314
|
+
return error(`重建技能索引失败:${r.error ?? '未知错误'}`);
|
|
315
|
+
return result('✅ 已重建当前 Agent 的技能索引');
|
|
316
|
+
}
|
|
317
|
+
return error(`未知子命令: ${sub}\n\n${USAGE}`);
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
return error(e instanceof Error ? e.message : String(e));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -43,7 +43,7 @@ export function probeSessionActivity(evidence, probes) {
|
|
|
43
43
|
return evaluateSessionActivity(collected);
|
|
44
44
|
}
|
|
45
45
|
// 支持的命令列表
|
|
46
|
-
const commands = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/stop', '/pause', '/compact', '/renew', '/repair', '/fork', '/del', '/perm', '/file', '/check', '/rewind', '/activity', '/observable', '/chatmode', '/mention', '/ask', '/resume', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade', '/pair', '/memory'];
|
|
46
|
+
const commands = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/stop', '/pause', '/compact', '/renew', '/repair', '/fork', '/del', '/perm', '/file', '/check', '/rewind', '/activity', '/observable', '/chatmode', '/mention', '/loop', '/ask', '/resume', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade', '/pair', '/memory', '/skill', '/thread'];
|
|
47
47
|
const deprecatedCommands = ['/clear'];
|
|
48
48
|
commands.push('/diag');
|
|
49
49
|
const exactBoundaryCommands = new Set(['/pause', '/resume', '/stop', '/pair', '/diag']);
|
|
@@ -69,6 +69,8 @@ const aliases = {
|
|
|
69
69
|
const GROUP_READ_ONLY_COMMANDS = new Set(['/model', '/effort', '/perm', '/baseagent']);
|
|
70
70
|
const GROUP_TASK_COMMANDS = new Set([
|
|
71
71
|
'/help', '/evolhelp', '/status', '/check', '/ask', '/pause', '/resume', '/stop', '/compact', '/renew', '/mention', '/memory',
|
|
72
|
+
// /skill 与 /thread 在群里只放行到 handler:写操作由各自 handler 内部按 admin 判定拒绝
|
|
73
|
+
'/skill', '/thread', '/loop',
|
|
72
74
|
]);
|
|
73
75
|
/** Group slash policy applies to every role, including old command-card clicks. */
|
|
74
76
|
export function guardGroupSlashCommand(content, chatType) {
|
|
@@ -95,7 +97,7 @@ export function guardGroupSlashCommand(content, chatType) {
|
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
99
|
// 命令快速路径前缀(所有命令都不进入消息队列)
|
|
98
|
-
const quickCommandPrefixes = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/pause', '/clear', '/compact', '/renew', '/del', '/perm', '/file', '/check', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/observable', '/chatmode', '/mention', '/ask', '/resume', '/base ', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade', '/pair', '/memory'];
|
|
100
|
+
const quickCommandPrefixes = ['/new', '/pwd', '/help', '/evolhelp', '/status', '/restart', '/reload', '/model', '/effort', '/baseagent', '/slist', '/session', '/rename', '/repair', '/fork', '/stop', '/pause', '/clear', '/compact', '/renew', '/del', '/perm', '/file', '/check', '/s ', '/name', '/rewind', '/rw', '/rw ', '/activity', '/observable', '/chatmode', '/mention', '/loop', '/ask', '/resume', '/base ', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade', '/pair', '/memory', '/skill', '/thread'];
|
|
99
101
|
quickCommandPrefixes.push('/diag');
|
|
100
102
|
/**
|
|
101
103
|
* 计算两个字符串的 Levenshtein 距离(编辑距离)
|
|
@@ -159,9 +161,9 @@ export function guardRoleCommand(content, activeChatType, isAdmin) {
|
|
|
159
161
|
return undefined;
|
|
160
162
|
// visitor/member 在群聊和私聊中均可访问的只读命令:纯查询形态(带参写操作由各 handler 内部守卫拦截)
|
|
161
163
|
const userGroupCommands = [
|
|
162
|
-
'/status', '/help', '/evolhelp', '/check', '/chatmode', '/mention',
|
|
164
|
+
'/status', '/help', '/evolhelp', '/check', '/chatmode', '/mention', '/loop',
|
|
163
165
|
'/model', '/effort', '/baseagent', '/perm', '/activity', '/stop', '/pause',
|
|
164
|
-
'/resume', '/trigger', '/file', '/ask', '/memory',
|
|
166
|
+
'/resume', '/trigger', '/file', '/ask', '/memory', '/skill', '/thread',
|
|
165
167
|
];
|
|
166
168
|
const userCommands = activeChatType === 'group' && !isAdmin
|
|
167
169
|
? userGroupCommands
|