evolcore 0.0.23 → 0.0.25
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 +583 -516
- package/LICENSE +21 -21
- package/README.md +455 -453
- package/bin/codex-managed-acl.ps1 +160 -155
- package/bin/codex-managed-hook.mjs +209 -92
- package/bin/ec-safe-output.js +327 -245
- package/bin/ec.js +29 -29
- package/bin/install-codex-managed-hooks.mjs +555 -544
- package/dist/agents/baseagent-availability.js +4 -4
- package/dist/agents/baseagent.js +29 -1
- package/dist/agents/claude-runner.js +152 -14
- package/dist/agents/codex/app-server-client.js +18 -8
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +215 -0
- package/dist/agents/codex-runner.js +445 -63
- package/dist/agents/ecagent-runner.js +31 -16
- package/dist/agents/gemini-runner.js +82 -4
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/agentmd.js +56 -4
- package/dist/aun/aid/index.js +1 -0
- package/dist/aun/aid/managed-operation.js +80 -6
- package/dist/aun/aid/store.js +41 -0
- package/dist/aun/msg/managed-operation.js +6 -1
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +4 -5
- package/dist/aun/storage/client.js +4 -1
- package/dist/aun/storage/index.js +1 -1
- package/dist/channels/aun.js +147 -76
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +214 -89
- package/dist/cli/agent-create.js +576 -0
- package/dist/cli/agent.js +129 -676
- package/dist/cli/aun-commands.js +419 -313
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +20 -20
- package/dist/cli/contact.js +10 -10
- package/dist/cli/ctl-command.js +22 -20
- package/dist/cli/daemon-commands.js +163 -32
- package/dist/cli/fs-command.js +67 -67
- package/dist/cli/handoff-command.js +7 -7
- package/dist/cli/index.js +79 -79
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +11 -11
- 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 +462 -38
- package/dist/cli/stats.js +161 -51
- package/dist/cli/trigger-command.js +56 -56
- package/dist/config/builtin-roles.js +3 -0
- package/dist/config/config-manager.js +15 -0
- package/dist/config/config-operation-service.js +20 -3
- package/dist/config/resolved-config-op.js +39 -1
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +72 -19
- package/dist/core/auth/operation-authorizer.js +125 -7
- package/dist/core/auth/operation-catalog.js +26 -2
- package/dist/core/auth/remote-access-store.js +3 -2
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +60 -26
- package/dist/core/command/cli-intent-parser.js +18 -0
- package/dist/core/command/command-handler.js +173 -36
- package/dist/core/command/menu-catalog.js +4 -1
- package/dist/core/command/menu-handler.js +315 -75
- package/dist/core/command/menu-protocol.js +7 -2
- package/dist/core/command/slash-gate.js +1 -3
- package/dist/core/command/slash-handler.js +166 -57
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +8 -0
- package/dist/core/evolagent.js +3 -3
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +85 -27
- package/dist/core/model/catalog-service.js +3 -1
- package/dist/core/model/model-catalog.js +10 -0
- package/dist/core/model/model-permission.js +4 -1
- package/dist/core/permission/approval-gateway.js +169 -33
- package/dist/core/permission/tool-policy.js +56 -10
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +327 -134
- package/dist/ipc.js +68 -21
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- 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/atomic-write.js +26 -2
- package/dist/utils/avatar-upload.js +282 -23
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +11 -4
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +24 -1
- package/dist/utils/welcome.js +22 -22
- package/dist/utils/windows-autostart.js +1 -1
- 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 +365 -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 -69
- package/kits/docs/evolcore/agent-create.md +170 -156
- package/kits/docs/evolcore/agent.md +83 -73
- package/kits/docs/evolcore/aid.md +64 -62
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
- package/kits/docs/evolcore/config.md +195 -195
- package/kits/docs/evolcore/contact.md +68 -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 +34 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +165 -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 +270 -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 +25 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +195 -183
- 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 +39 -37
- 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 +96 -82
- package/kits/templates/roles/member.json +97 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +86 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +38 -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 +98 -94
- package/AGENTS.md +0 -62
- package/dist/utils/codex-cli.js +0 -43
- package/skills/eclink/SKILL.md +0 -84
- package/skills/eclink/agents/openai.yaml +0 -4
|
@@ -25,8 +25,11 @@ export function buildMenuClientProtectedHeaders(input) {
|
|
|
25
25
|
...(input.remoteAccessToken || input.ecwebToken ? { auth_method: 'remote-access-token', remote_access_token: input.remoteAccessToken ?? input.ecwebToken } : {}),
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
const appClientVersion = input.clientVersion?.trim();
|
|
28
29
|
return {
|
|
30
|
+
...(clientName === 'Evol App' ? { client_name: clientName } : {}),
|
|
29
31
|
...(input.evolVersion ? { evol_version: input.evolVersion } : {}),
|
|
32
|
+
...(clientName === 'Evol App' && appClientVersion ? { client_version: appClientVersion } : {}),
|
|
30
33
|
...(input.remoteAccessToken || input.ecwebToken ? { auth_method: 'remote-access-token', remote_access_token: input.remoteAccessToken ?? input.ecwebToken } : {}),
|
|
31
34
|
};
|
|
32
35
|
}
|
|
@@ -40,7 +43,7 @@ export function parseMenuClientProtectedHeaders(value) {
|
|
|
40
43
|
normalized[key.trim().toLowerCase()] = raw;
|
|
41
44
|
}
|
|
42
45
|
const result = {};
|
|
43
|
-
if (normalized.client_name === 'evol' || normalized.client_name === 'ec-menu-client')
|
|
46
|
+
if (normalized.client_name === 'evol' || normalized.client_name === 'Evol App' || normalized.client_name === 'ec-menu-client')
|
|
44
47
|
result.client_name = normalized.client_name;
|
|
45
48
|
else if (normalized.client_name !== undefined)
|
|
46
49
|
result.client_name = normalized.client_name;
|
|
@@ -71,7 +74,7 @@ export function evaluateMenuClientCompatibility(input) {
|
|
|
71
74
|
}
|
|
72
75
|
return null;
|
|
73
76
|
}
|
|
74
|
-
if (clientName !== 'evol') {
|
|
77
|
+
if (clientName !== 'evol' && clientName !== 'Evol App') {
|
|
75
78
|
return { code: 'INVALID_CLIENT', message: `Unsupported Menu client: ${String(clientName)}` };
|
|
76
79
|
}
|
|
77
80
|
return evaluateEvolMenuVersionGate(input);
|
|
@@ -104,6 +107,7 @@ export const MENU_NAME_COMMANDS = Object.freeze({
|
|
|
104
107
|
session: '/session',
|
|
105
108
|
topic: '/topic',
|
|
106
109
|
baseagent: '/baseagent',
|
|
110
|
+
'baseagent-models': '/baseagent-models',
|
|
107
111
|
model: '/model',
|
|
108
112
|
effort: '/effort',
|
|
109
113
|
chatmode: '/chatmode',
|
|
@@ -116,6 +120,7 @@ export const MENU_NAME_COMMANDS = Object.freeze({
|
|
|
116
120
|
system: '/system',
|
|
117
121
|
cli: '/cli',
|
|
118
122
|
agent: '/agent',
|
|
123
|
+
aid: '/aid',
|
|
119
124
|
trigger: '/trigger',
|
|
120
125
|
file: '/file',
|
|
121
126
|
gateway: '/gateway',
|
|
@@ -119,10 +119,8 @@ export function guardThreadCommand(content, threadId) {
|
|
|
119
119
|
const threadBlocked = ['/new', '/slist', '/s', '/session', '/fork', '/del', '/repair'];
|
|
120
120
|
const hasCommandArgument = (command) => new RegExp(`^${command.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:\\s|$)`).test(content);
|
|
121
121
|
const isBlocked = threadBlocked.some(hasCommandArgument);
|
|
122
|
-
const isBaseagentSwitch = /^\/baseagent\s/.test(content);
|
|
123
|
-
const isBaseAliasSwitch = /^\/base\s/.test(content);
|
|
124
122
|
const isRewindMutation = /^\/rewind\s/.test(content);
|
|
125
|
-
if (!isBlocked && !
|
|
123
|
+
if (!isBlocked && !isRewindMutation)
|
|
126
124
|
return undefined;
|
|
127
125
|
return { kind: 'command.error', text: '⚠️ 话题中不支持此命令' };
|
|
128
126
|
}
|
|
@@ -25,7 +25,8 @@ import { chatmodeFieldForPeer, resolveChatModeForField } from '../message/peer-m
|
|
|
25
25
|
import { normalizePermissionMode as normalizePermissionModeContract, PUBLIC_PERMISSION_MODES } from '../permission/mode.js';
|
|
26
26
|
import { isManagementRole } from '../../config/builtin-roles.js';
|
|
27
27
|
import { isSystemControlChannel } from '../system-channels.js';
|
|
28
|
-
import {
|
|
28
|
+
import { launchMonitorAndConfirm, resolveMonitorLaunch, cleanupMonitorStage } from '../../utils/cross-platform.js';
|
|
29
|
+
import { scanInstances } from '../../utils/instance-registry.js';
|
|
29
30
|
import { inspectDataMigrationRequirement } from '../data-migration.js';
|
|
30
31
|
import { guardIdleCommand, guardKnownCommand, guardRoleCommand, guardThreadCommand, isRecognizedSlashCommand, normalizeSlashContent, probeSessionActivity, } from './slash-gate.js';
|
|
31
32
|
const allEfforts = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
@@ -1063,11 +1064,12 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1063
1064
|
const activeBaseagent = owningAgent?.baseagent || this.parseDefaultBaseagent();
|
|
1064
1065
|
// 尝试发送 CommandCard 卡片
|
|
1065
1066
|
if (this.interactionRouter && available.length > 1) {
|
|
1067
|
+
const baseagentSession = await getExistingSessionForCommand();
|
|
1066
1068
|
const interaction = {
|
|
1067
1069
|
type: 'interaction',
|
|
1068
1070
|
id: `agent-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`,
|
|
1069
1071
|
channelId,
|
|
1070
|
-
sessionId:
|
|
1072
|
+
sessionId: baseagentSession?.id || `agent-${Date.now()}`,
|
|
1071
1073
|
initiatorId: userId,
|
|
1072
1074
|
kind: {
|
|
1073
1075
|
kind: 'command-card',
|
|
@@ -1080,7 +1082,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1080
1082
|
})),
|
|
1081
1083
|
},
|
|
1082
1084
|
};
|
|
1083
|
-
const replyCtx =
|
|
1085
|
+
const replyCtx = baseagentSession ? this.getReplyContext(baseagentSession) : replyContext;
|
|
1084
1086
|
const cardResult = await this.sendCommandCard({ channel, channelId, interaction, replyCtx, canWrite: isOwner });
|
|
1085
1087
|
if (cardResult === null)
|
|
1086
1088
|
return null;
|
|
@@ -1100,29 +1102,79 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1100
1102
|
if (!owningAgent) {
|
|
1101
1103
|
return { kind: 'command.error', text: '❌ 当前 channel 无绑定 agent,无法设置 active_baseagent' };
|
|
1102
1104
|
}
|
|
1103
|
-
|
|
1104
|
-
const
|
|
1105
|
+
const currentSession = await getExistingSessionForCommand();
|
|
1106
|
+
const previousBaseagent = owningAgent.baseagent || this.parseDefaultBaseagent();
|
|
1107
|
+
if (currentSession?.threadId) {
|
|
1108
|
+
const rotation = await withIdleTopicBackend(currentSession.id, currentSession.threadId, async (current) => {
|
|
1109
|
+
try {
|
|
1110
|
+
owningAgent.setActiveBaseagent(args);
|
|
1111
|
+
}
|
|
1112
|
+
catch (error) {
|
|
1113
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
1114
|
+
}
|
|
1115
|
+
try {
|
|
1116
|
+
const result = await this.sessionManager.rotateTopicBackend({
|
|
1117
|
+
sessionId: current.id,
|
|
1118
|
+
expectedThreadId: current.threadId,
|
|
1119
|
+
baseagent: args,
|
|
1120
|
+
});
|
|
1121
|
+
return { ok: true, result };
|
|
1122
|
+
}
|
|
1123
|
+
catch (error) {
|
|
1124
|
+
try {
|
|
1125
|
+
owningAgent.setActiveBaseagent(previousBaseagent);
|
|
1126
|
+
}
|
|
1127
|
+
catch { }
|
|
1128
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
1129
|
+
}
|
|
1130
|
+
}, currentSession);
|
|
1131
|
+
if (!rotation.ok) {
|
|
1132
|
+
if (rotation.reason === 'busy')
|
|
1133
|
+
return { kind: 'command.error', text: '⚠️ 当前话题会话忙碌,无法切换 baseagent,请稍后再试' };
|
|
1134
|
+
if (rotation.reason === 'unknown')
|
|
1135
|
+
return { kind: 'command.error', text: '⚠️ 当前话题会话状态未知,无法安全切换 baseagent,请稍后再试' };
|
|
1136
|
+
return { kind: 'command.error', text: `❌ baseagent 切换失败:${rotation.reason === 'missing' ? '当前话题会话不存在' : '状态未知'}` };
|
|
1137
|
+
}
|
|
1138
|
+
if (!rotation.value.ok)
|
|
1139
|
+
return { kind: 'command.error', text: `❌ baseagent 切换失败:${rotation.value.error}` };
|
|
1140
|
+
this.eventBus.publish({ type: 'agent:baseagent-changed', aid: owningAgent.aid, baseagent: args, previousBaseagent, scope: 'agent', timestamp: Date.now() });
|
|
1141
|
+
return {
|
|
1142
|
+
kind: 'system.notice',
|
|
1143
|
+
text: `✓ 当前话题已切换 Baseagent: ${previousBaseagent} → ${args}\n 话题会话 ID 保持不变: ${currentSession.id}`,
|
|
1144
|
+
subtype: 'session.created',
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
await interruptPausedSessionBeforeReplacement(currentSession);
|
|
1105
1148
|
try {
|
|
1106
1149
|
owningAgent.setActiveBaseagent(args);
|
|
1107
1150
|
}
|
|
1108
1151
|
catch (error) {
|
|
1109
1152
|
return { kind: 'command.error', text: `❌ ${error instanceof Error ? error.message : String(error)}` };
|
|
1110
1153
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1154
|
+
let newSession;
|
|
1155
|
+
try {
|
|
1156
|
+
newSession = await this.sessionManager.createNewSession(channel, channelId, owningAgent.projectPath, undefined, args, currentSession?.metadata, { previousBaseagent, baseagentChanged: previousBaseagent !== args });
|
|
1157
|
+
}
|
|
1158
|
+
catch (error) {
|
|
1159
|
+
try {
|
|
1160
|
+
owningAgent.setActiveBaseagent(previousBaseagent);
|
|
1161
|
+
}
|
|
1162
|
+
catch { }
|
|
1163
|
+
return { kind: 'command.error', text: `❌ 创建新会话失败:${error instanceof Error ? error.message : String(error)}` };
|
|
1164
|
+
}
|
|
1165
|
+
if (currentSession) {
|
|
1166
|
+
const previousAgent = getActiveAgentIfAvailable();
|
|
1167
|
+
if (previousAgent) {
|
|
1168
|
+
await previousAgent.clearSession(currentSession.id, currentSession.agentSessionId || '', currentSession.projectPath);
|
|
1169
|
+
await previousAgent.closeSession(currentSession.id);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
this.eventBus.publish({ type: 'agent:baseagent-changed', aid: owningAgent.aid, baseagent: args, previousBaseagent, scope: 'agent', timestamp: Date.now() });
|
|
1173
|
+
return {
|
|
1174
|
+
kind: 'system.notice',
|
|
1175
|
+
text: `✓ 已切换 Baseagent: ${previousBaseagent} → ${args}\n 已创建新的主会话: ${newSession.id}\n 之前的对话历史已保留,可通过 /s 查看`,
|
|
1176
|
+
subtype: 'session.created',
|
|
1177
|
+
};
|
|
1126
1178
|
}
|
|
1127
1179
|
// /model 命令:查看或切换模型/推理强度
|
|
1128
1180
|
if (normalizedContent.startsWith('/model')) {
|
|
@@ -1317,7 +1369,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1317
1369
|
}),
|
|
1318
1370
|
},
|
|
1319
1371
|
};
|
|
1320
|
-
const replyCtx = modelSession ? this.getReplyContext(modelSession) :
|
|
1372
|
+
const replyCtx = modelSession ? this.getReplyContext(modelSession) : replyContext;
|
|
1321
1373
|
const cardResult = await this.sendCommandCard({ channel, channelId, interaction, replyCtx, canWrite: isAdmin });
|
|
1322
1374
|
if (cardResult === null)
|
|
1323
1375
|
return null;
|
|
@@ -1626,7 +1678,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1626
1678
|
})),
|
|
1627
1679
|
},
|
|
1628
1680
|
};
|
|
1629
|
-
const replyCtx = effortSession ? this.getReplyContext(effortSession) :
|
|
1681
|
+
const replyCtx = effortSession ? this.getReplyContext(effortSession) : replyContext;
|
|
1630
1682
|
const cardResult = await this.sendCommandCard({ channel, channelId, interaction, replyCtx, canWrite: isAdmin });
|
|
1631
1683
|
if (cardResult === null)
|
|
1632
1684
|
return null;
|
|
@@ -1879,7 +1931,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1879
1931
|
})),
|
|
1880
1932
|
},
|
|
1881
1933
|
};
|
|
1882
|
-
|
|
1934
|
+
// A topic session's persisted route is authoritative; an inbound
|
|
1935
|
+
// replyContext may be a stale/main-session snapshot (notably for card
|
|
1936
|
+
// callbacks), so prefer the resolved session context whenever present.
|
|
1937
|
+
const replyCtx = activitySession ? this.getReplyContext(activitySession) : replyContext;
|
|
1883
1938
|
const cardResult = await this.sendCommandCard({ channel, channelId, interaction, replyCtx, canWrite: isOwner });
|
|
1884
1939
|
if (cardResult === null)
|
|
1885
1940
|
return null;
|
|
@@ -1947,7 +2002,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
1947
2002
|
})),
|
|
1948
2003
|
},
|
|
1949
2004
|
};
|
|
1950
|
-
const replyCtx = existingChatmodeSession ? this.getReplyContext(existingChatmodeSession) :
|
|
2005
|
+
const replyCtx = existingChatmodeSession ? this.getReplyContext(existingChatmodeSession) : replyContext;
|
|
1951
2006
|
const cardResult = await this.sendCommandCard({
|
|
1952
2007
|
channel,
|
|
1953
2008
|
channelId,
|
|
@@ -2253,7 +2308,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2253
2308
|
}
|
|
2254
2309
|
return {
|
|
2255
2310
|
kind: 'system.notice',
|
|
2256
|
-
text:
|
|
2311
|
+
text: '已切换到新会话。下一条消息将在新会话中处理。',
|
|
2257
2312
|
subtype: 'session.renewed',
|
|
2258
2313
|
};
|
|
2259
2314
|
}
|
|
@@ -2290,7 +2345,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2290
2345
|
// The runner instance's model is only the final fallback; without this
|
|
2291
2346
|
// override Claude's /compact command could resume with a stale agent
|
|
2292
2347
|
// default (for example glm-5.3) while the turn itself used gpt-5.6-sol.
|
|
2293
|
-
const compactBaseagentRaw = session.baseagent
|
|
2348
|
+
const compactBaseagentRaw = session.baseagent;
|
|
2349
|
+
if (!compactBaseagentRaw) {
|
|
2350
|
+
return { kind: 'command.error', text: '❌ 当前会话缺少 baseagent 绑定,拒绝隐式切换' };
|
|
2351
|
+
}
|
|
2294
2352
|
const compactBaseagentNormalized = normalizeBaseagent(compactBaseagentRaw);
|
|
2295
2353
|
const compactBaseagent = compactBaseagentNormalized.canonical === 'unknown'
|
|
2296
2354
|
? compactBaseagentRaw
|
|
@@ -2610,7 +2668,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2610
2668
|
groups.set(type, []);
|
|
2611
2669
|
groups.get(type).push({ name, status });
|
|
2612
2670
|
}
|
|
2613
|
-
|
|
2671
|
+
// ECWeb 的系统控制面需要结构化健康数据(尤其是 daemon.aun.connected)。
|
|
2672
|
+
// 这里不能把全局 system 通道降级成仅文本摘要,否则前端会因缺少
|
|
2673
|
+
// daemonStatusProvider 返回值而把已连接的控制 AID 显示为 disconnect。
|
|
2674
|
+
if (!isAdmin && !isDaemonOwner && !isGlobalCheckChannel) {
|
|
2614
2675
|
// visitor/member: 仅显示实例通道摘要
|
|
2615
2676
|
const total = [...groups.values()].flat().length;
|
|
2616
2677
|
const healthy = [...groups.values()].flat().filter(i => i.status.includes('✓')).length;
|
|
@@ -2763,6 +2824,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2763
2824
|
// 进程级操作:必须是 daemon owner(daemon.json.owners),与 menu 协议 /system restart 一致。
|
|
2764
2825
|
// agent-channel 的 owner/admin 角色不足以重启整个 daemon。
|
|
2765
2826
|
const restartSelfAid = this.agentRegistry?.resolveByChannel(channel)?.aid;
|
|
2827
|
+
const commandSession = await getExistingSessionForCommand();
|
|
2766
2828
|
const authDenied = await authorizeIntent({
|
|
2767
2829
|
intent: {
|
|
2768
2830
|
operation: 'system.restart',
|
|
@@ -2772,7 +2834,7 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2772
2834
|
dangerous: true,
|
|
2773
2835
|
},
|
|
2774
2836
|
identity,
|
|
2775
|
-
session: activeSession,
|
|
2837
|
+
session: commandSession ?? activeSession,
|
|
2776
2838
|
channel,
|
|
2777
2839
|
channelId,
|
|
2778
2840
|
userId,
|
|
@@ -2784,7 +2846,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2784
2846
|
const selfAid = this.agentRegistry?.resolveByChannel(channel)?.aid;
|
|
2785
2847
|
// 排除当前会话(如果存在)。如果 activeSession 不存在,说明还没建立会话,
|
|
2786
2848
|
// 此时检查所有任务;如果有其他会话在处理,仍然阻塞重启。
|
|
2787
|
-
|
|
2849
|
+
// A topic command owns its topic session even when the channel's active
|
|
2850
|
+
// session remains the main conversation. Exclude that exact queue key
|
|
2851
|
+
// from the busy check so the current topic cannot block its own restart.
|
|
2852
|
+
const excludeKey = commandSession?.id ?? activeSession?.id;
|
|
2788
2853
|
const busyInfo = getAgentBusyInfo(this, selfAid, excludeKey);
|
|
2789
2854
|
if (busyInfo && busyInfo.count > 0) {
|
|
2790
2855
|
// busyInfo.count > 0 说明【除当前会话外】还有其他会话的任务在执行
|
|
@@ -2809,10 +2874,14 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2809
2874
|
const suppressRealRestart = shouldSuppressRealRestart();
|
|
2810
2875
|
let restartReplyContext = replyContext;
|
|
2811
2876
|
try {
|
|
2812
|
-
|
|
2877
|
+
// A topic's persisted session context is authoritative for card
|
|
2878
|
+
// callbacks and for inbound messages that only carry a partial route.
|
|
2879
|
+
// Do not let a stale/main-session replyContext win merely because it
|
|
2880
|
+
// happens to contain a delivery target.
|
|
2881
|
+
if (threadId) {
|
|
2813
2882
|
const threadSession = await this.sessionManager.getThreadSession(channel, channelId, threadId);
|
|
2814
2883
|
if (threadSession)
|
|
2815
|
-
restartReplyContext = this.getReplyContext(threadSession);
|
|
2884
|
+
restartReplyContext = { ...(replyContext ?? {}), ...(this.getReplyContext(threadSession) ?? {}), threadId };
|
|
2816
2885
|
}
|
|
2817
2886
|
else if (!restartReplyContext?.delivery && activeSession) {
|
|
2818
2887
|
restartReplyContext = this.getReplyContext(activeSession);
|
|
@@ -2854,16 +2923,39 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2854
2923
|
channel,
|
|
2855
2924
|
channelId,
|
|
2856
2925
|
timestamp: Date.now(),
|
|
2926
|
+
...(restartReplyContext?.threadId ? { threadId: restartReplyContext.threadId } : {}),
|
|
2857
2927
|
...(restartReplyContext?.delivery ? { delivery: restartReplyContext.delivery } : {}),
|
|
2858
2928
|
...(restartReplyContext?.replyToMessageId ? { rootId: restartReplyContext.replyToMessageId } : {}),
|
|
2859
2929
|
};
|
|
2860
2930
|
if (!suppressRealRestart) {
|
|
2931
|
+
// 这条路径上 daemon 会在下面 kill 掉自己,所以必须先确认 monitor 真的活起来:
|
|
2932
|
+
// 只查文件存在不够 —— 开发仓下 monitor 走 tsx,源码语法错误会让它加载即退出;
|
|
2933
|
+
// 安装包下 monitor 先复制到 stage 再启动,避免升级替换掉自己脚下的文件。
|
|
2934
|
+
const monitorEntry = resolveMonitorLaunch(getPackageRoot(), path.join(resolvePaths().root, 'data', 'monitor-stage'));
|
|
2935
|
+
if (monitorEntry.note)
|
|
2936
|
+
logger.info(`[System] ${monitorEntry.note}`);
|
|
2937
|
+
if (!fs.existsSync(monitorEntry.entry)) {
|
|
2938
|
+
logger.error(`[System] Refusing restart: restart-monitor entry not found: ${monitorEntry.entry}`);
|
|
2939
|
+
return { code: 'RESTART_ENTRY_MISSING', entry: monitorEntry.entry };
|
|
2940
|
+
}
|
|
2861
2941
|
const controlDir = daemonControlDir();
|
|
2862
2942
|
fs.mkdirSync(controlDir, { recursive: true });
|
|
2863
2943
|
fs.writeFileSync(path.join(controlDir, 'restart-pending.json'), JSON.stringify(restartInfo));
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2944
|
+
const confirmation = await launchMonitorAndConfirm([...monitorEntry.args, 'restart-monitor'], { EVOLCORE_HOME: resolvePaths().root, ...monitorEntry.env }, pid => scanInstances().restartMonitors.some(m => m.alive && m.record.pid === pid), pid => scanInstances().restartMonitors.some(m => m.alive && m.record.pid !== pid));
|
|
2945
|
+
if (!confirmation.ok) {
|
|
2946
|
+
// 没有看门狗就不要停服务:撤掉回执文件,让本进程继续跑。
|
|
2947
|
+
if (monitorEntry.stageDir)
|
|
2948
|
+
cleanupMonitorStage(monitorEntry.stageDir);
|
|
2949
|
+
try {
|
|
2950
|
+
fs.rmSync(path.join(controlDir, 'restart-pending.json'), { force: true });
|
|
2951
|
+
}
|
|
2952
|
+
catch { }
|
|
2953
|
+
logger.error(`[System] Refusing restart: restart-monitor did not come up (${confirmation.reason})`);
|
|
2954
|
+
for (const line of (confirmation.stderrTail ?? '').split(/\r?\n/).filter(Boolean).slice(-8)) {
|
|
2955
|
+
logger.error(`[System] | ${line}`);
|
|
2956
|
+
}
|
|
2957
|
+
return { code: 'RESTART_MONITOR_DEAD', reason: confirmation.reason ?? 'unknown' };
|
|
2958
|
+
}
|
|
2867
2959
|
}
|
|
2868
2960
|
else {
|
|
2869
2961
|
logger.info('[System] Suppressed real restart in test runtime');
|
|
@@ -2894,15 +2986,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2894
2986
|
logger.error('[System] Failed to send restart notification:', err);
|
|
2895
2987
|
}
|
|
2896
2988
|
}
|
|
2897
|
-
//
|
|
2898
|
-
//
|
|
2899
|
-
//
|
|
2900
|
-
|
|
2901
|
-
setTimeout(() => {
|
|
2902
|
-
logger.info('[System] Restarting by user command...');
|
|
2903
|
-
process.kill(process.pid, 'SIGTERM');
|
|
2904
|
-
}, 1000);
|
|
2905
|
-
}
|
|
2989
|
+
// restart-monitor owns the old daemon shutdown and the replacement
|
|
2990
|
+
// startup. Do not kill this process here: the monitor may have exited
|
|
2991
|
+
// after a read-only migration check, in which case the current service
|
|
2992
|
+
// must remain available.
|
|
2906
2993
|
return true;
|
|
2907
2994
|
};
|
|
2908
2995
|
// 文本确认流程
|
|
@@ -2931,7 +3018,21 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
2931
3018
|
if (typeof restarted === 'object' && restarted.code === 'DATA_MIGRATION_REQUIRED') {
|
|
2932
3019
|
return {
|
|
2933
3020
|
kind: 'command.error',
|
|
2934
|
-
text: `❌ 待处理的用户数据迁移(${restarted.operationCount}
|
|
3021
|
+
text: `❌ 待处理的用户数据迁移(${restarted.operationCount} 项)阻止重启;本次仅检查,未停止当前服务。请先运行 ec data migrate --dry-run,然后运行 ec data migrate --apply。`,
|
|
3022
|
+
reason: restarted.code,
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
if (typeof restarted === 'object' && restarted.code === 'RESTART_ENTRY_MISSING') {
|
|
3026
|
+
return {
|
|
3027
|
+
kind: 'command.error',
|
|
3028
|
+
text: `❌ 重启已中止:找不到 restart-monitor 入口 ${restarted.entry}。服务未重启,当前进程继续运行。请先执行 npm run build 重建产物。`,
|
|
3029
|
+
reason: restarted.code,
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
if (typeof restarted === 'object' && restarted.code === 'RESTART_MONITOR_DEAD') {
|
|
3033
|
+
return {
|
|
3034
|
+
kind: 'command.error',
|
|
3035
|
+
text: `❌ 重启已中止:看门狗进程未能启动(${restarted.reason})。服务未停止,仍在运行当前版本。开发仓下这通常是源码语法错误导致 tsx 无法加载,请执行 npx tsc --noEmit 检查。`,
|
|
2935
3036
|
reason: restarted.code,
|
|
2936
3037
|
};
|
|
2937
3038
|
}
|
|
@@ -3099,11 +3200,11 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3099
3200
|
? { delivery: { chatType: 'private' } }
|
|
3100
3201
|
: sendSession
|
|
3101
3202
|
? this.getReplyContext(sendSession)
|
|
3102
|
-
: chatType === 'group'
|
|
3203
|
+
: replyContext ?? (chatType === 'group'
|
|
3103
3204
|
? { delivery: { chatType: 'group', groupId: channelId } }
|
|
3104
3205
|
: chatType === 'private'
|
|
3105
3206
|
? { delivery: { chatType: 'private' } }
|
|
3106
|
-
: undefined;
|
|
3207
|
+
: undefined);
|
|
3107
3208
|
await targetAdapter.send(buildEnvelope({ channel: targetAdapter.channelName, channelId: targetChannelId, replyContext: replyCtx }), { kind: 'result.file', filePath: realPath });
|
|
3108
3209
|
const sizeStr = stat.size < 1024 ? `${stat.size} B`
|
|
3109
3210
|
: stat.size < 1024 * 1024 ? `${(stat.size / 1024).toFixed(1)} KB`
|
|
@@ -3122,10 +3223,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3122
3223
|
// /slist cli — 仅 CLI 会话(未导入的)
|
|
3123
3224
|
if (normalizedContent === '/slist' || normalizedContent === '/slist cli') {
|
|
3124
3225
|
if (!session) {
|
|
3125
|
-
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3126
|
-
|
|
3127
|
-
请先执行以下操作之一:
|
|
3128
|
-
1. 发送任意消息 - 自动创建新会话
|
|
3226
|
+
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3227
|
+
|
|
3228
|
+
请先执行以下操作之一:
|
|
3229
|
+
1. 发送任意消息 - 自动创建新会话
|
|
3129
3230
|
2. /new [名称] - 创建命名会话` };
|
|
3130
3231
|
}
|
|
3131
3232
|
const showCliOnly = normalizedContent === '/slist cli';
|
|
@@ -3329,7 +3430,12 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3329
3430
|
projectPaths.unshift(session.projectPath);
|
|
3330
3431
|
}
|
|
3331
3432
|
for (const projectPath of projectPaths) {
|
|
3332
|
-
const currentBaseagent = session?.baseagent
|
|
3433
|
+
const currentBaseagent = session?.baseagent
|
|
3434
|
+
?? this.agentRegistry?.resolveByChannel(channel)?.baseagent
|
|
3435
|
+
?? (this.agentMap?.size === 1 ? this.parseDefaultBaseagent() : undefined);
|
|
3436
|
+
if (!currentBaseagent) {
|
|
3437
|
+
return { kind: 'command.error', text: '❌ 无法确定当前会话的 baseagent,拒绝导入 CLI 会话' };
|
|
3438
|
+
}
|
|
3333
3439
|
const cliSessions = currentBaseagent === 'ecagent'
|
|
3334
3440
|
? await this.sessionManager.listImportableCliSessions(projectPath, currentBaseagent, selfAID ?? session?.selfAID)
|
|
3335
3441
|
: await this.sessionManager.listImportableCliSessions(projectPath, currentBaseagent);
|
|
@@ -3391,11 +3497,11 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3391
3497
|
if (!newName)
|
|
3392
3498
|
return { kind: 'command.result', text: '用法: /name <新名称> 或 /rename <新名称>' };
|
|
3393
3499
|
if (!session) {
|
|
3394
|
-
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3395
|
-
|
|
3396
|
-
请先执行以下操作之一:
|
|
3397
|
-
1. 发送任意消息 - 自动创建新会话
|
|
3398
|
-
2. /new [名称] - 创建命名会话
|
|
3500
|
+
return { kind: 'command.error', text: `❌ 当前没有活跃会话
|
|
3501
|
+
|
|
3502
|
+
请先执行以下操作之一:
|
|
3503
|
+
1. 发送任意消息 - 自动创建新会话
|
|
3504
|
+
2. /new [名称] - 创建命名会话
|
|
3399
3505
|
3. /session <名称> - 切换到已有会话` };
|
|
3400
3506
|
}
|
|
3401
3507
|
const existing = await this.sessionManager.getSessionByName(channel, channelId, newName);
|
|
@@ -3504,7 +3610,10 @@ export async function handleSlashCommand(content, channel, channelId, sendMessag
|
|
|
3504
3610
|
const session = await getExistingSessionForCommand();
|
|
3505
3611
|
if (!session)
|
|
3506
3612
|
return { kind: 'command.error', text: '❌ 当前没有活跃会话' };
|
|
3507
|
-
const rewindBaseagent = session.baseagent
|
|
3613
|
+
const rewindBaseagent = session.baseagent;
|
|
3614
|
+
if (!rewindBaseagent) {
|
|
3615
|
+
return { kind: 'command.error', text: '❌ 当前会话缺少 baseagent 绑定,拒绝隐式切换' };
|
|
3616
|
+
}
|
|
3508
3617
|
const rewindAgent = this.getAgent(channel, rewindBaseagent);
|
|
3509
3618
|
if (!session.agentSessionId) {
|
|
3510
3619
|
return { kind: 'command.error', text: '❌ 当前会话无历史记录\n\n请先发送一条消息,然后再使用 /rewind' };
|
|
@@ -752,21 +752,11 @@ export function planDataMigration(root) {
|
|
|
752
752
|
}
|
|
753
753
|
}
|
|
754
754
|
catch { }
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const aid = entry.name.slice(0, -'.jsonl'.length);
|
|
761
|
-
if (!aids.has(aid)) {
|
|
762
|
-
warnings.push(`Unresolved outbox owner retained: ${entry.name}`);
|
|
763
|
-
continue;
|
|
764
|
-
}
|
|
765
|
-
const operationId = nextId('outbox');
|
|
766
|
-
addCopy(operations, operationId, path.join(legacyOutbox, entry.name), path.join(root, 'agents', aid, 'data', 'outbox.jsonl'), stageFor(operationId));
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
catch { }
|
|
755
|
+
// Outbox layouts are imported lazily by src/aun/outbox.ts when a queue is
|
|
756
|
+
// first read or written. They are append-only delivery state, so including
|
|
757
|
+
// them in the maintenance migration would unnecessarily block startup and
|
|
758
|
+
// could race a live connector. Keep the explicit migration reserved for
|
|
759
|
+
// data that needs operator review or an exclusive maintenance window.
|
|
770
760
|
const contactAudit = path.join(root, 'data', 'contact-book-audit.jsonl');
|
|
771
761
|
// The legacy audit file may be provisioned as an empty placeholder (for
|
|
772
762
|
// example by a protected-path sandbox). An empty source has nothing to
|
|
@@ -89,6 +89,9 @@ const CATALOG = [
|
|
|
89
89
|
{ path: 'name', type: 'string', optional: true },
|
|
90
90
|
{ path: 'chatType', type: 'string', optional: true },
|
|
91
91
|
{ path: 'threadId', type: 'string', optional: true },
|
|
92
|
+
{ path: 'baseagent', type: 'string', optional: true },
|
|
93
|
+
{ path: 'previousBaseagent', type: 'string', optional: true },
|
|
94
|
+
{ path: 'baseagentChanged', type: 'boolean', optional: true },
|
|
92
95
|
{ path: 'cause', type: 'string', optional: true },
|
|
93
96
|
{ path: 'sourceSessionId', type: 'string', optional: true },
|
|
94
97
|
{ path: 'renewSource', type: 'string', optional: true },
|
|
@@ -793,6 +796,11 @@ const CATALOG = [
|
|
|
793
796
|
fields: [
|
|
794
797
|
{ path: 'aid', type: 'string' },
|
|
795
798
|
{ path: 'avatar', type: 'string' },
|
|
799
|
+
{ path: 'sha256', type: 'string', optional: true },
|
|
800
|
+
{ path: 'mime', type: 'string', optional: true },
|
|
801
|
+
{ path: 'width', type: 'number', optional: true },
|
|
802
|
+
{ path: 'height', type: 'number', optional: true },
|
|
803
|
+
{ path: 'aspectRatio', type: 'number', optional: true },
|
|
796
804
|
{ path: 'timestamp', type: 'number', optional: true },
|
|
797
805
|
],
|
|
798
806
|
},
|
package/dist/core/evolagent.js
CHANGED
|
@@ -275,9 +275,9 @@ export class EvolAgent {
|
|
|
275
275
|
return `agent-files:${this.aid}`;
|
|
276
276
|
}
|
|
277
277
|
/**
|
|
278
|
-
* 读取 personal/persona.md 内容。走 fileCache(mtime 门控):persona
|
|
279
|
-
*
|
|
280
|
-
* stat 比对、变了自动重读。文件不存在返回 null。
|
|
278
|
+
* 读取 personal/persona.md 内容。走 fileCache(mtime 门控):persona 可由
|
|
279
|
+
* `ec agent persona` 或 agent 自己更新,与 working memory 一样改了即应生效,
|
|
280
|
+
* 故每次读 stat 比对、变了自动重读。文件不存在返回 null。
|
|
281
281
|
*/
|
|
282
282
|
getPersona() {
|
|
283
283
|
const personaPath = path.join(agentPersonalDir(this.aid), 'persona.md');
|
|
@@ -118,6 +118,7 @@ export class HandoffRuntime {
|
|
|
118
118
|
targetSessionId: target.id,
|
|
119
119
|
payload: input.payload,
|
|
120
120
|
encrypt: input.encrypt,
|
|
121
|
+
contextOnly: input.contextOnly,
|
|
121
122
|
delivery: targetChatType === 'group'
|
|
122
123
|
? { chatType: 'group', groupId: input.to }
|
|
123
124
|
: { chatType: 'private' },
|
|
@@ -131,7 +132,8 @@ export class HandoffRuntime {
|
|
|
131
132
|
status: 'started',
|
|
132
133
|
refs: { handoffId: handoff.handoff_id, sessionId: origin.id, messageId: input.originMessageId },
|
|
133
134
|
});
|
|
134
|
-
|
|
135
|
+
if (!input.contextOnly)
|
|
136
|
+
await this.dispatcher.drain(input.selfAid, target.id);
|
|
135
137
|
const dispatched = this.store.get(input.selfAid, handoff.handoff_id) ?? handoff;
|
|
136
138
|
const replacedSessionId = [origin.id, target.id].find(sessionId => ((this.sessionDiscardSequence.get(this.sessionKey(input.selfAid, sessionId)) ?? 0) > operationSequence));
|
|
137
139
|
if (dispatched.state === 'discarded' || replacedSessionId) {
|
|
@@ -122,6 +122,7 @@ export class HandoffStore {
|
|
|
122
122
|
request: {
|
|
123
123
|
payload: clonePayload(input.payload),
|
|
124
124
|
encrypt: input.encrypt,
|
|
125
|
+
...(input.contextOnly ? { contextOnly: true } : {}),
|
|
125
126
|
...(input.delivery ? { delivery: input.delivery } : {}),
|
|
126
127
|
},
|
|
127
128
|
...(input.originAuthorization
|
|
@@ -129,7 +130,12 @@ export class HandoffStore {
|
|
|
129
130
|
: {}),
|
|
130
131
|
causation: normalizeCausation(input.causation),
|
|
131
132
|
return_policy: input.returnPolicy ?? 'required',
|
|
132
|
-
|
|
133
|
+
// Local-only requests are already available for the next target-session
|
|
134
|
+
// turn and must never enter the outbound dispatch queue. Keep them in
|
|
135
|
+
// target_sent while waiting for the target turn to bind the context;
|
|
136
|
+
// target_context_pending is reserved for a context-only handoff after
|
|
137
|
+
// it has been consumed by a returnPolicy=none target turn.
|
|
138
|
+
state: input.contextOnly ? 'target_sent' : 'queued',
|
|
133
139
|
target_message_id: null,
|
|
134
140
|
response_message_id: null,
|
|
135
141
|
consumed_at: null,
|
|
@@ -227,25 +227,32 @@ export class IMRenderer {
|
|
|
227
227
|
durationMs: opts.durationMs,
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
|
-
/** 发送不进入 activity/thought
|
|
231
|
-
async
|
|
230
|
+
/** 发送不进入 activity/thought 历史、也不代表执行活跃的任务级 notice。 */
|
|
231
|
+
async sendStatusNotice(message, severity = 'info', subtype, details) {
|
|
232
232
|
const payload = {
|
|
233
|
-
kind: 'status.
|
|
233
|
+
kind: 'status.notice',
|
|
234
234
|
metadata: {
|
|
235
|
-
activityType: 'progress',
|
|
236
235
|
message,
|
|
237
|
-
severity
|
|
236
|
+
severity,
|
|
238
237
|
...(subtype !== undefined && { subtype }),
|
|
239
238
|
...(details?.missingItems !== undefined && { missingItems: details.missingItems }),
|
|
239
|
+
...(details?.idleSec !== undefined && { idleSec: details.idleSec }),
|
|
240
|
+
...(details?.totalEvents !== undefined && { totalEvents: details.totalEvents }),
|
|
241
|
+
...(details?.totalToolCalls !== undefined && { totalToolCalls: details.totalToolCalls }),
|
|
242
|
+
...(details?.lastToolName !== undefined && { lastToolName: details.lastToolName }),
|
|
240
243
|
},
|
|
241
244
|
};
|
|
242
245
|
try {
|
|
243
246
|
await this.opts.send(payload);
|
|
244
247
|
}
|
|
245
248
|
catch (error) {
|
|
246
|
-
logger.warn(`[IMRenderer]
|
|
249
|
+
logger.warn(`[IMRenderer] status notice send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
247
250
|
}
|
|
248
251
|
}
|
|
252
|
+
/** 兼容旧调用方:发送 warning 级别的任务 notice。 */
|
|
253
|
+
async sendWarningStatus(message, subtype, details) {
|
|
254
|
+
return this.sendStatusNotice(message, 'warn', subtype, details);
|
|
255
|
+
}
|
|
249
256
|
/** 添加任务生命周期边界事件,复用 activity.batch 投影路径。 */
|
|
250
257
|
addLifecycle(phase, metadata, text) {
|
|
251
258
|
const item = {
|