evolcore 0.0.3 → 0.0.5
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 +83 -0
- package/README.md +45 -10
- package/bin/ec-safe-output.js +89 -24
- package/dist/agents/baseagent.js +46 -0
- package/dist/agents/claude-runner.js +1 -31
- package/dist/agents/codex-app-server-client.js +68 -0
- package/dist/agents/codex-runner.js +157 -5
- package/dist/agents/runner-types.js +2 -2
- package/dist/aun/aid/agentmd.js +79 -0
- package/dist/aun/aid/encryption-seed-policy.js +29 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +2 -5
- package/dist/channels/aun.js +220 -112
- package/dist/channels/daemon.js +18 -4
- package/dist/channels/dingtalk.js +52 -137
- package/dist/channels/feishu.js +56 -4
- package/dist/channels/qqbot.js +23 -1
- package/dist/channels/wechat.js +303 -155
- package/dist/channels/wecom.js +383 -7
- package/dist/cli/aun-commands.js +11 -11
- package/dist/cli/daemon-commands.js +46 -15
- package/dist/cli/handoff-command.js +2 -1
- package/dist/cli/init-channel.js +185 -67
- package/dist/cli/init.js +49 -27
- package/dist/cli/trigger-command.js +92 -11
- package/dist/config/access-policy-domain.js +18 -0
- package/dist/config/access-policy.js +110 -0
- package/dist/config/builtin-role-templates.js +27 -14
- package/dist/config/builtin-roles.js +25 -6
- package/dist/config/config-field-policy.js +17 -5
- package/dist/config/config-manager.js +198 -22
- package/dist/config/config-operation-service.js +35 -38
- package/dist/config/contact-bind-code.js +274 -0
- package/dist/config/contact-book-store.js +173 -5
- package/dist/config/contact-book.js +32 -0
- package/dist/config/contact-operation-service.js +9 -3
- package/dist/config/contact-request-service.js +331 -0
- package/dist/config/peer-role-resolver.js +3 -1
- package/dist/config/schema-registry.js +49 -24
- package/dist/config-store.js +14 -2
- package/dist/core/auth/agent-delegation.js +105 -11
- package/dist/core/auth/authorization-audit.js +6 -0
- package/dist/core/auth/operation-authorizer.js +8 -0
- package/dist/core/auth/operation-catalog.js +51 -3
- package/dist/core/auth/trigger-authorization.js +15 -0
- package/dist/core/bootstrap-service.js +2 -9
- package/dist/core/channel-loader.js +6 -2
- package/dist/core/command/command-handler.js +10 -13
- package/dist/core/command/connect-menu.js +229 -26
- package/dist/core/command/evol-menu-version-gate.js +38 -0
- package/dist/core/command/group-menu.js +421 -0
- package/dist/core/command/menu-handler.js +269 -85
- package/dist/core/command/menu-protocol.js +8 -2
- package/dist/core/command/menu-token-store.js +102 -0
- package/dist/core/command/role-menu.js +16 -0
- package/dist/core/command/slash-gate.js +1 -1
- package/dist/core/command/slash-handler.js +158 -31
- package/dist/core/daemon-file-cache.js +28 -0
- package/dist/core/event-catalog.js +29 -0
- package/dist/core/evolagent-registry.js +4 -39
- package/dist/core/handoff/runtime.js +162 -37
- package/dist/core/handoff/store.js +46 -2
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +16 -74
- package/dist/core/message/file-markers.js +7 -0
- package/dist/core/message/im-renderer.js +18 -14
- package/dist/core/message/inbound-admission.js +134 -0
- package/dist/core/message/message-bridge.js +616 -74
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +185 -10
- package/dist/core/message/peer-mode.js +7 -8
- package/dist/core/message/response-engine.js +381 -79
- package/dist/core/permission/approval-gateway.js +17 -10
- package/dist/core/permission/ec-command-parser.js +101 -46
- package/dist/core/permission/tool-policy.js +69 -24
- package/dist/core/protected-paths.js +36 -11
- package/dist/core/session/session-fs-store.js +19 -4
- package/dist/core/session/session-manager.js +232 -4
- package/dist/core/session/session-mapper.js +2 -0
- package/dist/core/session/session-renew.js +125 -69
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/eck/kit-renderer.js +12 -1
- package/dist/eck/message-renderer.js +79 -1
- package/dist/index.js +224 -89
- package/dist/ipc.js +81 -4
- package/dist/paths.js +3 -0
- package/dist/response-system/config-resolver.js +29 -0
- package/dist/response-system/coordinator.js +8 -32
- package/dist/response-system/engines/v1/proactive-flow.js +1 -1
- package/dist/response-system/index.js +1 -0
- package/dist/response-system/modes/single-session/index.js +7 -4
- package/dist/trigger/parser.js +55 -15
- package/dist/trigger/patch.js +4 -1
- package/dist/trigger/scheduler.js +441 -39
- package/dist/utils/cross-platform.js +8 -2
- package/dist/utils/error-dict.json +7 -0
- package/dist/utils/evolcore-version.js +21 -0
- package/dist/utils/logger.js +2 -2
- package/dist/utils/process-introspect.js +19 -3
- package/dist/utils/stable-semver.js +21 -0
- package/dist/utils/stats.js +33 -9
- package/kits/docs/channels/aun.md +4 -13
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/config.md +47 -2
- package/kits/docs/evolcore/contact.md +8 -3
- package/kits/docs/evolcore/group-rules.md +46 -4
- package/kits/docs/evolcore/group.md +4 -4
- package/kits/docs/evolcore/msg.md +5 -5
- package/kits/docs/evolcore/trigger.md +25 -8
- package/kits/docs/path-registry.md +36 -17
- package/kits/eck_message_manifest.json +12 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
- package/kits/rules/01-overview.md +17 -7
- package/kits/rules/02-navigation.md +38 -18
- package/kits/rules/03-identity.md +28 -24
- package/kits/rules/04-relation.md +44 -28
- package/kits/rules/05-venue.md +31 -15
- package/kits/rules/06-channel.md +11 -7
- package/kits/schemas/_meta.json +18 -7
- package/kits/schemas/agent-config.schema.7.json +303 -0
- package/kits/schemas/agent-config.schema.8.json +304 -0
- package/kits/schemas/agent-config.schema.9.json +364 -0
- package/kits/schemas/contact-book.schema.3.json +67 -0
- package/kits/schemas/daemon.schema.1.json +3 -2
- package/kits/schemas/daemon.schema.2.json +101 -0
- package/kits/schemas/daemon.schema.3.json +123 -0
- package/kits/schemas/defaults.schema.2.json +85 -0
- package/kits/schemas/defaults.schema.3.json +73 -0
- package/kits/schemas/relation-config.schema.6.json +46 -0
- package/kits/schemas/relation-config.schema.7.json +59 -0
- package/kits/schemas/single-session.schema.2.json +57 -0
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
- package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
- package/kits/templates/roles/admin.json +46 -0
- package/kits/templates/roles/member.json +4 -0
- package/kits/templates/roles/visitor.json +4 -0
- package/kits/templates/system-fragments/channel.md +12 -2
- package/kits/templates/system-fragments/identity.md +3 -1
- package/kits/templates/system-fragments/relation.md +1 -1
- package/kits/templates/system-fragments/session.md +6 -2
- package/package.json +4 -2
- package/MIGRATION-0.5.0.md +0 -378
- package/ROLE_ACCESS_CONTROL.md +0 -174
- package/dist/channels/contact-bind-code.js +0 -134
- package/dist/channels/wecom-card.js +0 -101
- package/dist/channels/wecom-onboarding.js +0 -82
- package/dist/channels/wecom-state.js +0 -191
package/dist/channels/aun.js
CHANGED
|
@@ -7,10 +7,11 @@ import { logger, localTimestamp } from '../utils/logger.js';
|
|
|
7
7
|
import { LogWriter } from '../utils/log-writer.js';
|
|
8
8
|
import { middleOutputModePolicy, resolveShowActivities, showActivitiesPolicy } from '../core/channel-loader.js';
|
|
9
9
|
import { DEFAULT_FLUSH_DELAY_SECONDS } from '../types.js';
|
|
10
|
-
import { resolvePaths,
|
|
10
|
+
import { resolvePaths, agentMdPath as agentMdPathFn, agentDir as agentDirPath, resolveRoot } from '../paths.js';
|
|
11
11
|
import { saveToUploads, sanitizeFileName, bufferToInboundImage, safeFetch } from '../utils/media-cache.js';
|
|
12
12
|
import { appendAidEvent } from '../utils/instance-registry.js';
|
|
13
13
|
import { appendMessageLog, buildOutboundEntry, buildInboundEntry, classifyAunPayloadForLog } from '../core/message/message-log.js';
|
|
14
|
+
import { createSendFileMarkerPattern } from '../core/message/file-markers.js';
|
|
14
15
|
import { chatDirPath } from '../core/session/session-fs-store.js';
|
|
15
16
|
import { appendHintAdd, appendHintRemove, parseInjectRequest } from '../core/message/pending-hints.js';
|
|
16
17
|
import { appendAidLifecycle } from '../aun/aid/identity.js';
|
|
@@ -31,6 +32,8 @@ import { consumeAunCausation, registerAunCausation } from '../core/causation/aun
|
|
|
31
32
|
import { deriveCausation, normalizeCausation } from '../core/causation/context.js';
|
|
32
33
|
import { recordCausationSpan } from '../core/causation/audit.js';
|
|
33
34
|
import { isExplicitGroupId } from '../aun/group-identity.js';
|
|
35
|
+
import { refreshAgentDisplayName, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
36
|
+
import { readInstalledEvolcoreVersion } from '../utils/evolcore-version.js';
|
|
34
37
|
export const AUN_HANDOFF_MARKER_FIELD = '_evolcore_handoff_id';
|
|
35
38
|
/**
|
|
36
39
|
* 构造 connect extra_info:自描述本进程身份。
|
|
@@ -45,7 +48,7 @@ function buildConnectExtraInfo(opts) {
|
|
|
45
48
|
const startedAt = getProcessStartTime(process.pid) ?? Date.now();
|
|
46
49
|
return {
|
|
47
50
|
app: 'evolcore',
|
|
48
|
-
version:
|
|
51
|
+
version: readInstalledEvolcoreVersion(),
|
|
49
52
|
pid: process.pid,
|
|
50
53
|
started_at: startedAt,
|
|
51
54
|
started_at_iso: new Date(startedAt).toISOString(),
|
|
@@ -59,23 +62,14 @@ function buildConnectExtraInfo(opts) {
|
|
|
59
62
|
channel_name: opts.channelName ?? '',
|
|
60
63
|
};
|
|
61
64
|
}
|
|
62
|
-
let _cachedVersion = null;
|
|
63
|
-
function getEvolcoreVersion() {
|
|
64
|
-
if (_cachedVersion !== null)
|
|
65
|
-
return _cachedVersion;
|
|
66
|
-
try {
|
|
67
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(getPackageRoot(), 'package.json'), 'utf-8'));
|
|
68
|
-
_cachedVersion = String(pkg.version ?? '');
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
_cachedVersion = '';
|
|
72
|
-
}
|
|
73
|
-
return _cachedVersion;
|
|
74
|
-
}
|
|
75
65
|
export function buildAunFilePayload(params) {
|
|
76
66
|
const correlationId = params.context?.metadata?.correlationId;
|
|
77
67
|
const payload = {
|
|
78
|
-
|
|
68
|
+
// `result.file` is EvolCore's internal outbound kind. On the AUN wire this
|
|
69
|
+
// must remain the standard `file` payload type so clients route it through
|
|
70
|
+
// their attachment/download pipeline; correlation_id carries the control
|
|
71
|
+
// request association without changing the media type.
|
|
72
|
+
type: 'file',
|
|
79
73
|
text: `📎 ${params.filename} (${formatSize(params.size)})`,
|
|
80
74
|
attachments: [params.attachment],
|
|
81
75
|
};
|
|
@@ -121,6 +115,7 @@ export function aunOptsToInbound(opts, channel, channelType) {
|
|
|
121
115
|
sameNetwork: opts.sameNetwork,
|
|
122
116
|
sameEgressIp: opts.sameEgressIp,
|
|
123
117
|
encrypted: opts.encrypted,
|
|
118
|
+
protectedHeaders: opts.protectedHeaders,
|
|
124
119
|
messageId: opts.messageId,
|
|
125
120
|
causation: opts.causation,
|
|
126
121
|
mentions: opts.mentions,
|
|
@@ -133,6 +128,54 @@ export function aunOptsToInbound(opts, channel, channelType) {
|
|
|
133
128
|
dispatchMode: opts.dispatchMode,
|
|
134
129
|
};
|
|
135
130
|
}
|
|
131
|
+
function verifiedProtectedHeaders(msg, env) {
|
|
132
|
+
const candidates = [
|
|
133
|
+
msg.e2ee?.protected_headers,
|
|
134
|
+
env.protected_headers,
|
|
135
|
+
msg.protected_headers,
|
|
136
|
+
];
|
|
137
|
+
for (const candidate of candidates) {
|
|
138
|
+
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate))
|
|
139
|
+
continue;
|
|
140
|
+
const headers = {};
|
|
141
|
+
for (const [key, value] of Object.entries(candidate)) {
|
|
142
|
+
const name = key.trim().toLowerCase();
|
|
143
|
+
if (!/^[a-z0-9_-]+$/.test(name) || typeof value !== 'string')
|
|
144
|
+
continue;
|
|
145
|
+
headers[name] = value;
|
|
146
|
+
}
|
|
147
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
148
|
+
}
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
function redactMenuTokensForTrace(value, seen = new WeakSet()) {
|
|
152
|
+
if (!value || typeof value !== 'object')
|
|
153
|
+
return value;
|
|
154
|
+
if (seen.has(value))
|
|
155
|
+
return '[Circular]';
|
|
156
|
+
seen.add(value);
|
|
157
|
+
if (Array.isArray(value))
|
|
158
|
+
return value.map(item => redactMenuTokensForTrace(item, seen));
|
|
159
|
+
const redacted = {};
|
|
160
|
+
for (const [key, item] of Object.entries(value)) {
|
|
161
|
+
redacted[key] = key.toLowerCase() === 'menu_token'
|
|
162
|
+
? '[REDACTED]'
|
|
163
|
+
: redactMenuTokensForTrace(item, seen);
|
|
164
|
+
}
|
|
165
|
+
return redacted;
|
|
166
|
+
}
|
|
167
|
+
function payloadTypeForObserver(value) {
|
|
168
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
169
|
+
return undefined;
|
|
170
|
+
const record = value;
|
|
171
|
+
const payload = record.payload
|
|
172
|
+
?? (record.message && typeof record.message === 'object' ? record.message.payload : undefined)
|
|
173
|
+
?? (record.envelope && typeof record.envelope === 'object' ? record.envelope.payload : undefined);
|
|
174
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload))
|
|
175
|
+
return undefined;
|
|
176
|
+
const type = payload.type;
|
|
177
|
+
return typeof type === 'string' ? type : undefined;
|
|
178
|
+
}
|
|
136
179
|
export class AUNChannel {
|
|
137
180
|
config;
|
|
138
181
|
client = null;
|
|
@@ -157,7 +200,8 @@ export class AUNChannel {
|
|
|
157
200
|
return;
|
|
158
201
|
// 自动从 data 推断顶层字段(self_aid / peer_aid / group_id / task_id / chatmode),
|
|
159
202
|
// 便于 jq 过滤:`jq 'select(.task_id == "task-xxx")'`
|
|
160
|
-
const
|
|
203
|
+
const safeData = redactMenuTokensForTrace(data);
|
|
204
|
+
const d = (safeData && typeof safeData === 'object') ? safeData : {};
|
|
161
205
|
const payload = d.payload ?? {};
|
|
162
206
|
// 入站事件(IN)路由字段在 d.envelope.*(SDK 0.5.*);出站 params(OUT)字段在顶层。两者兼顾。
|
|
163
207
|
const env = (d.envelope && typeof d.envelope === 'object') ? d.envelope : {};
|
|
@@ -178,7 +222,7 @@ export class AUNChannel {
|
|
|
178
222
|
const chatmode = payload.chatmode;
|
|
179
223
|
if (chatmode)
|
|
180
224
|
topContext.chatmode = chatmode;
|
|
181
|
-
const line = JSON.stringify({ ts: localTimestamp(), dir, event, ...topContext, data });
|
|
225
|
+
const line = JSON.stringify({ ts: localTimestamp(), dir, event, ...topContext, data: safeData });
|
|
182
226
|
this.traceWriter.write(line);
|
|
183
227
|
}
|
|
184
228
|
/** 日志前缀(含 self aid 简称,多实例可识别) */
|
|
@@ -285,8 +329,20 @@ export class AUNChannel {
|
|
|
285
329
|
: typeof obj.card_message_id === 'string' ? obj.card_message_id : '';
|
|
286
330
|
const cardInfo = cardMsgId ? this.cardMessageIdMap.get(cardMsgId) : undefined;
|
|
287
331
|
if (cardInfo) {
|
|
288
|
-
|
|
289
|
-
|
|
332
|
+
// compose 按钮点击后客户端收卡聚焦输入框,用户随后发送的自由文本由客户端结构化
|
|
333
|
+
// 回传为 action_card_reply{behavior:"compose", text},没有按钮 value/action_value。
|
|
334
|
+
// 映射为 _custom_input + values.custom_text,复用 claude-runner 已有的自定义输入分支
|
|
335
|
+
// (原为多选表单提交而设),不需要新增 action 类型,也不必拦截消息队列。
|
|
336
|
+
const behaviorRaw = typeof obj.behavior === 'string' ? obj.behavior : undefined;
|
|
337
|
+
if (behaviorRaw && behaviorRaw !== 'reply' && behaviorRaw !== 'compose' && behaviorRaw !== 'dismiss') {
|
|
338
|
+
// 非法 behavior 值:按协议兜底为 reply 处理,但记录下来便于排查不合规客户端
|
|
339
|
+
// (例如本该是 compose 手动输入却被误判为普通按钮点击)。
|
|
340
|
+
logger.warn(`${this.logPrefix()} action_card_reply with unrecognized behavior="${behaviorRaw}" mid=${cardMsgId}, treating as reply`);
|
|
341
|
+
}
|
|
342
|
+
const isComposeReply = behaviorRaw === 'compose';
|
|
343
|
+
const actionValue = isComposeReply ? '_custom_input'
|
|
344
|
+
: typeof obj.value === 'string' ? obj.value
|
|
345
|
+
: typeof obj.action_value === 'string' ? obj.action_value : text;
|
|
290
346
|
const threadId = typeof obj.thread_id === 'string' ? obj.thread_id : undefined;
|
|
291
347
|
// 卡片点击者身份:只信认证信封(senderAid 参数,由调用方从 msg.from / msg.sender_aid 提取)。
|
|
292
348
|
// payload 自报字段(from / sender_aid / user_id)不可信,可被客户端伪造,不读取。
|
|
@@ -361,7 +417,13 @@ export class AUNChannel {
|
|
|
361
417
|
type: 'interaction.response',
|
|
362
418
|
id: cardInfo.requestId,
|
|
363
419
|
action: actionValue,
|
|
364
|
-
values: {
|
|
420
|
+
values: {
|
|
421
|
+
text,
|
|
422
|
+
action_label: obj.label ?? obj.action_label,
|
|
423
|
+
behavior: obj.behavior,
|
|
424
|
+
card_message_id: cardMsgId,
|
|
425
|
+
...(isComposeReply ? { custom_text: text } : {}),
|
|
426
|
+
},
|
|
365
427
|
operatorId: cardClickerAid || undefined,
|
|
366
428
|
};
|
|
367
429
|
const settle = (accepted) => {
|
|
@@ -653,10 +715,6 @@ export class AUNChannel {
|
|
|
653
715
|
'text', 'quote', 'image', 'video', 'voice', 'file', 'json',
|
|
654
716
|
'merge', 'link', 'location', 'personal_card',
|
|
655
717
|
]);
|
|
656
|
-
/** Menu protocol 请求类型:自定义消息快速路径,绕过白名单直接分发到 bridge */
|
|
657
|
-
static MENU_REQUEST_TYPES = new Set([
|
|
658
|
-
'menu.list', 'menu.query', 'menu.options', 'menu.update', 'menu.action',
|
|
659
|
-
]);
|
|
660
718
|
/** 观察者插话请求类型(owner → agent.AID)。详见 docs/observer-insert-design.md。 */
|
|
661
719
|
static INJECT_REQUEST_TYPE = 'observer.inject';
|
|
662
720
|
/** 绑定请求类型(Evol App → 控制 AID):快速路径,绕过白名单透传原始 payload JSON 给 bridge。 */
|
|
@@ -1316,6 +1374,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1316
1374
|
// SDK 0.5.* 移除了顶层 from/to/group_id/encrypted 等别名,统一从 msg.envelope.* 读取。
|
|
1317
1375
|
// message_id / seq / payload / same_* 等仍是顶层独立字段,不在 envelope 内。
|
|
1318
1376
|
const env = (msg.envelope && typeof msg.envelope === 'object') ? msg.envelope : {};
|
|
1377
|
+
const protectedHeaders = verifiedProtectedHeaders(msg, env);
|
|
1319
1378
|
const fromAid = env.from ?? '';
|
|
1320
1379
|
const payload = msg.payload ?? '';
|
|
1321
1380
|
const causationRef = payload && typeof payload === 'object' && typeof payload._evolcore_causation_ref === 'string'
|
|
@@ -1371,7 +1430,9 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1371
1430
|
const shortAid = this.getShortAid(fromAid);
|
|
1372
1431
|
const displayName = peerIdentity.name || shortAid;
|
|
1373
1432
|
// 详细 dispatch 决策日志:记录消息为何被路由到 agent
|
|
1374
|
-
const p2pPayloadType = (payload && typeof payload === 'object'
|
|
1433
|
+
const p2pPayloadType = (payload && typeof payload === 'object' && typeof payload.type === 'string')
|
|
1434
|
+
? payload.type
|
|
1435
|
+
: '';
|
|
1375
1436
|
logger.info(`${this.logPrefix()} P2P dispatch decision: mid=${messageId} from=${shortAid}(${displayName}) peerType=${peerIdentity.type} payloadType=${p2pPayloadType} chatId=${chatId} encrypt=${msgEncrypted} textPreview=${JSON.stringify(text.slice(0, 80))}`);
|
|
1376
1437
|
// action_card_reply 已在 extractTextPayload 中消费,不分发给 agent
|
|
1377
1438
|
if (p2pPayloadType === 'action_card_reply') {
|
|
@@ -1379,7 +1440,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1379
1440
|
return;
|
|
1380
1441
|
}
|
|
1381
1442
|
// menu.* 协议:自定义消息快速路径,需要原始 payload JSON 传递给 bridge
|
|
1382
|
-
if (
|
|
1443
|
+
if (p2pPayloadType.startsWith('menu.')) {
|
|
1383
1444
|
this.acknowledgeImmediately(messageId, seq);
|
|
1384
1445
|
this.appendInboundPayloadJsonl(chatId, fromAid, messageId, msgEncrypted, false, logDescriptor, threadId);
|
|
1385
1446
|
this.dispatchMessage({
|
|
@@ -1388,6 +1449,9 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1388
1449
|
chatType: 'private', messageId, seq,
|
|
1389
1450
|
peerName: displayName || undefined,
|
|
1390
1451
|
peerType: peerIdentity.type,
|
|
1452
|
+
encrypted: msgEncrypted,
|
|
1453
|
+
protectedHeaders,
|
|
1454
|
+
replyContext: this.buildGroupReplyContext(threadId, fromAid, msgEncrypted, messageId),
|
|
1391
1455
|
msgType: logDescriptor.msgType,
|
|
1392
1456
|
payloadType: logDescriptor.payloadType,
|
|
1393
1457
|
payloadSummary: logDescriptor.payloadSummary,
|
|
@@ -1477,6 +1541,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1477
1541
|
// SDK 0.5.* 移除了顶层 from/sender_aid/group_id/encrypted 等别名,统一从 msg.envelope.* 读取。
|
|
1478
1542
|
// message_id / seq / payload / same_* / dispatch_mode 等仍是顶层独立字段,不在 envelope 内。
|
|
1479
1543
|
const env = (msg.envelope && typeof msg.envelope === 'object') ? msg.envelope : {};
|
|
1544
|
+
const protectedHeaders = verifiedProtectedHeaders(msg, env);
|
|
1480
1545
|
const groupId = env.group_id ?? '';
|
|
1481
1546
|
const senderAid = env.from ?? '';
|
|
1482
1547
|
const payload = msg.payload ?? '';
|
|
@@ -1554,15 +1619,19 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1554
1619
|
// 信号类消息(status / event / thought / task.update 等)不进 Agent
|
|
1555
1620
|
{
|
|
1556
1621
|
const payloadObj = (payload && typeof payload === 'object') ? payload : null;
|
|
1557
|
-
const payloadType = payloadObj?.type
|
|
1622
|
+
const payloadType = typeof payloadObj?.type === 'string' ? payloadObj.type : '';
|
|
1558
1623
|
// menu.* 协议:自定义消息快速路径
|
|
1559
|
-
if (
|
|
1624
|
+
if (payloadType.startsWith('menu.')) {
|
|
1625
|
+
const menuEncrypted = !!env.encrypted;
|
|
1560
1626
|
this.acknowledgeImmediately(messageId, seq);
|
|
1561
|
-
this.appendInboundPayloadJsonl(groupId, senderAid, messageId,
|
|
1627
|
+
this.appendInboundPayloadJsonl(groupId, senderAid, messageId, menuEncrypted, true, logDescriptor, threadId);
|
|
1562
1628
|
this.dispatchMessage({
|
|
1563
1629
|
channelId: groupId, userId: senderAid,
|
|
1564
1630
|
text: JSON.stringify(payload),
|
|
1565
1631
|
chatType: 'group', messageId, seq, groupId,
|
|
1632
|
+
encrypted: menuEncrypted,
|
|
1633
|
+
protectedHeaders,
|
|
1634
|
+
replyContext: this.buildGroupReplyContext(threadId, senderAid, menuEncrypted, messageId, payloadObj?.chatmode, topicName),
|
|
1566
1635
|
msgType: logDescriptor.msgType,
|
|
1567
1636
|
payloadType: logDescriptor.payloadType,
|
|
1568
1637
|
payloadSummary: logDescriptor.payloadSummary,
|
|
@@ -1781,6 +1850,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1781
1850
|
sameNetwork: event.sameNetwork,
|
|
1782
1851
|
sameEgressIp: event.sameEgressIp,
|
|
1783
1852
|
encrypted: event.encrypted,
|
|
1853
|
+
protectedHeaders: event.protectedHeaders,
|
|
1784
1854
|
messageId: event.messageId,
|
|
1785
1855
|
causation: event.causation,
|
|
1786
1856
|
threadId: event.threadId,
|
|
@@ -1849,6 +1919,8 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1849
1919
|
forwardInbound(data) {
|
|
1850
1920
|
if (!this.connected || !this.client)
|
|
1851
1921
|
return;
|
|
1922
|
+
if (payloadTypeForObserver(data)?.startsWith('menu.'))
|
|
1923
|
+
return;
|
|
1852
1924
|
const { observable, owners } = this.getObserverConfig();
|
|
1853
1925
|
if (!observable || owners.length === 0)
|
|
1854
1926
|
return;
|
|
@@ -1870,6 +1942,8 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
1870
1942
|
forwardOutbound(result) {
|
|
1871
1943
|
if (!this.connected || !this.client)
|
|
1872
1944
|
return;
|
|
1945
|
+
if (payloadTypeForObserver(result)?.startsWith('menu.'))
|
|
1946
|
+
return;
|
|
1873
1947
|
const { observable, owners } = this.getObserverConfig();
|
|
1874
1948
|
if (!observable || owners.length === 0)
|
|
1875
1949
|
return;
|
|
@@ -2362,6 +2436,30 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2362
2436
|
messageIdFromSendResult(result) {
|
|
2363
2437
|
return result?.message?.message_id ?? result?.message_id ?? null;
|
|
2364
2438
|
}
|
|
2439
|
+
/**
|
|
2440
|
+
* An AUN send result is only a gateway transport result. It is not a
|
|
2441
|
+
* receipt that the peer client has pulled, decrypted, stored, or rendered it.
|
|
2442
|
+
* Preserve the compact fields for later correlation with the receiving side.
|
|
2443
|
+
*/
|
|
2444
|
+
sendReceiptFromResult(result) {
|
|
2445
|
+
if (!result || typeof result !== 'object')
|
|
2446
|
+
return {};
|
|
2447
|
+
const response = result;
|
|
2448
|
+
return {
|
|
2449
|
+
status: typeof response.status === 'string' ? response.status : undefined,
|
|
2450
|
+
seq: typeof response.seq === 'number' ? response.seq : undefined,
|
|
2451
|
+
timestamp: typeof response.timestamp === 'number' ? response.timestamp : undefined,
|
|
2452
|
+
deliveryMode: typeof response.delivery_mode === 'string' ? response.delivery_mode : undefined,
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
receiptLogFields(result) {
|
|
2456
|
+
const receipt = this.sendReceiptFromResult(result);
|
|
2457
|
+
return `aun_status=${receipt.status ?? 'unknown'} seq=${receipt.seq ?? 'unknown'} delivery_mode=${receipt.deliveryMode ?? 'unknown'} gateway_timestamp=${receipt.timestamp ?? 'unknown'}`;
|
|
2458
|
+
}
|
|
2459
|
+
logAunSendAccepted(method, target, messageId, encrypt, result, text) {
|
|
2460
|
+
const preview = text === undefined ? '' : ` text=${text.slice(0, 60)}`;
|
|
2461
|
+
logger.info(`${this.logPrefix()} ${method} accepted by AUN: target=${target} mid=${messageId} encrypt=${encrypt} ${this.receiptLogFields(result)}${preview}`);
|
|
2462
|
+
}
|
|
2365
2463
|
stripUndefinedDeep(value) {
|
|
2366
2464
|
if (Array.isArray(value)) {
|
|
2367
2465
|
return value
|
|
@@ -2554,7 +2652,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2554
2652
|
return { ok: false };
|
|
2555
2653
|
}
|
|
2556
2654
|
}
|
|
2557
|
-
recordDurableOutbound(channelId, payload, messageId, encrypt, context, isGroup, contentKind, logText, result) {
|
|
2655
|
+
recordDurableOutbound(channelId, payload, messageId, encrypt, context, isGroup, contentKind, logText, result, statsContext) {
|
|
2558
2656
|
const causation = normalizeCausation(context?.metadata?.causation);
|
|
2559
2657
|
if (!isGroup && causation)
|
|
2560
2658
|
registerAunCausation(messageId, this.config.aid, channelId, causation);
|
|
@@ -2576,10 +2674,11 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2576
2674
|
len: Buffer.byteLength(logText, 'utf-8'),
|
|
2577
2675
|
...(isGroup && { groupId: channelId }),
|
|
2578
2676
|
});
|
|
2579
|
-
this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, this.payloadByteLength(payload, logText), logText, false, encrypt, context?.metadata?.chatmode);
|
|
2677
|
+
this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, this.payloadByteLength(payload, logText), logText, false, encrypt, context?.metadata?.chatmode, 'send', statsContext);
|
|
2580
2678
|
const source = context?.metadata?.source ?? 'daemon';
|
|
2581
2679
|
this.appendOutboundJsonl(channelId, {
|
|
2582
2680
|
...classifyAunPayloadForLog(payload), msgId: messageId, encrypt, context, isGroup, source,
|
|
2681
|
+
transport: this.sendReceiptFromResult(result),
|
|
2583
2682
|
});
|
|
2584
2683
|
this.forwardOutbound(result);
|
|
2585
2684
|
}
|
|
@@ -2817,7 +2916,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2817
2916
|
if (!messageId) {
|
|
2818
2917
|
return { ok: false, error: `message.send returned no message_id: ${JSON.stringify(result)}`, code: 'MISSING_MESSAGE_ID' };
|
|
2819
2918
|
}
|
|
2820
|
-
|
|
2919
|
+
this.logAunSendAccepted('message.send', this.peerLabel(args.to), messageId, encrypt, result);
|
|
2821
2920
|
if (outboundCausation) {
|
|
2822
2921
|
registerAunCausation(messageId, this.config.aid, args.to, outboundCausation);
|
|
2823
2922
|
recordCausationSpan(outboundCausation, 'message.outbound', {
|
|
@@ -2846,6 +2945,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2846
2945
|
payloadSummary: classified.payloadSummary ?? args.log.payloadSummary,
|
|
2847
2946
|
source: args.log.source ?? 'msg',
|
|
2848
2947
|
handoff_trace: args.log.handoffTrace,
|
|
2948
|
+
transport: this.sendReceiptFromResult(result),
|
|
2849
2949
|
}));
|
|
2850
2950
|
this.aidStatsCollector?.recordOutbound(this.config.aid, args.to, Buffer.byteLength(classified.content || args.log.content, 'utf-8'), classified.content || args.log.content, false, encrypt, chatmode, 'send');
|
|
2851
2951
|
logWritten = true;
|
|
@@ -2853,10 +2953,10 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2853
2953
|
return {
|
|
2854
2954
|
ok: true,
|
|
2855
2955
|
message_id: messageId,
|
|
2856
|
-
seq: result
|
|
2857
|
-
timestamp: result
|
|
2858
|
-
status: result
|
|
2859
|
-
delivery_mode: result
|
|
2956
|
+
seq: this.sendReceiptFromResult(result).seq,
|
|
2957
|
+
timestamp: this.sendReceiptFromResult(result).timestamp,
|
|
2958
|
+
status: this.sendReceiptFromResult(result).status,
|
|
2959
|
+
delivery_mode: this.sendReceiptFromResult(result).deliveryMode,
|
|
2860
2960
|
encrypt,
|
|
2861
2961
|
chatmode,
|
|
2862
2962
|
log_written: logWritten,
|
|
@@ -2920,7 +3020,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2920
3020
|
if (!messageId) {
|
|
2921
3021
|
return { ok: false, error: `group.send returned no message_id: ${JSON.stringify(result)}`, code: 'MISSING_MESSAGE_ID' };
|
|
2922
3022
|
}
|
|
2923
|
-
|
|
3023
|
+
this.logAunSendAccepted('group.send', args.groupId, messageId, encrypt, result);
|
|
2924
3024
|
this.forwardOutbound(result);
|
|
2925
3025
|
let logWritten = false;
|
|
2926
3026
|
if (args.log?.content !== undefined) {
|
|
@@ -2944,8 +3044,10 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2944
3044
|
payloadSummary: classified.payloadSummary ?? args.log.payloadSummary,
|
|
2945
3045
|
source: args.log.source ?? 'msg',
|
|
2946
3046
|
handoff_trace: args.log.handoffTrace,
|
|
3047
|
+
transport: this.sendReceiptFromResult(result),
|
|
2947
3048
|
}));
|
|
2948
|
-
this.
|
|
3049
|
+
const statsContext = await this.groupStatsContext(args.groupId);
|
|
3050
|
+
this.aidStatsCollector?.recordOutbound(this.config.aid, args.groupId, Buffer.byteLength(content, 'utf-8'), content, false, encrypt, typeof finalPayload.chatmode === 'string' ? finalPayload.chatmode : undefined, 'send', statsContext);
|
|
2949
3051
|
logWritten = true;
|
|
2950
3052
|
}
|
|
2951
3053
|
return {
|
|
@@ -2954,6 +3056,10 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
2954
3056
|
group_id: result?.group_id ?? args.groupId,
|
|
2955
3057
|
message: result?.message,
|
|
2956
3058
|
event: result?.event,
|
|
3059
|
+
seq: this.sendReceiptFromResult(result).seq,
|
|
3060
|
+
timestamp: this.sendReceiptFromResult(result).timestamp,
|
|
3061
|
+
status: this.sendReceiptFromResult(result).status,
|
|
3062
|
+
delivery_mode: this.sendReceiptFromResult(result).deliveryMode,
|
|
2957
3063
|
encrypt,
|
|
2958
3064
|
log_written: logWritten,
|
|
2959
3065
|
};
|
|
@@ -3057,11 +3163,13 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3057
3163
|
return false;
|
|
3058
3164
|
}
|
|
3059
3165
|
else {
|
|
3060
|
-
|
|
3166
|
+
this.logAunSendAccepted('group.send', channelId, mid, encrypt, result, finalText);
|
|
3061
3167
|
appendAidEvent({ ts: Date.now(), iso: new Date().toISOString(), event: 'message_out', aid: this.config.aid, to: channelId, msgId: mid, kind: 'text', len: finalText.length, groupId: channelId });
|
|
3062
|
-
this.
|
|
3168
|
+
const statsContext = await this.groupStatsContext(channelId);
|
|
3169
|
+
this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, Buffer.byteLength(finalText, 'utf-8'), finalText, false, encrypt, context?.metadata?.chatmode, 'send', statsContext);
|
|
3063
3170
|
this.appendOutboundJsonl(channelId, {
|
|
3064
3171
|
...classifyAunPayloadForLog(payload), msgId: mid, encrypt, context, isGroup: true, source,
|
|
3172
|
+
transport: this.sendReceiptFromResult(result),
|
|
3065
3173
|
});
|
|
3066
3174
|
// Observer forward: outbound (group) — 原样转发 SDK SendResult(含 envelope + payload)
|
|
3067
3175
|
this.forwardOutbound(result);
|
|
@@ -3075,7 +3183,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3075
3183
|
return false;
|
|
3076
3184
|
}
|
|
3077
3185
|
else {
|
|
3078
|
-
|
|
3186
|
+
this.logAunSendAccepted('message.send', this.peerLabel(targetAid), result.message_id, encrypt, result, finalText);
|
|
3079
3187
|
const causation = normalizeCausation(context?.metadata?.causation);
|
|
3080
3188
|
if (causation) {
|
|
3081
3189
|
registerAunCausation(result.message_id, this.config.aid, targetAid, causation);
|
|
@@ -3088,6 +3196,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3088
3196
|
this.aidStatsCollector?.recordOutbound(this.config.aid, targetAid, Buffer.byteLength(finalText, 'utf-8'), finalText, false, encrypt, context?.metadata?.chatmode, 'send');
|
|
3089
3197
|
this.appendOutboundJsonl(targetAid, {
|
|
3090
3198
|
...classifyAunPayloadForLog(payload), msgId: result.message_id, encrypt, context, isGroup: false, source,
|
|
3199
|
+
transport: this.sendReceiptFromResult(result),
|
|
3091
3200
|
});
|
|
3092
3201
|
// Observer forward: outbound (private) — 原样转发 SDK SendResult(含 envelope + payload)
|
|
3093
3202
|
this.forwardOutbound(result);
|
|
@@ -3111,9 +3220,11 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3111
3220
|
return false;
|
|
3112
3221
|
}
|
|
3113
3222
|
appendAidEvent({ ts: Date.now(), iso: new Date().toISOString(), event: 'message_out', aid: this.config.aid, to: channelId, msgId: mid, kind: 'text', len: finalText.length, groupId: channelId });
|
|
3114
|
-
this.
|
|
3223
|
+
const statsContext = await this.groupStatsContext(channelId);
|
|
3224
|
+
this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, Buffer.byteLength(finalText, 'utf-8'), finalText, false, false, context?.metadata?.chatmode, 'send', statsContext);
|
|
3115
3225
|
this.appendOutboundJsonl(channelId, {
|
|
3116
3226
|
...classifyAunPayloadForLog(payload), msgId: mid, encrypt: false, context, isGroup: true, source,
|
|
3227
|
+
transport: this.sendReceiptFromResult(result),
|
|
3117
3228
|
});
|
|
3118
3229
|
this.forwardOutbound(result);
|
|
3119
3230
|
}
|
|
@@ -3138,6 +3249,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3138
3249
|
this.aidStatsCollector?.recordOutbound(this.config.aid, targetAid, Buffer.byteLength(finalText, 'utf-8'), finalText, false, false, context?.metadata?.chatmode);
|
|
3139
3250
|
this.appendOutboundJsonl(targetAid, {
|
|
3140
3251
|
...classifyAunPayloadForLog(payload), msgId: mid, encrypt: false, context, isGroup: false, source,
|
|
3252
|
+
transport: this.sendReceiptFromResult(result),
|
|
3141
3253
|
});
|
|
3142
3254
|
this.forwardOutbound(result);
|
|
3143
3255
|
}
|
|
@@ -3178,15 +3290,16 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3178
3290
|
if (!sent.ok || !sent.messageId)
|
|
3179
3291
|
return sent;
|
|
3180
3292
|
const isGroup = this.isGroupId(channelId);
|
|
3181
|
-
|
|
3182
|
-
this.
|
|
3293
|
+
const statsContext = isGroup ? await this.groupStatsContext(channelId) : undefined;
|
|
3294
|
+
this.logAunSendAccepted(isGroup ? 'group.send' : 'message.send', isGroup ? channelId : this.peerLabel(channelId), sent.messageId, !!sent.encrypt, sent.result, logText);
|
|
3295
|
+
this.recordDurableOutbound(channelId, payload, sent.messageId, !!sent.encrypt, context, isGroup, contentKind, logText, sent.result, statsContext);
|
|
3183
3296
|
this.runPostSend(entry, sent.messageId);
|
|
3184
3297
|
return sent;
|
|
3185
3298
|
}
|
|
3186
3299
|
/** 有效会话正文写入 messages.jsonl(message.send/group.send 成功后调用)。 */
|
|
3187
3300
|
appendOutboundJsonl(channelId, descriptor) {
|
|
3188
3301
|
try {
|
|
3189
|
-
const { content, msgType, payloadType, payloadSummary, msgId, encrypt, context, isGroup, source = 'daemon' } = descriptor;
|
|
3302
|
+
const { content, msgType, payloadType, payloadSummary, msgId, encrypt, transport, context, isGroup, source = 'daemon' } = descriptor;
|
|
3190
3303
|
const sessionsDir = resolvePaths().sessionsDir;
|
|
3191
3304
|
const selfAID = this.config.aid;
|
|
3192
3305
|
const chatDir = chatDirPath(sessionsDir, 'aun', channelId, selfAID);
|
|
@@ -3206,6 +3319,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3206
3319
|
durationMs: null,
|
|
3207
3320
|
encrypt,
|
|
3208
3321
|
chatmode,
|
|
3322
|
+
transport,
|
|
3209
3323
|
msgType,
|
|
3210
3324
|
payloadType,
|
|
3211
3325
|
payloadSummary,
|
|
@@ -3278,7 +3392,8 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3278
3392
|
this.eventBus?.publish?.({ type: 'message:thought-put', agentName: this.config.aid, channelId, taskId, text: thoughtText });
|
|
3279
3393
|
this.forwardOutbound(putRes);
|
|
3280
3394
|
if (thoughtText) {
|
|
3281
|
-
this.
|
|
3395
|
+
const statsContext = await this.groupStatsContext(channelId);
|
|
3396
|
+
this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, Buffer.byteLength(thoughtText, 'utf-8'), thoughtText, false, encrypt, context?.metadata?.chatmode ?? 'proactive', 'thought', statsContext);
|
|
3282
3397
|
}
|
|
3283
3398
|
}
|
|
3284
3399
|
else {
|
|
@@ -3313,21 +3428,36 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3313
3428
|
finalPayload.thread_id = context.threadId;
|
|
3314
3429
|
if (context?.replyToMessageId && !finalPayload.ref_message_id)
|
|
3315
3430
|
finalPayload.ref_message_id = context.replyToMessageId;
|
|
3431
|
+
const isMenuPayload = typeof finalPayload.type === 'string' && finalPayload.type.startsWith('menu.');
|
|
3316
3432
|
const params = { payload: finalPayload, encrypt };
|
|
3433
|
+
const rawHeaders = context?.metadata?.protectedHeaders;
|
|
3434
|
+
if (encrypt && rawHeaders && typeof rawHeaders === 'object' && !Array.isArray(rawHeaders)) {
|
|
3435
|
+
const protectedHeaders = {};
|
|
3436
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
3437
|
+
const name = key.trim().toLowerCase();
|
|
3438
|
+
if (!/^[a-z0-9_-]+$/.test(name) || typeof value !== 'string')
|
|
3439
|
+
continue;
|
|
3440
|
+
protectedHeaders[name] = value;
|
|
3441
|
+
}
|
|
3442
|
+
if (Object.keys(protectedHeaders).length > 0)
|
|
3443
|
+
params.protected_headers = protectedHeaders;
|
|
3444
|
+
}
|
|
3317
3445
|
try {
|
|
3318
3446
|
if (isGroup) {
|
|
3319
3447
|
params.group_id = channelId;
|
|
3320
3448
|
const result = await this.callAndTrace('group.send', params);
|
|
3321
3449
|
const mid = result?.message?.message_id ?? result?.message_id ?? null;
|
|
3322
3450
|
logger.info(`${this.logPrefix()} group.send (${payload.type}) ok: group=${channelId} mid=${mid} encrypt=${encrypt}`);
|
|
3323
|
-
|
|
3451
|
+
if (!isMenuPayload)
|
|
3452
|
+
this.forwardOutbound(result);
|
|
3324
3453
|
return mid;
|
|
3325
3454
|
}
|
|
3326
3455
|
else {
|
|
3327
3456
|
params.to = targetAid;
|
|
3328
3457
|
const result = await this.callAndTrace('message.send', params);
|
|
3329
3458
|
logger.info(`${this.logPrefix()} message.send (${payload.type}) ok: to=${this.peerLabel(targetAid)} mid=${result?.message_id} encrypt=${encrypt}`);
|
|
3330
|
-
|
|
3459
|
+
if (!isMenuPayload)
|
|
3460
|
+
this.forwardOutbound(result);
|
|
3331
3461
|
return result?.message_id ?? null;
|
|
3332
3462
|
}
|
|
3333
3463
|
}
|
|
@@ -3510,7 +3640,8 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3510
3640
|
if (sentMid) {
|
|
3511
3641
|
const fileText = filePayload.text;
|
|
3512
3642
|
appendAidEvent({ ts: Date.now(), iso: new Date().toISOString(), event: 'message_out', aid: this.config.aid, to: channelId, msgId: sentMid, kind: 'file', len: fileText.length, ...(isGroup && { groupId: channelId }) });
|
|
3513
|
-
|
|
3643
|
+
const statsContext = isGroup ? await this.groupStatsContext(channelId) : undefined;
|
|
3644
|
+
this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, Buffer.byteLength(fileText, 'utf-8'), fileText, false, !!params.encrypt, context?.metadata?.chatmode, 'send', statsContext);
|
|
3514
3645
|
const source = context?.metadata?.source ?? 'daemon';
|
|
3515
3646
|
this.appendOutboundJsonl(channelId, {
|
|
3516
3647
|
...classifyAunPayloadForLog(filePayload), msgId: sentMid, encrypt: !!params.encrypt,
|
|
@@ -3635,9 +3766,13 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3635
3766
|
const initiator = statusPayload.initiator ?? '';
|
|
3636
3767
|
const refMsgId = statusPayload.ref_message_id ?? '';
|
|
3637
3768
|
const metaStr = statusPayload.metadata ? ` meta=${JSON.stringify(statusPayload.metadata)}` : '';
|
|
3638
|
-
|
|
3769
|
+
const logTaskStatus = `${this.logPrefix()} task.${status} task=${taskId} session=${sessionId} chatmode=${chatmode} target=${targetLabel} initiator=${initiator} ref_msg=${refMsgId}${metaStr}`;
|
|
3770
|
+
if (status === 'error' || status === 'timeout')
|
|
3771
|
+
logger.error(logTaskStatus);
|
|
3772
|
+
else
|
|
3773
|
+
logger.info(logTaskStatus);
|
|
3639
3774
|
}
|
|
3640
|
-
sendCustomPayload(channelId, payload) {
|
|
3775
|
+
async sendCustomPayload(channelId, payload, context) {
|
|
3641
3776
|
if (!this.client || !this.connected)
|
|
3642
3777
|
return;
|
|
3643
3778
|
// SDK 0.3.0 E2EE requires payload to be an object
|
|
@@ -3650,19 +3785,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3650
3785
|
catch {
|
|
3651
3786
|
payloadObj = { text: payload };
|
|
3652
3787
|
}
|
|
3653
|
-
|
|
3654
|
-
const customTargetAid = channelId;
|
|
3655
|
-
const sendParams = {
|
|
3656
|
-
to: customTargetAid, payload: payloadObj,
|
|
3657
|
-
encrypt: true,
|
|
3658
|
-
};
|
|
3659
|
-
this.trace('OUT', 'message.send.custom', sendParams);
|
|
3660
|
-
this.client.call('message.send', sendParams).then((result) => {
|
|
3661
|
-
this.trace('OUT', 'message.send.custom.ok', { message_id: result?.message_id });
|
|
3662
|
-
}).catch(e => {
|
|
3663
|
-
this.trace('OUT', 'message.send.custom.error', { error: String(e) });
|
|
3664
|
-
logger.warn(`${this.logPrefix()} Custom payload failed: ${e}`);
|
|
3665
|
-
});
|
|
3788
|
+
await this.sendStructured(channelId, payloadObj, context);
|
|
3666
3789
|
}
|
|
3667
3790
|
async disconnect() {
|
|
3668
3791
|
this.intentionalDisconnect = true;
|
|
@@ -3738,44 +3861,18 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3738
3861
|
}
|
|
3739
3862
|
/** 读取本地 agent.md 中的 name(用于身份上下文展示),若本地不存在则尝试远程拉取 */
|
|
3740
3863
|
loadSelfName(aid) {
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
this.fetchAndCacheSelfName(aidName);
|
|
3747
|
-
return undefined;
|
|
3748
|
-
}
|
|
3749
|
-
const content = fs.readFileSync(mdPath, 'utf-8');
|
|
3750
|
-
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
3751
|
-
if (!fmMatch)
|
|
3752
|
-
return undefined;
|
|
3753
|
-
const nameMatch = fmMatch[1].match(/^name:\s*["']?(.+?)["']?\s*$/m);
|
|
3754
|
-
return nameMatch?.[1]?.trim() || undefined;
|
|
3755
|
-
}
|
|
3756
|
-
catch {
|
|
3757
|
-
return undefined;
|
|
3758
|
-
}
|
|
3864
|
+
const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
|
|
3865
|
+
const name = resolveAgentDisplayName(aidName);
|
|
3866
|
+
if (!name)
|
|
3867
|
+
void this.fetchAndCacheSelfName(aidName);
|
|
3868
|
+
return name;
|
|
3759
3869
|
}
|
|
3760
3870
|
async fetchAndCacheSelfName(aidName) {
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
if (
|
|
3765
|
-
|
|
3766
|
-
if (fmMatch) {
|
|
3767
|
-
const nameMatch = fmMatch[1].match(/^name:\s*["']?(.+?)["']?\s*$/m);
|
|
3768
|
-
const name = nameMatch?.[1]?.trim();
|
|
3769
|
-
if (name) {
|
|
3770
|
-
this._selfName = name;
|
|
3771
|
-
if (this.aidStatsCollector)
|
|
3772
|
-
this.aidStatsCollector.setSelfName(this.config.aid, name);
|
|
3773
|
-
}
|
|
3774
|
-
}
|
|
3775
|
-
}
|
|
3776
|
-
}
|
|
3777
|
-
catch {
|
|
3778
|
-
// ignore — name will remain undefined
|
|
3871
|
+
const name = await refreshAgentDisplayName(aidName);
|
|
3872
|
+
if (name) {
|
|
3873
|
+
this._selfName = name;
|
|
3874
|
+
if (this.aidStatsCollector)
|
|
3875
|
+
this.aidStatsCollector.setSelfName(this.config.aid, name);
|
|
3779
3876
|
}
|
|
3780
3877
|
}
|
|
3781
3878
|
getSelfName() {
|
|
@@ -3826,7 +3923,7 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3826
3923
|
return result.content ?? '';
|
|
3827
3924
|
}
|
|
3828
3925
|
/**
|
|
3829
|
-
* 取群显示名(group.
|
|
3926
|
+
* 取群显示名(group.get_info → group.name),进程内缓存。
|
|
3830
3927
|
* 走长连接 callAndTrace,失败/未连接返回 undefined —— 绝不抛出阻塞消息处理。
|
|
3831
3928
|
*/
|
|
3832
3929
|
async getGroupName(groupId) {
|
|
@@ -3838,9 +3935,10 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3838
3935
|
if (!this.client)
|
|
3839
3936
|
return undefined;
|
|
3840
3937
|
try {
|
|
3841
|
-
const result = await this.callAndTrace('group.
|
|
3842
|
-
const
|
|
3843
|
-
|
|
3938
|
+
const result = await this.callAndTrace('group.get_info', { group_id: groupId });
|
|
3939
|
+
const group = result?.group ?? result;
|
|
3940
|
+
const name = typeof group?.name === 'string' ? group.name.trim() : '';
|
|
3941
|
+
if (name) {
|
|
3844
3942
|
this.groupNameCache.set(groupId, name);
|
|
3845
3943
|
return name;
|
|
3846
3944
|
}
|
|
@@ -3851,6 +3949,13 @@ EvolCore AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
|
|
|
3851
3949
|
return undefined; // 不写缓存,下次仍可重试
|
|
3852
3950
|
}
|
|
3853
3951
|
}
|
|
3952
|
+
/** 统计关系键继续使用群 ID,仅为智能体预览补充可读群名。 */
|
|
3953
|
+
async groupStatsContext(groupId) {
|
|
3954
|
+
return {
|
|
3955
|
+
channelType: 'aun',
|
|
3956
|
+
peerLabel: await this.getGroupName(groupId) ?? groupId,
|
|
3957
|
+
};
|
|
3958
|
+
}
|
|
3854
3959
|
/**
|
|
3855
3960
|
* 查询某成员在群里的角色(经 group.get_admins)。
|
|
3856
3961
|
* 仅用于话题创建权限校验(稀有事件),故不缓存:每次查权威源,结果天然最新。
|
|
@@ -4015,6 +4120,7 @@ export class AUNChannelPlugin {
|
|
|
4015
4120
|
return;
|
|
4016
4121
|
case 'interaction': {
|
|
4017
4122
|
const req = payload.interaction;
|
|
4123
|
+
const cardTtlMs = 20 * 60 * 1000;
|
|
4018
4124
|
if (req.kind.kind === 'action') {
|
|
4019
4125
|
const action = req.kind;
|
|
4020
4126
|
const aunCard = {
|
|
@@ -4024,10 +4130,12 @@ export class AUNChannelPlugin {
|
|
|
4024
4130
|
label: btn.label, value: btn.key, style: btn.style ?? 'default', behavior: 'reply',
|
|
4025
4131
|
})),
|
|
4026
4132
|
};
|
|
4027
|
-
// AUN action_card
|
|
4028
|
-
//
|
|
4133
|
+
// AUN action_card 无法内嵌输入框(只有按钮),手动输入用协议原生的 compose
|
|
4134
|
+
// behavior:客户端收到点击后自行收卡+聚焦输入框,用户在 timeout 内的下一条
|
|
4135
|
+
// 消息由客户端结构化回传为 action_card_reply{behavior:"compose"}(见
|
|
4136
|
+
// extractTextPayload 的 compose 分支),不依赖 interceptNextMessage 拦截消息队列。
|
|
4029
4137
|
if (action.allowCustomInput) {
|
|
4030
|
-
aunCard.actions.push({ label: '✏️ 手动输入',
|
|
4138
|
+
aunCard.actions.push({ label: '✏️ 手动输入', style: 'default', behavior: 'compose', compose_timeout_ms: cardTtlMs });
|
|
4031
4139
|
}
|
|
4032
4140
|
if (action.body) {
|
|
4033
4141
|
aunCard.text = action.body;
|
|
@@ -4046,7 +4154,7 @@ export class AUNChannelPlugin {
|
|
|
4046
4154
|
requestId: req.id,
|
|
4047
4155
|
isCommandCard: false,
|
|
4048
4156
|
initiatorAid: req.initiatorId,
|
|
4049
|
-
expiresAt: Date.now() +
|
|
4157
|
+
expiresAt: Date.now() + cardTtlMs,
|
|
4050
4158
|
},
|
|
4051
4159
|
});
|
|
4052
4160
|
}
|
|
@@ -4081,7 +4189,7 @@ export class AUNChannelPlugin {
|
|
|
4081
4189
|
requestId: req.id,
|
|
4082
4190
|
isCommandCard: true,
|
|
4083
4191
|
initiatorAid: req.initiatorId,
|
|
4084
|
-
expiresAt: Date.now() +
|
|
4192
|
+
expiresAt: Date.now() + cardTtlMs,
|
|
4085
4193
|
},
|
|
4086
4194
|
});
|
|
4087
4195
|
}
|
|
@@ -4092,7 +4200,7 @@ export class AUNChannelPlugin {
|
|
|
4092
4200
|
}
|
|
4093
4201
|
case 'custom': {
|
|
4094
4202
|
const text = typeof payload.payload === 'string' ? payload.payload : JSON.stringify(payload.payload);
|
|
4095
|
-
channel.sendCustomPayload(channelId, text);
|
|
4203
|
+
await channel.sendCustomPayload(channelId, text, replyCtx);
|
|
4096
4204
|
return;
|
|
4097
4205
|
}
|
|
4098
4206
|
default:
|
|
@@ -4124,7 +4232,7 @@ export class AUNChannelPlugin {
|
|
|
4124
4232
|
return {
|
|
4125
4233
|
channelType: 'aun', adapter, channel,
|
|
4126
4234
|
policy,
|
|
4127
|
-
options: { flushDelay: inst.flushDelay ?? DEFAULT_FLUSH_DELAY_SECONDS, fileMarkerPattern:
|
|
4235
|
+
options: { flushDelay: inst.flushDelay ?? DEFAULT_FLUSH_DELAY_SECONDS, fileMarkerPattern: createSendFileMarkerPattern() },
|
|
4128
4236
|
connect: () => channel.connect(),
|
|
4129
4237
|
disconnect: () => channel.disconnect(),
|
|
4130
4238
|
onProjectPathRequest: () => Promise.resolve(ctx.defaultProjectPath),
|