evolcore 0.0.11 → 0.0.12
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 +22 -0
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/claude-runner.js +35 -2
- package/dist/agents/codex-app-server-client.js +120 -14
- package/dist/agents/codex-runner.js +273 -35
- package/dist/agents/ecagent-runner.js +36 -4
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/channels/aun.js +288 -108
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +56 -8
- package/dist/cli/agent.js +170 -6
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +31 -31
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +30 -2
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +4 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +30 -7
- package/dist/core/auth/operation-catalog.js +187 -25
- package/dist/core/bootstrap-messages.js +18 -0
- package/dist/core/bootstrap-service.js +63 -4
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +1 -1
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +366 -0
- package/dist/core/command/menu-handler.js +147 -35
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +41 -21
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent.js +4 -2
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +236 -78
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +1 -0
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/tool-policy.js +73 -30
- package/dist/core/protected-paths.js +7 -2
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +384 -98
- package/dist/ipc.js +218 -9
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +22 -0
- package/kits/templates/roles/member.json +27 -0
- package/kits/templates/roles/visitor.json +33 -3
- package/kits/templates/system-fragments/bootstrap.md +1 -1
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +2 -2
- package/dist/core/command/evol-menu-version-gate.js +0 -39
package/dist/ipc.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import net from 'net';
|
|
2
2
|
import fs from 'fs';
|
|
3
|
+
import crypto from 'crypto';
|
|
3
4
|
import os from 'os';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import { logger } from './utils/logger.js';
|
|
@@ -11,8 +12,26 @@ import { getProcessStartTime, isSameOrOlderProcess } from './utils/process-intro
|
|
|
11
12
|
import { classifyProcessTree, ProcessTreeSampler } from './utils/process-tree-stats.js';
|
|
12
13
|
import { readSystemMemoryUsage } from './utils/system-memory.js';
|
|
13
14
|
import { hashCurrentDelegatedCommand } from './core/auth/agent-delegation.js';
|
|
15
|
+
import { normalizeAunMentionEntries } from './aun/msg/mention-schema.js';
|
|
14
16
|
const isWindows = process.platform === 'win32';
|
|
15
17
|
const isNamedPipe = (p) => isWindows && p.startsWith('\\\\.\\pipe\\');
|
|
18
|
+
const LOCAL_CONTROL_COMMANDS = new Set([
|
|
19
|
+
'bind.begin',
|
|
20
|
+
'bind.status',
|
|
21
|
+
'bind.cancel',
|
|
22
|
+
'dingtalk.contact-bind.register',
|
|
23
|
+
'feishu.contact-bind.register',
|
|
24
|
+
'qqbot.contact-bind.register',
|
|
25
|
+
'wecom.contact-bind.register',
|
|
26
|
+
'wechat.contact-bind.register',
|
|
27
|
+
'queue-snapshot',
|
|
28
|
+
'aun-aid-stats-record-outbound',
|
|
29
|
+
'evolagent.reload',
|
|
30
|
+
'evolagent.load',
|
|
31
|
+
'evolagent.resync',
|
|
32
|
+
'menu.exec',
|
|
33
|
+
'monitor-snapshot',
|
|
34
|
+
]);
|
|
16
35
|
export function summarizeMonitorAgents(agents, aids) {
|
|
17
36
|
const connectedAgents = new Set(aids.filter(aid => aid.status === 'connected').map(aid => aid.agentName || aid.aid));
|
|
18
37
|
const enabledAgents = agents.filter(agent => agent.status !== 'disabled');
|
|
@@ -39,10 +58,14 @@ export class IpcServer {
|
|
|
39
58
|
agentStatsProvider;
|
|
40
59
|
queueSnapshotProvider;
|
|
41
60
|
queueActionExecutor;
|
|
61
|
+
queueOperationExecutor;
|
|
62
|
+
statusAuthorizationExecutor;
|
|
63
|
+
storageAuthorizationExecutor;
|
|
42
64
|
triggerExecutor;
|
|
43
65
|
bootstrapCompleteExecutor;
|
|
44
66
|
agentReloadExecutor;
|
|
45
67
|
taskRuntimeContextProvider;
|
|
68
|
+
codexPreToolUseExecutor;
|
|
46
69
|
aunMsgSender;
|
|
47
70
|
handoffReturnExecutor;
|
|
48
71
|
handoffStatusExecutor;
|
|
@@ -51,11 +74,16 @@ export class IpcServer {
|
|
|
51
74
|
bindExecutor;
|
|
52
75
|
configOperationExecutor;
|
|
53
76
|
contactOperationExecutor;
|
|
77
|
+
aidOperationExecutor;
|
|
78
|
+
groupOperationExecutor;
|
|
79
|
+
msgOperationExecutor;
|
|
80
|
+
fsOperationExecutor;
|
|
54
81
|
dingtalkContactBindExecutor;
|
|
55
82
|
feishuContactBindExecutor;
|
|
56
83
|
qqbotContactBindExecutor;
|
|
57
84
|
wecomContactBindExecutor;
|
|
58
85
|
wechatContactBindExecutor;
|
|
86
|
+
controlToken;
|
|
59
87
|
// CPU 占用追踪:IPC handler 是一次性同步调用,无法在响应里做 200ms 异步采样,
|
|
60
88
|
// 故用后台 1s interval 累积 process.cpuUsage() 增量,handler 直接读最近值。
|
|
61
89
|
// procCpuPercent = 本 daemon 进程占单核的百分比(可 >100% 仅当多核,已 clamp 到 100);
|
|
@@ -78,6 +106,10 @@ export class IpcServer {
|
|
|
78
106
|
setAgentRegistry(registry) {
|
|
79
107
|
this.agentRegistry = registry;
|
|
80
108
|
}
|
|
109
|
+
/** Authenticate local control-plane callers that cannot use task delegation. */
|
|
110
|
+
setControlToken(token) {
|
|
111
|
+
this.controlToken = Buffer.from(token);
|
|
112
|
+
}
|
|
81
113
|
/** Inject menu.* executor (ECWeb Control proxies menu requests through this) */
|
|
82
114
|
setMenuExecutor(executor) {
|
|
83
115
|
this.menuExecutor = executor;
|
|
@@ -88,6 +120,18 @@ export class IpcServer {
|
|
|
88
120
|
setContactOperationExecutor(executor) {
|
|
89
121
|
this.contactOperationExecutor = executor;
|
|
90
122
|
}
|
|
123
|
+
setAidOperationExecutor(executor) {
|
|
124
|
+
this.aidOperationExecutor = executor;
|
|
125
|
+
}
|
|
126
|
+
setGroupOperationExecutor(executor) {
|
|
127
|
+
this.groupOperationExecutor = executor;
|
|
128
|
+
}
|
|
129
|
+
setMsgOperationExecutor(executor) {
|
|
130
|
+
this.msgOperationExecutor = executor;
|
|
131
|
+
}
|
|
132
|
+
setFsOperationExecutor(executor) {
|
|
133
|
+
this.fsOperationExecutor = executor;
|
|
134
|
+
}
|
|
91
135
|
setAgentReloadExecutor(executor) {
|
|
92
136
|
this.agentReloadExecutor = executor;
|
|
93
137
|
}
|
|
@@ -119,6 +163,15 @@ export class IpcServer {
|
|
|
119
163
|
setQueueActionExecutor(executor) {
|
|
120
164
|
this.queueActionExecutor = executor;
|
|
121
165
|
}
|
|
166
|
+
setQueueOperationExecutor(executor) {
|
|
167
|
+
this.queueOperationExecutor = executor;
|
|
168
|
+
}
|
|
169
|
+
setStatusAuthorizationExecutor(executor) {
|
|
170
|
+
this.statusAuthorizationExecutor = executor;
|
|
171
|
+
}
|
|
172
|
+
setStorageAuthorizationExecutor(executor) {
|
|
173
|
+
this.storageAuthorizationExecutor = executor;
|
|
174
|
+
}
|
|
122
175
|
/** Inject daemon-level trigger executor for ec trigger. */
|
|
123
176
|
setTriggerExecutor(executor) {
|
|
124
177
|
this.triggerExecutor = executor;
|
|
@@ -131,6 +184,9 @@ export class IpcServer {
|
|
|
131
184
|
setTaskRuntimeContextProvider(provider) {
|
|
132
185
|
this.taskRuntimeContextProvider = provider;
|
|
133
186
|
}
|
|
187
|
+
setCodexPreToolUseExecutor(executor) {
|
|
188
|
+
this.codexPreToolUseExecutor = executor;
|
|
189
|
+
}
|
|
134
190
|
/** Inject daemon-backed AUN private message sender for in-task `ec msg send`. */
|
|
135
191
|
setAunMsgSender(sender) {
|
|
136
192
|
this.aunMsgSender = sender;
|
|
@@ -323,9 +379,31 @@ export class IpcServer {
|
|
|
323
379
|
}
|
|
324
380
|
}
|
|
325
381
|
async handleCommand(cmd) {
|
|
382
|
+
if (LOCAL_CONTROL_COMMANDS.has(cmd.type) && !this.hasValidControlToken(cmd.controlToken)) {
|
|
383
|
+
return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
|
|
384
|
+
}
|
|
326
385
|
switch (cmd.type) {
|
|
327
386
|
case 'status':
|
|
328
387
|
return this.getStatus();
|
|
388
|
+
case 'codex-pre-tool-use': {
|
|
389
|
+
if (!this.codexPreToolUseExecutor) {
|
|
390
|
+
return { ok: false, applicable: true, decision: 'deny', reason: 'EvolCore Codex policy executor is unavailable' };
|
|
391
|
+
}
|
|
392
|
+
if (typeof cmd.threadId !== 'string' || !cmd.threadId
|
|
393
|
+
|| typeof cmd.toolName !== 'string' || !cmd.toolName) {
|
|
394
|
+
return { ok: false, applicable: true, decision: 'deny', reason: 'invalid Codex hook request' };
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
return await this.codexPreToolUseExecutor({
|
|
398
|
+
threadId: cmd.threadId,
|
|
399
|
+
toolName: cmd.toolName,
|
|
400
|
+
toolInput: cmd.toolInput,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
return { ok: false, applicable: true, decision: 'deny', reason: `EvolCore policy unavailable: ${error instanceof Error ? error.message : String(error)}` };
|
|
405
|
+
}
|
|
406
|
+
}
|
|
329
407
|
case 'ping':
|
|
330
408
|
return { pong: true, pid: process.pid, protocolVersion: 1 };
|
|
331
409
|
case 'bind.begin': {
|
|
@@ -454,6 +532,53 @@ export class IpcServer {
|
|
|
454
532
|
sessionKey: cmd.sessionKey,
|
|
455
533
|
});
|
|
456
534
|
}
|
|
535
|
+
case 'queue.op': {
|
|
536
|
+
if (!this.queueOperationExecutor)
|
|
537
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'queue.op not configured' };
|
|
538
|
+
if (typeof cmd.agent !== 'string' || !cmd.agent || typeof cmd.sessionId !== 'string' || !cmd.sessionId) {
|
|
539
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing agent or sessionId' };
|
|
540
|
+
}
|
|
541
|
+
if (cmd.action !== undefined && !['clear', 'cancel', 'interrupt'].includes(cmd.action)) {
|
|
542
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'invalid queue action' };
|
|
543
|
+
}
|
|
544
|
+
return await this.queueOperationExecutor({
|
|
545
|
+
agent: cmd.agent,
|
|
546
|
+
action: cmd.action,
|
|
547
|
+
messageId: typeof cmd.messageId === 'string' ? cmd.messageId : undefined,
|
|
548
|
+
sessionKey: typeof cmd.sessionKey === 'string' ? cmd.sessionKey : undefined,
|
|
549
|
+
sessionId: cmd.sessionId,
|
|
550
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
551
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
case 'status.auth': {
|
|
555
|
+
if (!this.statusAuthorizationExecutor)
|
|
556
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'status authorization not configured' };
|
|
557
|
+
if (typeof cmd.sessionId !== 'string' || !cmd.sessionId)
|
|
558
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId' };
|
|
559
|
+
return await this.statusAuthorizationExecutor({
|
|
560
|
+
sessionId: cmd.sessionId,
|
|
561
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
562
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
case 'storage.auth': {
|
|
566
|
+
if (!this.storageAuthorizationExecutor)
|
|
567
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'storage authorization not configured' };
|
|
568
|
+
if (typeof cmd.sessionId !== 'string' || !cmd.sessionId || typeof cmd.aid !== 'string' || !cmd.aid || typeof cmd.operation !== 'string') {
|
|
569
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId, aid, or operation' };
|
|
570
|
+
}
|
|
571
|
+
if (!['storage.list', 'storage.quota', 'storage.upload', 'storage.download', 'storage.delete'].includes(cmd.operation)) {
|
|
572
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'invalid storage operation' };
|
|
573
|
+
}
|
|
574
|
+
return await this.storageAuthorizationExecutor({
|
|
575
|
+
sessionId: cmd.sessionId,
|
|
576
|
+
aid: cmd.aid,
|
|
577
|
+
operation: cmd.operation,
|
|
578
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
579
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
457
582
|
case 'aun-aid-stats-record-outbound': {
|
|
458
583
|
if (!this.aunAidStatsRecorder)
|
|
459
584
|
return { ok: false, error: 'recorder not configured' };
|
|
@@ -519,14 +644,17 @@ export class IpcServer {
|
|
|
519
644
|
}
|
|
520
645
|
: undefined;
|
|
521
646
|
try {
|
|
647
|
+
const normalizedPayload = payload && Object.prototype.hasOwnProperty.call(payload, 'mentions')
|
|
648
|
+
? { ...payload, mentions: normalizeAunMentionEntries(payload.mentions) }
|
|
649
|
+
: payload;
|
|
522
650
|
return await this.aunMsgSender({
|
|
523
651
|
aid: cmd.aid,
|
|
524
652
|
to: cmd.to,
|
|
525
653
|
scope: cmd.scope === 'group' ? 'group' : 'msg',
|
|
526
|
-
payload,
|
|
654
|
+
payload: normalizedPayload,
|
|
527
655
|
file,
|
|
528
|
-
mentions:
|
|
529
|
-
? cmd.mentions
|
|
656
|
+
mentions: Object.prototype.hasOwnProperty.call(cmd, 'mentions')
|
|
657
|
+
? normalizeAunMentionEntries(cmd.mentions)
|
|
530
658
|
: undefined,
|
|
531
659
|
encrypt: typeof cmd.encrypt === 'boolean' ? cmd.encrypt : undefined,
|
|
532
660
|
thread: typeof cmd.thread === 'string' ? cmd.thread : undefined,
|
|
@@ -539,7 +667,7 @@ export class IpcServer {
|
|
|
539
667
|
});
|
|
540
668
|
}
|
|
541
669
|
catch (e) {
|
|
542
|
-
return { ok: false, error: e?.message || String(e) };
|
|
670
|
+
return { ok: false, error: e?.message || String(e), code: e?.code };
|
|
543
671
|
}
|
|
544
672
|
}
|
|
545
673
|
case 'handoff-return': {
|
|
@@ -568,6 +696,8 @@ export class IpcServer {
|
|
|
568
696
|
return this.handoffStatusExecutor({
|
|
569
697
|
sessionId: typeof cmd.sessionId === 'string' ? cmd.sessionId : undefined,
|
|
570
698
|
handoffId: cmd.handoffId,
|
|
699
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
700
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
571
701
|
});
|
|
572
702
|
}
|
|
573
703
|
case 'handoff-list': {
|
|
@@ -587,6 +717,8 @@ export class IpcServer {
|
|
|
587
717
|
state: state,
|
|
588
718
|
filterSessionId: typeof cmd.filterSessionId === 'string' ? cmd.filterSessionId : undefined,
|
|
589
719
|
limit,
|
|
720
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
721
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
590
722
|
});
|
|
591
723
|
}
|
|
592
724
|
case 'handoff-trace': {
|
|
@@ -604,6 +736,8 @@ export class IpcServer {
|
|
|
604
736
|
agent: typeof cmd.agent === 'string' ? cmd.agent : undefined,
|
|
605
737
|
handoffId: cmd.handoffId,
|
|
606
738
|
limit,
|
|
739
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
740
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
607
741
|
});
|
|
608
742
|
}
|
|
609
743
|
case 'ctl': {
|
|
@@ -638,6 +772,54 @@ export class IpcServer {
|
|
|
638
772
|
}
|
|
639
773
|
return await this.contactOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
640
774
|
}
|
|
775
|
+
case 'aid.op': {
|
|
776
|
+
if (!this.aidOperationExecutor)
|
|
777
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'aid.op not configured' };
|
|
778
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
779
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
780
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
781
|
+
}
|
|
782
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
783
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
784
|
+
}
|
|
785
|
+
return await this.aidOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
786
|
+
}
|
|
787
|
+
case 'group.op': {
|
|
788
|
+
if (!this.groupOperationExecutor)
|
|
789
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'group.op not configured' };
|
|
790
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
791
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
792
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
793
|
+
}
|
|
794
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
795
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
796
|
+
}
|
|
797
|
+
return await this.groupOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
798
|
+
}
|
|
799
|
+
case 'msg.op': {
|
|
800
|
+
if (!this.msgOperationExecutor)
|
|
801
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'msg.op not configured' };
|
|
802
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
803
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
804
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
805
|
+
}
|
|
806
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
807
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
808
|
+
}
|
|
809
|
+
return await this.msgOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
810
|
+
}
|
|
811
|
+
case 'fs.op': {
|
|
812
|
+
if (!this.fsOperationExecutor)
|
|
813
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'fs.op not configured' };
|
|
814
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
815
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
816
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
817
|
+
}
|
|
818
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
819
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
820
|
+
}
|
|
821
|
+
return await this.fsOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
822
|
+
}
|
|
641
823
|
case 'trigger.list':
|
|
642
824
|
case 'trigger.show':
|
|
643
825
|
case 'trigger.history':
|
|
@@ -684,7 +866,12 @@ export class IpcServer {
|
|
|
684
866
|
if (!this.bootstrapCompleteExecutor)
|
|
685
867
|
return { ok: false, error: 'bootstrap complete executor not configured' };
|
|
686
868
|
try {
|
|
687
|
-
return await this.bootstrapCompleteExecutor(aid
|
|
869
|
+
return await this.bootstrapCompleteExecutor(aid, {
|
|
870
|
+
sessionId: typeof cmd.sessionId === 'string' ? cmd.sessionId : undefined,
|
|
871
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
872
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
873
|
+
controlToken: typeof cmd.controlToken === 'string' ? cmd.controlToken : undefined,
|
|
874
|
+
});
|
|
688
875
|
}
|
|
689
876
|
catch (e) {
|
|
690
877
|
return { ok: false, error: e?.message || String(e) };
|
|
@@ -747,7 +934,11 @@ export class IpcServer {
|
|
|
747
934
|
if (!this.menuExecutor)
|
|
748
935
|
return { ok: false, error: 'menu.exec not configured' };
|
|
749
936
|
try {
|
|
750
|
-
const response = await this.menuExecutor(cmd.payload,
|
|
937
|
+
const response = await this.menuExecutor(cmd.payload, {
|
|
938
|
+
source: 'ecweb',
|
|
939
|
+
actorAid: null,
|
|
940
|
+
localDirect: true,
|
|
941
|
+
});
|
|
751
942
|
return { ok: true, response };
|
|
752
943
|
}
|
|
753
944
|
catch (e) {
|
|
@@ -831,17 +1022,35 @@ export class IpcServer {
|
|
|
831
1022
|
return { error: `unknown command: ${cmd.type}` };
|
|
832
1023
|
}
|
|
833
1024
|
}
|
|
1025
|
+
hasValidControlToken(value) {
|
|
1026
|
+
if (!this.controlToken || typeof value !== 'string')
|
|
1027
|
+
return false;
|
|
1028
|
+
const provided = Buffer.from(value);
|
|
1029
|
+
return provided.length === this.controlToken.length
|
|
1030
|
+
&& crypto.timingSafeEqual(provided, this.controlToken);
|
|
1031
|
+
}
|
|
834
1032
|
}
|
|
835
1033
|
/**
|
|
836
1034
|
* Query the running EvolCore daemon via Unix socket.
|
|
837
1035
|
* Returns null if the service is not running or the socket is unreachable.
|
|
838
1036
|
*/
|
|
839
1037
|
export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
|
|
840
|
-
|
|
841
|
-
|
|
1038
|
+
let authenticatedCmd = cmd;
|
|
1039
|
+
if (!process.env.EVOLCORE_SESSION_ID
|
|
1040
|
+
&& LOCAL_CONTROL_COMMANDS.has(cmd.type)
|
|
1041
|
+
&& typeof cmd.controlToken !== 'string') {
|
|
1042
|
+
try {
|
|
1043
|
+
const controlToken = fs.readFileSync(resolvePaths().controlToken, 'utf8').trim();
|
|
1044
|
+
if (controlToken)
|
|
1045
|
+
authenticatedCmd = { ...cmd, controlToken };
|
|
1046
|
+
}
|
|
1047
|
+
catch { }
|
|
1048
|
+
}
|
|
1049
|
+
const delegationCommandHash = typeof authenticatedCmd.delegationToken === 'string'
|
|
1050
|
+
&& typeof authenticatedCmd.delegationCommandHash !== 'string'
|
|
842
1051
|
? hashCurrentDelegatedCommand()
|
|
843
1052
|
: undefined;
|
|
844
|
-
const request = delegationCommandHash ? { ...
|
|
1053
|
+
const request = delegationCommandHash ? { ...authenticatedCmd, delegationCommandHash } : authenticatedCmd;
|
|
845
1054
|
return new Promise((resolve) => {
|
|
846
1055
|
const conn = net.connect(socketPath);
|
|
847
1056
|
let buf = '';
|
|
@@ -41,7 +41,8 @@ export class ResponseModeCoordinator {
|
|
|
41
41
|
const { mode, modeConfig } = resolved;
|
|
42
42
|
contextDeps.logger.debug('[ResponseSystem] resolveMode mode=' + mode.id + ' source=' + resolved.source + ' chatmode=' + (modeConfig.chatmode ?? 'none'));
|
|
43
43
|
const context = this.contextBuilder.build(mode.id, { ...contextDeps, modeConfig });
|
|
44
|
-
|
|
44
|
+
const boundMode = mode.bind?.(context) ?? mode;
|
|
45
|
+
return { mode: boundMode, context, source: resolved.source };
|
|
45
46
|
}
|
|
46
47
|
catch (e) {
|
|
47
48
|
contextDeps.logger.warn(`[Coordinator] resolveMode failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -64,9 +65,10 @@ export class ResponseModeCoordinator {
|
|
|
64
65
|
...deps.contextDeps,
|
|
65
66
|
modeConfig,
|
|
66
67
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const boundMode = mode.bind?.(context) ?? mode;
|
|
69
|
+
await boundMode.initialize(context);
|
|
70
|
+
const decision = await boundMode.handleInbound(message);
|
|
71
|
+
return { modeId: boundMode.id, decision, mode: boundMode, context };
|
|
70
72
|
}
|
|
71
73
|
catch (e) {
|
|
72
74
|
deps.contextDeps.logger.warn(`[Coordinator] resolveInbound failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -65,29 +65,37 @@ export const proactiveFlow = {
|
|
|
65
65
|
const state = buildState(ctx.message, ctx.modeConfig ?? {});
|
|
66
66
|
ctx.state.set(STATE_KEY, state);
|
|
67
67
|
},
|
|
68
|
-
// ─── configureRun:policyHook
|
|
68
|
+
// ─── configureRun:policyHook 只判定,不推进状态 ───
|
|
69
69
|
configureRun(ctx) {
|
|
70
70
|
const state = ctx.state.get(STATE_KEY);
|
|
71
|
-
if (!state
|
|
71
|
+
if (!state)
|
|
72
72
|
return undefined;
|
|
73
73
|
return {
|
|
74
74
|
policyHook: (toolName, toolInput) => {
|
|
75
|
+
// Codex only invokes this hook for local tool paths. Hosted tools stay
|
|
76
|
+
// enabled and do not satisfy or clear the pending first-send gate.
|
|
75
77
|
const isSendCommand = ctx.isSendCommand(toolName, toolInput);
|
|
76
78
|
if (state.firstSendRequired && !state.firstToolDone) {
|
|
77
79
|
if (!isSendCommand) {
|
|
78
80
|
const cmdHint = state.chatType === 'group' ? 'ec group send' : 'ec msg send';
|
|
79
81
|
const target = state.chatType === 'group' ? '群里' : '对方';
|
|
80
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
block: true,
|
|
84
|
+
policyCode: 'proactive_first_send_required',
|
|
85
|
+
reason: `请先用 ${cmdHint} 向${target}说明你的意图,再执行其他工具`,
|
|
86
|
+
};
|
|
81
87
|
}
|
|
82
|
-
state.firstToolDone = true;
|
|
83
88
|
}
|
|
84
89
|
if (state.toolReportPending) {
|
|
85
90
|
if (!isSendCommand) {
|
|
86
91
|
const cmdHint = state.chatType === 'group' ? 'ec group send' : 'ec msg send';
|
|
87
92
|
const target = state.chatType === 'group' ? '群里' : '对方';
|
|
88
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
block: true,
|
|
95
|
+
policyCode: 'proactive_tool_report_required',
|
|
96
|
+
reason: `工具调用已达到 ${state.toolCount} 次,请先用 ${cmdHint} 向${target}汇报当前情况和下一步意图,再执行其他工具`,
|
|
97
|
+
};
|
|
89
98
|
}
|
|
90
|
-
state.toolReportPending = false;
|
|
91
99
|
}
|
|
92
100
|
return undefined;
|
|
93
101
|
},
|
|
@@ -96,7 +104,18 @@ export const proactiveFlow = {
|
|
|
96
104
|
// ─── onToolUse:队列未读提醒 + 工具汇报提醒 ───
|
|
97
105
|
onToolUse(ctx) {
|
|
98
106
|
const state = ctx.state.get(STATE_KEY);
|
|
99
|
-
if (!state
|
|
107
|
+
if (!state)
|
|
108
|
+
return;
|
|
109
|
+
// PreToolUse 可能被工具子进程直接调用,因此审批阶段不能推进门禁状态。
|
|
110
|
+
// 只有 runner 发布真实工具事件后,精确匹配的发送命令才提交状态。
|
|
111
|
+
const isAllowedTool = ctx.isSendCommand(ctx.toolName, ctx.toolInput);
|
|
112
|
+
if (isAllowedTool) {
|
|
113
|
+
if (state.firstSendRequired && !state.firstToolDone)
|
|
114
|
+
state.firstToolDone = true;
|
|
115
|
+
if (state.toolReportPending)
|
|
116
|
+
state.toolReportPending = false;
|
|
117
|
+
}
|
|
118
|
+
if (!state.toolUseReminder)
|
|
100
119
|
return;
|
|
101
120
|
// 队列未读变化提醒
|
|
102
121
|
const queueLen = ctx.getQueueLength();
|
|
@@ -110,7 +129,6 @@ export const proactiveFlow = {
|
|
|
110
129
|
}
|
|
111
130
|
}
|
|
112
131
|
// 工具计数(排除表态命令白名单)
|
|
113
|
-
const isAllowedTool = ctx.isSendCommand(ctx.toolName, ctx.toolInput);
|
|
114
132
|
if (state.toolReportRequired && !isAllowedTool)
|
|
115
133
|
state.toolCount++;
|
|
116
134
|
// 每 N 次工具调用警告
|
|
@@ -19,6 +19,7 @@ import { loadSchema } from '../../../config/schema-registry.js';
|
|
|
19
19
|
* 解析后注入,见 implementation-plan.md §三;无 auto 值)。
|
|
20
20
|
*/
|
|
21
21
|
export class SingleSessionMode {
|
|
22
|
+
context;
|
|
22
23
|
id = 'single-session';
|
|
23
24
|
displayName = '单会话模式';
|
|
24
25
|
description = '单会话直接响应,按 chatMode 决定投递方式。';
|
|
@@ -26,8 +27,20 @@ export class SingleSessionMode {
|
|
|
26
27
|
applicableScenes = ['private', 'group'];
|
|
27
28
|
get configSchema() { return loadSchema('single-session').raw; }
|
|
28
29
|
queue = new FIFOQueue();
|
|
29
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Registry 中的实例只承担模式定义的职责。每个实际会话都由 bind() 创建
|
|
32
|
+
* 独立实例,以免并发会话覆盖彼此的 modeConfig 和流程上下文。
|
|
33
|
+
*/
|
|
34
|
+
constructor(context) {
|
|
35
|
+
this.context = context;
|
|
36
|
+
}
|
|
37
|
+
bind(context) {
|
|
38
|
+
return new SingleSessionMode(context);
|
|
39
|
+
}
|
|
30
40
|
async initialize(context) {
|
|
41
|
+
if (this.context && this.context !== context) {
|
|
42
|
+
throw new Error('[single-session] bound instance initialized with a different context');
|
|
43
|
+
}
|
|
31
44
|
this.context = context;
|
|
32
45
|
context.logger.debug('[ResponseSystem] single-session initialized chatMode=' + this.chatMode());
|
|
33
46
|
}
|
|
@@ -12,12 +12,13 @@ export class ResponseModeSelector {
|
|
|
12
12
|
if (!mode) {
|
|
13
13
|
throw new Error(`[ResponseModeSelector] mode not found: ${modeId}`);
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const boundMode = mode.bind?.(context) ?? mode;
|
|
16
|
+
await boundMode.initialize(context);
|
|
17
|
+
context.logger.debug('[ResponseSystem] selector selected mode=' + boundMode.id + ' engine=' + (boundMode.engineType ?? 'v1'));
|
|
17
18
|
return {
|
|
18
|
-
id:
|
|
19
|
-
engineType:
|
|
20
|
-
mode,
|
|
19
|
+
id: boundMode.id,
|
|
20
|
+
engineType: boundMode.engineType ?? 'v1',
|
|
21
|
+
mode: boundMode,
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -251,7 +251,11 @@ function anomalyClassKey(anomaly) {
|
|
|
251
251
|
anomaly.severity,
|
|
252
252
|
anomaly.phase,
|
|
253
253
|
anomaly.toolName,
|
|
254
|
+
anomaly.policyCode,
|
|
254
255
|
anomaly.policy,
|
|
256
|
+
anomaly.agentAid,
|
|
257
|
+
anomaly.sessionId,
|
|
258
|
+
anomaly.permissionMode,
|
|
255
259
|
anomaly.summary,
|
|
256
260
|
anomaly.effect,
|
|
257
261
|
]);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { resolvePaths } from '../paths.js';
|
|
4
|
+
import { getProcessStartTime } from './process-introspect.js';
|
|
5
|
+
const CODEX_APP_SERVER_RECORD_PREFIX = 'codex-app-server-';
|
|
6
|
+
const CODEX_APP_SERVER_RECORD_SUFFIX = '.json';
|
|
7
|
+
function recordFilePath(pid) {
|
|
8
|
+
return path.join(resolvePaths().instanceDir, `${CODEX_APP_SERVER_RECORD_PREFIX}${pid}${CODEX_APP_SERVER_RECORD_SUFFIX}`);
|
|
9
|
+
}
|
|
10
|
+
function writeAtomic(filePath, data) {
|
|
11
|
+
const temporaryPath = `${filePath}.tmp`;
|
|
12
|
+
fs.writeFileSync(temporaryPath, data, { mode: 0o600 });
|
|
13
|
+
fs.renameSync(temporaryPath, filePath);
|
|
14
|
+
}
|
|
15
|
+
/** Register a Codex app-server child owned by this EvolCore runtime. */
|
|
16
|
+
export function registerCodexAppServerProcess(pid) {
|
|
17
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
18
|
+
throw new Error(`Invalid Codex app-server PID: ${pid}`);
|
|
19
|
+
const startedAt = getProcessStartTime(pid) ?? Date.now();
|
|
20
|
+
const filePath = recordFilePath(pid);
|
|
21
|
+
const record = {
|
|
22
|
+
owner: 'evolcore',
|
|
23
|
+
pid,
|
|
24
|
+
startedAt,
|
|
25
|
+
startedAtIso: new Date(startedAt).toISOString(),
|
|
26
|
+
parentPid: process.pid,
|
|
27
|
+
};
|
|
28
|
+
fs.mkdirSync(resolvePaths().instanceDir, { recursive: true });
|
|
29
|
+
writeAtomic(filePath, JSON.stringify(record, null, 2));
|
|
30
|
+
return filePath;
|
|
31
|
+
}
|
|
32
|
+
/** Remove a process record. Safe to call after a crash or a failed spawn. */
|
|
33
|
+
export function unregisterCodexAppServerProcess(filePath) {
|
|
34
|
+
if (!filePath)
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
fs.unlinkSync(filePath);
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
try {
|
|
41
|
+
fs.unlinkSync(`${filePath}.tmp`);
|
|
42
|
+
}
|
|
43
|
+
catch { }
|
|
44
|
+
}
|
|
45
|
+
/** Read only well-formed records; malformed records are removed as stale. */
|
|
46
|
+
export function listCodexAppServerProcesses() {
|
|
47
|
+
const directory = resolvePaths().instanceDir;
|
|
48
|
+
let names;
|
|
49
|
+
try {
|
|
50
|
+
names = fs.readdirSync(directory);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const entries = [];
|
|
56
|
+
for (const name of names) {
|
|
57
|
+
if (!new RegExp(`^${CODEX_APP_SERVER_RECORD_PREFIX}\\d+\\${CODEX_APP_SERVER_RECORD_SUFFIX}$`).test(name))
|
|
58
|
+
continue;
|
|
59
|
+
const filePath = path.join(directory, name);
|
|
60
|
+
try {
|
|
61
|
+
const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
62
|
+
const pid = value.pid;
|
|
63
|
+
const startedAt = value.startedAt;
|
|
64
|
+
const parentPid = value.parentPid;
|
|
65
|
+
if (value.owner !== 'evolcore'
|
|
66
|
+
|| typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0
|
|
67
|
+
|| typeof startedAt !== 'number' || !Number.isFinite(startedAt) || startedAt <= 0
|
|
68
|
+
|| typeof parentPid !== 'number' || !Number.isInteger(parentPid) || parentPid <= 0) {
|
|
69
|
+
unregisterCodexAppServerProcess(filePath);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
entries.push({
|
|
73
|
+
record: {
|
|
74
|
+
owner: 'evolcore',
|
|
75
|
+
pid,
|
|
76
|
+
startedAt,
|
|
77
|
+
startedAtIso: typeof value.startedAtIso === 'string'
|
|
78
|
+
? value.startedAtIso
|
|
79
|
+
: new Date(startedAt).toISOString(),
|
|
80
|
+
parentPid,
|
|
81
|
+
},
|
|
82
|
+
filePath,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
unregisterCodexAppServerProcess(filePath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return entries;
|
|
90
|
+
}
|
package/dist/utils/codex-cli.js
CHANGED
|
@@ -35,5 +35,9 @@ export function execCodexCliSync(args, options) {
|
|
|
35
35
|
const launchCommand = resolveCodexLaunchCommand(args, { commandPath });
|
|
36
36
|
if (!launchCommand)
|
|
37
37
|
throw new Error('Codex CLI not found');
|
|
38
|
-
return execFileSync(launchCommand.command, launchCommand.args,
|
|
38
|
+
return execFileSync(launchCommand.command, launchCommand.args, {
|
|
39
|
+
...(options ?? {}),
|
|
40
|
+
// Keep the Windows console policy authoritative for every Codex CLI call.
|
|
41
|
+
windowsHide: process.platform === 'win32',
|
|
42
|
+
});
|
|
39
43
|
}
|