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
|
@@ -33,13 +33,6 @@ export class HandoffDeliveryFailedError extends Error {
|
|
|
33
33
|
this.name = 'HandoffDeliveryFailedError';
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
export class HandoffReturnPolicyRequiredError extends Error {
|
|
37
|
-
code = 'HANDOFF_RETURN_POLICY_REQUIRED';
|
|
38
|
-
constructor() {
|
|
39
|
-
super('跨会话 ec msg/group send 必须显式指定 --return none|required');
|
|
40
|
-
this.name = 'HandoffReturnPolicyRequiredError';
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
36
|
export class TriggerHandoffRequiredForbiddenError extends Error {
|
|
44
37
|
code = 'TRIGGER_HANDOFF_REQUIRED_FORBIDDEN';
|
|
45
38
|
constructor() {
|
|
@@ -99,9 +92,10 @@ export class HandoffRuntime {
|
|
|
99
92
|
throw new Error('handoff context requires a different target session');
|
|
100
93
|
return { crossSession: false, targetSession: target };
|
|
101
94
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
// --return 参数已废弃:跨会话 send 不再要求显式指定返回策略,默认 'none'。
|
|
96
|
+
// 内部调用方(agent-create 等)仍可显式传 'required' 走回流路径。
|
|
97
|
+
const effectiveReturnPolicy = input.explicitReturnPolicy ?? 'none';
|
|
98
|
+
if (effectiveReturnPolicy === 'required' && input.causation?.trigger?.path.length) {
|
|
105
99
|
throw new TriggerHandoffRequiredForbiddenError();
|
|
106
100
|
}
|
|
107
101
|
const handoffCausation = deriveCausation(normalizeCausation(input.causation) ?? createRootCausation());
|
|
@@ -127,7 +121,7 @@ export class HandoffRuntime {
|
|
|
127
121
|
delivery: targetChatType === 'group'
|
|
128
122
|
? { chatType: 'group', groupId: input.to }
|
|
129
123
|
: { chatType: 'private' },
|
|
130
|
-
returnPolicy:
|
|
124
|
+
returnPolicy: effectiveReturnPolicy,
|
|
131
125
|
originAuthorization: input.originAuthorization,
|
|
132
126
|
causation: handoffCausation,
|
|
133
127
|
now: this.now?.(),
|
|
@@ -1,10 +1,16 @@
|
|
|
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';
|
|
5
6
|
import path from 'path';
|
|
6
7
|
import { executeTaskResume } from './task/task-resume-operation.js';
|
|
7
|
-
import { isHClassPath, isSameOrDescendant } from './permission/protected-paths.js';
|
|
8
|
+
import { isHClassPath, isLClassPath, isSameOrDescendant } from './permission/protected-paths.js';
|
|
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';
|
|
8
14
|
import { computeBuildId } from '../utils/evolcore-version.js';
|
|
9
15
|
import { loadAgent } from '../config-store.js';
|
|
10
16
|
import { executeLocalInitializationCommand, hasSuppliedInitialization } from '../cli/agent-create.js';
|
|
@@ -16,6 +22,7 @@ import { registerHandoffIpc } from './handoff/ipc-registration.js';
|
|
|
16
22
|
import { registerAunIpcSender } from './message/aun-ipc-sender.js';
|
|
17
23
|
import { registerConsolidationCheckExecutor, registerConsolidationLaunchExecutor, } from './memory/consolidation.js';
|
|
18
24
|
import { createTriggerBaseagentResolver, registerTriggerIpcExecutor } from '../trigger/ipc-service.js';
|
|
25
|
+
import { createThreadOperationExecutor } from './thread/ipc-operation.js';
|
|
19
26
|
import { isLinkedInstall } from '../utils/npm-ops.js';
|
|
20
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, }) {
|
|
21
28
|
const authorizeManagedAgentOperation = createDelegationAuthorizer({
|
|
@@ -51,6 +58,137 @@ export function registerDaemonIpc({ ipcServer, agentRegistry, agentDelegationReg
|
|
|
51
58
|
agentMap,
|
|
52
59
|
isLinkedInstall: isLinkedInstall(),
|
|
53
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
|
+
});
|
|
54
192
|
registerHandoffIpc({
|
|
55
193
|
ipcServer,
|
|
56
194
|
agentDelegationRegistry,
|
|
@@ -60,7 +198,7 @@ export function registerDaemonIpc({ ipcServer, agentRegistry, agentDelegationReg
|
|
|
60
198
|
handoffRuntime,
|
|
61
199
|
getProcessOwners,
|
|
62
200
|
});
|
|
63
|
-
registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime });
|
|
201
|
+
registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime, triggerSchedulers });
|
|
64
202
|
ipcServer.setAgentReloadExecutor((aid, options) => reloadCoordinator.reload(aid, options));
|
|
65
203
|
registerConsolidationCheckExecutor({ ipcServer, agentRegistry, enqueueConsolidation });
|
|
66
204
|
ipcServer.setInitializationExecutor(async (command) => executeLocalInitializationCommand(agentInitializationService, command));
|
|
@@ -75,6 +213,7 @@ export function registerDaemonIpc({ ipcServer, agentRegistry, agentDelegationReg
|
|
|
75
213
|
handoffRuntime,
|
|
76
214
|
agentApplicationService,
|
|
77
215
|
agentInitializationService,
|
|
216
|
+
executeConfigOperation: (argv, sessionId, token, commandHash) => commandHandler.handleConfigOperation(argv, sessionId, token, commandHash),
|
|
78
217
|
});
|
|
79
218
|
registerOperationIpcProviders({
|
|
80
219
|
ipcServer,
|
|
@@ -210,7 +349,7 @@ export function registerRuntimeIpcProviders({ ipcServer, channelInstances, messa
|
|
|
210
349
|
const session = await sessionManager.getSessionById(sessionId);
|
|
211
350
|
if (!session)
|
|
212
351
|
return null;
|
|
213
|
-
const { buildDebugTaskContext } = await import('./
|
|
352
|
+
const { buildDebugTaskContext } = await import('./auth/debug-context.js');
|
|
214
353
|
const { ensureSessionRuntimeDir } = await import('../cli/task-context.js');
|
|
215
354
|
return buildDebugTaskContext({
|
|
216
355
|
session,
|
|
@@ -220,8 +359,12 @@ export function registerRuntimeIpcProviders({ ipcServer, channelInstances, messa
|
|
|
220
359
|
});
|
|
221
360
|
logger.info('[Memory] 开发仓安装:已启用 ec memory --debug <sessionId>');
|
|
222
361
|
}
|
|
223
|
-
ipcServer.setTaskRuntimeContextAuthorizer(({ sessionId, delegationToken, delegationCommandHash }) => {
|
|
224
|
-
|
|
362
|
+
ipcServer.setTaskRuntimeContextAuthorizer(({ sessionId, delegationToken, delegationCommandHash, consumeApproval }) => {
|
|
363
|
+
if (consumeApproval && !agentDelegationRegistry.validate)
|
|
364
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Command authorization is unavailable' };
|
|
365
|
+
const authorization = consumeApproval
|
|
366
|
+
? agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash)
|
|
367
|
+
: agentDelegationRegistry.validateForContext(delegationToken, sessionId, delegationCommandHash);
|
|
225
368
|
return authorization.ok
|
|
226
369
|
? { ok: true }
|
|
227
370
|
: { ok: false, code: authorization.code, error: authorization.reason };
|
|
@@ -383,8 +526,17 @@ export function registerOperationIpcProviders({ ipcServer, sessionManager, messa
|
|
|
383
526
|
recover: recoverTask,
|
|
384
527
|
}));
|
|
385
528
|
ipcServer.setQueueOperationExecutor(async (params) => {
|
|
529
|
+
try {
|
|
530
|
+
if (!params.argv)
|
|
531
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'Managed queue operation requires argv' };
|
|
532
|
+
const { parseQueueOperationArgv } = await import('../cli/queue-command.js');
|
|
533
|
+
params = { ...params, ...parseQueueOperationArgv(params.argv) };
|
|
534
|
+
}
|
|
535
|
+
catch (error) {
|
|
536
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: error instanceof Error ? error.message : String(error) };
|
|
537
|
+
}
|
|
386
538
|
const operation = `ec.queue.${params.action ?? 'read'}`;
|
|
387
|
-
const authorization = await authorizeManagedAgentOperation(params, operation);
|
|
539
|
+
const authorization = await authorizeManagedAgentOperation({ ...params, targetAid: params.agent }, operation);
|
|
388
540
|
if (!authorization.ok)
|
|
389
541
|
return authorization;
|
|
390
542
|
const handle = agentRegistry.get(authorization.aid);
|
|
@@ -419,10 +571,57 @@ export function registerOperationIpcProviders({ ipcServer, sessionManager, messa
|
|
|
419
571
|
return authorization.ok ? { ok: true } : authorization;
|
|
420
572
|
});
|
|
421
573
|
ipcServer.setStorageAuthorizationExecutor(async (params) => {
|
|
422
|
-
|
|
423
|
-
|
|
574
|
+
try {
|
|
575
|
+
if (!params.argv)
|
|
576
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'Managed storage requires argv' };
|
|
577
|
+
const command = resolveStorageCommandArgv(params.argv);
|
|
578
|
+
if (command.sub === 'upload')
|
|
579
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'Upload requires the storage upload endpoint' };
|
|
580
|
+
const authorization = await authorizeManagedAgentOperation({ ...params, targetAid: command.aid }, command.operation);
|
|
581
|
+
if (!authorization.ok)
|
|
582
|
+
return authorization;
|
|
583
|
+
const { storageDownload, storageLs, storageRm, storageQuota } = await import('../aun/storage/index.js');
|
|
584
|
+
if (command.sub === 'ls')
|
|
585
|
+
return storageLs(authorization.aid, command.prefix);
|
|
586
|
+
if (command.sub === 'quota')
|
|
587
|
+
return storageQuota(authorization.aid);
|
|
588
|
+
if (command.sub === 'rm') {
|
|
589
|
+
const result = await storageRm(authorization.aid, command.remotePath);
|
|
590
|
+
return { ...result, objectKey: command.remotePath };
|
|
591
|
+
}
|
|
592
|
+
const session = await sessionManager.getSessionById(params.sessionId);
|
|
593
|
+
if (!session)
|
|
594
|
+
return { ok: false, code: 'SESSION_NOT_FOUND', error: 'current session not found' };
|
|
595
|
+
const defaultName = path.basename(command.url.replace(/^https?:\/\//, '').split('/').slice(1).join('/'));
|
|
596
|
+
const destination = path.resolve(session.projectPath, command.localPath ?? defaultName);
|
|
597
|
+
const roots = [session.projectPath, authorization.sessionRuntimeDir].filter((root) => !!root).map(root => fs.realpathSync(root));
|
|
598
|
+
let parent = path.dirname(destination);
|
|
599
|
+
while (!fs.existsSync(parent) && path.dirname(parent) !== parent)
|
|
600
|
+
parent = path.dirname(parent);
|
|
601
|
+
const realParent = fs.realpathSync(parent);
|
|
602
|
+
if (!roots.some(root => isSameOrDescendant(destination, root) && isSameOrDescendant(realParent, root))
|
|
603
|
+
|| isHClassPath(destination) || isLClassPath(destination)
|
|
604
|
+
|| (fs.existsSync(destination) && (!fs.lstatSync(destination).isFile() || fs.lstatSync(destination).isSymbolicLink()))) {
|
|
605
|
+
return { ok: false, code: 'INVALID_LOCAL_PATH', error: 'Download destination must be an unprotected project/TMPDIR file' };
|
|
606
|
+
}
|
|
607
|
+
return storageDownload(authorization.aid, command.url, destination);
|
|
608
|
+
}
|
|
609
|
+
catch (error) {
|
|
610
|
+
return { ok: false, code: 'STORAGE_OPERATION_FAILED', error: error instanceof Error ? error.message : String(error) };
|
|
611
|
+
}
|
|
424
612
|
});
|
|
425
613
|
ipcServer.setStorageUploadExecutor(async (params) => {
|
|
614
|
+
try {
|
|
615
|
+
if (!params.argv)
|
|
616
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'Managed storage upload requires argv' };
|
|
617
|
+
const command = resolveStorageCommandArgv(params.argv);
|
|
618
|
+
if (command.sub !== 'upload')
|
|
619
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'Upload endpoint does not match argv' };
|
|
620
|
+
params = { ...params, aid: command.aid, localFile: command.localFile, remotePath: command.remotePath, isPublic: command.isPublic };
|
|
621
|
+
}
|
|
622
|
+
catch (error) {
|
|
623
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: error instanceof Error ? error.message : String(error) };
|
|
624
|
+
}
|
|
426
625
|
const authorization = await authorizeManagedAgentOperation({ ...params, targetAid: params.aid }, 'storage.upload');
|
|
427
626
|
if (!authorization.ok)
|
|
428
627
|
return authorization;
|
|
@@ -438,7 +637,9 @@ export function registerOperationIpcProviders({ ipcServer, sessionManager, messa
|
|
|
438
637
|
}
|
|
439
638
|
const localFile = fs.realpathSync(requestedFile);
|
|
440
639
|
const stat = fs.lstatSync(localFile);
|
|
441
|
-
|
|
640
|
+
const inProject = isSameOrDescendant(localFile, projectPath);
|
|
641
|
+
const inRuntime = authorization.sessionRuntimeDir && isSameOrDescendant(localFile, fs.realpathSync(authorization.sessionRuntimeDir));
|
|
642
|
+
if ((!inProject && !inRuntime) || isHClassPath(localFile) || isLClassPath(localFile)) {
|
|
442
643
|
return { ok: false, code: 'INVALID_LOCAL_PATH', error: 'Upload source must be an unprotected file inside the current session project' };
|
|
443
644
|
}
|
|
444
645
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
@@ -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
|
+
}
|