evolcore 0.0.32 → 0.0.34
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 +61 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +135 -37
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +111 -15
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +112 -54
- package/dist/agents/codex-runner.js +241 -63
- package/dist/agents/ecagent-runner.js +78 -33
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/channels/aun.js +44 -8
- package/dist/channels/daemon.js +261 -3
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +368 -31
- package/dist/cli/contact.js +8 -2
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +197 -27
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +98 -67
- package/dist/cli/init.js +2 -2
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +166 -146
- package/dist/cli/queue-command.js +9 -4
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +78 -2
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +622 -0
- package/dist/cli/trigger-command.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +6 -3
- package/dist/config/config-manager.js +26 -2
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +45 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +133 -36
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +25 -2
- package/dist/core/auth/operation-catalog.js +49 -0
- 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/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +197 -27
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +51 -1
- package/dist/core/command/menu-handler.js +114 -23
- package/dist/core/command/menu-protocol.js +25 -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 +195 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +175 -3
- package/dist/core/memory/batch-meta.js +132 -0
- package/dist/core/memory/consolidation-context.js +83 -29
- package/dist/core/memory/consolidation-ops-records.js +49 -16
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +264 -205
- 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 +30 -15
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +169 -10
- package/dist/core/message/message-bridge.js +15 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +929 -110
- 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 +217 -9
- 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 +30 -62
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +129 -14
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +21 -4
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +222 -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 +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 +770 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +19 -7
- package/dist/ipc.js +140 -50
- package/dist/paths.js +93 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +13 -2
- package/dist/trigger/parser.js +70 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/cross-platform.js +11 -2
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +143 -1
- 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 +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +40 -28
- package/kits/docs/evolcore/agent.md +21 -6
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +5 -1
- package/kits/docs/evolcore/fs-architecture.md +4 -5
- package/kits/docs/evolcore/fs.md +1 -1
- package/kits/docs/evolcore/group-collaboration.md +4 -6
- package/kits/docs/evolcore/group.md +11 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -15
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +8 -11
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -1
- package/kits/eck_manifest.json +55 -40
- package/kits/eck_manifest.memory.json +0 -12
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +199 -0
- 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/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 +16 -2
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -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 +7 -2
- package/kits/templates/memory-consolidation/generalize.md +9 -4
- package/kits/templates/memory-consolidation/notes.md +11 -11
- package/kits/templates/memory-consolidation/profile.md +8 -3
- package/kits/templates/memory-consolidation/promote.md +8 -3
- package/kits/templates/memory-consolidation/workflow.md +6 -6
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- 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 +67 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +7 -6
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +6 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
- 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
package/dist/channels/daemon.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { appendExpireEntry, recordNextLaunchCleanup, cleanupPreviousByKind, runExpireCleanup, cleanupSessionRecord, } from '../core/background-session/session-cleanup.js';
|
|
1
2
|
import { ExecutionTimeoutError } from '../utils/error-utils.js';
|
|
2
3
|
import { normalizeExecutionMode } from '../trigger/validation.js';
|
|
3
4
|
import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
|
|
5
|
+
import { assertSystemSessionLaunchable } from '../core/system-session/registry.js';
|
|
4
6
|
const DEFAULT_WATCHDOG_MS = 11 * 60 * 1000;
|
|
5
7
|
export class DaemonChannel {
|
|
6
8
|
sessionManager;
|
|
@@ -17,6 +19,7 @@ export class DaemonChannel {
|
|
|
17
19
|
thread: true,
|
|
18
20
|
};
|
|
19
21
|
pending = new Map();
|
|
22
|
+
bgPending = new Map();
|
|
20
23
|
watchdogMs;
|
|
21
24
|
totalExecutionMs;
|
|
22
25
|
constructor(sessionManager, messageQueue, opts = {}) {
|
|
@@ -26,6 +29,8 @@ export class DaemonChannel {
|
|
|
26
29
|
this.totalExecutionMs = normalizeTotalExecutionMs(opts.totalExecutionMs);
|
|
27
30
|
}
|
|
28
31
|
async converse(prompt, ctx) {
|
|
32
|
+
// 启动前跑一次 expire 扫描(成本低,只读少量小文件)
|
|
33
|
+
void runExpireCleanup(this.sessionManager).catch(() => { });
|
|
29
34
|
const runId = ctx.runId;
|
|
30
35
|
const pendingId = ctx.attemptId ?? runId;
|
|
31
36
|
const messageId = pendingId;
|
|
@@ -41,6 +46,11 @@ export class DaemonChannel {
|
|
|
41
46
|
? undefined
|
|
42
47
|
: setTimeout(() => this.totalExecutionTimeout(pendingId, remainingExecutionMs), remainingExecutionMs);
|
|
43
48
|
totalExecutionTimer?.unref?.();
|
|
49
|
+
// trigger 清理策略:once → expire(3天),其他 → expire(30天)
|
|
50
|
+
const isOnce = ctx.trigger.source?.type === 'once';
|
|
51
|
+
const triggerCleanupPolicy = isOnce
|
|
52
|
+
? { mode: 'expire', ttlMs: 3 * 24 * 3600 * 1000 }
|
|
53
|
+
: { mode: 'expire', ttlMs: 30 * 24 * 3600 * 1000 };
|
|
44
54
|
this.pending.set(pendingId, {
|
|
45
55
|
resolve,
|
|
46
56
|
startedAt,
|
|
@@ -52,6 +62,11 @@ export class DaemonChannel {
|
|
|
52
62
|
abortSignal: ctx.signal,
|
|
53
63
|
runId,
|
|
54
64
|
acc: { text: '', files: [], anomalies: [], toolCallCount: 0, seenToolCallIds: new Set() },
|
|
65
|
+
cleanupPolicy: triggerCleanupPolicy,
|
|
66
|
+
channelId: ctx.session.channelId,
|
|
67
|
+
agentAid: ctx.trigger.agentAid,
|
|
68
|
+
kind: `trigger:${ctx.trigger.id}`,
|
|
69
|
+
sessionId: ctx.session.id,
|
|
55
70
|
});
|
|
56
71
|
});
|
|
57
72
|
const message = {
|
|
@@ -85,8 +100,13 @@ export class DaemonChannel {
|
|
|
85
100
|
originChannelKey: ctx.trigger.origin?.channelKey,
|
|
86
101
|
originChannelType: ctx.trigger.origin?.channelType
|
|
87
102
|
?? (ctx.trigger.origin?.channelKey ? channelTypeFromKey(ctx.trigger.origin.channelKey) : undefined),
|
|
103
|
+
originChatType: ctx.trigger.origin?.delivery?.chatType,
|
|
104
|
+
originConversationId: ctx.trigger.origin?.delivery?.chatType === 'group'
|
|
105
|
+
? ctx.trigger.origin.channelId
|
|
106
|
+
: ctx.trigger.origin?.peerId,
|
|
88
107
|
originControl: ctx.trigger.origin?.channelKey === 'daemon',
|
|
89
108
|
authorizedBy: ctx.trigger.authorizedBy,
|
|
109
|
+
labels: ctx.trigger.labels,
|
|
90
110
|
},
|
|
91
111
|
};
|
|
92
112
|
const onAbort = () => {
|
|
@@ -128,6 +148,15 @@ export class DaemonChannel {
|
|
|
128
148
|
return promise;
|
|
129
149
|
}
|
|
130
150
|
async send(envelope, payload) {
|
|
151
|
+
// bg-session 路径:优先查 bgPending
|
|
152
|
+
const bgRunId = this.bgRunIdFromEnvelope(envelope);
|
|
153
|
+
if (bgRunId) {
|
|
154
|
+
const bgSlot = this.bgPending.get(bgRunId);
|
|
155
|
+
if (bgSlot && !bgSlot.terminated) {
|
|
156
|
+
this.handleBgSessionPayload(bgRunId, bgSlot, payload);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
131
160
|
const runId = this.runIdFromEnvelope(envelope);
|
|
132
161
|
if (!runId)
|
|
133
162
|
return;
|
|
@@ -225,6 +254,9 @@ export class DaemonChannel {
|
|
|
225
254
|
// 记忆整理会话:把会话原型与整理模式写进 metadata,供渲染层选 manifest / 手册,
|
|
226
255
|
// 供 ipc 侧校验写命令的 --scope。
|
|
227
256
|
const { memoryMode, memoryScope, consolidationBatchDir, consolidationChatDir, consolidationMemoryDir } = trigger.execution;
|
|
257
|
+
// 系统会话:类型 / subtype / runner 校验不过直接拒绝(Gemini 不支持),不降级成普通会话
|
|
258
|
+
if (memoryMode)
|
|
259
|
+
assertSystemSessionLaunchable({ type: 'memory', subtype: memoryMode }, baseagent);
|
|
228
260
|
// 反馈目标与触发方式(consolidation.ts 在 normalize 后补挂的字段):
|
|
229
261
|
// 收尾时据此投递反馈,并判断是否该反馈(定时触发不反馈)。
|
|
230
262
|
const { feedbackOrigin, consolidationTriggeredBy } = trigger.execution;
|
|
@@ -232,7 +264,7 @@ export class DaemonChannel {
|
|
|
232
264
|
channelKey: this.channelKey,
|
|
233
265
|
peerId: `trigger:${trigger.id}`,
|
|
234
266
|
peerName: trigger.name,
|
|
235
|
-
...(memoryMode ? { sessionType: 'memory', memoryMode } : {}),
|
|
267
|
+
...(memoryMode ? { systemSession: { type: 'memory', subtype: memoryMode }, sessionType: 'memory', memoryMode } : {}),
|
|
236
268
|
...(memoryScope ? { memoryScope } : {}),
|
|
237
269
|
...(consolidationBatchDir ? { consolidationBatchDir } : {}),
|
|
238
270
|
...(consolidationChatDir ? { consolidationChatDir } : {}),
|
|
@@ -270,6 +302,11 @@ export class DaemonChannel {
|
|
|
270
302
|
const pendingId = metadata?.triggerAttemptId ?? metadata?.triggerRunId;
|
|
271
303
|
return typeof pendingId === 'string' ? pendingId : undefined;
|
|
272
304
|
}
|
|
305
|
+
bgRunIdFromEnvelope(envelope) {
|
|
306
|
+
const metadata = envelope.replyContext?.metadata;
|
|
307
|
+
const runId = metadata?.bgSessionRunId;
|
|
308
|
+
return typeof runId === 'string' ? runId : undefined;
|
|
309
|
+
}
|
|
273
310
|
recordToolCall(slot, callId) {
|
|
274
311
|
if (typeof callId !== 'string' || !callId) {
|
|
275
312
|
slot.acc.toolCallCount += 1;
|
|
@@ -290,13 +327,14 @@ export class DaemonChannel {
|
|
|
290
327
|
?? (origin.channelKey ? channelTypeFromKey(origin.channelKey) : undefined);
|
|
291
328
|
if (!channelType)
|
|
292
329
|
return undefined;
|
|
330
|
+
const chatType = origin.delivery?.chatType ?? 'private';
|
|
293
331
|
return resolvePeerRoleDetail({
|
|
294
332
|
selfAid: trigger.agentAid,
|
|
295
333
|
channelKey: origin.channelKey,
|
|
296
334
|
channelType,
|
|
297
|
-
chatType
|
|
335
|
+
chatType,
|
|
298
336
|
actorId: origin.peerId,
|
|
299
|
-
conversationId: origin.peerId,
|
|
337
|
+
conversationId: chatType === 'group' ? origin.channelId : origin.peerId,
|
|
300
338
|
}).effectiveRole ?? undefined;
|
|
301
339
|
}
|
|
302
340
|
finish(runId, status, metadata) {
|
|
@@ -346,6 +384,29 @@ export class DaemonChannel {
|
|
|
346
384
|
toolCallCount: Math.max(slot.acc.toolCallCount, completedToolCallCount),
|
|
347
385
|
},
|
|
348
386
|
});
|
|
387
|
+
// trigger 清理策略
|
|
388
|
+
const policy = slot.cleanupPolicy;
|
|
389
|
+
if (policy.mode === 'immediate') {
|
|
390
|
+
void cleanupSessionRecord({ sessionId: slot.sessionId, channelId: slot.channelId, agentAid: slot.agentAid }, this.sessionManager);
|
|
391
|
+
}
|
|
392
|
+
else if (policy.mode === 'on_next_launch') {
|
|
393
|
+
recordNextLaunchCleanup(slot.kind, {
|
|
394
|
+
channelId: slot.channelId,
|
|
395
|
+
sessionId: slot.sessionId,
|
|
396
|
+
agentAid: slot.agentAid,
|
|
397
|
+
recordedAt: Date.now(),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
else if (policy.mode === 'expire') {
|
|
401
|
+
appendExpireEntry({
|
|
402
|
+
expireAt: Date.now() + policy.ttlMs,
|
|
403
|
+
sessionId: slot.sessionId,
|
|
404
|
+
channelId: slot.channelId,
|
|
405
|
+
kind: slot.kind,
|
|
406
|
+
agentAid: slot.agentAid,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
// 'keep' 模式:不做任何处理
|
|
349
410
|
}
|
|
350
411
|
fail(runId, reason, text) {
|
|
351
412
|
const slot = this.pending.get(runId);
|
|
@@ -412,6 +473,203 @@ export class DaemonChannel {
|
|
|
412
473
|
}
|
|
413
474
|
queue.cancelActive?.(messageId);
|
|
414
475
|
}
|
|
476
|
+
// ── bg-session 生命周期 ──
|
|
477
|
+
handleBgSessionPayload(runId, slot, payload) {
|
|
478
|
+
if (slot.terminated)
|
|
479
|
+
return;
|
|
480
|
+
switch (payload.kind) {
|
|
481
|
+
case 'result.text':
|
|
482
|
+
if (payload.isFinal)
|
|
483
|
+
slot.text = payload.text;
|
|
484
|
+
else
|
|
485
|
+
slot.text += payload.text;
|
|
486
|
+
break;
|
|
487
|
+
case 'status.completed':
|
|
488
|
+
this.finishBgSession(runId, slot, 'completed');
|
|
489
|
+
break;
|
|
490
|
+
case 'result.error':
|
|
491
|
+
this.finishBgSession(runId, slot, 'error', payload.text ?? payload.reason ?? 'unknown error');
|
|
492
|
+
break;
|
|
493
|
+
case 'system.error':
|
|
494
|
+
this.finishBgSession(runId, slot, 'error', payload.text ?? payload.subtype ?? 'unknown error');
|
|
495
|
+
break;
|
|
496
|
+
case 'status.error':
|
|
497
|
+
this.finishBgSession(runId, slot, 'error', payload.metadata?.message ?? 'status error');
|
|
498
|
+
break;
|
|
499
|
+
case 'status.interrupted':
|
|
500
|
+
{
|
|
501
|
+
const reason = typeof payload.metadata?.reason === 'string'
|
|
502
|
+
? payload.metadata.reason
|
|
503
|
+
: 'interrupted';
|
|
504
|
+
const outcome = slot.abortSignal?.aborted ? 'cancelled' : 'interrupted';
|
|
505
|
+
this.finishBgSession(runId, slot, outcome, reason);
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
case 'status.timeout':
|
|
509
|
+
this.finishBgSession(runId, slot, 'error', 'bg-session watchdog timeout');
|
|
510
|
+
break;
|
|
511
|
+
default:
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
finishBgSession(runId, slot, outcome, error) {
|
|
516
|
+
if (slot.terminated)
|
|
517
|
+
return;
|
|
518
|
+
slot.terminated = true;
|
|
519
|
+
if (slot.watchdogTimer)
|
|
520
|
+
clearTimeout(slot.watchdogTimer);
|
|
521
|
+
if (slot.abortListener)
|
|
522
|
+
slot.abortSignal?.removeEventListener('abort', slot.abortListener);
|
|
523
|
+
this.bgPending.delete(runId);
|
|
524
|
+
// 会话结束后按清理策略处理
|
|
525
|
+
const policy = slot.cleanupPolicy;
|
|
526
|
+
if (policy.mode === 'immediate') {
|
|
527
|
+
void cleanupSessionRecord({ sessionId: slot.sessionId, channelId: slot.channelId, agentAid: slot.agentAid }, this.sessionManager);
|
|
528
|
+
}
|
|
529
|
+
else if (policy.mode === 'on_next_launch') {
|
|
530
|
+
recordNextLaunchCleanup(slot.kind, {
|
|
531
|
+
channelId: slot.channelId,
|
|
532
|
+
sessionId: slot.sessionId,
|
|
533
|
+
agentAid: slot.agentAid,
|
|
534
|
+
recordedAt: Date.now(),
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
else if (policy.mode === 'expire') {
|
|
538
|
+
appendExpireEntry({
|
|
539
|
+
expireAt: Date.now() + policy.ttlMs,
|
|
540
|
+
sessionId: slot.sessionId,
|
|
541
|
+
channelId: slot.channelId,
|
|
542
|
+
kind: slot.kind,
|
|
543
|
+
agentAid: slot.agentAid,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
// 'keep' 模式:不做任何处理
|
|
547
|
+
slot.resolve({
|
|
548
|
+
sessionId: slot.sessionId,
|
|
549
|
+
outcome,
|
|
550
|
+
...(slot.text ? { text: slot.text } : {}),
|
|
551
|
+
...(error ? { error } : {}),
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* 拉起一个独立后台会话。会话创建、首条消息入队后即返回 handle;
|
|
556
|
+
* 会话结束由 handle.result 通知。实现 BackgroundSessionLauncher 接口。
|
|
557
|
+
* 详见 docs/background-session/background-session-launcher.md
|
|
558
|
+
*/
|
|
559
|
+
async launchBackgroundSession(spec) {
|
|
560
|
+
const runId = `bg-${spec.kind}-${spec.instanceId}`;
|
|
561
|
+
const channelId = `bg:${spec.kind}:${spec.instanceId}`;
|
|
562
|
+
const cleanupPolicy = spec.cleanupPolicy ?? { mode: 'on_next_launch' };
|
|
563
|
+
// 启动前清理:on_next_launch 清理同 kind 历史,expire 扫描到期条目
|
|
564
|
+
if (cleanupPolicy.mode === 'on_next_launch') {
|
|
565
|
+
await cleanupPreviousByKind(spec.kind, this.sessionManager);
|
|
566
|
+
}
|
|
567
|
+
else if (cleanupPolicy.mode !== 'keep') {
|
|
568
|
+
// immediate 和 expire 模式都在启动时跑一次 expire 扫描
|
|
569
|
+
await runExpireCleanup(this.sessionManager);
|
|
570
|
+
}
|
|
571
|
+
const session = await this.sessionManager.getOrCreateSession(this.channelName, channelId, spec.projectPath, channelId, // threadId,每次独立,保证 by_run 语义
|
|
572
|
+
{
|
|
573
|
+
channelKey: this.channelKey,
|
|
574
|
+
peerId: `bg:${spec.kind}`,
|
|
575
|
+
peerName: spec.kind,
|
|
576
|
+
peerType: 'system',
|
|
577
|
+
...(spec.metadata ?? {}),
|
|
578
|
+
}, spec.kind, undefined, 'private', spec.baseagent, spec.agentAid, this.channelName, 'system');
|
|
579
|
+
const promise = new Promise((resolve) => {
|
|
580
|
+
const slot = {
|
|
581
|
+
resolve,
|
|
582
|
+
sessionId: session.id,
|
|
583
|
+
channelId,
|
|
584
|
+
kind: spec.kind,
|
|
585
|
+
startedAt: Date.now(),
|
|
586
|
+
abortSignal: spec.signal,
|
|
587
|
+
text: '',
|
|
588
|
+
terminated: false,
|
|
589
|
+
cleanupPolicy,
|
|
590
|
+
agentAid: spec.agentAid,
|
|
591
|
+
runtimeDir: spec.runtimeDir,
|
|
592
|
+
watchdogTimer: setTimeout(() => {
|
|
593
|
+
this.finishBgSession(runId, slot, 'error', 'bg-session watchdog timeout');
|
|
594
|
+
}, this.watchdogMs),
|
|
595
|
+
};
|
|
596
|
+
slot.watchdogTimer?.unref?.();
|
|
597
|
+
if (spec.signal) {
|
|
598
|
+
const onAbort = () => {
|
|
599
|
+
void this.cancelDelivery(runId).catch(() => { });
|
|
600
|
+
};
|
|
601
|
+
slot.abortListener = onAbort;
|
|
602
|
+
spec.signal.addEventListener('abort', onAbort, { once: true });
|
|
603
|
+
if (spec.signal.aborted)
|
|
604
|
+
onAbort();
|
|
605
|
+
}
|
|
606
|
+
this.bgPending.set(runId, slot);
|
|
607
|
+
});
|
|
608
|
+
const message = {
|
|
609
|
+
channel: this.channelName,
|
|
610
|
+
channelType: this.channelName,
|
|
611
|
+
channelId: session.channelId,
|
|
612
|
+
selfAID: spec.agentAid,
|
|
613
|
+
baseagent: spec.baseagent,
|
|
614
|
+
threadId: session.threadId || '',
|
|
615
|
+
chatType: 'private',
|
|
616
|
+
peerId: `bg:${spec.kind}`,
|
|
617
|
+
peerName: spec.kind,
|
|
618
|
+
peerType: 'system',
|
|
619
|
+
content: spec.prompt,
|
|
620
|
+
messageId: runId,
|
|
621
|
+
timestamp: Date.now(),
|
|
622
|
+
source: 'bg-session',
|
|
623
|
+
bgSessionMeta: {
|
|
624
|
+
kind: spec.kind,
|
|
625
|
+
runId,
|
|
626
|
+
instanceId: spec.instanceId,
|
|
627
|
+
boundSessionId: session.id,
|
|
628
|
+
},
|
|
629
|
+
replyContext: {
|
|
630
|
+
metadata: { bgSessionRunId: runId },
|
|
631
|
+
},
|
|
632
|
+
};
|
|
633
|
+
void this.messageQueue.enqueue?.(session.id, message, spec.projectPath, {
|
|
634
|
+
interruptible: false,
|
|
635
|
+
agentName: spec.agentAid,
|
|
636
|
+
sessionKeyField: session.sessionKey,
|
|
637
|
+
selfAID: spec.agentAid,
|
|
638
|
+
});
|
|
639
|
+
let settled = false;
|
|
640
|
+
void promise.then(() => { settled = true; });
|
|
641
|
+
const sessionId = session.id;
|
|
642
|
+
return {
|
|
643
|
+
sessionId,
|
|
644
|
+
result: promise,
|
|
645
|
+
waitForAgentSessionId: (opts) => this.waitForAgentSessionId(sessionId, () => settled, opts),
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* 轮询 session 记录,等 runner 写入 agentSessionId。
|
|
650
|
+
* 会话刚创建时只有 evolcore sessionId,agentSessionId 要等 runner 初始化并开始第一轮才写入。
|
|
651
|
+
* 会话结束后再读一次即停止,避免对已结束的会话空等到超时。
|
|
652
|
+
*/
|
|
653
|
+
async waitForAgentSessionId(sessionId, isSettled, opts = {}) {
|
|
654
|
+
const deadline = Date.now() + (opts.timeoutMs ?? 10_000);
|
|
655
|
+
const interval = opts.intervalMs ?? 250;
|
|
656
|
+
while (true) {
|
|
657
|
+
const settledBeforeRead = isSettled();
|
|
658
|
+
try {
|
|
659
|
+
const id = (await this.sessionManager.getSessionById(sessionId))?.agentSessionId;
|
|
660
|
+
if (id)
|
|
661
|
+
return id;
|
|
662
|
+
}
|
|
663
|
+
catch { /* 会话记录写到一半,下一轮再读 */ }
|
|
664
|
+
if (settledBeforeRead || Date.now() >= deadline)
|
|
665
|
+
return undefined;
|
|
666
|
+
await new Promise(r => setTimeout(r, interval));
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/** 实现 BackgroundSessionLauncher 接口 */
|
|
670
|
+
launch(spec) {
|
|
671
|
+
return this.launchBackgroundSession(spec);
|
|
672
|
+
}
|
|
415
673
|
}
|
|
416
674
|
function isObject(value) {
|
|
417
675
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -41,6 +41,30 @@ function formatBytes(bytes) {
|
|
|
41
41
|
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
42
42
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
43
43
|
}
|
|
44
|
+
/** 终端显示宽度:CJK 及全角字符占 2 列,ASCII/拉丁占 1 列 */
|
|
45
|
+
function displayWidth(s) {
|
|
46
|
+
let w = 0;
|
|
47
|
+
for (const ch of s) {
|
|
48
|
+
const cp = ch.codePointAt(0);
|
|
49
|
+
if ((cp >= 0x1100 && cp <= 0x115F) ||
|
|
50
|
+
(cp >= 0x2E80 && cp <= 0x303E) ||
|
|
51
|
+
(cp >= 0x3040 && cp <= 0x33FF) ||
|
|
52
|
+
(cp >= 0x3400 && cp <= 0x9FFF) ||
|
|
53
|
+
(cp >= 0xAC00 && cp <= 0xD7AF) ||
|
|
54
|
+
(cp >= 0xF900 && cp <= 0xFAFF) ||
|
|
55
|
+
(cp >= 0xFF01 && cp <= 0xFF60) ||
|
|
56
|
+
(cp >= 0xFFE0 && cp <= 0xFFE6)) {
|
|
57
|
+
w += 2;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
w += 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return w;
|
|
64
|
+
}
|
|
65
|
+
function padEndDisplay(s, targetWidth) {
|
|
66
|
+
return s + ' '.repeat(Math.max(0, targetWidth - displayWidth(s)));
|
|
67
|
+
}
|
|
44
68
|
// ==================== Agent ====================
|
|
45
69
|
export async function cmdAgent(args) {
|
|
46
70
|
args = canonicalizeEvolcoreCommandArgv(['ec', 'agent', ...args]).slice(2);
|
|
@@ -78,8 +102,8 @@ Commands:
|
|
|
78
102
|
persona show 查看当前 Agent 的 persona.md
|
|
79
103
|
persona --text <text> 用文本设置当前 Agent 的 persona.md
|
|
80
104
|
persona --file <path> 用 .md/.txt 文件设置当前 Agent 的 persona.md
|
|
81
|
-
welcome show
|
|
82
|
-
welcome --text <text> 保存
|
|
105
|
+
welcome show 查看 Agent 自主准备的 Bootstrap welcome 文案
|
|
106
|
+
welcome --text <text> 保存 Agent 自主准备的 Bootstrap welcome 文案
|
|
83
107
|
rename <aid> <name> 修改 agent.md 中的显示名称
|
|
84
108
|
ready <aid> 标记 bootstrap 完成,进入 active 状态
|
|
85
109
|
reload [aid] 热重载配置(无参数=全量 resync)
|
|
@@ -220,25 +244,28 @@ Options:
|
|
|
220
244
|
// 表头跟随系统语言
|
|
221
245
|
const isChinese = isHostChinese();
|
|
222
246
|
const headers = isChinese
|
|
223
|
-
? { name: '名称', personal: 'Personal名', status: '状态', channels: '渠道', project: '项目', baseagent: '基座', lastActive: '最后活跃' }
|
|
224
|
-
: { name: 'NAME', personal: 'PERSONAL', status: 'STATUS', channels: 'CHANNELS', project: 'PROJECT', baseagent: 'BASEAGENT', lastActive: 'LAST ACTIVE' };
|
|
225
|
-
//
|
|
226
|
-
let maxNameLen = headers.name
|
|
227
|
-
let maxPersonalLen = headers.personal
|
|
228
|
-
let maxStatusLen = headers.status
|
|
229
|
-
let maxChannelsLen = headers.channels
|
|
230
|
-
let maxProjectLen = headers.project
|
|
231
|
-
let maxBaseagentLen = headers.baseagent
|
|
247
|
+
? { name: '名称', personal: 'Personal名', status: '状态', channels: '渠道', project: '项目', baseagent: '基座', model: '模型', lastActive: '最后活跃' }
|
|
248
|
+
: { name: 'NAME', personal: 'PERSONAL', status: 'STATUS', channels: 'CHANNELS', project: 'PROJECT', baseagent: 'BASEAGENT', model: 'MODEL', lastActive: 'LAST ACTIVE' };
|
|
249
|
+
// 计算各列实际需要的显示宽度(CJK 字符占 2 列)
|
|
250
|
+
let maxNameLen = displayWidth(headers.name);
|
|
251
|
+
let maxPersonalLen = displayWidth(headers.personal);
|
|
252
|
+
let maxStatusLen = displayWidth(headers.status);
|
|
253
|
+
let maxChannelsLen = displayWidth(headers.channels);
|
|
254
|
+
let maxProjectLen = displayWidth(headers.project);
|
|
255
|
+
let maxBaseagentLen = displayWidth(headers.baseagent);
|
|
256
|
+
let maxModelLen = displayWidth(headers.model);
|
|
232
257
|
for (const info of result.agents) {
|
|
233
|
-
maxNameLen = Math.max(maxNameLen, info.name
|
|
234
|
-
maxPersonalLen = Math.max(maxPersonalLen, (info.personalName || '—')
|
|
235
|
-
const
|
|
236
|
-
|
|
258
|
+
maxNameLen = Math.max(maxNameLen, displayWidth(info.name));
|
|
259
|
+
maxPersonalLen = Math.max(maxPersonalLen, displayWidth(info.personalName || '—'));
|
|
260
|
+
const lcSuffix = info.lifecycle && info.lifecycle !== 'active' ? ` (${info.lifecycle})` : '';
|
|
261
|
+
const displayedStatus = `${info.status || 'unknown'}${info.stale ? ' (stale)' : ''}${lcSuffix}`;
|
|
262
|
+
maxStatusLen = Math.max(maxStatusLen, displayWidth(displayedStatus));
|
|
237
263
|
const channelsStr = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
238
|
-
maxChannelsLen = Math.max(maxChannelsLen, channelsStr
|
|
264
|
+
maxChannelsLen = Math.max(maxChannelsLen, displayWidth(channelsStr));
|
|
239
265
|
const projectStr = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
240
|
-
maxProjectLen = Math.max(maxProjectLen, projectStr
|
|
241
|
-
maxBaseagentLen = Math.max(maxBaseagentLen, (info.baseagent || '—')
|
|
266
|
+
maxProjectLen = Math.max(maxProjectLen, displayWidth(projectStr));
|
|
267
|
+
maxBaseagentLen = Math.max(maxBaseagentLen, displayWidth(info.baseagent || '—'));
|
|
268
|
+
maxModelLen = Math.max(maxModelLen, displayWidth(info.model || '—'));
|
|
242
269
|
}
|
|
243
270
|
// 加 2 作为列间距
|
|
244
271
|
const colName = maxNameLen + 2;
|
|
@@ -247,18 +274,25 @@ Options:
|
|
|
247
274
|
const colChannels = maxChannelsLen + 1;
|
|
248
275
|
const colProject = maxProjectLen + 2;
|
|
249
276
|
const colBaseagent = maxBaseagentLen + 2;
|
|
250
|
-
|
|
251
|
-
|
|
277
|
+
const colModel = maxModelLen + 2;
|
|
278
|
+
console.log(padEndDisplay(headers.name, colName) + padEndDisplay(headers.personal, colPersonal) +
|
|
279
|
+
padEndDisplay(headers.status, colStatus) + padEndDisplay(headers.channels, colChannels) +
|
|
280
|
+
padEndDisplay(headers.project, colProject) + padEndDisplay(headers.baseagent, colBaseagent) +
|
|
281
|
+
padEndDisplay(headers.model, colModel) + headers.lastActive);
|
|
252
282
|
for (const info of result.agents) {
|
|
253
283
|
const name = info.name;
|
|
254
284
|
const personal = info.personalName || '—';
|
|
255
|
-
const
|
|
285
|
+
const lcSuffix = info.lifecycle && info.lifecycle !== 'active' ? ` (${info.lifecycle})` : '';
|
|
286
|
+
const status = `${info.status || 'unknown'}${info.stale ? ' (stale)' : ''}${lcSuffix}`;
|
|
256
287
|
const channels = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
257
288
|
const project = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
258
289
|
const baseagent = info.baseagent || '—';
|
|
290
|
+
const model = info.model || '—';
|
|
259
291
|
const lastActive = info.lastActivity ? formatTimeAgo(Date.now() - info.lastActivity) : '—';
|
|
260
|
-
console.log(name
|
|
261
|
-
|
|
292
|
+
console.log(padEndDisplay(name, colName) + padEndDisplay(personal, colPersonal) +
|
|
293
|
+
padEndDisplay(status, colStatus) + padEndDisplay(channels, colChannels) +
|
|
294
|
+
padEndDisplay(project, colProject) + padEndDisplay(baseagent, colBaseagent) +
|
|
295
|
+
padEndDisplay(model, colModel) + lastActive);
|
|
262
296
|
}
|
|
263
297
|
return;
|
|
264
298
|
}
|
|
@@ -697,7 +731,7 @@ Options:
|
|
|
697
731
|
console.log(`用法: ${MAIN_CLI_BIN} agent welcome show [--format json]
|
|
698
732
|
${MAIN_CLI_BIN} agent welcome --text <text> [--format json]
|
|
699
733
|
|
|
700
|
-
读取或保存当前 Agent 在 Bootstrap
|
|
734
|
+
读取或保存当前 Agent 在 Bootstrap 中自主准备的 welcome 文案。文案固定保存到 personal/welcome.md。`);
|
|
701
735
|
return;
|
|
702
736
|
}
|
|
703
737
|
const result = { ok: false, code: 'INVALID_ARGS', error: error.message };
|
package/dist/cli/agent.js
CHANGED
|
@@ -553,7 +553,7 @@ export async function agentPersona(aid, options = {}) {
|
|
|
553
553
|
return { ok: false, code: 'WRITE_FAILED', error: e?.message || String(e) };
|
|
554
554
|
}
|
|
555
555
|
}
|
|
556
|
-
/** Read or replace the current Agent's
|
|
556
|
+
/** Read or replace the current Agent's self-prepared Bootstrap welcome template. */
|
|
557
557
|
export async function agentWelcome(aid, options = {}) {
|
|
558
558
|
if (!isValidAid(aid))
|
|
559
559
|
return { ok: false, code: 'INVALID_ARGS', error: 'Invalid Agent AID' };
|