evolcore 0.0.31 → 0.0.33
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 +35 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +51 -24
- package/dist/agents/ecagent-runner.js +9 -13
- package/dist/agents/gemini-runner.js +4 -0
- package/dist/aun/aid/store.js +0 -81
- package/dist/aun/group/fs-download.js +1 -10
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/managed-operation.js +130 -32
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/aun/service-proxy.js +12 -18
- package/dist/aun/storage/index.js +29 -0
- package/dist/channels/aun.js +38 -137
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +64 -31
- package/dist/cli/agent-create.js +43 -24
- package/dist/cli/agent.js +16 -70
- package/dist/cli/aun-commands.js +512 -32
- package/dist/cli/contact.js +8 -2
- package/dist/cli/ctl-command.js +1 -2
- package/dist/cli/daemon-commands.js +82 -13
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/diag.js +2 -2
- package/dist/cli/handoff-command.js +18 -5
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +93 -12
- package/dist/cli/model.js +14 -138
- package/dist/cli/queue-command.js +51 -0
- package/dist/cli/response.js +5 -54
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +85 -3
- package/dist/cli/stats.js +10 -4
- package/dist/cli/task-command.js +1 -0
- package/dist/cli/task-context.js +16 -0
- package/dist/cli/thread-command.js +620 -0
- package/dist/cli/trigger-command.js +76 -50
- package/dist/config/builtin-roles.js +9 -7
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/agent-ipc-registration.js +65 -22
- package/dist/core/auth/agent-delegation.js +42 -2
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/delegation-authorization.js +1 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +44 -3
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/command-handler.js +224 -45
- package/dist/core/command/diag-service.js +5 -1
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/ipc-registration.js +38 -0
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +210 -9
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +207 -8
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +131 -3
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +379 -69
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +126 -7
- package/dist/core/permission/sandbox-runtime.js +14 -1
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/task/task-recovery-service.js +2 -0
- package/dist/core/task/task-resume-operation.js +11 -0
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +10 -7
- package/dist/ipc.js +234 -17
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +59 -29
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/ipc-service.js +49 -35
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/script-sandbox.js +58 -77
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/AUTHORING.md +1 -1
- package/kits/docs/channels/aun.md +8 -0
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +3 -2
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/evolcore/trigger.md +2 -2
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/01-overview.md +0 -9
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +7 -1
- package/kits/rules/07-memory-and-thread.md +202 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +66 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +2 -1
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
|
@@ -4,6 +4,8 @@ import { authorizeOperation, buildAuthSubject } from './auth-gateway.js';
|
|
|
4
4
|
import { formatPeerKey, parsePeerKey } from '../relation/peer-identity.js';
|
|
5
5
|
import { logger } from '../../utils/logger.js';
|
|
6
6
|
import { canonicalizeEvolcoreCommandArgv } from '../permission/ec-command-parser.js';
|
|
7
|
+
import { MAIN_CLI_BIN } from '../../product.js';
|
|
8
|
+
import { DebugDelegationRegistry, DEBUG_TOKEN_PREFIX } from './debug-context.js';
|
|
7
9
|
export const AGENT_DELEGATION_TOKEN_ENV = 'EVOLCORE_DELEGATION_TOKEN';
|
|
8
10
|
export const CODEX_SESSION_TOKEN_ENV = 'EVOLCORE_CODEX_SESSION_TOKEN';
|
|
9
11
|
export const AGENT_DELEGATION_COMMAND_TTL_MS = 5 * 60_000;
|
|
@@ -23,9 +25,11 @@ export class AgentDelegationRegistry {
|
|
|
23
25
|
grantsByHash = new Map();
|
|
24
26
|
activeHashBySession = new Map();
|
|
25
27
|
approvedCommands = new Map();
|
|
28
|
+
debugRegistry = new DebugDelegationRegistry();
|
|
26
29
|
getRuntimeEpoch() {
|
|
27
30
|
return this.runtimeEpoch;
|
|
28
31
|
}
|
|
32
|
+
/** 发行正常 task delegation token */
|
|
29
33
|
issue(input) {
|
|
30
34
|
if (input.executionIdentity && !isValidFullAccessExecutionIdentity(input.executionIdentity)) {
|
|
31
35
|
throw new Error('invalid fullaccess task execution identity');
|
|
@@ -37,11 +41,21 @@ export class AgentDelegationRegistry {
|
|
|
37
41
|
this.activeHashBySession.set(input.sessionId, tokenHash);
|
|
38
42
|
return token;
|
|
39
43
|
}
|
|
44
|
+
/** 发行 debug token(dbg: 前缀),仅 debug 模式下调用 */
|
|
45
|
+
issueDebug(input) {
|
|
46
|
+
return this.debugRegistry.issue(input);
|
|
47
|
+
}
|
|
40
48
|
validate(token, sessionId, commandHash) {
|
|
49
|
+
if (token?.startsWith(DEBUG_TOKEN_PREFIX)) {
|
|
50
|
+
return this.debugRegistry.validate(token, sessionId);
|
|
51
|
+
}
|
|
41
52
|
return this.validateToken(token, sessionId, commandHash, true);
|
|
42
53
|
}
|
|
43
54
|
/** Authenticate a CLI context lookup without spending its approved operation. */
|
|
44
55
|
validateForContext(token, sessionId, commandHash) {
|
|
56
|
+
if (token?.startsWith(DEBUG_TOKEN_PREFIX)) {
|
|
57
|
+
return this.debugRegistry.validate(token, sessionId);
|
|
58
|
+
}
|
|
45
59
|
return this.validateToken(token, sessionId, commandHash, false);
|
|
46
60
|
}
|
|
47
61
|
validateToken(token, sessionId, commandHash, consumeApproval) {
|
|
@@ -210,7 +224,18 @@ export function authorizeDelegatedAunMsgSend(registry, input) {
|
|
|
210
224
|
fromControlChannel: false,
|
|
211
225
|
...trustedExecutionIdentityInput(grant),
|
|
212
226
|
});
|
|
213
|
-
|
|
227
|
+
// 跨话题发送:目标 relation 由话题推断,按目标话题的会话类型和 relation 校验
|
|
228
|
+
// 「当前对端/当前群」约束;角色仍沿用来源会话,不因目标而提权。
|
|
229
|
+
const subject = input.threadRouted
|
|
230
|
+
? {
|
|
231
|
+
...builtSubject,
|
|
232
|
+
chatType: (input.scope === 'group' ? 'group' : 'private'),
|
|
233
|
+
channelId: input.to,
|
|
234
|
+
conversationId: input.to,
|
|
235
|
+
actorId: input.scope === 'group' ? builtSubject.actorId : input.to,
|
|
236
|
+
peerKey: formatPeerKey(grant.channelType, input.to),
|
|
237
|
+
}
|
|
238
|
+
: { ...builtSubject, peerKey: grant.peerKey };
|
|
214
239
|
const decision = authorizeOperation({
|
|
215
240
|
source: 'agent-tool',
|
|
216
241
|
intent: {
|
|
@@ -278,7 +303,22 @@ export function hashDelegatedCommandArgv(argv) {
|
|
|
278
303
|
.digest('hex');
|
|
279
304
|
}
|
|
280
305
|
export function hashCurrentDelegatedCommand(processArgv = process.argv) {
|
|
306
|
+
const argv = currentDelegatedCommandArgv(processArgv);
|
|
307
|
+
return argv ? hashDelegatedCommandArgv([MAIN_CLI_BIN, ...argv]) : undefined;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Return the command arguments used by a delegated CLI invocation.
|
|
311
|
+
*
|
|
312
|
+
* The returned value excludes the executable because IPC request builders add
|
|
313
|
+
* the canonical executable before hashing. Keeping this conversion beside
|
|
314
|
+
* hashCurrentDelegatedCommand prevents typed IPC requests from hashing a
|
|
315
|
+
* different representation than the implicit current-command path.
|
|
316
|
+
*/
|
|
317
|
+
export function currentDelegatedCommandArgv(processArgv = process.argv) {
|
|
281
318
|
if (processArgv.length < 3)
|
|
282
319
|
return undefined;
|
|
283
|
-
|
|
320
|
+
const canonical = canonicalizeEvolcoreCommandArgv([MAIN_CLI_BIN, ...processArgv.slice(2)]);
|
|
321
|
+
if (canonical[0] !== MAIN_CLI_BIN)
|
|
322
|
+
return undefined;
|
|
323
|
+
return canonical.slice(1);
|
|
284
324
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* debug-context.ts — 调试模式共享逻辑
|
|
3
|
+
*
|
|
4
|
+
* 供两端共用,不依赖 src/cli/:
|
|
5
|
+
* - CLI 侧:src/cli/debug-mode.ts(resolveDebugContext)
|
|
6
|
+
* - daemon 侧:src/ipc.ts(debug-issue-token IPC handler)
|
|
7
|
+
*
|
|
8
|
+
* 包含:
|
|
9
|
+
* - DebugDelegationRegistry:独立 grant 通道,token 带 dbg: 前缀,不影响主 registry
|
|
10
|
+
* - buildDebugTaskContext:从 Session 重建 TaskRuntimeContext(从 launch-consolidation.ts 迁移)
|
|
11
|
+
*/
|
|
12
|
+
import crypto from 'crypto';
|
|
13
|
+
import { formatPeerKey } from '../relation/peer-identity.js';
|
|
14
|
+
import { logger } from '../../utils/logger.js';
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// DebugDelegationRegistry
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/** Token TTL for debug grants — longer than production (30 min vs 5 min). */
|
|
19
|
+
const DEBUG_TOKEN_TTL_MS = 30 * 60_000;
|
|
20
|
+
/** Prefix that identifies a debug delegation token at the validation entry point. */
|
|
21
|
+
export const DEBUG_TOKEN_PREFIX = 'dbg:';
|
|
22
|
+
function sha256(data) {
|
|
23
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Standalone delegation registry for debug mode.
|
|
27
|
+
*
|
|
28
|
+
* Uses a separate Map (debugGrantsByHash) so debug tokens never touch
|
|
29
|
+
* activeHashBySession in AgentDelegationRegistry, and issuing a debug token
|
|
30
|
+
* never triggers revokeSession on a live task.
|
|
31
|
+
*
|
|
32
|
+
* Token format: `dbg:<32-byte random hex>`
|
|
33
|
+
* Hash is computed over the raw part (after the prefix) only.
|
|
34
|
+
*/
|
|
35
|
+
export class DebugDelegationRegistry {
|
|
36
|
+
debugGrantsByHash = new Map();
|
|
37
|
+
issue(input) {
|
|
38
|
+
const raw = crypto.randomBytes(32).toString('hex');
|
|
39
|
+
const token = `${DEBUG_TOKEN_PREFIX}${raw}`;
|
|
40
|
+
const tokenHash = sha256(raw);
|
|
41
|
+
const grant = {
|
|
42
|
+
tokenHash,
|
|
43
|
+
sessionId: input.sessionId,
|
|
44
|
+
taskId: `debug-${Date.now()}`,
|
|
45
|
+
actorId: input.actorId ?? input.selfAid,
|
|
46
|
+
channel: input.channel,
|
|
47
|
+
channelType: input.channelType,
|
|
48
|
+
chatType: input.chatType,
|
|
49
|
+
selfAid: input.selfAid,
|
|
50
|
+
peerKey: input.peerKey,
|
|
51
|
+
issuedRole: input.issuedRole ?? 'debug',
|
|
52
|
+
expiresAt: Date.now() + DEBUG_TOKEN_TTL_MS,
|
|
53
|
+
};
|
|
54
|
+
this.debugGrantsByHash.set(tokenHash, grant);
|
|
55
|
+
logger.debug(`[DebugDelegation] issued token for session=${input.sessionId}`);
|
|
56
|
+
return token;
|
|
57
|
+
}
|
|
58
|
+
validate(token, sessionId) {
|
|
59
|
+
const raw = token.startsWith(DEBUG_TOKEN_PREFIX) ? token.slice(DEBUG_TOKEN_PREFIX.length) : token;
|
|
60
|
+
const tokenHash = sha256(raw);
|
|
61
|
+
const grant = this.debugGrantsByHash.get(tokenHash);
|
|
62
|
+
if (!grant) {
|
|
63
|
+
return { ok: false, code: 'INVALID_DELEGATION', reason: 'unknown debug token', diagnosticCause: 'unknown_token' };
|
|
64
|
+
}
|
|
65
|
+
if (grant.sessionId !== sessionId) {
|
|
66
|
+
return { ok: false, code: 'INVALID_DELEGATION', reason: 'session mismatch', diagnosticCause: 'session_mismatch' };
|
|
67
|
+
}
|
|
68
|
+
if (Date.now() > grant.expiresAt) {
|
|
69
|
+
this.debugGrantsByHash.delete(tokenHash);
|
|
70
|
+
return { ok: false, code: 'INVALID_DELEGATION', reason: 'debug token expired', diagnosticCause: 'task_replaced' };
|
|
71
|
+
}
|
|
72
|
+
return { ok: true, grant };
|
|
73
|
+
}
|
|
74
|
+
/** Remove all grants for a session (called when session is unbound). */
|
|
75
|
+
revokeSession(sessionId) {
|
|
76
|
+
for (const [hash, grant] of this.debugGrantsByHash) {
|
|
77
|
+
if (grant.sessionId === sessionId) {
|
|
78
|
+
this.debugGrantsByHash.delete(hash);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Prune expired grants. */
|
|
83
|
+
pruneExpired() {
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
for (const [hash, grant] of this.debugGrantsByHash) {
|
|
86
|
+
if (now > grant.expiresAt)
|
|
87
|
+
this.debugGrantsByHash.delete(hash);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function buildDebugTaskContext(input) {
|
|
92
|
+
const { session } = input;
|
|
93
|
+
const meta = (session.metadata ?? {});
|
|
94
|
+
const sessionType = session.sessionType ?? meta.sessionType ?? 'main';
|
|
95
|
+
// channel = 类型名;channelKey = 完整实例 key。二者分开,
|
|
96
|
+
// 与 originFromActorSession 的 `session.metadata?.channelKey || session.channel` 同源。
|
|
97
|
+
let channel = session.channelType || session.channel;
|
|
98
|
+
let channelKey = meta.channelKey || session.channel;
|
|
99
|
+
let chatType = session.chatType || 'private';
|
|
100
|
+
let peerId = meta.peerId;
|
|
101
|
+
let peerKeyId = chatType === 'group'
|
|
102
|
+
? (meta.groupId || session.channelId)
|
|
103
|
+
: (meta.peerId || session.channelId);
|
|
104
|
+
let threadId = session.threadId || undefined;
|
|
105
|
+
if (session.channelId?.startsWith('trigger:') && session.selfAID && input.getTrigger) {
|
|
106
|
+
const triggerId = session.channelId.slice('trigger:'.length);
|
|
107
|
+
const origin = input.getTrigger(session.selfAID, triggerId)?.origin;
|
|
108
|
+
if (origin?.channelType) {
|
|
109
|
+
channel = origin.channelType;
|
|
110
|
+
if (origin.channelKey)
|
|
111
|
+
channelKey = origin.channelKey;
|
|
112
|
+
chatType = origin.delivery?.chatType ?? 'private';
|
|
113
|
+
peerId = origin.peerId;
|
|
114
|
+
peerKeyId = chatType === 'group' ? origin.channelId : (origin.peerId ?? origin.channelId);
|
|
115
|
+
threadId = origin.threadId;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
sessionId: session.id,
|
|
120
|
+
taskId: `debug-${Date.now()}`,
|
|
121
|
+
messageId: `debug-msg-${Date.now()}`,
|
|
122
|
+
baseagent: session.baseagent,
|
|
123
|
+
channel,
|
|
124
|
+
channelKey,
|
|
125
|
+
channelId: session.channelId,
|
|
126
|
+
chatType,
|
|
127
|
+
selfAid: session.selfAID,
|
|
128
|
+
peerId,
|
|
129
|
+
peerKey: channel && peerKeyId ? formatPeerKey(channel, peerKeyId) : undefined,
|
|
130
|
+
peerName: meta.peerName,
|
|
131
|
+
peerType: meta.peerType,
|
|
132
|
+
threadId,
|
|
133
|
+
sessionType,
|
|
134
|
+
memoryMode: meta.memoryMode,
|
|
135
|
+
memoryScope: meta.memoryScope,
|
|
136
|
+
consolidationBatchDir: meta.consolidationBatchDir ?? meta.consolidationLogBase,
|
|
137
|
+
sessionRuntimeDir: input.sessionRuntimeDir,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -52,6 +52,7 @@ export function createDelegationAuthorizer({ agentDelegationRegistry, sessionMan
|
|
|
52
52
|
aid: subject.dataScope === 'daemon' ? (params.targetAid ?? delegation.grant.selfAid) : delegation.grant.selfAid,
|
|
53
53
|
dataScope: subject.dataScope,
|
|
54
54
|
management: isManagementRole(subject.role),
|
|
55
|
+
sessionRuntimeDir: delegation.grant.sessionRuntimeDir,
|
|
55
56
|
}
|
|
56
57
|
: { ok: false, code: decision.code, error: decision.reason };
|
|
57
58
|
};
|
|
@@ -331,6 +331,15 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
|
|
|
331
331
|
};
|
|
332
332
|
}
|
|
333
333
|
let matchResult = matchCommandPermission(operation, opMeta.category, roleDef.commandPermissions || {}, opMeta.dangerous);
|
|
334
|
+
if (!matchResult && operation.startsWith('ec.thread.') && opMeta.category === 'read' && intent.scope === 'relation') {
|
|
335
|
+
const permission = roleDef.commandPermissions?.['session.topic.list'];
|
|
336
|
+
if (permission?.allow)
|
|
337
|
+
matchResult = {
|
|
338
|
+
permission: { ...permission, scopes: ['relation'], constraints: { ...permission.constraints, currentRelationOnly: true, targetCurrentAgentOnly: true } },
|
|
339
|
+
matchedRule: 'session.topic.list',
|
|
340
|
+
rank: 1000,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
334
343
|
// Agent self-service contact add deliberately reuses the same low-trust
|
|
335
344
|
// relation boundary as ec.msg.send. Older per-Agent role files may still
|
|
336
345
|
// contain the historical contact.* deny without an explicit contact.add
|
|
@@ -461,6 +470,14 @@ function requiresDaemonOwner(ctx, resolvedConfigCommand) {
|
|
|
461
470
|
if (operation === 'agent.reload') {
|
|
462
471
|
return !isCurrentAgentTarget(ctx);
|
|
463
472
|
}
|
|
473
|
+
// Same shape as agent.reload: the current Agent's owner/admin refresh their
|
|
474
|
+
// own skill index; refreshing another Agent's index needs the daemon owner.
|
|
475
|
+
if (operation === 'skill.refresh' && hasDaemonOwnerRole(ctx)) {
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
if (operation === 'skill.refresh') {
|
|
479
|
+
return !isCurrentAgentTarget(ctx);
|
|
480
|
+
}
|
|
464
481
|
if (operation.startsWith('trigger.')) {
|
|
465
482
|
if (ctx.fromControlChannel)
|
|
466
483
|
return true;
|
|
@@ -478,7 +495,7 @@ function hasDaemonOwnerRole(ctx) {
|
|
|
478
495
|
|| ctx.processRole === 'daemon-service';
|
|
479
496
|
}
|
|
480
497
|
function daemonOwnerDenialReasonCode(ctx) {
|
|
481
|
-
if ((ctx.intent.operation === 'agent.show' || ctx.intent.operation === 'agent.reload')
|
|
498
|
+
if ((ctx.intent.operation === 'agent.show' || ctx.intent.operation === 'agent.reload' || ctx.intent.operation === 'skill.refresh')
|
|
482
499
|
&& ctx.intent.scope !== 'control'
|
|
483
500
|
&& !ctx.fromControlChannel
|
|
484
501
|
&& !isCurrentAgentTarget(ctx)) {
|
|
@@ -894,6 +911,12 @@ function checkOwnPeer(ctx) {
|
|
|
894
911
|
? { ok: true }
|
|
895
912
|
: { ok: false, reason: 'Only the current group relation can be targeted' };
|
|
896
913
|
}
|
|
914
|
+
// For ec.msg.send / ec.msg.file, args.peer is the recipient (to), not a
|
|
915
|
+
// self-targeting field. Cross-topic sends are checked against their indexed
|
|
916
|
+
// target session by the managed message operation.
|
|
917
|
+
const isMsgSend = ctx.intent.operation === 'ec.msg.send' || ctx.intent.operation === 'ec.msg.file';
|
|
918
|
+
if (isMsgSend)
|
|
919
|
+
return { ok: true };
|
|
897
920
|
if (ctx.actorId) {
|
|
898
921
|
if (requestedPeerId && requestedPeerId !== ctx.actorId) {
|
|
899
922
|
return { ok: false, reason: ownPeerOnlyDenialReason(ctx, requestedPeerId) };
|
|
@@ -81,15 +81,15 @@ const GROUP_FS_READ_OPERATIONS = ['ls', 'stat', 'lstat', 'cat', 'find', 'df', 'g
|
|
|
81
81
|
category: 'read',
|
|
82
82
|
dangerous: false,
|
|
83
83
|
defaultScopes: ['relation', 'agent'],
|
|
84
|
-
description: `Read
|
|
84
|
+
description: `Read managed filesystem (${kind})`,
|
|
85
85
|
sources: ['agent-tool'],
|
|
86
86
|
}));
|
|
87
87
|
const GROUP_FS_WRITE_OPERATIONS = [['mkdir', 'write-own'], ['upload', 'write-own'], ['copy', 'write-own'], ['move', 'write-own'], ['remove', 'write-own'], ['setfacl', 'write-agent']].map(([kind, category]) => ({
|
|
88
88
|
id: `ec.fs.${kind}`,
|
|
89
89
|
category,
|
|
90
90
|
dangerous: false,
|
|
91
|
-
defaultScopes: ['relation'],
|
|
92
|
-
description: `Write
|
|
91
|
+
defaultScopes: ['relation', 'agent'],
|
|
92
|
+
description: `Write managed filesystem (${kind})`,
|
|
93
93
|
sources: ['agent-tool'],
|
|
94
94
|
}));
|
|
95
95
|
const HANDOFF_READ_OPERATIONS = ['status', 'list', 'trace'].map(kind => ({
|
|
@@ -307,6 +307,22 @@ const OPERATIONS = [
|
|
|
307
307
|
description: '查看当前会话状态',
|
|
308
308
|
sources: ['slash', 'menu', 'agent-tool'],
|
|
309
309
|
},
|
|
310
|
+
{
|
|
311
|
+
id: 'session.loop.current',
|
|
312
|
+
category: 'read',
|
|
313
|
+
dangerous: false,
|
|
314
|
+
defaultScopes: ['relation'],
|
|
315
|
+
description: '查看当前主会话或话题的 idle 触发设置',
|
|
316
|
+
sources: ['slash', 'menu', 'ecweb', 'control'],
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: 'session.loop.update',
|
|
320
|
+
category: 'write-own',
|
|
321
|
+
dangerous: false,
|
|
322
|
+
defaultScopes: ['relation'],
|
|
323
|
+
description: '更新当前主会话或话题的 idle 触发设置',
|
|
324
|
+
sources: ['slash', 'menu', 'ecweb', 'control'],
|
|
325
|
+
},
|
|
310
326
|
{
|
|
311
327
|
id: 'session.topic.current',
|
|
312
328
|
category: 'read',
|
|
@@ -719,6 +735,14 @@ const OPERATIONS = [
|
|
|
719
735
|
description: '读取或更新当前 Agent 的 Bootstrap welcome.md',
|
|
720
736
|
sources: ['agent-tool', 'control'],
|
|
721
737
|
},
|
|
738
|
+
{
|
|
739
|
+
id: 'agent.rename',
|
|
740
|
+
category: 'write-agent',
|
|
741
|
+
dangerous: true,
|
|
742
|
+
defaultScopes: ['agent'],
|
|
743
|
+
description: '更新并发布当前 Agent 的 agent.md 显示名称',
|
|
744
|
+
sources: ['agent-tool'],
|
|
745
|
+
},
|
|
722
746
|
{
|
|
723
747
|
id: 'agent.create',
|
|
724
748
|
category: 'process',
|
|
@@ -759,6 +783,23 @@ const OPERATIONS = [
|
|
|
759
783
|
description: '重载 agent 配置',
|
|
760
784
|
sources: ['slash', 'menu', 'agent-tool', 'control'],
|
|
761
785
|
},
|
|
786
|
+
{
|
|
787
|
+
id: 'skill.refresh',
|
|
788
|
+
category: 'write-agent',
|
|
789
|
+
dangerous: false,
|
|
790
|
+
defaultScopes: ['agent', 'control'],
|
|
791
|
+
description: '重建技能索引文件',
|
|
792
|
+
sources: ['agent-tool', 'control'],
|
|
793
|
+
},
|
|
794
|
+
...['list', 'list-peers', 'show', 'history', 'check-index', 'summary-show', 'summary-log', 'create', 'rename', 'label', 'status', 'delete', 'summary-set', 'summary-clear', 'rebuild-index'].map(action => ({
|
|
795
|
+
id: `ec.thread.${action}`,
|
|
796
|
+
category: (['list', 'list-peers', 'show', 'history', 'check-index', 'summary-show', 'summary-log'].includes(action)
|
|
797
|
+
? 'read' : action === 'rebuild-index' ? 'write-agent' : 'write-own'),
|
|
798
|
+
dangerous: false,
|
|
799
|
+
defaultScopes: ['relation', 'agent'],
|
|
800
|
+
description: `${action} managed conversation threads`,
|
|
801
|
+
sources: ['agent-tool'],
|
|
802
|
+
})),
|
|
762
803
|
{
|
|
763
804
|
id: 'agent.bootstrapComplete',
|
|
764
805
|
category: 'write-agent',
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-cleanup.ts — 独立后台会话(bg-session)和 trigger 会话的清理机制
|
|
3
|
+
*
|
|
4
|
+
* 三种自动清理模式:
|
|
5
|
+
* immediate — 会话结束时同步清理
|
|
6
|
+
* on_next_launch — 下次同 kind 会话启动时清理上一次的残留(默认)
|
|
7
|
+
* expire — 会话结束时写登记表,启动时扫描到期条目清理
|
|
8
|
+
*
|
|
9
|
+
* 登记表路径:$EVOLCORE_HOME/data/session-cleanup/expire-<YYYYMMDD>.jsonl
|
|
10
|
+
* 详见 docs/background-session/session-cleanup-policy.md
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { resolveRoot } from '../../paths.js';
|
|
15
|
+
import { logger } from '../../utils/logger.js';
|
|
16
|
+
// ── 登记表工具 ──────────────────────────────────────────────────────────────
|
|
17
|
+
function cleanupDir() {
|
|
18
|
+
return path.join(resolveRoot(), 'data', 'session-cleanup');
|
|
19
|
+
}
|
|
20
|
+
function expireFilePath(date) {
|
|
21
|
+
const y = date.getFullYear();
|
|
22
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
23
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
24
|
+
return path.join(cleanupDir(), `expire-${y}${m}${d}.jsonl`);
|
|
25
|
+
}
|
|
26
|
+
function doneFilePath(expirePath) {
|
|
27
|
+
return expirePath.replace(/\.jsonl$/, '.done.jsonl');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 把一条超期记录写进今天的登记表。
|
|
31
|
+
* 幂等:同一 channelId 写多次无害,runExpireCleanup 会逐条处理。
|
|
32
|
+
*/
|
|
33
|
+
export function appendExpireEntry(entry) {
|
|
34
|
+
try {
|
|
35
|
+
const dir = cleanupDir();
|
|
36
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
37
|
+
const filePath = expireFilePath(new Date(entry.expireAt));
|
|
38
|
+
fs.appendFileSync(filePath, JSON.stringify(entry) + '\n', 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
logger.warn(`[SessionCleanup] 写入超期登记表失败: ${e?.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 清理一条 evolcore session 记录(移入 _trash/)。
|
|
46
|
+
* runner 侧日志不在这里处理(runner 接口尚未提供 deleteSession)。
|
|
47
|
+
*/
|
|
48
|
+
export async function cleanupSessionRecord(target, cleaner) {
|
|
49
|
+
try {
|
|
50
|
+
await cleaner.softDeleteSession(target.channelId);
|
|
51
|
+
logger.debug(`[SessionCleanup] 已清理 session 记录: channelId=${target.channelId}` +
|
|
52
|
+
`${target.agentAid ? ` aid=${target.agentAid}` : ''}`);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
logger.warn(`[SessionCleanup] 清理 session 记录失败 channelId=${target.channelId}: ${e?.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ── expire 模式:启动时扫描 ──────────────────────────────────────────────────
|
|
59
|
+
/**
|
|
60
|
+
* 扫描 data/session-cleanup/ 下所有登记表,清理已到期的条目。
|
|
61
|
+
* 在任意 bg-session 或 trigger 会话启动时调用,成本低(只读少量小文件)。
|
|
62
|
+
*/
|
|
63
|
+
export async function runExpireCleanup(cleaner, now = Date.now()) {
|
|
64
|
+
const dir = cleanupDir();
|
|
65
|
+
if (!fs.existsSync(dir))
|
|
66
|
+
return;
|
|
67
|
+
let files;
|
|
68
|
+
try {
|
|
69
|
+
files = fs.readdirSync(dir).filter(f => /^expire-\d{8}\.jsonl$/.test(f));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const todayStr = (() => {
|
|
75
|
+
const d = new Date(now);
|
|
76
|
+
const y = d.getFullYear();
|
|
77
|
+
const m = String(d.getMonth() + 1).padStart(2, '0');
|
|
78
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
79
|
+
return `expire-${y}${m}${day}.jsonl`;
|
|
80
|
+
})();
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
// 跳过未来日期的文件(expireAt 不可能到期)
|
|
83
|
+
const fileDate = file.slice(7, 15); // expire-YYYYMMDD.jsonl → YYYYMMDD
|
|
84
|
+
const todayDate = todayStr.slice(7, 15);
|
|
85
|
+
if (fileDate > todayDate)
|
|
86
|
+
continue;
|
|
87
|
+
const filePath = path.join(dir, file);
|
|
88
|
+
const donePath = doneFilePath(filePath);
|
|
89
|
+
let lines;
|
|
90
|
+
try {
|
|
91
|
+
lines = fs.readFileSync(filePath, 'utf-8').split('\n').filter(l => l.trim());
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// 读取已处理的 channelId 集合
|
|
97
|
+
const doneIds = new Set();
|
|
98
|
+
if (fs.existsSync(donePath)) {
|
|
99
|
+
try {
|
|
100
|
+
for (const line of fs.readFileSync(donePath, 'utf-8').split('\n').filter(l => l.trim())) {
|
|
101
|
+
const entry = JSON.parse(line);
|
|
102
|
+
if (entry.channelId)
|
|
103
|
+
doneIds.add(entry.channelId);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch { /* 损坏的 done 文件,跳过去重,重复清理幂等 */ }
|
|
107
|
+
}
|
|
108
|
+
let processedCount = 0;
|
|
109
|
+
// 文件里是否还有未到期条目;有则保留文件,下次再扫
|
|
110
|
+
let hasPending = false;
|
|
111
|
+
for (const line of lines) {
|
|
112
|
+
let entry;
|
|
113
|
+
try {
|
|
114
|
+
entry = JSON.parse(line);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (!entry.channelId || !entry.expireAt)
|
|
120
|
+
continue;
|
|
121
|
+
if (entry.expireAt > now) {
|
|
122
|
+
hasPending = true;
|
|
123
|
+
continue;
|
|
124
|
+
} // 未到期
|
|
125
|
+
if (doneIds.has(entry.channelId))
|
|
126
|
+
continue; // 已处理
|
|
127
|
+
await cleanupSessionRecord({ sessionId: entry.sessionId, channelId: entry.channelId, agentAid: entry.agentAid }, cleaner);
|
|
128
|
+
// 标记已处理
|
|
129
|
+
try {
|
|
130
|
+
fs.appendFileSync(donePath, JSON.stringify({ channelId: entry.channelId, ts: now }) + '\n', 'utf-8');
|
|
131
|
+
}
|
|
132
|
+
catch { /* 写失败无妨,下次会重复处理,cleanupSessionRecord 幂等 */ }
|
|
133
|
+
doneIds.add(entry.channelId);
|
|
134
|
+
processedCount++;
|
|
135
|
+
}
|
|
136
|
+
if (processedCount > 0) {
|
|
137
|
+
logger.debug(`[SessionCleanup] expire 扫描:清理 ${processedCount} 条到期记录(${file})`);
|
|
138
|
+
}
|
|
139
|
+
// 整天文件全部处理完时,删除两个文件。
|
|
140
|
+
// 不能用 doneIds.size 与行数比较:同一 channelId 重复登记时两者永远不等,文件会残留。
|
|
141
|
+
if (!hasPending) {
|
|
142
|
+
try {
|
|
143
|
+
fs.unlinkSync(filePath);
|
|
144
|
+
}
|
|
145
|
+
catch { /* ignore */ }
|
|
146
|
+
try {
|
|
147
|
+
fs.unlinkSync(donePath);
|
|
148
|
+
}
|
|
149
|
+
catch { /* ignore */ }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ── on_next_launch 模式:按 kind 清理历史会话 ────────────────────────────────
|
|
154
|
+
/**
|
|
155
|
+
* 记录一条 on_next_launch 清理登记,供下次启动时使用。
|
|
156
|
+
* 存在一个独立的 JSONL 文件中,按 kind 分文件存储。
|
|
157
|
+
*/
|
|
158
|
+
function nextLaunchFilePath(kind) {
|
|
159
|
+
const safeKind = kind.replace(/[^a-zA-Z0-9\-_]/g, '_');
|
|
160
|
+
return path.join(cleanupDir(), `next-launch-${safeKind}.jsonl`);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 记录一条「下次启动时清理」的会话信息。
|
|
164
|
+
* 在会话正常或异常结束时调用。
|
|
165
|
+
*/
|
|
166
|
+
export function recordNextLaunchCleanup(kind, entry) {
|
|
167
|
+
try {
|
|
168
|
+
const dir = cleanupDir();
|
|
169
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
170
|
+
const filePath = nextLaunchFilePath(kind);
|
|
171
|
+
fs.appendFileSync(filePath, JSON.stringify(entry) + '\n', 'utf-8');
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
logger.warn(`[SessionCleanup] 写入 on_next_launch 登记失败 kind=${kind}: ${e?.message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 清理同 kind 的所有历史会话记录,然后清空登记文件。
|
|
179
|
+
* 在新会话启动前调用(on_next_launch 模式)。
|
|
180
|
+
*/
|
|
181
|
+
export async function cleanupPreviousByKind(kind, cleaner) {
|
|
182
|
+
const filePath = nextLaunchFilePath(kind);
|
|
183
|
+
if (!fs.existsSync(filePath))
|
|
184
|
+
return;
|
|
185
|
+
let lines;
|
|
186
|
+
try {
|
|
187
|
+
lines = fs.readFileSync(filePath, 'utf-8').split('\n').filter(l => l.trim());
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
for (const line of lines) {
|
|
193
|
+
let entry;
|
|
194
|
+
try {
|
|
195
|
+
entry = JSON.parse(line);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (!entry.channelId)
|
|
201
|
+
continue;
|
|
202
|
+
await cleanupSessionRecord({ sessionId: entry.sessionId, channelId: entry.channelId, agentAid: entry.agentAid }, cleaner);
|
|
203
|
+
}
|
|
204
|
+
// 清空登记文件(下一次重新积累)
|
|
205
|
+
try {
|
|
206
|
+
fs.unlinkSync(filePath);
|
|
207
|
+
}
|
|
208
|
+
catch { /* ignore */ }
|
|
209
|
+
if (lines.length > 0) {
|
|
210
|
+
logger.debug(`[SessionCleanup] on_next_launch:已清理 ${lines.length} 条历史记录(kind=${kind})`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -53,7 +53,7 @@ export function renderPostBootstrapWelcome(aid, owner, ownerName) {
|
|
|
53
53
|
}
|
|
54
54
|
const config = loadAgent(aid);
|
|
55
55
|
if (config && resolveAgentLifecycle(config) === 'bootstrapping' && config.initialization?.mode !== 'supplied') {
|
|
56
|
-
throw new Error('WELCOME_REQUIRED:
|
|
56
|
+
throw new Error('WELCOME_REQUIRED: Agent-prepared welcome.md is required before activation');
|
|
57
57
|
}
|
|
58
58
|
return generateWelcomeMessage({
|
|
59
59
|
channelType: 'aun',
|
|
@@ -25,14 +25,17 @@ export async function completeBootstrapWithWelcome(service, aid, welcome, sessio
|
|
|
25
25
|
}
|
|
26
26
|
if (lifecycle === 'bootstrapping') {
|
|
27
27
|
// Existing per-Owner records are immutable retry snapshots. New interactive
|
|
28
|
-
// Bootstrap completions require the text
|
|
28
|
+
// Bootstrap completions require the welcome text prepared by the Agent.
|
|
29
29
|
const config = loadAgent(aid);
|
|
30
30
|
const deliveries = config?.owners?.length ? postBootstrapWelcomeStatus(aid, config.owners).deliveries : [];
|
|
31
31
|
const alreadyPrepared = deliveries.length > 0 && deliveries.every(delivery => delivery.status !== 'not_prepared');
|
|
32
32
|
if (config?.initialization?.mode !== 'supplied' && !alreadyPrepared) {
|
|
33
33
|
try {
|
|
34
34
|
if (readAgentWelcomeTemplate(aid) === null) {
|
|
35
|
-
return {
|
|
35
|
+
return {
|
|
36
|
+
ok: false, code: 'WELCOME_REQUIRED', reloaded: false,
|
|
37
|
+
error: '请先自主生成正式欢迎文案并保存 welcome.md,无需 Owner 确认',
|
|
38
|
+
};
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
catch (error) {
|
|
@@ -446,7 +449,7 @@ export class BootstrapService {
|
|
|
446
449
|
const templatePath = path.join(kitsTemplatesDir(), 'bootstrap-welcome.md');
|
|
447
450
|
const template = fs.existsSync(templatePath)
|
|
448
451
|
? fs.readFileSync(templatePath, 'utf-8')
|
|
449
|
-
: '你好,我是 {{agentName}}
|
|
452
|
+
: '你好,我是 {{agentName}}。已有的有效基础信息会直接沿用;请补充缺失的显示名、简介或标签。persona 和头像可选,之后也可以完善。';
|
|
450
453
|
return renderTemplate(template, vars).trim();
|
|
451
454
|
}
|
|
452
455
|
resolveAgentDisplayName(aid) {
|