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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isValidAid } from '../../aun/aid/validation.js';
|
|
2
|
+
import { parseTimeArg, TIME_ARG_FORMATS } from '../../cli/cli-argv.js';
|
|
2
3
|
import { normalizeThreadPeer } from './thread-service.js';
|
|
4
|
+
import { resolveThreadRef } from './topic-id.js';
|
|
3
5
|
const STATUSES = new Set(['pending', 'in_progress', 'completed', 'archived']);
|
|
4
6
|
const COMMON_OPTIONS = ['--self', '--format', '--json'];
|
|
5
7
|
const VALUE_OPTIONS = new Set(['--self', '--agent', '--format', '--name', '--label', '--status', '--sort', '--page', '--limit', '--before', '--after', '--if-rev']);
|
|
@@ -45,6 +47,14 @@ export function resolveThreadCommandArgv(argv) {
|
|
|
45
47
|
throw new Error(`Invalid number: ${flag}`);
|
|
46
48
|
return value;
|
|
47
49
|
};
|
|
50
|
+
const time = (flag) => {
|
|
51
|
+
if (!options.has(flag))
|
|
52
|
+
return undefined;
|
|
53
|
+
const value = parseTimeArg(text(flag));
|
|
54
|
+
if (value === undefined)
|
|
55
|
+
throw new Error(`Invalid time: ${flag}(支持 ${TIME_ARG_FORMATS})`);
|
|
56
|
+
return value;
|
|
57
|
+
};
|
|
48
58
|
const selfAidValue = text('--self');
|
|
49
59
|
const agentAidValue = text('--agent');
|
|
50
60
|
const selfAid = (agentAidValue ?? selfAidValue)?.replace(/^@/, '');
|
|
@@ -77,18 +87,23 @@ export function resolveThreadCommandArgv(argv) {
|
|
|
77
87
|
else if (maximum !== 0) {
|
|
78
88
|
request.ref = positional[0];
|
|
79
89
|
if (request.ref !== 'current') {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
90
|
+
// 与 CLI 共用同一套解析:主话题是 `main:<aid>:<aid>`,话题是裸 id,
|
|
91
|
+
// 历史写法 `<peer>/<id>` 仍接受。裸 `main` 与 `<peer>/main` 都被拒绝。
|
|
92
|
+
const selfForRef = selfAid ?? (process.env.EVOLCORE_SELF_AID?.replace(/^@/, '') || '');
|
|
93
|
+
const resolved = resolveThreadRef(request.ref, selfForRef);
|
|
94
|
+
if (resolved.kind === 'invalid')
|
|
95
|
+
throw new Error(resolved.error);
|
|
96
|
+
if (resolved.kind === 'thread') {
|
|
97
|
+
request.peer = resolved.peer ? normalizeThreadPeer(resolved.peer) : undefined;
|
|
98
|
+
request.threadId = resolved.threadId;
|
|
99
|
+
}
|
|
100
|
+
else if (resolved.kind === 'main') {
|
|
101
|
+
request.peer = normalizeThreadPeer(resolved.peer);
|
|
102
|
+
request.threadId = resolved.topicId;
|
|
103
|
+
}
|
|
104
|
+
// 裸对端 AID 已由 resolveThreadRef 解析成主话题(peer + 规范 id),
|
|
105
|
+
// 这里不能再按"裸值就是对端"覆盖,否则会把 `rename <peer>` 变成
|
|
106
|
+
// "改当前会话所在话题",静默改错对象。
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
if (command === 'list')
|
|
@@ -97,7 +112,7 @@ export function resolveThreadCommandArgv(argv) {
|
|
|
97
112
|
page: number('--page', 1), limit: number('--limit', 1),
|
|
98
113
|
});
|
|
99
114
|
if (command === 'history')
|
|
100
|
-
Object.assign(request, { limit: number('--limit', 1), before:
|
|
115
|
+
Object.assign(request, { limit: number('--limit', 1), before: time('--before'), after: time('--after') });
|
|
101
116
|
if (command === 'create') {
|
|
102
117
|
if (!text('--name')?.trim())
|
|
103
118
|
throw new Error('Topic name is required');
|
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export { ThreadService, ThreadServiceError, normalizeThreadPeer } from './thread-service.js';
|
|
2
|
+
export { MAIN_TOPIC_PREFIX, normalizeTopicAid, formatMainTopicId, safeFormatMainTopicId, isMainTopicId, parseMainTopicId, mainTopicPeer, canonicalizeMainTopicId, isMainSessionThreadId, isTopicThreadId, resolveThreadRef, } from './topic-id.js';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { trustedExecutionIdentityInput } from '../auth/agent-delegation.js';
|
|
2
|
-
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
2
|
+
import { authorizeFreshOperation as authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
3
3
|
import { parsePeerKey } from '../relation/peer-identity.js';
|
|
4
4
|
import { resolveThreadCommandArgv } from './command-parser.js';
|
|
5
|
+
import { isTopicThreadId, sessionTopicThreadId } from './topic-id.js';
|
|
5
6
|
import { executeThreadOperation } from './thread-operation.js';
|
|
6
7
|
import { ThreadService } from './thread-service.js';
|
|
7
8
|
export function createThreadOperationExecutor(dependencies) {
|
|
@@ -12,9 +13,10 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
12
13
|
return { ok: false, code: 'INVALID_REQUEST', error: 'Thread operations require argv' };
|
|
13
14
|
const request = resolveThreadCommandArgv(incoming.argv);
|
|
14
15
|
const token = typeof incoming.delegationToken === 'string' ? incoming.delegationToken.trim() : '';
|
|
15
|
-
// `request` 是从 argv
|
|
16
|
-
//
|
|
17
|
-
//
|
|
16
|
+
// `request` 是从 argv 重建的,不含调用方凭据。创建话题的旧式
|
|
17
|
+
// `topic-created` 通知仍经 `sendAunMsg` 走托管授权;宿主终端没有 token,
|
|
18
|
+
// 这类通知会按尽力而为处理。`topic.renamed` 是已有的系统事件,发送器
|
|
19
|
+
// 对它沿用系统事件豁免,因此菜单改名不需要伪造托管任务凭据。
|
|
18
20
|
if (token) {
|
|
19
21
|
request.delegationToken = token;
|
|
20
22
|
if (incoming.delegationCommandHash)
|
|
@@ -58,15 +60,16 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
58
60
|
}
|
|
59
61
|
// `current` 必须先解析成具体 threadId,后续判断(日志、通知)都依赖它。
|
|
60
62
|
// 群主会话改名的限制不在这里做:普通成员允许改自己的本地称呼,
|
|
61
|
-
// 只是不发通知(见 thread-operation.ts 的 topic
|
|
63
|
+
// 只是不发通知(见 thread-operation.ts 的 topic.renamed 分支)。
|
|
62
64
|
if (request.ref === 'current') {
|
|
63
65
|
const indexed = threadService.resolveCurrentThread(request.selfAid, session.id);
|
|
64
66
|
request.peer = indexed?.peer ?? currentPeer;
|
|
65
|
-
|
|
67
|
+
// 主会话保持 ''(rename 等分支用 `threadId === undefined` 判缺参,'' 才表示主话题)。
|
|
68
|
+
request.threadId = indexed?.threadId ?? sessionTopicThreadId(session) ?? '';
|
|
66
69
|
request.sessionId = session.id;
|
|
67
70
|
}
|
|
68
71
|
const globalOperation = ['list-peers', 'check-index', 'rebuild-index'].includes(request.op);
|
|
69
|
-
const decision = authorizeOperation({
|
|
72
|
+
const decision = await authorizeOperation({
|
|
70
73
|
source: 'agent-tool', subject,
|
|
71
74
|
intent: { operation: `ec.thread.${request.op}`, scope: globalOperation && !(relationOnly && request.op === 'list-peers') ? 'agent' : 'relation', source: 'agent-tool', args: { self: request.selfAid, peer: request.peer ?? currentPeer, peerKey: delegation.grant.peerKey } },
|
|
72
75
|
auditMetadata: { taskId: delegation.grant.taskId, messageId: delegation.grant.messageId },
|
|
@@ -89,7 +92,7 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
89
92
|
// 被要求联系 C 时,查/建/写的是我与 C 的话题,不属于 G,也不该受 G 约束。
|
|
90
93
|
// "只能操作自己数据"由 authorizeOperation 的 targetCurrentAgentOnly 负责。
|
|
91
94
|
if (request.op.startsWith('summary-') && !request.sessionId) {
|
|
92
|
-
const target = request.threadId
|
|
95
|
+
const target = isTopicThreadId(request.threadId)
|
|
93
96
|
? await threadService.showThread({ selfAid: request.selfAid, peer: request.peer, threadId: request.threadId })
|
|
94
97
|
: await threadService.showMainSession(request.selfAid, request.peer);
|
|
95
98
|
if (!target)
|
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
import { logger } from '../../utils/logger.js';
|
|
2
2
|
import { normalizeThreadPeer } from './thread-service.js';
|
|
3
|
+
import { isMainSessionThreadId, isTopicThreadId } from './topic-id.js';
|
|
4
|
+
/**
|
|
5
|
+
* Emit the existing cross-device rename event after a local rename succeeds.
|
|
6
|
+
* Storage stays in ThreadService; transport stays in the operation layer.
|
|
7
|
+
*/
|
|
8
|
+
export async function notifyThreadRename(input) {
|
|
9
|
+
logger.info(`[Thread] notifyRename called: peer=${input.peer} threadId=${input.threadId} changed=${input.result.changed} nameUpdatedAt=${input.result.nameUpdatedAt} sendSystemMsg=${!!input.sendSystemMsg}`);
|
|
10
|
+
if (!input.sendSystemMsg || !input.result.changed || !input.result.nameUpdatedAt)
|
|
11
|
+
return;
|
|
12
|
+
const peerKind = input.result.peerKind
|
|
13
|
+
?? (isMainSessionThreadId(input.threadId)
|
|
14
|
+
? undefined
|
|
15
|
+
: input.threadService.findInIndex(input.selfAid, input.peer, input.threadId)?.peerKind)
|
|
16
|
+
?? 'private';
|
|
17
|
+
// 群主话题是“本地对群的称呼”,发给群只会覆盖所有人的本地叫法。
|
|
18
|
+
if (peerKind === 'group' && isMainSessionThreadId(input.threadId))
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
await input.sendSystemMsg({
|
|
22
|
+
from: input.selfAid,
|
|
23
|
+
to: input.peer,
|
|
24
|
+
threadId: input.threadId,
|
|
25
|
+
topicName: input.result.name,
|
|
26
|
+
peerKind,
|
|
27
|
+
kind: 'topic.renamed',
|
|
28
|
+
nameUpdatedAt: input.result.nameUpdatedAt,
|
|
29
|
+
originSessionId: input.originSessionId,
|
|
30
|
+
delegationToken: input.delegationToken,
|
|
31
|
+
delegationCommandHash: input.delegationCommandHash,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// 通知失败不影响本地改名,但必须留痕:静默失败会让“改名同步”
|
|
36
|
+
// 看起来像没实现(实测已踩过)。
|
|
37
|
+
logger.warn(`[Thread] topic.renamed notification failed: peer=${input.peer} thread=${input.threadId} name=${JSON.stringify(input.result.name)} error=${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
3
40
|
export function executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
4
41
|
return _executeThreadOperation(request, threadService, sendSystemMsg);
|
|
5
42
|
}
|
|
@@ -38,7 +75,7 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
38
75
|
case 'show': {
|
|
39
76
|
let peer = request.peer;
|
|
40
77
|
let threadId = request.threadId;
|
|
41
|
-
if ((!peer ||
|
|
78
|
+
if ((!peer || threadId === undefined) && request.sessionId) {
|
|
42
79
|
const resolved = threadService.resolveCurrentThread(selfAid, request.sessionId);
|
|
43
80
|
if (resolved) {
|
|
44
81
|
peer = resolved.peer;
|
|
@@ -47,8 +84,8 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
47
84
|
}
|
|
48
85
|
if (!peer)
|
|
49
86
|
return { ok: false, code: 'MISSING_ARGS', error: 'peer is required' };
|
|
50
|
-
//
|
|
51
|
-
if (!threadId) {
|
|
87
|
+
// 主话题(空 / `main` / 规范 id)→ 读该关系的主会话
|
|
88
|
+
if (!isTopicThreadId(threadId)) {
|
|
52
89
|
const result = await threadService.showMainSession(selfAid, peer);
|
|
53
90
|
if (!result)
|
|
54
91
|
return { ok: false, code: 'NOT_FOUND', error: `与 ${peer} 无活跃主会话` };
|
|
@@ -134,7 +171,7 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
134
171
|
case 'rename': {
|
|
135
172
|
let peer = request.peer;
|
|
136
173
|
let threadId = request.threadId;
|
|
137
|
-
if ((!peer ||
|
|
174
|
+
if ((!peer || threadId === undefined) && request.sessionId) {
|
|
138
175
|
const resolved = threadService.resolveCurrentThread(selfAid, request.sessionId);
|
|
139
176
|
if (resolved) {
|
|
140
177
|
peer = resolved.peer;
|
|
@@ -146,65 +183,49 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
146
183
|
return { ok: false, code: 'MISSING_ARGS', error: 'peer, threadId and name required' };
|
|
147
184
|
}
|
|
148
185
|
const result = await threadService.renameThread({ selfAid, peer, threadId, name: request.name });
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
to: peer,
|
|
162
|
-
threadId: threadId ?? '',
|
|
163
|
-
topicName: result.name,
|
|
164
|
-
peerKind,
|
|
165
|
-
kind: 'topic-renamed',
|
|
166
|
-
nameUpdatedAt: result.nameUpdatedAt,
|
|
167
|
-
originSessionId: request.sessionId,
|
|
168
|
-
delegationToken: request.delegationToken,
|
|
169
|
-
delegationCommandHash: request.delegationCommandHash,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
// 通知失败不影响本地改名,但必须留痕:静默失败会让"改名同步"
|
|
174
|
-
// 看起来像没实现(实测已踩过)。
|
|
175
|
-
logger.warn(`[Thread] topic-renamed notification failed: peer=${peer} thread=${threadId ?? 'main'} name=${JSON.stringify(result.name)} error=${error instanceof Error ? error.message : String(error)}`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
186
|
+
// 改名通知走统一出口(同名、失败和群主话题不发)。
|
|
187
|
+
await notifyThreadRename({
|
|
188
|
+
selfAid,
|
|
189
|
+
peer,
|
|
190
|
+
threadId: threadId ?? '',
|
|
191
|
+
result,
|
|
192
|
+
threadService,
|
|
193
|
+
sendSystemMsg,
|
|
194
|
+
originSessionId: request.sessionId,
|
|
195
|
+
delegationToken: request.delegationToken,
|
|
196
|
+
delegationCommandHash: request.delegationCommandHash,
|
|
197
|
+
});
|
|
179
198
|
return { ok: true, result };
|
|
180
199
|
}
|
|
181
200
|
case 'label': {
|
|
182
201
|
let peer = request.peer;
|
|
183
202
|
let threadId = request.threadId;
|
|
184
|
-
if ((!peer ||
|
|
203
|
+
if ((!peer || threadId === undefined) && request.sessionId) {
|
|
185
204
|
const resolved = threadService.resolveCurrentThread(selfAid, request.sessionId);
|
|
186
205
|
if (resolved) {
|
|
187
206
|
peer = resolved.peer;
|
|
188
207
|
threadId = resolved.threadId;
|
|
189
208
|
}
|
|
190
209
|
}
|
|
191
|
-
if (!peer || !threadId)
|
|
192
|
-
return { ok: false, code: 'MISSING_ARGS', error: '
|
|
210
|
+
if (!peer || !isTopicThreadId(threadId)) {
|
|
211
|
+
return { ok: false, code: 'MISSING_ARGS', error: '标签只适用于话题(主话题没有标签)' };
|
|
212
|
+
}
|
|
193
213
|
await threadService.labelThread({ selfAid, peer, threadId, label: request.label ?? null });
|
|
194
214
|
return { ok: true };
|
|
195
215
|
}
|
|
196
216
|
case 'status': {
|
|
197
217
|
let peer = request.peer;
|
|
198
218
|
let threadId = request.threadId;
|
|
199
|
-
if ((!peer ||
|
|
219
|
+
if ((!peer || threadId === undefined) && request.sessionId) {
|
|
200
220
|
const resolved = threadService.resolveCurrentThread(selfAid, request.sessionId);
|
|
201
221
|
if (resolved) {
|
|
202
222
|
peer = resolved.peer;
|
|
203
223
|
threadId = resolved.threadId;
|
|
204
224
|
}
|
|
205
225
|
}
|
|
206
|
-
if (!peer || !threadId || !request.topicStatus)
|
|
207
|
-
return { ok: false, code: 'MISSING_ARGS', error: '
|
|
226
|
+
if (!peer || !isTopicThreadId(threadId) || !request.topicStatus) {
|
|
227
|
+
return { ok: false, code: 'MISSING_ARGS', error: '状态只适用于话题(主话题没有业务状态)' };
|
|
228
|
+
}
|
|
208
229
|
await threadService.statusThread({ selfAid, peer, threadId, topicStatus: request.topicStatus });
|
|
209
230
|
return { ok: true };
|
|
210
231
|
}
|
|
@@ -221,15 +242,16 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
221
242
|
case 'delete': {
|
|
222
243
|
let peer = request.peer;
|
|
223
244
|
let threadId = request.threadId;
|
|
224
|
-
if ((!peer ||
|
|
245
|
+
if ((!peer || threadId === undefined) && request.sessionId) {
|
|
225
246
|
const resolved = threadService.resolveCurrentThread(selfAid, request.sessionId);
|
|
226
247
|
if (resolved) {
|
|
227
248
|
peer = resolved.peer;
|
|
228
249
|
threadId = resolved.threadId;
|
|
229
250
|
}
|
|
230
251
|
}
|
|
231
|
-
if (!peer || !threadId)
|
|
232
|
-
return { ok: false, code: '
|
|
252
|
+
if (!peer || !isTopicThreadId(threadId)) {
|
|
253
|
+
return { ok: false, code: 'NOT_A_TOPIC', error: '只能删除话题(主话题不可删除)' };
|
|
254
|
+
}
|
|
233
255
|
await threadService.deleteThread(selfAid, peer, threadId);
|
|
234
256
|
return { ok: true };
|
|
235
257
|
}
|
|
@@ -251,7 +273,7 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
251
273
|
if (!name)
|
|
252
274
|
return { ok: true, result };
|
|
253
275
|
// 解析改名目标:主会话(threadId 为空串)不进索引,靠 peer + sessionId 定位;
|
|
254
|
-
//
|
|
276
|
+
// 话题优先用索引里的 peer/threadId,索引查不到时退回请求里带的 ref。
|
|
255
277
|
const indexed = threadService.readThreadIndex(selfAid).threads.find(t => t.sessionId === request.sessionId);
|
|
256
278
|
const peer = request.peer ?? indexed?.peer;
|
|
257
279
|
const threadId = request.threadId ?? indexed?.threadId;
|
|
@@ -264,6 +286,19 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
264
286
|
}
|
|
265
287
|
try {
|
|
266
288
|
const renamed = await threadService.renameThread({ selfAid, peer, threadId: threadId ?? '', name });
|
|
289
|
+
// 总结伴随的真实改名同样要通知对端:与 `ec thread rename` 共用同一出口,
|
|
290
|
+
// 否则 App 与对端索引只在 CLI rename 路径同步,summary set 改名静默。
|
|
291
|
+
await notifyThreadRename({
|
|
292
|
+
selfAid,
|
|
293
|
+
peer,
|
|
294
|
+
threadId: threadId ?? '',
|
|
295
|
+
result: renamed,
|
|
296
|
+
threadService,
|
|
297
|
+
sendSystemMsg,
|
|
298
|
+
originSessionId: request.sessionId,
|
|
299
|
+
delegationToken: request.delegationToken,
|
|
300
|
+
delegationCommandHash: request.delegationCommandHash,
|
|
301
|
+
});
|
|
267
302
|
return { ok: true, result: { ...result, renamedTo: renamed.name, renamedChanged: renamed.changed } };
|
|
268
303
|
}
|
|
269
304
|
catch (e) {
|
|
@@ -1,18 +1,57 @@
|
|
|
1
1
|
import { sameGroupAid } from '../../aun/group/identity.js';
|
|
2
2
|
import { ThreadService } from './thread-service.js';
|
|
3
|
+
import { isMainSessionThreadId, resolveThreadRef } from './topic-id.js';
|
|
3
4
|
/**
|
|
4
5
|
* 解析 `--thread` 跨话题发送的目标话题。
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* 输入是**话题引用**:话题 id、主话题 id(`main:<a>:<b>`),或历史写法
|
|
8
|
+
* `<peer>/<id>`。目标对端由引用自身推出(话题取索引条目,主话题取 id 里的
|
|
9
|
+
* 另一端),`target` 仅用于矛盾校验——写了就必须与引用一致。
|
|
10
|
+
*
|
|
11
|
+
* 话题必须存在于本端:话题查索引,主话题查该关系的 active session。
|
|
12
|
+
* 返回 undefined 表示该引用不能作为跨话题路由依据。
|
|
9
13
|
*/
|
|
10
|
-
export function resolveThreadRouteTarget(selfAid,
|
|
11
|
-
if (!selfAid
|
|
14
|
+
export function resolveThreadRouteTarget(selfAid, threadRef, scope, target) {
|
|
15
|
+
if (!selfAid)
|
|
16
|
+
return undefined;
|
|
17
|
+
const normalizedSelf = selfAid.replace(/^@/, '');
|
|
18
|
+
const resolved = resolveThreadRef(threadRef, normalizedSelf);
|
|
19
|
+
if (resolved.kind === 'invalid' || resolved.kind === 'current')
|
|
12
20
|
return undefined;
|
|
21
|
+
// 主话题:对端只能从引用里来。旧的空串/`main` 没有对端信息,只能靠显式的
|
|
22
|
+
// target 补齐(保留给尚未归一的历史调用方)。
|
|
23
|
+
if (resolved.kind === 'main' || isMainSessionThreadId(threadRef)) {
|
|
24
|
+
const peerFromRef = resolved.kind === 'main' ? resolved.peer : undefined;
|
|
25
|
+
const effectiveTarget = peerFromRef ?? target;
|
|
26
|
+
if (!effectiveTarget)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (target !== undefined && peerFromRef !== undefined) {
|
|
29
|
+
const normalizedTarget = target.replace(/^@/, '');
|
|
30
|
+
const matches = scope === 'group'
|
|
31
|
+
? sameGroupAid(peerFromRef, normalizedTarget)
|
|
32
|
+
: peerFromRef === normalizedTarget;
|
|
33
|
+
if (!matches)
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
let entry;
|
|
37
|
+
try {
|
|
38
|
+
entry = ThreadService.findMainSessionEntry(normalizedSelf, effectiveTarget);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
if (!entry)
|
|
44
|
+
return undefined;
|
|
45
|
+
if ((entry.channelType ?? 'aun') !== 'aun')
|
|
46
|
+
return undefined;
|
|
47
|
+
if (entry.peerKind !== (scope === 'group' ? 'group' : 'private'))
|
|
48
|
+
return undefined;
|
|
49
|
+
return entry;
|
|
50
|
+
}
|
|
51
|
+
const threadId = resolved.threadId;
|
|
13
52
|
let entry;
|
|
14
53
|
try {
|
|
15
|
-
entry = ThreadService.findThreadById(
|
|
54
|
+
entry = ThreadService.findThreadById(normalizedSelf, threadId);
|
|
16
55
|
}
|
|
17
56
|
catch {
|
|
18
57
|
return undefined;
|
|
@@ -23,6 +62,15 @@ export function resolveThreadRouteTarget(selfAid, threadId, scope, target) {
|
|
|
23
62
|
return undefined;
|
|
24
63
|
if (entry.peerKind !== (scope === 'group' ? 'group' : 'private'))
|
|
25
64
|
return undefined;
|
|
65
|
+
// 引用里写了 `<peer>/<id>` 时,peer 必须与索引一致——不一致说明引用来自别处。
|
|
66
|
+
if (resolved.peer !== undefined) {
|
|
67
|
+
const normalizedPeer = resolved.peer.replace(/^@/, '');
|
|
68
|
+
const samePeer = scope === 'group'
|
|
69
|
+
? sameGroupAid(entry.peer, normalizedPeer)
|
|
70
|
+
: entry.peer === normalizedPeer;
|
|
71
|
+
if (!samePeer)
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
26
74
|
if (target === undefined)
|
|
27
75
|
return entry;
|
|
28
76
|
const normalizedTarget = target.replace(/^@/, '');
|