evolcore 0.0.28 → 0.0.29
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 +12 -0
- package/bin/ec-safe-output.js +270 -54
- package/dist/agents/baseagent-availability.js +37 -1
- package/dist/agents/baseagent.js +19 -9
- package/dist/agents/claude-runner.js +171 -13
- package/dist/agents/codex/app-server-client.js +6 -2
- package/dist/agents/codex/diagnostics.js +4 -3
- package/dist/agents/codex-runner.js +396 -95
- package/dist/agents/gemini-runner.js +35 -17
- package/dist/aun/group/history.js +130 -90
- package/dist/aun/group/operations.js +1 -3
- package/dist/aun/menu/transport.js +5 -3
- package/dist/aun/msg/managed-operation.js +70 -0
- package/dist/aun/msg/p2p.js +1 -3
- package/dist/aun/msg/upload.js +2 -2
- package/dist/aun/outbox.js +1 -37
- package/dist/channels/aun.js +249 -89
- package/dist/channels/daemon.js +18 -1
- package/dist/channels/feishu.js +1 -2
- package/dist/cli/agent-command.js +23 -3
- package/dist/cli/agent-create.js +68 -2
- package/dist/cli/agent.js +12 -2
- package/dist/cli/aun-commands.js +87 -13
- package/dist/cli/cli-argv.js +26 -0
- package/dist/cli/daemon-commands.js +40 -10
- package/dist/cli/data-command.js +4 -4
- package/dist/cli/handoff-command.js +2 -0
- package/dist/cli/index.js +18 -1
- package/dist/cli/managed-command-guard.js +10 -0
- package/dist/cli/memory-command.js +441 -0
- package/dist/cli/restart-monitor.js +62 -6
- package/dist/cli/skill-command.js +41 -0
- package/dist/cli/task-context.js +2 -23
- package/dist/cli/trigger-command.js +9 -1
- package/dist/cli/watch-msg-diag.js +209 -0
- package/dist/config/config-field-policy.js +52 -3
- package/dist/config/config-manager.js +17 -4
- package/dist/config/contact-book-store.js +2 -2
- package/dist/config/mention-mode.js +7 -2
- package/dist/config/merge.js +28 -9
- package/dist/config/resolved-config-op.js +1 -1
- package/dist/config/runtime-env.js +137 -0
- package/dist/config/schema-registry.js +11 -1
- package/dist/config-store.js +10 -10
- package/dist/core/audit/event-key.js +3 -1
- package/dist/core/audit/log-integrity.js +4 -0
- package/dist/core/auth/agent-delegation.js +2 -1
- package/dist/core/auth/auth-gateway.js +13 -6
- package/dist/core/auth/authorization-audit.js +54 -19
- package/dist/core/auth/operation-authorizer.js +17 -0
- package/dist/core/auth/operation-catalog.js +9 -0
- package/dist/core/causation/context.js +5 -1
- package/dist/core/command/agent-control.js +1 -2
- package/dist/core/command/command-handler.js +51 -23
- package/dist/core/command/memory-slash.js +270 -0
- package/dist/core/command/menu-catalog.js +1 -0
- package/dist/core/command/menu-handler.js +315 -60
- package/dist/core/command/menu-protocol.js +42 -4
- package/dist/core/command/slash-gate.js +10 -10
- package/dist/core/command/slash-handler.js +206 -64
- package/dist/core/data-migration.js +282 -505
- package/dist/core/event-catalog.js +26 -0
- package/dist/core/evolagent-registry.js +6 -0
- package/dist/core/evolagent.js +0 -10
- package/dist/core/handoff/store.js +1 -20
- package/dist/core/inference/text-inference.js +57 -1
- package/dist/core/memory/consolidation-context.js +505 -0
- package/dist/core/memory/consolidation-ops-records.js +124 -0
- package/dist/core/memory/consolidation-trigger.js +394 -0
- package/dist/core/memory/index-manager.js +308 -0
- package/dist/core/memory/launch-consolidation.js +395 -0
- package/dist/core/memory/memory-admin.js +234 -0
- package/dist/core/memory/memory-document.js +100 -0
- package/dist/core/memory/memory-session-policy.js +41 -0
- package/dist/core/memory/memory-signature.js +257 -0
- package/dist/core/memory/memory-writer.js +432 -0
- package/dist/core/memory/staleness.js +117 -0
- package/dist/core/message/auto-mention-gate.js +454 -0
- package/dist/core/message/im-renderer.js +69 -16
- package/dist/core/message/message-bridge.js +287 -21
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +114 -27
- package/dist/core/message/pending-hints.js +329 -161
- package/dist/core/message/response-engine.js +567 -80
- package/dist/core/permission/ec-command-parser.js +1029 -80
- package/dist/core/permission/protected-paths.js +376 -126
- package/dist/core/permission/sandbox-runtime.js +40 -12
- package/dist/core/permission/tool-error-code.js +96 -13
- package/dist/core/permission/tool-policy.js +276 -23
- package/dist/core/role/runtime-policy.js +3 -7
- package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
- package/dist/core/session/session-fs-store.js +0 -76
- package/dist/core/session/session-idle-monitor.js +355 -0
- package/dist/core/session/session-manager.js +478 -17
- package/dist/core/session/session-mapper.js +33 -16
- package/dist/core/session/session-renew.js +217 -155
- package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
- package/dist/core/skill/global-watcher.js +282 -0
- package/dist/core/skill/index-generator.js +84 -0
- package/dist/core/skill/startup.js +73 -0
- package/dist/core/skill/switch-resolver.js +115 -0
- package/dist/core/task/task-activity-store.js +219 -0
- package/dist/eck/kit-renderer.js +30 -3
- package/dist/eck/manifest-engine.js +26 -4
- package/dist/index.js +708 -18
- package/dist/ipc.js +564 -2
- package/dist/paths.js +30 -9
- package/dist/response-system/engines/v1/proactive-flow.js +116 -15
- package/dist/response-system/modes/single-session/index.js +3 -0
- package/dist/trigger/feedback.js +1 -0
- package/dist/trigger/manager.js +0 -10
- package/dist/trigger/scheduler.js +11 -0
- package/dist/trigger/validation.js +22 -0
- package/dist/utils/avatar-upload.js +59 -18
- package/dist/utils/error-dict.json +6 -3
- package/dist/utils/error-utils.js +27 -0
- package/dist/utils/logger.js +32 -10
- package/dist/utils/media-cache.js +84 -0
- package/dist/utils/msg-diag.js +97 -0
- package/dist/utils/welcome.js +37 -8
- package/kits/docs/AUTHORING.md +314 -0
- package/kits/docs/GUIDE.md +2 -0
- package/kits/docs/INDEX.md +2 -0
- package/kits/docs/aun/CHEATSHEET.md +4 -0
- package/kits/docs/aun/SDK.md +60 -0
- package/kits/docs/context-assembly.md +10 -6
- package/kits/docs/evolcore/INDEX.md +17 -3
- package/kits/docs/evolcore/agent-create.md +24 -0
- package/kits/docs/evolcore/agent.md +5 -2
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/ec-capabilities.md +364 -0
- package/kits/docs/evolcore/group.md +34 -18
- package/kits/docs/evolcore/memory-architecture.md +78 -0
- package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
- package/kits/docs/evolcore/memory-consolidation.md +263 -0
- package/kits/docs/evolcore/memory-tags.md +63 -0
- package/kits/docs/evolcore/memory.md +110 -0
- package/kits/docs/evolcore/menu-client.md +27 -11
- package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
- package/kits/docs/evolcore/menu-protocol.md +213 -0
- package/kits/docs/evolcore/msg.md +2 -0
- package/kits/docs/evolcore/trigger.md +7 -5
- package/kits/docs/path-registry.md +6 -8
- package/kits/docs/prompt-loading-architecture.md +5 -3
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_manifest.json +294 -88
- package/kits/eck_manifest.memory.json +257 -0
- package/kits/rules/03-identity.md +8 -8
- package/kits/rules/05-venue.md +4 -2
- package/kits/rules/06-channel.md +1 -1
- package/kits/schemas/_meta.json +276 -47
- package/kits/schemas/agent-config.schema.12.json +38 -2
- package/kits/schemas/agent-config.schema.13.json +591 -0
- package/kits/schemas/daemon.schema.6.json +9 -9
- package/kits/schemas/defaults.schema.5.json +29 -0
- package/kits/schemas/relation-config.schema.8.json +14 -1
- package/kits/schemas/relation-config.schema.9.json +129 -0
- package/kits/schemas/role-config.schema.1.json +3 -3
- package/kits/skills/.switches.json +3 -0
- package/kits/skills/ec-skill/SKILL.md +217 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
- package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
- package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
- package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
- package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
- package/kits/templates/decision/auto-mention.md +25 -0
- package/kits/templates/decision/session-renew.md +22 -0
- package/kits/templates/memory-consolidation/forget.md +104 -0
- package/kits/templates/memory-consolidation/generalize.md +76 -0
- package/kits/templates/memory-consolidation/notes.md +133 -0
- package/kits/templates/memory-consolidation/profile.md +58 -0
- package/kits/templates/memory-consolidation/promote.md +72 -0
- package/kits/templates/memory-consolidation/workflow.md +75 -0
- package/kits/templates/system-fragments/bootstrap.md +14 -5
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/group-history.md +6 -0
- package/kits/templates/system-fragments/identity.md +0 -3
- package/kits/templates/system-fragments/memory.md +79 -0
- package/kits/templates/system-fragments/reply.md +1 -0
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -6
- package/dist/trigger/legacy-session-history.js +0 -19
- package/kits/templates/system-fragments/group-unread-history.md +0 -7
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { loadAgent } from '../../config-store.js';
|
|
2
2
|
import { initializationBlocksTasks } from '../../cli/agent-create.js';
|
|
3
3
|
import { createHash, randomBytes } from 'crypto';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
4
6
|
import { agentStorageMenuService, isAgentStorageMenuRequest } from '../command/agent-storage-menu.js';
|
|
5
7
|
import { logger } from '../../utils/logger.js';
|
|
6
8
|
import { StreamDebouncer } from './stream-debouncer.js';
|
|
7
9
|
import { appendMessageLog, appendMessageLogStrict, buildInboundEntry, isTransientProtocolMessage } from './message-log.js';
|
|
8
10
|
import { buildEnvelope, isInteractionSendAccepted, sendInteractionPayload } from './message-utils.js';
|
|
9
11
|
import { chatDirPath } from '../session/session-fs-store.js';
|
|
10
|
-
import {
|
|
12
|
+
import { readTopicLabel, readTopicStatus } from '../session/session-mapper.js';
|
|
11
13
|
import { formatChannelKey, tryParseChannelKey } from '../channel-loader.js';
|
|
12
14
|
import { agentDir, resolvePaths } from '../../paths.js';
|
|
13
15
|
import { listStaticAgentAdmins, listStaticAgentOwners, resolvePeerRoleDetail, } from '../../config/peer-role-resolver.js';
|
|
14
16
|
import { resolveContactView, resolvePeerDisplayLabel, resolvePrincipal } from '../../config/contact-book.js';
|
|
15
17
|
import { findContactRequestNote, expirePendingContactRequests, reviewContactRequest, submitContactRequest, contactRequestSubmissionProtocolResult, queryOwnContactRequestStatus, } from '../../config/contact-request-service.js';
|
|
16
18
|
import { isValidAid } from '../../aun/aid/validation.js';
|
|
17
|
-
import { PeerIdentityCache } from '../relation/peer-identity.js';
|
|
19
|
+
import { PeerIdentityCache, formatPeerKey } from '../relation/peer-identity.js';
|
|
20
|
+
import { resolveEffective } from '../../config/config-manager.js';
|
|
21
|
+
import { createDecisionInferenceProvider } from '../inference/text-inference.js';
|
|
22
|
+
import { AutoMentionGate } from './auto-mention-gate.js';
|
|
23
|
+
import { syncGroupRulesContext } from '../../eck/group-rules-sync.js';
|
|
18
24
|
import { handlePendingDingtalkContactBindMessage, registerPendingDingtalkContactBind } from '../../channels/dingtalk.js';
|
|
19
25
|
import { handlePendingFeishuContactBindMessage, registerPendingFeishuContactBind } from '../../channels/feishu.js';
|
|
20
26
|
import { handlePendingQQBotContactBindMessage, registerPendingQQBotContactBind } from '../../channels/qqbot.js';
|
|
@@ -30,7 +36,6 @@ import { recordCausationSpan } from '../causation/audit.js';
|
|
|
30
36
|
import { renderActionAsText } from '../interaction-router.js';
|
|
31
37
|
import { planApprovalRoute } from '../permission/approval-gateway.js';
|
|
32
38
|
import { guardGroupSlashCommand, parseFullAccessCommand } from '../command/slash-gate.js';
|
|
33
|
-
import { isFullAccessEnabled } from '../../config-store.js';
|
|
34
39
|
import { createAgentPolicyAuthorization } from '../role/runtime-policy.js';
|
|
35
40
|
import { auditFullAccessEvent } from '../auth/authorization-audit.js';
|
|
36
41
|
import { DIAGNOSTIC_AGENT_AID, DiagnosticSessionRouter, parseDiagnosticEvent, isDiagnosticSessionActive } from '../command/diag-service.js';
|
|
@@ -105,6 +110,79 @@ export class MessageBridge {
|
|
|
105
110
|
cmdHandler;
|
|
106
111
|
eventBus;
|
|
107
112
|
processOwnersProvider;
|
|
113
|
+
autoMentionGate = new AutoMentionGate({
|
|
114
|
+
resolveInference: item => {
|
|
115
|
+
const config = resolveEffective({
|
|
116
|
+
self: item.selfAid,
|
|
117
|
+
peerKey: formatPeerKey('aun', item.groupId),
|
|
118
|
+
}, { cache: true, expand: true });
|
|
119
|
+
const model = config.decision_model?.model?.trim();
|
|
120
|
+
const provider = createDecisionInferenceProvider(config.decision_model);
|
|
121
|
+
return model && provider ? { model, provider } : undefined;
|
|
122
|
+
},
|
|
123
|
+
resolveResources: async (item) => {
|
|
124
|
+
const config = resolveEffective({
|
|
125
|
+
self: item.selfAid,
|
|
126
|
+
peerKey: formatPeerKey('aun', item.groupId),
|
|
127
|
+
}, { cache: true, expand: true });
|
|
128
|
+
const resources = {};
|
|
129
|
+
if (config.groupRules?.mode !== 'ignore') {
|
|
130
|
+
try {
|
|
131
|
+
const synced = await syncGroupRulesContext({ selfAid: item.selfAid, groupId: item.groupId, channel: 'aun' });
|
|
132
|
+
if (synced.groupRulesPath && fs.existsSync(synced.groupRulesPath)) {
|
|
133
|
+
resources.groupRules = fs.readFileSync(synced.groupRulesPath, 'utf8');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
logger.debug(`[MessageBridge] Decision context group rules unavailable: ${String(error)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const personalPath = path.join(agentDir(item.selfAid), 'personal', 'personal.md');
|
|
141
|
+
if (fs.existsSync(personalPath))
|
|
142
|
+
resources.personal = fs.readFileSync(personalPath, 'utf8');
|
|
143
|
+
if (item.projectPath) {
|
|
144
|
+
const files = [];
|
|
145
|
+
let current = path.resolve(item.projectPath);
|
|
146
|
+
while (true) {
|
|
147
|
+
const candidate = path.join(current, 'AGENTS.md');
|
|
148
|
+
if (fs.existsSync(candidate))
|
|
149
|
+
files.unshift(candidate);
|
|
150
|
+
const parent = path.dirname(current);
|
|
151
|
+
if (parent === current)
|
|
152
|
+
break;
|
|
153
|
+
current = parent;
|
|
154
|
+
}
|
|
155
|
+
if (files.length > 0) {
|
|
156
|
+
resources.projectInstructions = files.map(file => (`# ${path.relative(item.projectPath, file) || 'AGENTS.md'}\n${fs.readFileSync(file, 'utf8')}`)).join('\n\n');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return resources;
|
|
160
|
+
},
|
|
161
|
+
resolveQuietWindowMs: item => resolveEffective({
|
|
162
|
+
self: item.selfAid,
|
|
163
|
+
peerKey: formatPeerKey('aun', item.groupId),
|
|
164
|
+
}, { cache: true, expand: true }).autoMentionQuietWindowMs,
|
|
165
|
+
resolveMessageState: item => {
|
|
166
|
+
if (!item.messageId)
|
|
167
|
+
return 'missing';
|
|
168
|
+
if (item.sessionId) {
|
|
169
|
+
const state = this.messageQueue.getMessageState?.(item.sessionId, item.messageId, item.selfAid)?.state ?? 'missing';
|
|
170
|
+
if (state !== 'missing')
|
|
171
|
+
return state;
|
|
172
|
+
}
|
|
173
|
+
return this.messageQueue.getMessageStateByMessageId?.(item.messageId, item.selfAid, {
|
|
174
|
+
channelId: item.groupId,
|
|
175
|
+
groupId: item.groupId,
|
|
176
|
+
threadId: item.threadId || '',
|
|
177
|
+
})?.state ?? 'missing';
|
|
178
|
+
},
|
|
179
|
+
resolveProcessing: item => {
|
|
180
|
+
if (!item.sessionId)
|
|
181
|
+
return !!item.mainSessionBusy;
|
|
182
|
+
return (this.messageQueue.isProcessing?.(item.sessionId) ?? false)
|
|
183
|
+
|| (this.messageQueue.getQueueLength?.(item.sessionId) ?? 0) > 0;
|
|
184
|
+
},
|
|
185
|
+
});
|
|
108
186
|
debouncers = new Map();
|
|
109
187
|
defaultDebounce;
|
|
110
188
|
/** Serializes session selection/renewal with inbound log admission per route. */
|
|
@@ -529,6 +607,12 @@ export class MessageBridge {
|
|
|
529
607
|
}
|
|
530
608
|
return true;
|
|
531
609
|
}
|
|
610
|
+
async sendAdmissionMenuResponse(context, adapter, channel, msg, response) {
|
|
611
|
+
if (!consumeAdmissionReplyBudget(context))
|
|
612
|
+
return false;
|
|
613
|
+
await this.sendMenuResponse(adapter, channel, msg, response);
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
532
616
|
/** Controls handled before the normal slash handler still need structured group feedback. */
|
|
533
617
|
async sendSlashControlReply(channel, msg, sendReply, payload, adapter = this.processor.getChannelInfo?.(channel)?.adapter) {
|
|
534
618
|
if (msg.chatType === 'group' && adapter?.send) {
|
|
@@ -766,6 +850,12 @@ export class MessageBridge {
|
|
|
766
850
|
const conversationId = chatType === 'group' ? (msg.groupId || msg.channelId) : msg.peerId;
|
|
767
851
|
const selfAid = msg.selfAID || owningAgent?.aid || parsedChannelKey?.selfAID;
|
|
768
852
|
const resolvedChannelType = msg.channelType || parsedChannelKey?.type || effectiveChannelType;
|
|
853
|
+
const menuControl = resolvedChannelType === 'aun'
|
|
854
|
+
? parseMenuControl(content)
|
|
855
|
+
: { isMenu: false };
|
|
856
|
+
const isPrivateMenuTokenRequest = menuControl.isMenu
|
|
857
|
+
&& menuControl.type === 'menu.token.request'
|
|
858
|
+
&& chatType === 'private';
|
|
769
859
|
const admissionRoute = [
|
|
770
860
|
resolvedChannelType,
|
|
771
861
|
selfAid || '',
|
|
@@ -802,7 +892,8 @@ export class MessageBridge {
|
|
|
802
892
|
if (chatType === 'private' && !peerType) {
|
|
803
893
|
peerType = PeerIdentityCache.get('aun', primaryId, agentDir(selfAid))?.type;
|
|
804
894
|
}
|
|
805
|
-
if (
|
|
895
|
+
if (!menuControl.isMenu
|
|
896
|
+
&& chatType === 'private'
|
|
806
897
|
&& REJECTABLE_PEER_TYPES.has(String(peerType || '').trim().toLowerCase())) {
|
|
807
898
|
await this.sendAdmissionControlReply(admissionPreflight.context, msg, sendReply, CONTACT_REJECTION_NOTICE);
|
|
808
899
|
}
|
|
@@ -840,11 +931,23 @@ export class MessageBridge {
|
|
|
840
931
|
return;
|
|
841
932
|
}
|
|
842
933
|
const admissionDecision = evaluateOrdinaryInboundAdmission(admissionPreflight.context);
|
|
843
|
-
if (!admissionDecision.allow) {
|
|
934
|
+
if (!admissionDecision.allow && !isPrivateMenuTokenRequest) {
|
|
844
935
|
if (chatType === 'private') {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
936
|
+
if (menuControl.isMenu) {
|
|
937
|
+
if (hasValidMenuId(menuControl)) {
|
|
938
|
+
const header = { id: menuControl.id, ...(menuControl.name?.trim() ? { name: menuControl.name } : {}) };
|
|
939
|
+
await this.sendAdmissionMenuResponse(admissionDecision.context, adapter, channelName, msg, menuFailure(header, {
|
|
940
|
+
code: 'ROLE_ACCESS_DENIED',
|
|
941
|
+
message: this.admissionDeniedNotice(admissionDecision.context, resolvedChannelType)
|
|
942
|
+
?? '当前不接受陌生人消息。',
|
|
943
|
+
}));
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
const notice = this.admissionDeniedNotice(admissionDecision.context, resolvedChannelType);
|
|
948
|
+
if (notice) {
|
|
949
|
+
await this.sendAdmissionControlReply(admissionDecision.context, msg, sendReply, notice);
|
|
950
|
+
}
|
|
848
951
|
}
|
|
849
952
|
}
|
|
850
953
|
logger.info(`[MessageBridge] Inbound admission denied before business routing: self=${selfAid} actor=${actorId ?? '<none>'} channel=${channelKey} reason=${admissionDecision.reason}`);
|
|
@@ -853,9 +956,6 @@ export class MessageBridge {
|
|
|
853
956
|
// Menu is a protocol control plane. Its parsing and response path must
|
|
854
957
|
// remain available throughout the agent lifecycle; lifecycle gates
|
|
855
958
|
// ordinary model execution, not protocol handling.
|
|
856
|
-
const menuControl = resolvedChannelType === 'aun'
|
|
857
|
-
? parseMenuControl(content)
|
|
858
|
-
: { isMenu: false };
|
|
859
959
|
let menuResponseWarning;
|
|
860
960
|
if (menuControl.isMenu) {
|
|
861
961
|
// Menu is an AUN private control plane. Group messages must never
|
|
@@ -1043,10 +1143,6 @@ export class MessageBridge {
|
|
|
1043
1143
|
await reject('用法:/fa <提示词> 或 /fullaccess <提示词>');
|
|
1044
1144
|
return;
|
|
1045
1145
|
}
|
|
1046
|
-
if (!isFullAccessEnabled()) {
|
|
1047
|
-
await reject('fullaccess 功能未启用,请先设置 daemon.json.fullaccess.enabled=true');
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
1146
|
if (!authSubject.isDaemonOwner || !authSubject.principalId) {
|
|
1051
1147
|
await reject('仅可信 daemon-owner 可以使用 fullaccess');
|
|
1052
1148
|
return;
|
|
@@ -1113,6 +1209,27 @@ export class MessageBridge {
|
|
|
1113
1209
|
// 2. 命令快速路径(去除引用前缀后检查,兼容话题中引用上文的情况)
|
|
1114
1210
|
const contentForCmd = content.replace(/^(>[^\n]*\n)+\n?/, '').trim();
|
|
1115
1211
|
const cmdContent = contentForCmd || content;
|
|
1212
|
+
let autoMode = false;
|
|
1213
|
+
if (resolvedChannelType === 'aun' && chatType === 'group' && selfAid && msg.source !== 'handoff') {
|
|
1214
|
+
try {
|
|
1215
|
+
const config = resolveEffective({ self: selfAid, peerKey: formatPeerKey('aun', msg.groupId || msg.channelId) }, { cache: true, expand: true });
|
|
1216
|
+
autoMode = config.mentionMode === 'auto';
|
|
1217
|
+
}
|
|
1218
|
+
catch (error) {
|
|
1219
|
+
logger.warn(`[MessageBridge] Auto mention configuration unavailable: ${String(error)}`);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (autoMode && !msg.isMentioned && /^\//.test(cmdContent)) {
|
|
1223
|
+
const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', msg.channelId, selfAid || '', channelKey);
|
|
1224
|
+
appendMessageLog(chatDir, buildInboundEntry({
|
|
1225
|
+
from: msg.peerId, to: selfAid || '', chatType: 'group', groupId: msg.groupId,
|
|
1226
|
+
threadId: msg.threadId, msgId: msg.messageId, content: msg.messageLogContent ?? content,
|
|
1227
|
+
timestamp: receivedAt, msgType: 'command', groupAttention: 'command', groupDelivery: 'filtered',
|
|
1228
|
+
}));
|
|
1229
|
+
if (msg.messageId)
|
|
1230
|
+
adapter?.acknowledge?.(msg.messageId).catch(() => { });
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1116
1233
|
if (msg.source !== 'handoff' && !executionPermissionOverride) {
|
|
1117
1234
|
const groupGuard = guardGroupSlashCommand(cmdContent, chatType);
|
|
1118
1235
|
if (groupGuard?.kind === 'command.error') {
|
|
@@ -1194,6 +1311,72 @@ export class MessageBridge {
|
|
|
1194
1311
|
logger.info(`[MessageBridge] Thread creation denied (silent drop): channel=${channelName} channelId=${msg.channelId} thread=${msg.threadId} sender=${msg.peerId}`);
|
|
1195
1312
|
return;
|
|
1196
1313
|
}
|
|
1314
|
+
let autoResult;
|
|
1315
|
+
if (autoMode && selfAid && msg.groupId && !executionPermissionOverride
|
|
1316
|
+
&& !isCmd && !/\[EvolCore\.(receive|reply|agent)\]/.test(content)) {
|
|
1317
|
+
const attentionItem = {
|
|
1318
|
+
value: msg, selfAid, groupId: msg.groupId, threadId: msg.threadId || '', channelKey,
|
|
1319
|
+
sessionId: msg.replyContext?.sessionId
|
|
1320
|
+
?? this.sessionManager.getActiveSessionSync?.(channelName, msg.channelId, resolvedChannelType, selfAid)?.id,
|
|
1321
|
+
projectPath: owningAgent?.projectPath ?? this.defaultProjectPath,
|
|
1322
|
+
messageId: msg.messageId, sender: msg.peerId, role: identity.role,
|
|
1323
|
+
mentions: (msg.mentions ?? []).flatMap(mention => {
|
|
1324
|
+
if (typeof mention.aid === 'string')
|
|
1325
|
+
return [mention.aid];
|
|
1326
|
+
return mention.scope === 'all' ? ['all'] : [];
|
|
1327
|
+
}),
|
|
1328
|
+
content, receivedAt,
|
|
1329
|
+
mainSessionBusy: (() => {
|
|
1330
|
+
try {
|
|
1331
|
+
const active = this.sessionManager.getActiveSessionSync?.(channelName, msg.channelId, resolvedChannelType, selfAid);
|
|
1332
|
+
return !!active && ((this.messageQueue.isProcessing?.(active.id) ?? false)
|
|
1333
|
+
|| (this.messageQueue.getQueueLength?.(active.id) ?? 0) > 0);
|
|
1334
|
+
}
|
|
1335
|
+
catch {
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
})(),
|
|
1339
|
+
};
|
|
1340
|
+
if (msg.isMentioned) {
|
|
1341
|
+
autoResult = {
|
|
1342
|
+
decision: 'interrupt', source: 'mention', leader: true, batch: [attentionItem], background: [],
|
|
1343
|
+
current: [{ source: 'current', message_id: msg.messageId ?? null, sender: msg.peerId,
|
|
1344
|
+
role: identity.role, mentions: attentionItem.mentions,
|
|
1345
|
+
timestamp: new Date(receivedAt).toISOString(), content }],
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
if (msg.messageId)
|
|
1350
|
+
adapter?.acknowledge?.(msg.messageId).catch(() => { });
|
|
1351
|
+
autoResult = await this.autoMentionGate.offer(attentionItem);
|
|
1352
|
+
if (autoResult.decision === 'ignore') {
|
|
1353
|
+
try {
|
|
1354
|
+
const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', msg.channelId, selfAid, channelKey);
|
|
1355
|
+
appendMessageLogStrict(chatDir, buildInboundEntry({
|
|
1356
|
+
from: msg.peerId, to: selfAid, chatType: 'group', groupId: msg.groupId,
|
|
1357
|
+
threadId: msg.threadId, msgId: msg.messageId, content: msg.messageLogContent ?? content,
|
|
1358
|
+
permMode: identity.role, timestamp: receivedAt, receivedAt: msg.receivedAt,
|
|
1359
|
+
gatewaySeq: msg.gatewaySeq, encrypt: msg.encrypted, peerName: msg.peerName,
|
|
1360
|
+
peerType: msg.peerType, msgType: msg.msgType, payloadType: msg.payloadType,
|
|
1361
|
+
payloadSummary: msg.payloadSummary,
|
|
1362
|
+
groupAttention: autoResult.source === 'fallback' ? 'auto-fallback' : 'auto-ignore',
|
|
1363
|
+
groupDelivery: 'filtered',
|
|
1364
|
+
attentionDecision: { ...autoResult.details, decision: 'ignore', source: autoResult.source },
|
|
1365
|
+
}), true);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
catch (error) {
|
|
1369
|
+
logger.warn(`[MessageBridge] Auto ignore history append failed, responding: ${String(error)}`);
|
|
1370
|
+
autoResult = { ...autoResult, decision: 'interrupt', source: 'fallback', leader: true, batch: [attentionItem] };
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (!autoResult.leader)
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
const attentionDecision = autoResult
|
|
1378
|
+
? { ...autoResult.details, decision: autoResult.decision, source: autoResult.source }
|
|
1379
|
+
: undefined;
|
|
1197
1380
|
const metadata = {};
|
|
1198
1381
|
// 话题会话创建时写入 replyContext(用于 threadId 路由);主会话不写(避免群聊覆盖)
|
|
1199
1382
|
if (msg.threadId && msg.replyContext)
|
|
@@ -1229,7 +1412,7 @@ export class MessageBridge {
|
|
|
1229
1412
|
if (!releaseAdmission)
|
|
1230
1413
|
releaseAdmission = await this.acquireAdmission(admissionRoute);
|
|
1231
1414
|
const hadMainSession = msg.threadId ? true : this.sessionManager.hasMainSession(channelName, msg.channelId, msg.channelType || effectiveChannelType, msg.selfAID || selfAid);
|
|
1232
|
-
let session = diagnosticSession ?? await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, owningAgent?.baseagent, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType, identity,
|
|
1415
|
+
let session = diagnosticSession ?? await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, owningAgent?.baseagent, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType, identity, readTopicLabel(msg) ?? readTopicLabel(msg.replyContext?.metadata), readTopicStatus(msg) ?? readTopicStatus(msg.replyContext?.metadata));
|
|
1233
1416
|
if (session.threadId && typeof this.messageQueue.reserveSessionIngress === 'function') {
|
|
1234
1417
|
releaseSessionIngress = this.messageQueue.reserveSessionIngress(session.id);
|
|
1235
1418
|
}
|
|
@@ -1259,6 +1442,32 @@ export class MessageBridge {
|
|
|
1259
1442
|
causation: inboundCausation,
|
|
1260
1443
|
});
|
|
1261
1444
|
session = renewResult.session;
|
|
1445
|
+
if ('renewed' in renewResult && renewResult.renewed) {
|
|
1446
|
+
const notice = '已自动切换到新会话';
|
|
1447
|
+
try {
|
|
1448
|
+
if (adapter?.send) {
|
|
1449
|
+
await adapter.send(buildEnvelope({
|
|
1450
|
+
taskId: `session-renew-${randomBytes(5).toString('hex')}`,
|
|
1451
|
+
sessionId: session.id,
|
|
1452
|
+
channel: channelName,
|
|
1453
|
+
channelId: msg.channelId,
|
|
1454
|
+
agentName: owningAgent?.name ?? selfAid ?? '<unknown>',
|
|
1455
|
+
chatmode: 'interactive',
|
|
1456
|
+
replyContext: msg.replyContext,
|
|
1457
|
+
}), {
|
|
1458
|
+
kind: 'system.notice',
|
|
1459
|
+
text: notice,
|
|
1460
|
+
subtype: 'session.auto-renewed',
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
else {
|
|
1464
|
+
await sendReply(msg.channelId, notice, msg.replyContext);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
catch (error) {
|
|
1468
|
+
logger.warn(`[MessageBridge] Failed to send automatic session-renew notice: ${error instanceof Error ? error.message : String(error)}`);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1262
1471
|
// 4. 群聊发送者标注由消息渲染层(message-renderer)逐条承担,不再在此硬编码前缀,
|
|
1263
1472
|
// 消息日志因此保存干净原文。policy.messagePrefix 暂保留(未来清理)。
|
|
1264
1473
|
// 5. 构造完整消息(channel 字段存实例名,用于 session 精确匹配)
|
|
@@ -1285,15 +1494,45 @@ export class MessageBridge {
|
|
|
1285
1494
|
mentions: msg.mentions, mentionAids: msg.mentionAids, threadId: msg.threadId,
|
|
1286
1495
|
isMentioned: msg.isMentioned,
|
|
1287
1496
|
topicName: this.extractTopicName(msg),
|
|
1288
|
-
|
|
1497
|
+
label: session.label,
|
|
1498
|
+
topicStatus: session.topicStatus,
|
|
1289
1499
|
replyContext: msg.replyContext,
|
|
1290
1500
|
source: msg.source,
|
|
1291
1501
|
...(executionPermissionOverride ? { executionPermissionOverride } : {}),
|
|
1292
1502
|
boundSessionId: session.id,
|
|
1293
1503
|
resolvedIdentity: identity,
|
|
1294
1504
|
mentionMode: msg.mentionMode,
|
|
1505
|
+
...(autoResult && autoResult.decision !== 'ignore' ? { attentionPolicy: autoResult.decision } : {}),
|
|
1506
|
+
...(autoResult ? { autoMentionBatch: true } : {}),
|
|
1295
1507
|
causation: inboundCausation,
|
|
1296
1508
|
};
|
|
1509
|
+
if (autoResult && autoResult.batch.length > 1) {
|
|
1510
|
+
fullMessage.content = autoResult.batch.map(item => item.content).join('\n');
|
|
1511
|
+
fullMessage.sourceMessageIds = autoResult.batch.flatMap(item => item.messageId ? [item.messageId] : []);
|
|
1512
|
+
fullMessage.items = autoResult.batch.map(item => ({
|
|
1513
|
+
peerId: item.sender, peerName: item.value.peerName, peerType: item.value.peerType,
|
|
1514
|
+
peerRole: item.role, content: item.content,
|
|
1515
|
+
mentions: item.value.mentions, mentionAids: item.value.mentionAids,
|
|
1516
|
+
images: item.value.images, timestamp: item.receivedAt,
|
|
1517
|
+
receivedAt: item.value.receivedAt, gatewaySeq: item.value.gatewaySeq,
|
|
1518
|
+
encrypted: item.value.encrypted,
|
|
1519
|
+
sameDevice: item.value.sameDevice, sameNetwork: item.value.sameNetwork,
|
|
1520
|
+
sameEgressIp: item.value.sameEgressIp,
|
|
1521
|
+
}));
|
|
1522
|
+
fullMessage.images = autoResult.batch.flatMap(item => item.value.images ?? []);
|
|
1523
|
+
fullMessage.mentions = autoResult.batch.flatMap(item => item.value.mentions ?? []);
|
|
1524
|
+
fullMessage.timestamp = autoResult.batch[0].receivedAt;
|
|
1525
|
+
fullMessage.receivedAt = autoResult.batch[0].receivedAt;
|
|
1526
|
+
fullMessage.encrypted = autoResult.batch.some(item => item.value.encrypted === true) ? true : fullMessage.encrypted;
|
|
1527
|
+
if (fullMessage.encrypted && fullMessage.replyContext) {
|
|
1528
|
+
fullMessage.replyContext = {
|
|
1529
|
+
...fullMessage.replyContext,
|
|
1530
|
+
metadata: { ...fullMessage.replyContext.metadata, encrypted: true },
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
if (!autoResult.batch.every(item => item.role === identity.role))
|
|
1534
|
+
fullMessage.batchRole = 'visitor';
|
|
1535
|
+
}
|
|
1297
1536
|
recordCausationSpan(inboundCausation, 'message.inbound', {
|
|
1298
1537
|
status: 'completed',
|
|
1299
1538
|
refs: { messageId: msg.messageId, sessionId: session.id },
|
|
@@ -1326,6 +1565,11 @@ export class MessageBridge {
|
|
|
1326
1565
|
msgType: msg.msgType,
|
|
1327
1566
|
payloadType: msg.payloadType,
|
|
1328
1567
|
payloadSummary: msg.payloadSummary,
|
|
1568
|
+
groupAttention: autoResult?.source === 'mention' ? 'auto-mention'
|
|
1569
|
+
: autoResult?.source === 'fallback' ? 'auto-fallback'
|
|
1570
|
+
: autoResult?.decision === 'defer' ? 'auto-defer'
|
|
1571
|
+
: autoResult ? 'auto-interrupt' : undefined,
|
|
1572
|
+
attentionDecision,
|
|
1329
1573
|
}) };
|
|
1330
1574
|
})();
|
|
1331
1575
|
let handoffCandidate = false;
|
|
@@ -1358,6 +1602,23 @@ export class MessageBridge {
|
|
|
1358
1602
|
}
|
|
1359
1603
|
if (!handoffCandidate)
|
|
1360
1604
|
appendMessageLog(inboundEntry.chatDir, inboundEntry.entry);
|
|
1605
|
+
if (autoResult && autoResult.batch.length > 1) {
|
|
1606
|
+
for (const item of autoResult.batch.slice(0, -1)) {
|
|
1607
|
+
const member = item.value;
|
|
1608
|
+
appendMessageLog(inboundEntry.chatDir, buildInboundEntry({
|
|
1609
|
+
from: item.sender, to: selfAid || '', sessionId: session.id,
|
|
1610
|
+
threadId: session.threadId || null, chatType: 'group', groupId: msg.groupId,
|
|
1611
|
+
msgId: item.messageId, content: member.messageLogContent ?? item.content,
|
|
1612
|
+
permMode: item.role, timestamp: item.receivedAt,
|
|
1613
|
+
receivedAt: member.receivedAt, gatewaySeq: member.gatewaySeq,
|
|
1614
|
+
encrypt: member.encrypted, peerName: member.peerName, peerType: member.peerType,
|
|
1615
|
+
msgType: member.msgType, payloadType: member.payloadType,
|
|
1616
|
+
payloadSummary: member.payloadSummary,
|
|
1617
|
+
groupAttention: autoResult?.decision === 'defer' ? 'auto-defer' : 'auto-interrupt',
|
|
1618
|
+
attentionDecision,
|
|
1619
|
+
}));
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1361
1622
|
// The authoritative session choice is now paired with its first
|
|
1362
1623
|
// conversation record. Release before waiting for queue completion so
|
|
1363
1624
|
// later group messages can still exercise the normal interrupt/FIFO
|
|
@@ -1376,6 +1637,8 @@ export class MessageBridge {
|
|
|
1376
1637
|
adapter?.acknowledge?.(fullMessage.messageId).catch(() => { });
|
|
1377
1638
|
}
|
|
1378
1639
|
const isInterrupt = chatType !== 'group' && !handoffCandidate;
|
|
1640
|
+
const attentionPolicy = fullMessage.attentionPolicy;
|
|
1641
|
+
const shouldInterrupt = isInterrupt || attentionPolicy === 'interrupt';
|
|
1379
1642
|
const enqueueAgentName = owningAgent?.name ?? '<unknown>';
|
|
1380
1643
|
const selfAID = session.selfAID;
|
|
1381
1644
|
const doEnqueue = async (m) => {
|
|
@@ -1385,10 +1648,13 @@ export class MessageBridge {
|
|
|
1385
1648
|
const enqueue = handoffCandidate ? this.messageQueue.enqueuePersisted.bind(this.messageQueue) : this.messageQueue.enqueue.bind(this.messageQueue);
|
|
1386
1649
|
try {
|
|
1387
1650
|
return await enqueue(session.id, m, session.projectPath, {
|
|
1388
|
-
interruptible:
|
|
1389
|
-
|
|
1651
|
+
interruptible: shouldInterrupt,
|
|
1652
|
+
attentionPolicy,
|
|
1653
|
+
interruptSamePeer: !shouldInterrupt && !attentionPolicy, // 旧群聊路径保留同人连发打断
|
|
1654
|
+
priority: attentionPolicy === 'interrupt',
|
|
1390
1655
|
agentName: enqueueAgentName,
|
|
1391
|
-
role:
|
|
1656
|
+
role: chatType === 'group' ? (session.identity?.role ?? 'none')
|
|
1657
|
+
: (!shouldInterrupt ? (session.identity?.role ?? 'none') : undefined),
|
|
1392
1658
|
sessionKeyField: session.sessionKey,
|
|
1393
1659
|
selfAID, // ← 传递 selfAID,用于队列隔离
|
|
1394
1660
|
onPersisted: releaseIngress,
|
|
@@ -1748,7 +2014,7 @@ export class MessageBridge {
|
|
|
1748
2014
|
...(msg.peerDeviceId ? { toDeviceId: msg.peerDeviceId } : {}),
|
|
1749
2015
|
metadata: {
|
|
1750
2016
|
...(msg.replyContext?.metadata ?? {}),
|
|
1751
|
-
...evolMenuResponseTransportMetadata(msg.protectedHeaders?.sdk_version),
|
|
2017
|
+
...evolMenuResponseTransportMetadata(msg.protectedHeaders?.sdk_version, msg.protectedHeaders?.menu_volatile_v1),
|
|
1752
2018
|
...(isAgentStorageMenuRequest(request) ? { encrypted: true } : {}),
|
|
1753
2019
|
},
|
|
1754
2020
|
};
|
|
@@ -345,6 +345,7 @@ export function buildInboundEntry(opts) {
|
|
|
345
345
|
payloadSummary: opts.payloadSummary,
|
|
346
346
|
groupAttention: opts.groupAttention,
|
|
347
347
|
groupDelivery: opts.groupDelivery,
|
|
348
|
+
attentionDecision: opts.attentionDecision,
|
|
348
349
|
content: opts.content,
|
|
349
350
|
replyTo: opts.replyTo ?? null,
|
|
350
351
|
agent: null,
|