evolcore 0.0.35 → 0.0.36
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 +31 -0
- package/README.md +6 -0
- package/bin/install-codex-managed-hooks.mjs +20 -3
- package/dist/agents/baseagent-availability.js +1 -1
- package/dist/agents/baseagent.js +88 -0
- package/dist/agents/claude-runner.js +59 -12
- package/dist/agents/codex/app-server-client.js +18 -1
- package/dist/agents/codex-runner.js +262 -21
- package/dist/agents/ecagent-runner.js +47 -11
- package/dist/agents/gemini-runner.js +36 -12
- package/dist/aun/aid/client.js +4 -3
- package/dist/aun/aid/control-aid.js +270 -8
- package/dist/aun/aid/identity.js +6 -3
- package/dist/aun/aid/ownership-operation.js +58 -0
- package/dist/aun/aid/store.js +1 -0
- package/dist/aun/group/fs-download.js +67 -2
- package/dist/aun/group/history.js +20 -5
- package/dist/aun/group/operations.js +8 -2
- package/dist/aun/menu/client.js +39 -2
- package/dist/aun/menu/transport.js +1 -1
- package/dist/aun/msg/history.js +43 -4
- package/dist/aun/msg/managed-operation.js +30 -53
- package/dist/aun/msg/p2p.js +51 -15
- package/dist/aun/outbox.js +4 -0
- package/dist/channels/aun.js +260 -73
- package/dist/channels/daemon.js +6 -4
- package/dist/channels/feishu.js +3 -0
- package/dist/cli/agent-command.js +161 -223
- package/dist/cli/agent-create.js +190 -223
- package/dist/cli/agent.js +3 -1
- package/dist/cli/aid-bind-command.js +53 -0
- package/dist/cli/aun-commands.js +384 -91
- package/dist/cli/bench.js +1 -1
- package/dist/cli/cli-argv.js +74 -0
- package/dist/cli/daemon-commands.js +105 -74
- package/dist/cli/diag.js +1 -1
- package/dist/cli/fs-command.js +18 -19
- package/dist/cli/getmeta-command.js +159 -6
- package/dist/cli/index.js +6 -12
- package/dist/cli/init-channel.js +8 -55
- package/dist/cli/init.js +125 -218
- package/dist/cli/queue-command.js +11 -8
- package/dist/cli/restart-monitor.js +134 -48
- package/dist/cli/session-command.js +1 -1
- package/dist/cli/stats.js +7 -5
- package/dist/cli/thread-command.js +103 -30
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg-diag.js +1 -0
- package/dist/config/access-policy.js +2 -2
- package/dist/config/boot-log.js +14 -11
- package/dist/config/config-field-policy.js +9 -1
- package/dist/config/config-manager.js +176 -90
- package/dist/config/config-operation-service.js +7 -14
- package/dist/config/contact-book-store.js +142 -26
- package/dist/config/contact-book.js +4 -2
- package/dist/config/lifecycle.js +1 -1
- package/dist/config/owner-config.js +96 -0
- package/dist/config/peer-role-resolver.js +12 -18
- package/dist/config/providers-config.js +382 -0
- package/dist/config/providers-import.js +74 -0
- package/dist/config/providers-management.js +328 -0
- package/dist/config/providers.js +22 -0
- package/dist/config/resolved-config-op.js +17 -14
- package/dist/config/snapshot.js +28 -16
- package/dist/config-store.js +18 -14
- package/dist/core/agent-ipc-registration.js +21 -63
- package/dist/core/agent-runtime.js +16 -5
- package/dist/core/auth/agent-delegation.js +199 -25
- package/dist/core/auth/auth-gateway.js +44 -2
- package/dist/core/auth/authorization-denial.js +1 -1
- package/dist/core/auth/debug-context.js +2 -1
- package/dist/core/auth/delegation-authorization.js +2 -2
- package/dist/core/auth/delegation-diagnostics.js +4 -1
- package/dist/core/auth/operation-authorizer.js +10 -1
- package/dist/core/auth/operation-catalog.js +19 -1
- package/dist/core/bootstrap-service.js +43 -20
- package/dist/core/channel-loader.js +0 -1
- package/dist/core/channel-runtime.js +30 -4
- package/dist/core/command/agent-control.js +196 -37
- package/dist/core/command/cli-intent-parser.js +60 -4
- package/dist/core/command/command-handler.js +212 -60
- package/dist/core/command/connect-menu.js +1 -0
- package/dist/core/command/diag-service.js +5 -4
- package/dist/core/command/file-search.js +368 -0
- package/dist/core/command/menu-catalog.js +2 -1
- package/dist/core/command/menu-handler.js +292 -107
- package/dist/core/command/menu-protocol.js +23 -0
- package/dist/core/command/role-menu.js +7 -19
- package/dist/core/command/skill-slash.js +130 -25
- package/dist/core/command/slash-handler.js +101 -53
- package/dist/core/command/topic-slash.js +16 -5
- package/dist/core/daemon-bootstrap.js +7 -1
- package/dist/core/daemon-control.js +19 -1
- package/dist/core/daemon-lifecycle.js +2 -1
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/evolagent-registry.js +13 -2
- package/dist/core/evolagent.js +3 -6
- package/dist/core/handoff/ipc-registration.js +2 -2
- package/dist/core/handoff/runtime.js +40 -6
- package/dist/core/handoff/store.js +1 -0
- package/dist/core/inference/text-inference.js +95 -6
- package/dist/core/ipc-registration.js +43 -24
- package/dist/core/memory/consolidation-context.js +4 -1
- package/dist/core/memory/consolidation.js +25 -14
- package/dist/core/message/aun-ipc-sender.js +296 -88
- package/dist/core/message/aun-request-registry.js +226 -0
- package/dist/core/message/create-status.js +33 -4
- package/dist/core/message/message-bridge.js +62 -13
- package/dist/core/message/message-log.js +0 -1
- package/dist/core/message/message-queue.js +4 -1
- package/dist/core/message/message-utils.js +8 -1
- package/dist/core/message/peer-mode.js +17 -9
- package/dist/core/message/response-engine.js +266 -124
- package/dist/core/message/retry-scheduler.js +11 -8
- package/dist/core/model/catalog-service.js +60 -18
- package/dist/core/model/model-catalog.js +112 -9
- package/dist/core/model/model-fallback.js +94 -26
- package/dist/core/ownership/owner-resolver.js +687 -0
- package/dist/core/permission/approval-gateway.js +196 -62
- package/dist/core/permission/ec-command-parser.js +138 -25
- package/dist/core/permission/filesystem-approval-scope.js +141 -0
- package/dist/core/permission/protected-paths.js +14 -1
- package/dist/core/permission/sandbox-runtime.js +171 -18
- package/dist/core/permission/tool-policy.js +13 -2
- package/dist/core/role/runtime-policy.js +3 -4
- package/dist/core/session/session-idle-monitor.js +10 -8
- package/dist/core/session/session-manager.js +72 -45
- package/dist/core/session/session-mapper.js +18 -7
- package/dist/core/session/session-renew.js +5 -4
- package/dist/core/session/session-title.js +2 -1
- package/dist/core/session/session-turn-coordinator.js +4 -2
- package/dist/core/skill/index-generator.js +2 -16
- package/dist/core/task/task-recovery-service.js +15 -2
- package/dist/core/thread/command-parser.js +28 -13
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +11 -8
- package/dist/core/thread/thread-operation.js +79 -44
- package/dist/core/thread/thread-route.js +54 -6
- package/dist/core/thread/thread-service.js +236 -45
- package/dist/core/thread/topic-id.js +248 -0
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/eck/message-renderer.js +12 -2
- package/dist/index.js +50 -27
- package/dist/ipc.js +59 -6
- package/dist/paths.js +1 -0
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +10 -3
- package/dist/stats/db.js +8 -0
- package/dist/stats/writer.js +26 -4
- package/dist/trigger/authorization.js +48 -3
- package/dist/trigger/feedback.js +28 -10
- package/dist/trigger/ipc-service.js +86 -10
- package/dist/trigger/local-files.js +55 -23
- package/dist/trigger/scheduler.js +57 -11
- package/dist/trigger/system-definitions.js +1 -0
- package/dist/trigger/validation.js +44 -15
- package/dist/utils/aid-bind.js +17 -297
- package/dist/utils/avatar-upload.js +16 -4
- package/dist/utils/cross-platform.js +109 -34
- package/dist/utils/ecweb-supervisor.js +1 -1
- package/dist/utils/ecweb-utils.js +8 -1
- package/dist/utils/error-utils.js +44 -6
- package/dist/utils/instance-registry.js +12 -2
- package/dist/utils/npm-ops.js +102 -27
- package/dist/utils/package-version-info.js +72 -27
- package/dist/utils/restart-safety.js +50 -0
- package/dist/utils/upgrade-jobs.js +304 -0
- package/dist/utils/windows-autostart.js +17 -10
- package/kits/docs/INDEX.md +5 -0
- package/kits/docs/aun/ABILITY_CALLING.md +239 -0
- package/kits/docs/evolcore/INDEX.md +14 -3
- package/kits/docs/evolcore/agent-create.md +55 -141
- package/kits/docs/evolcore/agent.md +38 -16
- package/kits/docs/evolcore/aid.md +34 -0
- package/kits/docs/evolcore/config.md +20 -1
- package/kits/docs/evolcore/event.md +2 -2
- package/kits/docs/evolcore/fs.md +18 -2
- package/kits/docs/evolcore/group-collaboration.md +13 -246
- package/kits/docs/evolcore/group.md +2 -1
- package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +65 -1
- package/kits/docs/evolcore/msg.md +64 -2
- package/kits/docs/evolcore/skill.md +86 -0
- package/kits/docs/evolcore/stats.md +6 -0
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +145 -51
- package/kits/docs/evolcore/trigger.md +48 -3
- package/kits/eck_manifest.json +21 -0
- package/kits/eck_message_manifest.json +4 -4
- package/kits/migrations/migrate-role-config-v5.mjs +20 -7
- package/kits/rules/01-overview.md +2 -1
- package/kits/rules/03-identity.md +3 -1
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/07-memory-and-thread.md +1 -1
- package/kits/schemas/_meta.json +16 -1
- package/kits/schemas/agent-config.schema.13.json +44 -2
- package/kits/schemas/contact-book.schema.3.json +5 -0
- package/kits/schemas/daemon.schema.6.json +3 -1
- package/kits/schemas/daemon.schema.8.json +329 -0
- package/kits/schemas/defaults.schema.5.json +39 -3
- package/kits/schemas/providers.schema.1.json +76 -0
- package/kits/schemas/relation-config.schema.9.json +5 -1
- package/kits/skills/.switches.json +28 -0
- package/kits/skills/aid-ability/SKILL.md +214 -0
- package/kits/skills/ec-agent-create/SKILL.md +162 -0
- package/kits/skills/ec-group/SKILL.md +2 -1
- package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
- package/kits/skills/ec-identity/SKILL.md +143 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
- package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
- package/kits/skills/ec-trigger/SKILL.md +167 -0
- package/kits/skills/ec-trigger/references/examples.md +153 -0
- package/kits/skills/thread-ops/SKILL.md +108 -420
- package/kits/templates/message-fragments/thread-context.md +1 -2
- package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
- package/kits/templates/roles/member.json +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +1 -0
- package/kits/templates/system-fragments/channel.md +2 -1
- package/kits/templates/system-fragments/commands.md +5 -4
- package/kits/templates/system-fragments/identity.md +2 -0
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -3
package/dist/channels/aun.js
CHANGED
|
@@ -3,6 +3,7 @@ import { sanitizeMenuLogValue } from '../core/command/menu-protocol.js';
|
|
|
3
3
|
import crypto from 'crypto';
|
|
4
4
|
import { performance } from 'node:perf_hooks';
|
|
5
5
|
import fs from 'fs';
|
|
6
|
+
import { ownerResolver } from '../core/ownership/owner-resolver.js';
|
|
6
7
|
import path from 'path';
|
|
7
8
|
import os from 'os';
|
|
8
9
|
import { logger, localTimestamp } from '../utils/logger.js';
|
|
@@ -10,9 +11,10 @@ import { LogWriter } from '../utils/log-writer.js';
|
|
|
10
11
|
import { middleOutputModePolicy, resolveShowActivities, showActivitiesPolicy } from '../core/channel-loader.js';
|
|
11
12
|
import { DEFAULT_FLUSH_DELAY_SECONDS } from '../types.js';
|
|
12
13
|
import { resolvePaths, agentDir as agentDirPath, resolveRoot, channelStatePath } from '../paths.js';
|
|
14
|
+
import { getAunRequestRegistry, MAX_AUN_VOLATILE_TTL_SECONDS } from '../core/message/aun-request-registry.js';
|
|
13
15
|
import { saveToUploads, saveRelationAttachment, sanitizeFileName, bufferToInboundImage, safeFetch } from '../utils/media-cache.js';
|
|
14
16
|
import { appendAidEvent } from '../utils/instance-registry.js';
|
|
15
|
-
import { appendMessageLog, appendMessageLogStrict, buildOutboundEntry, buildInboundEntry, classifyAunPayloadForLog, findMessageLogOperationMessageId, hasMessageLogOperation } from '../core/message/message-log.js';
|
|
17
|
+
import { appendMessageLog, appendMessageLogStrict, buildOutboundEntry, buildInboundEntry, classifyAunPayloadForLog, findLatestMessageLogOperation, findMessageLogOperationMessageId, hasMessageLogOperation } from '../core/message/message-log.js';
|
|
16
18
|
import { createSendFileMarkerPattern } from '../core/message/file-markers.js';
|
|
17
19
|
import { chatDirPath } from '../core/session/session-fs-store.js';
|
|
18
20
|
import { readTopicLabel, readTopicStatus } from '../core/session/session-mapper.js';
|
|
@@ -21,6 +23,7 @@ import { appendAidLifecycle } from '../aun/aid/identity.js';
|
|
|
21
23
|
import { getAidStore, loadClient, SLOT } from '../aun/aid/store.js';
|
|
22
24
|
import { MAX_AUN_ATTACHMENT_SIZE, uploadBufferAndBuildPayload, uploadFileAndBuildPayload } from '../aun/msg/upload.js';
|
|
23
25
|
import { loadAgent } from '../config-store.js';
|
|
26
|
+
import { configuredOwnerList } from '../config/owner-config.js';
|
|
24
27
|
import { resolveAgentLifecycle } from '../config/lifecycle.js';
|
|
25
28
|
import { resolveEffective } from '../config/config-manager.js';
|
|
26
29
|
import { isManagementRole } from '../config/builtin-roles.js';
|
|
@@ -42,6 +45,7 @@ import { bindPostBootstrapWelcomeOutboxSession, hasPendingPostBootstrapWelcomeOu
|
|
|
42
45
|
import { hasMentionAll, mentionEntryAids, mentionEntryTargets, } from '../core/message/mention-schema.js';
|
|
43
46
|
import { aunOutboundAttention, normalizeAunMentionEntries, parseAunAttention, withAunAttention, } from '../aun/msg/mention-schema.js';
|
|
44
47
|
import { isDeliveryTarget, sameDeliveryTarget } from '../core/message/message-utils.js';
|
|
48
|
+
import { isTopicThreadId } from '../core/thread/topic-id.js';
|
|
45
49
|
import { sentReceipt, suppressedReceipt } from '../core/message/send-receipt.js';
|
|
46
50
|
export const AUN_HANDOFF_MARKER_FIELD = '_evolcore_handoff_id';
|
|
47
51
|
const AUN_INTERACTION_CARD_TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -331,7 +335,7 @@ export function buildAunFilePayload(params) {
|
|
|
331
335
|
text: `📎 ${params.filename} (${formatSize(params.size)})`,
|
|
332
336
|
attachments: [params.attachment],
|
|
333
337
|
};
|
|
334
|
-
if (params.context?.threadId)
|
|
338
|
+
if (isTopicThreadId(params.context?.threadId))
|
|
335
339
|
payload.thread_id = params.context.threadId;
|
|
336
340
|
if (params.context?.replyToMessageId)
|
|
337
341
|
payload.ref_message_id = params.context.replyToMessageId;
|
|
@@ -469,6 +473,12 @@ export function classifyAunSendFailure(value, fallback = 'AUN send failed') {
|
|
|
469
473
|
const retryByName = /(?:timeout|connection|rate.?limit|temporar(?:y|ily)|unavailable|network|socket)/i.test(details.name ?? '');
|
|
470
474
|
const relayTargetMissing = text.includes('relay_target_not_found') || text.includes('relay target not found');
|
|
471
475
|
const rpcHandlerTimeout = numericCode === -32004 && /rpc handler timeout/i.test(text);
|
|
476
|
+
// -32013 is otherwise permanent, but "client signature cert unavailable" is
|
|
477
|
+
// the gateway failing to resolve the signer's certificate while the local
|
|
478
|
+
// cert is valid and auth succeeded. It clears server-side, so keep the
|
|
479
|
+
// durable entry and retry until TTL (same idea as the SDK's transient
|
|
480
|
+
// "certificate not loaded").
|
|
481
|
+
const signatureCertUnavailable = numericCode === -32013 && /signature cert(?:ificate)? unavailable/i.test(text);
|
|
472
482
|
const groupStateCode = numericCode === -33002 || numericCode === -33003;
|
|
473
483
|
const groupClosed = groupStateCode && /\b(?:closed|dissolved|disbanded)\b|解散|已关闭/i.test(text);
|
|
474
484
|
const groupSuspended = groupStateCode && /\bsuspend(?:ed)?\b|暂停/i.test(text);
|
|
@@ -497,7 +507,7 @@ export function classifyAunSendFailure(value, fallback = 'AUN send failed') {
|
|
|
497
507
|
const unconfirmedDelivery = missingReceipt
|
|
498
508
|
&& (acceptedDispatch || ambiguousAccepted || ambiguousNoReceipt || successfulHttpWithoutReceipt);
|
|
499
509
|
let status;
|
|
500
|
-
if ((AUN_PERMANENT_ERROR_CODES.has(numericCode ?? Number.NaN) && !rpcHandlerTimeout)
|
|
510
|
+
if ((AUN_PERMANENT_ERROR_CODES.has(numericCode ?? Number.NaN) && !rpcHandlerTimeout && !signatureCertUnavailable)
|
|
501
511
|
|| permanentByText
|
|
502
512
|
|| AUN_PERMANENT_STRING_CODES.has(stringCode)
|
|
503
513
|
|| rejectedDispatch
|
|
@@ -515,6 +525,7 @@ export function classifyAunSendFailure(value, fallback = 'AUN send failed') {
|
|
|
515
525
|
else if (AUN_RETRYABLE_ERROR_CODES.has(numericCode ?? Number.NaN)
|
|
516
526
|
|| transientHttpStatus
|
|
517
527
|
|| rpcHandlerTimeout
|
|
528
|
+
|| signatureCertUnavailable
|
|
518
529
|
|| AUN_RETRYABLE_STRING_CODES.has(stringCode)
|
|
519
530
|
|| retryByName
|
|
520
531
|
|| relayTargetMissing
|
|
@@ -680,7 +691,7 @@ export class AUNChannel {
|
|
|
680
691
|
traceWriter = null;
|
|
681
692
|
eventBus = null;
|
|
682
693
|
queuedHandler = null;
|
|
683
|
-
/** `topic
|
|
694
|
+
/** `topic.renamed` 事件消费器,由 runtime 注入(见 setThreadRenameHandler)。 */
|
|
684
695
|
threadRenameHandler = null;
|
|
685
696
|
groupInviteHandler;
|
|
686
697
|
groupInviteConnectedHandler;
|
|
@@ -1412,19 +1423,33 @@ export class AUNChannel {
|
|
|
1412
1423
|
return undefined;
|
|
1413
1424
|
}
|
|
1414
1425
|
/**
|
|
1415
|
-
* 处理对端 `topic
|
|
1426
|
+
* 处理对端 `event/topic.renamed` 通知:校验字段后交给注入的改名消费器落索引,
|
|
1416
1427
|
* 成功后发 `thread:renamed` 让前端刷新。全程不进入 Agent 消息队列。
|
|
1417
1428
|
* 通知是尽力而为的,任何失败只记日志,不影响后续消息处理。
|
|
1418
1429
|
*/
|
|
1419
1430
|
applyInboundTopicRename(params) {
|
|
1420
1431
|
const payload = params.payload && typeof params.payload === 'object' ? params.payload : null;
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1432
|
+
const data = payload?.data && typeof payload.data === 'object'
|
|
1433
|
+
? payload.data : undefined;
|
|
1434
|
+
// 未知 schema 只忽略业务应用,不能按聊天文本处理。
|
|
1435
|
+
if (data?.schema_version !== 1) {
|
|
1436
|
+
logger.debug(`${this.logPrefix()} topic.renamed ignored: schema_version=${String(data?.schema_version)}`);
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1439
|
+
if (payload?.occurred_at !== undefined && data?.name_updated_at !== undefined
|
|
1440
|
+
&& payload.occurred_at !== data.name_updated_at) {
|
|
1441
|
+
logger.debug(`${this.logPrefix()} topic.renamed ignored: occurred_at does not match name_updated_at`);
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
const threadId = typeof payload?.thread_id === 'string' ? payload.thread_id.trim() || undefined : undefined;
|
|
1445
|
+
const name = this.sanitizeTopicName(data?.name);
|
|
1446
|
+
const nameUpdatedAt = typeof data?.name_updated_at === 'number' && Number.isFinite(data.name_updated_at)
|
|
1447
|
+
? data.name_updated_at : undefined;
|
|
1425
1448
|
const applier = this.threadRenameHandler;
|
|
1449
|
+
// 主话题与话题的改名是同一套逻辑,区别只在 id 形态:主话题 id(`main:<a>:<b>`)
|
|
1450
|
+
// 由 applyRemoteRename 落到主会话上,不进话题索引。
|
|
1426
1451
|
if (!applier || !threadId || !name || nameUpdatedAt === undefined || !params.peer) {
|
|
1427
|
-
logger.debug(`${this.logPrefix()} topic
|
|
1452
|
+
logger.debug(`${this.logPrefix()} topic.renamed ignored: peer=${params.peer || '<none>'} thread=${threadId || '<none>'} hasName=${!!name}`);
|
|
1428
1453
|
return;
|
|
1429
1454
|
}
|
|
1430
1455
|
void applier({
|
|
@@ -1432,7 +1457,7 @@ export class AUNChannel {
|
|
|
1432
1457
|
threadId, name, nameUpdatedAt,
|
|
1433
1458
|
}).then(result => {
|
|
1434
1459
|
if (!result.applied) {
|
|
1435
|
-
logger.debug(`${this.logPrefix()} topic
|
|
1460
|
+
logger.debug(`${this.logPrefix()} topic.renamed not applied (${result.reason ?? 'unknown'}): thread=${threadId}`);
|
|
1436
1461
|
return;
|
|
1437
1462
|
}
|
|
1438
1463
|
this.eventBus?.publish?.({
|
|
@@ -1444,9 +1469,9 @@ export class AUNChannel {
|
|
|
1444
1469
|
nameUpdatedAt,
|
|
1445
1470
|
by: params.senderAid ?? '',
|
|
1446
1471
|
});
|
|
1447
|
-
logger.info(`${this.logPrefix()} topic
|
|
1472
|
+
logger.info(`${this.logPrefix()} topic.renamed applied: thread=${threadId} name=${JSON.stringify(name)}`);
|
|
1448
1473
|
}).catch(error => {
|
|
1449
|
-
logger.warn(`${this.logPrefix()} topic
|
|
1474
|
+
logger.warn(`${this.logPrefix()} topic.renamed failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1450
1475
|
});
|
|
1451
1476
|
}
|
|
1452
1477
|
buildGroupReplyContext(threadId, senderAid, encrypted, messageId, chatmode, topicName, groupId, label, topicStatus) {
|
|
@@ -1793,6 +1818,7 @@ export class AUNChannel {
|
|
|
1793
1818
|
this.observerGroupMemberFetches.clear();
|
|
1794
1819
|
// Clean up existing client if any
|
|
1795
1820
|
if (this.client) {
|
|
1821
|
+
ownerResolver.detachClient(this.config.aid, this.client);
|
|
1796
1822
|
this.trace('OUT', 'client.close', { reason: 'initClient' });
|
|
1797
1823
|
try {
|
|
1798
1824
|
await this.client.close();
|
|
@@ -1830,6 +1856,15 @@ export class AUNChannel {
|
|
|
1830
1856
|
this.store = store;
|
|
1831
1857
|
const client = await loadClient(store, aidName);
|
|
1832
1858
|
this.client = client;
|
|
1859
|
+
const localConfig = loadAgent(aidName);
|
|
1860
|
+
ownerResolver.setAgentConfiguredOwners(aidName, configuredOwnerList(localConfig));
|
|
1861
|
+
ownerResolver.attachClient(aidName, client);
|
|
1862
|
+
try {
|
|
1863
|
+
await this.config.onClientReady?.(client);
|
|
1864
|
+
}
|
|
1865
|
+
catch (error) {
|
|
1866
|
+
logger.warn(`${this.logPrefix()} client-ready hook failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1867
|
+
}
|
|
1833
1868
|
// A reconnect replaces the SDK client. Late events from the retired
|
|
1834
1869
|
// instance must not mutate the new instance's health or message state.
|
|
1835
1870
|
const isCurrentClient = () => this.client === client;
|
|
@@ -2099,12 +2134,13 @@ export class AUNChannel {
|
|
|
2099
2134
|
logger.warn(`${this.logPrefix()} agent config not found for ${aidName}, skipping welcome message`);
|
|
2100
2135
|
return false;
|
|
2101
2136
|
}
|
|
2102
|
-
|
|
2137
|
+
await ownerResolver.refresh(aidName);
|
|
2138
|
+
const owners = ownerResolver.owners(aidName);
|
|
2103
2139
|
if (owners.length === 0) {
|
|
2104
2140
|
logger.info(`${this.logPrefix()} No owner configured, skipping post-bootstrap welcome message`);
|
|
2105
2141
|
return false;
|
|
2106
2142
|
}
|
|
2107
|
-
this.repairBootstrapRoutes();
|
|
2143
|
+
await this.repairBootstrapRoutes();
|
|
2108
2144
|
// Bootstrap welcomes target the configured personal Owner AID. They are
|
|
2109
2145
|
// not replies to an inbound group message, so their route is always
|
|
2110
2146
|
// private and must not be inferred with group.get_info.
|
|
@@ -2156,7 +2192,7 @@ export class AUNChannel {
|
|
|
2156
2192
|
return this.reconcilePostBootstrapWelcome();
|
|
2157
2193
|
}
|
|
2158
2194
|
async hasPendingPostBootstrapWelcome() {
|
|
2159
|
-
this.repairBootstrapRoutes();
|
|
2195
|
+
await this.repairBootstrapRoutes();
|
|
2160
2196
|
const aid = this.config.aid.replace(/^@/, '');
|
|
2161
2197
|
return hasPendingPostBootstrapWelcomeOutbox(aid);
|
|
2162
2198
|
}
|
|
@@ -2189,12 +2225,13 @@ export class AUNChannel {
|
|
|
2189
2225
|
* already-active agent from manufacturing another welcome message.
|
|
2190
2226
|
*/
|
|
2191
2227
|
async reconcilePostBootstrapWelcome() {
|
|
2192
|
-
this.repairBootstrapRoutes();
|
|
2228
|
+
await this.repairBootstrapRoutes();
|
|
2193
2229
|
const configuredAid = this.config.aid;
|
|
2194
2230
|
const aid = configuredAid.startsWith('@') ? configuredAid.slice(1) : configuredAid;
|
|
2195
2231
|
const entries = findPostBootstrapWelcomeOutbox(aid);
|
|
2196
2232
|
const agentConfig = loadAgent(aid);
|
|
2197
|
-
|
|
2233
|
+
await ownerResolver.refresh(aid);
|
|
2234
|
+
const owners = ownerResolver.owners(aid);
|
|
2198
2235
|
if (entries.length === 0)
|
|
2199
2236
|
return postBootstrapWelcomeStatus(aid, owners);
|
|
2200
2237
|
if (!agentConfig || resolveAgentLifecycle(agentConfig) !== 'active') {
|
|
@@ -2499,6 +2536,18 @@ export class AUNChannel {
|
|
|
2499
2536
|
return;
|
|
2500
2537
|
const protectedHeaders = verifiedProtectedHeaders(msg, env);
|
|
2501
2538
|
const payload = msg.payload ?? '';
|
|
2539
|
+
const responseRecord = getAunRequestRegistry().resolveIncoming({
|
|
2540
|
+
selfAid: this._aid || this.config.aid,
|
|
2541
|
+
fromAid,
|
|
2542
|
+
messageId,
|
|
2543
|
+
payload,
|
|
2544
|
+
});
|
|
2545
|
+
if (responseRecord) {
|
|
2546
|
+
logger.info(`${this.logPrefix()} AUN request response matched: request_id=${responseRecord.requestId} mid=${messageId}`);
|
|
2547
|
+
const responseLog = classifyAunPayloadForLog(payload);
|
|
2548
|
+
this.appendInboundPayloadJsonl(fromAid, fromAid, messageId, !!env.encrypted, false, responseLog, undefined, receivedAt, seq);
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2502
2551
|
if (this.managedMenuClient.accept(fromAid, payload, !!env.encrypted)) {
|
|
2503
2552
|
return;
|
|
2504
2553
|
}
|
|
@@ -2577,8 +2626,8 @@ export class AUNChannel {
|
|
|
2577
2626
|
if (p2pPayloadType === 'text') {
|
|
2578
2627
|
import('../utils/msg-diag.js').then(m => m.diagLog('aun_in', { aid: this.config.aid, from: fromAid, to: this.config.aid, msgId: messageId, text })).catch(() => { });
|
|
2579
2628
|
}
|
|
2580
|
-
// topic
|
|
2581
|
-
if (p2pPayloadType === 'topic
|
|
2629
|
+
// event.topic.renamed:对端话题改名通知。只更新本端话题索引,不进 Agent 队列。
|
|
2630
|
+
if (p2pPayloadType === 'event' && payload?.event === 'topic.renamed') {
|
|
2582
2631
|
this.applyInboundTopicRename({
|
|
2583
2632
|
payload, selfAid: this.config.aid, peer: fromAid, peerKind: 'private',
|
|
2584
2633
|
senderAid: fromAid,
|
|
@@ -2657,7 +2706,7 @@ export class AUNChannel {
|
|
|
2657
2706
|
// payload 类型白名单:信号类消息(status / event / thought 等)不进 Agent
|
|
2658
2707
|
if (p2pPayloadType && !AUNChannel.PROACTIVE_ALLOW_TYPES.has(p2pPayloadType)) {
|
|
2659
2708
|
this.appendInboundPayloadJsonl(chatId, fromAid, messageId, msgEncrypted, false, logDescriptor, threadId, receivedAt, seq);
|
|
2660
|
-
logger.info(`${this.logPrefix()} P2P
|
|
2709
|
+
logger.info(`${this.logPrefix()} P2P retained in generic inbound event stream (not Agent chat): type=${p2pPayloadType} from=${shortAid}(${displayName}) mid=${messageId}`);
|
|
2661
2710
|
return;
|
|
2662
2711
|
}
|
|
2663
2712
|
const msgChatmode = (payload && typeof payload === 'object') ? payload.chatmode : undefined;
|
|
@@ -2711,13 +2760,23 @@ export class AUNChannel {
|
|
|
2711
2760
|
const groupId = env.group_id ?? '';
|
|
2712
2761
|
const senderAid = env.from ?? '';
|
|
2713
2762
|
const payload = msg.payload ?? '';
|
|
2763
|
+
const responseRecord = getAunRequestRegistry().resolveIncoming({
|
|
2764
|
+
selfAid: this._aid || this.config.aid,
|
|
2765
|
+
fromAid: senderAid,
|
|
2766
|
+
messageId,
|
|
2767
|
+
payload,
|
|
2768
|
+
});
|
|
2769
|
+
if (responseRecord) {
|
|
2770
|
+
logger.info(`${this.logPrefix()} AUN group request response matched: request_id=${responseRecord.requestId} mid=${messageId}`);
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2714
2773
|
const inboundType = (payload && typeof payload === 'object') ? payload.type : undefined;
|
|
2715
2774
|
if (inboundType === 'observer.forward' || (typeof inboundType === 'string' && inboundType.startsWith('observer.inject'))) {
|
|
2716
2775
|
logger.debug(`${this.logPrefix()} Group dropped observer control envelope: mid=${messageId}`);
|
|
2717
2776
|
return;
|
|
2718
2777
|
}
|
|
2719
|
-
// topic
|
|
2720
|
-
if (inboundType === 'topic
|
|
2778
|
+
// event.topic.renamed:群内话题改名通知。只更新本端索引,不进 Agent 队列。
|
|
2779
|
+
if (inboundType === 'event' && payload?.event === 'topic.renamed') {
|
|
2721
2780
|
this.applyInboundTopicRename({
|
|
2722
2781
|
payload, selfAid: this.config.aid, peer: groupId || senderAid, peerKind: 'group',
|
|
2723
2782
|
senderAid,
|
|
@@ -2806,7 +2865,7 @@ export class AUNChannel {
|
|
|
2806
2865
|
}
|
|
2807
2866
|
if (payloadType && !AUNChannel.PROACTIVE_ALLOW_TYPES.has(payloadType)) {
|
|
2808
2867
|
this.appendInboundPayloadJsonl(groupId, senderAid, messageId, !!env.encrypted, true, logDescriptor, threadId, receivedAt, seq);
|
|
2809
|
-
logger.info(`${this.logPrefix()} Group
|
|
2868
|
+
logger.info(`${this.logPrefix()} Group payload retained in generic inbound event stream (not Agent chat): type=${payloadType} group=${groupId} sender=${senderAid} mid=${messageId}`);
|
|
2810
2869
|
return;
|
|
2811
2870
|
}
|
|
2812
2871
|
}
|
|
@@ -3271,7 +3330,7 @@ export class AUNChannel {
|
|
|
3271
3330
|
* result 为 SDK message.send / group.send 的 SendResult(已 attach envelope + payload)。
|
|
3272
3331
|
* 若对端本身是 owner,排除该 owner(不把"回复 A"转发给 A 自己)。
|
|
3273
3332
|
*/
|
|
3274
|
-
forwardOutbound(result) {
|
|
3333
|
+
forwardOutbound(result, outboundDebug) {
|
|
3275
3334
|
if (!this.connected || !this.client)
|
|
3276
3335
|
return Promise.resolve();
|
|
3277
3336
|
const outboundType = payloadTypeForObserver(result);
|
|
@@ -3292,13 +3351,13 @@ export class AUNChannel {
|
|
|
3292
3351
|
if (candidateOwners.length === 0)
|
|
3293
3352
|
return Promise.resolve();
|
|
3294
3353
|
if (typeof env.group_id !== 'string' || !env.group_id.trim()) {
|
|
3295
|
-
this.emitForward('outbound', result, candidateOwners);
|
|
3354
|
+
this.emitForward('outbound', result, candidateOwners, outboundDebug);
|
|
3296
3355
|
return Promise.resolve();
|
|
3297
3356
|
}
|
|
3298
3357
|
return this.observerRecipients(candidateOwners, env)
|
|
3299
3358
|
.then(recipientOwners => {
|
|
3300
3359
|
if (recipientOwners.length > 0)
|
|
3301
|
-
this.emitForward('outbound', result, recipientOwners);
|
|
3360
|
+
this.emitForward('outbound', result, recipientOwners, outboundDebug);
|
|
3302
3361
|
})
|
|
3303
3362
|
.catch(error => logger.debug(`${this.logPrefix()} observer outbound recipient lookup failed: ${error}`));
|
|
3304
3363
|
}
|
|
@@ -3306,14 +3365,17 @@ export class AUNChannel {
|
|
|
3306
3365
|
* 实际投递 observer.forward 给每个 owner,使用 daemon 的全局 AUN 默认加密设置。
|
|
3307
3366
|
* original 为 SDK 给到的原始对象(入站回调 data / 出站 SendResult),整体透传,
|
|
3308
3367
|
* 不挑字段、不改字段——SDK 加任何字段都会自动一并转发给 owner。
|
|
3368
|
+
* outboundDebug 是出站时的 payload.debug,SDK SendResult 里没有,需要从调用方透传。
|
|
3309
3369
|
*/
|
|
3310
|
-
emitForward(direction, original, owners) {
|
|
3370
|
+
emitForward(direction, original, owners, outboundDebug) {
|
|
3311
3371
|
const forwardPayload = {
|
|
3312
3372
|
type: 'observer.forward',
|
|
3313
3373
|
direction,
|
|
3314
3374
|
agent_aid: this.config.aid,
|
|
3315
3375
|
original,
|
|
3316
3376
|
};
|
|
3377
|
+
if (outboundDebug)
|
|
3378
|
+
forwardPayload.debug = outboundDebug;
|
|
3317
3379
|
const encrypt = this.shouldEncrypt('observer.forward');
|
|
3318
3380
|
for (const ownerAid of owners) {
|
|
3319
3381
|
this.callAndTrace('message.send', { to: ownerAid, payload: forwardPayload, encrypt }, { lane: 'observer' })
|
|
@@ -3341,7 +3403,7 @@ export class AUNChannel {
|
|
|
3341
3403
|
const payload = {
|
|
3342
3404
|
type: 'observer.inject.status', id: hintId,
|
|
3343
3405
|
target: { channel_id: target.channelId, chat_type: target.chatType,
|
|
3344
|
-
...(target.threadId ? { thread_id: target.threadId } : {}) },
|
|
3406
|
+
...(isTopicThreadId(target.threadId) ? { thread_id: target.threadId } : {}) },
|
|
3345
3407
|
data: {
|
|
3346
3408
|
status,
|
|
3347
3409
|
hint_id: hintId,
|
|
@@ -3371,7 +3433,7 @@ export class AUNChannel {
|
|
|
3371
3433
|
const requestAction = req.kind === 'reject' ? req.action : req.kind;
|
|
3372
3434
|
const requestHintId = req.kind === 'add' ? req.id : 'targetId' in req ? req.targetId : undefined;
|
|
3373
3435
|
const requestTarget = 'channelId' in req && req.channelId
|
|
3374
|
-
? { channel_id: req.channelId, chat_type: req.chatType ?? 'private', ...(req.threadId ? { thread_id: req.threadId } : {}) }
|
|
3436
|
+
? { channel_id: req.channelId, chat_type: req.chatType ?? 'private', ...(isTopicThreadId(req.threadId) ? { thread_id: req.threadId } : {}) }
|
|
3375
3437
|
: undefined;
|
|
3376
3438
|
const ackData = (status, hintId) => ({
|
|
3377
3439
|
status,
|
|
@@ -3793,6 +3855,7 @@ export class AUNChannel {
|
|
|
3793
3855
|
}
|
|
3794
3856
|
// Force close SDK client,中断它内部的重连循环
|
|
3795
3857
|
if (this.client) {
|
|
3858
|
+
ownerResolver.detachClient(this.config.aid, this.client);
|
|
3796
3859
|
this.trace('OUT', 'client.close', { reason: `takeover_${reason}` });
|
|
3797
3860
|
this.client.close().catch(() => { });
|
|
3798
3861
|
this.client = null;
|
|
@@ -3817,6 +3880,7 @@ export class AUNChannel {
|
|
|
3817
3880
|
if (gen !== this.reconnectGeneration) {
|
|
3818
3881
|
logger.info(`${this.logPrefix()} TS-layer reconnect (${reason}) succeeded but generation stale, closing stale client`);
|
|
3819
3882
|
if (this.client) {
|
|
3883
|
+
ownerResolver.detachClient(this.config.aid, this.client);
|
|
3820
3884
|
this.client.close().catch(() => { });
|
|
3821
3885
|
this.client = null;
|
|
3822
3886
|
}
|
|
@@ -3835,7 +3899,7 @@ export class AUNChannel {
|
|
|
3835
3899
|
}
|
|
3836
3900
|
// ── Public API (same interface as before) ───────────────────
|
|
3837
3901
|
/**
|
|
3838
|
-
* 对端发来的话题改名通知(`topic
|
|
3902
|
+
* 对端发来的话题改名通知(`event/topic.renamed`)。由 runtime 注入,交给
|
|
3839
3903
|
* ThreadService.applyRemoteRename 落本地索引;不进入 Agent 消息队列。
|
|
3840
3904
|
*/
|
|
3841
3905
|
setThreadRenameHandler(handler) {
|
|
@@ -3972,7 +4036,8 @@ export class AUNChannel {
|
|
|
3972
4036
|
const finalPayload = this.stripUndefinedDeep(withAunAttention({ ...payload }, context?.attention));
|
|
3973
4037
|
if (finalPayload.thread_id === '')
|
|
3974
4038
|
delete finalPayload.thread_id;
|
|
3975
|
-
|
|
4039
|
+
// 主话题 id(main:<a>:<b>)是本端身份,不上线;主话题的线上形态就是不带该字段。
|
|
4040
|
+
if (isTopicThreadId(context?.threadId) && !finalPayload.thread_id)
|
|
3976
4041
|
finalPayload.thread_id = context.threadId;
|
|
3977
4042
|
if (context?.replyToMessageId && !finalPayload.ref_message_id)
|
|
3978
4043
|
finalPayload.ref_message_id = context.replyToMessageId;
|
|
@@ -4125,6 +4190,79 @@ export class AUNChannel {
|
|
|
4125
4190
|
await this.recallInteractionCard(messageId, channelId, card?.delivery);
|
|
4126
4191
|
}
|
|
4127
4192
|
}
|
|
4193
|
+
restoreInteraction(envelope, interaction, receipt) {
|
|
4194
|
+
if (interaction.kind.kind !== 'action')
|
|
4195
|
+
return undefined;
|
|
4196
|
+
const channelId = envelope.channelId;
|
|
4197
|
+
const delivery = this.requireDelivery(channelId, envelope.replyContext, envelope.delivery);
|
|
4198
|
+
const operationId = envelope.operationId ?? envelope.taskId;
|
|
4199
|
+
const entries = outbox.findInteractionCards(this.config.aid, interaction.id).filter(entry => entry.channelId === channelId && isDeliveryTarget(entry.delivery)
|
|
4200
|
+
&& sameDeliveryTarget(entry.delivery, delivery));
|
|
4201
|
+
const messageIds = new Set();
|
|
4202
|
+
if (receipt?.status === 'sent' && receipt.operationId === operationId) {
|
|
4203
|
+
for (const message of receipt.messages) {
|
|
4204
|
+
if (message.messageId?.trim())
|
|
4205
|
+
messageIds.add(message.messageId);
|
|
4206
|
+
}
|
|
4207
|
+
if (receipt.messageId?.trim())
|
|
4208
|
+
messageIds.add(receipt.messageId);
|
|
4209
|
+
}
|
|
4210
|
+
const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', channelId, this.config.aid);
|
|
4211
|
+
const loggedMessageId = findMessageLogOperationMessageId(chatDir, operationId)
|
|
4212
|
+
?? findLatestMessageLogOperation(chatDir, `${interaction.id}-`)?.messageId;
|
|
4213
|
+
if (loggedMessageId)
|
|
4214
|
+
messageIds.add(loggedMessageId);
|
|
4215
|
+
for (const entry of entries) {
|
|
4216
|
+
const messageId = entry.deliveryReceipt?.messageId ?? entry.deliveryResult?.messageId;
|
|
4217
|
+
if (messageId?.trim())
|
|
4218
|
+
messageIds.add(messageId);
|
|
4219
|
+
}
|
|
4220
|
+
if (messageIds.size > 0) {
|
|
4221
|
+
for (const messageId of messageIds) {
|
|
4222
|
+
this.registerCardPostSend(messageId, {
|
|
4223
|
+
type: 'register_interaction_card', requestId: interaction.id,
|
|
4224
|
+
isCommandCard: false, initiatorAid: interaction.initiatorId,
|
|
4225
|
+
expiresAt: interaction.expiresAt, delivery,
|
|
4226
|
+
}, channelId, delivery, envelope.replyContext);
|
|
4227
|
+
}
|
|
4228
|
+
return sentReceipt(envelope, [...messageIds], envelope.replyContext?.threadId);
|
|
4229
|
+
}
|
|
4230
|
+
const pendingEntry = entries.find(entry => !entry.terminal && !entry.deliveryResult
|
|
4231
|
+
&& !entry.deliveryReceipt);
|
|
4232
|
+
if (pendingEntry) {
|
|
4233
|
+
const state = outbox.deliveryState(this.config.aid, pendingEntry.id);
|
|
4234
|
+
if (state.status === 'queued') {
|
|
4235
|
+
return {
|
|
4236
|
+
status: 'queued', operationId, messages: [], outboxId: pendingEntry.id,
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
if (state.status === 'failed') {
|
|
4240
|
+
return {
|
|
4241
|
+
status: 'failed', operationId, messages: [], outboxId: pendingEntry.id,
|
|
4242
|
+
error: state.error, code: state.code,
|
|
4243
|
+
};
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
const failedEntry = entries.find(entry => entry.terminal);
|
|
4247
|
+
if (failedEntry?.terminal) {
|
|
4248
|
+
return {
|
|
4249
|
+
status: 'failed', operationId, messages: [], outboxId: failedEntry.id,
|
|
4250
|
+
error: failedEntry.terminal.error, code: failedEntry.terminal.code,
|
|
4251
|
+
};
|
|
4252
|
+
}
|
|
4253
|
+
if (receipt?.status === 'queued') {
|
|
4254
|
+
const state = outbox.deliveryState(this.config.aid, receipt.outboxId);
|
|
4255
|
+
if (state.status === 'queued')
|
|
4256
|
+
return receipt;
|
|
4257
|
+
if (state.status === 'failed') {
|
|
4258
|
+
return {
|
|
4259
|
+
status: 'failed', operationId, messages: [], outboxId: receipt.outboxId,
|
|
4260
|
+
error: state.error, code: state.code,
|
|
4261
|
+
};
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
return undefined;
|
|
4265
|
+
}
|
|
4128
4266
|
forgetInteractionCard(messageId, interactionId) {
|
|
4129
4267
|
this.cardMessageIdMap.delete(messageId);
|
|
4130
4268
|
const ids = this.interactionCardMessageIds.get(interactionId);
|
|
@@ -4225,7 +4363,7 @@ export class AUNChannel {
|
|
|
4225
4363
|
const truncatedPayload = this.truncatePayloadIfNeeded(payloadWithCausationRef, label);
|
|
4226
4364
|
const params = { payload: truncatedPayload, encrypt };
|
|
4227
4365
|
const isMenuPayload = typeof truncatedPayload.type === 'string' && truncatedPayload.type.startsWith('menu.');
|
|
4228
|
-
if (!isGroup &&
|
|
4366
|
+
if (!isGroup && ['menu.response', 'menu.event'].includes(truncatedPayload.type))
|
|
4229
4367
|
params.to_device_id = context?.toDeviceId;
|
|
4230
4368
|
if (isMenuPayload && typeof context?.metadata?.menuClientSdkVersion === 'string') {
|
|
4231
4369
|
params.menu_client_sdk_version = context.metadata.menuClientSdkVersion;
|
|
@@ -4419,7 +4557,7 @@ export class AUNChannel {
|
|
|
4419
4557
|
base.agent_name = envelope.agentName;
|
|
4420
4558
|
if (envelope.chatmode)
|
|
4421
4559
|
base.chatmode = envelope.chatmode;
|
|
4422
|
-
if (context?.threadId)
|
|
4560
|
+
if (isTopicThreadId(context?.threadId))
|
|
4423
4561
|
base.thread_id = context.threadId;
|
|
4424
4562
|
if (context?.peerId)
|
|
4425
4563
|
base.initiator = context.peerId;
|
|
@@ -4670,11 +4808,17 @@ export class AUNChannel {
|
|
|
4670
4808
|
return { ok: false, error: 'payload must be an object', code: 'INVALID_PAYLOAD' };
|
|
4671
4809
|
}
|
|
4672
4810
|
let finalPayload;
|
|
4811
|
+
const preserveRawPayload = args.rawPayload === true;
|
|
4673
4812
|
try {
|
|
4674
|
-
const validatedPayload = this.normalizeAunPayloadMentions(args.payload);
|
|
4813
|
+
const validatedPayload = preserveRawPayload ? { ...args.payload } : this.normalizeAunPayloadMentions(args.payload);
|
|
4675
4814
|
finalPayload = this.stripUndefinedDeep(validatedPayload);
|
|
4676
|
-
if (finalPayload.thread_id === '')
|
|
4815
|
+
if (!preserveRawPayload && finalPayload.thread_id === '')
|
|
4677
4816
|
delete finalPayload.thread_id;
|
|
4817
|
+
// Ability call 省略 sent_at 时由 transport 在实际发送前补当前毫秒时间戳,Caller 无需自行取时间
|
|
4818
|
+
if (preserveRawPayload && finalPayload.type === 'ability' && finalPayload.role === 'call'
|
|
4819
|
+
&& finalPayload.sent_at === undefined) {
|
|
4820
|
+
finalPayload.sent_at = Date.now();
|
|
4821
|
+
}
|
|
4678
4822
|
}
|
|
4679
4823
|
catch (e) {
|
|
4680
4824
|
return {
|
|
@@ -4686,38 +4830,60 @@ export class AUNChannel {
|
|
|
4686
4830
|
if (!this.connected || !this.client) {
|
|
4687
4831
|
return { ok: false, error: 'AUN channel is not connected', code: 'AUN_NOT_CONNECTED' };
|
|
4688
4832
|
}
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
if (
|
|
4833
|
+
if (args.ttl !== undefined && (!Number.isInteger(args.ttl) || args.ttl < 1 || args.ttl > MAX_AUN_VOLATILE_TTL_SECONDS)) {
|
|
4834
|
+
return {
|
|
4835
|
+
ok: false,
|
|
4836
|
+
error: `volatile AUN ttl must be an integer from 1 through ${MAX_AUN_VOLATILE_TTL_SECONDS} seconds`,
|
|
4837
|
+
code: 'INVALID_REQUEST_TTL',
|
|
4838
|
+
};
|
|
4839
|
+
}
|
|
4840
|
+
if (preserveRawPayload && jsonUtf8Size(finalPayload) > AUN_MAX_PAYLOAD_SIZE) {
|
|
4841
|
+
return {
|
|
4842
|
+
ok: false,
|
|
4843
|
+
error: `raw payload exceeds the ${AUN_MAX_PAYLOAD_SIZE} byte AUN payload limit`,
|
|
4844
|
+
code: 'PAYLOAD_TOO_LARGE',
|
|
4845
|
+
};
|
|
4846
|
+
}
|
|
4847
|
+
// Raw payload delivery (ability calls) is an application-level envelope:
|
|
4848
|
+
// peer metadata is neither needed nor safe to inject into it.
|
|
4849
|
+
const info = preserveRawPayload ? undefined : await this.fetchPeerInfo(args.to);
|
|
4850
|
+
let chatmode;
|
|
4851
|
+
if (info) {
|
|
4852
|
+
// 与 p2p 发送路径共用同一判定:非人类 → proactive;人类需 owner/admin 才 interactive。
|
|
4853
|
+
const { chatmodeForPeerIdentity } = await import('../core/message/peer-mode.js');
|
|
4854
|
+
let peerRole = null;
|
|
4855
|
+
try {
|
|
4856
|
+
const { resolvePeerRoleDetail } = await import('../config/peer-role-resolver.js');
|
|
4857
|
+
peerRole = resolvePeerRoleDetail({
|
|
4858
|
+
selfAid: this.config.aid,
|
|
4859
|
+
channelKey: `aun#${this.config.aid}#main`,
|
|
4860
|
+
channelType: 'aun',
|
|
4861
|
+
chatType: 'private',
|
|
4862
|
+
actorId: args.to,
|
|
4863
|
+
conversationId: args.to,
|
|
4864
|
+
peerType: info.type ?? undefined,
|
|
4865
|
+
}).effectiveRole;
|
|
4866
|
+
}
|
|
4867
|
+
catch { /* 角色解析失败按未知处理 */ }
|
|
4868
|
+
chatmode = chatmodeForPeerIdentity({ isMachine: info.type !== 'human', type: info.type ?? undefined }, peerRole);
|
|
4869
|
+
}
|
|
4870
|
+
if (chatmode && !finalPayload.chatmode)
|
|
4708
4871
|
finalPayload.chatmode = chatmode;
|
|
4709
|
-
const parentCausation = normalizeCausation(args.causation);
|
|
4872
|
+
const parentCausation = preserveRawPayload ? undefined : normalizeCausation(args.causation);
|
|
4710
4873
|
const outboundCausation = parentCausation ? deriveCausation(parentCausation) : undefined;
|
|
4711
4874
|
const causationRef = outboundCausation ? `caus_${crypto.randomUUID().replace(/-/g, '')}` : undefined;
|
|
4712
4875
|
if (causationRef && outboundCausation) {
|
|
4713
4876
|
finalPayload._evolcore_causation_ref = causationRef;
|
|
4714
4877
|
registerAunCausation(causationRef, this.config.aid, args.to, outboundCausation);
|
|
4715
4878
|
}
|
|
4879
|
+
if (args.debug)
|
|
4880
|
+
finalPayload.debug = { ...args.debug, sentAt: localTimestamp() };
|
|
4716
4881
|
const encrypt = args.encrypt ?? this.shouldEncrypt(args.to);
|
|
4717
4882
|
const sendParams = {
|
|
4718
4883
|
to: args.to,
|
|
4719
|
-
payload: this.truncatePayloadIfNeeded(finalPayload, 'msg-send-ipc'),
|
|
4884
|
+
payload: preserveRawPayload ? finalPayload : this.truncatePayloadIfNeeded(finalPayload, 'msg-send-ipc'),
|
|
4720
4885
|
encrypt,
|
|
4886
|
+
...(args.ttl !== undefined ? { ttl: args.ttl } : {}),
|
|
4721
4887
|
};
|
|
4722
4888
|
try {
|
|
4723
4889
|
const result = await this.callAndTrace('message.send', sendParams);
|
|
@@ -4733,7 +4899,7 @@ export class AUNChannel {
|
|
|
4733
4899
|
refs: { messageId, sessionId: args.log?.sessionId },
|
|
4734
4900
|
});
|
|
4735
4901
|
}
|
|
4736
|
-
this.forwardOutbound(result);
|
|
4902
|
+
this.forwardOutbound(result, args.debug);
|
|
4737
4903
|
let logWritten = false;
|
|
4738
4904
|
if (args.log?.content !== undefined) {
|
|
4739
4905
|
const classified = classifyAunPayloadForLog(finalPayload);
|
|
@@ -4831,6 +4997,8 @@ export class AUNChannel {
|
|
|
4831
4997
|
if (!this.connected || !this.client) {
|
|
4832
4998
|
return { ok: false, error: 'AUN channel is not connected', code: 'AUN_NOT_CONNECTED' };
|
|
4833
4999
|
}
|
|
5000
|
+
if (args.debug)
|
|
5001
|
+
finalPayload.debug = { ...args.debug, sentAt: localTimestamp() };
|
|
4834
5002
|
const encrypt = args.encrypt ?? this.shouldEncrypt(args.groupId);
|
|
4835
5003
|
const result = await this.callAndTrace('group.send', {
|
|
4836
5004
|
group_id: args.groupId,
|
|
@@ -4842,7 +5010,7 @@ export class AUNChannel {
|
|
|
4842
5010
|
return { ok: false, error: `group.send returned no message_id: ${JSON.stringify(result)}`, code: 'MISSING_MESSAGE_ID' };
|
|
4843
5011
|
}
|
|
4844
5012
|
this.logAunSendAccepted('group.send', args.groupId, messageId, encrypt, result);
|
|
4845
|
-
this.forwardOutbound(result);
|
|
5013
|
+
this.forwardOutbound(result, args.debug);
|
|
4846
5014
|
let logWritten = false;
|
|
4847
5015
|
if (args.log?.content !== undefined) {
|
|
4848
5016
|
const classified = classifyAunPayloadForLog(finalPayload);
|
|
@@ -5000,7 +5168,7 @@ export class AUNChannel {
|
|
|
5000
5168
|
if (extracted.length > 0)
|
|
5001
5169
|
payload.mentions = extracted;
|
|
5002
5170
|
}
|
|
5003
|
-
if (context?.threadId)
|
|
5171
|
+
if (isTopicThreadId(context?.threadId))
|
|
5004
5172
|
payload.thread_id = context.threadId;
|
|
5005
5173
|
if (context?.replyToMessageId)
|
|
5006
5174
|
payload.ref_message_id = context.replyToMessageId;
|
|
@@ -5021,6 +5189,10 @@ export class AUNChannel {
|
|
|
5021
5189
|
// 诊断日志:记录 payload 构造结果(含 task_id / thread_id / chatmode)
|
|
5022
5190
|
logger.info(`${this.logPrefix()} deliverTextEntry: channelId=${channelId} thread_id=${payload.thread_id ?? 'none'} task_id=${payload.task_id ?? 'none'} chatmode=${payload.chatmode ?? 'none'} source=${source} textLen=${finalText.length}`);
|
|
5023
5191
|
import('../utils/msg-diag.js').then(m => m.diagLog('aun_out', { aid: this.config.aid, from: this.config.aid, to: channelId, msgId: payload.task_id ?? '', taskId: payload.task_id, text: finalText })).catch(() => { });
|
|
5192
|
+
// aun_out 是发送前打点;send_ok 只在网关返回 message_id 后记录,两者差值即发送失败次数
|
|
5193
|
+
const diagSendOk = (mid) => {
|
|
5194
|
+
import('../utils/msg-diag.js').then(m => m.diagLog('send_ok', { aid: this.config.aid, from: this.config.aid, to: channelId, msgId: mid, taskId: payload.task_id, text: finalText })).catch(() => { });
|
|
5195
|
+
};
|
|
5024
5196
|
const isGroup = delivery.chatType === 'group';
|
|
5025
5197
|
const targetAid = channelId;
|
|
5026
5198
|
if (entry.deliveryReceipt) {
|
|
@@ -5073,7 +5245,7 @@ export class AUNChannel {
|
|
|
5073
5245
|
transport: this.sendReceiptFromResult(result),
|
|
5074
5246
|
});
|
|
5075
5247
|
// Observer forward: outbound (group) — 原样转发 SDK SendResult(含 envelope + payload)
|
|
5076
|
-
this.forwardOutbound(result);
|
|
5248
|
+
this.forwardOutbound(result, payload.debug);
|
|
5077
5249
|
}
|
|
5078
5250
|
}
|
|
5079
5251
|
else {
|
|
@@ -5106,6 +5278,8 @@ export class AUNChannel {
|
|
|
5106
5278
|
this.forwardOutbound(result);
|
|
5107
5279
|
}
|
|
5108
5280
|
}
|
|
5281
|
+
if (acceptedMessageId)
|
|
5282
|
+
diagSendOk(acceptedMessageId);
|
|
5109
5283
|
return sentOutboxResult(acceptedMessageId);
|
|
5110
5284
|
}
|
|
5111
5285
|
catch (e) {
|
|
@@ -5170,6 +5344,8 @@ export class AUNChannel {
|
|
|
5170
5344
|
});
|
|
5171
5345
|
this.forwardOutbound(result);
|
|
5172
5346
|
}
|
|
5347
|
+
if (acceptedMessageId)
|
|
5348
|
+
diagSendOk(acceptedMessageId);
|
|
5173
5349
|
return sentOutboxResult(acceptedMessageId);
|
|
5174
5350
|
}
|
|
5175
5351
|
catch (e2) {
|
|
@@ -5326,7 +5502,8 @@ export class AUNChannel {
|
|
|
5326
5502
|
from: selfAID,
|
|
5327
5503
|
to: channelId,
|
|
5328
5504
|
sessionId: context?.sessionId,
|
|
5329
|
-
|
|
5505
|
+
// 日志用本地话题身份(含主话题规范 id),线上报文仍不带主话题字段。
|
|
5506
|
+
threadId: context?.localTopicId ?? (isTopicThreadId(context?.threadId) ? context.threadId : null),
|
|
5330
5507
|
chatType: isGroup ? 'group' : 'private',
|
|
5331
5508
|
groupId: isGroup ? channelId : null,
|
|
5332
5509
|
msgId,
|
|
@@ -5382,7 +5559,7 @@ export class AUNChannel {
|
|
|
5382
5559
|
: finalPayload;
|
|
5383
5560
|
if (payloadWithChatmode.thread_id === '')
|
|
5384
5561
|
delete payloadWithChatmode.thread_id;
|
|
5385
|
-
if (context?.threadId && !payloadWithChatmode.thread_id)
|
|
5562
|
+
if (isTopicThreadId(context?.threadId) && !payloadWithChatmode.thread_id)
|
|
5386
5563
|
payloadWithChatmode.thread_id = context.threadId;
|
|
5387
5564
|
// Same diagnostic dictionary as text payloads (sessionId/agentSessionId/model).
|
|
5388
5565
|
if (context?.debug)
|
|
@@ -5484,13 +5661,14 @@ export class AUNChannel {
|
|
|
5484
5661
|
: this.shouldEncrypt(encryptTarget);
|
|
5485
5662
|
if (finalPayload.thread_id === '')
|
|
5486
5663
|
delete finalPayload.thread_id;
|
|
5487
|
-
|
|
5664
|
+
// 主话题 id(main:<a>:<b>)是本端身份,不上线;主话题的线上形态就是不带该字段。
|
|
5665
|
+
if (isTopicThreadId(context?.threadId) && !finalPayload.thread_id)
|
|
5488
5666
|
finalPayload.thread_id = context.threadId;
|
|
5489
5667
|
if (context?.replyToMessageId && !finalPayload.ref_message_id)
|
|
5490
5668
|
finalPayload.ref_message_id = context.replyToMessageId;
|
|
5491
5669
|
const isMenuPayload = typeof finalPayload.type === 'string' && finalPayload.type.startsWith('menu.');
|
|
5492
5670
|
const params = { payload: finalPayload, encrypt };
|
|
5493
|
-
if (!isGroup &&
|
|
5671
|
+
if (!isGroup && ['menu.response', 'menu.event'].includes(finalPayload.type))
|
|
5494
5672
|
params.to_device_id = context?.toDeviceId;
|
|
5495
5673
|
if (isMenuPayload && typeof context?.metadata?.menuClientSdkVersion === 'string') {
|
|
5496
5674
|
params.menu_client_sdk_version = context.metadata.menuClientSdkVersion;
|
|
@@ -5932,7 +6110,7 @@ export class AUNChannel {
|
|
|
5932
6110
|
async drainOutbox() {
|
|
5933
6111
|
if (!this.connected || !this.client)
|
|
5934
6112
|
return;
|
|
5935
|
-
this.repairBootstrapRoutes();
|
|
6113
|
+
await this.repairBootstrapRoutes();
|
|
5936
6114
|
const drainQueue = async (queue) => {
|
|
5937
6115
|
if (!outbox.hasPending(this.config.aid, queue))
|
|
5938
6116
|
return;
|
|
@@ -5975,9 +6153,10 @@ export class AUNChannel {
|
|
|
5975
6153
|
await drainQueue('activity');
|
|
5976
6154
|
}
|
|
5977
6155
|
/** Repair bootstrap outbox entries to the configured personal Owner route. */
|
|
5978
|
-
repairBootstrapRoutes() {
|
|
6156
|
+
async repairBootstrapRoutes() {
|
|
5979
6157
|
const aid = this.config.aid.replace(/^@/, '');
|
|
5980
|
-
|
|
6158
|
+
await ownerResolver.refresh(aid);
|
|
6159
|
+
const owners = ownerResolver.owners(aid);
|
|
5981
6160
|
const entries = [
|
|
5982
6161
|
...outbox.findByDedupePrefix(aid, bootstrapInitialMessageOperationPrefix(aid), { includeTerminal: true }),
|
|
5983
6162
|
...findPostBootstrapWelcomeOutbox(aid, { includeTerminal: true }),
|
|
@@ -6074,7 +6253,7 @@ export class AUNChannel {
|
|
|
6074
6253
|
const metadata = metadataInput ? this.stripUndefinedDeep(metadataInput) : undefined;
|
|
6075
6254
|
if (metadata && Object.keys(metadata).length > 0)
|
|
6076
6255
|
statusPayload.metadata = metadata;
|
|
6077
|
-
if (context?.threadId)
|
|
6256
|
+
if (isTopicThreadId(context?.threadId))
|
|
6078
6257
|
statusPayload.thread_id = context.threadId;
|
|
6079
6258
|
if (context?.peerId)
|
|
6080
6259
|
statusPayload.initiator = context.peerId;
|
|
@@ -6149,6 +6328,8 @@ export class AUNChannel {
|
|
|
6149
6328
|
await this.sendStructuredOrThrow(channelId, payloadObj, context);
|
|
6150
6329
|
}
|
|
6151
6330
|
async disconnect() {
|
|
6331
|
+
if (this.client)
|
|
6332
|
+
ownerResolver.detachClient(this.config.aid, this.client);
|
|
6152
6333
|
this.managedMenuClient.close();
|
|
6153
6334
|
this.intentionalDisconnect = true;
|
|
6154
6335
|
if (this.reconnectTimer) {
|
|
@@ -6717,6 +6898,7 @@ export class AUNChannelPlugin {
|
|
|
6717
6898
|
return suppressedReceipt(envelope, 'interaction_not_sent');
|
|
6718
6899
|
};
|
|
6719
6900
|
if (req.kind.kind === 'action') {
|
|
6901
|
+
const isAsyncQuestion = /^aq-[a-f0-9]{40}$/.test(req.id);
|
|
6720
6902
|
const action = req.kind;
|
|
6721
6903
|
const aunCard = withAunAttention({
|
|
6722
6904
|
type: 'action_card',
|
|
@@ -6737,12 +6919,16 @@ export class AUNChannelPlugin {
|
|
|
6737
6919
|
}
|
|
6738
6920
|
if (req.initiatorId && replyCtx?.delivery?.chatType === 'group')
|
|
6739
6921
|
aunCard.initiator = req.initiatorId;
|
|
6740
|
-
if (replyCtx?.threadId)
|
|
6922
|
+
if (isTopicThreadId(replyCtx?.threadId))
|
|
6741
6923
|
aunCard.thread_id = replyCtx.threadId;
|
|
6924
|
+
const cardReplyContext = isAsyncQuestion
|
|
6925
|
+
? { ...replyCtx, metadata: { ...replyCtx?.metadata, operationId } }
|
|
6926
|
+
: replyCtx;
|
|
6742
6927
|
const result = await channel.sendContentPayload(channelId, aunCard, {
|
|
6743
6928
|
contentKind: 'card',
|
|
6744
|
-
context:
|
|
6745
|
-
dedupeKey: req.id.startsWith('gir-') ? operationId : undefined,
|
|
6929
|
+
context: cardReplyContext,
|
|
6930
|
+
dedupeKey: req.id.startsWith('gir-') || isAsyncQuestion ? operationId : undefined,
|
|
6931
|
+
ttl: isAsyncQuestion ? Math.max(0, cardExpiresAt - Date.now()) : undefined,
|
|
6746
6932
|
logText: action.title ? `[card] ${action.title}` : '[card]',
|
|
6747
6933
|
postSend: {
|
|
6748
6934
|
type: 'register_interaction_card',
|
|
@@ -6777,7 +6963,7 @@ export class AUNChannelPlugin {
|
|
|
6777
6963
|
}
|
|
6778
6964
|
if (req.initiatorId && replyCtx?.delivery?.chatType === 'group')
|
|
6779
6965
|
aunCard.initiator = req.initiatorId;
|
|
6780
|
-
if (replyCtx?.threadId)
|
|
6966
|
+
if (isTopicThreadId(replyCtx?.threadId))
|
|
6781
6967
|
aunCard.thread_id = replyCtx.threadId;
|
|
6782
6968
|
const result = await channel.sendContentPayload(channelId, aunCard, {
|
|
6783
6969
|
contentKind: 'card',
|
|
@@ -6831,6 +7017,7 @@ export class AUNChannelPlugin {
|
|
|
6831
7017
|
acknowledge: (messageId) => { channel.acknowledge(messageId); return Promise.resolve(); },
|
|
6832
7018
|
onInteraction: (cb) => { channel.interactionCallback = cb; },
|
|
6833
7019
|
invalidateInteraction: (interactionId, reason) => channel.invalidateInteraction(interactionId, reason),
|
|
7020
|
+
restoreInteraction: (envelope, interaction, receipt) => channel.restoreInteraction(envelope, interaction, receipt),
|
|
6834
7021
|
uploadAgentMd: (content) => channel.uploadAgentMd(content),
|
|
6835
7022
|
downloadAgentMd: (aid) => channel.downloadAgentMd(aid),
|
|
6836
7023
|
getGroupName: (groupId) => channel.getGroupName(groupId),
|