evolcore 0.0.20 → 0.0.22
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 +65 -0
- package/README.md +58 -9
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +3 -1
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +393 -108
- package/dist/agents/codex-app-server-client.js +41 -7
- package/dist/agents/codex-runner.js +1292 -220
- package/dist/agents/ecagent-runner.js +171 -61
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +189 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +618 -123
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +98 -123
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +50 -0
- package/dist/cli/trigger-command.js +14 -5
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +19 -5
- package/dist/config/config-manager.js +167 -22
- package/dist/config/contact-book-store.js +25 -3
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +43 -1
- package/dist/core/auth/auth-gateway.js +41 -4
- package/dist/core/auth/authorization-audit.js +216 -8
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +99 -27
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +311 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +288 -80
- package/dist/core/command/menu-protocol.js +1 -1
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +377 -36
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +37 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +33 -3
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +90 -87
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +184 -12
- package/dist/core/message/message-log.js +47 -7
- package/dist/core/message/message-queue.js +227 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +658 -109
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +627 -69
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +27 -14
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +618 -23
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +329 -30
- package/dist/core/session/session-renew.js +37 -13
- package/dist/core/session/session-turn-coordinator.js +16 -5
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +316 -50
- package/dist/ipc.js +459 -29
- package/dist/paths.js +82 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +70 -7
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +13 -3
- package/dist/trigger/scheduler.js +20 -3
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +30 -6
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/dist/utils/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +3 -3
- package/kits/docs/evolcore/agent-create.md +146 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/group-collaboration.md +251 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/message-fragments/item.md +1 -1
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +2 -1
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
|
-
import { kitsTemplatesDir, agentMdPath } from '../paths.js';
|
|
5
|
-
import { resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
4
|
+
import { kitsTemplatesDir, agentMdPath, resolvePaths } from '../paths.js';
|
|
5
|
+
import { buildInitialAgentMd, resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
6
6
|
import { logger } from '../utils/logger.js';
|
|
7
7
|
import { loadAgent, saveAgent } from '../config-store.js';
|
|
8
8
|
import { resolveAgentLifecycle, withLifecycleForWrite } from '../config/lifecycle.js';
|
|
9
9
|
import { renderTemplate } from '../eck/manifest-engine.js';
|
|
10
10
|
import { activeBaseagent } from './model/config-scope.js';
|
|
11
11
|
import { buildEnvelope } from './message/message-utils.js';
|
|
12
|
-
import { BOOTSTRAP_MESSAGE_TTL_MS, bootstrapInitialMessageOperationId } from './bootstrap-messages.js';
|
|
12
|
+
import { BOOTSTRAP_MESSAGE_TTL_MS, BOOTSTRAP_NOTIFICATION_WINDOW_MS, bootstrapInitialMessageOperationId, bootstrapInitialMessageOperationPrefix, bootstrapInitialRetryOperationId, } from './bootstrap-messages.js';
|
|
13
|
+
import { findLatestMessageLogOperation } from './message/message-log.js';
|
|
14
|
+
import * as outbox from '../aun/outbox.js';
|
|
15
|
+
import { chatDirPath } from './session/session-fs-store.js';
|
|
13
16
|
/** Coordinate the write-ahead completion welcome with the lifecycle commit. */
|
|
14
17
|
export async function completeBootstrapWithWelcome(service, aid, welcome, sessions) {
|
|
15
18
|
const lifecycle = service.lifecycleOf(aid);
|
|
@@ -102,6 +105,7 @@ export class BootstrapService {
|
|
|
102
105
|
agentRegistry;
|
|
103
106
|
eventBus;
|
|
104
107
|
inFlight = new Set();
|
|
108
|
+
notifiedInProcess = new Map();
|
|
105
109
|
constructor(agentRegistry, eventBus) {
|
|
106
110
|
this.agentRegistry = agentRegistry;
|
|
107
111
|
this.eventBus = eventBus;
|
|
@@ -161,41 +165,58 @@ export class BootstrapService {
|
|
|
161
165
|
try {
|
|
162
166
|
const agentName = this.resolveAgentDisplayName(aid);
|
|
163
167
|
const baseagent = this.resolveBaseagent(agent, aid);
|
|
168
|
+
const notificationState = this.bootstrapNotificationState({
|
|
169
|
+
aid,
|
|
170
|
+
channelType,
|
|
171
|
+
channelId,
|
|
172
|
+
channelKey: ctx.channelKey,
|
|
173
|
+
});
|
|
174
|
+
const operationId = starting && !notificationState.hasCompletedHistory
|
|
175
|
+
? bootstrapInitialMessageOperationId(aid)
|
|
176
|
+
: bootstrapInitialRetryOperationId(aid);
|
|
177
|
+
const shouldSend = !notificationState.recentlyNotified;
|
|
164
178
|
if (starting) {
|
|
165
179
|
await this.publishAgentMdIfSupported(ctx.adapter, aid, agentName);
|
|
166
180
|
this.setLifecycle(agent, aid, 'bootstrapping');
|
|
167
181
|
lifecycleStarted = true;
|
|
168
182
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
if (shouldSend) {
|
|
184
|
+
const text = this.renderWelcome({
|
|
185
|
+
agentAid: aid,
|
|
186
|
+
agentName,
|
|
187
|
+
ownerName: ctx.recipientName || recipientId,
|
|
188
|
+
channel: channelType || ctx.channelKey,
|
|
189
|
+
baseagent,
|
|
190
|
+
});
|
|
191
|
+
await ctx.adapter.send(buildEnvelope({
|
|
192
|
+
taskId: `bootstrap-${randomBytes(5).toString('hex')}`,
|
|
193
|
+
operationId,
|
|
194
|
+
channel: ctx.adapter.channelKey || ctx.adapter.channelName,
|
|
195
|
+
channelId,
|
|
196
|
+
agentName: aid,
|
|
197
|
+
replyContext: {
|
|
198
|
+
delivery,
|
|
199
|
+
metadata: {
|
|
200
|
+
source: 'daemon',
|
|
201
|
+
persistRequired: true,
|
|
202
|
+
operationId,
|
|
203
|
+
outboxTtl: BOOTSTRAP_MESSAGE_TTL_MS,
|
|
204
|
+
criticalDelivery: true,
|
|
205
|
+
},
|
|
189
206
|
},
|
|
190
|
-
},
|
|
191
|
-
|
|
207
|
+
}), { kind: 'result.text', text, isFinal: true });
|
|
208
|
+
this.notifiedInProcess.set(aid, Date.now());
|
|
209
|
+
}
|
|
192
210
|
if (starting) {
|
|
193
211
|
this.eventBus.publish({ type: 'agent:bootstrap-started', aid, channel: channelType || ctx.channelKey, timestamp: Date.now() });
|
|
194
212
|
logger.info(`[Bootstrap] Started for ${aid} via ${ctx.channelKey} (${ctx.source})`);
|
|
195
213
|
}
|
|
196
|
-
else {
|
|
214
|
+
else if (shouldSend) {
|
|
197
215
|
logger.info(`[Bootstrap] Reconciled initial message for ${aid} via ${ctx.channelKey} (${ctx.source})`);
|
|
198
216
|
}
|
|
217
|
+
else {
|
|
218
|
+
logger.debug(`[Bootstrap] Initial message already sent within the notification window for ${aid}`);
|
|
219
|
+
}
|
|
199
220
|
return true;
|
|
200
221
|
}
|
|
201
222
|
catch (e) {
|
|
@@ -288,11 +309,62 @@ export class BootstrapService {
|
|
|
288
309
|
return 'unknown';
|
|
289
310
|
}
|
|
290
311
|
}
|
|
312
|
+
bootstrapNotificationState(input) {
|
|
313
|
+
const prefix = bootstrapInitialMessageOperationPrefix(input.aid);
|
|
314
|
+
const now = Date.now();
|
|
315
|
+
const cutoff = now - BOOTSTRAP_NOTIFICATION_WINDOW_MS;
|
|
316
|
+
let latestTs = this.notifiedInProcess.get(input.aid) ?? 0;
|
|
317
|
+
let latestLoggedOperationId;
|
|
318
|
+
try {
|
|
319
|
+
const chatDir = chatDirPath(resolvePaths().sessionsDir, input.channelType || 'aun', input.channelId, input.aid, input.channelKey);
|
|
320
|
+
let latestLogged = findLatestMessageLogOperation(chatDir, prefix);
|
|
321
|
+
// Releases before the stable envelope operation ID fix persisted the
|
|
322
|
+
// random bootstrap task ID instead. Recognize that exact legacy shape
|
|
323
|
+
// so an upgrade/restart does not immediately resend the welcome.
|
|
324
|
+
if (!latestLogged) {
|
|
325
|
+
const legacyLogged = findLatestMessageLogOperation(chatDir, 'bootstrap-');
|
|
326
|
+
if (legacyLogged && /^bootstrap-[a-f0-9]{10}$/u.test(legacyLogged.operationId)) {
|
|
327
|
+
latestLogged = legacyLogged;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (latestLogged) {
|
|
331
|
+
latestTs = Math.max(latestTs, latestLogged.ts);
|
|
332
|
+
latestLoggedOperationId = latestLogged.operationId;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// A missing or malformed session path must not prevent bootstrap.
|
|
337
|
+
}
|
|
338
|
+
for (const entry of outbox.findByDedupePrefix(input.aid, prefix, { includeTerminal: true })) {
|
|
339
|
+
// A completed operation no longer needs its queued duplicate. Entries
|
|
340
|
+
// older than the notification window must not be delivered alongside
|
|
341
|
+
// the newly generated reminder operation.
|
|
342
|
+
if (entry.terminal) {
|
|
343
|
+
outbox.remove(input.aid, entry.id);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (entry.dedupeKey === latestLoggedOperationId || entry.ts < cutoff) {
|
|
347
|
+
outbox.remove(input.aid, entry.id);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
latestTs = Math.max(latestTs, entry.ts || 0);
|
|
351
|
+
}
|
|
352
|
+
const recentlyNotified = latestTs >= cutoff;
|
|
353
|
+
if (recentlyNotified)
|
|
354
|
+
this.notifiedInProcess.set(input.aid, latestTs);
|
|
355
|
+
else
|
|
356
|
+
this.notifiedInProcess.delete(input.aid);
|
|
357
|
+
return { recentlyNotified, hasCompletedHistory: !!latestLoggedOperationId };
|
|
358
|
+
}
|
|
291
359
|
async publishAgentMdIfSupported(adapter, aid, fallbackName) {
|
|
292
360
|
if (typeof adapter.uploadAgentMd !== 'function')
|
|
293
361
|
return;
|
|
294
362
|
const existing = fs.existsSync(agentMdPath(aid)) ? fs.readFileSync(agentMdPath(aid), 'utf-8') : '';
|
|
295
|
-
const content = existing.trim() ||
|
|
363
|
+
const content = existing.trim() || buildInitialAgentMd({
|
|
364
|
+
aid,
|
|
365
|
+
name: fallbackName,
|
|
366
|
+
type: 'codeagent',
|
|
367
|
+
});
|
|
296
368
|
try {
|
|
297
369
|
await adapter.uploadAgentMd(content);
|
|
298
370
|
logger.info(`[Bootstrap] Published agent.md for ${aid}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { resolvePaths } from '../../paths.js';
|
|
4
|
-
import { atomicReadJson,
|
|
4
|
+
import { atomicReadJson, stableWriteJson } from '../../utils/atomic-write.js';
|
|
5
5
|
import { cloneCausation, normalizeCausation } from './context.js';
|
|
6
6
|
const DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
7
7
|
const associations = new Map();
|
|
@@ -44,9 +44,12 @@ function ensureLoaded() {
|
|
|
44
44
|
}
|
|
45
45
|
function persist() {
|
|
46
46
|
try {
|
|
47
|
-
// Keep the protected mount target
|
|
48
|
-
//
|
|
49
|
-
|
|
47
|
+
// Keep the protected mount target's inode stable. Bubblewrap binds this
|
|
48
|
+
// H-class file by pathname while constructing its namespace; an atomic
|
|
49
|
+
// rename can otherwise replace the inode during bind/remount and make
|
|
50
|
+
// sandbox initialization fail. Causation state is best-effort and small,
|
|
51
|
+
// so an in-place write is preferable to changing the protected inode.
|
|
52
|
+
stableWriteJson(associationFile(), [...associations].map(([messageId, association]) => ({
|
|
50
53
|
messageId,
|
|
51
54
|
...association,
|
|
52
55
|
})));
|
|
@@ -8,6 +8,7 @@ import { agentProjectRootFromDefaults, deriveAgentProjectPath } from '../../util
|
|
|
8
8
|
import { uploadAvatar } from '../../utils/avatar-upload.js';
|
|
9
9
|
import { agentmdGet, agentmdPut, updateAgentMdFrontmatterName } from '../../aun/aid/agentmd.js';
|
|
10
10
|
import { isValidAid } from '../../aun/aid/validation.js';
|
|
11
|
+
import { isSupportedBaseagent } from '../../agents/baseagent.js';
|
|
11
12
|
const SUPPORTED_AGENT_PATCH_FIELDS = new Set(['aid', 'name', 'avatar', 'active_baseagent', 'baseagents', 'projects', 'owners', 'chatmode', 'channels', 'channelOwners']);
|
|
12
13
|
const HIDDEN_VALUE = '[hidden]';
|
|
13
14
|
const SENSITIVE_CONFIG_KEYS = new Set([
|
|
@@ -95,6 +96,9 @@ function normalizeBaseagentsPatch(value) {
|
|
|
95
96
|
}
|
|
96
97
|
const out = {};
|
|
97
98
|
for (const [name, raw] of Object.entries(value)) {
|
|
99
|
+
if (!isSupportedBaseagent(name)) {
|
|
100
|
+
return { ok: false, error: `不支持的 baseagent: ${name}(可选: claude/codex/gemini/ecagent)` };
|
|
101
|
+
}
|
|
98
102
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
99
103
|
return { ok: false, error: `baseagents.${name} 必须是对象` };
|
|
100
104
|
}
|
|
@@ -286,11 +290,11 @@ function buildCreateProgressOnlyAgent(aid, progress) {
|
|
|
286
290
|
* 失败仅写日志 + create-status,不回传(受理即返回)。
|
|
287
291
|
* agentSet key 对照 cli/agent.ts 的 setNestedValue:
|
|
288
292
|
* model/effort → 'baseagents.<baseagent>.*';chatmode → 'chatmode'(ChatmodeBlock 对象)。 */
|
|
289
|
-
async function runCreateInBackground(opts, w) {
|
|
293
|
+
async function runCreateInBackground(opts, w, service) {
|
|
290
294
|
let curPhase = 'validating'; // 跟踪当前环节,供 catch 兜底时标注正确 phase
|
|
291
295
|
try {
|
|
292
296
|
// onPhase 把 agentCreateNonInteractive 内部环节(0-3、5)映射到进度文件
|
|
293
|
-
const
|
|
297
|
+
const createOptions = {
|
|
294
298
|
aid: opts.aid, name: opts.name, baseagent: opts.baseagent,
|
|
295
299
|
project: opts.project, owner: opts.owner,
|
|
296
300
|
onPhase: (phase, state, detail) => {
|
|
@@ -305,7 +309,10 @@ async function runCreateInBackground(opts, w) {
|
|
|
305
309
|
else if (state === 'failed')
|
|
306
310
|
w.finishFailed(phase, detail ?? 'failed');
|
|
307
311
|
},
|
|
308
|
-
}
|
|
312
|
+
};
|
|
313
|
+
const res = service
|
|
314
|
+
? await service.create(createOptions)
|
|
315
|
+
: await agentCreateNonInteractive(createOptions);
|
|
309
316
|
if (!('ok' in res) || res.ok !== true) {
|
|
310
317
|
// 硬失败:onPhase('failed') 已写终态;这里仅兜底日志(防回调未覆盖的 return 路径)
|
|
311
318
|
const err = res.error;
|
|
@@ -318,17 +325,23 @@ async function runCreateInBackground(opts, w) {
|
|
|
318
325
|
w.begin('applying_config');
|
|
319
326
|
let warned;
|
|
320
327
|
if (opts.model) {
|
|
321
|
-
const r =
|
|
328
|
+
const r = service
|
|
329
|
+
? await service.setConfig(opts.aid, `baseagents.${opts.baseagent}.model`, opts.model)
|
|
330
|
+
: await agentSet(opts.aid, `baseagents.${opts.baseagent}.model`, opts.model);
|
|
322
331
|
if (!('ok' in r) || !r.ok)
|
|
323
332
|
warned = `model: ${r.error}`;
|
|
324
333
|
}
|
|
325
334
|
if (opts.effort) {
|
|
326
|
-
const r =
|
|
335
|
+
const r = service
|
|
336
|
+
? await service.setConfig(opts.aid, `baseagents.${opts.baseagent}.effort`, opts.effort)
|
|
337
|
+
: await agentSet(opts.aid, `baseagents.${opts.baseagent}.effort`, opts.effort);
|
|
327
338
|
if (!('ok' in r) || !r.ok)
|
|
328
339
|
warned = `${warned ? warned + '; ' : ''}effort: ${r.error}`;
|
|
329
340
|
}
|
|
330
341
|
if (opts.chatmode) {
|
|
331
|
-
const r =
|
|
342
|
+
const r = service
|
|
343
|
+
? await service.setConfig(opts.aid, 'chatmode', JSON.stringify(opts.chatmode))
|
|
344
|
+
: await agentSet(opts.aid, 'chatmode', JSON.stringify(opts.chatmode));
|
|
332
345
|
if (!('ok' in r) || !r.ok)
|
|
333
346
|
warned = `${warned ? warned + '; ' : ''}chatmode: ${r.error}`;
|
|
334
347
|
}
|
|
@@ -361,7 +374,7 @@ async function runCreateInBackground(opts, w) {
|
|
|
361
374
|
/** name=agent 的 menu.action 执行。create 使用显式 owner AID;peerId 仅作为控制面鉴权主体。
|
|
362
375
|
* create 受理即返回(D3);delete/enable/disable 同步等结果。
|
|
363
376
|
* 调用方负责传入已兜底的 args.project(见 command-handler 装配)。 */
|
|
364
|
-
export async function execAgentAction(action, args, peerId, eventBus) {
|
|
377
|
+
export async function execAgentAction(action, args, peerId, eventBus, service) {
|
|
365
378
|
const a = args ?? {};
|
|
366
379
|
if (action === 'create') {
|
|
367
380
|
if (!peerId)
|
|
@@ -394,13 +407,15 @@ export async function execAgentAction(action, args, peerId, eventBus) {
|
|
|
394
407
|
project: a.project, owner,
|
|
395
408
|
model, effort, chatmode: a.chatmode,
|
|
396
409
|
eventBus,
|
|
397
|
-
}, progressWriter).catch(e => logger.error(`[agent-control] runCreateInBackground unhandled ${a.aid}: ${e?.message || e}`));
|
|
410
|
+
}, progressWriter, service).catch(e => logger.error(`[agent-control] runCreateInBackground unhandled ${a.aid}: ${e?.message || e}`));
|
|
398
411
|
return { data: { accepted: true, aid: a.aid } };
|
|
399
412
|
}
|
|
400
413
|
if (action === 'delete') {
|
|
401
414
|
if (!a.aid)
|
|
402
415
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
403
|
-
const res =
|
|
416
|
+
const res = service
|
|
417
|
+
? await service.delete(a.aid, false)
|
|
418
|
+
: await agentDelete(a.aid, false);
|
|
404
419
|
if (!('ok' in res) || res.ok !== true)
|
|
405
420
|
return { error: res.error, code: classifyError(res.error) };
|
|
406
421
|
eventBus?.publish({ type: 'agent:deleted', aid: res.aid, purged: res.purged, timestamp: Date.now() });
|
|
@@ -410,7 +425,13 @@ export async function execAgentAction(action, args, peerId, eventBus) {
|
|
|
410
425
|
if (!a.aid)
|
|
411
426
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
412
427
|
const options = { force: a.force === true };
|
|
413
|
-
const res =
|
|
428
|
+
const res = service
|
|
429
|
+
? action === 'enable'
|
|
430
|
+
? await service.enable(a.aid, options.force === true)
|
|
431
|
+
: await service.disable(a.aid, options.force === true)
|
|
432
|
+
: action === 'enable'
|
|
433
|
+
? await agentEnable(a.aid, options)
|
|
434
|
+
: await agentDisable(a.aid, options);
|
|
414
435
|
if (!('ok' in res) || res.ok !== true)
|
|
415
436
|
return { error: res.error, code: res.code || classifyError(res.error) };
|
|
416
437
|
eventBus?.publish({
|
|
@@ -424,7 +445,9 @@ export async function execAgentAction(action, args, peerId, eventBus) {
|
|
|
424
445
|
if (action === 'reload') {
|
|
425
446
|
if (!a.aid)
|
|
426
447
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
427
|
-
const res =
|
|
448
|
+
const res = service
|
|
449
|
+
? await service.reload(a.aid, { force: a.force === true })
|
|
450
|
+
: await agentReload(a.aid, { force: a.force === true });
|
|
428
451
|
if (!('ok' in res) || res.ok !== true)
|
|
429
452
|
return { error: res.error, code: res.code || classifyError(res.error) };
|
|
430
453
|
eventBus?.publish({ type: 'agent:reloaded', aid: a.aid, timestamp: Date.now() });
|
|
@@ -508,6 +531,9 @@ export async function execAgentUpdate(args) {
|
|
|
508
531
|
if (typeof p.active_baseagent !== 'string' || !p.active_baseagent.trim()) {
|
|
509
532
|
return { error: `无效 active_baseagent: ${JSON.stringify(p.active_baseagent)}(必须是非空字符串)`, code: 'INVALID_ARGS' };
|
|
510
533
|
}
|
|
534
|
+
if (!isSupportedBaseagent(p.active_baseagent.trim())) {
|
|
535
|
+
return { error: `不支持的 active_baseagent: ${p.active_baseagent}(可选: claude/codex/gemini/ecagent)`, code: 'INVALID_ARGS' };
|
|
536
|
+
}
|
|
511
537
|
config.active_baseagent = p.active_baseagent.trim();
|
|
512
538
|
touched = true;
|
|
513
539
|
}
|
|
@@ -576,7 +602,7 @@ export async function execAgentUpdate(args) {
|
|
|
576
602
|
/**
|
|
577
603
|
* project 解析与 ec agent create 保持一致:
|
|
578
604
|
* 显式值 > 默认项目根目录 + AID 前缀。
|
|
579
|
-
* 默认项目根目录由 defaults.projects.
|
|
605
|
+
* 默认项目根目录由 defaults.projects.defaultPath 或运行时根目录推导。
|
|
580
606
|
*/
|
|
581
607
|
export function resolveProjectPath(explicit, aid, defaults) {
|
|
582
608
|
if (explicit && explicit.trim())
|
|
@@ -585,13 +611,15 @@ export function resolveProjectPath(explicit, aid, defaults) {
|
|
|
585
611
|
return deriveAgentProjectPath(root, aid);
|
|
586
612
|
}
|
|
587
613
|
/** name=agent 的 menu.query:查单个 agent 详情,附构建进度(D3)。 */
|
|
588
|
-
export async function execAgentQuery(args) {
|
|
614
|
+
export async function execAgentQuery(args, service) {
|
|
589
615
|
const aid = args?.aid;
|
|
590
616
|
if (!aid)
|
|
591
617
|
return { error: '缺少 aid', code: 'INVALID_ARGS' };
|
|
592
618
|
const agentDir = path.join(resolvePaths().agentsDir, aid);
|
|
593
619
|
const progress = readCreateStatus(agentDir);
|
|
594
|
-
const res =
|
|
620
|
+
const res = service
|
|
621
|
+
? await service.show(aid)
|
|
622
|
+
: await agentShow(aid);
|
|
595
623
|
if (!('ok' in res) || res.ok !== true) {
|
|
596
624
|
const code = classifyError(res.error);
|
|
597
625
|
if (code === 'NOT_FOUND' && progress)
|
|
@@ -616,9 +644,21 @@ export async function execAgentQuery(args) {
|
|
|
616
644
|
return { data: progress ? { ...data, createProgress: progress } : data };
|
|
617
645
|
}
|
|
618
646
|
/** name=agent 的 menu.options:列出 agent(enabled 默认 / all) */
|
|
619
|
-
export async function execAgentOptions(args) {
|
|
647
|
+
export async function execAgentOptions(args, service) {
|
|
620
648
|
const scope = args?.options === 'all' ? 'all' : 'enabled';
|
|
621
|
-
const res =
|
|
649
|
+
const res = service
|
|
650
|
+
? { ok: true, agents: service.list().map(info => ({
|
|
651
|
+
aid: info.aid || info.name,
|
|
652
|
+
name: info.name,
|
|
653
|
+
personalName: info.personalName || null,
|
|
654
|
+
status: info.status || 'stopped',
|
|
655
|
+
channels: info.channels || [],
|
|
656
|
+
projectPath: info.projectPath ? toPosix(info.projectPath) : null,
|
|
657
|
+
baseagent: info.baseagent || null,
|
|
658
|
+
model: info.model || null,
|
|
659
|
+
lastActivity: info.lastActivity || null,
|
|
660
|
+
})) }
|
|
661
|
+
: await agentList();
|
|
622
662
|
if (!('ok' in res) || res.ok !== true)
|
|
623
663
|
return { error: res.error, code: classifyError(res.error) };
|
|
624
664
|
const agents = scope === 'all'
|