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
package/dist/channels/wecom.js
CHANGED
|
@@ -257,8 +257,9 @@ export class PersistentWecomCardStore {
|
|
|
257
257
|
let changed = false;
|
|
258
258
|
for (const [taskId, record] of this.records) {
|
|
259
259
|
const terminalExpiry = record.updatedAt + DEFAULT_CARD_RETENTION_MS;
|
|
260
|
-
const pendingExpiry =
|
|
261
|
-
|
|
260
|
+
const pendingExpiry = record.interaction.blocking === true
|
|
261
|
+
? Number.POSITIVE_INFINITY
|
|
262
|
+
: (record.expiresAt ?? record.createdAt + 7 * DEFAULT_CARD_RETENTION_MS) + DEFAULT_CARD_RETENTION_MS;
|
|
262
263
|
const expiresAt = record.status === 'pending' ? pendingExpiry : terminalExpiry;
|
|
263
264
|
if (now > expiresAt) {
|
|
264
265
|
this.records.delete(taskId);
|
|
@@ -322,7 +323,10 @@ export function buildWecomInteractionCard(interaction, taskId) {
|
|
|
322
323
|
.filter(({ button }) => !('disabled' in button) || !button.disabled)
|
|
323
324
|
.slice(0, 6)
|
|
324
325
|
.map(({ button, index }) => ({
|
|
325
|
-
text: truncate(
|
|
326
|
+
text: truncate(interaction.kind.kind === 'action' && 'key' in button
|
|
327
|
+
&& interaction.kind.recommendedOption === button.key
|
|
328
|
+
? `💡 ${button.label}`
|
|
329
|
+
: button.label, 10) || `选项 ${index + 1}`,
|
|
326
330
|
style: buttonStyle(button.style),
|
|
327
331
|
key: interaction.kind.kind === 'command-card' ? `cmd_${index}` : `act_${index}`,
|
|
328
332
|
})),
|
package/dist/cli/agent-create.js
CHANGED
|
@@ -881,8 +881,15 @@ export class AgentInitializationService {
|
|
|
881
881
|
return;
|
|
882
882
|
try {
|
|
883
883
|
const record = readInitialization(aid);
|
|
884
|
-
if (!record ||
|
|
885
|
-
|
|
884
|
+
if (!record || this.running.has(aid))
|
|
885
|
+
return;
|
|
886
|
+
const waitingForOwner = record.status === 'in_progress' && record.phase === 'ownerpending';
|
|
887
|
+
// Records written before ownerBindSubmitted existed could fail by resubmitting the bind
|
|
888
|
+
// while SDK ownership events invalidated the consensus cache. Retry them once the
|
|
889
|
+
// requested Owner is observable; other failures still require an explicit --resume.
|
|
890
|
+
const lostConsensusRace = record.status === 'failed' && record.code === 'CONSENSUS_REQUIRED'
|
|
891
|
+
&& record.plan.ownerAid === ownerAid;
|
|
892
|
+
if (!waitingForOwner && !lostConsensusRace)
|
|
886
893
|
return;
|
|
887
894
|
this.resume(aid, record.actor);
|
|
888
895
|
}
|
|
@@ -1052,8 +1059,20 @@ export class AgentInitializationService {
|
|
|
1052
1059
|
}
|
|
1053
1060
|
this.phase(r, 'hot_loading');
|
|
1054
1061
|
await this.deps.hotLoad(p.aid);
|
|
1055
|
-
if (this.deps.bindOwner)
|
|
1056
|
-
|
|
1062
|
+
if (this.deps.bindOwner) {
|
|
1063
|
+
// Owner-consensus recovery and --resume re-run from the start. The SDK keeps emitting
|
|
1064
|
+
// ownership events for a few seconds after approval, each invalidating the cached
|
|
1065
|
+
// consensus, so resubmitting an accepted bind then fails with CONSENSUS_REQUIRED.
|
|
1066
|
+
// Retry when no pending or completed task for this Owner remains.
|
|
1067
|
+
const bindStillActive = this.deps.ownerBindActive
|
|
1068
|
+
? this.deps.ownerBindActive(p.aid, p.ownerAid)
|
|
1069
|
+
: r.ownerBindSubmitted === true;
|
|
1070
|
+
if (!bindStillActive) {
|
|
1071
|
+
this.phase(r, 'binding_owner');
|
|
1072
|
+
await this.deps.bindOwner(p.aid, p.ownerAid);
|
|
1073
|
+
r.ownerBindSubmitted = true;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1057
1076
|
this.phase(r, 'ownerpending');
|
|
1058
1077
|
const ownership = await this.deps.checkOwner(p.aid, p.ownerAid);
|
|
1059
1078
|
this.check(r);
|
|
@@ -20,10 +20,11 @@ export async function cmdAidBind(args) {
|
|
|
20
20
|
const delegationToken = sessionId ? readDelegationCredential() : undefined;
|
|
21
21
|
if (sessionId && !delegationToken)
|
|
22
22
|
throw Object.assign(new Error('Active task delegation is required'), { code: 'DELEGATION_REQUIRED' });
|
|
23
|
+
const isDecision = args[0] === 'accept' || args[0] === 'reject';
|
|
23
24
|
const response = await ipcQuery(resolvePaths().socket, {
|
|
24
25
|
type: 'aid.ownership', argv: ['aid', 'bind', ...args],
|
|
25
26
|
...(sessionId ? { sessionId, delegationToken } : {}),
|
|
26
|
-
}, 30_000);
|
|
27
|
+
}, isDecision ? 150_000 : 30_000);
|
|
27
28
|
if (!response?.ok)
|
|
28
29
|
throw Object.assign(new Error(response?.error ?? 'daemon 未运行或 IPC 不可用'), { code: response?.code, retryable: response?.retryable });
|
|
29
30
|
if (formatJson)
|
|
@@ -40,6 +41,8 @@ export async function cmdAidBind(args) {
|
|
|
40
41
|
console.log(`绑定任务: ${state.task.operation_id} (${state.task.status}/${state.task.step})`);
|
|
41
42
|
if (state.decisionTask)
|
|
42
43
|
console.log(`审批任务: ${state.decisionTask.operation_id} (${state.decisionTask.status}/${state.decisionTask.step})`);
|
|
44
|
+
if (state.decisionConsensus)
|
|
45
|
+
console.log(`审批目标共识: ${state.decisionConsensus.status} (owner=${state.decisionConsensus.owner_aid ?? '-'}, version=${state.decisionConsensus.relation_version}, lock=${state.decisionConsensus.lock})`);
|
|
43
46
|
for (const request of state.requests ?? []) {
|
|
44
47
|
console.log(`待确认邀约: ${request.operation_id} (sub=${request.sub_aid}, owner=${request.owner_aid}, 本端=${request.local_role})`);
|
|
45
48
|
}
|
|
@@ -31,6 +31,8 @@ import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
|
31
31
|
import { inspectDataMigrationRequirement } from '../core/data-migration.js';
|
|
32
32
|
import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
33
33
|
import { computeBuildId } from '../utils/evolcore-version.js';
|
|
34
|
+
import { readHostInfo } from '../utils/host-info.js';
|
|
35
|
+
import { queryStatusActivity, queryStatusActivityByAgent } from '../stats/query.js';
|
|
34
36
|
import { listCodexAppServerProcesses, unregisterCodexAppServerProcess } from '../agents/codex/app-server-registry.js';
|
|
35
37
|
import { DEFAULT_ECWEB_PORT, ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
|
|
36
38
|
import { checkCodexProjectPath, inspectCodexManagedHook, ensureCodexManagedHook, runCodexHookLaunchDiagnostic, } from '../agents/codex/diagnostics.js';
|
|
@@ -301,7 +303,20 @@ async function requireFreshSourceBuild() {
|
|
|
301
303
|
async function launchPackageLifecycle(source, log, options = {}) {
|
|
302
304
|
const correlated = options.requestId ?? process.env[RESTART_REQUEST_ID_ENV];
|
|
303
305
|
const upgradeJobId = correlated ? readRestartRequest(path.join(daemonControlDir(), 'restart-pending.json'), correlated)?.upgradeJobId : undefined;
|
|
304
|
-
|
|
306
|
+
let configuredAutoUpgrade = true;
|
|
307
|
+
if (!upgradeJobId && options.applyPackageUpgrades === undefined) {
|
|
308
|
+
try {
|
|
309
|
+
configuredAutoUpgrade = loadDaemonConfig().upgrade?.autoOnStart !== false;
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
configuredAutoUpgrade = false;
|
|
313
|
+
log('Could not read autoOnStart; skipping automatic package upgrade');
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const applyPackageUpgrades = upgradeJobId
|
|
317
|
+
? true
|
|
318
|
+
: options.applyPackageUpgrades ?? configuredAutoUpgrade;
|
|
319
|
+
if (!options.prepared && !upgradeJobId && applyPackageUpgrades)
|
|
305
320
|
await ensureFreshPackageVersionInfo(undefined, { source, log });
|
|
306
321
|
const root = resolvePaths().root;
|
|
307
322
|
const launch = platform.resolveMonitorLaunch(getPackageRoot(), path.join(root, 'data', 'monitor-stage'));
|
|
@@ -313,7 +328,12 @@ async function launchPackageLifecycle(source, log, options = {}) {
|
|
|
313
328
|
return { ok: false, reason: 'RESTART_ENTRY_MISSING' };
|
|
314
329
|
}
|
|
315
330
|
const requestId = options.requestId ?? process.env[RESTART_REQUEST_ID_ENV];
|
|
316
|
-
const args = [
|
|
331
|
+
const args = [
|
|
332
|
+
...launch.args,
|
|
333
|
+
'restart-monitor',
|
|
334
|
+
...(options.clear ? ['--clear'] : []),
|
|
335
|
+
applyPackageUpgrades ? '--apply-package-upgrades' : '--skip-package-upgrades',
|
|
336
|
+
];
|
|
317
337
|
const env = {
|
|
318
338
|
...options.env,
|
|
319
339
|
[RUNTIME_HOME_ENV]: root,
|
|
@@ -758,7 +778,7 @@ export async function cmdStart(opts = {}) {
|
|
|
758
778
|
// 这条路由 restart-monitor 拉起 daemon,同样靠覆盖文件把 --as-mode 带过去
|
|
759
779
|
const overrideFile = writeAsModeOverride(opts.asMode);
|
|
760
780
|
const launched = await launchPackageLifecycle('start', console.log, {
|
|
761
|
-
prepared: true, foreground: opts.foreground, env: buildDaemonEnv(p, opts),
|
|
781
|
+
prepared: true, foreground: opts.foreground, env: buildDaemonEnv(p, opts), applyPackageUpgrades: true,
|
|
762
782
|
});
|
|
763
783
|
if (!launched.ok) {
|
|
764
784
|
if (!launched.pending)
|
|
@@ -1291,7 +1311,180 @@ function showConfigChannels(config) {
|
|
|
1291
1311
|
console.log(' (no channels configured)');
|
|
1292
1312
|
}
|
|
1293
1313
|
}
|
|
1294
|
-
|
|
1314
|
+
function showLiveChannels(status) {
|
|
1315
|
+
const channels = status?.channels;
|
|
1316
|
+
if (!channels || typeof channels !== 'object' || Array.isArray(channels))
|
|
1317
|
+
return false;
|
|
1318
|
+
const entries = Object.entries(channels);
|
|
1319
|
+
console.log('🔌 Channels (live status):');
|
|
1320
|
+
if (entries.length === 0) {
|
|
1321
|
+
console.log(' (no channel instances loaded)');
|
|
1322
|
+
return true;
|
|
1323
|
+
}
|
|
1324
|
+
for (const [name, channel] of entries) {
|
|
1325
|
+
const state = channel?.connected === true ? '✓ Connected'
|
|
1326
|
+
: channel?.connected === false ? '✗ Disconnected' : '? Unknown';
|
|
1327
|
+
const type = typeof channel?.channelType === 'string' ? ` (${channel.channelType})` : '';
|
|
1328
|
+
console.log(` ${name}: ${state}${type}`);
|
|
1329
|
+
}
|
|
1330
|
+
return true;
|
|
1331
|
+
}
|
|
1332
|
+
function localDayStartMs(now = new Date()) {
|
|
1333
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
1334
|
+
}
|
|
1335
|
+
function statusActivityJson(window) {
|
|
1336
|
+
return {
|
|
1337
|
+
received: window.received,
|
|
1338
|
+
sent: window.sent,
|
|
1339
|
+
completed: window.completed,
|
|
1340
|
+
errors: window.errors,
|
|
1341
|
+
interrupts: window.interrupts,
|
|
1342
|
+
avgResponseMs: window.avgResponseMs,
|
|
1343
|
+
modelCalls: window.modelCalls,
|
|
1344
|
+
tokens: window.tokens,
|
|
1345
|
+
cost: window.cost,
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
function readLocalPackageVersion() {
|
|
1349
|
+
try {
|
|
1350
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(getPackageRoot(), 'package.json'), 'utf8'));
|
|
1351
|
+
return typeof pkg.version === 'string' ? pkg.version : null;
|
|
1352
|
+
}
|
|
1353
|
+
catch {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
async function cmdStatusJson() {
|
|
1358
|
+
const p = resolvePaths();
|
|
1359
|
+
const started = Date.now();
|
|
1360
|
+
const instanceStatus = scanInstances();
|
|
1361
|
+
const aliveMains = instanceStatus.mains.filter(instance => instance.alive);
|
|
1362
|
+
let ipcError;
|
|
1363
|
+
const pingPromise = probeDaemon(p.socket, DAEMON_PROBE_TIMEOUT_MS, true).catch(error => {
|
|
1364
|
+
ipcError = error;
|
|
1365
|
+
return null;
|
|
1366
|
+
});
|
|
1367
|
+
const [ping, statusResponse, aidsResponse, agentsResponse, agentStatsResponse] = await Promise.all([
|
|
1368
|
+
pingPromise,
|
|
1369
|
+
ipcQuery(p.socket, { type: 'status' }).catch(() => null),
|
|
1370
|
+
ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null),
|
|
1371
|
+
ipcQuery(p.socket, { type: 'evolagent.list' }).catch(() => null),
|
|
1372
|
+
ipcQuery(p.socket, { type: 'agent-stats' }).catch(() => null),
|
|
1373
|
+
]);
|
|
1374
|
+
const now = Date.now();
|
|
1375
|
+
const runningPid = ping?.pid ?? aliveMains[0]?.record.pid ?? null;
|
|
1376
|
+
const mainRecord = runningPid ? aliveMains.find(instance => instance.record.pid === runningPid)?.record : undefined;
|
|
1377
|
+
const statusAvailable = !!statusResponse;
|
|
1378
|
+
const overallStatus = statusAvailable
|
|
1379
|
+
? 'running'
|
|
1380
|
+
: platform.isWindows && ipcError instanceof IpcTransportError
|
|
1381
|
+
&& ['ENOENT', 'ECONNREFUSED'].includes(ipcError.code)
|
|
1382
|
+
? 'stopped'
|
|
1383
|
+
: 'unknown';
|
|
1384
|
+
const todayStart = localDayStartMs(new Date(now));
|
|
1385
|
+
const lastHourActivity = queryStatusActivity(p.root, now - 3_600_000, now);
|
|
1386
|
+
const todayActivity = statusResponse?.activity?.today ?? queryStatusActivity(p.root, todayStart, now);
|
|
1387
|
+
const daemonLastHourActivity = statusResponse?.activity?.lastHour;
|
|
1388
|
+
const effectiveLastHourActivity = daemonLastHourActivity ?? lastHourActivity;
|
|
1389
|
+
const agents = agentsResponse?.agents ?? [];
|
|
1390
|
+
const agentStats = new Map((agentStatsResponse?.stats ?? []).map((item) => [item.aid, item]));
|
|
1391
|
+
const statusAgents = Array.isArray(statusResponse?.agents) ? statusResponse.agents : null;
|
|
1392
|
+
const agentUsage = statusAgents
|
|
1393
|
+
? new Map()
|
|
1394
|
+
: queryStatusActivityByAgent(p.root, todayStart, now, agents.map((agent) => String(agent.aid ?? agent.name ?? '')));
|
|
1395
|
+
const diagnostics = [];
|
|
1396
|
+
if (!statusAvailable) {
|
|
1397
|
+
diagnostics.push({ code: 'DAEMON_IPC_UNAVAILABLE', message: ipcError instanceof Error ? ipcError.message : 'daemon status unavailable' });
|
|
1398
|
+
}
|
|
1399
|
+
const agentItems = statusAgents ?? agents.map((agent) => {
|
|
1400
|
+
const aid = String(agent.aid ?? agent.name ?? '');
|
|
1401
|
+
const runtime = agentStats.get(aid);
|
|
1402
|
+
const usage = agentUsage.get(aid) ?? queryStatusActivity(p.root, todayStart, now, aid);
|
|
1403
|
+
return {
|
|
1404
|
+
aid,
|
|
1405
|
+
name: agent.name ?? aid,
|
|
1406
|
+
status: agent.status ?? 'unknown',
|
|
1407
|
+
baseagent: agent.baseagent ?? null,
|
|
1408
|
+
model: agent.model ?? null,
|
|
1409
|
+
effort: agent.effort ?? null,
|
|
1410
|
+
queue: runtime ? { pending: runtime.queued ?? 0, processing: runtime.processing ?? 0 } : null,
|
|
1411
|
+
usage: {
|
|
1412
|
+
calls: usage.modelCalls,
|
|
1413
|
+
tokens: usage.tokens,
|
|
1414
|
+
cost: usage.cost,
|
|
1415
|
+
},
|
|
1416
|
+
};
|
|
1417
|
+
});
|
|
1418
|
+
const channels = Object.entries(statusResponse?.channels ?? {})
|
|
1419
|
+
.filter(([, channel]) => channel?.channelType !== 'aun')
|
|
1420
|
+
.map(([name, channel]) => ({
|
|
1421
|
+
name,
|
|
1422
|
+
type: channel?.channelType ?? name,
|
|
1423
|
+
status: channel?.status ?? (channel?.connected ? 'connected' : 'disconnected'),
|
|
1424
|
+
connected: channel?.connected ?? false,
|
|
1425
|
+
}));
|
|
1426
|
+
const aunItems = (aidsResponse?.aids ?? []).map((aid) => ({
|
|
1427
|
+
aid: aid.aid,
|
|
1428
|
+
channelName: aid.channelName,
|
|
1429
|
+
status: aid.status ?? 'unknown',
|
|
1430
|
+
connected: aid.status === 'connected',
|
|
1431
|
+
queue: aid.queueStatus ? {
|
|
1432
|
+
pending: aid.queueStatus.queued ?? 0,
|
|
1433
|
+
processing: aid.queueStatus.processing ?? 0,
|
|
1434
|
+
} : null,
|
|
1435
|
+
reconnectCount: aid.reconnectCount ?? 0,
|
|
1436
|
+
}));
|
|
1437
|
+
const host = statusResponse?.host ?? readHostInfo(readLocalPackageVersion());
|
|
1438
|
+
const ecwebEntry = readEcwebInstanceEntries(p)[0];
|
|
1439
|
+
const ecweb = ecwebEntry
|
|
1440
|
+
? { source: 'instance-record', status: platform.isProcessRunning(ecwebEntry.record.pid) ? 'running' : 'unknown', port: ecwebEntry.record.port, url: `http://localhost:${ecwebEntry.record.port}` }
|
|
1441
|
+
: { source: 'instance-record', status: 'stopped', port: null, url: null };
|
|
1442
|
+
const triggerSummary = statusResponse?.triggers
|
|
1443
|
+
? { source: 'runtime+history', ...statusResponse.triggers }
|
|
1444
|
+
: { source: 'unavailable', enabled: null, today: { calls: null, failures: null } };
|
|
1445
|
+
const build = statusResponse?.build
|
|
1446
|
+
? { runtimeMode: statusResponse.build.runtimeMode, version: statusResponse.build.version ?? null }
|
|
1447
|
+
: null;
|
|
1448
|
+
const output = {
|
|
1449
|
+
schemaVersion: 1,
|
|
1450
|
+
generatedAt: new Date(now).toISOString(),
|
|
1451
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
1452
|
+
overall: {
|
|
1453
|
+
status: overallStatus,
|
|
1454
|
+
degraded: !statusAvailable || statusResponse?.agentRuntime?.state === 'error',
|
|
1455
|
+
},
|
|
1456
|
+
host,
|
|
1457
|
+
daemon: {
|
|
1458
|
+
pid: runningPid,
|
|
1459
|
+
uptimeMs: ping?.uptime ?? statusResponse?.uptime ?? null,
|
|
1460
|
+
ipc: { reachable: statusAvailable, latencyMs: Date.now() - started },
|
|
1461
|
+
runMode: statusResponse?.runMode ?? mainRecord?.runMode ?? null,
|
|
1462
|
+
build,
|
|
1463
|
+
queue: statusResponse?.queue ?? { pending: null, processing: null },
|
|
1464
|
+
},
|
|
1465
|
+
baseagents: statusResponse?.baseagents ?? { source: 'unavailable', items: [] },
|
|
1466
|
+
activity: {
|
|
1467
|
+
lastHour: statusActivityJson(effectiveLastHourActivity),
|
|
1468
|
+
today: statusActivityJson(todayActivity),
|
|
1469
|
+
},
|
|
1470
|
+
agents: {
|
|
1471
|
+
source: statusAvailable ? 'daemon+stats-db' : 'stats-db',
|
|
1472
|
+
items: agentItems,
|
|
1473
|
+
},
|
|
1474
|
+
aun: { source: aidsResponse ? 'daemon' : 'unavailable', items: aunItems },
|
|
1475
|
+
channels: { source: statusAvailable ? 'daemon' : 'unavailable', items: channels },
|
|
1476
|
+
controlAid: statusResponse?.controlAid ?? null,
|
|
1477
|
+
triggers: triggerSummary,
|
|
1478
|
+
ecweb,
|
|
1479
|
+
diagnostics,
|
|
1480
|
+
};
|
|
1481
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1482
|
+
}
|
|
1483
|
+
export async function cmdStatus(options = {}) {
|
|
1484
|
+
if (options.format === 'json') {
|
|
1485
|
+
await cmdStatusJson();
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1295
1488
|
const p = resolvePaths();
|
|
1296
1489
|
const status = scanInstances();
|
|
1297
1490
|
const aliveMains = status.mains.filter(m => m.alive);
|
|
@@ -1300,13 +1493,19 @@ export async function cmdStatus() {
|
|
|
1300
1493
|
// query can otherwise wait behind the same IPC/event-loop delay and make the
|
|
1301
1494
|
// total `ec status` time the sum of several individual timeouts.
|
|
1302
1495
|
let ipcFailure;
|
|
1496
|
+
let agentsQueryFailure;
|
|
1303
1497
|
const pingPromise = probeDaemon(p.socket, DAEMON_PROBE_TIMEOUT_MS, true).catch(error => {
|
|
1304
1498
|
ipcFailure = error;
|
|
1305
1499
|
return null;
|
|
1306
1500
|
});
|
|
1307
1501
|
const statusPromise = ipcQuery(p.socket, { type: 'status' }).catch(() => null);
|
|
1308
1502
|
const aidsPromise = ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null);
|
|
1309
|
-
const agentsPromise =
|
|
1503
|
+
const agentsPromise = process.env.EVOLCORE_SESSION_ID
|
|
1504
|
+
? Promise.resolve({ ok: false, error: 'per-agent listing requires a local control token' })
|
|
1505
|
+
: ipcQuery(p.socket, { type: 'evolagent.list' }, 5_000).catch(error => {
|
|
1506
|
+
agentsQueryFailure = error;
|
|
1507
|
+
return null;
|
|
1508
|
+
});
|
|
1310
1509
|
// Only the ping is needed to decide whether to show the live-daemon
|
|
1311
1510
|
// sections. The remaining responses continue in parallel while local
|
|
1312
1511
|
// process/session information is rendered.
|
|
@@ -1484,6 +1683,7 @@ export async function cmdStatus() {
|
|
|
1484
1683
|
// Channel status. A running daemon is authoritative and avoids reading
|
|
1485
1684
|
// protected config files from the CLI's potentially sandboxed process.
|
|
1486
1685
|
let agentsResponse = null;
|
|
1686
|
+
let liveAgentRuntime = null;
|
|
1487
1687
|
if (pid) {
|
|
1488
1688
|
console.log('');
|
|
1489
1689
|
const [statusResponse, aidsResponse, agentResponse] = await Promise.all([
|
|
@@ -1492,6 +1692,7 @@ export async function cmdStatus() {
|
|
|
1492
1692
|
agentsPromise,
|
|
1493
1693
|
]);
|
|
1494
1694
|
agentsResponse = agentResponse;
|
|
1695
|
+
liveAgentRuntime = statusResponse?.agentRuntime ?? null;
|
|
1495
1696
|
if (statusResponse) {
|
|
1496
1697
|
if (statusResponse.build) {
|
|
1497
1698
|
if (statusResponse.build.runtimeMode === 'source') {
|
|
@@ -1511,6 +1712,16 @@ export async function cmdStatus() {
|
|
|
1511
1712
|
console.log(` Build ID: runtime=unavailable (daemon 未提供 build identity) dist=${computeBuildId(path.join(getPackageRoot(), 'dist')) ?? '?'}`);
|
|
1512
1713
|
console.log(' ⚠ 当前 daemon 未提供 build ID;请重启到包含 build identity 的版本后再判断源码行为。');
|
|
1513
1714
|
}
|
|
1715
|
+
if (!showLiveChannels(statusResponse)) {
|
|
1716
|
+
const config = readStatusDefaultsConfig(p.defaultsConfig);
|
|
1717
|
+
if (config) {
|
|
1718
|
+
console.log('🔌 Channels (live status unavailable; configuration is stale):');
|
|
1719
|
+
showConfigChannels(config);
|
|
1720
|
+
}
|
|
1721
|
+
else {
|
|
1722
|
+
console.log('🔌 Channels: live status unavailable');
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1514
1725
|
// 🔑 AUN AIDs 表格(详细 AUN 实例状态)
|
|
1515
1726
|
try {
|
|
1516
1727
|
if (aidsResponse?.ok && aidsResponse.aids?.length > 0) {
|
|
@@ -1558,17 +1769,17 @@ export async function cmdStatus() {
|
|
|
1558
1769
|
if (pid) {
|
|
1559
1770
|
try {
|
|
1560
1771
|
const agentResult = agentsResponse;
|
|
1561
|
-
if (agentResult?.ok && agentResult.agents
|
|
1772
|
+
if (agentResult?.ok && Array.isArray(agentResult.agents)) {
|
|
1562
1773
|
const agents = agentResult.agents;
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
}
|
|
1774
|
+
console.log('');
|
|
1775
|
+
console.log('🤖 EvolAgents (live status):');
|
|
1776
|
+
if (agents.length === 0)
|
|
1777
|
+
console.log(' (no agents loaded)');
|
|
1778
|
+
for (const a of agents) {
|
|
1779
|
+
const statusIcon = a.status === 'running' ? '●' : a.status === 'error' ? '✗' : a.status === 'disabled' ? '○' : '◌';
|
|
1780
|
+
const channels = summarizeChannelFingerprints(a.channels || []);
|
|
1781
|
+
const shortName = a.name.replace(/\.agentid\.pub$/, '');
|
|
1782
|
+
console.log(` ${statusIcon} ${shortName.padEnd(20)} ${a.status.padEnd(10)} ${channels}`);
|
|
1572
1783
|
}
|
|
1573
1784
|
}
|
|
1574
1785
|
else {
|
|
@@ -1578,7 +1789,18 @@ export async function cmdStatus() {
|
|
|
1578
1789
|
const agents = diskRegistry.list();
|
|
1579
1790
|
if (agents.length > 0) {
|
|
1580
1791
|
console.log('');
|
|
1581
|
-
|
|
1792
|
+
const detail = typeof agentResult?.error === 'string'
|
|
1793
|
+
? agentResult.error
|
|
1794
|
+
: agentsQueryFailure instanceof Error ? agentsQueryFailure.message : 'IPC request failed';
|
|
1795
|
+
const running = Number.isFinite(liveAgentRuntime?.runningAgents) ? liveAgentRuntime.runningAgents : null;
|
|
1796
|
+
const runnable = Number.isFinite(liveAgentRuntime?.runnableAgents) ? liveAgentRuntime.runnableAgents : null;
|
|
1797
|
+
if (running !== null && runnable !== null) {
|
|
1798
|
+
console.log(`🤖 EvolAgents (live aggregate: ${running} running / ${runnable} runnable):`);
|
|
1799
|
+
console.log(` Per-agent details unavailable (${detail}); following entries are stale configuration only:`);
|
|
1800
|
+
}
|
|
1801
|
+
else {
|
|
1802
|
+
console.log(`🤖 EvolAgents (live status unavailable: ${detail}; stale configuration):`);
|
|
1803
|
+
}
|
|
1582
1804
|
for (const a of agents) {
|
|
1583
1805
|
const diskStatus = a.status === 'disabled' || a.status === 'error' ? a.status : 'unknown';
|
|
1584
1806
|
const channels = summarizeChannelFingerprints(a.channels || []);
|
|
@@ -2965,7 +3187,7 @@ async function printEcwebStatus(p) {
|
|
|
2965
3187
|
console.log('');
|
|
2966
3188
|
if (aliveEntries.length > 0) {
|
|
2967
3189
|
const visibleEntry = aliveEntries[0];
|
|
2968
|
-
console.log(`🔭 ECWeb: 进程存活 (PID: ${visibleEntry.record.pid}) 但 HTTP 未就绪 (端口 ${visibleEntry.record.port}),查看 logs/
|
|
3190
|
+
console.log(`🔭 ECWeb: 进程存活 (PID: ${visibleEntry.record.pid}) 但 HTTP 未就绪 (端口 ${visibleEntry.record.port}),查看 logs/ecweb-daemon.log`);
|
|
2969
3191
|
}
|
|
2970
3192
|
else if (occupiedPids.size > 0) {
|
|
2971
3193
|
console.log(`🔭 ECWeb: 端口仍被未登记进程占用 (PID: ${[...occupiedPids].join(', ')}),请先执行 ec restart`);
|
|
@@ -2973,6 +3195,9 @@ async function printEcwebStatus(p) {
|
|
|
2973
3195
|
else if (entries.length > 0) {
|
|
2974
3196
|
console.log('🔭 ECWeb: instance 记录存在,但宿主 PID 不可见且 HTTP 未就绪');
|
|
2975
3197
|
}
|
|
3198
|
+
else if (externalUrl) {
|
|
3199
|
+
console.log('🔭 ECWeb: 已启用但未运行,查看 logs/ecweb-daemon.log');
|
|
3200
|
+
}
|
|
2976
3201
|
else {
|
|
2977
3202
|
console.log('🔭 ECWeb: 未运行');
|
|
2978
3203
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -82,7 +82,7 @@ function printLifecycleHelp(command) {
|
|
|
82
82
|
start: `用法: ec start [--foreground] [--diagnose|-d] [--debug] [--as-mode dev|install]\n\n --foreground 前台运行,适合 systemd 或容器直接管理\n --diagnose, -d 以诊断模式启动 daemon\n --debug 调试模式:注册调试 IPC,可在宿主终端以任意 session 身份发命令\n --as-mode 覆盖运行模式判定(仅本次启动生效,不写配置):\n install = 按安装模式(校验随包文件完整性,篡改自动恢复)\n dev = 按开发模式(不校验随包文件)`,
|
|
83
83
|
stop: '用法: ec stop\n\n停止当前 EVOLCORE_HOME 下运行的 EvolCore 服务。',
|
|
84
84
|
restart: `用法: ec restart [--clear] [--diagnose|-d]\n\n --clear 同时清理跨 HOME 残留的 EvolCore 主进程\n --diagnose, -d 以诊断模式启动新 daemon`,
|
|
85
|
-
status: '用法: ec status\n\n查看 daemon、
|
|
85
|
+
status: '用法: ec status [--format json]\n\n查看 daemon、Agent、渠道与队列运行状态。JSON 模式输出结构化状态快照。',
|
|
86
86
|
logs: `用法: ec logs [选项]\n\n --level <error|warn|info|debug> 最低日志级别\n --module <name> 只显示指定模块\n --raw 原始输出\n --no-color 禁用颜色`,
|
|
87
87
|
watch: `用法: ec watch [logs|log|aid|msg|diag|web|session]\n\n ec watch 打开监控选择菜单\n ec watch logs 选择日志类型后监控\n ec watch log <类型...> 直接监控指定日志类型\n ec watch aid 监控 AID 连接\n ec watch msg 打开消息 TUI\n ec watch diag 实时诊断消息生命周期(AUN→daemon→任务)\n ec watch web 安装/升级并启动 EC Web\n ec watch session ec watch web 的兼容别名`,
|
|
88
88
|
'restart-monitor': '用法: ec restart-monitor\n\n运行内 daemon 重启与升级监控器。',
|
|
@@ -266,21 +266,33 @@ export async function main(args) {
|
|
|
266
266
|
printLifecycleHelp('status');
|
|
267
267
|
break;
|
|
268
268
|
}
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
console.error(`❌ ${result?.error || 'status is not allowed in this task'}`);
|
|
269
|
+
{
|
|
270
|
+
const statusArgs = strippedArgs.slice(1);
|
|
271
|
+
const formatValue = getArgValue(statusArgs, '--format');
|
|
272
|
+
const validStatusArgs = statusArgs.length === 0
|
|
273
|
+
|| (statusArgs.length === 2 && statusArgs[0] === '--format' && formatValue === 'json');
|
|
274
|
+
if (!validStatusArgs) {
|
|
275
|
+
console.error('用法: ec status [--format json]');
|
|
277
276
|
process.exitCode = 1;
|
|
278
277
|
break;
|
|
279
278
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
const format = formatValue;
|
|
280
|
+
if (process.env.EVOLCORE_SESSION_ID) {
|
|
281
|
+
const sessionId = process.env.EVOLCORE_SESSION_ID;
|
|
282
|
+
const delegationToken = readDelegationCredential();
|
|
283
|
+
const result = await ipcQuery(resolvePaths().socket, {
|
|
284
|
+
type: 'status.auth', sessionId, delegationToken,
|
|
285
|
+
});
|
|
286
|
+
if (!result?.ok) {
|
|
287
|
+
console.error(`❌ ${result?.error || 'status is not allowed in this task'}`);
|
|
288
|
+
process.exitCode = 1;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
await cmdStatus({ format });
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
await cmdStatus({ format });
|
|
295
|
+
}
|
|
284
296
|
}
|
|
285
297
|
break;
|
|
286
298
|
case 'logs':
|
|
@@ -43,11 +43,12 @@ ec memory note drop <slug>
|
|
|
43
43
|
ec memory note list
|
|
44
44
|
|
|
45
45
|
ec memory create --scope <relation|agent|owner> --slug <slug> --desc '<description>' \\
|
|
46
|
-
--tags '<tag1>,<tag2>' [--keywords '<kw1>,<kw2>'] [--replaces '<path1>,<path2>']
|
|
46
|
+
--tags '<tag1>,<tag2>' [--keywords '<kw1>,<kw2>'] [--replaces '<path1>,<path2>'] \\
|
|
47
|
+
[--peer <peerKey>] [--reason '<why>'] [--generalized-from <path>] [--promoted-from <path>] '<content>'
|
|
47
48
|
|
|
48
|
-
ec memory update <path> [--tags '<tag1>,<tag2>'] [--touch]
|
|
49
|
+
ec memory update <path> [--tags '<tag1>,<tag2>'] [--touch] [--reason '<why>']
|
|
49
50
|
|
|
50
|
-
ec memory archive <path...>
|
|
51
|
+
ec memory archive <path...> [--reason '<why>']
|
|
51
52
|
|
|
52
53
|
ec memory consolidate [--mode <notes|generalize|promote|profile|forget>]
|
|
53
54
|
|
|
@@ -168,8 +169,9 @@ export function resolveMemoryCommandArgv(argv) {
|
|
|
168
169
|
const kind = noteSub ? `note.${noteSub}` : argv[1];
|
|
169
170
|
const known = {
|
|
170
171
|
note: ['--slug', '--tags', '--speakers'], 'note.update': ['--slug', '--tags'],
|
|
171
|
-
'note.drop': [], 'note.list': [],
|
|
172
|
-
|
|
172
|
+
'note.drop': [], 'note.list': [],
|
|
173
|
+
create: ['--scope', '--slug', '--desc', '--tags', '--keywords', '--replaces', '--peer', '--reason', '--generalized-from', '--promoted-from'],
|
|
174
|
+
update: ['--tags', '--touch', '--reason'], archive: ['--reason'], demote: ['--scope'], consolidate: ['--mode'],
|
|
173
175
|
};
|
|
174
176
|
// feedback 已移除:旧会话/旧文档可能还在调用,给一句能照做的说明而不是泛化的非法命令。
|
|
175
177
|
if (kind === 'feedback') {
|
|
@@ -209,17 +211,19 @@ export function resolveMemoryCommandArgv(argv) {
|
|
|
209
211
|
throw new Error('--peer requires relation scope');
|
|
210
212
|
return { type, scope, slug: required(flags.get('--slug'), '--slug'), description: required(flags.get('--desc'), '--desc'),
|
|
211
213
|
content: required(content, 'content'), tags: parseTags(flags.get('--tags')), keywords: parseTags(flags.get('--keywords')),
|
|
212
|
-
replaces: parseTags(flags.get('--replaces')), targetPeerKey: flags.get('--peer')
|
|
214
|
+
replaces: parseTags(flags.get('--replaces')), targetPeerKey: flags.get('--peer'),
|
|
215
|
+
reason: flags.get('--reason'), generalizedFrom: flags.get('--generalized-from'), promotedFrom: flags.get('--promoted-from') };
|
|
213
216
|
}
|
|
214
217
|
if (kind === 'update') {
|
|
215
218
|
if (!flags.has('--tags') && !flags.has('--touch'))
|
|
216
219
|
throw new Error('No memory changes supplied');
|
|
217
|
-
return { type, filePath: required(positional[0], 'path'), tags: parseTags(flags.get('--tags')), touch: flags.has('--touch')
|
|
220
|
+
return { type, filePath: required(positional[0], 'path'), tags: parseTags(flags.get('--tags')), touch: flags.has('--touch'),
|
|
221
|
+
reason: flags.get('--reason') };
|
|
218
222
|
}
|
|
219
223
|
if (kind === 'archive') {
|
|
220
224
|
if (!positional.length)
|
|
221
225
|
throw new Error('Memory paths are required');
|
|
222
|
-
return { type, filePaths: positional };
|
|
226
|
+
return { type, filePaths: positional, reason: flags.get('--reason') };
|
|
223
227
|
}
|
|
224
228
|
if (kind === 'demote') {
|
|
225
229
|
const scope = flags.get('--scope') ?? 'agent';
|
|
@@ -240,9 +244,9 @@ const USAGE = {
|
|
|
240
244
|
note: "ec memory note --slug <slug> [--tags '<tag1>,<tag2>'] [--speakers '<aid1>,<aid2>'] '<正文>'",
|
|
241
245
|
noteUpdate: "ec memory note update <slug> [--slug <新slug>] [--tags '<tag1>,<tag2>'] ['<新正文>']",
|
|
242
246
|
noteDrop: 'ec memory note drop <slug>',
|
|
243
|
-
create: "ec memory create --scope <relation|agent|owner> --slug <slug> --desc '<描述>' --tags '<tag1>,<tag2>' [--keywords '<kw1>,<kw2>'] [--replaces '<路径1>,<路径2>'] [--peer <peerKey>] '<正文>'",
|
|
244
|
-
update: "ec memory update <路径> [--tags '<tag1>,<tag2>'] [--touch](至少给一项)",
|
|
245
|
-
archive:
|
|
247
|
+
create: "ec memory create --scope <relation|agent|owner> --slug <slug> --desc '<描述>' --tags '<tag1>,<tag2>' [--keywords '<kw1>,<kw2>'] [--replaces '<路径1>,<路径2>'] [--peer <peerKey>] [--reason '<为什么>'] [--generalized-from <路径>] [--promoted-from <路径>] '<正文>'",
|
|
248
|
+
update: "ec memory update <路径> [--tags '<tag1>,<tag2>'] [--touch] [--reason '<为什么>'](至少给一项)",
|
|
249
|
+
archive: "ec memory archive <路径...> [--reason '<为什么>']",
|
|
246
250
|
};
|
|
247
251
|
/**
|
|
248
252
|
* 统一失败出口。JSON 模式与 `ec fs` / `ec response` 一致:stdout 输出 `{ ok: false, code, error }`;
|
|
@@ -406,6 +410,7 @@ async function cmdMemoryNoteList(args, json) {
|
|
|
406
410
|
async function cmdMemoryCreate(args, json) {
|
|
407
411
|
const { flags, positional } = parseCliArgs(args, [
|
|
408
412
|
'--scope', '--slug', '--desc', '--tags', '--keywords', '--replaces', '--peer',
|
|
413
|
+
'--reason', '--generalized-from', '--promoted-from',
|
|
409
414
|
], json);
|
|
410
415
|
const content = positional.join(' ').trim();
|
|
411
416
|
const scope = flags.get('--scope');
|
|
@@ -415,6 +420,9 @@ async function cmdMemoryCreate(args, json) {
|
|
|
415
420
|
const keywordsRaw = flags.get('--keywords');
|
|
416
421
|
const replacesRaw = flags.get('--replaces');
|
|
417
422
|
const targetPeerKey = flags.get('--peer');
|
|
423
|
+
const reason = flags.get('--reason');
|
|
424
|
+
const generalizedFrom = flags.get('--generalized-from');
|
|
425
|
+
const promotedFrom = flags.get('--promoted-from');
|
|
418
426
|
if (!scope || !['agent', 'relation', 'owner'].includes(scope)) {
|
|
419
427
|
failWithUsage(json, '--scope 必须是 agent / relation / owner 之一', USAGE.create);
|
|
420
428
|
}
|
|
@@ -429,6 +437,12 @@ async function cmdMemoryCreate(args, json) {
|
|
|
429
437
|
if (targetPeerKey !== undefined && scope !== 'relation') {
|
|
430
438
|
failWithUsage(json, '--peer 只在 --scope relation 时有效', USAGE.create);
|
|
431
439
|
}
|
|
440
|
+
if (generalizedFrom !== undefined && scope !== 'agent') {
|
|
441
|
+
failWithUsage(json, '--generalized-from 只在 --scope agent 时有效', USAGE.create);
|
|
442
|
+
}
|
|
443
|
+
if (promotedFrom !== undefined && scope !== 'owner') {
|
|
444
|
+
failWithUsage(json, '--promoted-from 只在 --scope owner 时有效', USAGE.create);
|
|
445
|
+
}
|
|
432
446
|
const { socketPath, ...session } = requireManagedSession('ec memory create', flags, json);
|
|
433
447
|
const result = await queryDaemon(socketPath, {
|
|
434
448
|
type: 'memory.create', argv: ['memory', 'create', ...args], ...session, scope, slug, description: desc,
|
|
@@ -437,6 +451,9 @@ async function cmdMemoryCreate(args, json) {
|
|
|
437
451
|
keywords: parseTags(keywordsRaw),
|
|
438
452
|
replaces: replacesRaw ? replacesRaw.split(',').map(s => s.trim()).filter(Boolean) : undefined,
|
|
439
453
|
targetPeerKey: targetPeerKey ?? undefined,
|
|
454
|
+
reason: reason ?? undefined,
|
|
455
|
+
generalizedFrom: generalizedFrom ?? undefined,
|
|
456
|
+
promotedFrom: promotedFrom ?? undefined,
|
|
440
457
|
}, 15_000);
|
|
441
458
|
reportResult(result, json, (r) => {
|
|
442
459
|
if (r.filePath)
|
|
@@ -447,10 +464,11 @@ async function cmdMemoryCreate(args, json) {
|
|
|
447
464
|
});
|
|
448
465
|
}
|
|
449
466
|
async function cmdMemoryConsolidatedUpdate(args, json) {
|
|
450
|
-
const { flags, positional } = parseCliArgs(args, ['--tags', '--touch'], json);
|
|
467
|
+
const { flags, positional } = parseCliArgs(args, ['--tags', '--touch', '--reason'], json);
|
|
451
468
|
const filePath = positional[0];
|
|
452
469
|
const tagsRaw = flags.get('--tags');
|
|
453
470
|
const touch = flags.has('--touch');
|
|
471
|
+
const reason = flags.get('--reason');
|
|
454
472
|
if (!filePath)
|
|
455
473
|
failWithUsage(json, '必须提供要修改的记忆文件路径', USAGE.update);
|
|
456
474
|
if (tagsRaw === undefined && !touch)
|
|
@@ -458,19 +476,20 @@ async function cmdMemoryConsolidatedUpdate(args, json) {
|
|
|
458
476
|
const { socketPath, ...session } = requireManagedSession('ec memory update', flags, json);
|
|
459
477
|
const result = await queryDaemon(socketPath, {
|
|
460
478
|
type: 'memory.update', argv: ['memory', 'update', ...args], ...session, filePath,
|
|
461
|
-
tags: parseTags(tagsRaw), touch,
|
|
479
|
+
tags: parseTags(tagsRaw), touch, reason: reason ?? undefined,
|
|
462
480
|
}, 15_000);
|
|
463
481
|
reportResult(result, json, () => {
|
|
464
482
|
console.log(`✓ 已更新: ${filePath}`);
|
|
465
483
|
});
|
|
466
484
|
}
|
|
467
485
|
async function cmdMemoryArchive(args, json) {
|
|
468
|
-
const { flags, positional } = parseCliArgs(args, [], json);
|
|
486
|
+
const { flags, positional } = parseCliArgs(args, ['--reason'], json);
|
|
469
487
|
if (positional.length === 0)
|
|
470
488
|
failWithUsage(json, '必须提供至少一个要归档的文件路径', USAGE.archive);
|
|
471
489
|
const { socketPath, ...session } = requireManagedSession('ec memory archive', flags, json);
|
|
472
490
|
const result = await queryDaemon(socketPath, {
|
|
473
491
|
type: 'memory.archive', argv: ['memory', 'archive', ...args], ...session, filePaths: positional,
|
|
492
|
+
reason: flags.get('--reason') ?? undefined,
|
|
474
493
|
}, 15_000);
|
|
475
494
|
reportResult(result, json, (r) => {
|
|
476
495
|
const archived = r.archived ?? positional;
|