evolcore 0.0.32 → 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 +24 -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 +23 -19
- package/dist/agents/ecagent-runner.js +5 -13
- 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 +28 -2
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +365 -29
- package/dist/cli/contact.js +8 -2
- package/dist/cli/daemon-commands.js +81 -12
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/handoff-command.js +3 -0
- 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 +8 -1
- 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 +620 -0
- package/dist/cli/trigger-command.js +4 -2
- package/dist/config/builtin-roles.js +1 -0
- 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/auth/agent-delegation.js +25 -1
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +33 -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/command-handler.js +60 -24
- 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/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +139 -2
- 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 +161 -6
- 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/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +349 -67
- 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/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/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 +8 -5
- package/dist/ipc.js +89 -10
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +1 -0
- 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/thread-chain.md +267 -0
- 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/05-venue.md +1 -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 +1 -0
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TriggerDefinitionManager } from '../trigger/manager.js';
|
|
2
|
+
import { ensureSessionRuntimeDir } from '../cli/task-context.js';
|
|
2
3
|
import { logger } from '../utils/logger.js';
|
|
3
4
|
import crypto from 'crypto';
|
|
4
5
|
import fs from 'fs';
|
|
@@ -6,6 +7,10 @@ import path from 'path';
|
|
|
6
7
|
import { executeTaskResume } from './task/task-resume-operation.js';
|
|
7
8
|
import { isHClassPath, isLClassPath, isSameOrDescendant } from './permission/protected-paths.js';
|
|
8
9
|
import { resolveStorageCommandArgv } from '../aun/storage/index.js';
|
|
10
|
+
import { agentDir } from '../paths.js';
|
|
11
|
+
import { parsePeerKey } from './relation/peer-identity.js';
|
|
12
|
+
import { resolvePeerRoleDetail } from '../config/peer-role-resolver.js';
|
|
13
|
+
import { buildDebugTaskContext } from './auth/debug-context.js';
|
|
9
14
|
import { computeBuildId } from '../utils/evolcore-version.js';
|
|
10
15
|
import { loadAgent } from '../config-store.js';
|
|
11
16
|
import { executeLocalInitializationCommand, hasSuppliedInitialization } from '../cli/agent-create.js';
|
|
@@ -17,6 +22,7 @@ import { registerHandoffIpc } from './handoff/ipc-registration.js';
|
|
|
17
22
|
import { registerAunIpcSender } from './message/aun-ipc-sender.js';
|
|
18
23
|
import { registerConsolidationCheckExecutor, registerConsolidationLaunchExecutor, } from './memory/consolidation.js';
|
|
19
24
|
import { createTriggerBaseagentResolver, registerTriggerIpcExecutor } from '../trigger/ipc-service.js';
|
|
25
|
+
import { createThreadOperationExecutor } from './thread/ipc-operation.js';
|
|
20
26
|
import { isLinkedInstall } from '../utils/npm-ops.js';
|
|
21
27
|
export function registerDaemonIpc({ ipcServer, agentRegistry, agentDelegationRegistry, sessionManager, statsCollector, aidStatsCollector, responseEngine, messageQueue, agentMap, commandHandler, channelInstances, eventBus, handoffRuntime, reloadCoordinator, agentApplicationService, agentInitializationService, enqueueConsolidation, completeAgentBootstrap, contactBinds, bindService, diagService, getProcessOwners, getControlAidState, daemonAid, triggerControlToken, triggerSchedulers, daemonTriggerOwner, primaryAgent, triggerAuthorization, }) {
|
|
22
28
|
const authorizeManagedAgentOperation = createDelegationAuthorizer({
|
|
@@ -52,6 +58,137 @@ export function registerDaemonIpc({ ipcServer, agentRegistry, agentDelegationReg
|
|
|
52
58
|
agentMap,
|
|
53
59
|
isLinkedInstall: isLinkedInstall(),
|
|
54
60
|
});
|
|
61
|
+
if (process.env.EVOLCORE_DEBUG_MODE === '1') {
|
|
62
|
+
ipcServer.setDebugIssueTokenExecutor(async ({ sessionId }) => {
|
|
63
|
+
try {
|
|
64
|
+
const activeContext = responseEngine.getTaskRuntimeContext(sessionId);
|
|
65
|
+
let runtimeContext = activeContext;
|
|
66
|
+
if (!runtimeContext) {
|
|
67
|
+
const session = await sessionManager.getSessionById(sessionId);
|
|
68
|
+
if (!session)
|
|
69
|
+
return { ok: false, error: `session not found: ${sessionId}` };
|
|
70
|
+
runtimeContext = buildDebugTaskContext({
|
|
71
|
+
session,
|
|
72
|
+
getTrigger: (agentAid, triggerId) => new TriggerDefinitionManager(agentAid).get(triggerId),
|
|
73
|
+
sessionRuntimeDir: ensureSessionRuntimeDir(session.id),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (!runtimeContext.selfAid)
|
|
77
|
+
return { ok: false, error: 'debug session has no selfAid' };
|
|
78
|
+
const channelType = runtimeContext.channel || 'aun';
|
|
79
|
+
const channel = runtimeContext.channelKey || channelType;
|
|
80
|
+
const peerKeyId = (() => {
|
|
81
|
+
try {
|
|
82
|
+
return parsePeerKey(runtimeContext.peerKey || '').channelId;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return runtimeContext.peerId || runtimeContext.channelId || '';
|
|
86
|
+
}
|
|
87
|
+
})();
|
|
88
|
+
const chatType = runtimeContext.chatType === 'group' ? 'group' : 'private';
|
|
89
|
+
const conversationId = chatType === 'group'
|
|
90
|
+
? (runtimeContext.channelId || peerKeyId)
|
|
91
|
+
: peerKeyId;
|
|
92
|
+
const peerRole = resolvePeerRoleDetail({
|
|
93
|
+
selfAid: runtimeContext.selfAid,
|
|
94
|
+
channelKey: channel,
|
|
95
|
+
channelType,
|
|
96
|
+
chatType,
|
|
97
|
+
actorId: peerKeyId,
|
|
98
|
+
conversationId,
|
|
99
|
+
});
|
|
100
|
+
const token = agentDelegationRegistry.issueDebug({
|
|
101
|
+
sessionId,
|
|
102
|
+
selfAid: runtimeContext.selfAid,
|
|
103
|
+
channel,
|
|
104
|
+
channelType,
|
|
105
|
+
chatType,
|
|
106
|
+
peerKey: runtimeContext.peerKey || '',
|
|
107
|
+
actorId: peerKeyId || runtimeContext.selfAid,
|
|
108
|
+
issuedRole: peerRole.effectiveRole || 'none',
|
|
109
|
+
});
|
|
110
|
+
return { ok: true, token, runtimeContext };
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
ipcServer.setThreadOperationExecutor(createThreadOperationExecutor({
|
|
118
|
+
sessionManager,
|
|
119
|
+
agentDelegationRegistry,
|
|
120
|
+
getProcessOwners,
|
|
121
|
+
sendSystemMsg: async (params) => {
|
|
122
|
+
if (ipcServer.hasAunMsgSender()) {
|
|
123
|
+
const sent = await ipcServer.sendAunMsg({
|
|
124
|
+
aid: params.from,
|
|
125
|
+
to: params.to,
|
|
126
|
+
scope: params.peerKind === 'group' ? 'group' : 'msg',
|
|
127
|
+
payload: { type: 'topic-created', topicName: params.topicName, threadId: params.threadId },
|
|
128
|
+
thread: params.threadId,
|
|
129
|
+
});
|
|
130
|
+
if (!sent?.ok)
|
|
131
|
+
throw new Error(sent?.error || 'Topic notification failed');
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
onDeleted: (aid, threadId) => {
|
|
135
|
+
const scheduler = triggerSchedulers.get(aid);
|
|
136
|
+
if (scheduler) {
|
|
137
|
+
const related = scheduler.list({ all: true }).filter((trigger) => trigger.origin?.threadId === threadId
|
|
138
|
+
|| trigger.labels?.originThreadId === threadId
|
|
139
|
+
|| trigger.labels?.targetThreadId === threadId);
|
|
140
|
+
for (const trigger of related) {
|
|
141
|
+
try {
|
|
142
|
+
scheduler.delete(trigger.id);
|
|
143
|
+
}
|
|
144
|
+
catch { }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
}));
|
|
149
|
+
ipcServer.setSkillRefreshExecutor(async (aid, sessionId, delegationToken, delegationCommandHash) => {
|
|
150
|
+
const fail = (code, error) => ({ ok: false, code, error });
|
|
151
|
+
if (!agentRegistry.get(aid))
|
|
152
|
+
return fail('AGENT_NOT_FOUND', `Agent "${aid}" not found`);
|
|
153
|
+
if (sessionId) {
|
|
154
|
+
const authorization = await authorizeManagedAgentOperation({
|
|
155
|
+
sessionId,
|
|
156
|
+
delegationToken,
|
|
157
|
+
delegationCommandHash,
|
|
158
|
+
targetAid: aid,
|
|
159
|
+
}, 'skill.refresh');
|
|
160
|
+
if (!authorization.ok)
|
|
161
|
+
return fail(authorization.code, authorization.error);
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const { refreshSkillIndex } = await import('./skill/startup.js');
|
|
165
|
+
refreshSkillIndex(agentDir(aid), aid);
|
|
166
|
+
const skillsDir = path.join(agentDir(aid), 'skills');
|
|
167
|
+
return {
|
|
168
|
+
ok: true,
|
|
169
|
+
result: {
|
|
170
|
+
aid,
|
|
171
|
+
builtinIndexPath: path.join(skillsDir, '.index-builtin.md'),
|
|
172
|
+
localIndexPath: path.join(skillsDir, '.index-local.md'),
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return fail('INTERNAL', error?.message || String(error));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
commandHandler.setSkillIndexRefresher(async (aid) => {
|
|
181
|
+
if (!agentRegistry.get(aid))
|
|
182
|
+
return { ok: false, error: `Agent "${aid}" not found` };
|
|
183
|
+
try {
|
|
184
|
+
const { refreshSkillIndex } = await import('./skill/startup.js');
|
|
185
|
+
refreshSkillIndex(agentDir(aid), aid);
|
|
186
|
+
return { ok: true };
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
190
|
+
}
|
|
191
|
+
});
|
|
55
192
|
registerHandoffIpc({
|
|
56
193
|
ipcServer,
|
|
57
194
|
agentDelegationRegistry,
|
|
@@ -61,7 +198,7 @@ export function registerDaemonIpc({ ipcServer, agentRegistry, agentDelegationReg
|
|
|
61
198
|
handoffRuntime,
|
|
62
199
|
getProcessOwners,
|
|
63
200
|
});
|
|
64
|
-
registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime });
|
|
201
|
+
registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime, triggerSchedulers });
|
|
65
202
|
ipcServer.setAgentReloadExecutor((aid, options) => reloadCoordinator.reload(aid, options));
|
|
66
203
|
registerConsolidationCheckExecutor({ ipcServer, agentRegistry, enqueueConsolidation });
|
|
67
204
|
ipcServer.setInitializationExecutor(async (command) => executeLocalInitializationCommand(agentInitializationService, command));
|
|
@@ -212,7 +349,7 @@ export function registerRuntimeIpcProviders({ ipcServer, channelInstances, messa
|
|
|
212
349
|
const session = await sessionManager.getSessionById(sessionId);
|
|
213
350
|
if (!session)
|
|
214
351
|
return null;
|
|
215
|
-
const { buildDebugTaskContext } = await import('./
|
|
352
|
+
const { buildDebugTaskContext } = await import('./auth/debug-context.js');
|
|
216
353
|
const { ensureSessionRuntimeDir } = await import('../cli/task-context.js');
|
|
217
354
|
return buildDebugTaskContext({
|
|
218
355
|
session,
|
|
@@ -34,7 +34,14 @@ export const MODE_SCOPE = {
|
|
|
34
34
|
// 超期判据集中在 staleness.ts;这里做转发,保证 trigger 与 context 用同一套规则
|
|
35
35
|
export { DEFAULT_FORGET_AFTER_DAYS, LONG_TERM_REVIEW_DAYS } from './staleness.js';
|
|
36
36
|
// ── Checkpoint 读写 ──────────────────────────────────────────────────────────
|
|
37
|
-
const
|
|
37
|
+
const LEGACY_CHECKPOINT_FILE = 'consolidation-checkpoint.json';
|
|
38
|
+
/** C1: checkpoint file name per mode; forget uses scopeOverride suffix. */
|
|
39
|
+
function checkpointFileName(mode, scopeOverride) {
|
|
40
|
+
if (mode === 'forget' && scopeOverride) {
|
|
41
|
+
return `consolidation-checkpoint-forget-${scopeOverride}.json`;
|
|
42
|
+
}
|
|
43
|
+
return `consolidation-checkpoint-${mode}.json`;
|
|
44
|
+
}
|
|
38
45
|
/**
|
|
39
46
|
* Move the pre-0.0.29 consolidation artifact root to its new name. Existing
|
|
40
47
|
* batches are user data, so conflicts are preserved under a legacy suffix
|
|
@@ -70,22 +77,37 @@ export function migrateLegacyConsolidationBatches() {
|
|
|
70
77
|
fs.rmSync(legacyRoot, { recursive: true, force: true });
|
|
71
78
|
}
|
|
72
79
|
/** 读上次整理时间戳(毫秒)。文件不存在或解析失败返回 0(取全量历史)。 */
|
|
73
|
-
export function readCheckpoint(chatDir) {
|
|
80
|
+
export function readCheckpoint(chatDir, mode, scopeOverride) {
|
|
81
|
+
const filePath = path.join(chatDir, checkpointFileName(mode, scopeOverride));
|
|
74
82
|
try {
|
|
75
|
-
const raw = fs.readFileSync(
|
|
83
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
76
84
|
const parsed = JSON.parse(raw);
|
|
77
85
|
const v = Number(parsed.lastConsolidatedAt);
|
|
78
|
-
|
|
86
|
+
if (Number.isFinite(v))
|
|
87
|
+
return v;
|
|
79
88
|
}
|
|
80
|
-
catch {
|
|
81
|
-
|
|
89
|
+
catch { /* 新文件不存在,尝试迁移旧文件 */ }
|
|
90
|
+
// 向后兼容: notes 模式读旧的 consolidation-checkpoint.json,成功后自动迁移
|
|
91
|
+
if (mode === 'notes') {
|
|
92
|
+
try {
|
|
93
|
+
const legacyPath = path.join(chatDir, LEGACY_CHECKPOINT_FILE);
|
|
94
|
+
const raw = fs.readFileSync(legacyPath, 'utf-8');
|
|
95
|
+
const parsed = JSON.parse(raw);
|
|
96
|
+
const v = Number(parsed.lastConsolidatedAt);
|
|
97
|
+
if (Number.isFinite(v)) {
|
|
98
|
+
writeCheckpoint(chatDir, v, mode, scopeOverride);
|
|
99
|
+
return v;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch { /* ignore */ }
|
|
82
103
|
}
|
|
104
|
+
return 0;
|
|
83
105
|
}
|
|
84
106
|
/** 写整理完成时间戳。由 index.ts 在整理会话完成后调用。 */
|
|
85
|
-
export function writeCheckpoint(chatDir, lastConsolidatedAt) {
|
|
107
|
+
export function writeCheckpoint(chatDir, lastConsolidatedAt, mode, scopeOverride) {
|
|
86
108
|
try {
|
|
87
109
|
fs.mkdirSync(chatDir, { recursive: true });
|
|
88
|
-
fs.writeFileSync(path.join(chatDir,
|
|
110
|
+
fs.writeFileSync(path.join(chatDir, checkpointFileName(mode, scopeOverride)), JSON.stringify({ lastConsolidatedAt }, null, 2) + '\n', 'utf-8');
|
|
89
111
|
}
|
|
90
112
|
catch { /* 失败静默 */ }
|
|
91
113
|
}
|
|
@@ -276,7 +298,7 @@ function shortTime(time) {
|
|
|
276
298
|
*
|
|
277
299
|
* 返回生成的文件路径列表(空列表表示无增量消息或读取失败)。
|
|
278
300
|
*/
|
|
279
|
-
function buildThreadSnapshots(historyJsonlPath, sinceMs, threadsDir) {
|
|
301
|
+
function buildThreadSnapshots(historyJsonlPath, sinceMs, threadsDir, throughMs) {
|
|
280
302
|
const rawLines = fs.readFileSync(historyJsonlPath, 'utf-8').split('\n');
|
|
281
303
|
// 第一遍:扫全量行(不限 sinceMs)收集会话元信息
|
|
282
304
|
let chatType = 'private';
|
|
@@ -312,7 +334,7 @@ function buildThreadSnapshots(historyJsonlPath, sinceMs, threadsDir) {
|
|
|
312
334
|
try {
|
|
313
335
|
const entry = JSON.parse(line);
|
|
314
336
|
const ts = typeof entry.ts === 'number' ? entry.ts : 0;
|
|
315
|
-
if (ts
|
|
337
|
+
if (ts <= sinceMs || ts > throughMs)
|
|
316
338
|
continue;
|
|
317
339
|
const content = typeof entry.content === 'string' ? entry.content.trim() : '';
|
|
318
340
|
if (!content)
|
|
@@ -410,26 +432,36 @@ export function serializeConsolidationContext(input) {
|
|
|
410
432
|
const selfAid = input.taskCtx.selfAid ?? 'unknown';
|
|
411
433
|
// 目录名:YYYYMMDDTHHmmss-<selfAid>-<mode>
|
|
412
434
|
const prettyTs = now.toISOString().replace(/[-:]/g, '').slice(0, 15);
|
|
413
|
-
|
|
435
|
+
fs.mkdirSync(batchRoot, { recursive: true });
|
|
436
|
+
const batchDir = fs.mkdtempSync(path.join(batchRoot, `${prettyTs}-${selfAid}-${input.mode}-`));
|
|
414
437
|
fs.mkdirSync(batchDir, { recursive: true });
|
|
415
438
|
// ── 话题快照(notes / compress / session-new 模式,有 peerId 时生成)───────
|
|
416
439
|
let historyJsonlPath;
|
|
417
440
|
let threadFiles;
|
|
418
441
|
let chatDir;
|
|
419
|
-
|
|
442
|
+
// C2: generate thread snapshots for all modes that operate on relation-level memories
|
|
443
|
+
const needsThreadSnapshots = (input.mode === 'notes'
|
|
444
|
+
|| input.mode === 'generalize'
|
|
445
|
+
|| input.mode === 'promote'
|
|
446
|
+
|| input.mode === 'profile'
|
|
447
|
+
|| (input.mode === 'forget' && input.scopeOverride === 'relation')
|
|
448
|
+
|| input.triggeredBy === 'compress'
|
|
449
|
+
|| input.triggeredBy === 'session-new');
|
|
450
|
+
if (needsThreadSnapshots
|
|
420
451
|
&& input.taskCtx.channel
|
|
421
452
|
&& input.taskCtx.peerId
|
|
422
453
|
&& input.taskCtx.selfAid) {
|
|
423
454
|
try {
|
|
424
455
|
const sessionsDir = resolvePaths().sessionsDir;
|
|
425
|
-
|
|
456
|
+
const peerId = input.taskCtx.chatType === 'group' ? input.taskCtx.channelId || input.taskCtx.peerId : input.taskCtx.peerId;
|
|
457
|
+
chatDir = chatDirPath(sessionsDir, input.taskCtx.channel, peerId, input.taskCtx.selfAid, input.taskCtx.channelKey);
|
|
426
458
|
const rawJsonlPath = path.join(chatDir, 'messages.jsonl');
|
|
427
459
|
if (fs.existsSync(rawJsonlPath)) {
|
|
428
460
|
historyJsonlPath = rawJsonlPath;
|
|
429
|
-
const sinceMs = readCheckpoint(chatDir);
|
|
461
|
+
const sinceMs = readCheckpoint(chatDir, input.mode, input.scopeOverride);
|
|
430
462
|
const threadsDir = path.join(batchDir, 'threads');
|
|
431
463
|
try {
|
|
432
|
-
const files = buildThreadSnapshots(rawJsonlPath, sinceMs, threadsDir);
|
|
464
|
+
const files = buildThreadSnapshots(rawJsonlPath, sinceMs, threadsDir, now.getTime());
|
|
433
465
|
if (files.length > 0)
|
|
434
466
|
threadFiles = files;
|
|
435
467
|
}
|
|
@@ -494,7 +526,8 @@ export function serializeConsolidationContext(input) {
|
|
|
494
526
|
fs.writeFileSync(contextFilePath, text, 'utf-8');
|
|
495
527
|
}
|
|
496
528
|
catch { /* 批次任务书写入失败不影响整理 */ }
|
|
497
|
-
return { filePath, batchDir, contextFilePath, scopes, historyJsonlPath, threadFiles, chatDir
|
|
529
|
+
return { filePath, batchDir, contextFilePath, scopes, historyJsonlPath, threadFiles, chatDir,
|
|
530
|
+
...(threadFiles?.length ? { snapshotThroughTs: now.getTime() } : {}) };
|
|
498
531
|
}
|
|
499
532
|
/** 会话结束后回填本次产出,追加到任务书末尾。 */
|
|
500
533
|
export function recordBatchOutcome(contextFilePath, outcome) {
|
|
@@ -122,3 +122,38 @@ export function readRollbackRecords(batchDir) {
|
|
|
122
122
|
return [];
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* 统计 batchDir/threads/ 下所有快照文件里的消息条数。
|
|
127
|
+
* 每个 .md 文件里以 "L" 开头(后跟数字)的行就是一条消息。
|
|
128
|
+
* 文件不存在或为空时返回 0。
|
|
129
|
+
*/
|
|
130
|
+
export function countInputMessages(batchDir) {
|
|
131
|
+
const threadsDir = path.join(batchDir, 'threads');
|
|
132
|
+
if (!fs.existsSync(threadsDir))
|
|
133
|
+
return 0;
|
|
134
|
+
let total = 0;
|
|
135
|
+
try {
|
|
136
|
+
for (const f of fs.readdirSync(threadsDir)) {
|
|
137
|
+
if (!f.endsWith('.md'))
|
|
138
|
+
continue;
|
|
139
|
+
const content = fs.readFileSync(path.join(threadsDir, f), 'utf-8');
|
|
140
|
+
for (const line of content.split('\n')) {
|
|
141
|
+
if (/^L\d+\s/.test(line))
|
|
142
|
+
total++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch { /* ignore */ }
|
|
147
|
+
return total;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 读取 batchDir/context.md 的字节数。文件不存在返回 0。
|
|
151
|
+
*/
|
|
152
|
+
export function readContextBytes(batchDir) {
|
|
153
|
+
try {
|
|
154
|
+
return fs.statSync(path.join(batchDir, 'context.md')).size;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 整理请求入队时的提示文案。CLI(ec memory consolidate)和 /memory consolidate 共用。
|
|
3
|
+
* 不依赖其他模块,CLI 侧引入不会拖进 daemon 代码。
|
|
4
|
+
*/
|
|
5
|
+
/** 入队原因:本 agent 已有整理会话在跑,或全局并发已满 */
|
|
6
|
+
export function queueReason(q) {
|
|
7
|
+
if (q.agentBusy)
|
|
8
|
+
return '当前 agent 已有整理会话在运行';
|
|
9
|
+
if (q.activeCount >= q.maxConcurrent)
|
|
10
|
+
return `全局并发已满(${q.activeCount}/${q.maxConcurrent})`;
|
|
11
|
+
return '前面还有待处理的整理请求';
|
|
12
|
+
}
|
|
13
|
+
/** 多行明细,每行一个字段 */
|
|
14
|
+
export function formatQueueDetails(q) {
|
|
15
|
+
return [
|
|
16
|
+
`原因: ${queueReason(q)}`,
|
|
17
|
+
`本 agent 排队: 第 ${q.position} 位 / 共 ${q.agentQueueLength} 个`,
|
|
18
|
+
`全局运行中: ${q.activeCount}/${q.maxConcurrent}`,
|
|
19
|
+
`全局排队: ${q.totalQueued} 个`,
|
|
20
|
+
];
|
|
21
|
+
}
|