evolcore 0.0.39 → 0.0.40
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 +49 -0
- package/README.md +31 -20
- package/bin/ec-safe-output.js +14 -2
- package/dist/agents/baseagent-availability.js +1 -0
- package/dist/agents/claude-runner.js +32 -46
- package/dist/agents/codex-runner.js +51 -27
- package/dist/agents/ecagent-runner.js +18 -7
- package/dist/agents/gemini-runner.js +1 -2
- package/dist/agents/runner-types.js +28 -6
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +23 -98
- package/dist/aun/aid/ownership-operation.js +1 -3
- package/dist/aun/group/invite-service.js +6 -0
- package/dist/aun/msg/p2p.js +8 -12
- package/dist/channels/aun.js +66 -48
- package/dist/channels/dingtalk.js +8 -2
- package/dist/channels/feishu.js +6 -1
- package/dist/channels/wecom.js +7 -3
- package/dist/cli/agent-create.js +23 -4
- package/dist/cli/aid-bind-command.js +4 -1
- package/dist/cli/daemon-commands.js +242 -17
- package/dist/cli/index.js +25 -13
- package/dist/cli/memory-command.js +33 -14
- package/dist/cli/restart-monitor.js +35 -13
- package/dist/cli/task-context.js +1 -0
- package/dist/config/config-field-policy.js +1 -0
- package/dist/contracts/ecweb-service-protocol.js +44 -0
- package/dist/core/agent-runtime.js +6 -0
- package/dist/core/auth/agent-delegation.js +7 -4
- package/dist/core/auth/auth-gateway.js +3 -1
- package/dist/core/auth/authorization-audit.js +5 -1
- package/dist/core/auth/debug-context.js +5 -1
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/command/command-handler.js +7 -1
- package/dist/core/command/menu-catalog.js +48 -12
- package/dist/core/command/menu-handler.js +72 -4
- package/dist/core/command/menu-protocol.js +20 -1
- package/dist/core/command/slash-gate.js +17 -3
- package/dist/core/command/slash-handler.js +90 -7
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/interaction-router.js +2 -1
- package/dist/core/ipc-registration.js +26 -7
- package/dist/core/memory/batch-meta.js +2 -0
- package/dist/core/memory/consolidation-context.js +42 -51
- package/dist/core/memory/consolidation-ops-records.js +39 -7
- package/dist/core/memory/consolidation-trigger.js +71 -64
- package/dist/core/memory/consolidation.js +226 -20
- package/dist/core/memory/memory-writer.js +160 -34
- package/dist/core/memory/relation-idle-monitor.js +287 -0
- package/dist/core/memory/relation-sweep.js +206 -0
- package/dist/core/message/aun-ipc-sender.js +5 -0
- package/dist/core/message/message-bridge.js +64 -55
- package/dist/core/message/message-log.js +17 -0
- package/dist/core/message/message-queue.js +6 -0
- package/dist/core/message/outbound.js +70 -0
- package/dist/core/message/peer-mode.js +7 -2
- package/dist/core/message/response-engine.js +341 -73
- package/dist/core/ownership/owner-resolver.js +384 -178
- package/dist/core/permission/ec-command-parser.js +19 -3
- package/dist/core/permission/tool-policy.js +7 -7
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/session/session-manager.js +25 -5
- package/dist/core/system-session/memory.js +3 -18
- package/dist/ecweb/basic-settings-service.js +709 -0
- package/dist/ecweb/providers-service.js +172 -0
- package/dist/index.js +92 -4
- package/dist/ipc.js +78 -11
- package/dist/stats/db.js +33 -0
- package/dist/stats/index.js +2 -2
- package/dist/stats/query.js +246 -0
- package/dist/stats/writer.js +41 -1
- package/dist/trigger/application-service.js +12 -0
- package/dist/trigger/authorization.js +2 -2
- package/dist/trigger/history.js +36 -0
- package/dist/trigger/scheduler.js +3 -0
- package/dist/utils/ecweb-supervisor.js +31 -10
- package/dist/utils/error-utils.js +8 -4
- package/dist/utils/evolcore-version.js +3 -2
- package/dist/utils/host-info.js +52 -0
- package/ecagent/dist/harness/agent-harness.js +4 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/agent.md +1 -1
- package/kits/docs/evolcore/aid.md +29 -12
- package/kits/docs/evolcore/config.md +9 -7
- package/kits/docs/evolcore/memory-architecture.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +28 -7
- package/kits/docs/evolcore/memory-tags.md +1 -1
- package/kits/docs/evolcore/memory.md +1 -1
- package/kits/docs/evolcore/menu-client.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +6 -0
- package/kits/docs/evolcore/msg.md +1 -1
- package/kits/eck_manifest.json +3 -3
- package/kits/rules/04-relation.md +3 -2
- package/kits/schemas/agent-config.schema.13.json +5 -1
- package/kits/templates/memory-consolidation/generalize.md +32 -4
- package/kits/templates/memory-consolidation/notes.md +2 -2
- package/kits/templates/memory-consolidation/promote.md +10 -2
- package/kits/templates/system-fragments/baseagent.md +5 -0
- package/kits/templates/system-fragments/memory.md +12 -3
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/{rules/07-memory-and-thread.md → templates/system-fragments/thread.md} +17 -80
- package/package.json +3 -3
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { readProvidersFile, resolveProviderService } from '../config/providers.js';
|
|
2
|
+
import { createProvider, importOrMergeBaseagentProvider, parseProviderImport, previewProviderImport, previewProviderValue, readProvidersSnapshot, resolveProviderDraftService, saveProviderChanges, updateProvider, } from '../config/providers-management.js';
|
|
3
|
+
import { listBaseagentImportSources, providerFromBaseagent } from '../config/providers-import.js';
|
|
4
|
+
import { getProviderCatalog, previewProviderCatalog } from '../core/model/model-catalog.js';
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function response(status, body) {
|
|
9
|
+
return { status, body };
|
|
10
|
+
}
|
|
11
|
+
function managementCatalog(snapshot, overrides) {
|
|
12
|
+
const models = snapshot.models.map((model) => ({ ...model, enabled: overrides[model.id]?.enabled !== false, available: true }));
|
|
13
|
+
const listed = new Set(snapshot.models.map((model) => model.id));
|
|
14
|
+
const manualModels = Object.entries(overrides).filter(([id, override]) => override.manual === true && !listed.has(id))
|
|
15
|
+
.map(([id, override]) => ({
|
|
16
|
+
id,
|
|
17
|
+
...(typeof override.name === 'string' && override.name ? { displayName: override.name } : {}),
|
|
18
|
+
...(typeof override.imageIn === 'boolean' ? { imageIn: override.imageIn } : {}),
|
|
19
|
+
manual: true,
|
|
20
|
+
enabled: override.enabled !== false,
|
|
21
|
+
available: true,
|
|
22
|
+
}));
|
|
23
|
+
const unseenModels = Object.entries(overrides).filter(([id, override]) => !listed.has(id) && override.manual !== true)
|
|
24
|
+
.map(([id, override]) => ({ id, ...override, enabled: override.enabled !== false, available: false }));
|
|
25
|
+
return { ...snapshot, models: [...models, ...manualModels], unseenModels };
|
|
26
|
+
}
|
|
27
|
+
async function resolveImports(value) {
|
|
28
|
+
if (value === undefined)
|
|
29
|
+
return [];
|
|
30
|
+
if (!Array.isArray(value))
|
|
31
|
+
throw new Error('Provider imports must be an array');
|
|
32
|
+
return value.map(item => {
|
|
33
|
+
if (!isRecord(item) || Object.keys(item).length !== 1)
|
|
34
|
+
throw new Error('Invalid Provider import');
|
|
35
|
+
if (item.baseagent !== undefined) {
|
|
36
|
+
if (typeof item.baseagent !== 'string')
|
|
37
|
+
throw new Error('Invalid Baseagent import');
|
|
38
|
+
return providerFromBaseagent(item.baseagent);
|
|
39
|
+
}
|
|
40
|
+
if (item.text !== undefined)
|
|
41
|
+
return parseProviderImport(item.text);
|
|
42
|
+
if (item.provider !== undefined)
|
|
43
|
+
return item.provider;
|
|
44
|
+
throw new Error('Invalid Provider import');
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function requestBody(value) {
|
|
48
|
+
if (!isRecord(value))
|
|
49
|
+
throw new Error('Provider request must be a JSON object');
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
export async function handleProvidersService(input) {
|
|
53
|
+
try {
|
|
54
|
+
if (!isRecord(input) || typeof input.method !== 'string' || typeof input.pathname !== 'string'
|
|
55
|
+
|| !input.pathname.startsWith('/api/providers')) {
|
|
56
|
+
throw new Error('Invalid Provider service request');
|
|
57
|
+
}
|
|
58
|
+
const parts = input.pathname.replace(/\/+$/u, '').split('/').slice(3).map(decodeURIComponent);
|
|
59
|
+
const query = input.query ?? {};
|
|
60
|
+
if (!isRecord(query) || Object.values(query).some(value => typeof value !== 'string'))
|
|
61
|
+
throw new Error('Invalid Provider query');
|
|
62
|
+
if (input.method === 'GET' && !parts.length) {
|
|
63
|
+
return response(200, { success: true, data: readProvidersSnapshot() });
|
|
64
|
+
}
|
|
65
|
+
if (input.method === 'POST' && !parts.length) {
|
|
66
|
+
const body = requestBody(input.body);
|
|
67
|
+
if (typeof body.revision !== 'string')
|
|
68
|
+
throw new Error('Provider revision is required');
|
|
69
|
+
return response(200, { success: true, data: await createProvider({ revision: body.revision, value: body.provider }) });
|
|
70
|
+
}
|
|
71
|
+
if (input.method === 'PATCH' && !parts.length) {
|
|
72
|
+
const body = requestBody(input.body);
|
|
73
|
+
if (typeof body.revision !== 'string' || !Array.isArray(body.changes))
|
|
74
|
+
throw new Error('Provider revision and changes are required');
|
|
75
|
+
const changes = [];
|
|
76
|
+
for (const raw of body.changes) {
|
|
77
|
+
if (!isRecord(raw) || typeof raw.providerId !== 'string')
|
|
78
|
+
throw new Error('Invalid provider change');
|
|
79
|
+
changes.push({ ...raw, providerId: raw.providerId, imports: await resolveImports(raw.imports) });
|
|
80
|
+
}
|
|
81
|
+
return response(200, { success: true, data: await saveProviderChanges({ revision: body.revision, changes }) });
|
|
82
|
+
}
|
|
83
|
+
if (input.method === 'GET' && parts[0] === 'import' && parts[1] === 'baseagents' && parts.length === 2) {
|
|
84
|
+
return response(200, { success: true, data: { baseagents: listBaseagentImportSources() } });
|
|
85
|
+
}
|
|
86
|
+
if (input.method === 'POST' && parts[0] === 'import' && parts[1] === 'text'
|
|
87
|
+
&& (parts.length === 2 || (parts[2] === 'preview' && parts.length === 3))) {
|
|
88
|
+
const body = requestBody(input.body);
|
|
89
|
+
if (parts[2] === 'preview')
|
|
90
|
+
return response(200, { success: true, data: previewProviderImport(body.text) });
|
|
91
|
+
if (typeof body.revision !== 'string')
|
|
92
|
+
throw new Error('Provider revision is required');
|
|
93
|
+
return response(200, { success: true, data: await createProvider({ revision: body.revision, value: parseProviderImport(body.text) }) });
|
|
94
|
+
}
|
|
95
|
+
if (input.method === 'POST' && parts[0] === 'import' && parts[1] === 'baseagent' && parts.length === 2) {
|
|
96
|
+
const body = requestBody(input.body);
|
|
97
|
+
if (typeof body.revision !== 'string')
|
|
98
|
+
throw new Error('Provider revision is required');
|
|
99
|
+
const baseagent = typeof body.baseagent === 'string' ? body.baseagent : '';
|
|
100
|
+
if (body.agentAid !== undefined || body.providerId !== undefined)
|
|
101
|
+
throw new Error('Source Agent AID and Provider ID are not accepted; IDs are generated automatically');
|
|
102
|
+
return response(200, { success: true, data: await importOrMergeBaseagentProvider({ revision: body.revision, value: providerFromBaseagent(baseagent) }) });
|
|
103
|
+
}
|
|
104
|
+
if (input.method === 'POST' && parts[0] === 'import' && parts[1] === 'baseagent' && parts[2] === 'preview' && parts.length === 3) {
|
|
105
|
+
const body = requestBody(input.body);
|
|
106
|
+
if (typeof body.baseagent !== 'string')
|
|
107
|
+
throw new Error('Baseagent is required');
|
|
108
|
+
return response(200, { success: true, data: previewProviderValue(providerFromBaseagent(body.baseagent)).provider });
|
|
109
|
+
}
|
|
110
|
+
const providerId = parts[0];
|
|
111
|
+
if (!providerId || !/^[A-Za-z0-9_-]+$/u.test(providerId))
|
|
112
|
+
throw new Error('Invalid provider identifier');
|
|
113
|
+
if (input.method === 'POST' && parts[1] === 'models' && parts[2] === 'preview' && parts.length === 3) {
|
|
114
|
+
const body = requestBody(input.body);
|
|
115
|
+
if (typeof body.serviceType !== 'string')
|
|
116
|
+
throw new Error('Service type is required');
|
|
117
|
+
if (body.baseagent !== undefined && typeof body.baseagent !== 'string')
|
|
118
|
+
throw new Error('Baseagent must be a string');
|
|
119
|
+
const provider = resolveProviderDraftService({
|
|
120
|
+
providerId,
|
|
121
|
+
serviceType: body.serviceType,
|
|
122
|
+
baseagent: body.baseagent,
|
|
123
|
+
value: body.provider,
|
|
124
|
+
imports: await resolveImports(body.imports),
|
|
125
|
+
});
|
|
126
|
+
const snapshot = await previewProviderCatalog(provider, body.baseagent);
|
|
127
|
+
if (snapshot.freshness !== 'fresh') {
|
|
128
|
+
const diagnostic = snapshot.diagnostics[0];
|
|
129
|
+
return response(502, { success: false, code: 'PROVIDER_MODEL_TEST_FAILED',
|
|
130
|
+
error: `${diagnostic?.message || 'Model list test failed'}${diagnostic?.status ? ` (HTTP ${diagnostic.status})` : ''}` });
|
|
131
|
+
}
|
|
132
|
+
return response(200, { success: true, data: managementCatalog(snapshot, provider.modelOverrides) });
|
|
133
|
+
}
|
|
134
|
+
if (input.method === 'GET' && parts[1] === 'secret' && parts.length === 2) {
|
|
135
|
+
const provider = readProvidersFile().providers[providerId];
|
|
136
|
+
if (!provider)
|
|
137
|
+
return response(404, { success: false, error: 'Provider not found' });
|
|
138
|
+
return response(200, { success: true, data: { apiKey: provider.common.apiKey || '' } });
|
|
139
|
+
}
|
|
140
|
+
if (input.method === 'GET' && parts[1] === 'models' && parts.length === 2) {
|
|
141
|
+
const serviceType = query.serviceType || '';
|
|
142
|
+
const baseagent = query.baseagent || undefined;
|
|
143
|
+
if (query.view === 'selection') {
|
|
144
|
+
const snapshot = await getProviderCatalog(providerId, serviceType, query.refresh === 'true', false, baseagent);
|
|
145
|
+
return response(200, { success: true, data: snapshot });
|
|
146
|
+
}
|
|
147
|
+
const provider = resolveProviderService(providerId, serviceType);
|
|
148
|
+
const snapshot = await getProviderCatalog(providerId, serviceType, query.refresh === 'true', true);
|
|
149
|
+
return response(200, { success: true, data: managementCatalog(snapshot, provider.modelOverrides) });
|
|
150
|
+
}
|
|
151
|
+
if ((input.method === 'PUT' || input.method === 'DELETE') && parts.length === 1) {
|
|
152
|
+
const body = requestBody(input.body);
|
|
153
|
+
if (typeof body.revision !== 'string')
|
|
154
|
+
throw new Error('Provider revision is required');
|
|
155
|
+
return response(200, { success: true, data: await updateProvider({
|
|
156
|
+
providerId,
|
|
157
|
+
revision: body.revision,
|
|
158
|
+
value: body.provider,
|
|
159
|
+
remove: input.method === 'DELETE',
|
|
160
|
+
}) });
|
|
161
|
+
}
|
|
162
|
+
return response(405, { success: false, error: 'Method not allowed' });
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return response(error.statusCode || 400, {
|
|
166
|
+
success: false,
|
|
167
|
+
...(error.code ? { code: error.code } : {}),
|
|
168
|
+
error: error.message || 'Provider operation failed',
|
|
169
|
+
...(error.data !== undefined ? { data: error.data } : {}),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -45,7 +45,7 @@ import { readFastaunVersion, readInstalledEvolcoreVersion, reportEvolcoreStartup
|
|
|
45
45
|
import { recoverAllRoleMutationsSync } from './core/command/role-menu.js';
|
|
46
46
|
import { HandoffRuntime } from './core/handoff/runtime.js';
|
|
47
47
|
import { startDaemonControlAid } from './core/daemon-control.js';
|
|
48
|
-
import { ownerResolver, getDaemonOwners } from './core/ownership/owner-resolver.js';
|
|
48
|
+
import { ownerResolver, getDaemonOwners, getPrimaryOwner } from './core/ownership/owner-resolver.js';
|
|
49
49
|
import { cleanupStartupHandoffs, recoverStartupHandoffs, resumePendingSessions, createDaemonShutdown, daemonConversationTotalExecutionMs, daemonConversationWatchdogMs, daemonIdleTimeoutMs, } from './core/daemon-lifecycle.js';
|
|
50
50
|
import { BootstrapService, createBootstrapCompletionHandler, createBootstrapOwnerBoundHandler } from './core/bootstrap-service.js';
|
|
51
51
|
import { MessageCache } from './core/message/message-cache.js';
|
|
@@ -55,7 +55,10 @@ import { AgentApplicationService } from './core/agent-application-service.js';
|
|
|
55
55
|
import { EventBus } from './core/event-bus.js';
|
|
56
56
|
import { detectAvailableBaseagents, runBindBootstrapDaemon } from './core/daemon-bootstrap.js';
|
|
57
57
|
import { migrateLegacyConsolidationBatches } from './core/memory/consolidation-context.js';
|
|
58
|
-
import { ConsolidationQueue, createConsolidationFeedbackSender, createConsolidationSessionFinalizer, registerConsolidationSessionEvents, registerRelationConsolidationEvents, } from './core/memory/consolidation.js';
|
|
58
|
+
import { ConsolidationQueue, consolidationBlockedReason, createConsolidationFeedbackSender, createConsolidationSessionFinalizer, registerConsolidationSessionEvents, registerRelationConsolidationEvents, } from './core/memory/consolidation.js';
|
|
59
|
+
import { RelationIdleMonitor, relationInboundFromLog } from './core/memory/relation-idle-monitor.js';
|
|
60
|
+
import { RelationSweepRunner } from './core/memory/relation-sweep.js';
|
|
61
|
+
import { setInboundLogListener } from './core/message/message-log.js';
|
|
59
62
|
import { StatsCollector } from './utils/stats.js';
|
|
60
63
|
import { AidStatsCollector } from './utils/stats.js';
|
|
61
64
|
import { initializeStatsPersistence } from './stats/writer.js';
|
|
@@ -86,6 +89,9 @@ import { DaemonChannel } from './channels/daemon.js';
|
|
|
86
89
|
import { DAEMON_RUNTIME_EPOCH_ENV, ensureProcessManagedTempDir } from './cli/task-context.js';
|
|
87
90
|
import fs from 'fs';
|
|
88
91
|
import crypto from 'crypto';
|
|
92
|
+
import { getBaseagentSnapshot, loadedBaseagentIds } from './agents/baseagent-availability.js';
|
|
93
|
+
import { readHostInfo } from './utils/host-info.js';
|
|
94
|
+
import { queryStatusActivity, queryStatusActivityByAgent } from './stats/query.js';
|
|
89
95
|
export { resolvePersistedRestartDelivery } from './core/message/outbound.js';
|
|
90
96
|
export function readEvolcoreVersion() {
|
|
91
97
|
return readInstalledEvolcoreVersion();
|
|
@@ -94,7 +100,7 @@ async function main() {
|
|
|
94
100
|
// Service managers do not necessarily export TMPDIR. Establish the private
|
|
95
101
|
// managed root before any runner or task runtime is initialized.
|
|
96
102
|
ensureProcessManagedTempDir();
|
|
97
|
-
const { runtimeIsDist, runtimeBuildIdentity, diskDistDir } = reportEvolcoreStartup(import.meta.url, message => logger.info(message));
|
|
103
|
+
const { runtimeIsDist, runtimeBuildIdentity, diskDistDir, diskDistId } = reportEvolcoreStartup(import.meta.url, message => logger.info(message));
|
|
98
104
|
installChannelSdkConsoleFilter();
|
|
99
105
|
console.error('[EvolCore] ✓ Console filter installed');
|
|
100
106
|
logger.info(`EvolCore v${readEvolcoreVersion()} starting... (fastaun v${readFastaunVersion()})`);
|
|
@@ -258,7 +264,7 @@ async function main() {
|
|
|
258
264
|
const statsCollector = new StatsCollector(eventBus);
|
|
259
265
|
// Per-AID 消息统计收集器(累计,供 watch aid 实时展示)
|
|
260
266
|
const aidStatsCollector = new AidStatsCollector(eventBus);
|
|
261
|
-
const stopStatsPersistence = initializeStatsPersistence(paths.root, aidStatsCollector);
|
|
267
|
+
const stopStatsPersistence = initializeStatsPersistence(paths.root, aidStatsCollector, eventBus);
|
|
262
268
|
// 初始化 SessionManager(文件系统后端)
|
|
263
269
|
const resolveSessionIdentity = createSessionIdentityResolver(agentRegistry);
|
|
264
270
|
const sessionManager = new SessionManager(paths.sessionsDir, eventBus, resolveSessionIdentity);
|
|
@@ -474,6 +480,45 @@ async function main() {
|
|
|
474
480
|
const consolidationQueue = new ConsolidationQueue(daemonChannel);
|
|
475
481
|
const enqueueConsolidation = (params) => consolidationQueue.enqueue(params);
|
|
476
482
|
cmdHandler.setMemoryConsolidationLauncher(enqueueConsolidation);
|
|
483
|
+
// 关系级记忆整理:入站推后计时,空闲 / 兜底到期跑该关系的整理链(notes → generalize → promote → profile → forget)
|
|
484
|
+
const relationIdleMonitor = new RelationIdleMonitor({
|
|
485
|
+
statePath: path.join(paths.dataDir, 'relation-idle-state.json'),
|
|
486
|
+
getTiming: aid => agentRegistry.get(aid)?.config?.memory,
|
|
487
|
+
canSweep: aid => {
|
|
488
|
+
const agent = agentRegistry.get(aid);
|
|
489
|
+
if (!agent?.baseagent)
|
|
490
|
+
return false;
|
|
491
|
+
if (agent.config?.memory?.enableRelation === false)
|
|
492
|
+
return false;
|
|
493
|
+
return !consolidationBlockedReason(aid);
|
|
494
|
+
},
|
|
495
|
+
startSweep: request => relationSweeps.start(request),
|
|
496
|
+
});
|
|
497
|
+
const relationSweeps = new RelationSweepRunner({
|
|
498
|
+
enqueue: enqueueConsolidation,
|
|
499
|
+
getAgent: aid => agentRegistry.get(aid),
|
|
500
|
+
resolveOwner: async (aid) => {
|
|
501
|
+
await ownerResolver.refresh(aid);
|
|
502
|
+
return getPrimaryOwner(aid);
|
|
503
|
+
},
|
|
504
|
+
onSweepFinished: ({ selfAid, peerKey, touchedPeerKeys }) => {
|
|
505
|
+
relationIdleMonitor.onSweepFinished(selfAid, peerKey);
|
|
506
|
+
// 群链按发言人写入过的关系:它们不一定有自己的入站,各补一轮(不再传播)
|
|
507
|
+
for (const touched of touchedPeerKeys)
|
|
508
|
+
relationIdleMonitor.requestSweep(selfAid, touched, false);
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
consolidationQueue.setLaunchFailedListener(params => relationSweeps.onLaunchFailed(params));
|
|
512
|
+
relationIdleMonitor.load();
|
|
513
|
+
setInboundLogListener((chatDir, entry) => {
|
|
514
|
+
const inbound = relationInboundFromLog(chatDir, entry);
|
|
515
|
+
if (inbound)
|
|
516
|
+
relationIdleMonitor.recordInbound(inbound);
|
|
517
|
+
});
|
|
518
|
+
onShutdown(() => {
|
|
519
|
+
setInboundLogListener(undefined);
|
|
520
|
+
relationIdleMonitor.stop();
|
|
521
|
+
});
|
|
477
522
|
const triggerControlToken = crypto.randomBytes(32).toString('base64url');
|
|
478
523
|
fs.writeFileSync(resolvePaths().controlToken, triggerControlToken, { mode: 0o600 });
|
|
479
524
|
try {
|
|
@@ -523,6 +568,7 @@ async function main() {
|
|
|
523
568
|
releaseSlot: (aid, sessionId) => consolidationQueue.release(aid, sessionId),
|
|
524
569
|
enqueueConsolidation,
|
|
525
570
|
sendFeedback: sendConsolidationFeedback,
|
|
571
|
+
onSessionFinalized: info => relationSweeps.onSessionFinalized(info),
|
|
526
572
|
});
|
|
527
573
|
consolidationQueue.setFinalizer(finishConsolidationSession);
|
|
528
574
|
registerConsolidationSessionEvents({
|
|
@@ -658,11 +704,42 @@ async function main() {
|
|
|
658
704
|
cmdHandler.setDiagService(diagService);
|
|
659
705
|
let shutdown;
|
|
660
706
|
let pendingShutdownReason;
|
|
707
|
+
const hostInfo = readHostInfo(readEvolcoreVersion());
|
|
708
|
+
const baseagentSnapshot = await getBaseagentSnapshot({ loaded: loadedBaseagentIds(agentMap.keys()), probe: true });
|
|
709
|
+
const getStatusSnapshot = () => {
|
|
710
|
+
const now = Date.now();
|
|
711
|
+
const todayStart = new Date(new Date(now).getFullYear(), new Date(now).getMonth(), new Date(now).getDate()).getTime();
|
|
712
|
+
const lastHour = queryStatusActivity(paths.root, now - 3_600_000, now);
|
|
713
|
+
const today = queryStatusActivity(paths.root, todayStart, now);
|
|
714
|
+
const agents = agentRegistry.list();
|
|
715
|
+
const usageByAgent = queryStatusActivityByAgent(paths.root, todayStart, now, agents.map(agent => agent.aid));
|
|
716
|
+
return {
|
|
717
|
+
activity: { lastHour, today },
|
|
718
|
+
agents: agents.map(agent => {
|
|
719
|
+
const usage = usageByAgent.get(agent.aid);
|
|
720
|
+
const queueAgentName = agentRegistry.get(agent.aid)?.name ?? agent.name ?? agent.aid;
|
|
721
|
+
return {
|
|
722
|
+
aid: agent.aid,
|
|
723
|
+
name: agent.name ?? agent.aid,
|
|
724
|
+
status: agent.status,
|
|
725
|
+
baseagent: agent.baseagent ?? null,
|
|
726
|
+
model: agent.model ?? null,
|
|
727
|
+
effort: agent.effort ?? null,
|
|
728
|
+
queue: {
|
|
729
|
+
pending: messageQueue.getQueueLengthByAgent(queueAgentName),
|
|
730
|
+
processing: messageQueue.getProcessingCountByAgent(queueAgentName),
|
|
731
|
+
},
|
|
732
|
+
usage: { calls: usage.modelCalls, tokens: usage.tokens, cost: usage.cost },
|
|
733
|
+
};
|
|
734
|
+
}),
|
|
735
|
+
};
|
|
736
|
+
};
|
|
661
737
|
const ipcServer = new IpcServer(resolvePaths().socket, createIpcStatusProvider({
|
|
662
738
|
channelInstances,
|
|
663
739
|
statsCollector,
|
|
664
740
|
agentRegistry,
|
|
665
741
|
messageQueue,
|
|
742
|
+
runMode: devMode ? 'dev' : 'install',
|
|
666
743
|
runtimeIsDist,
|
|
667
744
|
runtimeBuildIdentity,
|
|
668
745
|
diskDistDir,
|
|
@@ -671,6 +748,15 @@ async function main() {
|
|
|
671
748
|
getAgentRuntimeError: () => agentRuntimeError,
|
|
672
749
|
daemonAid: daemonCfg.aid,
|
|
673
750
|
getControlAidState: () => controlChannel?.getAidState(),
|
|
751
|
+
host: hostInfo,
|
|
752
|
+
baseagents: baseagentSnapshot,
|
|
753
|
+
diskDistId,
|
|
754
|
+
getTriggerSummary: () => {
|
|
755
|
+
const now = new Date();
|
|
756
|
+
const from = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
757
|
+
return triggerSchedulers.statusSummary(from, Date.now());
|
|
758
|
+
},
|
|
759
|
+
getStatusSnapshot,
|
|
674
760
|
}), async (argv, sessionId, delegationToken, delegationCommandHash) => {
|
|
675
761
|
const delegation = agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
676
762
|
if (!delegation.ok)
|
|
@@ -794,6 +880,7 @@ async function main() {
|
|
|
794
880
|
agentApplicationService,
|
|
795
881
|
agentInitializationService,
|
|
796
882
|
enqueueConsolidation,
|
|
883
|
+
requestRelationSweep: (selfAid, peerKey) => relationIdleMonitor.requestSweep(selfAid, peerKey, true),
|
|
797
884
|
completeAgentBootstrap,
|
|
798
885
|
contactBinds: {
|
|
799
886
|
dingtalk: { register: registerPendingDingtalkContactBind, isChannelReady: isContactBindChannelReady },
|
|
@@ -854,6 +941,7 @@ async function main() {
|
|
|
854
941
|
})),
|
|
855
942
|
]);
|
|
856
943
|
sessionIdleMonitor.activate();
|
|
944
|
+
relationIdleMonitor.start();
|
|
857
945
|
controlChannel = startDaemonControlAid({
|
|
858
946
|
daemonCfg,
|
|
859
947
|
cmdHandler,
|
package/dist/ipc.js
CHANGED
|
@@ -25,6 +25,7 @@ import { RUNTIME_ENVIRONMENT_NAMES, setRuntimeEnvironmentOverride } from './conf
|
|
|
25
25
|
import { MAIN_CLI_BIN } from './product.js';
|
|
26
26
|
import { ownerResolver } from './core/ownership/owner-resolver.js';
|
|
27
27
|
import { parseOwnershipCommand, executeOwnershipCommand, ownershipFailure } from './aun/aid/ownership-operation.js';
|
|
28
|
+
import { ECWEB_SERVICE_PROTOCOL, validateEcwebClientProtocolRequirements, } from './contracts/ecweb-service-protocol.js';
|
|
28
29
|
import { delegationDiagnosticMessage, withDelegationDiagnosticContext, } from './core/auth/delegation-diagnostics.js';
|
|
29
30
|
const isWindows = process.platform === 'win32';
|
|
30
31
|
const isNamedPipe = (p) => isWindows && p.toLowerCase().startsWith('\\\\.\\pipe\\');
|
|
@@ -53,6 +54,7 @@ function parseRuntimeEnvironmentInput(value) {
|
|
|
53
54
|
return { ok: true, values };
|
|
54
55
|
}
|
|
55
56
|
const LOCAL_CONTROL_COMMANDS = new Set([
|
|
57
|
+
'service.handshake',
|
|
56
58
|
'bind.begin',
|
|
57
59
|
'bind.status',
|
|
58
60
|
'bind.cancel',
|
|
@@ -70,7 +72,10 @@ const LOCAL_CONTROL_COMMANDS = new Set([
|
|
|
70
72
|
'evolagent.load',
|
|
71
73
|
'evolagent.resync',
|
|
72
74
|
'menu.exec',
|
|
75
|
+
'ecweb.providers',
|
|
76
|
+
'ecweb.basic-settings',
|
|
73
77
|
'model.catalog',
|
|
78
|
+
'baseagent.snapshot',
|
|
74
79
|
'monitor-snapshot',
|
|
75
80
|
'monitor-subscribe',
|
|
76
81
|
'monitor-unsubscribe',
|
|
@@ -1094,6 +1099,14 @@ export class IpcServer {
|
|
|
1094
1099
|
return this.diagExecutor ? this.diagExecutor(cmd) : { ok: false, code: 'NOT_CONFIGURED', message: '诊断入口未配置' };
|
|
1095
1100
|
case 'menu-client.request':
|
|
1096
1101
|
return this.menuClientExecutor ? this.menuClientExecutor(cmd) : { ok: false, code: 'NOT_CONFIGURED', error: 'Managed Menu client is not configured' };
|
|
1102
|
+
case 'ecweb.providers': {
|
|
1103
|
+
const { handleProvidersService } = await import('./ecweb/providers-service.js');
|
|
1104
|
+
return handleProvidersService(cmd.request);
|
|
1105
|
+
}
|
|
1106
|
+
case 'ecweb.basic-settings': {
|
|
1107
|
+
const { handleBasicSettingsService } = await import('./ecweb/basic-settings-service.js');
|
|
1108
|
+
return handleBasicSettingsService(cmd.request);
|
|
1109
|
+
}
|
|
1097
1110
|
case 'model.catalog': {
|
|
1098
1111
|
if ((cmd.self !== undefined && typeof cmd.self !== 'string')
|
|
1099
1112
|
|| (cmd.baseagent !== undefined && !['claude', 'codex', 'ecagent', 'gemini'].includes(cmd.baseagent))) {
|
|
@@ -1113,6 +1126,25 @@ export class IpcServer {
|
|
|
1113
1126
|
return { ok: false, error: 'Unable to resolve model catalog configuration' };
|
|
1114
1127
|
}
|
|
1115
1128
|
}
|
|
1129
|
+
case 'baseagent.snapshot': {
|
|
1130
|
+
if (cmd.probe !== undefined && typeof cmd.probe !== 'boolean') {
|
|
1131
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'probe must be a boolean' };
|
|
1132
|
+
}
|
|
1133
|
+
try {
|
|
1134
|
+
const { getBaseagentSnapshot } = await import('./agents/baseagent-availability.js');
|
|
1135
|
+
return {
|
|
1136
|
+
ok: true,
|
|
1137
|
+
snapshot: await getBaseagentSnapshot({ probe: cmd.probe !== false }),
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
catch (error) {
|
|
1141
|
+
return {
|
|
1142
|
+
ok: false,
|
|
1143
|
+
code: 'BASEAGENT_SNAPSHOT_UNAVAILABLE',
|
|
1144
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1116
1148
|
case 'status':
|
|
1117
1149
|
return this.getStatus();
|
|
1118
1150
|
case 'codex-pre-tool-use': {
|
|
@@ -1152,6 +1184,34 @@ export class IpcServer {
|
|
|
1152
1184
|
protocolVersion: 1,
|
|
1153
1185
|
uptime: Math.round(process.uptime() * 1000),
|
|
1154
1186
|
};
|
|
1187
|
+
case 'service.handshake': {
|
|
1188
|
+
const protocol = {
|
|
1189
|
+
major: ECWEB_SERVICE_PROTOCOL.major,
|
|
1190
|
+
minor: ECWEB_SERVICE_PROTOCOL.minor,
|
|
1191
|
+
};
|
|
1192
|
+
const requirementsError = validateEcwebClientProtocolRequirements(cmd.requirements);
|
|
1193
|
+
const error = cmd.service !== ECWEB_SERVICE_PROTOCOL.name
|
|
1194
|
+
? `Unsupported service: ${String(cmd.service ?? 'missing')}`
|
|
1195
|
+
: requirementsError;
|
|
1196
|
+
if (error) {
|
|
1197
|
+
return {
|
|
1198
|
+
ok: false,
|
|
1199
|
+
code: 'SERVICE_PROTOCOL_MISMATCH',
|
|
1200
|
+
service: ECWEB_SERVICE_PROTOCOL.name,
|
|
1201
|
+
protocol,
|
|
1202
|
+
capabilities: ECWEB_SERVICE_PROTOCOL.capabilities,
|
|
1203
|
+
compatible: false,
|
|
1204
|
+
error,
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
return {
|
|
1208
|
+
ok: true,
|
|
1209
|
+
service: ECWEB_SERVICE_PROTOCOL.name,
|
|
1210
|
+
protocol,
|
|
1211
|
+
capabilities: ECWEB_SERVICE_PROTOCOL.capabilities,
|
|
1212
|
+
compatible: true,
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1155
1215
|
case 'shutdown': {
|
|
1156
1216
|
if (!this.shutdownExecutor)
|
|
1157
1217
|
return { ok: false, error: 'shutdown executor not configured' };
|
|
@@ -1849,7 +1909,7 @@ export class IpcServer {
|
|
|
1849
1909
|
}
|
|
1850
1910
|
}
|
|
1851
1911
|
case 'memory.create': {
|
|
1852
|
-
const { sessionId, scope, slug, description, content, tags, keywords, replaces, targetPeerKey, generalizedFrom, promotedFrom } = cmd;
|
|
1912
|
+
const { sessionId, scope, slug, description, content, tags, keywords, replaces, targetPeerKey, generalizedFrom, promotedFrom, reason } = cmd;
|
|
1853
1913
|
if (!sessionId)
|
|
1854
1914
|
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId' };
|
|
1855
1915
|
try {
|
|
@@ -1876,12 +1936,12 @@ export class IpcServer {
|
|
|
1876
1936
|
catch (e) {
|
|
1877
1937
|
const batchDir = guard.taskCtx.consolidationBatchDir;
|
|
1878
1938
|
if (batchDir)
|
|
1879
|
-
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'create', scope: scope ?? '', target: slug ?? '', ok: false, error: e?.message });
|
|
1939
|
+
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'create', scope: scope ?? '', target: slug ?? '', ok: false, error: e?.message, ...(reason ? { reason } : {}) });
|
|
1880
1940
|
throw e;
|
|
1881
1941
|
}
|
|
1882
1942
|
const batchDir = guard.taskCtx.consolidationBatchDir;
|
|
1883
1943
|
if (batchDir)
|
|
1884
|
-
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'create', scope: scope ?? '', target: slug ?? '', filePath: createResult.filePath, archivedFiles: createResult.archived.map((dest, i) => ({ source:
|
|
1944
|
+
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'create', scope: scope ?? '', target: slug ?? '', filePath: createResult.filePath, archivedFiles: createResult.archived.map((dest, i) => ({ source: createResult.archivedSources[i] ?? dest, archivedTo: dest })), tags: Array.isArray(tags) ? tags : [], memoryDir: createResult.scopeDirs.memoryDir, ok: true, ...(reason ? { reason } : {}) });
|
|
1885
1945
|
return { ok: true, filePath: createResult.filePath, archived: createResult.archived };
|
|
1886
1946
|
}
|
|
1887
1947
|
catch (e) {
|
|
@@ -1889,7 +1949,7 @@ export class IpcServer {
|
|
|
1889
1949
|
}
|
|
1890
1950
|
}
|
|
1891
1951
|
case 'memory.update': {
|
|
1892
|
-
const { sessionId, filePath, tags, touch } = cmd;
|
|
1952
|
+
const { sessionId, filePath, tags, touch, reason } = cmd;
|
|
1893
1953
|
if (!sessionId)
|
|
1894
1954
|
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId' };
|
|
1895
1955
|
try {
|
|
@@ -1912,12 +1972,14 @@ export class IpcServer {
|
|
|
1912
1972
|
catch (e) {
|
|
1913
1973
|
const batchDir = guard.taskCtx.consolidationBatchDir;
|
|
1914
1974
|
if (batchDir)
|
|
1915
|
-
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'update', scope: guard.writeScope, target: filePath ?? '', ok: false, error: e?.message });
|
|
1975
|
+
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'update', scope: guard.writeScope, target: filePath ?? '', ok: false, error: e?.message, ...(reason ? { reason } : {}) });
|
|
1916
1976
|
throw e;
|
|
1917
1977
|
}
|
|
1918
1978
|
const batchDirU = guard.taskCtx.consolidationBatchDir;
|
|
1979
|
+
// target 记解析后的绝对路径:回滚直接拿它读写文件,argv 原文(如 consolidated/x.md)
|
|
1980
|
+
// 会相对 daemon 的 cwd 解析而找不到。memoryDir 记文件实际所在层,供收尾重建索引。
|
|
1919
1981
|
if (batchDirU)
|
|
1920
|
-
appendOpsRecord(batchDirU, { ts: new Date().toISOString(), op: 'update', scope: guard.writeScope, target: filePath
|
|
1982
|
+
appendOpsRecord(batchDirU, { ts: new Date().toISOString(), op: 'update', scope: guard.writeScope, target: updateResult.filePath, tags: Array.isArray(tags) ? tags : undefined, prevTags: updateResult.prevTags, memoryDir: updateResult.targetMemoryDir, ok: true, ...(reason ? { reason } : {}) });
|
|
1921
1983
|
return { ok: true, filePath: updateResult.filePath };
|
|
1922
1984
|
}
|
|
1923
1985
|
catch (e) {
|
|
@@ -1925,14 +1987,14 @@ export class IpcServer {
|
|
|
1925
1987
|
}
|
|
1926
1988
|
}
|
|
1927
1989
|
case 'memory.archive': {
|
|
1928
|
-
const { sessionId, filePaths } = cmd;
|
|
1990
|
+
const { sessionId, filePaths, reason } = cmd;
|
|
1929
1991
|
if (!sessionId)
|
|
1930
1992
|
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId' };
|
|
1931
1993
|
try {
|
|
1932
1994
|
const guard = await this.assertMemoryWriteSession(sessionId, undefined, 'archive', undefined, cmd.debug === true);
|
|
1933
1995
|
if ('error' in guard)
|
|
1934
1996
|
return guard.error;
|
|
1935
|
-
const { archiveMemory } = await import('./core/memory/memory-writer.js');
|
|
1997
|
+
const { archiveMemory, sourceMemoryDirOf } = await import('./core/memory/memory-writer.js');
|
|
1936
1998
|
const { appendOpsRecord } = await import('./core/memory/consolidation-ops-records.js');
|
|
1937
1999
|
let archiveResult;
|
|
1938
2000
|
try {
|
|
@@ -1946,15 +2008,20 @@ export class IpcServer {
|
|
|
1946
2008
|
catch (e) {
|
|
1947
2009
|
const batchDir = guard.taskCtx.consolidationBatchDir;
|
|
1948
2010
|
if (batchDir)
|
|
1949
|
-
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'archive', scope: guard.writeScope, target: (Array.isArray(filePaths) ? filePaths : []).join(', '), ok: false, error: e?.message });
|
|
2011
|
+
appendOpsRecord(batchDir, { ts: new Date().toISOString(), op: 'archive', scope: guard.writeScope, target: (Array.isArray(filePaths) ? filePaths : []).join(', '), ok: false, error: e?.message, ...(reason ? { reason } : {}) });
|
|
1950
2012
|
throw e;
|
|
1951
2013
|
}
|
|
1952
2014
|
const batchDirA = guard.taskCtx.consolidationBatchDir;
|
|
1953
2015
|
if (batchDirA) {
|
|
1954
2016
|
for (let i = 0; i < archiveResult.archived.length; i++) {
|
|
1955
2017
|
const archivedTo = archiveResult.archived[i];
|
|
1956
|
-
|
|
1957
|
-
|
|
2018
|
+
// 用解析后的绝对路径,不用 argv 原文:回滚直接拿 sourceFile 做 renameSync,
|
|
2019
|
+
// 相对路径会相对 daemon 的 cwd 解析,跨关系回滚会落到不相干的位置。
|
|
2020
|
+
const sourceFile = archiveResult.archivedSources[i] ?? archivedTo;
|
|
2021
|
+
// memoryDir 取**源文件所属层**,不是本作用域:collectAffectedScopes 靠它
|
|
2022
|
+
// 决定收尾重建哪几层索引,写成本作用域会导致关系级索引不重建。
|
|
2023
|
+
const srcMemoryDir = sourceMemoryDirOf(sourceFile);
|
|
2024
|
+
appendOpsRecord(batchDirA, { ts: new Date().toISOString(), op: 'archive', scope: guard.writeScope, target: archivedTo, sourceFile, archivedTo, memoryDir: srcMemoryDir, ok: true, ...(reason ? { reason } : {}) });
|
|
1958
2025
|
}
|
|
1959
2026
|
}
|
|
1960
2027
|
return { ok: true, archived: archiveResult.archived };
|
package/dist/stats/db.js
CHANGED
|
@@ -28,6 +28,10 @@ export function getStatsDir(evolcoreHome) {
|
|
|
28
28
|
export function getDbPath(evolcoreHome) {
|
|
29
29
|
return path.join(getStatsDir(evolcoreHome), 'usage.db');
|
|
30
30
|
}
|
|
31
|
+
const ACTIVITY_EVENT_RETENTION_MS = 48 * 60 * 60 * 1000;
|
|
32
|
+
function pruneActivityEvents(db, now = Date.now()) {
|
|
33
|
+
db.prepare('DELETE FROM activity_events WHERE ts < ?').run(now - ACTIVITY_EVENT_RETENTION_MS);
|
|
34
|
+
}
|
|
31
35
|
export function closeStatsDb() {
|
|
32
36
|
if (!_db)
|
|
33
37
|
return;
|
|
@@ -73,6 +77,12 @@ export function startDailyRollupMaintenance(evolcoreHome) {
|
|
|
73
77
|
const db = getDb(evolcoreHome);
|
|
74
78
|
if (!db)
|
|
75
79
|
return undefined;
|
|
80
|
+
try {
|
|
81
|
+
pruneActivityEvents(db);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.warn(`[Stats] activity_events 清理失败(非致命): ${error}`);
|
|
85
|
+
}
|
|
76
86
|
try {
|
|
77
87
|
const daily = db.prepare('SELECT COUNT(*) AS n FROM usage_daily').get();
|
|
78
88
|
const events = db.prepare('SELECT COUNT(*) AS n FROM usage_events').get();
|
|
@@ -85,6 +95,12 @@ export function startDailyRollupMaintenance(evolcoreHome) {
|
|
|
85
95
|
logger.warn(`[Stats] usage_daily 回填检测失败(非致命): ${error}`);
|
|
86
96
|
}
|
|
87
97
|
return setInterval(() => {
|
|
98
|
+
try {
|
|
99
|
+
pruneActivityEvents(db);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
logger.warn(`[Stats] activity_events 清理失败(非致命): ${error}`);
|
|
103
|
+
}
|
|
88
104
|
try {
|
|
89
105
|
rebuildDailyRollup(evolcoreHome);
|
|
90
106
|
}
|
|
@@ -179,6 +195,22 @@ function _initTables(db) {
|
|
|
179
195
|
CREATE INDEX IF NOT EXISTS idx_me_agent_ts ON message_events(agent_aid, ts);
|
|
180
196
|
CREATE INDEX IF NOT EXISTS idx_me_peer_ts ON message_events(agent_aid, peer_key, ts);
|
|
181
197
|
|
|
198
|
+
-- Persisted task lifecycle counters used by status activity windows.
|
|
199
|
+
CREATE TABLE IF NOT EXISTS activity_events (
|
|
200
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
201
|
+
ts INTEGER NOT NULL,
|
|
202
|
+
agent_aid TEXT NOT NULL DEFAULT '',
|
|
203
|
+
type TEXT NOT NULL,
|
|
204
|
+
duration_ms INTEGER,
|
|
205
|
+
error_type TEXT
|
|
206
|
+
);
|
|
207
|
+
CREATE INDEX IF NOT EXISTS idx_ae_ts ON activity_events(ts);
|
|
208
|
+
CREATE INDEX IF NOT EXISTS idx_ae_agent_ts ON activity_events(agent_aid, ts);
|
|
209
|
+
CREATE TABLE IF NOT EXISTS activity_meta (
|
|
210
|
+
key TEXT PRIMARY KEY,
|
|
211
|
+
value TEXT NOT NULL
|
|
212
|
+
);
|
|
213
|
+
|
|
182
214
|
-- 按天预聚合表:grain = 天 × agent × peer × session × model × billing_fn。
|
|
183
215
|
-- 由 writer.ts 写时增量 UPSERT 维护,rebuildDailyRollup() 全量重建纠偏。
|
|
184
216
|
-- 保留 model+billing_fn 维度以便查询时仍按 calcCost 现算成本。
|
|
@@ -240,6 +272,7 @@ function _initTables(db) {
|
|
|
240
272
|
CREATE INDEX IF NOT EXISTS idx_mc_agentsession ON model_calls(agent_session_id);
|
|
241
273
|
CREATE INDEX IF NOT EXISTS idx_mc_ts ON model_calls(ts);
|
|
242
274
|
`);
|
|
275
|
+
db.prepare(`INSERT OR IGNORE INTO activity_meta (key, value) VALUES ('activity_events_started_at', ?)`).run(String(Date.now()));
|
|
243
276
|
// ── Schema Migration: 添加 cost 列 ───────────────────────────────────────
|
|
244
277
|
try {
|
|
245
278
|
const cols = db.prepare(`PRAGMA table_info(usage_events)`).all();
|
package/dist/stats/index.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { getDb, openReadonlyDb, getStatsDir, getDbPath, closeStatsDb, archiveOldData, listArchivePaths, rebuildDailyRollup } from './db.js';
|
|
5
5
|
export { normalizeUsage } from './normalizer.js';
|
|
6
|
-
export { initializeStatsPersistence, insertUsageEvent, insertContextBreakdown, insertMessageEvent, insertModelCalls } from './writer.js';
|
|
6
|
+
export { initializeStatsPersistence, insertUsageEvent, insertContextBreakdown, insertMessageEvent, insertActivityEvent, insertModelCalls } from './writer.js';
|
|
7
7
|
export { calcCost, resolvePriceRow, resolveCanonicalModel, registerBillingFn } from './billing.js';
|
|
8
|
-
export { queryAggregated, queryTodaySummary, querySessionTurns, queryContextBreakdown, queryTopPeers, queryTopModels, queryMessageAggregated, queryMessageTodaySummary, queryPeerList, querySummary, queryPeerDaily, queryTaskModelCalls, querySessionModelCalls } from './query.js';
|
|
8
|
+
export { queryAggregated, queryTodaySummary, queryStatusActivity, queryStatusActivityByAgent, querySessionTurns, queryContextBreakdown, queryTopPeers, queryTopModels, queryMessageAggregated, queryMessageTodaySummary, queryPeerList, querySummary, queryPeerDaily, queryTaskModelCalls, querySessionModelCalls } from './query.js';
|
|
9
9
|
export { getBudgetStatus } from './budget.js';
|
|
10
10
|
export { getRoleBudgetStatus, formatUsageSubjectKey } from './role-budget.js';
|
|
11
11
|
export { buildSystemVars, buildTurnVars } from './eck-vars.js';
|