evolcore 0.0.21 → 0.0.23
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/AGENTS.md +62 -0
- package/CHANGELOG.md +53 -0
- package/README.md +2 -0
- package/bin/codex-managed-acl.ps1 +155 -0
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +376 -96
- package/dist/agents/baseagent-availability.js +48 -0
- package/dist/agents/baseagent.js +1 -1
- package/dist/agents/claude-runner.js +44 -226
- package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +103 -17
- package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
- package/dist/agents/codex/catalog.js +70 -0
- package/dist/agents/codex/diagnostics.js +154 -0
- package/dist/agents/codex-runner.js +1292 -219
- package/dist/agents/ecagent-runner.js +10 -31
- package/dist/agents/gemini-runner.js +2 -6
- package/dist/aun/aid/store.js +2 -0
- package/dist/aun/menu-client.js +272 -0
- package/dist/aun/msg/group.js +65 -4
- package/dist/aun/msg/index.js +1 -1
- package/dist/aun/msg/managed-operation.js +51 -4
- package/dist/aun/outbox.js +7 -0
- package/dist/aun/rpc/client.js +8 -1
- package/dist/channels/aun.js +240 -36
- package/dist/channels/wechat.js +81 -10
- package/dist/cli/aun-commands.js +48 -4
- package/dist/cli/command-log.js +21 -2
- package/dist/cli/daemon-commands.js +169 -51
- package/dist/cli/index.js +20 -0
- package/dist/cli/init.js +2 -10
- package/dist/cli/menu-client.js +95 -0
- package/dist/cli/model.js +23 -8
- package/dist/cli/task-context.js +16 -2
- package/dist/cli/trigger-command.js +22 -5
- package/dist/config/config-field-policy.js +3 -0
- package/dist/config/config-manager.js +32 -5
- package/dist/config/contact-book-store.js +25 -3
- package/dist/config/schema-migration.js +14 -2
- package/dist/core/auth/agent-delegation.js +15 -0
- package/dist/core/auth/auth-gateway.js +8 -0
- package/dist/core/auth/authorization-audit.js +70 -6
- package/dist/core/auth/operation-catalog.js +4 -0
- package/dist/core/auth/remote-access-store.js +135 -0
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +93 -25
- package/dist/core/command/command-handler.js +62 -5
- package/dist/core/command/daemon-diagnostics.js +448 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +150 -33
- package/dist/core/command/menu-protocol.js +120 -1
- package/dist/core/command/slash-gate.js +3 -3
- package/dist/core/command/slash-handler.js +83 -40
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +32 -0
- package/dist/core/handoff/runtime.js +23 -3
- package/dist/core/message/im-renderer.js +7 -3
- package/dist/core/message/message-bridge.js +60 -2
- package/dist/core/message/message-log.js +33 -0
- package/dist/core/message/message-queue.js +41 -15
- package/dist/core/message/response-engine.js +246 -114
- package/dist/core/model/catalog-service.js +308 -0
- package/dist/core/model/model-catalog.js +124 -334
- package/dist/core/model/model-diagnostics.js +4 -5
- package/dist/core/model/model-fallback.js +9 -16
- package/dist/core/model/model-permission.js +1 -1
- package/dist/core/permission/approval-gateway.js +16 -1
- package/dist/core/permission/ec-command-parser.js +272 -70
- package/dist/core/permission/protected-paths.js +74 -14
- package/dist/core/permission/sandbox-runtime.js +4 -2
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +290 -30
- package/dist/core/runtime-lock.js +13 -8
- package/dist/core/session/session-manager.js +30 -0
- package/dist/core/session/session-renew.js +18 -1
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/index.js +104 -34
- package/dist/ipc.js +218 -96
- package/dist/paths.js +18 -0
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/feedback.js +14 -2
- package/dist/trigger/manager.js +3 -1
- package/dist/trigger/parser.js +86 -13
- package/dist/trigger/patch.js +104 -1
- package/dist/trigger/scheduler.js +20 -3
- package/dist/utils/ecweb-supervisor.js +11 -2
- package/dist/utils/ecweb-utils.js +8 -26
- package/dist/utils/error-utils.js +7 -0
- package/dist/utils/logger.js +9 -4
- package/dist/utils/npm-ops.js +47 -15
- package/dist/utils/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/evolcore/INDEX.md +3 -2
- package/kits/docs/evolcore/agent-create.md +159 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/group-collaboration.md +256 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/menu-client.md +34 -0
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
- package/kits/migrations/migrate-role-config-v4.mjs +11 -5
- package/kits/migrations/migrate-role-config-v5.mjs +8 -2
- package/kits/schemas/_meta.json +3 -2
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/templates/message-fragments/item.md +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +10 -2
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +11 -3
package/dist/ipc.js
CHANGED
|
@@ -8,18 +8,22 @@ import path from 'path';
|
|
|
8
8
|
import { logger } from './utils/logger.js';
|
|
9
9
|
import { withMenuProcessingTime, logMenuRequestCompleted, logMenuRequestReceived } from './core/command/menu-protocol.js';
|
|
10
10
|
import { fileCache } from './core/daemon-file-cache.js';
|
|
11
|
+
import { DAEMON_RUNTIME_EPOCH_ENV } from './cli/task-context.js';
|
|
11
12
|
import { HANDOFF_QUERY_MAX_LIMIT, HANDOFF_STATES } from './core/handoff/types.js';
|
|
12
13
|
import { AgentReloadBusyError } from './core/agent-reload-coordinator.js';
|
|
13
|
-
import { getWindowsInstanceSocketPathCandidates, resolvePaths } from './paths.js';
|
|
14
|
+
import { getWindowsInstanceSocketPathCandidates, normalizeIpcEndpoint, resolvePaths } from './paths.js';
|
|
14
15
|
import { getProcessStartTime, isSameOrOlderProcess } from './utils/process-introspect.js';
|
|
15
16
|
import { classifyProcessTree, ProcessTreeSampler, readProcessMetrics } from './utils/process-tree-stats.js';
|
|
16
17
|
import { readSystemMemoryUsage } from './utils/system-memory.js';
|
|
17
18
|
import { hashCurrentDelegatedCommand } from './core/auth/agent-delegation.js';
|
|
18
19
|
import { normalizeAunMentionEntries } from './aun/msg/mention-schema.js';
|
|
20
|
+
import { remoteAccessService } from './core/auth/remote-access-store.js';
|
|
21
|
+
import { setModelCatalogDaemonOwner } from './core/model/model-catalog.js';
|
|
19
22
|
const isWindows = process.platform === 'win32';
|
|
20
|
-
const isNamedPipe = (p) => isWindows && p.startsWith('\\\\.\\pipe\\');
|
|
23
|
+
const isNamedPipe = (p) => isWindows && p.toLowerCase().startsWith('\\\\.\\pipe\\');
|
|
21
24
|
const CPU_SAMPLE_INTERVAL_MS = 1000;
|
|
22
25
|
const PROCESS_TREE_SAMPLE_INTERVAL_MS = 5000;
|
|
26
|
+
const MONITOR_LEASE_TTL_MS = 15_000;
|
|
23
27
|
const PROCESS_TREE_SAMPLE_WARN_MS = 250;
|
|
24
28
|
const EVENT_LOOP_LAG_WARN_MS = 100;
|
|
25
29
|
const SAMPLE_WARN_LOG_INTERVAL_MS = 60_000;
|
|
@@ -42,8 +46,15 @@ const LOCAL_CONTROL_COMMANDS = new Set([
|
|
|
42
46
|
'evolagent.load',
|
|
43
47
|
'evolagent.resync',
|
|
44
48
|
'menu.exec',
|
|
49
|
+
'model.catalog',
|
|
45
50
|
'monitor-snapshot',
|
|
51
|
+
'monitor-subscribe',
|
|
52
|
+
'monitor-unsubscribe',
|
|
46
53
|
'shutdown',
|
|
54
|
+
'remote-auth.pair-code',
|
|
55
|
+
'remote-auth.issue-local',
|
|
56
|
+
'remote-auth.redeem',
|
|
57
|
+
'remote-auth.validate',
|
|
47
58
|
]);
|
|
48
59
|
let ipcQuerySequence = 0;
|
|
49
60
|
function ipcLogToken(value, fallback = 'unknown') {
|
|
@@ -80,9 +91,9 @@ export function summarizeMonitorAgents(agents, aids) {
|
|
|
80
91
|
};
|
|
81
92
|
}
|
|
82
93
|
export class IpcServer {
|
|
83
|
-
socketPath;
|
|
84
94
|
getStatus;
|
|
85
95
|
commandExecutor;
|
|
96
|
+
socketPath;
|
|
86
97
|
server = null;
|
|
87
98
|
agentRegistry;
|
|
88
99
|
agentApplicationService;
|
|
@@ -122,6 +133,8 @@ export class IpcServer {
|
|
|
122
133
|
wecomContactBindExecutor;
|
|
123
134
|
wechatContactBindExecutor;
|
|
124
135
|
controlToken;
|
|
136
|
+
daemonRuntimeEpoch;
|
|
137
|
+
remoteAccessService = remoteAccessService;
|
|
125
138
|
// CPU 占用追踪:IPC handler 是一次性同步调用,无法在响应里做 200ms 异步采样,
|
|
126
139
|
// 故用后台 1s interval 累积 process.cpuUsage() 增量,handler 直接读最近值。
|
|
127
140
|
// procCpuPercent = 本 daemon 进程占单核的百分比(可 >100% 仅当多核,已 clamp 到 100);
|
|
@@ -138,6 +151,7 @@ export class IpcServer {
|
|
|
138
151
|
processTreeWorkerRequestStartedAt = 0;
|
|
139
152
|
processTreeWorkerRequestTimer = null;
|
|
140
153
|
processTreeSamplingInFlight = false;
|
|
154
|
+
processTreeMonitorLeases = new Map();
|
|
141
155
|
lastCpuTickMono = null;
|
|
142
156
|
eventLoopLagMs = 0;
|
|
143
157
|
maxEventLoopLagMs = 0;
|
|
@@ -155,9 +169,9 @@ export class IpcServer {
|
|
|
155
169
|
lastError: null,
|
|
156
170
|
};
|
|
157
171
|
constructor(socketPath, getStatus, commandExecutor) {
|
|
158
|
-
this.socketPath = socketPath;
|
|
159
172
|
this.getStatus = getStatus;
|
|
160
173
|
this.commandExecutor = commandExecutor;
|
|
174
|
+
this.socketPath = normalizeIpcEndpoint(socketPath);
|
|
161
175
|
}
|
|
162
176
|
/** Inject EvolAgentRegistry for evolagent.* IPC handlers */
|
|
163
177
|
setAgentRegistry(registry) {
|
|
@@ -171,6 +185,14 @@ export class IpcServer {
|
|
|
171
185
|
setControlToken(token) {
|
|
172
186
|
this.controlToken = Buffer.from(token);
|
|
173
187
|
}
|
|
188
|
+
/** Use the daemon-owned remote access service for local consumers such as ECWeb. */
|
|
189
|
+
setRemoteAccessService(service) {
|
|
190
|
+
this.remoteAccessService = service;
|
|
191
|
+
}
|
|
192
|
+
/** Set the process-lifetime marker used to reject stale managed children. */
|
|
193
|
+
setDaemonRuntimeEpoch(epoch) {
|
|
194
|
+
this.daemonRuntimeEpoch = epoch;
|
|
195
|
+
}
|
|
174
196
|
/** Inject menu.* executor (ECWeb Control proxies menu requests through this) */
|
|
175
197
|
setMenuExecutor(executor) {
|
|
176
198
|
this.menuExecutor = executor;
|
|
@@ -223,6 +245,87 @@ export class IpcServer {
|
|
|
223
245
|
setAgentStatsProvider(provider) {
|
|
224
246
|
this.agentStatsProvider = provider;
|
|
225
247
|
}
|
|
248
|
+
/** Single current snapshot shared by ECWeb IPC and the daemon Menu view. */
|
|
249
|
+
getMonitorSnapshot(leaseId) {
|
|
250
|
+
if (typeof leaseId === 'string' && leaseId.trim())
|
|
251
|
+
this.refreshProcessTreeMonitor(leaseId);
|
|
252
|
+
else
|
|
253
|
+
this.sampleProcessTree();
|
|
254
|
+
const mem = process.memoryUsage();
|
|
255
|
+
const systemMemory = readSystemMemoryUsage();
|
|
256
|
+
const aids = this.aunAidProvider ? this.aunAidProvider() : [];
|
|
257
|
+
const aidStats = this.aunAidStatsProvider ? this.aunAidStatsProvider() : [];
|
|
258
|
+
const statsMap = new Map(aidStats.map(stat => [stat.aid, stat]));
|
|
259
|
+
const agentInfos = this.agentRegistry ? this.agentRegistry.list() : [];
|
|
260
|
+
const agentRuntimeStats = this.agentStatsProvider ? this.agentStatsProvider() : [];
|
|
261
|
+
const agentRuntimeStatsMap = new Map(agentRuntimeStats.map(stat => [stat.aid, stat]));
|
|
262
|
+
const aidStateMap = new Map(aids.map(state => [state.agentName || state.aid, state]));
|
|
263
|
+
const processTree = this.processTreeSnapshot;
|
|
264
|
+
const processTreeCpu = processTree?.processCount ? processTree.cpuPercent : this.cpuPercent;
|
|
265
|
+
const processTreeRss = processTree?.processCount ? processTree.rss : mem.rss;
|
|
266
|
+
const processTreeDetails = processTree ? {
|
|
267
|
+
...processTree,
|
|
268
|
+
processes: classifyProcessTree(processTree.processes, process.pid, this.associatedProcessKinds),
|
|
269
|
+
} : {
|
|
270
|
+
rootPid: process.pid,
|
|
271
|
+
rootPids: [process.pid],
|
|
272
|
+
processCount: 1,
|
|
273
|
+
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
274
|
+
rss: mem.rss,
|
|
275
|
+
processes: [{
|
|
276
|
+
pid: process.pid,
|
|
277
|
+
ppid: process.ppid,
|
|
278
|
+
name: 'evolcore',
|
|
279
|
+
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
280
|
+
rss: mem.rss,
|
|
281
|
+
kind: 'daemon',
|
|
282
|
+
}],
|
|
283
|
+
};
|
|
284
|
+
return {
|
|
285
|
+
ok: true,
|
|
286
|
+
snapshot: {
|
|
287
|
+
ts: Date.now(),
|
|
288
|
+
uptimeMs: Math.round(process.uptime() * 1000),
|
|
289
|
+
cpuCount: os.cpus().length,
|
|
290
|
+
memory: {
|
|
291
|
+
rss: processTreeRss,
|
|
292
|
+
heapUsed: mem.heapUsed,
|
|
293
|
+
heapTotal: mem.heapTotal,
|
|
294
|
+
external: mem.external,
|
|
295
|
+
},
|
|
296
|
+
cpuPercent: processTreeCpu,
|
|
297
|
+
mainProcess: {
|
|
298
|
+
pid: process.pid,
|
|
299
|
+
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
300
|
+
memory: mem,
|
|
301
|
+
},
|
|
302
|
+
processTree: processTreeDetails,
|
|
303
|
+
sampling: {
|
|
304
|
+
cpuIntervalMs: CPU_SAMPLE_INTERVAL_MS,
|
|
305
|
+
processTreeIntervalMs: PROCESS_TREE_SAMPLE_INTERVAL_MS,
|
|
306
|
+
eventLoopLagMs: this.eventLoopLagMs,
|
|
307
|
+
maxEventLoopLagMs: this.maxEventLoopLagMs,
|
|
308
|
+
processTree: { ...this.processTreeSampling },
|
|
309
|
+
},
|
|
310
|
+
system: {
|
|
311
|
+
...systemMemory,
|
|
312
|
+
cpuPercent: Math.round(this.sysCpuPercent * 10) / 10,
|
|
313
|
+
loadAvg: os.loadavg(),
|
|
314
|
+
},
|
|
315
|
+
stats: this.statsProvider ? this.statsProvider() : null,
|
|
316
|
+
agentSummary: summarizeMonitorAgents(agentInfos, aids),
|
|
317
|
+
agents: agentInfos.map(agent => ({
|
|
318
|
+
aid: agent.aid,
|
|
319
|
+
agentName: agent.name,
|
|
320
|
+
status: agent.status,
|
|
321
|
+
connectionStatus: aidStateMap.get(agent.aid)?.status ?? (agent.status === 'disabled' ? 'disabled' : 'unavailable'),
|
|
322
|
+
online: agent.status !== 'disabled' && aidStateMap.get(agent.aid)?.status === 'connected',
|
|
323
|
+
stats: statsMap.get(agent.aid) ?? null,
|
|
324
|
+
runtimeStats: agentRuntimeStatsMap.get(agent.aid) ?? null,
|
|
325
|
+
})),
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
226
329
|
/** Inject queue snapshot provider for queue-snapshot IPC handler (query mode) */
|
|
227
330
|
setQueueSnapshotProvider(provider) {
|
|
228
331
|
this.queueSnapshotProvider = provider;
|
|
@@ -296,11 +399,10 @@ export class IpcServer {
|
|
|
296
399
|
setWechatContactBindExecutor(executor) {
|
|
297
400
|
this.wechatContactBindExecutor = executor;
|
|
298
401
|
}
|
|
299
|
-
/** Start the background CPU
|
|
402
|
+
/** Start the lightweight background CPU sampling loop. Call after start(). */
|
|
300
403
|
startCpuTracking() {
|
|
301
404
|
if (this.cpuTimer)
|
|
302
405
|
return;
|
|
303
|
-
this.sampleProcessTree();
|
|
304
406
|
this.lastCpuTickMono = performance.now();
|
|
305
407
|
this.cpuTimer = setInterval(() => {
|
|
306
408
|
const tickMono = performance.now();
|
|
@@ -342,17 +444,30 @@ export class IpcServer {
|
|
|
342
444
|
}, CPU_SAMPLE_INTERVAL_MS);
|
|
343
445
|
// Don't keep the event loop alive for sampling alone.
|
|
344
446
|
this.cpuTimer.unref?.();
|
|
345
|
-
// Process enumeration is substantially more expensive on Windows than
|
|
346
|
-
// process.cpuUsage()/os.cpus(), so keep it at a lower refresh rate.
|
|
347
|
-
this.processTreeTimer = setInterval(() => this.sampleProcessTree(), PROCESS_TREE_SAMPLE_INTERVAL_MS);
|
|
348
|
-
this.processTreeTimer.unref?.();
|
|
349
447
|
}
|
|
350
|
-
/** Stop
|
|
448
|
+
/** Stop all CPU and process-tree sampling loops. */
|
|
351
449
|
stopCpuTracking() {
|
|
352
450
|
if (this.cpuTimer) {
|
|
353
451
|
clearInterval(this.cpuTimer);
|
|
354
452
|
this.cpuTimer = null;
|
|
355
453
|
}
|
|
454
|
+
this.processTreeMonitorLeases.clear();
|
|
455
|
+
this.stopProcessTreeTracking();
|
|
456
|
+
this.lastCpuTickMono = null;
|
|
457
|
+
}
|
|
458
|
+
startProcessTreeTracking() {
|
|
459
|
+
if (this.processTreeTimer)
|
|
460
|
+
return;
|
|
461
|
+
this.sampleProcessTree();
|
|
462
|
+
this.processTreeTimer = setInterval(() => {
|
|
463
|
+
this.pruneExpiredMonitorLeases();
|
|
464
|
+
if (this.processTreeMonitorLeases.size === 0)
|
|
465
|
+
return;
|
|
466
|
+
this.sampleProcessTree();
|
|
467
|
+
}, PROCESS_TREE_SAMPLE_INTERVAL_MS);
|
|
468
|
+
this.processTreeTimer.unref?.();
|
|
469
|
+
}
|
|
470
|
+
stopProcessTreeTracking() {
|
|
356
471
|
if (this.processTreeTimer) {
|
|
357
472
|
clearInterval(this.processTreeTimer);
|
|
358
473
|
this.processTreeTimer = null;
|
|
@@ -372,7 +487,31 @@ export class IpcServer {
|
|
|
372
487
|
this.processTreeWorker = null;
|
|
373
488
|
if (worker)
|
|
374
489
|
void worker.terminate();
|
|
375
|
-
this.
|
|
490
|
+
this.processTreeSnapshot = null;
|
|
491
|
+
this.associatedProcessKinds.clear();
|
|
492
|
+
}
|
|
493
|
+
acquireProcessTreeMonitor(leaseId) {
|
|
494
|
+
this.processTreeMonitorLeases.set(leaseId, Date.now() + MONITOR_LEASE_TTL_MS);
|
|
495
|
+
this.startProcessTreeTracking();
|
|
496
|
+
}
|
|
497
|
+
refreshProcessTreeMonitor(leaseId) {
|
|
498
|
+
if (typeof leaseId !== 'string' || !this.processTreeMonitorLeases.has(leaseId))
|
|
499
|
+
return;
|
|
500
|
+
this.processTreeMonitorLeases.set(leaseId, Date.now() + MONITOR_LEASE_TTL_MS);
|
|
501
|
+
}
|
|
502
|
+
releaseProcessTreeMonitor(leaseId) {
|
|
503
|
+
this.processTreeMonitorLeases.delete(leaseId);
|
|
504
|
+
if (this.processTreeMonitorLeases.size === 0)
|
|
505
|
+
this.stopProcessTreeTracking();
|
|
506
|
+
}
|
|
507
|
+
pruneExpiredMonitorLeases() {
|
|
508
|
+
const now = Date.now();
|
|
509
|
+
for (const [leaseId, expiresAt] of this.processTreeMonitorLeases) {
|
|
510
|
+
if (expiresAt <= now)
|
|
511
|
+
this.processTreeMonitorLeases.delete(leaseId);
|
|
512
|
+
}
|
|
513
|
+
if (this.processTreeMonitorLeases.size === 0)
|
|
514
|
+
this.stopProcessTreeTracking();
|
|
376
515
|
}
|
|
377
516
|
sampleProcessTree() {
|
|
378
517
|
if (isWindows) {
|
|
@@ -577,6 +716,7 @@ export class IpcServer {
|
|
|
577
716
|
return roots;
|
|
578
717
|
}
|
|
579
718
|
start() {
|
|
719
|
+
setModelCatalogDaemonOwner(true);
|
|
580
720
|
if (this.server)
|
|
581
721
|
return Promise.resolve();
|
|
582
722
|
// Remove stale socket file (Unix only — named pipes auto-cleanup on process exit)
|
|
@@ -639,6 +779,7 @@ export class IpcServer {
|
|
|
639
779
|
});
|
|
640
780
|
}
|
|
641
781
|
stop() {
|
|
782
|
+
setModelCatalogDaemonOwner(false);
|
|
642
783
|
if (this.server) {
|
|
643
784
|
this.server.close();
|
|
644
785
|
this.server = null;
|
|
@@ -654,7 +795,31 @@ export class IpcServer {
|
|
|
654
795
|
if (requiresLocalControlToken(cmd) && !this.hasValidControlToken(cmd.controlToken)) {
|
|
655
796
|
return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
|
|
656
797
|
}
|
|
798
|
+
if (typeof cmd.sessionId === 'string' && cmd.sessionId
|
|
799
|
+
&& typeof cmd.delegationToken === 'string' && cmd.delegationToken
|
|
800
|
+
&& this.daemonRuntimeEpoch
|
|
801
|
+
&& cmd.daemonRuntimeEpoch !== this.daemonRuntimeEpoch) {
|
|
802
|
+
logger.warn(`[IPC] stale delegation rejected: session=${ipcLogToken(cmd.sessionId)} type=${ipcLogToken(cmd.type)} cause=daemon_runtime_epoch_mismatch`);
|
|
803
|
+
return {
|
|
804
|
+
ok: false,
|
|
805
|
+
code: 'DAEMON_RESTARTED',
|
|
806
|
+
error: 'The task delegation token belongs to a previous daemon runtime; start a new task authorization and do not retry this command in the current task',
|
|
807
|
+
};
|
|
808
|
+
}
|
|
657
809
|
switch (cmd.type) {
|
|
810
|
+
case 'model.catalog': {
|
|
811
|
+
if ((cmd.self !== undefined && typeof cmd.self !== 'string')
|
|
812
|
+
|| (cmd.baseagent !== undefined && !['claude', 'codex', 'ecagent', 'gemini'].includes(cmd.baseagent))) {
|
|
813
|
+
return { ok: false, error: 'Invalid model catalog scope' };
|
|
814
|
+
}
|
|
815
|
+
try {
|
|
816
|
+
const { getLocalCatalog } = await import('./core/model/model-catalog.js');
|
|
817
|
+
return { ok: true, snapshot: await getLocalCatalog(cmd.self, cmd.baseagent, cmd.refresh === true) };
|
|
818
|
+
}
|
|
819
|
+
catch {
|
|
820
|
+
return { ok: false, error: 'Unable to resolve model catalog configuration' };
|
|
821
|
+
}
|
|
822
|
+
}
|
|
658
823
|
case 'status':
|
|
659
824
|
return this.getStatus();
|
|
660
825
|
case 'codex-pre-tool-use': {
|
|
@@ -670,6 +835,7 @@ export class IpcServer {
|
|
|
670
835
|
threadId: cmd.threadId,
|
|
671
836
|
toolName: cmd.toolName,
|
|
672
837
|
toolInput: cmd.toolInput,
|
|
838
|
+
callId: typeof cmd.callId === 'string' ? cmd.callId : undefined,
|
|
673
839
|
signal: requestSignal,
|
|
674
840
|
});
|
|
675
841
|
}
|
|
@@ -711,6 +877,19 @@ export class IpcServer {
|
|
|
711
877
|
ttlMs: cmd.ttlMs,
|
|
712
878
|
});
|
|
713
879
|
}
|
|
880
|
+
case 'remote-auth.pair-code': {
|
|
881
|
+
const pair = this.remoteAccessService.getPairingCode(typeof cmd.authorizedBy === 'string' ? cmd.authorizedBy : undefined);
|
|
882
|
+
return { ok: true, ...pair };
|
|
883
|
+
}
|
|
884
|
+
case 'remote-auth.issue-local': {
|
|
885
|
+
return this.remoteAccessService.issueLocalToken(cmd.label);
|
|
886
|
+
}
|
|
887
|
+
case 'remote-auth.redeem': {
|
|
888
|
+
return this.remoteAccessService.redeemPairingCode(cmd.code, cmd.label, cmd.authorizedBy);
|
|
889
|
+
}
|
|
890
|
+
case 'remote-auth.validate': {
|
|
891
|
+
return { ok: true, ...this.remoteAccessService.validateToken(cmd.token) };
|
|
892
|
+
}
|
|
714
893
|
case 'bind.status': {
|
|
715
894
|
if (!this.bindExecutor)
|
|
716
895
|
return { ok: false, error: 'bind executor not configured' };
|
|
@@ -1327,85 +1506,21 @@ export class IpcServer {
|
|
|
1327
1506
|
return { ok: false, error: e?.message ?? String(e) };
|
|
1328
1507
|
}
|
|
1329
1508
|
}
|
|
1330
|
-
case 'monitor-snapshot':
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
const
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
const processTreeDetails = processTree ? {
|
|
1346
|
-
...processTree,
|
|
1347
|
-
processes: classifyProcessTree(processTree.processes, process.pid, this.associatedProcessKinds),
|
|
1348
|
-
} : {
|
|
1349
|
-
rootPid: process.pid,
|
|
1350
|
-
rootPids: [process.pid],
|
|
1351
|
-
processCount: 1,
|
|
1352
|
-
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
1353
|
-
rss: mem.rss,
|
|
1354
|
-
processes: [{
|
|
1355
|
-
pid: process.pid,
|
|
1356
|
-
ppid: process.ppid,
|
|
1357
|
-
name: 'evolcore',
|
|
1358
|
-
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
1359
|
-
rss: mem.rss,
|
|
1360
|
-
kind: 'daemon',
|
|
1361
|
-
}],
|
|
1362
|
-
};
|
|
1363
|
-
return {
|
|
1364
|
-
ok: true,
|
|
1365
|
-
snapshot: {
|
|
1366
|
-
ts: Date.now(),
|
|
1367
|
-
uptimeMs: Math.round(process.uptime() * 1000),
|
|
1368
|
-
cpuCount: os.cpus().length,
|
|
1369
|
-
// 进程级:daemon + 子进程 + 登记在册的关联进程树
|
|
1370
|
-
memory: {
|
|
1371
|
-
rss: processTreeRss,
|
|
1372
|
-
heapUsed: mem.heapUsed,
|
|
1373
|
-
heapTotal: mem.heapTotal,
|
|
1374
|
-
external: mem.external,
|
|
1375
|
-
},
|
|
1376
|
-
cpuPercent: processTreeCpu,
|
|
1377
|
-
mainProcess: {
|
|
1378
|
-
pid: process.pid,
|
|
1379
|
-
cpuPercent: Math.round(this.cpuPercent * 10) / 10,
|
|
1380
|
-
memory: mem,
|
|
1381
|
-
},
|
|
1382
|
-
processTree: processTreeDetails,
|
|
1383
|
-
sampling: {
|
|
1384
|
-
cpuIntervalMs: CPU_SAMPLE_INTERVAL_MS,
|
|
1385
|
-
processTreeIntervalMs: PROCESS_TREE_SAMPLE_INTERVAL_MS,
|
|
1386
|
-
eventLoopLagMs: this.eventLoopLagMs,
|
|
1387
|
-
maxEventLoopLagMs: this.maxEventLoopLagMs,
|
|
1388
|
-
processTree: { ...this.processTreeSampling },
|
|
1389
|
-
},
|
|
1390
|
-
// 系统级:整机
|
|
1391
|
-
system: {
|
|
1392
|
-
...systemMemory,
|
|
1393
|
-
cpuPercent: Math.round(this.sysCpuPercent * 10) / 10,
|
|
1394
|
-
loadAvg: os.loadavg(), // [1m, 5m, 15m](Windows 恒 0)
|
|
1395
|
-
},
|
|
1396
|
-
stats: this.statsProvider ? this.statsProvider() : null,
|
|
1397
|
-
agentSummary,
|
|
1398
|
-
agents: agentInfos.map((a) => ({
|
|
1399
|
-
aid: a.aid,
|
|
1400
|
-
agentName: a.name,
|
|
1401
|
-
status: a.status,
|
|
1402
|
-
connectionStatus: aidStateMap.get(a.aid)?.status ?? (a.status === 'disabled' ? 'disabled' : 'unavailable'),
|
|
1403
|
-
online: a.status !== 'disabled' && aidStateMap.get(a.aid)?.status === 'connected',
|
|
1404
|
-
stats: statsMap.get(a.aid) ?? null,
|
|
1405
|
-
runtimeStats: agentRuntimeStatsMap.get(a.aid) ?? null,
|
|
1406
|
-
})),
|
|
1407
|
-
},
|
|
1408
|
-
};
|
|
1509
|
+
case 'monitor-snapshot':
|
|
1510
|
+
return this.getMonitorSnapshot(cmd.leaseId);
|
|
1511
|
+
case 'monitor-subscribe': {
|
|
1512
|
+
const leaseId = typeof cmd.leaseId === 'string' ? cmd.leaseId.trim() : '';
|
|
1513
|
+
if (!leaseId || leaseId.length > 128)
|
|
1514
|
+
return { ok: false, error: 'invalid monitor lease id' };
|
|
1515
|
+
this.acquireProcessTreeMonitor(leaseId);
|
|
1516
|
+
return { ok: true, leaseTtlMs: MONITOR_LEASE_TTL_MS };
|
|
1517
|
+
}
|
|
1518
|
+
case 'monitor-unsubscribe': {
|
|
1519
|
+
const leaseId = typeof cmd.leaseId === 'string' ? cmd.leaseId.trim() : '';
|
|
1520
|
+
if (!leaseId || leaseId.length > 128)
|
|
1521
|
+
return { ok: false, error: 'invalid monitor lease id' };
|
|
1522
|
+
this.releaseProcessTreeMonitor(leaseId);
|
|
1523
|
+
return { ok: true };
|
|
1409
1524
|
}
|
|
1410
1525
|
default:
|
|
1411
1526
|
return { error: `unknown command: ${cmd.type}` };
|
|
@@ -1425,14 +1540,15 @@ export class IpcServer {
|
|
|
1425
1540
|
*/
|
|
1426
1541
|
/** Pass `null` as timeoutMs to wait without a client-side deadline. */
|
|
1427
1542
|
export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
|
|
1428
|
-
|
|
1429
|
-
|
|
1543
|
+
const endpoint = normalizeIpcEndpoint(socketPath);
|
|
1544
|
+
let socketCandidates = [endpoint];
|
|
1545
|
+
if (isNamedPipe(endpoint)) {
|
|
1430
1546
|
const paths = resolvePaths();
|
|
1431
|
-
if (
|
|
1547
|
+
if (endpoint === paths.socket)
|
|
1432
1548
|
socketCandidates = getWindowsInstanceSocketPathCandidates(paths.root);
|
|
1433
1549
|
}
|
|
1434
1550
|
if (socketCandidates.length <= 1)
|
|
1435
|
-
return ipcQueryOnce(
|
|
1551
|
+
return ipcQueryOnce(endpoint, cmd, timeoutMs);
|
|
1436
1552
|
return (async () => {
|
|
1437
1553
|
const startedAt = Date.now();
|
|
1438
1554
|
for (const candidate of socketCandidates) {
|
|
@@ -1464,7 +1580,13 @@ function ipcQueryOnce(socketPath, cmd, timeoutMs = 3000) {
|
|
|
1464
1580
|
&& typeof authenticatedCmd.delegationCommandHash !== 'string'
|
|
1465
1581
|
? hashCurrentDelegatedCommand()
|
|
1466
1582
|
: undefined;
|
|
1467
|
-
const request =
|
|
1583
|
+
const request = {
|
|
1584
|
+
...(delegationCommandHash ? { ...authenticatedCmd, delegationCommandHash } : authenticatedCmd),
|
|
1585
|
+
...(process.env[DAEMON_RUNTIME_EPOCH_ENV]
|
|
1586
|
+
&& typeof authenticatedCmd.delegationToken === 'string'
|
|
1587
|
+
? { daemonRuntimeEpoch: process.env[DAEMON_RUNTIME_EPOCH_ENV] }
|
|
1588
|
+
: {}),
|
|
1589
|
+
};
|
|
1468
1590
|
return new Promise((resolve) => {
|
|
1469
1591
|
const startedAtMono = performance.now();
|
|
1470
1592
|
const queryId = nextIpcQueryId(request);
|
package/dist/paths.js
CHANGED
|
@@ -179,6 +179,24 @@ function windowsSocketPathForRoot(root) {
|
|
|
179
179
|
const hash = crypto.createHash('sha1').update(root).digest('hex').slice(0, 12);
|
|
180
180
|
return `\\\\.\\pipe\\${WINDOWS_PIPE_PREFIX}-${hash}`;
|
|
181
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Normalize an IPC endpoint before it reaches node:net.
|
|
184
|
+
*
|
|
185
|
+
* Windows does not support Unix-domain socket files at ordinary filesystem
|
|
186
|
+
* paths. Callers that construct an ad-hoc `.sock` path (tests and embedded
|
|
187
|
+
* runtimes in particular) must therefore use the same deterministic named
|
|
188
|
+
* pipe on both the server and client sides. Existing named pipes and all
|
|
189
|
+
* non-Windows endpoints are preserved verbatim.
|
|
190
|
+
*/
|
|
191
|
+
export function normalizeIpcEndpoint(endpoint) {
|
|
192
|
+
// Windows object-manager paths are case-insensitive. Preserve an explicit
|
|
193
|
+
// named pipe verbatim even when a caller spells the namespace as `PIPE`.
|
|
194
|
+
if (!isWindows || endpoint.toLowerCase().startsWith('\\\\.\\pipe\\'))
|
|
195
|
+
return endpoint;
|
|
196
|
+
const normalized = path.win32.resolve(endpoint).toLowerCase();
|
|
197
|
+
const hash = crypto.createHash('sha1').update(normalized).digest('hex').slice(0, 20);
|
|
198
|
+
return `\\\\.\\pipe\\${WINDOWS_PIPE_PREFIX}-endpoint-${hash}`;
|
|
199
|
+
}
|
|
182
200
|
/**
|
|
183
201
|
* Return the canonical and pre-normalization Windows daemon pipe names.
|
|
184
202
|
*
|
|
@@ -197,6 +197,7 @@ export const proactiveFlow = {
|
|
|
197
197
|
const queueMsg = queueLen >= 5
|
|
198
198
|
? `⚠️ 有 ${queueLen} 条消息未读,请尽快完成当前任务,或使用 ec ctl queue 读取后同步处理。`
|
|
199
199
|
: `⚠️ 有 ${queueLen} 条消息未读,可使用 ec ctl queue 读取。`;
|
|
200
|
+
ctx.recordReminder?.({ kind: 'queue_unread', text: queueMsg, queueLength: queueLen });
|
|
200
201
|
ctx.injectToModel(queueMsg);
|
|
201
202
|
}
|
|
202
203
|
}
|
|
@@ -208,13 +209,17 @@ export const proactiveFlow = {
|
|
|
208
209
|
state.toolReportPending = true;
|
|
209
210
|
const cmdHint = state.chatType === 'group' ? 'ec group send' : 'ec msg send';
|
|
210
211
|
const target = state.chatType === 'group' ? '群里' : '对方';
|
|
211
|
-
|
|
212
|
+
const reportMsg = `⚠️ 工具调用已达到 ${state.toolCount} 次,请立即用 ${cmdHint} 向${target}汇报当前情况和下一步意图。`;
|
|
213
|
+
ctx.recordReminder?.({ kind: 'tool_report', text: reportMsg, toolCount: state.toolCount });
|
|
214
|
+
ctx.injectToModel(reportMsg);
|
|
212
215
|
}
|
|
213
216
|
},
|
|
214
217
|
// ─── onToolResult:成功发送回执后解除门禁 ───
|
|
215
218
|
onToolResult(ctx) {
|
|
216
219
|
const state = ctx.state.get(STATE_KEY);
|
|
217
|
-
if (!state
|
|
220
|
+
if (!state)
|
|
221
|
+
return;
|
|
222
|
+
if (!ctx.isSendCommand(ctx.toolName, ctx.toolInput))
|
|
218
223
|
return;
|
|
219
224
|
if (!hasSuccessfulSendReceipt(ctx.result, ctx.isError || !!ctx.error))
|
|
220
225
|
return;
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
import { resolvePriceRow, BILLING_FNS } from './billing.js';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
+
/** Keep wire/log metadata readable while preserving full precision in storage. */
|
|
9
|
+
export function roundCostForOutput(value) {
|
|
10
|
+
return Number.isFinite(value) ? Number(value.toFixed(4)) : value;
|
|
11
|
+
}
|
|
8
12
|
// 网关价格表缓存(从用户覆盖层 model-prices-gateway.jsonl 读取)
|
|
9
13
|
let _gatewayPriceCache = null;
|
|
10
14
|
let _gatewayPriceCacheTs = 0;
|
package/dist/trigger/feedback.js
CHANGED
|
@@ -251,12 +251,24 @@ export class TriggerFeedbackDispatcher {
|
|
|
251
251
|
};
|
|
252
252
|
}
|
|
253
253
|
if (event?.type === 'trigger:skipped') {
|
|
254
|
+
const interruption = event.executionState === 'interrupted' && event.decisionSource === 'infrastructure'
|
|
255
|
+
? {
|
|
256
|
+
reasonCode: event.reasonCode ?? event.reason,
|
|
257
|
+
decisionSource: event.decisionSource,
|
|
258
|
+
executionState: event.executionState,
|
|
259
|
+
...(event.generation !== undefined ? { generation: event.generation } : {}),
|
|
260
|
+
...(event.currentGeneration !== undefined ? { currentGeneration: event.currentGeneration } : {}),
|
|
261
|
+
}
|
|
262
|
+
: undefined;
|
|
254
263
|
return {
|
|
255
|
-
status: 'failed',
|
|
264
|
+
status: interruption ? 'skipped' : 'failed',
|
|
256
265
|
reason: event.reason,
|
|
266
|
+
...(interruption ? { interruption } : {}),
|
|
257
267
|
feedback,
|
|
258
268
|
effects: [this.inboundEffect('success', target, session.id, startedAt)],
|
|
259
|
-
error:
|
|
269
|
+
error: interruption
|
|
270
|
+
? null
|
|
271
|
+
: { code: event.reason || 'target_session_skipped', message: event.reason || 'target session skipped' },
|
|
260
272
|
};
|
|
261
273
|
}
|
|
262
274
|
return {
|
package/dist/trigger/manager.js
CHANGED
|
@@ -92,7 +92,9 @@ export class TriggerDefinitionManager {
|
|
|
92
92
|
assertExplicitAunDeliveryRoutes(updated);
|
|
93
93
|
this.assertAgent(updated);
|
|
94
94
|
if (existing.execution.baseagent !== updated.execution.baseagent) {
|
|
95
|
-
|
|
95
|
+
if (existing.execution.type !== 'trigger_session' || updated.execution.type !== 'trigger_session') {
|
|
96
|
+
throw new Error('execution.baseagent is only mutable between trigger_session Triggers');
|
|
97
|
+
}
|
|
96
98
|
}
|
|
97
99
|
updated.createdAt = existing.createdAt;
|
|
98
100
|
updated.updatedAt = Date.now();
|