evolcore 0.0.10 → 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 +44 -0
- package/README.md +3 -3
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/baseagent.js +4 -0
- package/dist/agents/claude-runner.js +158 -44
- package/dist/agents/codex-app-server-client.js +153 -23
- package/dist/agents/codex-runner.js +331 -43
- package/dist/agents/ecagent-runner.js +53 -6
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/agents/request-identity.js +55 -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/aun/outbox.js +28 -31
- package/dist/channels/aun.js +418 -235
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +72 -17
- package/dist/cli/agent.js +249 -22
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +52 -33
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +106 -63
- package/dist/cli/init-cancel.js +208 -0
- package/dist/cli/init-channel.js +343 -195
- package/dist/cli/init.js +21 -9
- 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 +5 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/gateway-config.js +26 -10
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/agent-reload-coordinator.js +53 -0
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +62 -154
- package/dist/core/auth/operation-catalog.js +264 -22
- package/dist/core/bootstrap-messages.js +68 -0
- package/dist/core/bootstrap-service.js +144 -10
- 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 +24 -7
- package/dist/core/command/agent-control.js +14 -11
- 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 +213 -110
- 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 +45 -25
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent-registry.js +125 -35
- package/dist/core/evolagent.js +12 -5
- 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/inference/text-inference.js +38 -4
- 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 +237 -79
- package/dist/core/message/message-log.js +23 -0
- package/dist/core/message/message-queue.js +20 -4
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/model/model-catalog.js +143 -24
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/index.js +1 -0
- package/dist/core/permission/readonly-shell-query.js +532 -0
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/shell-environment.js +46 -0
- package/dist/core/permission/tool-policy.js +292 -111
- package/dist/core/protected-paths.js +17 -9
- package/dist/core/runner-reload-transaction.js +57 -0
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +644 -180
- package/dist/ipc.js +246 -19
- 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/aid-bind.js +3 -8
- 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/log-writer.js +6 -10
- package/dist/utils/logger.js +5 -5
- 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/msg.md +13 -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/01-overview.md +9 -0
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/agent-config.schema.3.json +1 -1
- package/kits/schemas/agent-config.schema.4.json +1 -1
- package/kits/schemas/relation-config.schema.2.json +1 -1
- package/kits/schemas/role-config.schema.1.json +1 -1
- 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 +44 -0
- package/kits/templates/roles/visitor.json +40 -2
- package/kits/templates/system-fragments/bootstrap.md +12 -6
- package/kits/templates/system-fragments/channel.md +6 -0
- package/kits/templates/system-fragments/session.md +2 -0
- package/package.json +3 -2
- package/skills/eclink/SKILL.md +15 -3
- package/skills/eclink/agents/openai.yaml +3 -3
- package/dist/core/command/evol-menu-version-gate.js +0 -39
package/dist/channels/daemon.js
CHANGED
|
@@ -415,7 +415,11 @@ function normalizeExecutionAnomaly(value) {
|
|
|
415
415
|
: Date.now();
|
|
416
416
|
const toolName = nonEmptyString(value.toolName);
|
|
417
417
|
const requestId = nonEmptyString(value.requestId);
|
|
418
|
+
const policyCode = nonEmptyString(value.policyCode);
|
|
418
419
|
const policy = normalizeApproverPolicy(value.policy);
|
|
420
|
+
const agentAid = nonEmptyString(value.agentAid);
|
|
421
|
+
const sessionId = nonEmptyString(value.sessionId);
|
|
422
|
+
const permissionMode = normalizePermissionMode(value.permissionMode);
|
|
419
423
|
const summary = nonEmptyString(value.summary);
|
|
420
424
|
return {
|
|
421
425
|
code: code,
|
|
@@ -425,7 +429,11 @@ function normalizeExecutionAnomaly(value) {
|
|
|
425
429
|
effect,
|
|
426
430
|
...(toolName ? { toolName } : {}),
|
|
427
431
|
...(requestId ? { requestId } : {}),
|
|
432
|
+
...(policyCode ? { policyCode } : {}),
|
|
428
433
|
...(policy ? { policy } : {}),
|
|
434
|
+
...(agentAid ? { agentAid } : {}),
|
|
435
|
+
...(sessionId ? { sessionId } : {}),
|
|
436
|
+
...(permissionMode ? { permissionMode } : {}),
|
|
429
437
|
...(summary ? { summary: summary.slice(0, 512) } : {}),
|
|
430
438
|
};
|
|
431
439
|
}
|
|
@@ -437,3 +445,8 @@ function normalizeApproverPolicy(value) {
|
|
|
437
445
|
? value
|
|
438
446
|
: undefined;
|
|
439
447
|
}
|
|
448
|
+
function normalizePermissionMode(value) {
|
|
449
|
+
return value === 'readonly' || value === 'auto' || value === 'request' || value === 'bypass'
|
|
450
|
+
? value
|
|
451
|
+
: undefined;
|
|
452
|
+
}
|
|
@@ -59,6 +59,13 @@ Options:
|
|
|
59
59
|
ec agent delete mybot.agentid.pub --purge`);
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
+
if (process.env.EVOLCORE_SESSION_ID && !wantsHelp(args) && !new Set(['get', 'set', 'ready']).has(sub)) {
|
|
63
|
+
if (formatJson)
|
|
64
|
+
console.log(JSON.stringify({ ok: false, code: 'NOT_ALLOWED', error: `agent ${sub} is not available in a managed task` }));
|
|
65
|
+
else
|
|
66
|
+
console.error(`❌ [NOT_ALLOWED] agent ${sub} is not available in a managed task`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
62
69
|
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentRename, agentDelete, agentReady, } = await import('./agent.js');
|
|
63
70
|
// --- list ---
|
|
64
71
|
if (sub === 'list') {
|
|
@@ -137,17 +144,21 @@ Options:
|
|
|
137
144
|
ec agent new <aid> --non-interactive [选项]
|
|
138
145
|
|
|
139
146
|
非交互模式选项:
|
|
140
|
-
--
|
|
141
|
-
--
|
|
142
|
-
|
|
143
|
-
--
|
|
144
|
-
--
|
|
147
|
+
--project <path> 默认: 当前目录
|
|
148
|
+
--baseagent <claude|codex|gemini|ecagent>
|
|
149
|
+
默认: defaults 配置,否则检测本机可用基座
|
|
150
|
+
--owner <aid> 默认: daemon.json 第一个 owner
|
|
151
|
+
--name <display-name> 默认: package.json name,否则目录名
|
|
152
|
+
--description <text> 默认: package.json description,否则留空
|
|
153
|
+
--dry-run 只预览最终参数,不注册 AID、不写文件
|
|
145
154
|
--force 覆盖已有 config.json
|
|
146
155
|
--format json 输出 JSON
|
|
147
156
|
|
|
148
157
|
示例:
|
|
149
158
|
ec agent new mybot.agentid.pub
|
|
150
|
-
ec agent new mybot.agentid.pub --non-interactive
|
|
159
|
+
ec agent new mybot.agentid.pub --non-interactive
|
|
160
|
+
ec agent new mybot.agentid.pub --non-interactive --dry-run --format json
|
|
161
|
+
ec agent new mybot.agentid.pub --non-interactive --project ../project --baseagent claude`);
|
|
151
162
|
return;
|
|
152
163
|
}
|
|
153
164
|
const name = args[1];
|
|
@@ -161,15 +172,52 @@ Options:
|
|
|
161
172
|
const idx = args.indexOf(flag);
|
|
162
173
|
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
|
|
163
174
|
};
|
|
164
|
-
const
|
|
175
|
+
const { resolveAgentCreatePlan, validateAgentCreatePlan } = await import('./agent.js');
|
|
176
|
+
const plan = resolveAgentCreatePlan({
|
|
165
177
|
aid: name,
|
|
178
|
+
project: getArg('--project'),
|
|
166
179
|
baseagent: getArg('--baseagent'),
|
|
167
|
-
project: getArg('--project') || '',
|
|
168
180
|
owner: getArg('--owner'),
|
|
169
181
|
name: getArg('--name'),
|
|
170
182
|
description: getArg('--description'),
|
|
171
183
|
force: args.includes('--force'),
|
|
172
184
|
});
|
|
185
|
+
const validationError = validateAgentCreatePlan(plan);
|
|
186
|
+
if (validationError) {
|
|
187
|
+
const error = { ok: false, error: validationError };
|
|
188
|
+
if (formatJson)
|
|
189
|
+
console.log(JSON.stringify(error));
|
|
190
|
+
else
|
|
191
|
+
console.error(`❌ ${error.error}`);
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (args.includes('--dry-run')) {
|
|
196
|
+
const preview = { ok: true, dryRun: true, plan };
|
|
197
|
+
if (formatJson)
|
|
198
|
+
console.log(JSON.stringify(preview, null, 2));
|
|
199
|
+
else {
|
|
200
|
+
console.log('EvolAgent 创建计划(dry-run)');
|
|
201
|
+
console.log(` AID: ${plan.aid}`);
|
|
202
|
+
console.log(` Project: ${plan.project}`);
|
|
203
|
+
console.log(` Baseagent: ${plan.baseagent} (${plan.baseagentSource})`);
|
|
204
|
+
console.log(` Owner: ${plan.owner || '未设置'} (${plan.ownerSource})`);
|
|
205
|
+
console.log(` Name: ${plan.name} (${plan.nameSource})`);
|
|
206
|
+
if (plan.description)
|
|
207
|
+
console.log(` Description: ${plan.description} (${plan.descriptionSource})`);
|
|
208
|
+
console.log(` Existing config: ${plan.configExists ? (plan.force ? '将覆盖' : '是') : '否'}`);
|
|
209
|
+
}
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const result = await agentCreateNonInteractive({
|
|
213
|
+
aid: plan.aid,
|
|
214
|
+
baseagent: plan.baseagent,
|
|
215
|
+
project: plan.project,
|
|
216
|
+
owner: plan.owner,
|
|
217
|
+
name: plan.name,
|
|
218
|
+
description: plan.description,
|
|
219
|
+
force: args.includes('--force'),
|
|
220
|
+
});
|
|
173
221
|
if (!result.ok) {
|
|
174
222
|
if (formatJson) {
|
|
175
223
|
console.log(JSON.stringify(result));
|
|
@@ -179,6 +227,8 @@ Options:
|
|
|
179
227
|
}
|
|
180
228
|
process.exit(1);
|
|
181
229
|
}
|
|
230
|
+
if (result.ownerBindInterrupted)
|
|
231
|
+
return;
|
|
182
232
|
if (formatJson) {
|
|
183
233
|
console.log(JSON.stringify(result, null, 2));
|
|
184
234
|
}
|
|
@@ -211,6 +261,8 @@ Options:
|
|
|
211
261
|
}
|
|
212
262
|
process.exit(1);
|
|
213
263
|
}
|
|
264
|
+
if (result.ownerBindInterrupted)
|
|
265
|
+
return;
|
|
214
266
|
if (formatJson) {
|
|
215
267
|
console.log(JSON.stringify(result, null, 2));
|
|
216
268
|
}
|
|
@@ -259,15 +311,16 @@ Options:
|
|
|
259
311
|
// --- reload ---
|
|
260
312
|
if (sub === 'reload') {
|
|
261
313
|
if (wantsHelp(args)) {
|
|
262
|
-
console.log(`用法: ec agent reload [aid] [--format json]
|
|
314
|
+
console.log(`用法: ec agent reload [aid] [--force] [--format json]
|
|
263
315
|
|
|
264
316
|
热重载 agent 配置。
|
|
265
317
|
无参数 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
|
|
266
|
-
<aid> 仅热重载指定 agent
|
|
318
|
+
<aid> 仅热重载指定 agent
|
|
319
|
+
--force 中断目标 Agent 的运行中任务并清空排队任务后重载(仅与 <aid> 同用)`);
|
|
267
320
|
return;
|
|
268
321
|
}
|
|
269
322
|
const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
270
|
-
const result = await agentReload(target);
|
|
323
|
+
const result = await agentReload(target, { force: args.includes('--force') });
|
|
271
324
|
if (!result.ok) {
|
|
272
325
|
if (formatJson) {
|
|
273
326
|
console.log(JSON.stringify(result));
|
|
@@ -294,9 +347,10 @@ Options:
|
|
|
294
347
|
// --- enable ---
|
|
295
348
|
if (sub === 'enable') {
|
|
296
349
|
if (wantsHelp(args)) {
|
|
297
|
-
console.log(`用法: ec agent enable <aid> [--format json]
|
|
350
|
+
console.log(`用法: ec agent enable <aid> [--force] [--format json]
|
|
298
351
|
|
|
299
|
-
启用 agent。若服务运行中会热重载,否则下次 ec start
|
|
352
|
+
启用 agent。若服务运行中会热重载,否则下次 ec start 时生效。
|
|
353
|
+
--force 中断目标 Agent 的运行中任务并清空排队任务后重载`);
|
|
300
354
|
return;
|
|
301
355
|
}
|
|
302
356
|
const aid = args[1];
|
|
@@ -304,7 +358,7 @@ Options:
|
|
|
304
358
|
console.error('用法: ec agent enable <aid>');
|
|
305
359
|
process.exit(1);
|
|
306
360
|
}
|
|
307
|
-
const result = await agentEnable(aid);
|
|
361
|
+
const result = await agentEnable(aid, { force: args.includes('--force') });
|
|
308
362
|
if (!result.ok) {
|
|
309
363
|
if (formatJson) {
|
|
310
364
|
console.log(JSON.stringify(result));
|
|
@@ -325,9 +379,10 @@ Options:
|
|
|
325
379
|
// --- disable ---
|
|
326
380
|
if (sub === 'disable') {
|
|
327
381
|
if (wantsHelp(args)) {
|
|
328
|
-
console.log(`用法: ec agent disable <aid> [--format json]
|
|
382
|
+
console.log(`用法: ec agent disable <aid> [--force] [--format json]
|
|
329
383
|
|
|
330
|
-
停用 agent
|
|
384
|
+
停用 agent。若服务运行中会热重载离线,否则在配置中标记为禁用。
|
|
385
|
+
--force 中断目标 Agent 的运行中任务并清空排队任务后重载`);
|
|
331
386
|
return;
|
|
332
387
|
}
|
|
333
388
|
const aid = args[1];
|
|
@@ -335,7 +390,7 @@ Options:
|
|
|
335
390
|
console.error('用法: ec agent disable <aid>');
|
|
336
391
|
process.exit(1);
|
|
337
392
|
}
|
|
338
|
-
const result = await agentDisable(aid);
|
|
393
|
+
const result = await agentDisable(aid, { force: args.includes('--force') });
|
|
339
394
|
if (!result.ok) {
|
|
340
395
|
if (formatJson) {
|
|
341
396
|
console.log(JSON.stringify(result));
|
package/dist/cli/agent.js
CHANGED
|
@@ -2,20 +2,25 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import readline from 'readline';
|
|
4
4
|
import { resolvePaths, agentMdPath as getAgentMdPathFromPaths, aunPath as defaultAunPath } from '../paths.js';
|
|
5
|
-
import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton } from '../config-store.js';
|
|
5
|
+
import { loadDefaults, loadAllAgents, loadAgent, saveAgent, ensureAgentDirSkeleton, loadDaemonConfig } from '../config-store.js';
|
|
6
6
|
import { ConfigTarget, write as cfgWrite, ensureFile as cfgEnsure, read as cfgRead, initConfigManager } from '../config/config-manager.js';
|
|
7
7
|
import { resolveConfigCommand } from '../config/resolved-config-op.js';
|
|
8
8
|
import { executeResolvedConfigCommand } from '../config/config-operation-service.js';
|
|
9
9
|
import { ipcQuery } from '../ipc.js';
|
|
10
10
|
import { CONFIG_SCHEMA_VERSION } from '../types.js';
|
|
11
|
-
import { withLifecycleForWrite } from '../config/lifecycle.js';
|
|
11
|
+
import { normalizeAgentLifecycle, withLifecycleForWrite } from '../config/lifecycle.js';
|
|
12
|
+
import { bindPostBootstrapWelcomeOutboxSession, hasPendingPostBootstrapWelcomeOutbox, postBootstrapWelcomeOperationId, preparePostBootstrapWelcomeOutbox, } from '../core/bootstrap-messages.js';
|
|
12
13
|
import { ensureInitialRoleRegistry } from '../config/role-service.js';
|
|
13
|
-
import { isValidChannelName } from '../core/channel-loader.js';
|
|
14
|
+
import { formatChannelKey, isValidChannelName } from '../core/channel-loader.js';
|
|
14
15
|
import { commandExists } from '../utils/cross-platform.js';
|
|
15
16
|
import { getCodexAppServerAvailability, isCodexAppServerAvailable } from '../agents/codex-runner.js';
|
|
16
17
|
import { resolveEcagentConfig } from '../agents/baseagent.js';
|
|
17
18
|
import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../utils/project-path.js';
|
|
18
19
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../core/auth/agent-delegation.js';
|
|
20
|
+
import { readTaskRuntimeContextFromEnv } from './task-context.js';
|
|
21
|
+
import { EventBus } from '../core/event-bus.js';
|
|
22
|
+
import { SessionManager } from '../core/session/session-manager.js';
|
|
23
|
+
import { isValidAid } from '../aun/aid/validation.js';
|
|
19
24
|
function withStaticOwner(config, owner) {
|
|
20
25
|
const value = owner?.trim();
|
|
21
26
|
if (!value)
|
|
@@ -41,6 +46,100 @@ function detectAvailableBaseagents() {
|
|
|
41
46
|
function pickDefaultBaseagent(available) {
|
|
42
47
|
return AUTO_BASEAGENT_CANDIDATES.find(candidate => available.includes(candidate)) ?? null;
|
|
43
48
|
}
|
|
49
|
+
function readProjectMetadata(project) {
|
|
50
|
+
try {
|
|
51
|
+
const packageJsonPath = path.join(project, 'package.json');
|
|
52
|
+
if (!fs.existsSync(packageJsonPath))
|
|
53
|
+
return {};
|
|
54
|
+
const parsed = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
55
|
+
return {
|
|
56
|
+
name: typeof parsed.name === 'string' ? parsed.name.trim() : undefined,
|
|
57
|
+
description: typeof parsed.description === 'string' ? parsed.description.trim() : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function projectSlug(project) {
|
|
65
|
+
const basename = path.basename(project) || 'agent';
|
|
66
|
+
const slug = basename
|
|
67
|
+
.normalize('NFKD')
|
|
68
|
+
.replace(/[^a-zA-Z0-9-]+/g, '-')
|
|
69
|
+
.replace(/^-+|-+$/g, '')
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.slice(0, 63)
|
|
72
|
+
.replace(/-+$/g, '');
|
|
73
|
+
return slug || 'agent';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve non-interactive creation defaults without performing any network or
|
|
77
|
+
* filesystem mutation. The CLI uses this for both execution and --dry-run.
|
|
78
|
+
*/
|
|
79
|
+
export function resolveAgentCreatePlan(options) {
|
|
80
|
+
const aid = options.aid.trim();
|
|
81
|
+
const project = path.resolve(options.project || process.cwd());
|
|
82
|
+
const metadata = readProjectMetadata(project);
|
|
83
|
+
const defaults = loadDefaults();
|
|
84
|
+
const daemon = loadDaemonConfig();
|
|
85
|
+
const configuredBaseagent = defaults?.active_baseagent;
|
|
86
|
+
const available = detectAvailableBaseagents();
|
|
87
|
+
const defaultBaseagent = configuredBaseagent && available.includes(configuredBaseagent)
|
|
88
|
+
? configuredBaseagent
|
|
89
|
+
: pickDefaultBaseagent(available) ?? undefined;
|
|
90
|
+
const baseagent = options.baseagent || defaultBaseagent;
|
|
91
|
+
const baseagentSource = options.baseagent
|
|
92
|
+
? 'argument'
|
|
93
|
+
: configuredBaseagent && baseagent === configuredBaseagent
|
|
94
|
+
? 'defaults'
|
|
95
|
+
: 'detected';
|
|
96
|
+
const owner = options.owner?.trim() || daemon.owners?.[0];
|
|
97
|
+
const ownerSource = options.owner?.trim() ? 'argument' : owner ? 'daemon' : 'none';
|
|
98
|
+
const name = options.name?.trim() || metadata.name || projectSlug(project);
|
|
99
|
+
const description = options.description?.trim() || metadata.description || '';
|
|
100
|
+
const configExists = isValidAid(aid)
|
|
101
|
+
&& fs.existsSync(path.join(resolvePaths().agentsDir, aid, 'config.json'));
|
|
102
|
+
return {
|
|
103
|
+
aid,
|
|
104
|
+
project,
|
|
105
|
+
baseagent,
|
|
106
|
+
owner,
|
|
107
|
+
name,
|
|
108
|
+
description,
|
|
109
|
+
force: options.force === true,
|
|
110
|
+
configExists,
|
|
111
|
+
baseagentSource,
|
|
112
|
+
ownerSource,
|
|
113
|
+
nameSource: options.name?.trim() ? 'argument' : metadata.name ? 'package' : 'directory',
|
|
114
|
+
descriptionSource: options.description?.trim() ? 'argument' : metadata.description ? 'package' : 'none',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function validateAgentCreatePlan(plan) {
|
|
118
|
+
if (!isValidAid(plan.aid)) {
|
|
119
|
+
return `Invalid AID "${plan.aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`;
|
|
120
|
+
}
|
|
121
|
+
if (plan.configExists && !plan.force) {
|
|
122
|
+
return `Agent "${plan.aid}" already exists (use --force to overwrite)`;
|
|
123
|
+
}
|
|
124
|
+
if (!plan.baseagent) {
|
|
125
|
+
return 'No usable baseagent detected. Pass --baseagent or configure a baseagent in agents/defaults.json.';
|
|
126
|
+
}
|
|
127
|
+
if (!BASEAGENT_CANDIDATES.includes(plan.baseagent)) {
|
|
128
|
+
return `Invalid baseagent: ${plan.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})`;
|
|
129
|
+
}
|
|
130
|
+
if (!isBaseagentAvailable(plan.baseagent)) {
|
|
131
|
+
return `${plan.baseagent} is not available in the current environment`;
|
|
132
|
+
}
|
|
133
|
+
if (plan.baseagent === 'ecagent') {
|
|
134
|
+
const reason = ecagentUnavailableReason();
|
|
135
|
+
if (reason)
|
|
136
|
+
return reason;
|
|
137
|
+
}
|
|
138
|
+
if (plan.owner && !isValidAid(plan.owner)) {
|
|
139
|
+
return `Invalid owner: ${plan.owner}`;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
44
143
|
function ecagentUnavailableReason() {
|
|
45
144
|
try {
|
|
46
145
|
resolveEcagentConfig({ agents: { ecagent: {} } });
|
|
@@ -458,6 +557,7 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
458
557
|
catch { /* daemon not running */ }
|
|
459
558
|
let ownerBoundAid;
|
|
460
559
|
let ownerBindSkipped = false;
|
|
560
|
+
let ownerBindInterrupted = false;
|
|
461
561
|
// Only prompt for agent owner binding if:
|
|
462
562
|
// 1. User didn't manually input owner during setup (owner is undefined)
|
|
463
563
|
// 2. User didn't skip QR binding explicitly (skipQrBinding is false)
|
|
@@ -467,15 +567,18 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
467
567
|
console.log('\n📡 Agent owner 绑定 — 请用 evol app 扫描二维码,10 分钟内有效\n');
|
|
468
568
|
try {
|
|
469
569
|
const { runAgentOwnerQrBindFlow } = await import('./init-channel.js');
|
|
470
|
-
const bound = await runAgentOwnerQrBindFlow(aid, agentName
|
|
471
|
-
if (bound
|
|
570
|
+
const bound = await runAgentOwnerQrBindFlow(aid, agentName);
|
|
571
|
+
if (bound.action === 'bound') {
|
|
472
572
|
ownerBoundAid = bound.boundAid;
|
|
473
573
|
console.log(` ✓ 已配置 agent owner: ${bound.boundAid}`);
|
|
474
574
|
}
|
|
475
|
-
else {
|
|
575
|
+
else if (bound.action === 'manual') {
|
|
476
576
|
ownerBindSkipped = true;
|
|
477
577
|
ownerBoundAid = await promptAgentOwnerManually(aid);
|
|
478
578
|
}
|
|
579
|
+
else {
|
|
580
|
+
ownerBindInterrupted = true;
|
|
581
|
+
}
|
|
479
582
|
}
|
|
480
583
|
catch (e) {
|
|
481
584
|
ownerBindSkipped = true;
|
|
@@ -493,6 +596,7 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
493
596
|
hotLoadError,
|
|
494
597
|
ownerBoundAid,
|
|
495
598
|
ownerBindSkipped,
|
|
599
|
+
ownerBindInterrupted,
|
|
496
600
|
};
|
|
497
601
|
}
|
|
498
602
|
finally {
|
|
@@ -784,12 +888,17 @@ export async function agentSyncAids() {
|
|
|
784
888
|
return { ok: true, created, template: templateAgent.aid, hotReloaded };
|
|
785
889
|
}
|
|
786
890
|
// ==================== agentReload ====================
|
|
787
|
-
|
|
891
|
+
const AGENT_RELOAD_TIMEOUT_MS = 45_000;
|
|
892
|
+
const AGENT_RESYNC_TIMEOUT_MS = 5 * 60_000;
|
|
893
|
+
export async function agentReload(aid, options = {}) {
|
|
788
894
|
const p = resolvePaths();
|
|
789
895
|
if (!aid) {
|
|
896
|
+
if (options.force) {
|
|
897
|
+
return { ok: false, code: 'INVALID_ARGS', error: '--force requires a target Agent AID' };
|
|
898
|
+
}
|
|
790
899
|
// Full resync
|
|
791
900
|
try {
|
|
792
|
-
const result = await ipcQuery(p.socket, { type: 'evolagent.resync' });
|
|
901
|
+
const result = await ipcQuery(p.socket, { type: 'evolagent.resync' }, AGENT_RESYNC_TIMEOUT_MS);
|
|
793
902
|
if (result === null) {
|
|
794
903
|
return { ok: false, error: 'evolcore 未运行,请先 ec start' };
|
|
795
904
|
}
|
|
@@ -804,27 +913,27 @@ export async function agentReload(aid) {
|
|
|
804
913
|
}
|
|
805
914
|
// Single agent reload
|
|
806
915
|
try {
|
|
807
|
-
const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid });
|
|
916
|
+
const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name: aid, force: options.force === true }, AGENT_RELOAD_TIMEOUT_MS);
|
|
808
917
|
if (result === null) {
|
|
809
918
|
return { ok: false, error: 'evolcore 未运行,请先 ec start 后再 reload' };
|
|
810
919
|
}
|
|
811
920
|
if (result?.ok) {
|
|
812
921
|
return { ok: true };
|
|
813
922
|
}
|
|
814
|
-
return { ok: false, error: result?.error || 'unknown error' };
|
|
923
|
+
return { ok: false, code: result?.code, error: result?.error || 'unknown error' };
|
|
815
924
|
}
|
|
816
925
|
catch {
|
|
817
926
|
return { ok: false, error: 'evolcore 未运行,请先 ec start 后再 reload' };
|
|
818
927
|
}
|
|
819
928
|
}
|
|
820
929
|
// ==================== agentEnable / agentDisable ====================
|
|
821
|
-
export async function agentEnable(aid) {
|
|
822
|
-
return agentSetEnabled(aid, true);
|
|
930
|
+
export async function agentEnable(aid, options = {}) {
|
|
931
|
+
return agentSetEnabled(aid, true, options);
|
|
823
932
|
}
|
|
824
|
-
export async function agentDisable(aid) {
|
|
825
|
-
return agentSetEnabled(aid, false);
|
|
933
|
+
export async function agentDisable(aid, options = {}) {
|
|
934
|
+
return agentSetEnabled(aid, false, options);
|
|
826
935
|
}
|
|
827
|
-
async function agentSetEnabled(aid, enabled) {
|
|
936
|
+
async function agentSetEnabled(aid, enabled, options = {}) {
|
|
828
937
|
const p = resolvePaths();
|
|
829
938
|
let config;
|
|
830
939
|
try {
|
|
@@ -836,15 +945,28 @@ async function agentSetEnabled(aid, enabled) {
|
|
|
836
945
|
if (!config) {
|
|
837
946
|
return { ok: false, error: `Agent "${aid}" not found` };
|
|
838
947
|
}
|
|
948
|
+
const previousEnabled = config.enabled;
|
|
839
949
|
config.enabled = enabled;
|
|
840
950
|
saveAgent(config);
|
|
841
951
|
// Try to hot-reload if daemon is running, but don't fail if it's not
|
|
842
952
|
let reloaded = false;
|
|
843
953
|
try {
|
|
844
|
-
const result = await ipcQuery(p.socket, {
|
|
954
|
+
const result = await ipcQuery(p.socket, {
|
|
955
|
+
type: 'evolagent.reload',
|
|
956
|
+
name: aid,
|
|
957
|
+
force: options.force === true,
|
|
958
|
+
}, AGENT_RELOAD_TIMEOUT_MS);
|
|
845
959
|
if (result?.ok) {
|
|
846
960
|
reloaded = true;
|
|
847
961
|
}
|
|
962
|
+
else if (result?.code === 'AGENT_BUSY') {
|
|
963
|
+
if (previousEnabled === undefined)
|
|
964
|
+
delete config.enabled;
|
|
965
|
+
else
|
|
966
|
+
config.enabled = previousEnabled;
|
|
967
|
+
saveAgent(config);
|
|
968
|
+
return { ok: false, code: result.code, error: result.error || 'Agent is busy' };
|
|
969
|
+
}
|
|
848
970
|
}
|
|
849
971
|
catch {
|
|
850
972
|
// Daemon not running or unreachable — that's ok, config is already saved
|
|
@@ -1074,17 +1196,122 @@ export async function agentRename(aid, name) {
|
|
|
1074
1196
|
return { ok: true, aid, name };
|
|
1075
1197
|
}
|
|
1076
1198
|
// ==================== agentReady ====================
|
|
1199
|
+
async function ensureOfflinePostBootstrapMainSession(aid, config) {
|
|
1200
|
+
const owner = config.owners?.[0];
|
|
1201
|
+
if (!owner)
|
|
1202
|
+
throw new Error(`Agent "${aid}" has no owner for the post-bootstrap main session`);
|
|
1203
|
+
const p = resolvePaths();
|
|
1204
|
+
const aunName = config.channels?.find(channel => channel.type === 'aun')?.name || 'main';
|
|
1205
|
+
const channel = formatChannelKey({ type: 'aun', selfAID: aid, name: aunName });
|
|
1206
|
+
const manager = new SessionManager(p.sessionsDir, new EventBus());
|
|
1207
|
+
const active = await manager.getActiveSession(channel, owner);
|
|
1208
|
+
const ensured = await manager.ensureNewMainSessionForOperation({
|
|
1209
|
+
channel,
|
|
1210
|
+
channelId: owner,
|
|
1211
|
+
projectPath: active?.projectPath
|
|
1212
|
+
|| config.projects?.defaultPath
|
|
1213
|
+
|| path.join(p.root, 'projects', aid.split('.')[0]),
|
|
1214
|
+
baseagent: config.active_baseagent || active?.baseagent || 'claude',
|
|
1215
|
+
operationId: postBootstrapWelcomeOperationId(aid),
|
|
1216
|
+
identityMetadata: {
|
|
1217
|
+
...(active?.metadata ?? {}),
|
|
1218
|
+
channelKey: channel,
|
|
1219
|
+
peerId: owner,
|
|
1220
|
+
},
|
|
1221
|
+
});
|
|
1222
|
+
return ensured.session.id;
|
|
1223
|
+
}
|
|
1077
1224
|
export async function agentReady(aid) {
|
|
1225
|
+
const taskSelfAid = readTaskRuntimeContextFromEnv()?.selfAid;
|
|
1226
|
+
if (taskSelfAid && taskSelfAid.replace(/^@/, '') !== aid.replace(/^@/, '')) {
|
|
1227
|
+
return {
|
|
1228
|
+
ok: false,
|
|
1229
|
+
code: 'SELF_AGENT_REQUIRED',
|
|
1230
|
+
error: `A managed task can only complete bootstrap for itself (${taskSelfAid})`,
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1078
1233
|
const p = resolvePaths();
|
|
1234
|
+
const managedSessionId = process.env.EVOLCORE_SESSION_ID;
|
|
1235
|
+
if (managedSessionId) {
|
|
1236
|
+
const delegationToken = process.env[AGENT_DELEGATION_TOKEN_ENV];
|
|
1237
|
+
if (!delegationToken)
|
|
1238
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: `${AGENT_DELEGATION_TOKEN_ENV} is not set for the active task` };
|
|
1239
|
+
const result = await ipcQuery(p.socket, {
|
|
1240
|
+
type: 'evolagent.bootstrapComplete',
|
|
1241
|
+
aid,
|
|
1242
|
+
sessionId: managedSessionId,
|
|
1243
|
+
delegationToken,
|
|
1244
|
+
}, 30_000);
|
|
1245
|
+
if (!result)
|
|
1246
|
+
return { ok: false, code: 'DAEMON_UNAVAILABLE', error: 'Unable to connect to the evolcore daemon' };
|
|
1247
|
+
return result.ok
|
|
1248
|
+
? { ok: true, aid, reloaded: result.reloaded === true }
|
|
1249
|
+
: { ok: false, code: result.code, error: result.error || 'Failed to complete bootstrap' };
|
|
1250
|
+
}
|
|
1079
1251
|
const config = loadAgent(aid);
|
|
1080
1252
|
if (!config)
|
|
1081
1253
|
return { ok: false, error: `Agent "${aid}" not found` };
|
|
1082
|
-
saveAgent(withLifecycleForWrite(config, 'active'));
|
|
1083
|
-
let reloaded = false;
|
|
1084
1254
|
try {
|
|
1085
|
-
|
|
1086
|
-
|
|
1255
|
+
let controlToken;
|
|
1256
|
+
try {
|
|
1257
|
+
controlToken = fs.readFileSync(p.controlToken, 'utf8').trim() || undefined;
|
|
1258
|
+
}
|
|
1259
|
+
catch { }
|
|
1260
|
+
const result = await ipcQuery(p.socket, { type: 'evolagent.bootstrapComplete', aid, controlToken }, 30_000);
|
|
1261
|
+
if (!result) {
|
|
1262
|
+
const offlineConfig = loadAgent(aid) || config;
|
|
1263
|
+
const lifecycle = normalizeAgentLifecycle(offlineConfig).lifecycle;
|
|
1264
|
+
if (lifecycle !== 'bootstrapping') {
|
|
1265
|
+
if (lifecycle === 'active') {
|
|
1266
|
+
if (hasPendingPostBootstrapWelcomeOutbox(aid)) {
|
|
1267
|
+
try {
|
|
1268
|
+
const sessionId = await ensureOfflinePostBootstrapMainSession(aid, offlineConfig);
|
|
1269
|
+
if (!bindPostBootstrapWelcomeOutboxSession(aid, sessionId)) {
|
|
1270
|
+
throw new Error(`Failed to bind the completion welcome to session ${sessionId}`);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
catch (e) {
|
|
1274
|
+
return {
|
|
1275
|
+
ok: false,
|
|
1276
|
+
code: 'MAIN_SESSION_CREATE_FAILED',
|
|
1277
|
+
error: e instanceof Error ? e.message : 'Failed to recover the post-bootstrap main session',
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
return { ok: true, aid, reloaded: false };
|
|
1282
|
+
}
|
|
1283
|
+
return { ok: false, code: 'INVALID_LIFECYCLE', error: `Agent "${aid}" is ${lifecycle}; only a bootstrapping agent can become ready` };
|
|
1284
|
+
}
|
|
1285
|
+
const owner = offlineConfig.owners?.[0];
|
|
1286
|
+
if (!owner)
|
|
1287
|
+
return { ok: false, code: 'OWNER_REQUIRED', error: `Agent "${aid}" has no owner for the completion welcome` };
|
|
1288
|
+
try {
|
|
1289
|
+
preparePostBootstrapWelcomeOutbox(aid, owner);
|
|
1290
|
+
const sessionId = await ensureOfflinePostBootstrapMainSession(aid, offlineConfig);
|
|
1291
|
+
if (!bindPostBootstrapWelcomeOutboxSession(aid, sessionId)) {
|
|
1292
|
+
throw new Error(`Failed to bind the completion welcome to session ${sessionId}`);
|
|
1293
|
+
}
|
|
1294
|
+
saveAgent(withLifecycleForWrite(offlineConfig, 'active'));
|
|
1295
|
+
}
|
|
1296
|
+
catch (e) {
|
|
1297
|
+
return {
|
|
1298
|
+
ok: false,
|
|
1299
|
+
code: 'READY_PERSIST_FAILED',
|
|
1300
|
+
error: e instanceof Error ? e.message : 'Failed to persist bootstrap completion',
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
return { ok: true, aid, reloaded: false };
|
|
1304
|
+
}
|
|
1305
|
+
if (!result.ok) {
|
|
1306
|
+
return { ok: false, code: result.code, error: result.error || 'Failed to complete bootstrap' };
|
|
1307
|
+
}
|
|
1308
|
+
return { ok: true, aid, reloaded: true };
|
|
1309
|
+
}
|
|
1310
|
+
catch (e) {
|
|
1311
|
+
return {
|
|
1312
|
+
ok: false,
|
|
1313
|
+
code: 'DAEMON_UNAVAILABLE',
|
|
1314
|
+
error: e instanceof Error ? e.message : 'Unable to connect to the EvolCore daemon',
|
|
1315
|
+
};
|
|
1087
1316
|
}
|
|
1088
|
-
catch { /* daemon not running */ }
|
|
1089
|
-
return { ok: true, aid, reloaded };
|
|
1090
1317
|
}
|