evolcore 0.0.33 → 0.0.35-test.0
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 +59 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +38 -4
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +93 -14
- package/dist/agents/codex/diagnostics.js +79 -22
- package/dist/agents/codex-runner.js +245 -47
- package/dist/agents/ecagent-runner.js +90 -27
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/agentmd.js +13 -2
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/msg/managed-operation.js +16 -0
- package/dist/aun/msg/p2p.js +18 -2
- package/dist/channels/aun.js +123 -7
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +39 -14
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +130 -23
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/getmeta-command.js +211 -0
- package/dist/cli/index.js +26 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/memory-command.js +200 -156
- package/dist/cli/thread-command.js +17 -7
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +25 -8
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +22 -3
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/channel-runtime.js +1 -1
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +145 -5
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +24 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/command/topic-slash.js +4 -7
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/event-catalog.js +14 -0
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +47 -2
- package/dist/core/memory/batch-meta.js +133 -0
- package/dist/core/memory/consolidation-context.js +132 -28
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation-trigger.js +15 -7
- package/dist/core/memory/consolidation.js +146 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +50 -19
- package/dist/core/message/aun-ipc-sender.js +19 -1
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +5 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +17 -0
- package/dist/core/message/response-engine.js +598 -50
- package/dist/core/permission/ec-command-parser.js +169 -44
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +60 -62
- package/dist/core/relation/peer-identity.js +87 -12
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +131 -6
- package/dist/core/session/session-title.js +26 -1
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +289 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +6 -1
- package/dist/core/thread/ipc-operation.js +33 -16
- package/dist/core/thread/thread-operation.js +62 -24
- package/dist/core/thread/thread-service.js +258 -51
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +17 -5
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +9 -10
- package/kits/docs/evolcore/fs.md +2 -2
- package/kits/docs/evolcore/group-collaboration.md +3 -3
- package/kits/docs/evolcore/group.md +14 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -16
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/stats.md +1 -1
- package/kits/docs/evolcore/thread-chain.md +30 -14
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.auxiliary.json +9 -0
- package/kits/eck_manifest.json +20 -20
- package/kits/eck_manifest.memory.json +20 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +13 -16
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +8 -5
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/thread-ops/SKILL.md +18 -18
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +12 -12
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +42 -7
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bash-description.md +9 -0
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* system-session/registry.ts — 系统会话模块注册表
|
|
3
|
+
*
|
|
4
|
+
* 只登记仓库内的模块,不做运行时动态加载、不读配置。
|
|
5
|
+
*/
|
|
6
|
+
import { SYSTEM_SESSION_TYPES } from './types.js';
|
|
7
|
+
import { memorySystemSession } from './memory.js';
|
|
8
|
+
const MODULES = new Map([
|
|
9
|
+
[memorySystemSession.type, memorySystemSession],
|
|
10
|
+
]);
|
|
11
|
+
if (SYSTEM_SESSION_TYPES.length !== MODULES.size || SYSTEM_SESSION_TYPES.some(type => !MODULES.has(type))) {
|
|
12
|
+
throw new Error('SYSTEM_SESSION_TYPES 与系统会话注册表不一致');
|
|
13
|
+
}
|
|
14
|
+
export function getSystemSessionModule(type) {
|
|
15
|
+
return MODULES.get(type);
|
|
16
|
+
}
|
|
17
|
+
export function isSystemSessionType(type) {
|
|
18
|
+
return !!type && MODULES.has(type);
|
|
19
|
+
}
|
|
20
|
+
/** 从 session.metadata 读出系统会话标记;存在但不合法时拒绝降级为普通会话。 */
|
|
21
|
+
export function readSystemSessionMarker(metadata) {
|
|
22
|
+
const rawValue = metadata?.systemSession;
|
|
23
|
+
if (rawValue === undefined)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
|
|
26
|
+
throw new Error('系统会话标记格式非法');
|
|
27
|
+
}
|
|
28
|
+
const raw = rawValue;
|
|
29
|
+
if (typeof raw.type !== 'string' || typeof raw.subtype !== 'string') {
|
|
30
|
+
throw new Error('系统会话标记缺少 type 或 subtype');
|
|
31
|
+
}
|
|
32
|
+
const module = MODULES.get(raw.type);
|
|
33
|
+
if (!module)
|
|
34
|
+
throw new Error(`未注册的系统会话类型: ${raw.type}`);
|
|
35
|
+
if (!module.subtypes.includes(raw.subtype)) {
|
|
36
|
+
throw new Error(`系统会话 ${raw.type} 不支持 subtype: ${raw.subtype}`);
|
|
37
|
+
}
|
|
38
|
+
return { type: raw.type, subtype: raw.subtype };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 拉起前校验:类型与 subtype 已注册、runner 受支持。
|
|
42
|
+
* 不合法直接抛错,调用方不应降级成普通会话。
|
|
43
|
+
*/
|
|
44
|
+
export function assertSystemSessionLaunchable(marker, runner) {
|
|
45
|
+
const module = MODULES.get(marker.type);
|
|
46
|
+
if (!module)
|
|
47
|
+
throw new Error(`未注册的系统会话类型: ${marker.type}`);
|
|
48
|
+
if (!module.subtypes.includes(marker.subtype)) {
|
|
49
|
+
throw new Error(`系统会话 ${marker.type} 不支持 subtype: ${marker.subtype}`);
|
|
50
|
+
}
|
|
51
|
+
if (!module.supportedRunners.includes(runner)) {
|
|
52
|
+
throw new Error(`系统会话 ${marker.type} 不支持在 ${runner} 上运行(支持:${module.supportedRunners.join(', ')})`);
|
|
53
|
+
}
|
|
54
|
+
return module;
|
|
55
|
+
}
|
|
56
|
+
/** 从 session.metadata 构造模块输入事实。 */
|
|
57
|
+
export function buildSystemSessionContext(params) {
|
|
58
|
+
const m = params.metadata ?? {};
|
|
59
|
+
const str = (key) => (typeof m[key] === 'string' && m[key] ? m[key] : undefined);
|
|
60
|
+
const scope = str('memoryScope');
|
|
61
|
+
const batchDir = str('consolidationBatchDir');
|
|
62
|
+
return {
|
|
63
|
+
sessionId: params.sessionId,
|
|
64
|
+
subtype: params.marker.subtype,
|
|
65
|
+
selfAid: params.selfAid,
|
|
66
|
+
projectPath: params.projectPath,
|
|
67
|
+
...(params.managedTempDir ? { managedTempDir: params.managedTempDir } : {}),
|
|
68
|
+
...(str('peerKey') ? { peerKey: str('peerKey') } : {}),
|
|
69
|
+
...(str('consolidationOwnerAid') ? { ownerAid: str('consolidationOwnerAid') } : {}),
|
|
70
|
+
...(batchDir ? { batchDir } : {}),
|
|
71
|
+
...(str('consolidationChatDir') ? { chatDir: str('consolidationChatDir') } : {}),
|
|
72
|
+
...(scope === 'relation' || scope === 'agent' || scope === 'owner' ? { memoryScope: scope } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* system-session/types.ts — 系统会话模块接口
|
|
3
|
+
*
|
|
4
|
+
* 系统会话(记忆整理等)与角色体系无关:不带角色、不走角色权限判定。
|
|
5
|
+
* 路径、工具、ec 命令、manifest、runner 启动参数全部由对应类型的代码模块决定,
|
|
6
|
+
* 模块的结论就是最终结论,不可配置。
|
|
7
|
+
*
|
|
8
|
+
* 详见 docs/memory/system-session-plugin-design.md
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* 已注册的系统会话类型。放在无依赖的 types.ts,供 eck / config 层判断而不引入模块实现;
|
|
12
|
+
* registry.ts 启动时校验与注册表一致。
|
|
13
|
+
*/
|
|
14
|
+
export const SYSTEM_SESSION_TYPES = ['memory'];
|
|
@@ -81,7 +81,12 @@ export function resolveThreadCommandArgv(argv) {
|
|
|
81
81
|
if (pieces.length > 2 || !pieces[0] || pieces[1] === '')
|
|
82
82
|
throw new Error('Invalid thread reference');
|
|
83
83
|
request.peer = normalizeThreadPeer(pieces[0]);
|
|
84
|
-
|
|
84
|
+
// 主会话的 threadId 是空串;`<peer>/main` 是同义写法(list 也这样显示)。
|
|
85
|
+
// `rename` 额外把裸 peer 也当成主会话:不写斜杠时改的是主会话名,
|
|
86
|
+
// 与 `list` / `history` 不传 threadId 读主会话的口径一致。
|
|
87
|
+
request.threadId = pieces[1] === 'main' || (command === 'rename' && pieces.length === 1)
|
|
88
|
+
? ''
|
|
89
|
+
: pieces[1];
|
|
85
90
|
if (!request.peer || request.threadId && !/^[A-Za-z0-9][A-Za-z0-9_.-]*$/.test(request.threadId))
|
|
86
91
|
throw new Error('Invalid thread reference');
|
|
87
92
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { trustedExecutionIdentityInput } from '../auth/agent-delegation.js';
|
|
2
2
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
3
|
-
import { isManagementRole } from '../../config/builtin-roles.js';
|
|
4
3
|
import { parsePeerKey } from '../relation/peer-identity.js';
|
|
5
4
|
import { resolveThreadCommandArgv } from './command-parser.js';
|
|
6
5
|
import { executeThreadOperation } from './thread-operation.js';
|
|
@@ -13,6 +12,16 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
13
12
|
return { ok: false, code: 'INVALID_REQUEST', error: 'Thread operations require argv' };
|
|
14
13
|
const request = resolveThreadCommandArgv(incoming.argv);
|
|
15
14
|
const token = typeof incoming.delegationToken === 'string' ? incoming.delegationToken.trim() : '';
|
|
15
|
+
// `request` 是从 argv 重建的,不含调用方凭据。后续系统通知(topic-renamed
|
|
16
|
+
// 等)要经 `sendAunMsg` 再授权一次,必须带上来源会话与 token;否则会被
|
|
17
|
+
// 判为 missing_token。宿主终端调用没有 token,这里如实留空。
|
|
18
|
+
if (token) {
|
|
19
|
+
request.delegationToken = token;
|
|
20
|
+
if (incoming.delegationCommandHash)
|
|
21
|
+
request.delegationCommandHash = incoming.delegationCommandHash;
|
|
22
|
+
if (incoming.sessionId)
|
|
23
|
+
request.sessionId = incoming.sessionId;
|
|
24
|
+
}
|
|
16
25
|
let currentPeer;
|
|
17
26
|
let relationOnly = false;
|
|
18
27
|
if (token) {
|
|
@@ -38,12 +47,24 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
38
47
|
});
|
|
39
48
|
relationOnly = subject.dataScope === 'relation';
|
|
40
49
|
const readOnly = ['list', 'list-peers', 'show', 'history', 'check-index', 'summary-show', 'summary-log'].includes(request.op);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
// 话题数据是**本 agent 自己的**(`agents/<self>/data/threads/`),与当前
|
|
51
|
+
// 所在的关系(私聊对端 / 群)无关。因此:
|
|
52
|
+
// - 触发场景的 chatType 不能决定能否操作话题:在群里被要求联系第三方,
|
|
53
|
+
// 查索引、建话题、写摘要都是对自己数据的操作,不涉及当前群。
|
|
54
|
+
// - 建/改话题也不再要求管理员:member 建一个"我与 C"的话题与管理员无关。
|
|
55
|
+
// 保留的唯一硬约束:readonly 权限模式(visitor 兜底)不得写。
|
|
56
|
+
if (!readOnly && subject.permissionMode === 'readonly') {
|
|
57
|
+
return { ok: false, code: 'NOT_ALLOWED', error: 'Thread changes require a writable task' };
|
|
58
|
+
}
|
|
59
|
+
// `current` 必须先解析成具体 threadId,后续判断(日志、通知)都依赖它。
|
|
60
|
+
// 群主会话改名的限制不在这里做:普通成员允许改自己的本地称呼,
|
|
61
|
+
// 只是不发通知(见 thread-operation.ts 的 topic-renamed 分支)。
|
|
62
|
+
if (request.ref === 'current') {
|
|
63
|
+
const indexed = threadService.resolveCurrentThread(request.selfAid, session.id);
|
|
64
|
+
request.peer = indexed?.peer ?? currentPeer;
|
|
65
|
+
request.threadId = indexed?.threadId ?? session.threadId ?? undefined;
|
|
66
|
+
request.sessionId = session.id;
|
|
44
67
|
}
|
|
45
|
-
if (!readOnly && (!management || subject.permissionMode === 'readonly'))
|
|
46
|
-
return { ok: false, code: 'NOT_ALLOWED', error: 'Thread changes require a writable administrator task' };
|
|
47
68
|
const globalOperation = ['list-peers', 'check-index', 'rebuild-index'].includes(request.op);
|
|
48
69
|
const decision = authorizeOperation({
|
|
49
70
|
source: 'agent-tool', subject,
|
|
@@ -54,12 +75,6 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
54
75
|
return { ok: false, code: decision.code, error: decision.reason };
|
|
55
76
|
if (relationOnly && globalOperation && request.op !== 'list-peers')
|
|
56
77
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Index management requires Agent scope' };
|
|
57
|
-
if (request.ref === 'current') {
|
|
58
|
-
const indexed = threadService.resolveCurrentThread(request.selfAid, session.id);
|
|
59
|
-
request.peer = indexed?.peer ?? currentPeer;
|
|
60
|
-
request.threadId = indexed?.threadId ?? session.threadId ?? undefined;
|
|
61
|
-
request.sessionId = session.id;
|
|
62
|
-
}
|
|
63
78
|
}
|
|
64
79
|
else {
|
|
65
80
|
if (incoming.sessionId || incoming.delegationToken || incoming.localControl !== true)
|
|
@@ -69,8 +84,10 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
69
84
|
if (request.ref === 'current')
|
|
70
85
|
return { ok: false, code: 'INVALID_REQUEST', error: 'current requires a managed caller session' };
|
|
71
86
|
}
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
// 话题操作的目标对端与"当前在哪个关系里被触发"是两个不同维度:
|
|
88
|
+
// 索引、话题会话、摘要全部落在本 agent 自己的数据目录下。在群 G 里
|
|
89
|
+
// 被要求联系 C 时,查/建/写的是我与 C 的话题,不属于 G,也不该受 G 约束。
|
|
90
|
+
// "只能操作自己数据"由 authorizeOperation 的 targetCurrentAgentOnly 负责。
|
|
74
91
|
if (request.op.startsWith('summary-') && !request.sessionId) {
|
|
75
92
|
const target = request.threadId
|
|
76
93
|
? await threadService.showThread({ selfAid: request.selfAid, peer: request.peer, threadId: request.threadId })
|
|
@@ -84,8 +101,8 @@ export function createThreadOperationExecutor(dependencies) {
|
|
|
84
101
|
const response = await executeThreadOperation(request, threadService, dependencies.sendSystemMsg);
|
|
85
102
|
if (response.ok && request.op === 'delete' && request.threadId)
|
|
86
103
|
dependencies.onDeleted?.(request.selfAid, request.threadId);
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
// list-peers 不再裁剪到当前对端:它就是本 agent 自己的对端目录索引,
|
|
105
|
+
// 跨话题协作的前提恰恰是先能看到"我和谁是过话题"。
|
|
89
106
|
return response;
|
|
90
107
|
}
|
|
91
108
|
catch (error) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { logger } from '../../utils/logger.js';
|
|
1
2
|
import { normalizeThreadPeer } from './thread-service.js';
|
|
2
3
|
export function executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
3
4
|
return _executeThreadOperation(request, threadService, sendSystemMsg);
|
|
@@ -118,6 +119,10 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
118
119
|
threadId: result.threadId,
|
|
119
120
|
topicName: result.name,
|
|
120
121
|
peerKind: request.peerKind ?? 'private',
|
|
122
|
+
// 与 rename 相同:授权链需要托管上下文,宿主调用则留空(跳过通知)。
|
|
123
|
+
originSessionId: request.sessionId,
|
|
124
|
+
delegationToken: request.delegationToken,
|
|
125
|
+
delegationCommandHash: request.delegationCommandHash,
|
|
121
126
|
});
|
|
122
127
|
}
|
|
123
128
|
catch {
|
|
@@ -136,9 +141,41 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
136
141
|
threadId = resolved.threadId;
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
|
-
|
|
144
|
+
// threadId === '' 是主会话的合法写法(`<peer>/main`),不能被当成缺参。
|
|
145
|
+
if (!peer || threadId === undefined || !request.name) {
|
|
140
146
|
return { ok: false, code: 'MISSING_ARGS', error: 'peer, threadId and name required' };
|
|
147
|
+
}
|
|
141
148
|
const result = await threadService.renameThread({ selfAid, peer, threadId, name: request.name });
|
|
149
|
+
// 改名通知(尽力而为,失败不影响本地改名):
|
|
150
|
+
// - 私聊主会话/子话题:通知对端 agent 或人类,便于其按名字选择话题。
|
|
151
|
+
// - 群子话题:发到群,所有成员同步。
|
|
152
|
+
// - 群主会话:不发(只有 admin/owner 能改,且它是本地对群本身的称呼)。
|
|
153
|
+
if (sendSystemMsg && result.changed && result.nameUpdatedAt) {
|
|
154
|
+
const entry = threadId ? threadService.findInIndex(selfAid, peer, threadId) : undefined;
|
|
155
|
+
const peerKind = entry?.peerKind ?? (request.peerKind ?? 'private');
|
|
156
|
+
const isGroupMain = peerKind === 'group' && !threadId;
|
|
157
|
+
if (!isGroupMain) {
|
|
158
|
+
try {
|
|
159
|
+
await sendSystemMsg({
|
|
160
|
+
from: selfAid,
|
|
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
|
+
}
|
|
142
179
|
return { ok: true, result };
|
|
143
180
|
}
|
|
144
181
|
case 'label': {
|
|
@@ -206,35 +243,36 @@ async function _executeThreadOperation(request, threadService, sendSystemMsg) {
|
|
|
206
243
|
if (!request.sessionId || !request.content)
|
|
207
244
|
return { ok: false, code: 'MISSING_ARGS', error: 'sessionId and content required' };
|
|
208
245
|
// 覆盖游标由 service 自动记录(写入时刻话题最后一条消息的 ts),调用方不用传。
|
|
209
|
-
const result = threadService.setSummary(selfAid, request.sessionId, request.content, request.ifRev);
|
|
246
|
+
const result = await threadService.setSummary(selfAid, request.sessionId, request.content, request.ifRev);
|
|
210
247
|
// 顺带改名:总结时往往正好把话题主题想清楚了,一次命令写完更省事。
|
|
211
248
|
// 调用方(CLI / slash)保证 name 一定存在;`-` 表示"保持原标题不改"。
|
|
212
|
-
// 主会话没有话题名可改(threadId 为空),忽略该参数而不是报错。
|
|
213
249
|
const rawName = typeof request.name === 'string' ? request.name.trim() : '';
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
250
|
+
const name = rawName && rawName !== '-' ? rawName : undefined;
|
|
251
|
+
if (!name)
|
|
252
|
+
return { ok: true, result };
|
|
253
|
+
// 解析改名目标:主会话(threadId 为空串)不进索引,靠 peer + sessionId 定位;
|
|
254
|
+
// 子话题优先用索引里的 peer/threadId,索引查不到时退回请求里带的 ref。
|
|
255
|
+
const indexed = threadService.readThreadIndex(selfAid).threads.find(t => t.sessionId === request.sessionId);
|
|
256
|
+
const peer = request.peer ?? indexed?.peer;
|
|
257
|
+
const threadId = request.threadId ?? indexed?.threadId;
|
|
258
|
+
if (!peer) {
|
|
259
|
+
// 摘要已写入,改名目标无从确定——如实返回原因,别静默丢弃。
|
|
260
|
+
return {
|
|
261
|
+
ok: true,
|
|
262
|
+
result: { ...result, renameError: '无法解析改名目标:缺少 peer' },
|
|
263
|
+
};
|
|
219
264
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
265
|
+
try {
|
|
266
|
+
const renamed = await threadService.renameThread({ selfAid, peer, threadId: threadId ?? '', name });
|
|
267
|
+
return { ok: true, result: { ...result, renamedTo: renamed.name, renamedChanged: renamed.changed } };
|
|
223
268
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// 摘要已写入,改名失败不回滚——如实返回,让调用方知道只有改名这步没成
|
|
231
|
-
return {
|
|
232
|
-
ok: true,
|
|
233
|
-
result: { ...result, renameError: e?.message ?? String(e) },
|
|
234
|
-
};
|
|
235
|
-
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
// 摘要已写入,改名失败不回滚——如实返回,让调用方知道只有改名这步没成
|
|
271
|
+
return {
|
|
272
|
+
ok: true,
|
|
273
|
+
result: { ...result, renameError: e?.message ?? String(e) },
|
|
274
|
+
};
|
|
236
275
|
}
|
|
237
|
-
return { ok: true, result };
|
|
238
276
|
}
|
|
239
277
|
case 'summary-log': {
|
|
240
278
|
if (!request.sessionId)
|