evolcore 0.0.28 → 0.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/bin/ec-safe-output.js +270 -54
- package/dist/agents/baseagent-availability.js +37 -1
- package/dist/agents/baseagent.js +19 -9
- package/dist/agents/claude-runner.js +171 -13
- package/dist/agents/codex/app-server-client.js +6 -2
- package/dist/agents/codex/diagnostics.js +4 -3
- package/dist/agents/codex-runner.js +396 -95
- package/dist/agents/gemini-runner.js +35 -17
- package/dist/aun/group/history.js +130 -90
- package/dist/aun/group/operations.js +1 -3
- package/dist/aun/menu/transport.js +5 -3
- package/dist/aun/msg/managed-operation.js +70 -0
- package/dist/aun/msg/p2p.js +1 -3
- package/dist/aun/msg/upload.js +2 -2
- package/dist/aun/outbox.js +1 -37
- package/dist/channels/aun.js +249 -89
- package/dist/channels/daemon.js +18 -1
- package/dist/channels/feishu.js +1 -2
- package/dist/cli/agent-command.js +23 -3
- package/dist/cli/agent-create.js +68 -2
- package/dist/cli/agent.js +12 -2
- package/dist/cli/aun-commands.js +87 -13
- package/dist/cli/cli-argv.js +26 -0
- package/dist/cli/daemon-commands.js +40 -10
- package/dist/cli/data-command.js +4 -4
- package/dist/cli/handoff-command.js +2 -0
- package/dist/cli/index.js +18 -1
- package/dist/cli/managed-command-guard.js +10 -0
- package/dist/cli/memory-command.js +441 -0
- package/dist/cli/restart-monitor.js +62 -6
- package/dist/cli/skill-command.js +41 -0
- package/dist/cli/task-context.js +2 -23
- package/dist/cli/trigger-command.js +9 -1
- package/dist/cli/watch-msg-diag.js +209 -0
- package/dist/config/config-field-policy.js +52 -3
- package/dist/config/config-manager.js +17 -4
- package/dist/config/contact-book-store.js +2 -2
- package/dist/config/mention-mode.js +7 -2
- package/dist/config/merge.js +28 -9
- package/dist/config/resolved-config-op.js +1 -1
- package/dist/config/runtime-env.js +137 -0
- package/dist/config/schema-registry.js +11 -1
- package/dist/config-store.js +10 -10
- package/dist/core/audit/event-key.js +3 -1
- package/dist/core/audit/log-integrity.js +4 -0
- package/dist/core/auth/agent-delegation.js +2 -1
- package/dist/core/auth/auth-gateway.js +13 -6
- package/dist/core/auth/authorization-audit.js +54 -19
- package/dist/core/auth/operation-authorizer.js +17 -0
- package/dist/core/auth/operation-catalog.js +9 -0
- package/dist/core/causation/context.js +5 -1
- package/dist/core/command/agent-control.js +1 -2
- package/dist/core/command/command-handler.js +51 -23
- package/dist/core/command/memory-slash.js +270 -0
- package/dist/core/command/menu-catalog.js +1 -0
- package/dist/core/command/menu-handler.js +315 -60
- package/dist/core/command/menu-protocol.js +42 -4
- package/dist/core/command/slash-gate.js +10 -10
- package/dist/core/command/slash-handler.js +206 -64
- package/dist/core/data-migration.js +282 -505
- package/dist/core/event-catalog.js +26 -0
- package/dist/core/evolagent-registry.js +6 -0
- package/dist/core/evolagent.js +0 -10
- package/dist/core/handoff/store.js +1 -20
- package/dist/core/inference/text-inference.js +57 -1
- package/dist/core/memory/consolidation-context.js +505 -0
- package/dist/core/memory/consolidation-ops-records.js +124 -0
- package/dist/core/memory/consolidation-trigger.js +394 -0
- package/dist/core/memory/index-manager.js +308 -0
- package/dist/core/memory/launch-consolidation.js +395 -0
- package/dist/core/memory/memory-admin.js +234 -0
- package/dist/core/memory/memory-document.js +100 -0
- package/dist/core/memory/memory-session-policy.js +41 -0
- package/dist/core/memory/memory-signature.js +257 -0
- package/dist/core/memory/memory-writer.js +432 -0
- package/dist/core/memory/staleness.js +117 -0
- package/dist/core/message/auto-mention-gate.js +454 -0
- package/dist/core/message/im-renderer.js +69 -16
- package/dist/core/message/message-bridge.js +287 -21
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +114 -27
- package/dist/core/message/pending-hints.js +329 -161
- package/dist/core/message/response-engine.js +567 -80
- package/dist/core/permission/ec-command-parser.js +1029 -80
- package/dist/core/permission/protected-paths.js +376 -126
- package/dist/core/permission/sandbox-runtime.js +40 -12
- package/dist/core/permission/tool-error-code.js +96 -13
- package/dist/core/permission/tool-policy.js +276 -23
- package/dist/core/role/runtime-policy.js +3 -7
- package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
- package/dist/core/session/session-fs-store.js +0 -76
- package/dist/core/session/session-idle-monitor.js +355 -0
- package/dist/core/session/session-manager.js +478 -17
- package/dist/core/session/session-mapper.js +33 -16
- package/dist/core/session/session-renew.js +217 -155
- package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
- package/dist/core/skill/global-watcher.js +282 -0
- package/dist/core/skill/index-generator.js +84 -0
- package/dist/core/skill/startup.js +73 -0
- package/dist/core/skill/switch-resolver.js +115 -0
- package/dist/core/task/task-activity-store.js +219 -0
- package/dist/eck/kit-renderer.js +30 -3
- package/dist/eck/manifest-engine.js +26 -4
- package/dist/index.js +708 -18
- package/dist/ipc.js +564 -2
- package/dist/paths.js +30 -9
- package/dist/response-system/engines/v1/proactive-flow.js +116 -15
- package/dist/response-system/modes/single-session/index.js +3 -0
- package/dist/trigger/feedback.js +1 -0
- package/dist/trigger/manager.js +0 -10
- package/dist/trigger/scheduler.js +11 -0
- package/dist/trigger/validation.js +22 -0
- package/dist/utils/avatar-upload.js +59 -18
- package/dist/utils/error-dict.json +6 -3
- package/dist/utils/error-utils.js +27 -0
- package/dist/utils/logger.js +32 -10
- package/dist/utils/media-cache.js +84 -0
- package/dist/utils/msg-diag.js +97 -0
- package/dist/utils/welcome.js +37 -8
- package/kits/docs/AUTHORING.md +314 -0
- package/kits/docs/GUIDE.md +2 -0
- package/kits/docs/INDEX.md +2 -0
- package/kits/docs/aun/CHEATSHEET.md +4 -0
- package/kits/docs/aun/SDK.md +60 -0
- package/kits/docs/context-assembly.md +10 -6
- package/kits/docs/evolcore/INDEX.md +17 -3
- package/kits/docs/evolcore/agent-create.md +24 -0
- package/kits/docs/evolcore/agent.md +5 -2
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/ec-capabilities.md +364 -0
- package/kits/docs/evolcore/group.md +34 -18
- package/kits/docs/evolcore/memory-architecture.md +78 -0
- package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
- package/kits/docs/evolcore/memory-consolidation.md +263 -0
- package/kits/docs/evolcore/memory-tags.md +63 -0
- package/kits/docs/evolcore/memory.md +110 -0
- package/kits/docs/evolcore/menu-client.md +27 -11
- package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
- package/kits/docs/evolcore/menu-protocol.md +213 -0
- package/kits/docs/evolcore/msg.md +2 -0
- package/kits/docs/evolcore/trigger.md +7 -5
- package/kits/docs/path-registry.md +6 -8
- package/kits/docs/prompt-loading-architecture.md +5 -3
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_manifest.json +294 -88
- package/kits/eck_manifest.memory.json +257 -0
- package/kits/rules/03-identity.md +8 -8
- package/kits/rules/05-venue.md +4 -2
- package/kits/rules/06-channel.md +1 -1
- package/kits/schemas/_meta.json +276 -47
- package/kits/schemas/agent-config.schema.12.json +38 -2
- package/kits/schemas/agent-config.schema.13.json +591 -0
- package/kits/schemas/daemon.schema.6.json +9 -9
- package/kits/schemas/defaults.schema.5.json +29 -0
- package/kits/schemas/relation-config.schema.8.json +14 -1
- package/kits/schemas/relation-config.schema.9.json +129 -0
- package/kits/schemas/role-config.schema.1.json +3 -3
- package/kits/skills/.switches.json +3 -0
- package/kits/skills/ec-skill/SKILL.md +217 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
- package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
- package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
- package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
- package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
- package/kits/templates/decision/auto-mention.md +25 -0
- package/kits/templates/decision/session-renew.md +22 -0
- package/kits/templates/memory-consolidation/forget.md +104 -0
- package/kits/templates/memory-consolidation/generalize.md +76 -0
- package/kits/templates/memory-consolidation/notes.md +133 -0
- package/kits/templates/memory-consolidation/profile.md +58 -0
- package/kits/templates/memory-consolidation/promote.md +72 -0
- package/kits/templates/memory-consolidation/workflow.md +75 -0
- package/kits/templates/system-fragments/bootstrap.md +14 -5
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/group-history.md +6 -0
- package/kits/templates/system-fragments/identity.md +0 -3
- package/kits/templates/system-fragments/memory.md +79 -0
- package/kits/templates/system-fragments/reply.md +1 -0
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -6
- package/dist/trigger/legacy-session-history.js +0 -19
- package/kits/templates/system-fragments/group-unread-history.md +0 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { memoryHelpEntries } from './memory-slash.js';
|
|
1
2
|
import { getBaseagentSnapshot, loadedBaseagentIds } from '../../agents/baseagent-availability.js';
|
|
2
3
|
import { normalizePeer, writeScope } from '../model/config-scope.js';
|
|
3
4
|
import { formatPeerKey } from '../relation/peer-identity.js';
|
|
@@ -18,13 +19,14 @@ import { refreshManagedPackageVersions, VERSION_INFO_PACKAGES } from '../../util
|
|
|
18
19
|
import { resolveCliEntry, launchMonitorAndConfirm } from '../../utils/cross-platform.js';
|
|
19
20
|
import { cachedCliVersion } from '../../utils/cross-platform.js';
|
|
20
21
|
import { scanInstances } from '../../utils/instance-registry.js';
|
|
21
|
-
import {
|
|
22
|
+
import { loadDefaults, loadDaemonConfig } from '../../config-store.js';
|
|
22
23
|
import { WEB_PACKAGE_NAME } from '../../product.js';
|
|
23
24
|
import { read as cfgRead, resolveEffective, resolveEffectiveFieldWithSource, routeFieldPath, write as cfgWrite, ConfigTarget, } from '../../config/config-manager.js';
|
|
24
25
|
import { execAgentAction, execAgentQuery, execAgentOptions, execAidAvatar, resolveProjectPath } from './agent-control.js';
|
|
25
26
|
import { gatewayList, gatewayUpdate, gatewayDelete, gatewayTest, gatewayModels, gatewaySetPrice, gatewaySyncEnv } from '../../config/gateway-config.js';
|
|
26
27
|
import { displaySessionTitle } from '../session/session-title.js';
|
|
27
|
-
import {
|
|
28
|
+
import { buildTranscriptTurns } from '../session/session-transcript.js';
|
|
29
|
+
import { buildForkHistorySnapshot, pageForkHistory } from '../session/session-manager.js';
|
|
28
30
|
import { isCapabilityType, listCapabilityOptions, queryCapabilityTypes, resolveCapabilityContext, updateCapabilityPolicy, } from '../capability/capability-manager.js';
|
|
29
31
|
import { normalizeCliArgv, parseCliIntent, parseLegacyCliCommand, validateCliArgv, withDefaultRelationContext } from './cli-intent-parser.js';
|
|
30
32
|
import { auditCommandAuthorization, auditFullAccessEvent, hashArgv } from '../auth/authorization-audit.js';
|
|
@@ -41,18 +43,20 @@ import { SYSTEM_CONTROL_CHANNEL } from '../system-channels.js';
|
|
|
41
43
|
import { logger } from '../../utils/logger.js';
|
|
42
44
|
import { shouldSuppressRealRestart } from '../../utils/restart-safety.js';
|
|
43
45
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../auth/agent-delegation.js';
|
|
44
|
-
import { evolMenuResponseTransportMetadata, menuFailure, menuCommandForName, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest, } from './menu-protocol.js';
|
|
46
|
+
import { evolMenuResponseTransportMetadata, menuFailure, menuCommandForName, menuSuccess, normalizeMenuError, validateTaskActivityQueryArgs, validateConfigWriteScope, validateMenuRequest, } from './menu-protocol.js';
|
|
45
47
|
import { parseMenuClientProtectedHeaders } from './menu-protocol.js';
|
|
48
|
+
import { TaskActivityStore } from '../task/task-activity-store.js';
|
|
46
49
|
import { remoteAccessService, validateAndRenewRemoteAccessToken } from '../auth/remote-access-store.js';
|
|
47
50
|
import { listDiagnosticLogOptions, listRawConfigResources, queryDiagnosticLogs, readRawConfigResource, } from './daemon-diagnostics.js';
|
|
48
51
|
import { roleMenuAction, roleMenuOperation, roleMenuOptions, roleMenuQuery, roleMenuUpdate, } from './role-menu.js';
|
|
49
52
|
import { handleConnectMenu, connectMenuOperation } from './connect-menu.js';
|
|
50
53
|
import { groupMenuAuthorizationArgs, groupMenuIntent, handleGroupMenu, } from './group-menu.js';
|
|
51
|
-
import { getMenuCatalog, execMenuValueBatch, resolveExplicitMenuExecutionContext, validateMenuCatalogArgs, validateMenuValueBatchArgs } from './menu-catalog.js';
|
|
54
|
+
import { getMenuCatalog, execMenuValueBatch, resolveExplicitMenuExecutionContext, resolveMenuCatalogContext, validateMenuCatalogArgs, validateMenuValueBatchArgs } from './menu-catalog.js';
|
|
52
55
|
import { probeSessionActivity } from './slash-gate.js';
|
|
53
56
|
import { dataRootFailure, dataRootResolver } from './data-root-resolver.js';
|
|
54
57
|
import { agentStorageMenuService, isAgentStorageMenuRequest } from './agent-storage-menu.js';
|
|
55
|
-
import {
|
|
58
|
+
import { MAX_AUN_ATTACHMENT_SIZE } from '../../aun/msg/upload.js';
|
|
59
|
+
import { normalizeTopicLabel, normalizeTopicStatus, resolveTopicArchiveFilter, } from '../session/session-mapper.js';
|
|
56
60
|
/**
|
|
57
61
|
* 获取 baseagent CLI 的版本号(claude/gemini/codex)。
|
|
58
62
|
* 失败返回 null(命令不存在或执行失败)。
|
|
@@ -62,8 +66,8 @@ function getBaseagentVersion(cmd) {
|
|
|
62
66
|
}
|
|
63
67
|
const allEfforts = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
64
68
|
const PERMISSION_MODE_KEYS = PUBLIC_PERMISSION_MODES;
|
|
65
|
-
/** menu file: fetch 文件大小上限(与 /file 一致) */
|
|
66
|
-
const FILE_FETCH_MAX_SIZE =
|
|
69
|
+
/** menu file: fetch 文件大小上限(与 /file 和 AUN sendFile 一致) */
|
|
70
|
+
const FILE_FETCH_MAX_SIZE = MAX_AUN_ATTACHMENT_SIZE;
|
|
67
71
|
/** menu file: query 的 sha256 仅对 ≤ 2 MB 文件计算,超过返回 null(见设计文档 §7 决策 2) */
|
|
68
72
|
const FILE_HASH_MAX_SIZE = 2 * 1024 * 1024;
|
|
69
73
|
const FILE_LIST_DEFAULT_LIMIT = 500;
|
|
@@ -184,12 +188,31 @@ function resolveCapabilityTarget(params) {
|
|
|
184
188
|
function getRenameName(args) {
|
|
185
189
|
return (args?.name ?? args?.title ?? args?.value ?? '').toString().trim();
|
|
186
190
|
}
|
|
191
|
+
export function buildTopicForkSourcePayload(session) {
|
|
192
|
+
const source = session.metadata?.forkSource;
|
|
193
|
+
if (!source)
|
|
194
|
+
return undefined;
|
|
195
|
+
return {
|
|
196
|
+
sourceThreadId: source.threadId,
|
|
197
|
+
sourceAssistantMessageId: source.assistantMessageId,
|
|
198
|
+
sourceTurn: source.turn,
|
|
199
|
+
createdAt: source.createdAt,
|
|
200
|
+
historyVersion: source.historyVersion ?? null,
|
|
201
|
+
historyCount: source.historyCount ?? null,
|
|
202
|
+
historyDigest: source.historyDigest ?? null,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
187
205
|
function buildSessionPayload(session, name) {
|
|
188
206
|
const payload = { id: session.id, name };
|
|
189
|
-
if (session.
|
|
190
|
-
payload.
|
|
207
|
+
if (session.label)
|
|
208
|
+
payload.label = session.label;
|
|
209
|
+
if (session.threadId)
|
|
210
|
+
payload.topicStatus = session.topicStatus ?? 'pending';
|
|
191
211
|
if (session.agentSessionId)
|
|
192
212
|
payload.agentSessionId = session.agentSessionId;
|
|
213
|
+
const forkSource = buildTopicForkSourcePayload(session);
|
|
214
|
+
if (forkSource)
|
|
215
|
+
payload.forkSource = forkSource;
|
|
193
216
|
return payload;
|
|
194
217
|
}
|
|
195
218
|
async function findMainSessionTarget(sessionManager, channel, channelId, target, activeSession) {
|
|
@@ -389,6 +412,11 @@ async function getAvailableEfforts(agent, model) {
|
|
|
389
412
|
const baseagent = agent?.baseagent || agent?.name;
|
|
390
413
|
return (TRANSPORT_EFFORTS[baseagent] ?? []);
|
|
391
414
|
}
|
|
415
|
+
function isDaemonControlSubject(subject) {
|
|
416
|
+
return subject.isDaemonOwner === true
|
|
417
|
+
|| subject.processRole === 'daemon-owner'
|
|
418
|
+
|| subject.processRole === 'daemon-service';
|
|
419
|
+
}
|
|
392
420
|
function modelDisplayLabel(agent, model) {
|
|
393
421
|
const full = agent.resolveModelId?.(model);
|
|
394
422
|
return full && full !== model ? `${model} (${full})` : model;
|
|
@@ -642,7 +670,7 @@ function resolveMenuMentionModeTarget(params) {
|
|
|
642
670
|
function readMenuMentionMode(target, fallback = null, fallbackSource = null) {
|
|
643
671
|
try {
|
|
644
672
|
const resolved = resolveEffectiveFieldWithSource(target.fieldPath, target.sel, { cache: true });
|
|
645
|
-
if (resolved.value === 'disabled' || resolved.value === 'mention-only') {
|
|
673
|
+
if (resolved.value === 'disabled' || resolved.value === 'mention-only' || resolved.value === 'auto') {
|
|
646
674
|
return { value: resolved.value, source: toMenuSource(resolved.source) };
|
|
647
675
|
}
|
|
648
676
|
}
|
|
@@ -943,12 +971,14 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
943
971
|
return intent('model.effort.current', modelScope);
|
|
944
972
|
if (cmdBase === '/chatmode')
|
|
945
973
|
return intent('chatmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
946
|
-
if (cmdBase === '/
|
|
974
|
+
if (cmdBase === '/mention')
|
|
947
975
|
return intent('mentionmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
948
976
|
if (cmdBase === '/perm')
|
|
949
977
|
return intent('permission.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
950
978
|
if (cmdBase === '/activity')
|
|
951
979
|
return intent('activity.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
980
|
+
if (cmdBase === '/task')
|
|
981
|
+
return intent('task.activity.read', 'relation');
|
|
952
982
|
if (cmdBase === '/observable')
|
|
953
983
|
return intent('observable.current', 'agent');
|
|
954
984
|
if (cmdBase === '/capability')
|
|
@@ -963,7 +993,8 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
963
993
|
return intent(fromControlChannel && !args?.aid ? 'agent.list' : 'agent.show', fromControlChannel ? 'control' : 'agent');
|
|
964
994
|
}
|
|
965
995
|
if (cmdBase === '/trigger')
|
|
966
|
-
return intent('
|
|
996
|
+
return intent(args?.view === 'history' ? 'trigger.history'
|
|
997
|
+
: args?.view === 'detail' ? 'trigger.show' : 'trigger.list', 'relation');
|
|
967
998
|
}
|
|
968
999
|
if (verb === 'options') {
|
|
969
1000
|
if (cmdBase === '/system')
|
|
@@ -988,7 +1019,7 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
988
1019
|
return intent('model.effort.current', modelScope);
|
|
989
1020
|
if (cmdBase === '/chatmode')
|
|
990
1021
|
return intent('chatmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
991
|
-
if (cmdBase === '/
|
|
1022
|
+
if (cmdBase === '/mention')
|
|
992
1023
|
return intent('mentionmode.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
993
1024
|
if (cmdBase === '/activity')
|
|
994
1025
|
return intent('activity.current', args?.scope === 'agent' ? 'agent' : 'relation');
|
|
@@ -1004,7 +1035,7 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
1004
1035
|
return intent('model.effort', modelScope, { effort: value });
|
|
1005
1036
|
if (cmdBase === '/chatmode')
|
|
1006
1037
|
return intent('chatmode.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
|
|
1007
|
-
if (cmdBase === '/
|
|
1038
|
+
if (cmdBase === '/mention')
|
|
1008
1039
|
return intent('mentionmode.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
|
|
1009
1040
|
if (cmdBase === '/activity')
|
|
1010
1041
|
return intent('activity.update', args?.scope === 'agent' ? 'agent' : 'relation', { value });
|
|
@@ -1142,7 +1173,12 @@ async function authorizeMenuIntent(params) {
|
|
|
1142
1173
|
}
|
|
1143
1174
|
const source = params.source ?? 'menu';
|
|
1144
1175
|
intent.source = source;
|
|
1145
|
-
const decision = await authorizeOperation({
|
|
1176
|
+
const decision = await authorizeOperation({
|
|
1177
|
+
source,
|
|
1178
|
+
intent,
|
|
1179
|
+
subject,
|
|
1180
|
+
auditMetadata: subject.auditMetadata,
|
|
1181
|
+
});
|
|
1146
1182
|
if (!decision.allow) {
|
|
1147
1183
|
logger.warn(`[MenuAuth] denied operation=${intent.operation} scope=${intent.scope} source=${source} selfAid=${subject.selfAid ?? '<none>'} actor=${subject.actorId ?? '<none>'} role=${subject.role} processRole=${subject.processRole} dataScope=${subject.dataScope} daemonOwner=${subject.isDaemonOwner} control=${subject.fromControlChannel} reason=${decision.reason}`);
|
|
1148
1184
|
if (intent.operation.startsWith('role.') || intent.operation.startsWith('connect.')) {
|
|
@@ -1431,7 +1467,7 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent', authSu
|
|
|
1431
1467
|
{ value: 'interactive', label: '交互模式', desc: '仅在收到消息时响应' },
|
|
1432
1468
|
{ value: 'proactive', label: '主动模式', desc: 'Agent 可主动推进任务' },
|
|
1433
1469
|
] } }] : []),
|
|
1434
|
-
...(canSetMentionmode ? [{ cmd: '/
|
|
1470
|
+
...(canSetMentionmode ? [{ cmd: '/mention', label: '切换 @ 处理模式', desc: '控制群聊消息过滤(仅@提及或全部响应)', next: { type: 'select', items: [
|
|
1435
1471
|
{ value: 'mention-only', label: '@ 提及', desc: '仅在被 @ 提及时响应' },
|
|
1436
1472
|
{ value: 'disabled', label: '全部响应', desc: '响应群内所有消息' },
|
|
1437
1473
|
] } }] : []),
|
|
@@ -1496,6 +1532,26 @@ export function getMenuItems(role, chatType = 'private', scope = 'agent', authSu
|
|
|
1496
1532
|
]
|
|
1497
1533
|
});
|
|
1498
1534
|
}
|
|
1535
|
+
// 记忆管理(紧跟权限管理):条目与 evolhelp 同源,无参数的一点即执行,需要 slug 的弹输入框
|
|
1536
|
+
const memoryOwnerAid = authSubject?.selfAid
|
|
1537
|
+
? this?.agentRegistry?.get?.(authSubject.selfAid)?.config?.owners?.[0]
|
|
1538
|
+
: undefined;
|
|
1539
|
+
const memoryEntries = isControlScope ? [] : memoryHelpEntries({
|
|
1540
|
+
chatType: normalizedChatType,
|
|
1541
|
+
role,
|
|
1542
|
+
isAdmin,
|
|
1543
|
+
isOwnerSelf: isOwner && !!memoryOwnerAid && authSubject?.principalId === memoryOwnerAid,
|
|
1544
|
+
});
|
|
1545
|
+
if (memoryEntries.length > 0) {
|
|
1546
|
+
items.push({
|
|
1547
|
+
group: '记忆管理',
|
|
1548
|
+
commands: memoryEntries.map(e => ({
|
|
1549
|
+
cmd: e.command,
|
|
1550
|
+
label: e.description,
|
|
1551
|
+
...(e.args ? { args: e.args, next: { type: 'text' } } : {}),
|
|
1552
|
+
})),
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1499
1555
|
items.push({
|
|
1500
1556
|
group: isAdmin ? '运维' : '其他',
|
|
1501
1557
|
commands: [
|
|
@@ -1697,10 +1753,9 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1697
1753
|
const isAdmin = isManagementRole(role) || subject.isDaemonOwner || subject.processRole === 'daemon-service';
|
|
1698
1754
|
if (!triggerScheduler)
|
|
1699
1755
|
return [];
|
|
1700
|
-
const list = triggerScheduler.list({ all: scope === 'all' })
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
.map((definition) => {
|
|
1756
|
+
const list = triggerScheduler.list({ all: scope === 'all' })
|
|
1757
|
+
.filter((definition) => this.canAccessTriggerDefinition(definition, userId ?? '', channel, isAdmin));
|
|
1758
|
+
return list.map((definition) => {
|
|
1704
1759
|
const view = this.definitionToTriggerView(definition, triggerScheduler);
|
|
1705
1760
|
return {
|
|
1706
1761
|
...view,
|
|
@@ -1724,7 +1779,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1724
1779
|
const agent = this.getAgent(channel, source.baseagent);
|
|
1725
1780
|
if (!agent.capabilities?.forkAtTurn || !agent.getSessionMessages || !agent.forkSessionAt)
|
|
1726
1781
|
return [];
|
|
1727
|
-
const turns =
|
|
1782
|
+
const turns = buildTranscriptTurns(await agent.getSessionMessages(source.agentSessionId, source.projectPath));
|
|
1728
1783
|
return turns.map((turn) => ({
|
|
1729
1784
|
value: turn.assistantUuid,
|
|
1730
1785
|
label: `#${turn.index} ${turn.userContent}`,
|
|
@@ -1735,13 +1790,17 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1735
1790
|
userMessageId: turn.userUuid,
|
|
1736
1791
|
userTextHash: turn.userTextHash,
|
|
1737
1792
|
assistantTextHashes: turn.assistantTextHashes,
|
|
1793
|
+
assistantTextAnchors: turn.assistantTextAnchors,
|
|
1794
|
+
toolResultAnchors: turn.toolResultAnchors,
|
|
1795
|
+
forkAtMessage: agent.capabilities.forkAtMessage === true,
|
|
1796
|
+
forkHistoryVersion: 1,
|
|
1738
1797
|
}));
|
|
1739
1798
|
}
|
|
1740
|
-
const
|
|
1741
|
-
let
|
|
1742
|
-
if (
|
|
1799
|
+
const hasLabel = args && (Object.hasOwn(args, 'label') || Object.hasOwn(args, 'lable'));
|
|
1800
|
+
let label;
|
|
1801
|
+
if (hasLabel) {
|
|
1743
1802
|
try {
|
|
1744
|
-
|
|
1803
|
+
label = normalizeTopicLabel(Object.hasOwn(args, 'label') ? args.label : args.lable);
|
|
1745
1804
|
}
|
|
1746
1805
|
catch (error) {
|
|
1747
1806
|
throw { code: 'INVALID_ARGS', message: error.message };
|
|
@@ -1749,15 +1808,23 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1749
1808
|
}
|
|
1750
1809
|
let archiveFilter;
|
|
1751
1810
|
try {
|
|
1752
|
-
|
|
1753
|
-
archiveFilter = resolveTopicArchiveFilter(archived);
|
|
1811
|
+
archiveFilter = resolveTopicArchiveFilter(args?.archived === undefined && args?.topicStatus === 'archived' ? true : args?.archived);
|
|
1754
1812
|
}
|
|
1755
1813
|
catch (error) {
|
|
1756
1814
|
throw { code: 'INVALID_ARGS', message: error.message };
|
|
1757
1815
|
}
|
|
1758
1816
|
const sessions = await this.sessionManager.listSessions(channel, channelId, { archiveFilter });
|
|
1817
|
+
let topicStatus;
|
|
1818
|
+
if (args?.topicStatus !== undefined) {
|
|
1819
|
+
try {
|
|
1820
|
+
topicStatus = normalizeTopicStatus(args.topicStatus);
|
|
1821
|
+
}
|
|
1822
|
+
catch (error) {
|
|
1823
|
+
throw { code: 'INVALID_ARGS', message: error.message };
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1759
1826
|
return sessions
|
|
1760
|
-
.filter((s) => !!s.threadId && (!
|
|
1827
|
+
.filter((s) => !!s.threadId && (!hasLabel || s.label === label) && (!topicStatus || s.topicStatus === topicStatus))
|
|
1761
1828
|
.map((s) => this.buildTopicMenuItem(s));
|
|
1762
1829
|
}
|
|
1763
1830
|
if (cmd === '/s' || cmd === '/session' || cmd === '/del') {
|
|
@@ -1828,7 +1895,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1828
1895
|
agent = this.getAgent(channel, target.baseagent);
|
|
1829
1896
|
}
|
|
1830
1897
|
catch (error) {
|
|
1831
|
-
if (subject
|
|
1898
|
+
if (isDaemonControlSubject(subject)) {
|
|
1832
1899
|
return [{ value: target.baseagent, label: target.baseagent, selected: true }];
|
|
1833
1900
|
}
|
|
1834
1901
|
throw error;
|
|
@@ -1899,7 +1966,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1899
1966
|
agent = this.getAgent(channel, target.baseagent);
|
|
1900
1967
|
}
|
|
1901
1968
|
catch (error) {
|
|
1902
|
-
if (subject
|
|
1969
|
+
if (isDaemonControlSubject(subject)) {
|
|
1903
1970
|
return ['low', 'medium', 'high', 'auto'].map(e => ({ value: e, label: e === 'auto' ? 'auto (SDK默认)' : e }));
|
|
1904
1971
|
}
|
|
1905
1972
|
throw error;
|
|
@@ -1931,7 +1998,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1931
1998
|
{ value: 'proactive', label: '主动模式', selected: currentMode === 'proactive' },
|
|
1932
1999
|
];
|
|
1933
2000
|
}
|
|
1934
|
-
if (cmd === '/
|
|
2001
|
+
if (cmd === '/mention') {
|
|
1935
2002
|
const target = resolveMenuMentionModeTarget.call(this, {
|
|
1936
2003
|
args,
|
|
1937
2004
|
session,
|
|
@@ -1949,6 +2016,7 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1949
2016
|
return [
|
|
1950
2017
|
{ value: 'mention-only', label: '@提及时响应', selected: currentMode === 'mention-only' },
|
|
1951
2018
|
{ value: 'disabled', label: '所有消息响应', selected: currentMode === 'disabled' },
|
|
2019
|
+
{ value: 'auto', label: '根据上下文自动判断', selected: currentMode === 'auto' },
|
|
1952
2020
|
];
|
|
1953
2021
|
}
|
|
1954
2022
|
if (cmd === '/perm') {
|
|
@@ -2062,6 +2130,59 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2062
2130
|
explicitChatType, channel, channelId, userId, fromControlChannel, source,
|
|
2063
2131
|
});
|
|
2064
2132
|
}
|
|
2133
|
+
if (cmdBase === '/task') {
|
|
2134
|
+
const invalid = validateTaskActivityQueryArgs(args);
|
|
2135
|
+
if (invalid)
|
|
2136
|
+
return { error: invalid.message, code: invalid.code };
|
|
2137
|
+
if (fromControlChannel || !session || !subject.selfAid || !subject.actorId || !subject.allowAccess) {
|
|
2138
|
+
return { error: 'Task is not available in this session', code: 'FORBIDDEN' };
|
|
2139
|
+
}
|
|
2140
|
+
try {
|
|
2141
|
+
let boundSubject = subject;
|
|
2142
|
+
if (session.chatType === 'group') {
|
|
2143
|
+
const groupId = session.metadata?.groupId || session.channelId;
|
|
2144
|
+
const context = await resolveMenuCatalogContext({
|
|
2145
|
+
subject,
|
|
2146
|
+
channelType: subject.channelType || channel.split('#')[0],
|
|
2147
|
+
channelScope: 'agent',
|
|
2148
|
+
channelId,
|
|
2149
|
+
request: { kind: 'group_relation', groupId },
|
|
2150
|
+
});
|
|
2151
|
+
if (context.groupId !== groupId)
|
|
2152
|
+
return { error: 'Task is not available in this group', code: 'FORBIDDEN' };
|
|
2153
|
+
boundSubject = context.subject;
|
|
2154
|
+
}
|
|
2155
|
+
const authDenied = await authorizeMenuIntent.call(this, {
|
|
2156
|
+
intent: buildMenuIntent('query', cmdBase, args, undefined, undefined, false),
|
|
2157
|
+
identity, subject: boundSubject, session, explicitChatType, channel, channelId,
|
|
2158
|
+
userId, fromControlChannel, source,
|
|
2159
|
+
});
|
|
2160
|
+
if (authDenied)
|
|
2161
|
+
return authDenied;
|
|
2162
|
+
const store = new TaskActivityStore(subject.selfAid);
|
|
2163
|
+
const meta = store.getMeta(args.taskId);
|
|
2164
|
+
if (!meta)
|
|
2165
|
+
return { error: 'Task not found', code: 'NOT_FOUND' };
|
|
2166
|
+
const groupId = session.metadata?.groupId || session.channelId;
|
|
2167
|
+
if (meta.selfAid !== subject.selfAid || meta.sessionId !== session.id
|
|
2168
|
+
|| meta.channel !== (subject.channelType || channel.split('#')[0])
|
|
2169
|
+
|| meta.chatType !== session.chatType
|
|
2170
|
+
|| (meta.chatType === 'group'
|
|
2171
|
+
? meta.groupId !== groupId || meta.channelId !== groupId
|
|
2172
|
+
: meta.peerId !== subject.actorId || meta.channelId !== subject.actorId)) {
|
|
2173
|
+
return { error: 'Task is not available in this session', code: 'FORBIDDEN' };
|
|
2174
|
+
}
|
|
2175
|
+
return { data: args.view === 'activities'
|
|
2176
|
+
? store.readList(args.taskId, args.offset ?? 0, args.limit ?? 50)
|
|
2177
|
+
: store.readDetail(args.taskId, args.activityId) };
|
|
2178
|
+
}
|
|
2179
|
+
catch (error) {
|
|
2180
|
+
if (error?.code === 'INVALID_CONTEXT') {
|
|
2181
|
+
return { error: 'Task is not available in this group', code: 'FORBIDDEN' };
|
|
2182
|
+
}
|
|
2183
|
+
return menuResultFailure(error);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2065
2186
|
const authDenied = await authorizeMenuIntent.call(this, {
|
|
2066
2187
|
intent: buildMenuIntent('query', cmdBase, args, undefined, undefined, fromControlChannel),
|
|
2067
2188
|
identity,
|
|
@@ -2076,6 +2197,40 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2076
2197
|
});
|
|
2077
2198
|
if (authDenied)
|
|
2078
2199
|
return authDenied;
|
|
2200
|
+
if (cmdBase === '/trigger') {
|
|
2201
|
+
if (args?.view !== 'detail' && args?.view !== 'history') {
|
|
2202
|
+
return { error: 'Unsupported trigger query view', code: 'INVALID_ARGS' };
|
|
2203
|
+
}
|
|
2204
|
+
const triggerId = args?.triggerId;
|
|
2205
|
+
if (typeof triggerId !== 'string' || !triggerId.trim()) {
|
|
2206
|
+
return { error: 'triggerId is required', code: 'INVALID_ARGS' };
|
|
2207
|
+
}
|
|
2208
|
+
const limit = args?.limit ?? 50;
|
|
2209
|
+
if (args.view === 'history' && (!Number.isInteger(limit) || limit < 1 || limit > 200)) {
|
|
2210
|
+
return { error: 'limit must be between 1 and 200', code: 'INVALID_ARGS' };
|
|
2211
|
+
}
|
|
2212
|
+
const scheduler = this.getTriggerSchedulerForChannel?.(channel);
|
|
2213
|
+
if (!scheduler)
|
|
2214
|
+
return { error: 'Trigger is unavailable', code: 'NOT_SUPPORTED' };
|
|
2215
|
+
const role = identity.role;
|
|
2216
|
+
const isAdmin = isManagementRole(role) || subject.isDaemonOwner || subject.processRole === 'daemon-service';
|
|
2217
|
+
const definition = scheduler.list({ all: true }).find((item) => item.id === triggerId);
|
|
2218
|
+
if (!definition || !this.canAccessTriggerDefinition(definition, userId ?? '', channel, isAdmin)) {
|
|
2219
|
+
return { error: 'Trigger not found', code: 'NOT_FOUND' };
|
|
2220
|
+
}
|
|
2221
|
+
if (args.view === 'detail') {
|
|
2222
|
+
const details = scheduler.show(triggerId, { includeScriptPreview: false });
|
|
2223
|
+
return { data: {
|
|
2224
|
+
trigger: this.definitionToTriggerView(definition, scheduler),
|
|
2225
|
+
active: details.active,
|
|
2226
|
+
recentRuns: details.recentRuns,
|
|
2227
|
+
} };
|
|
2228
|
+
}
|
|
2229
|
+
return { data: {
|
|
2230
|
+
triggerId,
|
|
2231
|
+
runs: scheduler.recentRuns(triggerId, limit),
|
|
2232
|
+
} };
|
|
2233
|
+
}
|
|
2079
2234
|
if (cmdBase === '/agent-data-root') {
|
|
2080
2235
|
if (args && Object.keys(args).length > 0) {
|
|
2081
2236
|
return { error: 'agent-data-root does not accept args', code: 'INVALID_ARGUMENT' };
|
|
@@ -2272,6 +2427,69 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2272
2427
|
const topic = await this.sessionManager.getThreadSession(channel, channelId, target);
|
|
2273
2428
|
if (!topic)
|
|
2274
2429
|
return { error: '话题不存在', code: 'NOT_FOUND' };
|
|
2430
|
+
if (args?.view === 'fork-history') {
|
|
2431
|
+
const source = topic.metadata?.forkSource;
|
|
2432
|
+
if (topic.chatType !== 'private' || !source) {
|
|
2433
|
+
return { error: '该话题没有分叉历史', code: 'NOT_SUPPORTED' };
|
|
2434
|
+
}
|
|
2435
|
+
const limit = args?.limit === undefined ? 20 : args.limit;
|
|
2436
|
+
const before = args?.before;
|
|
2437
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 20
|
|
2438
|
+
|| (before !== undefined && (!Number.isInteger(before) || before < 1))) {
|
|
2439
|
+
return { error: '分叉历史分页参数无效', code: 'INVALID_ARGUMENT' };
|
|
2440
|
+
}
|
|
2441
|
+
try {
|
|
2442
|
+
let current = topic;
|
|
2443
|
+
let snapshot = this.sessionManager.readForkHistory(current);
|
|
2444
|
+
if (!snapshot) {
|
|
2445
|
+
const recovered = await this.sessionManager.attachForkHistory(topic.id);
|
|
2446
|
+
if (recovered) {
|
|
2447
|
+
current = recovered;
|
|
2448
|
+
snapshot = this.sessionManager.readForkHistory(current);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
if (!snapshot) {
|
|
2452
|
+
const candidate = async (session) => {
|
|
2453
|
+
if (!session?.agentSessionId || session.channel !== channel || session.channelId !== channelId)
|
|
2454
|
+
return undefined;
|
|
2455
|
+
const agent = this.getAgent(channel, session.baseagent);
|
|
2456
|
+
if (!agent.getSessionMessages)
|
|
2457
|
+
return undefined;
|
|
2458
|
+
try {
|
|
2459
|
+
return buildForkHistorySnapshot(await agent.getSessionMessages(session.agentSessionId, session.projectPath), source.assistantMessageId);
|
|
2460
|
+
}
|
|
2461
|
+
catch {
|
|
2462
|
+
return undefined;
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
const projected = await candidate(topic)
|
|
2466
|
+
?? await candidate(await this.sessionManager.getSessionById(source.sessionId));
|
|
2467
|
+
if (!projected)
|
|
2468
|
+
return { error: '无法从现有会话恢复分叉历史', code: 'INVALID_STATE' };
|
|
2469
|
+
current = await this.sessionManager.attachForkHistory(topic.id, projected) ?? topic;
|
|
2470
|
+
snapshot = this.sessionManager.readForkHistory(current);
|
|
2471
|
+
}
|
|
2472
|
+
if (!snapshot)
|
|
2473
|
+
return { error: '分叉历史未持久化', code: 'INVALID_STATE' };
|
|
2474
|
+
if (before !== undefined && before > snapshot.items.length) {
|
|
2475
|
+
return { error: '分叉历史分页游标无效', code: 'INVALID_ARGUMENT' };
|
|
2476
|
+
}
|
|
2477
|
+
const page = pageForkHistory(snapshot, before, limit);
|
|
2478
|
+
const forkSource = current.metadata?.forkSource;
|
|
2479
|
+
return { data: {
|
|
2480
|
+
threadId: current.threadId,
|
|
2481
|
+
sourceAssistantMessageId: source.assistantMessageId,
|
|
2482
|
+
historyVersion: 1,
|
|
2483
|
+
historyDigest: forkSource?.historyDigest,
|
|
2484
|
+
createdAt: source.createdAt,
|
|
2485
|
+
...page,
|
|
2486
|
+
} };
|
|
2487
|
+
}
|
|
2488
|
+
catch (error) {
|
|
2489
|
+
logger.warn(`[MenuHandler] Fork history read failed: ${error}`);
|
|
2490
|
+
return { error: '分叉历史读取失败,请稍后重试', code: 'INVALID_STATE' };
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2275
2493
|
if (args?.view === 'subtasks') {
|
|
2276
2494
|
return { data: { scope: 'retained-history', tasks: this.sessionManager.getRunnerTaskHistory(topic.id) } };
|
|
2277
2495
|
}
|
|
@@ -2293,7 +2511,8 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2293
2511
|
const data = {
|
|
2294
2512
|
threadId: topic.threadId,
|
|
2295
2513
|
name: topic.name || null,
|
|
2296
|
-
|
|
2514
|
+
label: topic.label || null,
|
|
2515
|
+
topicStatus: topic.topicStatus ?? 'pending',
|
|
2297
2516
|
agentSessionId: topic.agentSessionId || null,
|
|
2298
2517
|
status: isProcessing ? 'processing' : 'idle',
|
|
2299
2518
|
createdAt: topic.createdAt,
|
|
@@ -2415,7 +2634,7 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
2415
2634
|
},
|
|
2416
2635
|
};
|
|
2417
2636
|
}
|
|
2418
|
-
if (cmdBase === '/
|
|
2637
|
+
if (cmdBase === '/mention') {
|
|
2419
2638
|
const chatType = session?.chatType ?? explicitChatType ?? 'private';
|
|
2420
2639
|
if (chatType !== 'group') {
|
|
2421
2640
|
return { error: 'mentionMode 仅在群聊会话中有效', code: 'NOT_APPLICABLE' };
|
|
@@ -3086,8 +3305,8 @@ export async function execMenuUpdate(cmd, value, channel, channelId, userId, ove
|
|
|
3086
3305
|
},
|
|
3087
3306
|
};
|
|
3088
3307
|
}
|
|
3089
|
-
if (cmdBase === '/
|
|
3090
|
-
if (arg !== 'mention-only' && arg !== 'disabled' && arg !== 'clear') {
|
|
3308
|
+
if (cmdBase === '/mention') {
|
|
3309
|
+
if (arg !== 'mention-only' && arg !== 'disabled' && arg !== 'auto' && arg !== 'clear') {
|
|
3091
3310
|
return { error: `无效模式: ${arg}`, code: 'INVALID_VALUE' };
|
|
3092
3311
|
}
|
|
3093
3312
|
const chatType = session?.chatType;
|
|
@@ -3521,8 +3740,6 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3521
3740
|
if (!trigger)
|
|
3522
3741
|
return { error: '触发器不存在或无权限', code: 'NOT_FOUND' };
|
|
3523
3742
|
if (action === 'enable' && trigger.execution.permissionMode === 'fullaccess') {
|
|
3524
|
-
if (!isFullAccessEnabled())
|
|
3525
|
-
return { error: 'fullaccess 功能未启用', code: 'NOT_SUPPORTED' };
|
|
3526
3743
|
if (!subject.isDaemonOwner && subject.processRole !== 'daemon-service')
|
|
3527
3744
|
return { error: '只有 daemon-owner 可以启用 fullaccess Trigger', code: 'FORBIDDEN' };
|
|
3528
3745
|
if (subject.canApprovePersistentFullAccess === false) {
|
|
@@ -3619,11 +3836,11 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3619
3836
|
return { error: `不支持的 trigger action: ${action}`, code: 'INVALID_ARGS' };
|
|
3620
3837
|
}
|
|
3621
3838
|
if (cmdBase === '/topic') {
|
|
3622
|
-
const
|
|
3623
|
-
let
|
|
3624
|
-
if (
|
|
3839
|
+
const hasLabel = args && (Object.hasOwn(args, 'label') || Object.hasOwn(args, 'lable'));
|
|
3840
|
+
let label;
|
|
3841
|
+
if (hasLabel && (action === 'fork' || action === 'label')) {
|
|
3625
3842
|
try {
|
|
3626
|
-
|
|
3843
|
+
label = normalizeTopicLabel(Object.hasOwn(args, 'label') ? args.label : args.lable);
|
|
3627
3844
|
}
|
|
3628
3845
|
catch (error) {
|
|
3629
3846
|
return { error: error.message, code: 'INVALID_ARGS' };
|
|
@@ -3652,7 +3869,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3652
3869
|
: null;
|
|
3653
3870
|
if (requestId
|
|
3654
3871
|
&& forkSource?.requestId === requestId
|
|
3655
|
-
&& forkSource.assistantMessageId === sourceAssistantMessageId
|
|
3872
|
+
&& (forkSource.requestedAssistantMessageId ?? forkSource.assistantMessageId) === sourceAssistantMessageId
|
|
3656
3873
|
&& forkSource.threadId === requestedSourceThreadId) {
|
|
3657
3874
|
return {
|
|
3658
3875
|
data: {
|
|
@@ -3704,25 +3921,33 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3704
3921
|
try {
|
|
3705
3922
|
const executeFork = async (current, currentAgent) => {
|
|
3706
3923
|
const messages = await currentAgent.getSessionMessages(current.agentSessionId, current.projectPath);
|
|
3707
|
-
const turns =
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
if (
|
|
3712
|
-
return { error: '
|
|
3713
|
-
const
|
|
3924
|
+
const turns = buildTranscriptTurns(messages);
|
|
3925
|
+
const matchingTurns = turns.filter(turn => turn.userUuid === sourceAssistantMessageId
|
|
3926
|
+
|| turn.assistantUuids?.includes(sourceAssistantMessageId)
|
|
3927
|
+
|| turn.toolResultAnchors.some(anchor => anchor.messageId === sourceAssistantMessageId));
|
|
3928
|
+
if (matchingTurns.length !== 1)
|
|
3929
|
+
return { error: '分叉点已失效或无法唯一定位,请刷新历史后重试', code: 'NOT_FOUND' };
|
|
3930
|
+
const forkTurn = matchingTurns[0];
|
|
3931
|
+
if (!currentAgent.capabilities?.forkAtMessage
|
|
3932
|
+
&& sourceAssistantMessageId !== forkTurn.assistantUuid) {
|
|
3933
|
+
return { error: '当前 Agent 无法在所选消息处精确分叉', code: 'NOT_SUPPORTED' };
|
|
3934
|
+
}
|
|
3935
|
+
const actualSourceMessageId = sourceAssistantMessageId;
|
|
3936
|
+
const historySnapshot = buildForkHistorySnapshot(messages, actualSourceMessageId);
|
|
3937
|
+
const forkedAgentSessionId = await currentAgent.forkSessionAt(current.agentSessionId, current.projectPath, actualSourceMessageId, name || undefined, { sessionId: current.id, permissionMode: subject.permissionMode, role: subject.role });
|
|
3714
3938
|
const topic = await this.sessionManager.createForkedThreadSession(current, forkedAgentSessionId, targetThreadId, {
|
|
3715
3939
|
name: name || undefined,
|
|
3716
|
-
...(
|
|
3940
|
+
...(hasLabel ? { label: label ?? null } : {}),
|
|
3717
3941
|
creatorPeerId: userId,
|
|
3718
|
-
sourceAssistantMessageId:
|
|
3942
|
+
sourceAssistantMessageId: actualSourceMessageId,
|
|
3719
3943
|
sourceTurn: forkTurn.index,
|
|
3944
|
+
historySnapshot,
|
|
3720
3945
|
requestId,
|
|
3721
3946
|
});
|
|
3722
3947
|
await currentAgent.updateSessionMetadata?.(forkedAgentSessionId, {
|
|
3723
3948
|
evolcoreSessionId: topic.id,
|
|
3724
3949
|
sourceSessionId: current.id,
|
|
3725
|
-
sourceAssistantMessageId:
|
|
3950
|
+
sourceAssistantMessageId: actualSourceMessageId,
|
|
3726
3951
|
sourceTurn: forkTurn.index,
|
|
3727
3952
|
}).catch((error) => {
|
|
3728
3953
|
logger.debug(`[MenuHandler] Topic fork metadata sync failed: ${error}`);
|
|
@@ -3733,7 +3958,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3733
3958
|
sourceSessionId: current.id,
|
|
3734
3959
|
name: topic.name,
|
|
3735
3960
|
threadId: topic.threadId,
|
|
3736
|
-
sourceAssistantMessageId:
|
|
3961
|
+
sourceAssistantMessageId: actualSourceMessageId,
|
|
3737
3962
|
sourceTurn: forkTurn.index,
|
|
3738
3963
|
});
|
|
3739
3964
|
return {
|
|
@@ -3745,7 +3970,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3745
3970
|
threadId: topic.threadId,
|
|
3746
3971
|
sourceSessionId: current.id,
|
|
3747
3972
|
sourceThreadId: current.threadId || null,
|
|
3748
|
-
sourceAssistantMessageId:
|
|
3973
|
+
sourceAssistantMessageId: actualSourceMessageId,
|
|
3749
3974
|
sourceTurn: forkTurn.index,
|
|
3750
3975
|
},
|
|
3751
3976
|
},
|
|
@@ -3834,7 +4059,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3834
4059
|
topicForkTargetsInFlight.delete(targetKey);
|
|
3835
4060
|
}
|
|
3836
4061
|
}
|
|
3837
|
-
if (action !== 'delete' && action !== 'rename' && action !== 'label') {
|
|
4062
|
+
if (action !== 'delete' && action !== 'rename' && action !== 'label' && action !== 'status') {
|
|
3838
4063
|
return { error: `不支持的 topic action: ${action}`, code: 'NOT_SUPPORTED' };
|
|
3839
4064
|
}
|
|
3840
4065
|
const target = (args?.target ?? '').toString().trim();
|
|
@@ -3843,8 +4068,17 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3843
4068
|
const renameName = action === 'rename' ? getRenameName(args) : '';
|
|
3844
4069
|
if (action === 'rename' && !renameName)
|
|
3845
4070
|
return { error: '缺少 args.name', code: 'MISSING_VALUE' };
|
|
3846
|
-
if (action === 'label' && !
|
|
3847
|
-
return { error: '缺少 args.
|
|
4071
|
+
if (action === 'label' && !hasLabel)
|
|
4072
|
+
return { error: '缺少 args.label', code: 'MISSING_VALUE' };
|
|
4073
|
+
let topicStatus;
|
|
4074
|
+
if (action === 'status') {
|
|
4075
|
+
try {
|
|
4076
|
+
topicStatus = normalizeTopicStatus(args?.topicStatus);
|
|
4077
|
+
}
|
|
4078
|
+
catch (error) {
|
|
4079
|
+
return { error: error.message, code: 'INVALID_ARGS' };
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
3848
4082
|
const topic = await this.sessionManager.getThreadSession(channel, channelId, target);
|
|
3849
4083
|
if (!topic)
|
|
3850
4084
|
return { error: '话题不存在', code: 'NOT_FOUND' };
|
|
@@ -3854,19 +4088,25 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3854
4088
|
? 'private'
|
|
3855
4089
|
: this.resolveMenuChatType(channel, channelId, explicitChatType);
|
|
3856
4090
|
if (!this.canDeleteTopic(identity.role, chatType, topic, userId)) {
|
|
3857
|
-
return { error: action === 'label' ? '无权限修改话题分类' : action === 'rename' ? '无权限重命名话题' : '无权限删除话题', code: 'FORBIDDEN' };
|
|
4091
|
+
return { error: action === 'label' ? '无权限修改话题分类' : action === 'status' ? '无权限修改话题状态' : action === 'rename' ? '无权限重命名话题' : '无权限删除话题', code: 'FORBIDDEN' };
|
|
3858
4092
|
}
|
|
3859
4093
|
if (action === 'label') {
|
|
3860
|
-
const success = await this.sessionManager.
|
|
4094
|
+
const success = await this.sessionManager.setTopicLabel(topic.id, label ?? null);
|
|
3861
4095
|
if (!success)
|
|
3862
4096
|
return { error: '话题不存在', code: 'NOT_FOUND' };
|
|
3863
4097
|
return {
|
|
3864
4098
|
data: {
|
|
3865
4099
|
action: 'label', success: true,
|
|
3866
|
-
topic: { ...buildSessionPayload(topic, topic.name || ''), threadId: topic.threadId,
|
|
4100
|
+
topic: { ...buildSessionPayload(topic, topic.name || ''), threadId: topic.threadId, label: label ?? null },
|
|
3867
4101
|
},
|
|
3868
4102
|
};
|
|
3869
4103
|
}
|
|
4104
|
+
if (action === 'status') {
|
|
4105
|
+
const success = await this.sessionManager.setTopicStatus(topic.id, topicStatus);
|
|
4106
|
+
if (!success)
|
|
4107
|
+
return { error: '话题不存在', code: 'NOT_FOUND' };
|
|
4108
|
+
return { data: { action: 'status', success: true, topic: { ...buildSessionPayload(topic, topic.name || ''), threadId: topic.threadId, topicStatus } } };
|
|
4109
|
+
}
|
|
3870
4110
|
if (action === 'rename') {
|
|
3871
4111
|
const newName = renameName;
|
|
3872
4112
|
const existing = await this.sessionManager.getSessionByName?.(channel, channelId, newName);
|
|
@@ -4513,6 +4753,14 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
4513
4753
|
ecwebManagement: context.source === 'ecweb',
|
|
4514
4754
|
canApprovePersistentFullAccess: context.canApprovePersistentFullAccess,
|
|
4515
4755
|
trustedProcessRole: context.source === 'ecweb' ? 'daemon-owner' : undefined,
|
|
4756
|
+
auditMetadata: context.source === 'ecweb'
|
|
4757
|
+
? {
|
|
4758
|
+
ecwebRequestId: context.ecwebRequestId,
|
|
4759
|
+
targetAgentAid: targetAid,
|
|
4760
|
+
endpoint: context.endpoint,
|
|
4761
|
+
logicalToolName: context.logicalToolName,
|
|
4762
|
+
}
|
|
4763
|
+
: undefined,
|
|
4516
4764
|
});
|
|
4517
4765
|
const trustedIdentity = trustedSubject.identity;
|
|
4518
4766
|
const cmd = name ? (menuCommandForName(name) ?? payload.cmd) : payload.cmd;
|
|
@@ -4660,6 +4908,10 @@ export async function execMenuForEcweb(payload, trusted) {
|
|
|
4660
4908
|
const userId = (trusted.actorAid && owners.includes(trusted.actorAid))
|
|
4661
4909
|
? trusted.actorAid
|
|
4662
4910
|
: owners[0] ?? (trusted.localDirect ? 'local-direct' : 'ecweb-remote');
|
|
4911
|
+
const ecwebRequestId = trusted.ecwebRequestId
|
|
4912
|
+
?? (id !== undefined && id !== null && String(id) ? String(id) : crypto.randomUUID());
|
|
4913
|
+
const logicalToolName = trusted.logicalToolName
|
|
4914
|
+
?? (typeof name === 'string' && name.trim() ? name.trim() : menuCommandToken(payload?.cmd) ?? payload?.type);
|
|
4663
4915
|
return execMenuForSystemControl.call(this, payload, {
|
|
4664
4916
|
source: 'ecweb',
|
|
4665
4917
|
actorAid: userId,
|
|
@@ -4671,6 +4923,9 @@ export async function execMenuForEcweb(payload, trusted) {
|
|
|
4671
4923
|
// approve persistent fullaccess without owner-bound token metadata.
|
|
4672
4924
|
canApprovePersistentFullAccess: true,
|
|
4673
4925
|
owners,
|
|
4926
|
+
ecwebRequestId,
|
|
4927
|
+
endpoint: trusted.endpoint ?? 'ECWeb menu.exec',
|
|
4928
|
+
logicalToolName,
|
|
4674
4929
|
});
|
|
4675
4930
|
}
|
|
4676
4931
|
/** Control AID entry: daemon-owner and valid ECWeb token modes share the same
|