evolcore 0.0.26 → 0.0.27
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 +25 -0
- package/bin/codex-managed-acl.ps1 +43 -0
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/claude-runner.js +123 -22
- package/dist/agents/codex/app-server-client.js +63 -26
- package/dist/agents/codex-runner.js +316 -411
- package/dist/agents/ecagent-runner.js +170 -25
- package/dist/agents/gemini-runner.js +6 -0
- package/dist/agents/runner-types.js +50 -2
- package/dist/aun/aid/agentmd.js +73 -31
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +3 -3
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +161 -38
- package/dist/aun/{menu-client.js → menu/client.js} +12 -7
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +84 -18
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +13 -8
- package/dist/aun/msg/upload.js +43 -88
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +522 -233
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +108 -6
- package/dist/cli/agent-create.js +515 -2
- package/dist/cli/agent.js +109 -8
- package/dist/cli/aun-commands.js +191 -42
- package/dist/cli/config.js +8 -5
- package/dist/cli/contact.js +2 -2
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +35 -23
- package/dist/cli/diag.js +2 -2
- package/dist/cli/fs-command.js +3 -3
- package/dist/cli/handoff-command.js +6 -6
- package/dist/cli/index.js +8 -2
- package/dist/cli/init.js +34 -1
- package/dist/cli/managed-command-guard.js +2 -1
- package/dist/cli/menu-client.js +9 -5
- package/dist/cli/model.js +3 -3
- package/dist/cli/queue-command.js +2 -2
- package/dist/cli/response.js +2 -2
- package/dist/cli/restart-monitor.js +16 -2
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +20 -10
- package/dist/config/builtin-roles.js +10 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +20 -5
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +1 -1
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +5 -0
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +34 -38
- package/dist/core/auth/operation-authorizer.js +67 -5
- package/dist/core/auth/operation-catalog.js +110 -0
- package/dist/core/auth/remote-access-store.js +115 -9
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +110 -19
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +76 -39
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +34 -4
- package/dist/core/command/menu-handler.js +348 -77
- package/dist/core/command/menu-protocol.js +40 -6
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-handler.js +37 -22
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +76 -15
- package/dist/core/message/message-bridge.js +94 -11
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +73 -41
- package/dist/core/message/response-engine.js +709 -117
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +61 -29
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +72 -3
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +7 -7
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +34 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +308 -54
- package/dist/ipc.js +141 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +2 -0
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-utils.js +40 -2
- package/dist/utils/logger.js +23 -13
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +174 -1
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/INDEX.md +0 -6
- package/kits/docs/channels/aun.md +15 -2
- package/kits/docs/channels/feishu.md +1 -1
- package/kits/docs/context-assembly.md +29 -17
- package/kits/docs/evolcore/INDEX.md +8 -3
- package/kits/docs/evolcore/agent-create.md +39 -7
- package/kits/docs/evolcore/agent.md +33 -1
- package/kits/docs/evolcore/aid.md +9 -1
- package/kits/docs/evolcore/config.md +40 -1
- package/kits/docs/evolcore/group-collaboration.md +1 -1
- package/kits/docs/evolcore/group.md +117 -2
- package/kits/docs/evolcore/menu-client.md +16 -0
- package/kits/docs/evolcore/msg.md +3 -0
- package/kits/docs/evolcore/storage.md +3 -0
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +51 -3
- package/kits/docs/venues/group.md +5 -0
- package/kits/eck_manifest.json +27 -0
- package/kits/rules/01-overview.md +11 -7
- package/kits/rules/04-relation.md +2 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +6 -4
- package/kits/schemas/agent-config.schema.12.json +25 -1
- package/kits/schemas/daemon.schema.6.json +2 -2
- package/kits/schemas/defaults.schema.5.json +1 -1
- package/kits/schemas/relation-config.schema.1.json +1 -1
- package/kits/schemas/relation-config.schema.5.json +1 -1
- package/kits/schemas/role-config.schema.1.json +14 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/baseagent.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +26 -9
- package/kits/templates/system-fragments/channel.md +8 -44
- package/kits/templates/system-fragments/commands.md +10 -2
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/reply.md +56 -0
- package/package.json +7 -3
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
|
@@ -51,6 +51,8 @@ export function parseMenuClientProtectedHeaders(value) {
|
|
|
51
51
|
result.client_version = normalized.client_version;
|
|
52
52
|
if (normalized.evol_version !== undefined)
|
|
53
53
|
result.evol_version = normalized.evol_version;
|
|
54
|
+
if (normalized.sdk_version !== undefined)
|
|
55
|
+
result.sdk_version = normalized.sdk_version;
|
|
54
56
|
if (normalized.auth_method === 'daemon-owner' || normalized.auth_method === 'remote-access-token' || normalized.auth_method === 'ecweb-token')
|
|
55
57
|
result.auth_method = normalized.auth_method;
|
|
56
58
|
else if (normalized.auth_method !== undefined)
|
|
@@ -118,11 +120,16 @@ export const MENU_NAME_COMMANDS = Object.freeze({
|
|
|
118
120
|
dispatch: '/dispatch',
|
|
119
121
|
observable: '/observable',
|
|
120
122
|
system: '/system',
|
|
123
|
+
baseagents: '/baseagents',
|
|
121
124
|
cli: '/cli',
|
|
122
125
|
agent: '/agent',
|
|
123
126
|
aid: '/aid',
|
|
124
127
|
trigger: '/trigger',
|
|
125
128
|
file: '/file',
|
|
129
|
+
'agent-data-root': '/agent-data-root',
|
|
130
|
+
'relation-data-root': '/relation-data-root',
|
|
131
|
+
'agent-storage-root': '/agent-storage-root',
|
|
132
|
+
storage: '/storage',
|
|
126
133
|
gateway: '/gateway',
|
|
127
134
|
config: '/config',
|
|
128
135
|
capability: '/capability',
|
|
@@ -208,11 +215,17 @@ export function isEvolMenuVersionGateEnabled() {
|
|
|
208
215
|
export function isAunMenuTokenRequired() {
|
|
209
216
|
return loadDaemonConfig().aun?.menuTokenRequired === true;
|
|
210
217
|
}
|
|
211
|
-
export function evolMenuResponseTransportMetadata() {
|
|
218
|
+
export function evolMenuResponseTransportMetadata(sdkVersion) {
|
|
212
219
|
return {
|
|
213
220
|
// Clear inherited request state so AUNChannel applies daemon aun.defaultEncrypt.
|
|
214
221
|
encrypted: undefined,
|
|
215
|
-
protectedHeaders: {
|
|
222
|
+
protectedHeaders: {
|
|
223
|
+
ec_version: readInstalledEvolcoreVersion(),
|
|
224
|
+
...(typeof sdkVersion === 'string' && sdkVersion.trim() ? { sdk_version: sdkVersion.trim() } : {}),
|
|
225
|
+
},
|
|
226
|
+
...(typeof sdkVersion === 'string' && sdkVersion.trim()
|
|
227
|
+
? { menuClientSdkVersion: sdkVersion.trim() }
|
|
228
|
+
: {}),
|
|
216
229
|
};
|
|
217
230
|
}
|
|
218
231
|
const EXPLICIT_SCOPE_UPDATE_NAMES = new Set([
|
|
@@ -329,6 +342,12 @@ export function validateMenuRequest(request) {
|
|
|
329
342
|
return { code: 'INVALID_ARGUMENT', message: 'File fetch requires args.path' };
|
|
330
343
|
}
|
|
331
344
|
}
|
|
345
|
+
if ((request.name === 'file' || request.cmd === '/file') && request.args !== undefined) {
|
|
346
|
+
const args = request.args;
|
|
347
|
+
if (args.rootId !== undefined && (typeof args.rootId !== 'string' || args.rootId.trim().length === 0)) {
|
|
348
|
+
return { code: 'INVALID_ARGUMENT', message: 'File rootId must be a non-empty string' };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
332
351
|
return null;
|
|
333
352
|
}
|
|
334
353
|
export function menuSuccess(header, data) {
|
|
@@ -371,10 +390,10 @@ export function enforceMenuResponseSize(response, maxBytes = 128 * 1024) {
|
|
|
371
390
|
});
|
|
372
391
|
}
|
|
373
392
|
const INVALID_ARGUMENT_CODES = new Set([
|
|
374
|
-
'ARGUMENT_MISMATCH', '
|
|
393
|
+
'ARGUMENT_MISMATCH', 'INVALID_ARGS', 'INVALID_DELEGATION',
|
|
375
394
|
'INVALID_FIELD', 'INVALID_REVISION', 'INVALID_SCOPE', 'INVALID_TYPE', 'INVALID_VALUE', 'MISSING_AID',
|
|
376
395
|
'MISSING_ARGV', 'MISSING_BASEAGENT', 'MISSING_CMD', 'MISSING_PEER', 'MISSING_VALUE',
|
|
377
|
-
'
|
|
396
|
+
'REVISION_REQUIRED', 'ROLE_VIOLATION',
|
|
378
397
|
]);
|
|
379
398
|
const STABLE_CODES = new Set([
|
|
380
399
|
'ROLE_ACCESS_DENIED', 'PERMISSION_DENIED', 'UNAUTHORIZED', 'FORBIDDEN',
|
|
@@ -386,6 +405,9 @@ const STABLE_CODES = new Set([
|
|
|
386
405
|
'RATE_LIMITED', 'CATALOG_STALE', 'DATA_MIGRATION_REQUIRED',
|
|
387
406
|
'INVALID_CLIENT', 'REMOTE_ACCESS_TOKEN_INVALID', 'ECWEB_TOKEN_INVALID', 'CLIENT_BUSY', 'INVALID_RESPONSE', 'TIMEOUT',
|
|
388
407
|
'RESPONSE_TOO_LARGE',
|
|
408
|
+
'TOPIC_SESSION_NOT_FOUND', 'TOPIC_SESSION_LOOKUP_FAILED',
|
|
409
|
+
'ROOT_NOT_FOUND', 'ROOT_FORBIDDEN', 'ROOT_EXPIRED', 'ROOT_SCOPE_MISMATCH', 'ROOT_PEER_MISMATCH',
|
|
410
|
+
'INVALID_ROOT_ID', 'MISSING_ROOT_ID', 'INVALID_ROOT_PATH', 'NOT_A_DIRECTORY', 'FILE_TOO_LARGE',
|
|
389
411
|
]);
|
|
390
412
|
export function normalizeMenuError(error) {
|
|
391
413
|
const source = error && typeof error === 'object' ? error : {};
|
|
@@ -436,6 +458,16 @@ export class MenuRequestDeduper {
|
|
|
436
458
|
this.ttlMs = ttlMs;
|
|
437
459
|
this.maxEntries = maxEntries;
|
|
438
460
|
}
|
|
461
|
+
/** Replace an acknowledged deferred operation's result after it fails. */
|
|
462
|
+
async replaceResult(key, fingerprint, previous, value) {
|
|
463
|
+
const entry = this.entries.get(key);
|
|
464
|
+
if (!entry || entry.fingerprint !== fingerprint || await entry.promise !== previous)
|
|
465
|
+
return;
|
|
466
|
+
if (this.entries.get(key) !== entry)
|
|
467
|
+
return;
|
|
468
|
+
entry.promise = Promise.resolve(value);
|
|
469
|
+
entry.expiresAt = Date.now() + this.ttlMs;
|
|
470
|
+
}
|
|
439
471
|
async execute(key, fingerprint, operation) {
|
|
440
472
|
this.prune();
|
|
441
473
|
const existing = this.entries.get(key);
|
|
@@ -510,14 +542,16 @@ const MAX_LOG_DEPTH = 8;
|
|
|
510
542
|
const MAX_LOG_KEYS = 100;
|
|
511
543
|
const MAX_LOG_ARRAY_ITEMS = 100;
|
|
512
544
|
const MAX_LOG_STRING_LENGTH = 4096;
|
|
513
|
-
const SENSITIVE_LOG_KEY = /(?:^|[_-])(?:api[_-]?key|access[_-]?token|refresh[_-]?token|menu[_-]?token|token|secret(?:[_-]?key)?|password|passwd|credentials?|authorization|cookie|private[_-]?key|seed)(?:$|[_-])/i;
|
|
545
|
+
const SENSITIVE_LOG_KEY = /(?:^|[_-])(?:api[_-]?key|access[_-]?token|refresh[_-]?token|menu[_-]?token|root[_-]?id|token|secret(?:[_-]?key)?|password|passwd|credentials?|authorization|cookie|private[_-]?key|seed)(?:$|[_-])/i;
|
|
514
546
|
const SENSITIVE_LOG_ARG = /(?:api[_-]?key|access[_-]?token|refresh[_-]?token|menu[_-]?token|token|secret|password|passwd|credential|authorization|cookie|private[_-]?key|seed)/i;
|
|
515
547
|
function isSensitiveLogKey(key) {
|
|
516
548
|
const normalized = key.replace(/([a-z0-9])([A-Z])/g, '$1_$2');
|
|
517
|
-
return SENSITIVE_LOG_KEY.test(normalized);
|
|
549
|
+
return SENSITIVE_LOG_KEY.test(normalized) || /^(?:next[_-]?)?cursor$|^(?:download[_-]?)?url$|^ticket$/i.test(normalized);
|
|
518
550
|
}
|
|
519
551
|
function sanitizeInlineLogSecrets(value) {
|
|
520
552
|
return value
|
|
553
|
+
.replace(/\b(?:sr1|sc1|dr1)\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '[REDACTED]')
|
|
554
|
+
.replace(/https?:\/\/[^\s"'<>]*[?&](?:t|token|signature|x-amz-signature)=[^\s"'<>]*/gi, '[REDACTED_URL]')
|
|
521
555
|
.replace(/\b(authorization\s*:\s*Bearer\s+)[^\s"',;]+/gi, '$1[REDACTED]')
|
|
522
556
|
.replace(/\bBearer\s+[^\s,;]+/gi, 'Bearer [REDACTED]')
|
|
523
557
|
.replace(/\b(api[_-]?key|access[_-]?token|refresh[_-]?token|menu[_-]?token|token|password|passwd|secret|credential|authorization|cookie)\s*([=:])\s*(?:"[^"]*"|'[^']*'|\S+)/gi, '$1$2[REDACTED]')
|
|
@@ -16,9 +16,9 @@ import { fileCache } from '../daemon-file-cache.js';
|
|
|
16
16
|
import { currentVersion } from '../../config/schema-registry.js';
|
|
17
17
|
import { loadSchema } from '../../config/schema-registry.js';
|
|
18
18
|
import { rolePermissionFields, rolePermissionFieldOptions, rolePermissionFieldPatterns, validateRoleConfig, } from '../../config/role-schema.js';
|
|
19
|
-
import { groupInfo, groupList, groupDirectoryErrorCode } from '../../aun/
|
|
19
|
+
import { groupInfo, groupList, groupDirectoryErrorCode } from '../../aun/group/index.js';
|
|
20
20
|
import { aidLookup, isValidAid } from '../../aun/aid/identity.js';
|
|
21
|
-
import { isExplicitGroupId } from '../../aun/group
|
|
21
|
+
import { isExplicitGroupId } from '../../aun/group/index.js';
|
|
22
22
|
import { readJsonFile, readLastJsonlLine, scanChatDirs, scanMetaFiles } from '../session/session-fs-store.js';
|
|
23
23
|
import { constrainResolvedModelForRole } from '../model/model-permission.js';
|
|
24
24
|
import { resolveRuntimePermissionMode, resolveRuntimeStringField } from '../role/runtime-policy.js';
|
|
@@ -711,7 +711,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
711
711
|
' /del <名称> - 删除指定会话(仅解绑,不删除文件)',
|
|
712
712
|
...(threadId ? [' /renew - 在当前话题中轮换 backend'] : []),
|
|
713
713
|
' /pause - 在下一次工具调用前暂停当前任务',
|
|
714
|
-
' /resume -
|
|
714
|
+
' /resume - 继续暂停或异常中断的当前任务',
|
|
715
715
|
' /stop - 中断当前任务',
|
|
716
716
|
' /status - 显示会话状态',
|
|
717
717
|
' /check - 检查 EvolAgent 实例健康',
|
|
@@ -761,7 +761,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
761
761
|
' /compact - 压缩会话上下文(减少 token 用量)',
|
|
762
762
|
' /renew - 在话题中轮换 backend(下一条消息懒初始化)',
|
|
763
763
|
' /pause - 在下一次工具调用前暂停当前任务',
|
|
764
|
-
' /resume -
|
|
764
|
+
' /resume - 继续暂停或异常中断的当前任务',
|
|
765
765
|
' /stop - 中断当前任务',
|
|
766
766
|
'',
|
|
767
767
|
'🤖 Agent 与模型:',
|
|
@@ -782,7 +782,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
782
782
|
'🛠️ 运维:',
|
|
783
783
|
' /status - 显示会话状态',
|
|
784
784
|
' /pause - 在下一次工具调用前暂停当前任务',
|
|
785
|
-
' /resume -
|
|
785
|
+
' /resume - 继续暂停或异常中断的当前任务',
|
|
786
786
|
' /stop - 中断当前任务',
|
|
787
787
|
' /check - 检查 EvolAgent 实例健康',
|
|
788
788
|
...(isOwner ? [
|
|
@@ -825,11 +825,11 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
825
825
|
text: '❌ /pair 仅限 daemon-owner 使用;请向 daemon 控制 AID 发送 /pair。',
|
|
826
826
|
};
|
|
827
827
|
}
|
|
828
|
-
const pair = remoteAccessService.getPairingCode(userId);
|
|
828
|
+
const pair = remoteAccessService.getPairingCode(userId, Date.now(), 'pair');
|
|
829
829
|
const mins = Math.max(0, Math.round((pair.expiresAt - Date.now()) / 60000));
|
|
830
830
|
return {
|
|
831
831
|
kind: 'command.result',
|
|
832
|
-
text: `远程控制配对码:${pair.code}(约 ${mins}
|
|
832
|
+
text: `远程控制配对码:${pair.code}(约 ${mins} 分钟内有效,仅可配对一次)\n可用于 ECWeb 登录或远程 Menu 客户端授权`,
|
|
833
833
|
};
|
|
834
834
|
}
|
|
835
835
|
// /evolhelp 命令:返回 JSON 格式的命令列表(供程序解析)
|
|
@@ -898,7 +898,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
898
898
|
// 运维
|
|
899
899
|
cmds.push({ command: '/status', description: '显示会话状态', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] });
|
|
900
900
|
cmds.push({ command: '/pause', description: '在下一次工具调用前暂停当前任务', category: '运维', roles: ['admin', 'owner'] });
|
|
901
|
-
cmds.push({ command: '/resume', description: '
|
|
901
|
+
cmds.push({ command: '/resume', description: '继续暂停或异常中断的当前任务', category: '运维', roles: ['admin', 'owner'] });
|
|
902
902
|
cmds.push({ command: '/stop', description: '中断当前任务', category: '运维', roles: ['admin', 'owner'] });
|
|
903
903
|
cmds.push({ command: '/check', description: '检查 EvolAgent 实例健康', category: '运维', roles: ['visitor', 'member', 'admin', 'owner'] });
|
|
904
904
|
if (isAdmin) {
|
|
@@ -1071,19 +1071,20 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1071
1071
|
if (!args) {
|
|
1072
1072
|
if (!askSession)
|
|
1073
1073
|
return { kind: 'command.result', text: '当前没有待回答的问题' };
|
|
1074
|
-
const pendingIds = this.interactionRouter?.getPending(askSession.id) || [];
|
|
1074
|
+
const pendingIds = this.interactionRouter?.getPending(askSession.id, 'ask') || [];
|
|
1075
1075
|
if (pendingIds.length === 0)
|
|
1076
1076
|
return { kind: 'command.result', text: '当前没有待回答的问题' };
|
|
1077
|
-
return { kind: 'command.result',
|
|
1077
|
+
return { kind: 'command.result',
|
|
1078
|
+
text: `当前有 ${pendingIds.length} 个待回答问题,请回复 /ask <问题ID> <回答>:\n${pendingIds.join('\n')}` };
|
|
1078
1079
|
}
|
|
1079
1080
|
if (!askSession)
|
|
1080
1081
|
return { kind: 'command.error', text: '❌ 当前没有待回答的问题' };
|
|
1081
|
-
const fb = await this.handleInteractionFallback('ask', args, askSession.id, userId);
|
|
1082
|
+
const fb = await this.handleInteractionFallback('ask', args, askSession.id, userId, this.getAdapter(channel)?.channelKey);
|
|
1082
1083
|
if (fb.matched)
|
|
1083
1084
|
return { kind: 'command.result', text: fb.result ?? '✓ 已回答' };
|
|
1084
1085
|
return { kind: 'command.error', text: '❌ 当前没有待回答的问题' };
|
|
1085
1086
|
}
|
|
1086
|
-
// /pause
|
|
1087
|
+
// /pause gates the next tool; /resume also recovers a failed backend turn.
|
|
1087
1088
|
if (normalizedContent.startsWith('/pause ')) {
|
|
1088
1089
|
return { kind: 'command.error', text: '用法: /pause' };
|
|
1089
1090
|
}
|
|
@@ -1111,8 +1112,16 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1111
1112
|
const resumeSession = await getExistingSessionForCommand();
|
|
1112
1113
|
if (!resumeSession)
|
|
1113
1114
|
return { kind: 'command.result', text: '当前没有活跃会话' };
|
|
1114
|
-
const resumed = this.processor.
|
|
1115
|
-
|
|
1115
|
+
const resumed = await this.processor.recoverTask({
|
|
1116
|
+
sessionId: resumeSession.id,
|
|
1117
|
+
agentAid: resumeSession.selfAID,
|
|
1118
|
+
peerId: userId,
|
|
1119
|
+
encrypted: replyContext?.metadata?.encrypted,
|
|
1120
|
+
});
|
|
1121
|
+
return {
|
|
1122
|
+
kind: resumed.ok ? 'command.result' : 'command.error',
|
|
1123
|
+
text: resumed.message ?? (resumed.ok ? '已继续当前任务' : '任务恢复失败'),
|
|
1124
|
+
};
|
|
1116
1125
|
}
|
|
1117
1126
|
// /baseagent 命令:查看或切换 Agent 后端
|
|
1118
1127
|
if (normalizedContent === '/baseagent' || normalizedContent.startsWith('/baseagent ')) {
|
|
@@ -1536,10 +1545,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1536
1545
|
}
|
|
1537
1546
|
else {
|
|
1538
1547
|
const resolvedArg = hasModelSwitcher(modelAgent) ? (modelAgent.resolveModelId?.(arg) ?? arg) : arg;
|
|
1539
|
-
if (models.includes(resolvedArg)) {
|
|
1540
|
-
newModel = resolvedArg;
|
|
1541
|
-
}
|
|
1542
|
-
else if (models.includes(arg)) {
|
|
1548
|
+
if (models.includes(resolvedArg) || models.includes(arg)) {
|
|
1543
1549
|
newModel = arg;
|
|
1544
1550
|
}
|
|
1545
1551
|
else {
|
|
@@ -1566,7 +1572,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1566
1572
|
const errorLabel = allEfforts.includes(effortArg) ? '⚠️' : '❌';
|
|
1567
1573
|
return { kind: 'command.result', text: `${errorLabel} ${modelArg} 不支持 ${effortArg} 推理强度\n可选: ${targetEfforts.join(' / ')}` };
|
|
1568
1574
|
}
|
|
1569
|
-
newModel =
|
|
1575
|
+
newModel = modelArgRaw;
|
|
1570
1576
|
newEffort = effortArg;
|
|
1571
1577
|
}
|
|
1572
1578
|
// 优先写关系级覆盖;只有无法定位关系且具备管理权限时才回退到 runner/agent 级。
|
|
@@ -1606,11 +1612,13 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1606
1612
|
requestedModel: newModel,
|
|
1607
1613
|
models,
|
|
1608
1614
|
resolveModelId: modelAgent.resolveModelId?.bind(modelAgent),
|
|
1615
|
+
selfAid: modelSelector.self,
|
|
1609
1616
|
});
|
|
1610
1617
|
if (!decision.ok) {
|
|
1611
1618
|
return { kind: 'command.error', text: decision.message || `invalid model: ${newModel}` };
|
|
1612
1619
|
}
|
|
1613
|
-
|
|
1620
|
+
// The decision validates the resolved model; retain the user's selection
|
|
1621
|
+
// for configuration and replies, just like menu.update and local model use.
|
|
1614
1622
|
}
|
|
1615
1623
|
const changes = [];
|
|
1616
1624
|
if (!('error' in relationTarget)) {
|
|
@@ -2260,6 +2268,9 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2260
2268
|
const hasActive = stopAgent.hasActiveStream(sessionKey);
|
|
2261
2269
|
const isProcessing = this.messageQueue.isProcessing(sessionKey);
|
|
2262
2270
|
if (queueLength === 0 && !hasActive && !isProcessing) {
|
|
2271
|
+
// Even an idle session can have an undelivered task:error recovery.
|
|
2272
|
+
// Persist the stop fence before acknowledging that nothing is running.
|
|
2273
|
+
await this.processor.interruptSession(sessionKey, 'stop');
|
|
2263
2274
|
return { kind: 'command.result', text: '当前没有正在处理的任务' };
|
|
2264
2275
|
}
|
|
2265
2276
|
// 发布中断事件,让 MessageProcessor 标记为 interrupted(而非 done)
|
|
@@ -2551,6 +2562,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2551
2562
|
session.name = fileInfo.title;
|
|
2552
2563
|
}
|
|
2553
2564
|
}
|
|
2565
|
+
const sessionSubtasks = this.sessionManager.getRunnerTaskHistory(session.id).length;
|
|
2554
2566
|
const lines = [];
|
|
2555
2567
|
const chatMode = getEffectiveChatmode(session);
|
|
2556
2568
|
const sessionRole = session.identity?.role || identity.role || 'none';
|
|
@@ -2573,7 +2585,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2573
2585
|
if (gitInfo) {
|
|
2574
2586
|
lines.push(`Git: ${gitInfo}`);
|
|
2575
2587
|
}
|
|
2576
|
-
lines.push(`会话状态: ${sessionStatus} / 轮数: ${sessionTurns}`, sessionRoleLine, chatModeLine, ...(mentionModeLine ? [mentionModeLine] : []));
|
|
2588
|
+
lines.push(`会话状态: ${sessionStatus} / 轮数: ${sessionTurns} / 已记录子任务数: ${sessionSubtasks}`, sessionRoleLine, chatModeLine, ...(mentionModeLine ? [mentionModeLine] : []));
|
|
2577
2589
|
if (health.consecutiveErrors > 0) {
|
|
2578
2590
|
lines.push(`异常计数: ${health.consecutiveErrors}`);
|
|
2579
2591
|
}
|
|
@@ -2581,7 +2593,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2581
2593
|
}
|
|
2582
2594
|
else {
|
|
2583
2595
|
lines.push(`📊 ${isThread ? '话题' : '会话'}状态 (Agent: ${agentName}):`, `渠道: ${channel} / 项目: ${projectName} / ${session.baseagent}会话`);
|
|
2584
|
-
lines.push(`状态: ${sessionStatus} / 轮数: ${sessionTurns}`, sessionRoleLine, chatModeLine, ...(mentionModeLine ? [mentionModeLine] : []), `最后活跃: ${timeStr}`);
|
|
2596
|
+
lines.push(`状态: ${sessionStatus} / 轮数: ${sessionTurns} / 已记录子任务数: ${sessionSubtasks}`, sessionRoleLine, chatModeLine, ...(mentionModeLine ? [mentionModeLine] : []), `最后活跃: ${timeStr}`);
|
|
2585
2597
|
}
|
|
2586
2598
|
if (health.lastError) {
|
|
2587
2599
|
lines.push('');
|
|
@@ -2928,7 +2940,8 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2928
2940
|
text: `❌ ${excludeKey ? '其他会话' : '该 Agent'} 有 ${busyInfo.count} 个任务执行中,无法重启。\n\n处理中的会话:\n${processingLines}\n\n建议:等待这些任务完成后重试。可通过 ec ctl status 查看队列状态;典型等待时间 30-60 秒,群聊大型任务可能更长。`,
|
|
2929
2941
|
};
|
|
2930
2942
|
}
|
|
2931
|
-
|
|
2943
|
+
// Archiving only changes list visibility; pending work must still be checked.
|
|
2944
|
+
const allSessions = await this.sessionManager.listSessions(channel, channelId, { archiveFilter: 'all' });
|
|
2932
2945
|
const sessionsWithMessages = allSessions
|
|
2933
2946
|
.filter((s) => this.messageCache.hasMessages(s.id))
|
|
2934
2947
|
.map((s) => {
|
|
@@ -3649,7 +3662,9 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3649
3662
|
}
|
|
3650
3663
|
await interruptPausedSessionBeforeReplacement(session);
|
|
3651
3664
|
try {
|
|
3652
|
-
const forkedSessionId = await forkAgent.forkSession(session.agentSessionId, session.projectPath, forkName
|
|
3665
|
+
const forkedSessionId = await forkAgent.forkSession(session.agentSessionId, session.projectPath, forkName, {
|
|
3666
|
+
sessionId: session.id, permissionMode: authSubject.permissionMode, role: authSubject.role,
|
|
3667
|
+
});
|
|
3653
3668
|
const newSession = await this.sessionManager.createForkedSession(session, forkedSessionId, forkName);
|
|
3654
3669
|
await forkAgent.updateSessionMetadata?.(forkedSessionId, {
|
|
3655
3670
|
gitInfo: {
|
|
@@ -294,6 +294,10 @@ const CATALOG = [
|
|
|
294
294
|
description: 'agent 任务失败',
|
|
295
295
|
fields: [
|
|
296
296
|
{ path: 'sessionId', type: 'string' },
|
|
297
|
+
{ path: 'agentAid', type: 'string', optional: true },
|
|
298
|
+
{ path: 'peerId', type: 'string', optional: true },
|
|
299
|
+
{ path: 'encrypted', type: 'boolean', optional: true },
|
|
300
|
+
{ path: 'originTriggerId', type: 'string', optional: true },
|
|
297
301
|
{ path: 'taskId', type: 'string', optional: true },
|
|
298
302
|
{ path: 'generation', type: 'number', optional: true },
|
|
299
303
|
{ path: 'error', type: 'string' },
|
|
@@ -6,6 +6,7 @@ import { agentPersonalDir } from '../paths.js';
|
|
|
6
6
|
import { invalidateAgentDisplayName, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
7
7
|
import { loadAllAgents, ensureAgentDirSkeleton, loadAgent, validateAgentConfig, } from '../config-store.js';
|
|
8
8
|
import { ConfigTarget, read as readConfig, resolveEffective } from '../config/config-manager.js';
|
|
9
|
+
import { resolveAgentLifecycle } from '../config/lifecycle.js';
|
|
9
10
|
// ── Channel Fingerprint ───────────────────────────────────────────────────
|
|
10
11
|
// 用于检测多 agent 之间复用同一外部凭证的冲突(appId、aid、token 等)。
|
|
11
12
|
// 格式:{type}:{primaryKey}
|
|
@@ -58,6 +59,8 @@ export class EvolAgentRegistry {
|
|
|
58
59
|
agents = new Map();
|
|
59
60
|
/** channel key (`<type>#<selfAID>#<name>`) → agent aid */
|
|
60
61
|
channelIndex = new Map();
|
|
62
|
+
/** Agents stopped through stopAgent; distinct from the initial load state. */
|
|
63
|
+
manuallyStopped = new Set();
|
|
61
64
|
/** 启动期被 ConfigStore 跳过的目录(命名非法 / 缺 config.json / 校验失败等) */
|
|
62
65
|
skipped = [];
|
|
63
66
|
/**
|
|
@@ -81,6 +84,7 @@ export class EvolAgentRegistry {
|
|
|
81
84
|
void _globalConfig;
|
|
82
85
|
this.agents.clear();
|
|
83
86
|
this.channelIndex.clear();
|
|
87
|
+
this.manuallyStopped.clear();
|
|
84
88
|
this.skipped = [];
|
|
85
89
|
const { agents: rawAgents, skipped, invalidAgents = [] } = loadAllAgents({ includeInvalid: true });
|
|
86
90
|
this.skipped = skipped;
|
|
@@ -285,6 +289,7 @@ export class EvolAgentRegistry {
|
|
|
285
289
|
if (!hotLoad)
|
|
286
290
|
throw new Error(`Cannot enable agent "${aidOrName}": hot-load handler not initialized`);
|
|
287
291
|
this.agents.delete(oldAgent.aid);
|
|
292
|
+
this.manuallyStopped.delete(oldAgent.aid);
|
|
288
293
|
this.channelIndex.clear();
|
|
289
294
|
this.buildChannelIndex();
|
|
290
295
|
await hotLoad(oldAgent.aid);
|
|
@@ -298,6 +303,7 @@ export class EvolAgentRegistry {
|
|
|
298
303
|
const previousConfig = oldAgent.captureConfigSnapshot();
|
|
299
304
|
const previousStatus = oldAgent.status;
|
|
300
305
|
const previousError = oldAgent.error;
|
|
306
|
+
const previousManuallyStopped = this.manuallyStopped.has(oldAgent.aid);
|
|
301
307
|
try {
|
|
302
308
|
await hooks.prepareHandoffReload?.(oldAgent.aid);
|
|
303
309
|
prepared = true;
|
|
@@ -312,6 +318,7 @@ export class EvolAgentRegistry {
|
|
|
312
318
|
}
|
|
313
319
|
oldAgent.swapConfig(raw, merged);
|
|
314
320
|
oldAgent.status = 'disabled';
|
|
321
|
+
this.manuallyStopped.delete(oldAgent.aid);
|
|
315
322
|
this.channelIndex.clear();
|
|
316
323
|
this.buildChannelIndex();
|
|
317
324
|
runnerTransaction?.commit();
|
|
@@ -331,6 +338,10 @@ export class EvolAgentRegistry {
|
|
|
331
338
|
oldAgent.invalidatePersonaCache();
|
|
332
339
|
oldAgent.status = previousStatus;
|
|
333
340
|
oldAgent.error = previousError;
|
|
341
|
+
if (previousManuallyStopped)
|
|
342
|
+
this.manuallyStopped.add(oldAgent.aid);
|
|
343
|
+
else
|
|
344
|
+
this.manuallyStopped.delete(oldAgent.aid);
|
|
334
345
|
await runnerTransaction?.rollback().catch(rollbackError => {
|
|
335
346
|
rollbackErrors.push(`runners: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
|
|
336
347
|
});
|
|
@@ -370,11 +381,16 @@ export class EvolAgentRegistry {
|
|
|
370
381
|
const previousConfig = oldAgent.captureConfigSnapshot();
|
|
371
382
|
const previousStatus = oldAgent.status;
|
|
372
383
|
const previousError = oldAgent.error;
|
|
384
|
+
const previousManuallyStopped = this.manuallyStopped.has(oldAgent.aid);
|
|
373
385
|
try {
|
|
374
386
|
await hooks.prepareHandoffReload?.(oldAgent.aid);
|
|
375
387
|
prepared = true;
|
|
376
388
|
runnerTransaction = await hooks.stageAgentRunners?.(new EvolAgent(raw, merged));
|
|
377
|
-
|
|
389
|
+
// stopAgent removes runtime transports but preserves configuration.
|
|
390
|
+
// Reload transitions a manually stopped Agent back to running.
|
|
391
|
+
const oldChannels = this.manuallyStopped.has(oldAgent.aid)
|
|
392
|
+
? new Set()
|
|
393
|
+
: new Set(oldAgent.channelInstanceNames());
|
|
378
394
|
const aunKey = oldAgent.effectiveChannelName('aun', 'main');
|
|
379
395
|
const otherKeys = merged.channels.filter(c => c.type !== 'aun').map(c => oldAgent.effectiveChannelName(c.type, c.name));
|
|
380
396
|
const newChannels = new Set([aunKey, ...otherKeys]);
|
|
@@ -404,6 +420,7 @@ export class EvolAgentRegistry {
|
|
|
404
420
|
addedSuccessfully.push(ch);
|
|
405
421
|
}
|
|
406
422
|
oldAgent.status = 'running';
|
|
423
|
+
this.manuallyStopped.delete(oldAgent.aid);
|
|
407
424
|
this.channelIndex.clear();
|
|
408
425
|
this.buildChannelIndex();
|
|
409
426
|
runnerTransaction?.commit();
|
|
@@ -466,6 +483,10 @@ export class EvolAgentRegistry {
|
|
|
466
483
|
if (rollbackErrors.length === 0) {
|
|
467
484
|
oldAgent.status = previousStatus;
|
|
468
485
|
oldAgent.error = previousError;
|
|
486
|
+
if (previousManuallyStopped)
|
|
487
|
+
this.manuallyStopped.add(oldAgent.aid);
|
|
488
|
+
else
|
|
489
|
+
this.manuallyStopped.delete(oldAgent.aid);
|
|
469
490
|
}
|
|
470
491
|
else {
|
|
471
492
|
oldAgent.status = 'error';
|
|
@@ -481,8 +502,10 @@ export class EvolAgentRegistry {
|
|
|
481
502
|
throw new Error(`Agent "${aidOrName}" not found`);
|
|
482
503
|
if (agent.status === 'disabled')
|
|
483
504
|
throw new Error('Agent is disabled; use enable/disable instead');
|
|
484
|
-
if (agent.status === 'stopped')
|
|
505
|
+
if (agent.status === 'stopped') {
|
|
506
|
+
this.manuallyStopped.add(agent.aid);
|
|
485
507
|
return;
|
|
508
|
+
}
|
|
486
509
|
for (const ch of agent.channelInstanceNames()) {
|
|
487
510
|
try {
|
|
488
511
|
await hooks.disconnectChannel(ch);
|
|
@@ -490,6 +513,7 @@ export class EvolAgentRegistry {
|
|
|
490
513
|
catch { }
|
|
491
514
|
}
|
|
492
515
|
agent.status = 'stopped';
|
|
516
|
+
this.manuallyStopped.add(agent.aid);
|
|
493
517
|
this.channelIndex.clear();
|
|
494
518
|
this.buildChannelIndex();
|
|
495
519
|
logger.info(`[Registry] Stopped agent ${aidOrName}`);
|
|
@@ -508,6 +532,7 @@ export class EvolAgentRegistry {
|
|
|
508
532
|
await hooks.startChannel(agent, ch);
|
|
509
533
|
}
|
|
510
534
|
agent.status = 'running';
|
|
535
|
+
this.manuallyStopped.delete(agent.aid);
|
|
511
536
|
this.channelIndex.clear();
|
|
512
537
|
this.buildChannelIndex();
|
|
513
538
|
logger.info(`[Registry] Started agent ${aidOrName}`);
|
|
@@ -575,6 +600,7 @@ export class EvolAgentRegistry {
|
|
|
575
600
|
personalName,
|
|
576
601
|
aid: agent.aid,
|
|
577
602
|
status: agent.status,
|
|
603
|
+
lifecycle: resolveAgentLifecycle(agent.config),
|
|
578
604
|
channels,
|
|
579
605
|
projectPath,
|
|
580
606
|
baseagent,
|