evolcore 0.0.11 → 0.0.13
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 +44 -0
- package/bin/codex-managed-hook.mjs +80 -0
- package/bin/ec-safe-output.js +26 -7
- package/dist/agents/claude-runner.js +86 -7
- package/dist/agents/codex-app-server-client.js +174 -20
- package/dist/agents/codex-runner.js +375 -92
- package/dist/agents/ecagent-runner.js +39 -4
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/control-aid.js +36 -13
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/aid/store.js +13 -7
- package/dist/aun/group-fs-download.js +56 -0
- package/dist/aun/group-identity.js +1 -2
- package/dist/aun/msg/group.js +91 -44
- package/dist/aun/msg/index.js +1 -2
- package/dist/aun/msg/managed-operation.js +865 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/aun/msg/upload.js +16 -1
- package/dist/aun/outbox.js +31 -2
- package/dist/aun/rpc/client.js +66 -0
- package/dist/aun/rpc/index.js +1 -2
- package/dist/aun/storage/{upload.js → client.js} +11 -2
- package/dist/aun/storage/index.js +1 -2
- package/dist/channels/aun.js +655 -240
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +56 -8
- package/dist/cli/agent.js +108 -31
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/cli-argv.js +10 -0
- package/dist/cli/config.js +9 -17
- package/dist/cli/ctl-command.js +1 -1
- package/dist/cli/daemon-commands.js +70 -55
- package/dist/cli/fs-command.js +79 -10
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +30 -2
- package/dist/cli/init.js +77 -8
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +53 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +7 -32
- package/dist/cli/trigger-command.js +11 -0
- package/dist/config/access-policy.js +1 -2
- package/dist/config/builtin-roles.js +9 -0
- package/dist/config/config-manager.js +21 -1
- 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/config/peer-role-resolver.js +6 -2
- package/dist/config/role-migration-startup.js +53 -0
- package/dist/config/role-store.js +1 -1
- package/dist/core/auth/agent-delegation.js +1 -2
- package/dist/core/auth/auth-gateway.js +29 -1
- package/dist/core/auth/authorization-audit.js +15 -0
- package/dist/core/auth/operation-authorizer.js +31 -7
- package/dist/core/auth/operation-catalog.js +196 -25
- package/dist/core/bootstrap-messages.js +25 -0
- package/dist/core/bootstrap-service.js +64 -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 +650 -42
- 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 +370 -0
- package/dist/core/command/menu-handler.js +230 -51
- package/dist/core/command/menu-protocol.js +94 -2
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +99 -31
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/event-catalog.js +6 -0
- package/dist/core/evolagent.js +4 -2
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +157 -22
- package/dist/core/handoff/store.js +29 -1
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/interaction-router.js +10 -0
- package/dist/core/message/im-renderer.js +19 -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 +298 -88
- package/dist/core/message/message-log.js +3 -0
- package/dist/core/message/message-queue.js +268 -39
- package/dist/core/message/message-utils.js +81 -4
- package/dist/core/message/response-engine.js +161 -77
- package/dist/core/message/stream-debouncer.js +10 -2
- package/dist/core/permission/approval-gateway.js +1 -0
- package/dist/core/permission/ec-command-parser.js +243 -18
- package/dist/core/permission/index.js +1 -1
- package/dist/core/permission/mode.js +15 -0
- package/dist/core/permission/readonly-shell-query.js +148 -8
- package/dist/core/permission/sandbox-runtime.js +43 -1
- package/dist/core/permission/tool-policy.js +322 -57
- package/dist/core/protected-paths.js +114 -4
- package/dist/core/session/session-manager.js +42 -4
- package/dist/core/session/session-mapper.js +20 -1
- package/dist/eck/detect.js +18 -1
- package/dist/eck/group-rules-sync.js +15 -44
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +541 -180
- package/dist/ipc.js +232 -10
- package/dist/paths.js +3 -0
- 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/feedback.js +64 -11
- package/dist/trigger/manager.js +21 -1
- package/dist/trigger/parser.js +14 -2
- package/dist/trigger/scheduler.js +1 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/trigger/validation.js +67 -6
- 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/ecweb-supervisor.js +332 -0
- package/dist/utils/error-utils.js +10 -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/evolcore/trigger.md +17 -1
- 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/migrations/migrate-contact-book-v2.mjs +1 -2
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/role-config.schema.1.json +1 -0
- 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 +27 -0
- package/kits/templates/roles/member.json +34 -1
- package/kits/templates/roles/visitor.json +35 -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/aun/msg/payload-type.js +0 -27
- package/dist/aun/rpc/caller.js +0 -42
- package/dist/aun/rpc/connection.js +0 -25
- package/dist/aun/storage/manage.js +0 -10
- package/dist/config/access-policy-domain.js +0 -18
- package/dist/config/config-batch-get.js +0 -11
- package/dist/config/owner-policy.js +0 -4
- package/dist/config/role-config-v4-startup.js +0 -32
- package/dist/config/role-config-v5-startup.js +0 -27
- package/dist/core/auth/trigger-authorization.js +0 -15
- package/dist/core/command/evol-menu-version-gate.js +0 -39
- package/dist/core/interaction-registration.js +0 -10
- package/dist/core/permission/execution-sandbox.js +0 -16
- package/dist/core/relation/peer-key.js +0 -1
- package/dist/core/session/session-key.js +0 -24
- package/dist/eck/baseagent-caps.js +0 -18
- package/dist/eck/rules-loader.js +0 -28
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,33 @@ 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 finiteTimestamp = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : undefined);
|
|
19
|
+
const LOCAL_CONTROL_COMMANDS = new Set([
|
|
20
|
+
'bind.begin',
|
|
21
|
+
'bind.status',
|
|
22
|
+
'bind.cancel',
|
|
23
|
+
'dingtalk.contact-bind.register',
|
|
24
|
+
'feishu.contact-bind.register',
|
|
25
|
+
'qqbot.contact-bind.register',
|
|
26
|
+
'wecom.contact-bind.register',
|
|
27
|
+
'wechat.contact-bind.register',
|
|
28
|
+
'queue-snapshot',
|
|
29
|
+
'aun-aid-stats-record-outbound',
|
|
30
|
+
'evolagent.reload',
|
|
31
|
+
'evolagent.load',
|
|
32
|
+
'evolagent.resync',
|
|
33
|
+
'menu.exec',
|
|
34
|
+
'monitor-snapshot',
|
|
35
|
+
]);
|
|
36
|
+
function requiresLocalControlToken(cmd) {
|
|
37
|
+
// Managed bootstrap completion is authenticated by the session delegation
|
|
38
|
+
// ticket. A standalone CLI invocation still needs the daemon control token.
|
|
39
|
+
return LOCAL_CONTROL_COMMANDS.has(String(cmd.type ?? ''))
|
|
40
|
+
|| (cmd.type === 'evolagent.bootstrapComplete' && typeof cmd.sessionId !== 'string');
|
|
41
|
+
}
|
|
16
42
|
export function summarizeMonitorAgents(agents, aids) {
|
|
17
43
|
const connectedAgents = new Set(aids.filter(aid => aid.status === 'connected').map(aid => aid.agentName || aid.aid));
|
|
18
44
|
const enabledAgents = agents.filter(agent => agent.status !== 'disabled');
|
|
@@ -39,10 +65,14 @@ export class IpcServer {
|
|
|
39
65
|
agentStatsProvider;
|
|
40
66
|
queueSnapshotProvider;
|
|
41
67
|
queueActionExecutor;
|
|
68
|
+
queueOperationExecutor;
|
|
69
|
+
statusAuthorizationExecutor;
|
|
70
|
+
storageAuthorizationExecutor;
|
|
42
71
|
triggerExecutor;
|
|
43
72
|
bootstrapCompleteExecutor;
|
|
44
73
|
agentReloadExecutor;
|
|
45
74
|
taskRuntimeContextProvider;
|
|
75
|
+
codexPreToolUseExecutor;
|
|
46
76
|
aunMsgSender;
|
|
47
77
|
handoffReturnExecutor;
|
|
48
78
|
handoffStatusExecutor;
|
|
@@ -51,11 +81,16 @@ export class IpcServer {
|
|
|
51
81
|
bindExecutor;
|
|
52
82
|
configOperationExecutor;
|
|
53
83
|
contactOperationExecutor;
|
|
84
|
+
aidOperationExecutor;
|
|
85
|
+
groupOperationExecutor;
|
|
86
|
+
msgOperationExecutor;
|
|
87
|
+
fsOperationExecutor;
|
|
54
88
|
dingtalkContactBindExecutor;
|
|
55
89
|
feishuContactBindExecutor;
|
|
56
90
|
qqbotContactBindExecutor;
|
|
57
91
|
wecomContactBindExecutor;
|
|
58
92
|
wechatContactBindExecutor;
|
|
93
|
+
controlToken;
|
|
59
94
|
// CPU 占用追踪:IPC handler 是一次性同步调用,无法在响应里做 200ms 异步采样,
|
|
60
95
|
// 故用后台 1s interval 累积 process.cpuUsage() 增量,handler 直接读最近值。
|
|
61
96
|
// procCpuPercent = 本 daemon 进程占单核的百分比(可 >100% 仅当多核,已 clamp 到 100);
|
|
@@ -78,6 +113,10 @@ export class IpcServer {
|
|
|
78
113
|
setAgentRegistry(registry) {
|
|
79
114
|
this.agentRegistry = registry;
|
|
80
115
|
}
|
|
116
|
+
/** Authenticate local control-plane callers that cannot use task delegation. */
|
|
117
|
+
setControlToken(token) {
|
|
118
|
+
this.controlToken = Buffer.from(token);
|
|
119
|
+
}
|
|
81
120
|
/** Inject menu.* executor (ECWeb Control proxies menu requests through this) */
|
|
82
121
|
setMenuExecutor(executor) {
|
|
83
122
|
this.menuExecutor = executor;
|
|
@@ -88,6 +127,18 @@ export class IpcServer {
|
|
|
88
127
|
setContactOperationExecutor(executor) {
|
|
89
128
|
this.contactOperationExecutor = executor;
|
|
90
129
|
}
|
|
130
|
+
setAidOperationExecutor(executor) {
|
|
131
|
+
this.aidOperationExecutor = executor;
|
|
132
|
+
}
|
|
133
|
+
setGroupOperationExecutor(executor) {
|
|
134
|
+
this.groupOperationExecutor = executor;
|
|
135
|
+
}
|
|
136
|
+
setMsgOperationExecutor(executor) {
|
|
137
|
+
this.msgOperationExecutor = executor;
|
|
138
|
+
}
|
|
139
|
+
setFsOperationExecutor(executor) {
|
|
140
|
+
this.fsOperationExecutor = executor;
|
|
141
|
+
}
|
|
91
142
|
setAgentReloadExecutor(executor) {
|
|
92
143
|
this.agentReloadExecutor = executor;
|
|
93
144
|
}
|
|
@@ -119,6 +170,15 @@ export class IpcServer {
|
|
|
119
170
|
setQueueActionExecutor(executor) {
|
|
120
171
|
this.queueActionExecutor = executor;
|
|
121
172
|
}
|
|
173
|
+
setQueueOperationExecutor(executor) {
|
|
174
|
+
this.queueOperationExecutor = executor;
|
|
175
|
+
}
|
|
176
|
+
setStatusAuthorizationExecutor(executor) {
|
|
177
|
+
this.statusAuthorizationExecutor = executor;
|
|
178
|
+
}
|
|
179
|
+
setStorageAuthorizationExecutor(executor) {
|
|
180
|
+
this.storageAuthorizationExecutor = executor;
|
|
181
|
+
}
|
|
122
182
|
/** Inject daemon-level trigger executor for ec trigger. */
|
|
123
183
|
setTriggerExecutor(executor) {
|
|
124
184
|
this.triggerExecutor = executor;
|
|
@@ -131,6 +191,9 @@ export class IpcServer {
|
|
|
131
191
|
setTaskRuntimeContextProvider(provider) {
|
|
132
192
|
this.taskRuntimeContextProvider = provider;
|
|
133
193
|
}
|
|
194
|
+
setCodexPreToolUseExecutor(executor) {
|
|
195
|
+
this.codexPreToolUseExecutor = executor;
|
|
196
|
+
}
|
|
134
197
|
/** Inject daemon-backed AUN private message sender for in-task `ec msg send`. */
|
|
135
198
|
setAunMsgSender(sender) {
|
|
136
199
|
this.aunMsgSender = sender;
|
|
@@ -323,9 +386,31 @@ export class IpcServer {
|
|
|
323
386
|
}
|
|
324
387
|
}
|
|
325
388
|
async handleCommand(cmd) {
|
|
389
|
+
if (requiresLocalControlToken(cmd) && !this.hasValidControlToken(cmd.controlToken)) {
|
|
390
|
+
return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
|
|
391
|
+
}
|
|
326
392
|
switch (cmd.type) {
|
|
327
393
|
case 'status':
|
|
328
394
|
return this.getStatus();
|
|
395
|
+
case 'codex-pre-tool-use': {
|
|
396
|
+
if (!this.codexPreToolUseExecutor) {
|
|
397
|
+
return { ok: false, applicable: true, decision: 'deny', reason: 'EvolCore Codex policy executor is unavailable' };
|
|
398
|
+
}
|
|
399
|
+
if (typeof cmd.threadId !== 'string' || !cmd.threadId
|
|
400
|
+
|| typeof cmd.toolName !== 'string' || !cmd.toolName) {
|
|
401
|
+
return { ok: false, applicable: true, decision: 'deny', reason: 'invalid Codex hook request' };
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
return await this.codexPreToolUseExecutor({
|
|
405
|
+
threadId: cmd.threadId,
|
|
406
|
+
toolName: cmd.toolName,
|
|
407
|
+
toolInput: cmd.toolInput,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
return { ok: false, applicable: true, decision: 'deny', reason: `EvolCore policy unavailable: ${error instanceof Error ? error.message : String(error)}` };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
329
414
|
case 'ping':
|
|
330
415
|
return { pong: true, pid: process.pid, protocolVersion: 1 };
|
|
331
416
|
case 'bind.begin': {
|
|
@@ -442,7 +527,11 @@ export class IpcServer {
|
|
|
442
527
|
// 纯查询
|
|
443
528
|
if (!this.queueSnapshotProvider)
|
|
444
529
|
return { ok: false, error: 'queue-snapshot not configured' };
|
|
445
|
-
return { ok: true, items: this.queueSnapshotProvider({
|
|
530
|
+
return { ok: true, items: this.queueSnapshotProvider({
|
|
531
|
+
agent: cmd.agent,
|
|
532
|
+
after: finiteTimestamp(cmd.after),
|
|
533
|
+
before: finiteTimestamp(cmd.before),
|
|
534
|
+
}) };
|
|
446
535
|
}
|
|
447
536
|
// 操作:clear / cancel / interrupt
|
|
448
537
|
if (!this.queueActionExecutor)
|
|
@@ -454,6 +543,55 @@ export class IpcServer {
|
|
|
454
543
|
sessionKey: cmd.sessionKey,
|
|
455
544
|
});
|
|
456
545
|
}
|
|
546
|
+
case 'queue.op': {
|
|
547
|
+
if (!this.queueOperationExecutor)
|
|
548
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'queue.op not configured' };
|
|
549
|
+
if (typeof cmd.agent !== 'string' || !cmd.agent || typeof cmd.sessionId !== 'string' || !cmd.sessionId) {
|
|
550
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing agent or sessionId' };
|
|
551
|
+
}
|
|
552
|
+
if (cmd.action !== undefined && !['clear', 'cancel', 'interrupt'].includes(cmd.action)) {
|
|
553
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'invalid queue action' };
|
|
554
|
+
}
|
|
555
|
+
return await this.queueOperationExecutor({
|
|
556
|
+
agent: cmd.agent,
|
|
557
|
+
action: cmd.action,
|
|
558
|
+
messageId: typeof cmd.messageId === 'string' ? cmd.messageId : undefined,
|
|
559
|
+
sessionKey: typeof cmd.sessionKey === 'string' ? cmd.sessionKey : undefined,
|
|
560
|
+
after: finiteTimestamp(cmd.after),
|
|
561
|
+
before: finiteTimestamp(cmd.before),
|
|
562
|
+
sessionId: cmd.sessionId,
|
|
563
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
564
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
case 'status.auth': {
|
|
568
|
+
if (!this.statusAuthorizationExecutor)
|
|
569
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'status authorization not configured' };
|
|
570
|
+
if (typeof cmd.sessionId !== 'string' || !cmd.sessionId)
|
|
571
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId' };
|
|
572
|
+
return await this.statusAuthorizationExecutor({
|
|
573
|
+
sessionId: cmd.sessionId,
|
|
574
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
575
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
case 'storage.auth': {
|
|
579
|
+
if (!this.storageAuthorizationExecutor)
|
|
580
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'storage authorization not configured' };
|
|
581
|
+
if (typeof cmd.sessionId !== 'string' || !cmd.sessionId || typeof cmd.aid !== 'string' || !cmd.aid || typeof cmd.operation !== 'string') {
|
|
582
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing sessionId, aid, or operation' };
|
|
583
|
+
}
|
|
584
|
+
if (!['storage.list', 'storage.quota', 'storage.upload', 'storage.download', 'storage.delete'].includes(cmd.operation)) {
|
|
585
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'invalid storage operation' };
|
|
586
|
+
}
|
|
587
|
+
return await this.storageAuthorizationExecutor({
|
|
588
|
+
sessionId: cmd.sessionId,
|
|
589
|
+
aid: cmd.aid,
|
|
590
|
+
operation: cmd.operation,
|
|
591
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
592
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
593
|
+
});
|
|
594
|
+
}
|
|
457
595
|
case 'aun-aid-stats-record-outbound': {
|
|
458
596
|
if (!this.aunAidStatsRecorder)
|
|
459
597
|
return { ok: false, error: 'recorder not configured' };
|
|
@@ -519,14 +657,17 @@ export class IpcServer {
|
|
|
519
657
|
}
|
|
520
658
|
: undefined;
|
|
521
659
|
try {
|
|
660
|
+
const normalizedPayload = payload && Object.prototype.hasOwnProperty.call(payload, 'mentions')
|
|
661
|
+
? { ...payload, mentions: normalizeAunMentionEntries(payload.mentions) }
|
|
662
|
+
: payload;
|
|
522
663
|
return await this.aunMsgSender({
|
|
523
664
|
aid: cmd.aid,
|
|
524
665
|
to: cmd.to,
|
|
525
666
|
scope: cmd.scope === 'group' ? 'group' : 'msg',
|
|
526
|
-
payload,
|
|
667
|
+
payload: normalizedPayload,
|
|
527
668
|
file,
|
|
528
|
-
mentions:
|
|
529
|
-
? cmd.mentions
|
|
669
|
+
mentions: Object.prototype.hasOwnProperty.call(cmd, 'mentions')
|
|
670
|
+
? normalizeAunMentionEntries(cmd.mentions)
|
|
530
671
|
: undefined,
|
|
531
672
|
encrypt: typeof cmd.encrypt === 'boolean' ? cmd.encrypt : undefined,
|
|
532
673
|
thread: typeof cmd.thread === 'string' ? cmd.thread : undefined,
|
|
@@ -539,7 +680,7 @@ export class IpcServer {
|
|
|
539
680
|
});
|
|
540
681
|
}
|
|
541
682
|
catch (e) {
|
|
542
|
-
return { ok: false, error: e?.message || String(e) };
|
|
683
|
+
return { ok: false, error: e?.message || String(e), code: e?.code };
|
|
543
684
|
}
|
|
544
685
|
}
|
|
545
686
|
case 'handoff-return': {
|
|
@@ -568,6 +709,8 @@ export class IpcServer {
|
|
|
568
709
|
return this.handoffStatusExecutor({
|
|
569
710
|
sessionId: typeof cmd.sessionId === 'string' ? cmd.sessionId : undefined,
|
|
570
711
|
handoffId: cmd.handoffId,
|
|
712
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
713
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
571
714
|
});
|
|
572
715
|
}
|
|
573
716
|
case 'handoff-list': {
|
|
@@ -587,6 +730,8 @@ export class IpcServer {
|
|
|
587
730
|
state: state,
|
|
588
731
|
filterSessionId: typeof cmd.filterSessionId === 'string' ? cmd.filterSessionId : undefined,
|
|
589
732
|
limit,
|
|
733
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
734
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
590
735
|
});
|
|
591
736
|
}
|
|
592
737
|
case 'handoff-trace': {
|
|
@@ -604,6 +749,8 @@ export class IpcServer {
|
|
|
604
749
|
agent: typeof cmd.agent === 'string' ? cmd.agent : undefined,
|
|
605
750
|
handoffId: cmd.handoffId,
|
|
606
751
|
limit,
|
|
752
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
753
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
607
754
|
});
|
|
608
755
|
}
|
|
609
756
|
case 'ctl': {
|
|
@@ -638,6 +785,54 @@ export class IpcServer {
|
|
|
638
785
|
}
|
|
639
786
|
return await this.contactOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
640
787
|
}
|
|
788
|
+
case 'aid.op': {
|
|
789
|
+
if (!this.aidOperationExecutor)
|
|
790
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'aid.op not configured' };
|
|
791
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
792
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
793
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
794
|
+
}
|
|
795
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
796
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
797
|
+
}
|
|
798
|
+
return await this.aidOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
799
|
+
}
|
|
800
|
+
case 'group.op': {
|
|
801
|
+
if (!this.groupOperationExecutor)
|
|
802
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'group.op not configured' };
|
|
803
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
804
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
805
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
806
|
+
}
|
|
807
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
808
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
809
|
+
}
|
|
810
|
+
return await this.groupOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
811
|
+
}
|
|
812
|
+
case 'msg.op': {
|
|
813
|
+
if (!this.msgOperationExecutor)
|
|
814
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'msg.op not configured' };
|
|
815
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
816
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
817
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
818
|
+
}
|
|
819
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
820
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
821
|
+
}
|
|
822
|
+
return await this.msgOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
823
|
+
}
|
|
824
|
+
case 'fs.op': {
|
|
825
|
+
if (!this.fsOperationExecutor)
|
|
826
|
+
return { ok: false, code: 'NOT_CONFIGURED', error: 'fs.op not configured' };
|
|
827
|
+
const { argv, sessionId, delegationToken } = cmd;
|
|
828
|
+
if (!Array.isArray(argv) || argv.some(value => typeof value !== 'string') || !sessionId) {
|
|
829
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'missing argv or sessionId' };
|
|
830
|
+
}
|
|
831
|
+
if (delegationToken !== undefined && typeof delegationToken !== 'string') {
|
|
832
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegationToken must be a string' };
|
|
833
|
+
}
|
|
834
|
+
return await this.fsOperationExecutor(argv, sessionId, delegationToken, typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined);
|
|
835
|
+
}
|
|
641
836
|
case 'trigger.list':
|
|
642
837
|
case 'trigger.show':
|
|
643
838
|
case 'trigger.history':
|
|
@@ -684,7 +879,12 @@ export class IpcServer {
|
|
|
684
879
|
if (!this.bootstrapCompleteExecutor)
|
|
685
880
|
return { ok: false, error: 'bootstrap complete executor not configured' };
|
|
686
881
|
try {
|
|
687
|
-
return await this.bootstrapCompleteExecutor(aid
|
|
882
|
+
return await this.bootstrapCompleteExecutor(aid, {
|
|
883
|
+
sessionId: typeof cmd.sessionId === 'string' ? cmd.sessionId : undefined,
|
|
884
|
+
delegationToken: typeof cmd.delegationToken === 'string' ? cmd.delegationToken : undefined,
|
|
885
|
+
delegationCommandHash: typeof cmd.delegationCommandHash === 'string' ? cmd.delegationCommandHash : undefined,
|
|
886
|
+
controlToken: typeof cmd.controlToken === 'string' ? cmd.controlToken : undefined,
|
|
887
|
+
});
|
|
688
888
|
}
|
|
689
889
|
catch (e) {
|
|
690
890
|
return { ok: false, error: e?.message || String(e) };
|
|
@@ -747,7 +947,11 @@ export class IpcServer {
|
|
|
747
947
|
if (!this.menuExecutor)
|
|
748
948
|
return { ok: false, error: 'menu.exec not configured' };
|
|
749
949
|
try {
|
|
750
|
-
const response = await this.menuExecutor(cmd.payload,
|
|
950
|
+
const response = await this.menuExecutor(cmd.payload, {
|
|
951
|
+
source: 'ecweb',
|
|
952
|
+
actorAid: null,
|
|
953
|
+
localDirect: true,
|
|
954
|
+
});
|
|
751
955
|
return { ok: true, response };
|
|
752
956
|
}
|
|
753
957
|
catch (e) {
|
|
@@ -831,17 +1035,35 @@ export class IpcServer {
|
|
|
831
1035
|
return { error: `unknown command: ${cmd.type}` };
|
|
832
1036
|
}
|
|
833
1037
|
}
|
|
1038
|
+
hasValidControlToken(value) {
|
|
1039
|
+
if (!this.controlToken || typeof value !== 'string')
|
|
1040
|
+
return false;
|
|
1041
|
+
const provided = Buffer.from(value);
|
|
1042
|
+
return provided.length === this.controlToken.length
|
|
1043
|
+
&& crypto.timingSafeEqual(provided, this.controlToken);
|
|
1044
|
+
}
|
|
834
1045
|
}
|
|
835
1046
|
/**
|
|
836
1047
|
* Query the running EvolCore daemon via Unix socket.
|
|
837
1048
|
* Returns null if the service is not running or the socket is unreachable.
|
|
838
1049
|
*/
|
|
839
1050
|
export function ipcQuery(socketPath, cmd, timeoutMs = 3000) {
|
|
840
|
-
|
|
841
|
-
|
|
1051
|
+
let authenticatedCmd = cmd;
|
|
1052
|
+
if (!process.env.EVOLCORE_SESSION_ID
|
|
1053
|
+
&& requiresLocalControlToken(cmd)
|
|
1054
|
+
&& typeof cmd.controlToken !== 'string') {
|
|
1055
|
+
try {
|
|
1056
|
+
const controlToken = fs.readFileSync(resolvePaths().controlToken, 'utf8').trim();
|
|
1057
|
+
if (controlToken)
|
|
1058
|
+
authenticatedCmd = { ...cmd, controlToken };
|
|
1059
|
+
}
|
|
1060
|
+
catch { }
|
|
1061
|
+
}
|
|
1062
|
+
const delegationCommandHash = typeof authenticatedCmd.delegationToken === 'string'
|
|
1063
|
+
&& typeof authenticatedCmd.delegationCommandHash !== 'string'
|
|
842
1064
|
? hashCurrentDelegatedCommand()
|
|
843
1065
|
: undefined;
|
|
844
|
-
const request = delegationCommandHash ? { ...
|
|
1066
|
+
const request = delegationCommandHash ? { ...authenticatedCmd, delegationCommandHash } : authenticatedCmd;
|
|
845
1067
|
return new Promise((resolve) => {
|
|
846
1068
|
const conn = net.connect(socketPath);
|
|
847
1069
|
let buf = '';
|
package/dist/paths.js
CHANGED
|
@@ -149,6 +149,9 @@ export function daemonControlDir() {
|
|
|
149
149
|
export function daemonQueuePath() {
|
|
150
150
|
return path.join(daemonDataDir(), 'message-queue.json');
|
|
151
151
|
}
|
|
152
|
+
export function daemonMessageDedupPath() {
|
|
153
|
+
return path.join(daemonDataDir(), 'message-dedup.json');
|
|
154
|
+
}
|
|
152
155
|
export function channelStateDir(channelKey) {
|
|
153
156
|
if (!channelKey || channelKey === '.' || channelKey === '..') {
|
|
154
157
|
throw new Error('channelKey is required');
|
|
@@ -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
|
]);
|