evolcore 0.0.25 → 0.0.26
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 +607 -565
- package/LICENSE +21 -21
- package/README.md +455 -455
- package/bin/codex-managed-acl.ps1 +160 -160
- package/bin/codex-managed-hook.mjs +208 -208
- package/bin/ec-safe-output.js +327 -327
- package/bin/ec.js +40 -29
- package/bin/install-codex-managed-hooks.mjs +582 -555
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +50 -12
- package/dist/agents/codex/app-server-client.js +13 -5
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +121 -38
- package/dist/agents/ecagent-runner.js +4 -9
- package/dist/agents/gemini-runner.js +2 -7
- package/dist/agents/runner-types.js +25 -17
- package/dist/aun/aid/agentmd.js +81 -8
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +17 -13
- package/dist/aun/menu-client.js +74 -0
- package/dist/aun/msg/group.js +1 -1
- package/dist/aun/msg/managed-operation.js +1 -1
- package/dist/aun/msg/p2p.js +6 -1
- package/dist/aun/msg/upload.js +30 -22
- package/dist/channels/aun.js +329 -94
- package/dist/cli/agent-command.js +105 -84
- package/dist/cli/agent-create.js +112 -10
- package/dist/cli/agent.js +16 -5
- package/dist/cli/aun-commands.js +297 -296
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +47 -28
- package/dist/cli/contact.js +70 -13
- package/dist/cli/ctl-command.js +22 -22
- package/dist/cli/daemon-commands.js +105 -40
- package/dist/cli/diag.js +31 -0
- package/dist/cli/fs-command.js +67 -67
- package/dist/cli/handoff-command.js +41 -9
- package/dist/cli/index.js +83 -77
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +46 -31
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +29 -29
- package/dist/cli/response.js +30 -30
- package/dist/cli/restart-monitor.js +87 -40
- package/dist/cli/stats.js +51 -51
- package/dist/cli/trigger-command.js +56 -56
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-operation-service.js +145 -0
- package/dist/config/contact-operation-service.js +96 -7
- package/dist/config/resolved-config-op.js +27 -7
- package/dist/core/agent-application-service.js +2 -1
- package/dist/core/auth/authorization-audit.js +20 -12
- package/dist/core/auth/operation-authorizer.js +25 -4
- package/dist/core/auth/operation-catalog.js +35 -4
- package/dist/core/auth/remote-access-store.js +40 -1
- package/dist/core/bootstrap-service.js +1 -1
- package/dist/core/command/command-handler.js +83 -26
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/menu-catalog.js +12 -3
- package/dist/core/command/menu-handler.js +47 -26
- package/dist/core/command/menu-protocol.js +5 -0
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +86 -21
- package/dist/core/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/message/im-renderer.js +72 -21
- package/dist/core/message/message-bridge.js +69 -9
- package/dist/core/message/message-queue.js +26 -0
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +47 -28
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/ec-command-parser.js +51 -12
- package/dist/core/permission/sandbox-runtime.js +36 -14
- package/dist/core/permission/tool-policy.js +2 -1
- package/dist/index.js +280 -163
- package/dist/ipc.js +54 -2
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/price-resolver.js +28 -10
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- package/dist/utils/cross-platform.js +30 -1
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +21 -0
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +3 -1
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -23
- package/dist/utils/npm-ops.js +176 -35
- package/dist/utils/welcome.js +22 -22
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +66 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +56 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +364 -364
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +71 -71
- package/kits/docs/evolcore/agent-create.md +170 -165
- package/kits/docs/evolcore/agent.md +66 -66
- package/kits/docs/evolcore/aid.md +60 -60
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
- package/kits/docs/evolcore/config.md +202 -195
- package/kits/docs/evolcore/contact.md +75 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +243 -243
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +152 -152
- package/kits/docs/evolcore/menu-client.md +42 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +166 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +50 -50
- package/kits/docs/evolcore/trigger.md +575 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +269 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +37 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +190 -190
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +162 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +36 -36
- package/kits/rules/04-relation.md +75 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +63 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +427 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +201 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +92 -92
- package/kits/templates/roles/member.json +96 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +85 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +31 -29
- package/kits/templates/system-fragments/channel.md +64 -64
- package/kits/templates/system-fragments/commands.md +28 -28
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +100 -98
|
@@ -4,6 +4,20 @@ import { agentDataDir } from '../../paths.js';
|
|
|
4
4
|
import { atomicReadJson, atomicWriteJson } from '../../utils/atomic-write.js';
|
|
5
5
|
export const DEFAULT_MENU_TOKEN_IDLE_TIMEOUT_SECONDS = 30 * 24 * 60 * 60;
|
|
6
6
|
export const MAX_MENU_TOKENS_PER_AGENT = 1_000;
|
|
7
|
+
function validInstallationId(value) {
|
|
8
|
+
return value === undefined || (typeof value === 'string' && value.length <= 256
|
|
9
|
+
&& !/[\u0000-\u001f\u007f]/.test(value));
|
|
10
|
+
}
|
|
11
|
+
function normalizeInstallation(value) {
|
|
12
|
+
if (!value || typeof value !== 'object' || !validInstallationId(value.deviceId)
|
|
13
|
+
|| !validInstallationId(value.slotId))
|
|
14
|
+
return null;
|
|
15
|
+
const deviceId = value.deviceId?.trim() || '';
|
|
16
|
+
const slotId = value.slotId?.trim() || '';
|
|
17
|
+
if (slotId && !deviceId)
|
|
18
|
+
return null;
|
|
19
|
+
return { deviceId, slotId };
|
|
20
|
+
}
|
|
7
21
|
function hashToken(token) {
|
|
8
22
|
return createHash('sha256').update(token).digest('hex');
|
|
9
23
|
}
|
|
@@ -13,6 +27,9 @@ function isTokenRecord(value) {
|
|
|
13
27
|
const record = value;
|
|
14
28
|
return typeof record.peerAid === 'string'
|
|
15
29
|
&& record.peerAid.trim().length > 0
|
|
30
|
+
&& validInstallationId(record.peerDeviceId)
|
|
31
|
+
&& validInstallationId(record.peerSlotId)
|
|
32
|
+
&& (!record.peerSlotId?.trim() || !!record.peerDeviceId?.trim())
|
|
16
33
|
&& typeof record.tokenHash === 'string'
|
|
17
34
|
&& /^[a-f0-9]{64}$/.test(record.tokenHash)
|
|
18
35
|
&& typeof record.requestedAt === 'number'
|
|
@@ -28,6 +45,10 @@ function isTokenRecord(value) {
|
|
|
28
45
|
function isExpired(record, now) {
|
|
29
46
|
return now - record.lastUsedAt >= record.idleTimeout * 1000;
|
|
30
47
|
}
|
|
48
|
+
function installationMatches(record, installation) {
|
|
49
|
+
return (record.peerDeviceId?.trim() || '') === (installation.deviceId?.trim() || '')
|
|
50
|
+
&& (record.peerSlotId?.trim() || '') === (installation.slotId?.trim() || '');
|
|
51
|
+
}
|
|
31
52
|
function hashesEqual(left, right) {
|
|
32
53
|
const leftBytes = Buffer.from(left, 'hex');
|
|
33
54
|
const rightBytes = Buffer.from(right, 'hex');
|
|
@@ -41,10 +62,14 @@ export class MenuTokenStore {
|
|
|
41
62
|
constructor(maxRecords = MAX_MENU_TOKENS_PER_AGENT) {
|
|
42
63
|
this.maxRecords = maxRecords;
|
|
43
64
|
}
|
|
44
|
-
issue(selfAid, peerAid, metadata = {}, now = Date.now()) {
|
|
65
|
+
issue(selfAid, peerAid, metadata = {}, installationOrNow = {}, now = Date.now()) {
|
|
66
|
+
const installation = normalizeInstallation(typeof installationOrNow === 'number' ? {} : installationOrNow);
|
|
67
|
+
if (!installation)
|
|
68
|
+
return null;
|
|
69
|
+
const issuedAt = typeof installationOrNow === 'number' ? installationOrNow : now;
|
|
45
70
|
const file = this.read(selfAid);
|
|
46
71
|
const idleTimeout = DEFAULT_MENU_TOKEN_IDLE_TIMEOUT_SECONDS;
|
|
47
|
-
const tokens = file.tokens.filter(record => record.peerAid !== peerAid && !isExpired(record,
|
|
72
|
+
const tokens = file.tokens.filter(record => (record.peerAid !== peerAid || !installationMatches(record, installation)) && !isExpired(record, issuedAt));
|
|
48
73
|
if (tokens.length >= this.maxRecords) {
|
|
49
74
|
if (tokens.length !== file.tokens.length)
|
|
50
75
|
this.write(selfAid, tokens);
|
|
@@ -53,9 +78,11 @@ export class MenuTokenStore {
|
|
|
53
78
|
const menuToken = `mt_${randomBytes(32).toString('base64url')}`;
|
|
54
79
|
tokens.push({
|
|
55
80
|
peerAid,
|
|
81
|
+
...(installation.deviceId?.trim() ? { peerDeviceId: installation.deviceId.trim() } : {}),
|
|
82
|
+
...(installation.slotId?.trim() ? { peerSlotId: installation.slotId.trim() } : {}),
|
|
56
83
|
tokenHash: hashToken(menuToken),
|
|
57
|
-
requestedAt:
|
|
58
|
-
lastUsedAt:
|
|
84
|
+
requestedAt: issuedAt,
|
|
85
|
+
lastUsedAt: issuedAt,
|
|
59
86
|
idleTimeout,
|
|
60
87
|
...(metadata.clientName ? { clientName: metadata.clientName } : {}),
|
|
61
88
|
...(metadata.clientVersion ? { clientVersion: metadata.clientVersion } : {}),
|
|
@@ -64,21 +91,27 @@ export class MenuTokenStore {
|
|
|
64
91
|
this.write(selfAid, tokens);
|
|
65
92
|
return { menuToken, idleTimeout };
|
|
66
93
|
}
|
|
67
|
-
use(selfAid, peerAid, menuToken, now = Date.now()) {
|
|
94
|
+
use(selfAid, peerAid, menuToken, installationOrNow = {}, now = Date.now()) {
|
|
95
|
+
const installation = normalizeInstallation(typeof installationOrNow === 'number' ? {} : installationOrNow);
|
|
96
|
+
if (!installation)
|
|
97
|
+
return false;
|
|
98
|
+
const usedAt = typeof installationOrNow === 'number' ? installationOrNow : now;
|
|
68
99
|
const file = this.read(selfAid);
|
|
69
|
-
const activeTokens = file.tokens.filter(record => !isExpired(record,
|
|
100
|
+
const activeTokens = file.tokens.filter(record => !isExpired(record, usedAt));
|
|
70
101
|
const tokenHash = typeof menuToken === 'string' && menuToken.length > 0 && menuToken.length <= 256
|
|
71
102
|
? hashToken(menuToken)
|
|
72
103
|
: undefined;
|
|
73
104
|
const matchingIndex = tokenHash
|
|
74
|
-
? activeTokens.findIndex(record => record.peerAid === peerAid
|
|
105
|
+
? activeTokens.findIndex(record => record.peerAid === peerAid
|
|
106
|
+
&& installationMatches(record, installation)
|
|
107
|
+
&& hashesEqual(record.tokenHash, tokenHash))
|
|
75
108
|
: -1;
|
|
76
109
|
if (matchingIndex < 0) {
|
|
77
110
|
if (activeTokens.length !== file.tokens.length)
|
|
78
111
|
this.write(selfAid, activeTokens);
|
|
79
112
|
return false;
|
|
80
113
|
}
|
|
81
|
-
activeTokens[matchingIndex] = { ...activeTokens[matchingIndex], lastUsedAt:
|
|
114
|
+
activeTokens[matchingIndex] = { ...activeTokens[matchingIndex], lastUsedAt: usedAt };
|
|
82
115
|
this.write(selfAid, activeTokens);
|
|
83
116
|
return true;
|
|
84
117
|
}
|
|
@@ -45,7 +45,8 @@ export function probeSessionActivity(evidence, probes) {
|
|
|
45
45
|
// 支持的命令列表
|
|
46
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', '/mentionmode', '/ask', '/resume', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade', '/pair'];
|
|
47
47
|
const deprecatedCommands = ['/clear'];
|
|
48
|
-
|
|
48
|
+
commands.push('/diag');
|
|
49
|
+
const exactBoundaryCommands = new Set(['/pause', '/resume', '/stop', '/pair', '/diag']);
|
|
49
50
|
/** Parse the execution command without treating it as a quick/result command. */
|
|
50
51
|
export function parseFullAccessCommand(content) {
|
|
51
52
|
const match = content.match(/^\s*(\/fa|\/fullaccess)(?=\s|$)([\s\S]*)$/);
|
|
@@ -64,8 +65,37 @@ const aliases = {
|
|
|
64
65
|
'/rw': '/rewind',
|
|
65
66
|
'/base': '/baseagent',
|
|
66
67
|
};
|
|
68
|
+
const GROUP_READ_ONLY_COMMANDS = new Set(['/mentionmode', '/model', '/effort', '/perm', '/baseagent']);
|
|
69
|
+
const GROUP_TASK_COMMANDS = new Set([
|
|
70
|
+
'/help', '/evolhelp', '/status', '/check', '/ask', '/pause', '/resume', '/stop', '/compact', '/renew',
|
|
71
|
+
]);
|
|
72
|
+
/** Group slash policy applies to every role, including old command-card clicks. */
|
|
73
|
+
export function guardGroupSlashCommand(content, chatType) {
|
|
74
|
+
if (chatType !== 'group' || !content.trim().startsWith('/'))
|
|
75
|
+
return undefined;
|
|
76
|
+
const [inputCommand, ...args] = content.trim().split(/\s+/);
|
|
77
|
+
const command = aliases[inputCommand] ?? inputCommand;
|
|
78
|
+
if (GROUP_READ_ONLY_COMMANDS.has(command)) {
|
|
79
|
+
if (!args.length || (command === '/model' && args.length === 1 && ['current', 'list', 'check'].includes(args[0]))) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
kind: 'command.error',
|
|
84
|
+
text: `❌ 群聊中 ${command} 仅支持查询,请在私聊或管理界面操作`,
|
|
85
|
+
reason: 'GROUP_COMMAND_READ_ONLY',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (GROUP_TASK_COMMANDS.has(command))
|
|
89
|
+
return undefined;
|
|
90
|
+
return {
|
|
91
|
+
kind: 'command.error',
|
|
92
|
+
text: `❌ 群聊中不支持 ${inputCommand},输入 /help 查看可用命令`,
|
|
93
|
+
reason: 'GROUP_COMMAND_UNSUPPORTED',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
67
96
|
// 命令快速路径前缀(所有命令都不进入消息队列)
|
|
68
97
|
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', '/mentionmode', '/ask', '/resume', '/base ', '/aid', '/rpc', '/storage', '/agent', '/trigger', '/upgrade', '/pair'];
|
|
98
|
+
quickCommandPrefixes.push('/diag');
|
|
69
99
|
/**
|
|
70
100
|
* 计算两个字符串的 Levenshtein 距离(编辑距离)
|
|
71
101
|
*/
|
|
@@ -95,7 +125,7 @@ function levenshteinDistance(str1, str2) {
|
|
|
95
125
|
return matrix[len1][len2];
|
|
96
126
|
}
|
|
97
127
|
export function isQuickCommand(content) {
|
|
98
|
-
return content === '/s' || quickCommandPrefixes.some(cmd => exactBoundaryCommands.has(cmd)
|
|
128
|
+
return content === '/s' || content === '/base' || quickCommandPrefixes.some(cmd => exactBoundaryCommands.has(cmd)
|
|
99
129
|
? content === cmd || content.startsWith(cmd + ' ')
|
|
100
130
|
: content.startsWith(cmd));
|
|
101
131
|
}
|
|
@@ -131,7 +161,7 @@ export function guardRoleCommand(content, activeChatType, isAdmin) {
|
|
|
131
161
|
const userGroupCommands = [
|
|
132
162
|
'/status', '/help', '/evolhelp', '/check', '/chatmode', '/mentionmode',
|
|
133
163
|
'/model', '/effort', '/baseagent', '/perm', '/activity', '/stop', '/pause',
|
|
134
|
-
'/resume', '/trigger', '/file',
|
|
164
|
+
'/resume', '/trigger', '/file', '/ask',
|
|
135
165
|
];
|
|
136
166
|
const userCommands = activeChatType === 'group' && !isAdmin
|
|
137
167
|
? userGroupCommands
|
|
@@ -146,7 +176,7 @@ export function guardRoleCommand(content, activeChatType, isAdmin) {
|
|
|
146
176
|
return {
|
|
147
177
|
kind: 'command.error',
|
|
148
178
|
text: activeChatType === 'group'
|
|
149
|
-
? '❌
|
|
179
|
+
? '❌ 无权限:此命令仅限管理员使用,输入 /help 查看可用命令'
|
|
150
180
|
: '❌ 无权限:此命令仅限管理员使用',
|
|
151
181
|
};
|
|
152
182
|
}
|
|
@@ -28,7 +28,7 @@ import { isSystemControlChannel } from '../system-channels.js';
|
|
|
28
28
|
import { launchMonitorAndConfirm, resolveMonitorLaunch, cleanupMonitorStage } from '../../utils/cross-platform.js';
|
|
29
29
|
import { scanInstances } from '../../utils/instance-registry.js';
|
|
30
30
|
import { inspectDataMigrationRequirement } from '../data-migration.js';
|
|
31
|
-
import { guardIdleCommand, guardKnownCommand, guardRoleCommand, guardThreadCommand, isRecognizedSlashCommand, normalizeSlashContent, probeSessionActivity, } from './slash-gate.js';
|
|
31
|
+
import { guardIdleCommand, guardGroupSlashCommand, guardKnownCommand, guardRoleCommand, guardThreadCommand, isRecognizedSlashCommand, normalizeSlashContent, probeSessionActivity, } from './slash-gate.js';
|
|
32
32
|
const allEfforts = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
33
33
|
const PERMISSION_MODE_KEYS = PUBLIC_PERMISSION_MODES;
|
|
34
34
|
function defaultSlashChatmode(field) {
|
|
@@ -368,7 +368,7 @@ async function getGitWorkingDirInfo(projectPath) {
|
|
|
368
368
|
return null; // git 命令失败时静默返回 null
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
|
-
export async function handleSlashCommand(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, overrideSubject, replyContext) {
|
|
371
|
+
export async function handleSlashCommand(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, overrideSubject, replyContext, applyGroupSlashPolicy = false) {
|
|
372
372
|
// 卡片回调的 chatType 不可靠(飞书 bot 单聊 chatId 也是 oc_ 前缀),
|
|
373
373
|
// 不应覆盖 session 中已有的正确值
|
|
374
374
|
if (source === 'card-trigger')
|
|
@@ -434,9 +434,18 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
434
434
|
logger.warn(`[CommandHandler] topic session lookup failed: thread=${threadId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
|
-
const activeChatType =
|
|
437
|
+
const activeChatType = (applyGroupSlashPolicy
|
|
438
|
+
? replyContext?.delivery?.chatType || overrideSubject?.chatType || chatType
|
|
439
|
+
: undefined)
|
|
440
|
+
|| topicAuthSession?.chatType
|
|
438
441
|
|| activeSession?.chatType
|
|
439
442
|
|| (chatType === 'group' ? 'group' : 'private');
|
|
443
|
+
const groupSlash = applyGroupSlashPolicy && activeChatType === 'group';
|
|
444
|
+
if (applyGroupSlashPolicy) {
|
|
445
|
+
const groupGuard = guardGroupSlashCommand(normalizedContent, activeChatType);
|
|
446
|
+
if (groupGuard)
|
|
447
|
+
return groupGuard;
|
|
448
|
+
}
|
|
440
449
|
const getExistingSessionForCommand = async () => {
|
|
441
450
|
if (threadId) {
|
|
442
451
|
if (topicAuthSessionLookupFailed)
|
|
@@ -485,7 +494,8 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
485
494
|
|| normalizedContent === '/upgrade'
|
|
486
495
|
|| normalizedContent === '/reload'
|
|
487
496
|
|| normalizedContent.startsWith('/reload ')
|
|
488
|
-
|| normalizedContent === '/pair'
|
|
497
|
+
|| normalizedContent === '/pair'
|
|
498
|
+
|| /^\/diag(?:\s|$)/.test(normalizedContent);
|
|
489
499
|
const roleGuard = guardRoleCommand(normalizedContent, activeChatType, isAdmin || (isDaemonOwner && isProcessLevelSlash));
|
|
490
500
|
if (roleGuard)
|
|
491
501
|
return roleGuard;
|
|
@@ -617,6 +627,30 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
617
627
|
};
|
|
618
628
|
// /help 命令不需要会话
|
|
619
629
|
if (normalizedContent === '/help') {
|
|
630
|
+
if (groupSlash) {
|
|
631
|
+
const lines = [
|
|
632
|
+
'群聊可用命令:',
|
|
633
|
+
' /help - 显示帮助',
|
|
634
|
+
' /status - 显示会话状态',
|
|
635
|
+
' /check - 检查 Agent 健康',
|
|
636
|
+
' /model [current|list|check] - 查询模型或检查网关',
|
|
637
|
+
' /effort - 查看推理强度',
|
|
638
|
+
' /baseagent - 查看 Agent 后端',
|
|
639
|
+
' /perm - 查看权限模式',
|
|
640
|
+
' /mentionmode - 查看群聊 @ 处理模式',
|
|
641
|
+
' /ask <选项> - 回答待处理问题',
|
|
642
|
+
' /pause - 在下一次工具调用前暂停任务',
|
|
643
|
+
' /resume - 继续已暂停的任务',
|
|
644
|
+
' /stop - 中断当前任务',
|
|
645
|
+
...(isAdmin ? [
|
|
646
|
+
' /compact - 压缩会话上下文',
|
|
647
|
+
...(threadId ? [' /renew - 轮换当前话题的后端会话'] : []),
|
|
648
|
+
] : []),
|
|
649
|
+
'',
|
|
650
|
+
'模型、推理强度、后端、权限与 @ 模式在群聊中仅支持查询。',
|
|
651
|
+
];
|
|
652
|
+
return { kind: 'command.result', text: lines.join('\n') };
|
|
653
|
+
}
|
|
620
654
|
const canReadModel = canAccessSlashOperation('model.current', 'relation')
|
|
621
655
|
|| canAccessSlashOperation('model.list', 'relation');
|
|
622
656
|
const canUseModel = canAccessSlashOperation('model.use', 'relation');
|
|
@@ -633,7 +667,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
633
667
|
const appendProcessCommands = (lines) => {
|
|
634
668
|
if (!isDaemonOwner)
|
|
635
669
|
return;
|
|
636
|
-
lines.push('', '🛠️ 进程级运维:', ' /restart - 重启服务', ' /upgrade - 检查版本更新', ' /reload [aid] - 热重载 Agent 配置', ' /fa <提示词> - 单次使用宿主级 fullaccess 执行(别名: /fullaccess)', ' /pair - 获取远程控制配对码');
|
|
670
|
+
lines.push('', '🛠️ 进程级运维:', ' /restart - 重启服务', ' /upgrade - 检查版本更新', ' /reload [aid] - 热重载 Agent 配置', ' /fa <提示词> - 单次使用宿主级 fullaccess 执行(别名: /fullaccess)', ' /pair - 获取远程控制配对码', ' /diag <问题描述> - 邀请远程诊断;/diag stop 撤销授权');
|
|
637
671
|
};
|
|
638
672
|
if (!isAdmin && activeChatType === 'group') {
|
|
639
673
|
const lines = [
|
|
@@ -773,6 +807,14 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
773
807
|
];
|
|
774
808
|
return { kind: 'command.result', text: lines.join('\n') };
|
|
775
809
|
}
|
|
810
|
+
if (/^\/diag(?:\s|$)/.test(normalizedContent)) {
|
|
811
|
+
if (!isDaemonOwner || !userId || !this.diagService)
|
|
812
|
+
return { kind: 'command.error', text: '❌ /diag 需要本机 DaemonOwner 和可用的诊断入口' };
|
|
813
|
+
const problem = content.trim().slice(5).trim();
|
|
814
|
+
const result = await this.diagService.execute(problem.toLowerCase() === 'stop'
|
|
815
|
+
? { action: 'stop' } : { action: 'start', problem }, userId);
|
|
816
|
+
return { kind: result.ok ? 'command.result' : 'command.error', text: result.message };
|
|
817
|
+
}
|
|
776
818
|
// /pair is also recognized on an Agent channel so it cannot fall through
|
|
777
819
|
// into model input. The daemon control channel has its own fast path; this
|
|
778
820
|
// branch preserves the same daemon-owner boundary for ordinary AUN chats.
|
|
@@ -792,6 +834,26 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
792
834
|
}
|
|
793
835
|
// /evolhelp 命令:返回 JSON 格式的命令列表(供程序解析)
|
|
794
836
|
if (normalizedContent === '/evolhelp') {
|
|
837
|
+
if (groupSlash) {
|
|
838
|
+
const commands = [
|
|
839
|
+
{ command: '/help', description: '显示帮助信息', category: '帮助', roles: ['visitor', 'member', 'admin', 'owner'] },
|
|
840
|
+
{ command: '/status', description: '显示会话状态', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] },
|
|
841
|
+
{ command: '/check', description: '检查 EvolAgent 实例健康', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] },
|
|
842
|
+
{ command: '/model', args: '[current|list|check]', description: '查询模型或检查模型网关', category: 'Agent 与模型', roles: [identity.role] },
|
|
843
|
+
{ command: '/effort', description: '查看推理强度', category: 'Agent 与模型', roles: [identity.role] },
|
|
844
|
+
{ command: '/baseagent', description: '查看 Agent 后端', category: 'Agent 与模型', roles: [identity.role] },
|
|
845
|
+
{ command: '/perm', description: '查看当前权限模式', category: '权限管理', roles: [identity.role] },
|
|
846
|
+
{ command: '/mentionmode', description: '查看群聊 @ 处理模式', category: '聊天设置', roles: [identity.role] },
|
|
847
|
+
{ command: '/ask', args: '<选项>', description: '回答 Agent 的交互式问题', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] },
|
|
848
|
+
{ command: '/pause', description: '在下一次工具调用前暂停当前任务', category: '运维', roles: ['admin', 'owner'] },
|
|
849
|
+
{ command: '/resume', description: '继续已暂停的当前任务', category: '运维', roles: ['admin', 'owner'] },
|
|
850
|
+
{ command: '/stop', description: '中断当前任务', category: '运维', roles: ['admin', 'owner'] },
|
|
851
|
+
];
|
|
852
|
+
if (isAdmin) {
|
|
853
|
+
commands.push({ command: '/compact', description: '压缩会话上下文', category: '会话管理', roles: ['admin', 'owner'] }, ...(threadId ? [{ command: '/renew', description: '轮换当前话题 backend', category: '会话管理', roles: ['admin', 'owner'] }] : []));
|
|
854
|
+
}
|
|
855
|
+
return { kind: 'command.result', text: JSON.stringify({ commands, categories: [...new Set(commands.map(c => c.category))] }) };
|
|
856
|
+
}
|
|
795
857
|
const cmds = [];
|
|
796
858
|
const canReadModel = canAccessSlashOperation('model.current', 'relation')
|
|
797
859
|
|| canAccessSlashOperation('model.list', 'relation');
|
|
@@ -851,6 +913,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
851
913
|
cmds.push({ command: '/upgrade', description: '检查版本更新', category: '运维', roles: ['daemon-owner'] });
|
|
852
914
|
cmds.push({ command: '/reload', args: '[aid]', description: '热重载 Agent 配置', category: '运维', roles: ['daemon-owner'] });
|
|
853
915
|
cmds.push({ command: '/pair', description: '获取远程控制配对码', category: '运维', roles: ['daemon-owner'] });
|
|
916
|
+
cmds.push({ command: '/diag', args: '<问题描述>|stop', description: '邀请远程诊断或撤销授权', category: '运维', roles: ['daemon-owner'] });
|
|
854
917
|
}
|
|
855
918
|
if (!isDaemonOwner && isAdmin) {
|
|
856
919
|
cmds.push({ command: '/reload', description: '热重载当前 Agent 配置', category: '运维', roles: ['admin', 'owner'] });
|
|
@@ -1063,7 +1126,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1063
1126
|
if (!args) {
|
|
1064
1127
|
const activeBaseagent = owningAgent?.baseagent || this.parseDefaultBaseagent();
|
|
1065
1128
|
// 尝试发送 CommandCard 卡片
|
|
1066
|
-
if (this.interactionRouter && available.length > 1) {
|
|
1129
|
+
if (!groupSlash && this.interactionRouter && available.length > 1) {
|
|
1067
1130
|
const baseagentSession = await getExistingSessionForCommand();
|
|
1068
1131
|
const interaction = {
|
|
1069
1132
|
type: 'interaction',
|
|
@@ -1090,7 +1153,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1090
1153
|
}
|
|
1091
1154
|
// 降级:文本
|
|
1092
1155
|
const list = available.map((a) => `${a === activeBaseagent ? ' ✓' : ' '} ${a}`).join('\n');
|
|
1093
|
-
const canSwitchAgent = isOwner;
|
|
1156
|
+
const canSwitchAgent = isOwner && !groupSlash;
|
|
1094
1157
|
if (canSwitchAgent) {
|
|
1095
1158
|
return { kind: 'command.result', text: `当前 Baseagent: ${activeBaseagent}\n\n可用:\n${list}\n用法: /baseagent <name>` };
|
|
1096
1159
|
}
|
|
@@ -1348,7 +1411,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1348
1411
|
isDaemonOwner,
|
|
1349
1412
|
});
|
|
1350
1413
|
// 尝试发送 CommandCard 卡片
|
|
1351
|
-
if (!args && canReadModelList && this.interactionRouter && models.length > 0) {
|
|
1414
|
+
if (!groupSlash && !args && canReadModelList && this.interactionRouter && models.length > 0) {
|
|
1352
1415
|
const interaction = {
|
|
1353
1416
|
type: 'interaction',
|
|
1354
1417
|
id: `model-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`,
|
|
@@ -1379,7 +1442,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1379
1442
|
const catalogHint = `[${catalog.source}/${catalog.freshness}] ${catalog.diagnostics.map(d => d.message).join('; ')}`;
|
|
1380
1443
|
const modelList = models.map((m) => ` ${modelMatches(modelAgent, m, currentModel) ? '✓' : ' '} ${modelDisplayLabel(modelAgent, m)}`).join('\n');
|
|
1381
1444
|
const effortHint = efforts.length > 0
|
|
1382
|
-
? `\n推理强度: ${currentEffort === 'auto' ? 'auto (SDK默认)' : currentEffort} (使用 /effort 调整)`
|
|
1445
|
+
? `\n推理强度: ${currentEffort === 'auto' ? 'auto (SDK默认)' : currentEffort}${groupSlash ? '' : ' (使用 /effort 调整)'}`
|
|
1383
1446
|
: '';
|
|
1384
1447
|
if (args === 'current') {
|
|
1385
1448
|
return {
|
|
@@ -1389,7 +1452,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1389
1452
|
};
|
|
1390
1453
|
}
|
|
1391
1454
|
if (canReadModelList) {
|
|
1392
|
-
const usage = !args && isAdmin ? '\n\n用法: /model <模型>' : '';
|
|
1455
|
+
const usage = !groupSlash && !args && isAdmin ? '\n\n用法: /model <模型>' : '';
|
|
1393
1456
|
return {
|
|
1394
1457
|
kind: 'command.result',
|
|
1395
1458
|
text: `当前模型: ${modelDisplayLabel(modelAgent, currentModel)}${effortHint}${rawModels.includes(currentModel) ? '' : ' [当前配置,未列入目录]'}\n${catalogHint}\n\n可用模型:\n${modelList}${usage}`,
|
|
@@ -1659,7 +1722,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1659
1722
|
return { kind: 'command.error', text: '⚠️ 当前模型不支持推理强度设置' };
|
|
1660
1723
|
}
|
|
1661
1724
|
// /effort(无参数):显示当前推理强度 + 发送 CommandCard 卡片
|
|
1662
|
-
if (this.interactionRouter) {
|
|
1725
|
+
if (!groupSlash && this.interactionRouter) {
|
|
1663
1726
|
const allItems = [...efforts, 'auto'];
|
|
1664
1727
|
const interaction = {
|
|
1665
1728
|
type: 'interaction',
|
|
@@ -1687,7 +1750,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1687
1750
|
// 降级:文本
|
|
1688
1751
|
const effortDisplay = currentEffort === 'auto' ? 'auto (SDK默认)' : currentEffort;
|
|
1689
1752
|
const effortOptions = [...efforts, 'auto'].join(' / ');
|
|
1690
|
-
if (isAdmin) {
|
|
1753
|
+
if (isAdmin && !groupSlash) {
|
|
1691
1754
|
return { kind: 'command.result', text: `推理强度: ${effortDisplay} 可选: ${effortOptions} 用法: /effort <level>` };
|
|
1692
1755
|
}
|
|
1693
1756
|
return { kind: 'command.result', text: `推理强度: ${effortDisplay}` };
|
|
@@ -2109,6 +2172,8 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2109
2172
|
const currentMode = readSlashMentionMode(mentionTarget, mentionFallback);
|
|
2110
2173
|
if (!arg) {
|
|
2111
2174
|
const displayMode = currentMode ?? '未设置(跟随群设置)';
|
|
2175
|
+
if (groupSlash)
|
|
2176
|
+
return { kind: 'command.result', text: `@ 处理模式: ${displayMode}` };
|
|
2112
2177
|
// 尝试发送 CommandCard 卡片
|
|
2113
2178
|
if (isAdmin) {
|
|
2114
2179
|
const modes = [
|
|
@@ -3223,10 +3288,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3223
3288
|
// /slist cli — 仅 CLI 会话(未导入的)
|
|
3224
3289
|
if (normalizedContent === '/slist' || normalizedContent === '/slist cli') {
|
|
3225
3290
|
if (!session) {
|
|
3226
|
-
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3227
|
-
|
|
3228
|
-
请先执行以下操作之一:
|
|
3229
|
-
1. 发送任意消息 - 自动创建新会话
|
|
3291
|
+
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3292
|
+
|
|
3293
|
+
请先执行以下操作之一:
|
|
3294
|
+
1. 发送任意消息 - 自动创建新会话
|
|
3230
3295
|
2. /new [名称] - 创建命名会话` };
|
|
3231
3296
|
}
|
|
3232
3297
|
const showCliOnly = normalizedContent === '/slist cli';
|
|
@@ -3497,11 +3562,11 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3497
3562
|
if (!newName)
|
|
3498
3563
|
return { kind: 'command.result', text: '用法: /name <新名称> 或 /rename <新名称>' };
|
|
3499
3564
|
if (!session) {
|
|
3500
|
-
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3501
|
-
|
|
3502
|
-
请先执行以下操作之一:
|
|
3503
|
-
1. 发送任意消息 - 自动创建新会话
|
|
3504
|
-
2. /new [名称] - 创建命名会话
|
|
3565
|
+
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3566
|
+
|
|
3567
|
+
请先执行以下操作之一:
|
|
3568
|
+
1. 发送任意消息 - 自动创建新会话
|
|
3569
|
+
2. /new [名称] - 创建命名会话
|
|
3505
3570
|
3. /session <名称> - 切换到已有会话` };
|
|
3506
3571
|
}
|
|
3507
3572
|
const existing = await this.sessionManager.getSessionByName(channel, channelId, newName);
|