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/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { CodexSessionFileAdapter } from './core/session/adapters/codex-session-f
|
|
|
14
14
|
import { GeminiSessionFileAdapter } from './core/session/adapters/gemini-session-file-adapter.js';
|
|
15
15
|
import { EcagentSessionFileAdapter } from './core/session/adapters/ecagent-session-file-adapter.js';
|
|
16
16
|
import { loadDefaults, loadAllAgents, migrateIdentitiesIfNeeded, loadDaemonConfig, initializeEckSnapshotsConfig } from './config-store.js';
|
|
17
|
-
import { ConfigTarget, initConfigManager, onConfigWrite } from './config/config-manager.js';
|
|
17
|
+
import { ConfigTarget, initConfigManager, onConfigWrite, read as readConfig } from './config/config-manager.js';
|
|
18
18
|
import { ensureRoleConfigV4OnStartup } from './config/role-config-v4-startup.js';
|
|
19
19
|
import { ensureRoleConfigV5OnStartup } from './config/role-config-v5-startup.js';
|
|
20
20
|
import { ensureContactBookV2OnStartup } from './config/contact-book-v2-startup.js';
|
|
@@ -45,12 +45,12 @@ import { buildEnvelope } from './core/message/message-utils.js';
|
|
|
45
45
|
import { ResponseEngine } from './core/message/response-engine.js';
|
|
46
46
|
import { MessageQueue } from './core/message/message-queue.js';
|
|
47
47
|
import { MessageBridge } from './core/message/message-bridge.js';
|
|
48
|
-
import { MenuRequestDeduper, hasValidMenuId, menuFailure, menuPayloadFingerprint, parseMenuControl, validateMenuRequest } from './core/command/menu-protocol.js';
|
|
49
|
-
import { evaluateEvolMenuVersionGate, evolMenuResponseTransportMetadata } from './core/command/evol-menu-version-gate.js';
|
|
48
|
+
import { evaluateEvolMenuVersionGate, evolMenuResponseTransportMetadata, MenuRequestDeduper, hasValidMenuId, menuFailure, menuPayloadFingerprint, parseMenuControl, validateMenuRequest } from './core/command/menu-protocol.js';
|
|
50
49
|
import { readInstalledEvolcoreVersion } from './utils/evolcore-version.js';
|
|
51
50
|
import { recoverAllRoleMutationsSync } from './core/command/role-menu.js';
|
|
52
51
|
import { HandoffRuntime } from './core/handoff/runtime.js';
|
|
53
|
-
import { BootstrapService } from './core/bootstrap-service.js';
|
|
52
|
+
import { BootstrapService, completeBootstrapWithWelcome } from './core/bootstrap-service.js';
|
|
53
|
+
import { postBootstrapWelcomeOperationId } from './core/bootstrap-messages.js';
|
|
54
54
|
import { MessageCache } from './core/message/message-cache.js';
|
|
55
55
|
import { CommandHandler, isProcessLevelOwner } from './core/command/command-handler.js';
|
|
56
56
|
import { EventBus } from './core/event-bus.js';
|
|
@@ -62,10 +62,13 @@ import { InteractionRouter } from './core/interaction-router.js';
|
|
|
62
62
|
import { registerAdapterInteractions } from './core/interaction-registration.js';
|
|
63
63
|
import { AgentDelegationRegistry, authorizeDelegatedAunMsgSend } from './core/auth/agent-delegation.js';
|
|
64
64
|
import { authorizeOperation, buildAuthSubject } from './core/auth/auth-gateway.js';
|
|
65
|
+
import { parsePeerKey } from './core/relation/peer-identity.js';
|
|
65
66
|
import { isHClassPath } from './core/protected-paths.js';
|
|
66
67
|
import { ChannelLoader, tryParseChannelKey } from './core/channel-loader.js';
|
|
67
68
|
import { AgentLoader } from './core/baseagent-loader.js';
|
|
68
69
|
import { EvolAgentRegistry } from './core/evolagent-registry.js';
|
|
70
|
+
import { createRunnerReloadTransaction, disposeAgentInstances } from './core/runner-reload-transaction.js';
|
|
71
|
+
import { AgentReloadBusyError, AgentReloadCoordinator } from './core/agent-reload-coordinator.js';
|
|
69
72
|
import { buildReloadHooks } from './core/channel-loader.js';
|
|
70
73
|
import { IpcServer } from './ipc.js';
|
|
71
74
|
import { logger, setLogLevel } from './utils/logger.js';
|
|
@@ -88,6 +91,7 @@ import { validateModelSelectionForRole } from './core/model/model-permission.js'
|
|
|
88
91
|
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride, } from './core/role/runtime-policy.js';
|
|
89
92
|
import { atomicWriteJson } from './core/session/session-fs-store.js';
|
|
90
93
|
import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from './core/message/message-log.js';
|
|
94
|
+
import { normalizeAunMentionEntries } from './aun/msg/mention-schema.js';
|
|
91
95
|
import { MAIN_PACKAGE_NAME } from './product.js';
|
|
92
96
|
import fs from 'fs';
|
|
93
97
|
import crypto from 'crypto';
|
|
@@ -205,7 +209,7 @@ function originFromActorSession(session, authenticatedPeerId, authenticatedChann
|
|
|
205
209
|
}
|
|
206
210
|
function seedUpgradeCheckTrigger(manager, owner) {
|
|
207
211
|
const now = Date.now();
|
|
208
|
-
const scriptName = 'upgrade-check.
|
|
212
|
+
const scriptName = 'upgrade-check.cjs';
|
|
209
213
|
const definition = normalizeTriggerDefinition({
|
|
210
214
|
$schema_version: 3.1,
|
|
211
215
|
id: '__upgrade-check',
|
|
@@ -232,7 +236,7 @@ function seedUpgradeCheckTrigger(manager, owner) {
|
|
|
232
236
|
type: 'script',
|
|
233
237
|
script: {
|
|
234
238
|
path: scriptName,
|
|
235
|
-
runtime: '
|
|
239
|
+
runtime: 'node',
|
|
236
240
|
timeoutMs: 60_000,
|
|
237
241
|
},
|
|
238
242
|
permissionMode: 'bypass',
|
|
@@ -258,67 +262,107 @@ function seedUpgradeCheckTrigger(manager, owner) {
|
|
|
258
262
|
const cliPath = path.join(packageRoot, 'dist', 'cli', 'index.js');
|
|
259
263
|
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
260
264
|
const devMode = fs.existsSync(path.join(packageRoot, 'src', 'index.ts'));
|
|
261
|
-
fs.writeFileSync(scriptPath,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
265
|
+
fs.writeFileSync(scriptPath, `#!/usr/bin/env node
|
|
266
|
+
const { existsSync, readFileSync } = require('node:fs');
|
|
267
|
+
const { spawn } = require('node:child_process');
|
|
268
|
+
|
|
269
|
+
const CLI_PATH = ${JSON.stringify(cliPath)};
|
|
270
|
+
const PACKAGE_JSON = ${JSON.stringify(packageJsonPath)};
|
|
271
|
+
const PACKAGE_NAME = ${JSON.stringify(MAIN_PACKAGE_NAME)};
|
|
272
|
+
const DEV_MODE = ${devMode ? 'true' : 'false'};
|
|
273
|
+
|
|
274
|
+
function emit(outcome, text) {
|
|
275
|
+
process.stdout.write(JSON.stringify({ outcome, text, files: [] }));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function compareVersions(a, b) {
|
|
279
|
+
const pa = a.split('-')[0].split('.').map(Number);
|
|
280
|
+
const pb = b.split('-')[0].split('.').map(Number);
|
|
281
|
+
const count = Math.max(pa.length, pb.length);
|
|
282
|
+
for (let i = 0; i < count; i += 1) {
|
|
283
|
+
const av = Number.isFinite(pa[i]) ? pa[i] : 0;
|
|
284
|
+
const bv = Number.isFinite(pb[i]) ? pb[i] : 0;
|
|
285
|
+
if (av < bv) return -1;
|
|
286
|
+
if (av > bv) return 1;
|
|
287
|
+
}
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function latestVersion() {
|
|
292
|
+
const controller = new AbortController();
|
|
293
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
294
|
+
try {
|
|
295
|
+
const response = await fetch('https://registry.npmjs.org/' + encodeURIComponent(PACKAGE_NAME) + '/latest', {
|
|
296
|
+
signal: controller.signal,
|
|
297
|
+
headers: { Accept: 'application/json' },
|
|
298
|
+
});
|
|
299
|
+
if (!response.ok) return null;
|
|
300
|
+
const data = await response.json();
|
|
301
|
+
return typeof data.version === 'string' ? data.version : null;
|
|
302
|
+
} finally {
|
|
303
|
+
clearTimeout(timer);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function main() {
|
|
308
|
+
if (DEV_MODE) {
|
|
309
|
+
emit('noop', 'upgrade check skipped in dev mode');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let localVersion;
|
|
314
|
+
try {
|
|
315
|
+
localVersion = JSON.parse(readFileSync(PACKAGE_JSON, 'utf8')).version;
|
|
316
|
+
} catch {
|
|
317
|
+
emit('error', 'failed to read local ec version');
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (typeof localVersion !== 'string' || !localVersion) {
|
|
321
|
+
emit('error', 'failed to read local ec version');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let remoteVersion;
|
|
326
|
+
try {
|
|
327
|
+
remoteVersion = await latestVersion();
|
|
328
|
+
} catch {
|
|
329
|
+
remoteVersion = null;
|
|
330
|
+
}
|
|
331
|
+
if (!remoteVersion) {
|
|
332
|
+
emit('error', 'failed to check evolcore latest version');
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (compareVersions(localVersion, remoteVersion) >= 0) {
|
|
336
|
+
emit('noop', 'evolcore is already up to date (' + localVersion + '; latest ' + remoteVersion + ')');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!existsSync(CLI_PATH)) {
|
|
340
|
+
emit('error', 'restart-monitor entry not found: ' + CLI_PATH);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
const child = spawn(process.execPath, [CLI_PATH, 'restart-monitor'], {
|
|
346
|
+
detached: true,
|
|
347
|
+
stdio: 'ignore',
|
|
348
|
+
windowsHide: process.platform === 'win32',
|
|
349
|
+
env: { ...process.env },
|
|
350
|
+
});
|
|
351
|
+
child.unref();
|
|
352
|
+
emit('success', 'evolcore upgrade available: ' + localVersion + ' -> ' + remoteVersion + '; restart-monitor started');
|
|
353
|
+
} catch (error) {
|
|
354
|
+
emit('error', 'failed to start restart-monitor: ' + (error && error.message ? error.message : String(error)));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
main().catch((error) => {
|
|
359
|
+
emit('error', 'upgrade check failed: ' + (error && error.message ? error.message : String(error)));
|
|
360
|
+
});
|
|
361
|
+
`);
|
|
362
|
+
// Remove the pre-Node script left by older installations so it cannot be
|
|
363
|
+
// selected by a stale trigger definition or run manually by accident.
|
|
364
|
+
fs.rmSync(path.join(dir, 'upgrade-check.sh'), { force: true });
|
|
365
|
+
fs.rmSync(path.join(dir, 'upgrade-check.js'), { force: true });
|
|
322
366
|
fs.chmodSync(scriptPath, 0o755);
|
|
323
367
|
atomicWriteJson(manager.definitionPath(definition.id), definition);
|
|
324
368
|
fs.rmSync(manager.activePath(definition.id), { force: true });
|
|
@@ -336,7 +380,9 @@ function removeLegacyAgentUpgradeCheck(manager) {
|
|
|
336
380
|
return;
|
|
337
381
|
const isLegacySystemOrigin = existing.origin?.channelKey === '__system__' || existing.origin?.channelKey === 'daemon';
|
|
338
382
|
const isUpgradeScript = existing.execution.type === 'script'
|
|
339
|
-
&& existing.execution.script?.path === 'upgrade-check.sh'
|
|
383
|
+
&& (existing.execution.script?.path === 'upgrade-check.sh'
|
|
384
|
+
|| existing.execution.script?.path === 'upgrade-check.js'
|
|
385
|
+
|| existing.execution.script?.path === 'upgrade-check.cjs');
|
|
340
386
|
if (!isLegacySystemOrigin || !isUpgradeScript)
|
|
341
387
|
return;
|
|
342
388
|
fs.rmSync(manager.triggerDir('__upgrade-check'), { recursive: true, force: true });
|
|
@@ -394,6 +440,13 @@ async function runBindBootstrapDaemon(daemonCfg) {
|
|
|
394
440
|
await controlChannel.sendMessage(opts.channelId, JSON.stringify(response));
|
|
395
441
|
}
|
|
396
442
|
});
|
|
443
|
+
const controlToken = crypto.randomBytes(32).toString('base64url');
|
|
444
|
+
fs.mkdirSync(path.dirname(resolvePaths().controlToken), { recursive: true });
|
|
445
|
+
fs.writeFileSync(resolvePaths().controlToken, controlToken, { mode: 0o600 });
|
|
446
|
+
try {
|
|
447
|
+
fs.chmodSync(resolvePaths().controlToken, 0o600);
|
|
448
|
+
}
|
|
449
|
+
catch { }
|
|
397
450
|
const ipcServer = new IpcServer(resolvePaths().socket, () => ({
|
|
398
451
|
pid: process.pid,
|
|
399
452
|
uptime: Math.round(process.uptime() * 1000),
|
|
@@ -415,6 +468,7 @@ async function runBindBootstrapDaemon(daemonCfg) {
|
|
|
415
468
|
};
|
|
416
469
|
})(),
|
|
417
470
|
}));
|
|
471
|
+
ipcServer.setControlToken(controlToken);
|
|
418
472
|
ipcServer.setBindExecutor({
|
|
419
473
|
begin: (cmd) => bindService.begin(cmd),
|
|
420
474
|
status: (taskId) => bindService.status(taskId),
|
|
@@ -974,6 +1028,7 @@ async function main() {
|
|
|
974
1028
|
}
|
|
975
1029
|
const channelInstances = evolagentInstances;
|
|
976
1030
|
logger.info(`✓ Created ${channelInstances.length} channel instance(s)`);
|
|
1031
|
+
let bootstrapService;
|
|
977
1032
|
const contactRequestExpiryTimer = setInterval(() => {
|
|
978
1033
|
for (const agent of agentRegistry.runnableAgents()) {
|
|
979
1034
|
void expirePendingContactRequests(agent.aid).catch(error => {
|
|
@@ -988,6 +1043,35 @@ async function main() {
|
|
|
988
1043
|
getAvailableBaseagents: detectAvailableBaseagentsForBind,
|
|
989
1044
|
getUptimeSeconds: () => Math.floor(process.uptime()),
|
|
990
1045
|
onDaemonOwnersUpdated: (owners) => { processLevelOwners = owners; },
|
|
1046
|
+
onAgentOwnerUpdated: (agentAid, ownerAid) => {
|
|
1047
|
+
const service = bootstrapService;
|
|
1048
|
+
if (!service)
|
|
1049
|
+
return;
|
|
1050
|
+
const instance = channelInstances.find(candidate => {
|
|
1051
|
+
const owner = agentRegistry.resolveByChannel(candidate.adapter.channelKey)
|
|
1052
|
+
?? agentRegistry.resolveByChannel(candidate.adapter.channelName);
|
|
1053
|
+
return owner?.aid === agentAid;
|
|
1054
|
+
});
|
|
1055
|
+
if (!instance)
|
|
1056
|
+
return;
|
|
1057
|
+
eventBus.publish({
|
|
1058
|
+
type: 'channel:owner-bound',
|
|
1059
|
+
channel: instance.channelType || instance.adapter.channelName,
|
|
1060
|
+
channelName: instance.adapter.channelName,
|
|
1061
|
+
userId: ownerAid,
|
|
1062
|
+
});
|
|
1063
|
+
void service.tryStartBootstrap({
|
|
1064
|
+
adapter: instance.adapter,
|
|
1065
|
+
channelKey: instance.adapter.channelKey,
|
|
1066
|
+
channelType: instance.channelType,
|
|
1067
|
+
agentAid,
|
|
1068
|
+
recipientId: ownerAid,
|
|
1069
|
+
channelId: instance.channelType === 'aun' ? ownerAid : undefined,
|
|
1070
|
+
source: 'owner-bound',
|
|
1071
|
+
}).catch((error) => {
|
|
1072
|
+
logger.warn(`[Bootstrap] Owner-bound start failed for ${agentAid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1073
|
+
});
|
|
1074
|
+
},
|
|
991
1075
|
})
|
|
992
1076
|
: null;
|
|
993
1077
|
bindService?.startCleanup();
|
|
@@ -1097,6 +1181,7 @@ async function main() {
|
|
|
1097
1181
|
return { ok: result.ok, message_id: result.message_id, error: result.error };
|
|
1098
1182
|
}, undefined, { queueTtlMs: taskExecutionTtlMs });
|
|
1099
1183
|
responseEngine.setHandoffRuntime(handoffRuntime);
|
|
1184
|
+
cmdHandler.setHandoffRuntime(handoffRuntime);
|
|
1100
1185
|
// Trigger runtime: daemon-level script + feedback scheduler.
|
|
1101
1186
|
const triggerSchedulers = new Map();
|
|
1102
1187
|
const targetSessionLocks = new TargetSessionLockRegistry();
|
|
@@ -1541,7 +1626,7 @@ async function main() {
|
|
|
1541
1626
|
msgBridge.setInteractionRouter(interactionRouter);
|
|
1542
1627
|
msgBridge.setContactBindRuntimeChecker(isContactBindChannelReady);
|
|
1543
1628
|
msgBridge.setAidStatsCollector(aidStatsCollector);
|
|
1544
|
-
|
|
1629
|
+
bootstrapService = new BootstrapService(agentRegistry, eventBus);
|
|
1545
1630
|
msgBridge.setBootstrapService(bootstrapService);
|
|
1546
1631
|
msgBridge.setHandoffRuntime(handoffRuntime);
|
|
1547
1632
|
// ── Channel instance registration (shared by startup and hot-load) ──
|
|
@@ -1961,7 +2046,7 @@ async function main() {
|
|
|
1961
2046
|
const delegation = agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1962
2047
|
if (!delegation.ok)
|
|
1963
2048
|
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1964
|
-
return cmdHandler.handleCtl(cmd, sessionId);
|
|
2049
|
+
return cmdHandler.handleCtl(cmd, sessionId, delegation.grant);
|
|
1965
2050
|
});
|
|
1966
2051
|
// Register every IPC executor/provider before exposing the endpoint. The
|
|
1967
2052
|
// function declaration is hoisted, while its invocation remains here so a
|
|
@@ -2048,10 +2133,11 @@ async function main() {
|
|
|
2048
2133
|
}
|
|
2049
2134
|
else {
|
|
2050
2135
|
try {
|
|
2051
|
-
const ecwebProc = spawn(
|
|
2136
|
+
const ecwebProc = spawn(process.execPath, [ecwebEntry], {
|
|
2052
2137
|
cwd: ecwebPath,
|
|
2053
2138
|
detached: true,
|
|
2054
2139
|
stdio: 'ignore',
|
|
2140
|
+
windowsHide: platform.isWindows,
|
|
2055
2141
|
env: { ...process.env, EVOLCORE_HOME: resolvePaths().root }
|
|
2056
2142
|
});
|
|
2057
2143
|
ecwebProc.unref();
|
|
@@ -2227,6 +2313,9 @@ async function main() {
|
|
|
2227
2313
|
const recoveryAids = triggerStartupAgents.map(agent => agent.aid);
|
|
2228
2314
|
for (const aid of recoveryAids) {
|
|
2229
2315
|
try {
|
|
2316
|
+
const discarded = await handoffRuntime.discardExpired([aid]);
|
|
2317
|
+
if (discarded > 0)
|
|
2318
|
+
logger.info(`[Handoff] discarded ${discarded} expired handoff(s) for ${aid}`);
|
|
2230
2319
|
const expired = handoffRuntime.expireQueued([aid]);
|
|
2231
2320
|
if (expired > 0)
|
|
2232
2321
|
logger.info(`[Handoff] expired ${expired} stale queued delivery item(s) for ${aid}`);
|
|
@@ -2312,6 +2401,60 @@ async function main() {
|
|
|
2312
2401
|
function configureIpc() {
|
|
2313
2402
|
// M3: direct call (not cast) — wire EvolAgentRegistry into IPC for evolagent.* handlers
|
|
2314
2403
|
ipcServer.setAgentRegistry(agentRegistry);
|
|
2404
|
+
ipcServer.setControlToken(triggerControlToken);
|
|
2405
|
+
ipcServer.setBootstrapCompleteExecutor(async (aid, auth) => {
|
|
2406
|
+
if (auth?.sessionId) {
|
|
2407
|
+
const authorization = await authorizeManagedAgentOperation({
|
|
2408
|
+
sessionId: auth.sessionId,
|
|
2409
|
+
delegationToken: auth.delegationToken,
|
|
2410
|
+
delegationCommandHash: auth.delegationCommandHash,
|
|
2411
|
+
targetAid: aid,
|
|
2412
|
+
}, 'agent.bootstrapComplete');
|
|
2413
|
+
if (!authorization.ok)
|
|
2414
|
+
return authorization;
|
|
2415
|
+
}
|
|
2416
|
+
else {
|
|
2417
|
+
const providedToken = typeof auth?.controlToken === 'string' ? Buffer.from(auth.controlToken) : undefined;
|
|
2418
|
+
const expectedToken = Buffer.from(triggerControlToken);
|
|
2419
|
+
if (!providedToken
|
|
2420
|
+
|| providedToken.length !== expectedToken.length
|
|
2421
|
+
|| !crypto.timingSafeEqual(providedToken, expectedToken)) {
|
|
2422
|
+
return { ok: false, code: 'INVALID_CONTROL_TOKEN', error: 'valid local control token is required' };
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
const aunInstance = channelInstances.find(candidate => {
|
|
2426
|
+
if (candidate.channelType !== 'aun')
|
|
2427
|
+
return false;
|
|
2428
|
+
const owner = agentRegistry.resolveByChannel(candidate.adapter.channelKey)
|
|
2429
|
+
?? agentRegistry.resolveByChannel(candidate.adapter.channelName);
|
|
2430
|
+
return owner?.aid === aid;
|
|
2431
|
+
});
|
|
2432
|
+
const welcome = aunInstance?.channel;
|
|
2433
|
+
const ownerAid = getFirstStaticAgentOwner(aid);
|
|
2434
|
+
const owningAgent = agentRegistry.get(aid);
|
|
2435
|
+
const sessions = aunInstance && ownerAid && owningAgent
|
|
2436
|
+
? {
|
|
2437
|
+
ensurePostBootstrapMainSession: async () => {
|
|
2438
|
+
const channel = aunInstance.adapter.channelName;
|
|
2439
|
+
const active = await sessionManager.getActiveSession(channel, ownerAid);
|
|
2440
|
+
const ensured = await sessionManager.ensureNewMainSessionForOperation({
|
|
2441
|
+
channel,
|
|
2442
|
+
channelId: ownerAid,
|
|
2443
|
+
projectPath: active?.projectPath || owningAgent.projectPath || defaultProjectPath,
|
|
2444
|
+
baseagent: owningAgent.baseagent || active?.baseagent || primaryBaseagent,
|
|
2445
|
+
operationId: postBootstrapWelcomeOperationId(aid),
|
|
2446
|
+
identityMetadata: {
|
|
2447
|
+
...(active?.metadata ?? {}),
|
|
2448
|
+
channelKey: aunInstance.adapter.channelKey,
|
|
2449
|
+
peerId: ownerAid,
|
|
2450
|
+
},
|
|
2451
|
+
});
|
|
2452
|
+
return { sessionId: ensured.session.id, created: ensured.created };
|
|
2453
|
+
},
|
|
2454
|
+
}
|
|
2455
|
+
: undefined;
|
|
2456
|
+
return completeBootstrapWithWelcome(bootstrapService, aid, welcome, sessions);
|
|
2457
|
+
});
|
|
2315
2458
|
ipcServer.setDingtalkContactBindExecutor({
|
|
2316
2459
|
register: (cmd) => registerPendingDingtalkContactBind(cmd),
|
|
2317
2460
|
isChannelReady: isContactBindChannelReady,
|
|
@@ -2335,6 +2478,10 @@ async function main() {
|
|
|
2335
2478
|
ipcServer.setMenuExecutor((payload, auth) => cmdHandler.execMenuForEcweb(payload, auth));
|
|
2336
2479
|
ipcServer.setConfigOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleConfigOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2337
2480
|
ipcServer.setContactOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleContactOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2481
|
+
ipcServer.setAidOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleAidOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2482
|
+
ipcServer.setGroupOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleGroupOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2483
|
+
ipcServer.setMsgOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleMsgOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2484
|
+
ipcServer.setFsOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleFsOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2338
2485
|
cmdHandler.setDaemonStatusProvider(() => {
|
|
2339
2486
|
const aidState = controlChannel?.getAidState?.();
|
|
2340
2487
|
return {
|
|
@@ -2403,30 +2550,82 @@ async function main() {
|
|
|
2403
2550
|
aidStatsCollector.recordOutbound(params.aid, params.toPeer, Buffer.byteLength(params.text || '', 'utf-8'), params.text, false, params.encrypt, params.chatmode, 'send');
|
|
2404
2551
|
});
|
|
2405
2552
|
ipcServer.setTaskRuntimeContextProvider(({ sessionId }) => responseEngine.getTaskRuntimeContext(sessionId));
|
|
2406
|
-
ipcServer.
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2553
|
+
ipcServer.setCodexPreToolUseExecutor(async (params) => {
|
|
2554
|
+
for (const runner of new Set(agentMap.values())) {
|
|
2555
|
+
if (typeof runner?.evaluatePreToolUse !== 'function')
|
|
2556
|
+
continue;
|
|
2557
|
+
const result = await runner.evaluatePreToolUse(params.threadId, params.toolName, params.toolInput);
|
|
2558
|
+
if (result.applicable)
|
|
2559
|
+
return result;
|
|
2560
|
+
}
|
|
2561
|
+
return {
|
|
2562
|
+
ok: false,
|
|
2563
|
+
applicable: true,
|
|
2564
|
+
decision: 'deny',
|
|
2565
|
+
reason: 'No active EvolCore Codex runner accepted this managed hook request',
|
|
2566
|
+
};
|
|
2567
|
+
});
|
|
2568
|
+
const resolveManagedHandoffScope = async (params, operation) => {
|
|
2569
|
+
const delegation = agentDelegationRegistry.validate(params.delegationToken, params.sessionId, params.delegationCommandHash);
|
|
2570
|
+
if (!delegation.ok)
|
|
2571
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
2410
2572
|
const runtime = responseEngine.getTaskRuntimeContext(params.sessionId);
|
|
2411
2573
|
const session = await sessionManager.getSessionById(params.sessionId);
|
|
2412
2574
|
const selfAid = runtime?.selfAid || session?.selfAID;
|
|
2413
|
-
if (!selfAid)
|
|
2414
|
-
return { ok: false, code: '
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2575
|
+
if (!selfAid || !session) {
|
|
2576
|
+
return { ok: false, code: 'HANDOFF_CALL_SESSION_INVALID', error: 'current session is invalid' };
|
|
2577
|
+
}
|
|
2578
|
+
if (delegation.grant.selfAid !== selfAid) {
|
|
2579
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegation self agent does not match current session' };
|
|
2580
|
+
}
|
|
2581
|
+
if (params.agent && params.agent !== selfAid) {
|
|
2582
|
+
return { ok: false, code: 'HANDOFF_AGENT_SCOPE_MISMATCH', error: 'agent does not match the current session' };
|
|
2583
|
+
}
|
|
2584
|
+
let conversationId;
|
|
2585
|
+
try {
|
|
2586
|
+
conversationId = parsePeerKey(delegation.grant.peerKey).channelId;
|
|
2587
|
+
}
|
|
2588
|
+
catch {
|
|
2589
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegation contains an invalid relation key' };
|
|
2590
|
+
}
|
|
2591
|
+
const subject = buildAuthSubject({
|
|
2592
|
+
selfAid,
|
|
2593
|
+
actorId: delegation.grant.actorId,
|
|
2594
|
+
channel: delegation.grant.channel,
|
|
2595
|
+
channelType: delegation.grant.channelType,
|
|
2596
|
+
channelId: conversationId,
|
|
2597
|
+
chatType: delegation.grant.chatType,
|
|
2598
|
+
conversationId,
|
|
2599
|
+
processOwners: daemonCfg.owners ?? [],
|
|
2600
|
+
});
|
|
2601
|
+
const managementRole = isManagementRole(subject.role);
|
|
2602
|
+
const decision = authorizeOperation({
|
|
2603
|
+
source: 'agent-tool',
|
|
2604
|
+
subject,
|
|
2605
|
+
intent: {
|
|
2606
|
+
operation,
|
|
2607
|
+
scope: managementRole ? 'agent' : 'relation',
|
|
2608
|
+
source: 'agent-tool',
|
|
2609
|
+
args: {
|
|
2610
|
+
self: selfAid,
|
|
2611
|
+
peer: conversationId,
|
|
2612
|
+
peerKey: delegation.grant.peerKey,
|
|
2613
|
+
},
|
|
2614
|
+
},
|
|
2615
|
+
auditMetadata: { taskId: delegation.grant.taskId, messageId: delegation.grant.messageId },
|
|
2616
|
+
});
|
|
2617
|
+
if (!decision.allow) {
|
|
2618
|
+
return {
|
|
2619
|
+
ok: false,
|
|
2620
|
+
code: decision.command?.code ?? 'ROLE_ACCESS_DENIED',
|
|
2621
|
+
error: decision.reason,
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
return { ok: true, aid: selfAid, currentSessionId: params.sessionId, managementRole };
|
|
2625
|
+
};
|
|
2626
|
+
const resolveHandoffQueryScope = async (params, operation) => {
|
|
2419
2627
|
if (params.sessionId) {
|
|
2420
|
-
|
|
2421
|
-
const session = await sessionManager.getSessionById(params.sessionId);
|
|
2422
|
-
const selfAid = runtime?.selfAid || session?.selfAID;
|
|
2423
|
-
if (!selfAid) {
|
|
2424
|
-
return { ok: false, code: 'HANDOFF_CALL_SESSION_INVALID', error: 'current session is invalid' };
|
|
2425
|
-
}
|
|
2426
|
-
if (params.agent && params.agent !== selfAid) {
|
|
2427
|
-
return { ok: false, code: 'HANDOFF_AGENT_SCOPE_MISMATCH', error: 'agent does not match the current session' };
|
|
2428
|
-
}
|
|
2429
|
-
return { ok: true, aid: selfAid };
|
|
2628
|
+
return resolveManagedHandoffScope({ ...params, sessionId: params.sessionId }, operation);
|
|
2430
2629
|
}
|
|
2431
2630
|
if (!params.agent) {
|
|
2432
2631
|
return { ok: false, code: 'HANDOFF_AGENT_REQUIRED', error: '--agent is required outside a task context' };
|
|
@@ -2434,23 +2633,56 @@ async function main() {
|
|
|
2434
2633
|
if (!agentRegistry.get(params.agent)) {
|
|
2435
2634
|
return { ok: false, code: 'HANDOFF_AGENT_NOT_FOUND', error: `agent not found: ${params.agent}` };
|
|
2436
2635
|
}
|
|
2437
|
-
return { ok: true, aid: params.agent };
|
|
2636
|
+
return { ok: true, aid: params.agent, currentSessionId: undefined, managementRole: true };
|
|
2438
2637
|
};
|
|
2638
|
+
ipcServer.setHandoffReturnExecutor(async (params) => {
|
|
2639
|
+
if (!params.sessionId)
|
|
2640
|
+
return responseEngine.returnHandoffResult(params);
|
|
2641
|
+
const scope = await resolveManagedHandoffScope({ ...params, sessionId: params.sessionId }, 'ec.handoff.return');
|
|
2642
|
+
if (!scope.ok)
|
|
2643
|
+
return scope;
|
|
2644
|
+
return responseEngine.returnAuthorizedHandoffResult({ sessionId: params.sessionId, handoffId: params.handoffId, content: params.content }, scope.aid);
|
|
2645
|
+
});
|
|
2646
|
+
ipcServer.setHandoffStatusExecutor(async (params) => {
|
|
2647
|
+
const scope = await resolveHandoffQueryScope(params, 'ec.handoff.status');
|
|
2648
|
+
if (!scope.ok)
|
|
2649
|
+
return scope;
|
|
2650
|
+
const status = handoffRuntime.status(scope.aid, params.handoffId);
|
|
2651
|
+
if (!status)
|
|
2652
|
+
return { ok: false, code: 'HANDOFF_NOT_FOUND', error: 'handoff not found' };
|
|
2653
|
+
if (!scope.managementRole
|
|
2654
|
+
&& status.origin_session_id !== scope.currentSessionId
|
|
2655
|
+
&& status.target_session_id !== scope.currentSessionId) {
|
|
2656
|
+
return { ok: false, code: 'HANDOFF_NOT_FOUND', error: 'handoff not found in the current session' };
|
|
2657
|
+
}
|
|
2658
|
+
return status;
|
|
2659
|
+
});
|
|
2439
2660
|
ipcServer.setHandoffListExecutor(async (params) => {
|
|
2440
|
-
const scope = await
|
|
2661
|
+
const scope = await resolveHandoffQueryScope(params, 'ec.handoff.list');
|
|
2441
2662
|
if (!scope.ok)
|
|
2442
2663
|
return scope;
|
|
2664
|
+
if (!scope.managementRole && params.filterSessionId && params.filterSessionId !== scope.currentSessionId) {
|
|
2665
|
+
return { ok: false, code: 'HANDOFF_SESSION_SCOPE_MISMATCH', error: 'only the current session can be queried' };
|
|
2666
|
+
}
|
|
2443
2667
|
return handoffRuntime.listHandoffs({
|
|
2444
2668
|
selfAid: scope.aid,
|
|
2445
2669
|
state: params.state,
|
|
2446
|
-
sessionId: params.filterSessionId,
|
|
2670
|
+
sessionId: scope.managementRole ? params.filterSessionId : scope.currentSessionId,
|
|
2447
2671
|
limit: params.limit,
|
|
2448
2672
|
});
|
|
2449
2673
|
});
|
|
2450
2674
|
ipcServer.setHandoffTraceExecutor(async (params) => {
|
|
2451
|
-
const scope = await
|
|
2675
|
+
const scope = await resolveHandoffQueryScope(params, 'ec.handoff.trace');
|
|
2452
2676
|
if (!scope.ok)
|
|
2453
2677
|
return scope;
|
|
2678
|
+
if (!scope.managementRole) {
|
|
2679
|
+
const status = handoffRuntime.status(scope.aid, params.handoffId);
|
|
2680
|
+
if (!status
|
|
2681
|
+
|| (status.origin_session_id !== scope.currentSessionId
|
|
2682
|
+
&& status.target_session_id !== scope.currentSessionId)) {
|
|
2683
|
+
return { ok: false, code: 'HANDOFF_NOT_FOUND', error: 'handoff not found in the current session' };
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2454
2686
|
return handoffRuntime.traceHandoff(scope.aid, params.handoffId, params.limit)
|
|
2455
2687
|
?? { ok: false, code: 'HANDOFF_NOT_FOUND', error: 'handoff not found' };
|
|
2456
2688
|
});
|
|
@@ -2522,12 +2754,27 @@ async function main() {
|
|
|
2522
2754
|
if (!payload) {
|
|
2523
2755
|
return { ok: false, error: 'message payload is required', code: 'INVALID_PAYLOAD' };
|
|
2524
2756
|
}
|
|
2757
|
+
try {
|
|
2758
|
+
if (Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
|
|
2759
|
+
payload = { ...payload, mentions: normalizeAunMentionEntries(payload.mentions) };
|
|
2760
|
+
}
|
|
2761
|
+
if (params.scope === 'group' && params.mentions !== undefined) {
|
|
2762
|
+
const normalizedMentions = normalizeAunMentionEntries(params.mentions);
|
|
2763
|
+
if (normalizedMentions.length > 0) {
|
|
2764
|
+
payload = { ...payload, mentions: normalizedMentions };
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
catch (error) {
|
|
2769
|
+
return {
|
|
2770
|
+
ok: false,
|
|
2771
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2772
|
+
code: error?.code ?? 'INVALID_MENTION_SCHEMA',
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2525
2775
|
if (params.thread && typeof payload.thread_id !== 'string') {
|
|
2526
2776
|
payload = { ...payload, thread_id: params.thread };
|
|
2527
2777
|
}
|
|
2528
|
-
if (params.scope === 'group' && params.mentions?.length) {
|
|
2529
|
-
payload = { ...payload, mentions: params.mentions };
|
|
2530
|
-
}
|
|
2531
2778
|
let targetSessionId;
|
|
2532
2779
|
if (params.originSessionId && params.originMessageId) {
|
|
2533
2780
|
try {
|
|
@@ -2600,16 +2847,17 @@ async function main() {
|
|
|
2600
2847
|
});
|
|
2601
2848
|
});
|
|
2602
2849
|
// ── Reload hooks: enable agentRegistry.reload() to drain/disconnect/restart channels ──
|
|
2850
|
+
const unregisterRuntimeChannel = (channelName) => {
|
|
2851
|
+
markChannelDisconnected(channelName);
|
|
2852
|
+
processor.unregisterChannel(channelName);
|
|
2853
|
+
cmdHandler.unregisterChannel(channelName);
|
|
2854
|
+
msgBridge.removeChannel(channelName);
|
|
2855
|
+
};
|
|
2603
2856
|
const reloadHooks = buildReloadHooks({
|
|
2604
2857
|
channelLoader,
|
|
2605
2858
|
channelInstances,
|
|
2606
2859
|
registerChannelInstance,
|
|
2607
|
-
unregisterChannelInstance:
|
|
2608
|
-
markChannelDisconnected(channelName);
|
|
2609
|
-
processor.unregisterChannel(channelName);
|
|
2610
|
-
cmdHandler.unregisterChannel(channelName);
|
|
2611
|
-
msgBridge.removeChannel(channelName);
|
|
2612
|
-
},
|
|
2860
|
+
unregisterChannelInstance: unregisterRuntimeChannel,
|
|
2613
2861
|
onChannelStarted: (inst) => {
|
|
2614
2862
|
// startChannel 重建渠道时重新注入 AidStatsCollector(与 hot-load 路径对齐)
|
|
2615
2863
|
if (inst.channelType === 'aun') {
|
|
@@ -2619,9 +2867,112 @@ async function main() {
|
|
|
2619
2867
|
}
|
|
2620
2868
|
},
|
|
2621
2869
|
onChannelConnected: markChannelConnected,
|
|
2870
|
+
onAgentReloaded: async (agent) => {
|
|
2871
|
+
const instance = channelInstances.find(candidate => {
|
|
2872
|
+
const owner = agentRegistry.resolveByChannel(candidate.adapter.channelKey)
|
|
2873
|
+
?? agentRegistry.resolveByChannel(candidate.adapter.channelName);
|
|
2874
|
+
return owner?.aid === agent.aid;
|
|
2875
|
+
});
|
|
2876
|
+
if (!instance)
|
|
2877
|
+
return;
|
|
2878
|
+
await bootstrapService.tryStartBootstrap({
|
|
2879
|
+
adapter: instance.adapter,
|
|
2880
|
+
channelKey: instance.adapter.channelKey,
|
|
2881
|
+
channelType: instance.channelType,
|
|
2882
|
+
agentAid: agent.aid,
|
|
2883
|
+
source: 'owner-bound',
|
|
2884
|
+
});
|
|
2885
|
+
},
|
|
2622
2886
|
messageQueue,
|
|
2623
2887
|
handoffRuntime,
|
|
2888
|
+
stageAgentRunners: async (candidate) => {
|
|
2889
|
+
const creationErrors = [];
|
|
2890
|
+
const staged = candidate.config.enabled === false
|
|
2891
|
+
? []
|
|
2892
|
+
: agentLoader.createForAgent(candidate, {
|
|
2893
|
+
onSessionIdUpdate: async (sessionId, agentSessionId) => {
|
|
2894
|
+
await sessionManager.updateAgentSessionIdBySessionId(sessionId, agentSessionId);
|
|
2895
|
+
},
|
|
2896
|
+
}, creationErrors);
|
|
2897
|
+
const onDisposeError = (instance, error) => {
|
|
2898
|
+
logger.warn(`[Reload] Failed to dispose runner ${instance.evolagentName}::${instance.baseagent}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2899
|
+
};
|
|
2900
|
+
if (creationErrors.length > 0) {
|
|
2901
|
+
await disposeAgentInstances(staged, onDisposeError);
|
|
2902
|
+
throw new Error(creationErrors.map(error => `${error.baseagent}: ${error.message}`).join('; '));
|
|
2903
|
+
}
|
|
2904
|
+
if (candidate.config.enabled !== false && staged.length === 0 && candidate.config.active_baseagent) {
|
|
2905
|
+
throw new Error(`No runner could be created for ${candidate.aid}::${candidate.config.active_baseagent}`);
|
|
2906
|
+
}
|
|
2907
|
+
for (const instance of staged) {
|
|
2908
|
+
instance.agent.setPermissionGateway?.(permissionGateway);
|
|
2909
|
+
instance.agent.setCompactStartCallback?.((sessionId) => {
|
|
2910
|
+
processor.handleCompactStart(sessionId);
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
return createRunnerReloadTransaction({
|
|
2914
|
+
aid: candidate.aid,
|
|
2915
|
+
agentMap,
|
|
2916
|
+
staged,
|
|
2917
|
+
onDisposeError,
|
|
2918
|
+
});
|
|
2919
|
+
},
|
|
2920
|
+
});
|
|
2921
|
+
const reloadOwnedMutes = new Set();
|
|
2922
|
+
const reloadCoordinator = new AgentReloadCoordinator({
|
|
2923
|
+
registry: agentRegistry,
|
|
2924
|
+
hooks: reloadHooks,
|
|
2925
|
+
getBusyCount: (aid) => {
|
|
2926
|
+
const agentName = agentRegistry.get(aid)?.name;
|
|
2927
|
+
if (!agentName)
|
|
2928
|
+
return 0;
|
|
2929
|
+
return messageQueue.getProcessingCountByAgent(agentName)
|
|
2930
|
+
+ messageQueue.getQueueLengthByAgent(agentName);
|
|
2931
|
+
},
|
|
2932
|
+
beforeReload: invalidateKitCache,
|
|
2933
|
+
forceReload: async (aid) => {
|
|
2934
|
+
const agentName = agentRegistry.get(aid)?.name;
|
|
2935
|
+
if (!agentName)
|
|
2936
|
+
return;
|
|
2937
|
+
messageQueue.clearByAgent(agentName);
|
|
2938
|
+
await messageQueue.interruptByAgentAndWait(agentName);
|
|
2939
|
+
const deadline = Date.now() + 5_000;
|
|
2940
|
+
while (messageQueue.getProcessingCountByAgent(agentName) > 0) {
|
|
2941
|
+
if (Date.now() >= deadline) {
|
|
2942
|
+
throw new Error(`Timed out waiting for Agent "${aid}" tasks to stop`);
|
|
2943
|
+
}
|
|
2944
|
+
await new Promise(resolve => setTimeout(resolve, 25));
|
|
2945
|
+
}
|
|
2946
|
+
},
|
|
2947
|
+
acquireWorkGate: (aid) => {
|
|
2948
|
+
const agentName = agentRegistry.get(aid)?.name;
|
|
2949
|
+
if (!agentName)
|
|
2950
|
+
return;
|
|
2951
|
+
const inheritedReloadMute = reloadOwnedMutes.delete(agentName);
|
|
2952
|
+
const alreadyMuted = messageQueue.isAgentMuted(agentName);
|
|
2953
|
+
const ownsMute = inheritedReloadMute || !alreadyMuted;
|
|
2954
|
+
if (!alreadyMuted)
|
|
2955
|
+
messageQueue.muteAgent(agentName);
|
|
2956
|
+
return () => {
|
|
2957
|
+
const current = agentRegistry.get(aid);
|
|
2958
|
+
if (!current) {
|
|
2959
|
+
messageQueue.clearByAgent(agentName);
|
|
2960
|
+
if (ownsMute)
|
|
2961
|
+
messageQueue.unmuteAgent(agentName);
|
|
2962
|
+
return;
|
|
2963
|
+
}
|
|
2964
|
+
if (current.status === 'disabled') {
|
|
2965
|
+
messageQueue.clearByAgent(agentName);
|
|
2966
|
+
if (ownsMute)
|
|
2967
|
+
reloadOwnedMutes.add(agentName);
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2970
|
+
if (ownsMute)
|
|
2971
|
+
messageQueue.unmuteAgent(agentName);
|
|
2972
|
+
};
|
|
2973
|
+
},
|
|
2624
2974
|
});
|
|
2975
|
+
ipcServer.setAgentReloadExecutor((aid, options) => reloadCoordinator.reload(aid, options));
|
|
2625
2976
|
// Make reload hooks accessible to IPC handler & ctl handler (both run in this process)
|
|
2626
2977
|
globalThis.__evolcore_reloadHooks = reloadHooks;
|
|
2627
2978
|
// Hot-load handler: dynamically add a new agent at runtime
|
|
@@ -2677,7 +3028,7 @@ async function main() {
|
|
|
2677
3028
|
agent.status = 'running';
|
|
2678
3029
|
// 连接
|
|
2679
3030
|
await channelLoader.connectAll(instances, { onConnected: markChannelConnected });
|
|
2680
|
-
await handoffRuntime.recover([aid]);
|
|
3031
|
+
await handoffRuntime.recover([aid], { discardExpired: false });
|
|
2681
3032
|
await ensureTriggerSchedulerStarted(agent);
|
|
2682
3033
|
handoffRuntime.resumeAgent(aid);
|
|
2683
3034
|
resumed = true;
|
|
@@ -2691,90 +3042,121 @@ async function main() {
|
|
|
2691
3042
|
}
|
|
2692
3043
|
};
|
|
2693
3044
|
// Full resync handler: scan disk, load new agents, unload removed/disabled, reload changed
|
|
3045
|
+
let resyncInFlight;
|
|
2694
3046
|
globalThis.__evolcore_resyncAgents = async () => {
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
3047
|
+
if (resyncInFlight)
|
|
3048
|
+
return resyncInFlight;
|
|
3049
|
+
const operation = (async () => {
|
|
3050
|
+
// 先清 kit 缓存:'kits' 组(manifest / fragment / schema / 角色模板)走 on-reload
|
|
3051
|
+
// 策略,平时不查盘。放在扫盘与 reload 之前,本轮上下线的 agent 才能读到磁盘上的
|
|
3052
|
+
// 最新版本——放在末尾的话,这一轮全用旧值,改动要等下一次 resync 才生效。
|
|
3053
|
+
invalidateKitCache();
|
|
3054
|
+
const { loadAllAgents: scanAgents } = await import('./config-store.js');
|
|
3055
|
+
const { agents: diskAgents } = scanAgents();
|
|
3056
|
+
const results = [];
|
|
3057
|
+
// 1. 下线:运行时有但磁盘上没有 / disabled 的
|
|
3058
|
+
for (const [aid] of [...agentRegistry.agents.entries()]) {
|
|
3059
|
+
const diskCfg = diskAgents.find(a => a.aid === aid);
|
|
3060
|
+
if (!diskCfg || diskCfg.enabled === false) {
|
|
3061
|
+
try {
|
|
3062
|
+
const offlined = await reloadCoordinator.runExclusive(aid, async () => {
|
|
3063
|
+
const latestDiskCfg = readConfig(ConfigTarget.Agent, { self: aid }, { cache: false, expand: true });
|
|
3064
|
+
if (latestDiskCfg && latestDiskCfg.enabled !== false)
|
|
3065
|
+
return false;
|
|
3066
|
+
const runtimeAgent = agentRegistry.get(aid);
|
|
3067
|
+
if (!runtimeAgent)
|
|
3068
|
+
return false;
|
|
3069
|
+
let removed = false;
|
|
3070
|
+
handoffRuntime.pauseAgent(aid);
|
|
3071
|
+
try {
|
|
3072
|
+
await handoffRuntime.drainAgent(aid);
|
|
3073
|
+
await triggerSchedulers.get(aid)?.stop();
|
|
3074
|
+
triggerSchedulers.delete(aid);
|
|
3075
|
+
triggerSchedulerStarts.delete(aid);
|
|
3076
|
+
// 断开所有 channels
|
|
3077
|
+
for (const chName of runtimeAgent.channelInstanceNames()) {
|
|
3078
|
+
const inst = channelInstances.find(i => i.adapter.channelName === chName);
|
|
3079
|
+
if (inst) {
|
|
3080
|
+
try {
|
|
3081
|
+
await inst.disconnect();
|
|
3082
|
+
}
|
|
3083
|
+
catch { }
|
|
3084
|
+
const idx = channelInstances.indexOf(inst);
|
|
3085
|
+
if (idx >= 0)
|
|
3086
|
+
channelInstances.splice(idx, 1);
|
|
3087
|
+
}
|
|
3088
|
+
unregisterRuntimeChannel(chName);
|
|
3089
|
+
}
|
|
3090
|
+
agentRegistry.agents.delete(aid);
|
|
3091
|
+
messageQueue.clearByAgent(runtimeAgent.name);
|
|
3092
|
+
removed = true;
|
|
3093
|
+
return true;
|
|
3094
|
+
}
|
|
3095
|
+
finally {
|
|
3096
|
+
if (!removed)
|
|
3097
|
+
handoffRuntime.resumeAgent(aid);
|
|
3098
|
+
}
|
|
3099
|
+
});
|
|
3100
|
+
if (offlined)
|
|
3101
|
+
results.push(`- ${aid} (offline)`);
|
|
3102
|
+
}
|
|
3103
|
+
catch (error) {
|
|
3104
|
+
if (error instanceof AgentReloadBusyError) {
|
|
3105
|
+
results.push(`⏸ ${aid} (busy: ${error.busyCount} task(s); reload skipped)`);
|
|
3106
|
+
}
|
|
3107
|
+
else {
|
|
3108
|
+
results.push(`⚠ ${aid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2727
3109
|
}
|
|
2728
|
-
catch { }
|
|
2729
|
-
markChannelDisconnected(inst.adapter.channelName);
|
|
2730
|
-
const idx = channelInstances.indexOf(inst);
|
|
2731
|
-
if (idx >= 0)
|
|
2732
|
-
channelInstances.splice(idx, 1);
|
|
2733
3110
|
}
|
|
3111
|
+
continue;
|
|
2734
3112
|
}
|
|
2735
|
-
agentRegistry.agents.delete(aid);
|
|
2736
|
-
results.push(`- ${aid} (offline)`);
|
|
2737
|
-
continue;
|
|
2738
3113
|
}
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
3114
|
+
// 2. 新增:磁盘上有但运行时没有的
|
|
3115
|
+
for (const cfg of diskAgents) {
|
|
3116
|
+
if (cfg.enabled === false)
|
|
3117
|
+
continue;
|
|
3118
|
+
if (agentRegistry.agents.has(cfg.aid))
|
|
3119
|
+
continue;
|
|
3120
|
+
try {
|
|
3121
|
+
await globalThis.__evolcore_hotLoadAgent(cfg.aid);
|
|
3122
|
+
results.push(`+ ${cfg.aid} (online)`);
|
|
3123
|
+
}
|
|
3124
|
+
catch (e) {
|
|
3125
|
+
results.push(`✗ ${cfg.aid}: ${e?.message || e}`);
|
|
3126
|
+
}
|
|
2749
3127
|
}
|
|
2750
|
-
|
|
2751
|
-
|
|
3128
|
+
// 3. 已有的:重新 reload(config 可能改了)
|
|
3129
|
+
for (const cfg of diskAgents) {
|
|
3130
|
+
if (cfg.enabled === false)
|
|
3131
|
+
continue;
|
|
3132
|
+
if (!agentRegistry.agents.has(cfg.aid))
|
|
3133
|
+
continue;
|
|
3134
|
+
// 只有磁盘上存在且运行时也存在的才 reload
|
|
3135
|
+
try {
|
|
3136
|
+
await reloadCoordinator.reload(cfg.aid);
|
|
3137
|
+
const runtimeAgent = agentRegistry.get(cfg.aid);
|
|
3138
|
+
if (runtimeAgent)
|
|
3139
|
+
await startTriggerScheduler(runtimeAgent);
|
|
3140
|
+
results.push(`↻ ${cfg.aid} (reloaded)`);
|
|
3141
|
+
}
|
|
3142
|
+
catch (e) {
|
|
3143
|
+
results.push(`⚠ ${cfg.aid}: ${e?.message || e}`);
|
|
3144
|
+
}
|
|
2752
3145
|
}
|
|
3146
|
+
// 重建 channel index(kit 缓存已在本轮开头清过)
|
|
3147
|
+
agentRegistry.channelIndex.clear();
|
|
3148
|
+
agentRegistry.buildChannelIndex();
|
|
3149
|
+
logger.info(`[Resync] Done: ${results.length} agent(s) processed`);
|
|
3150
|
+
return results;
|
|
3151
|
+
})();
|
|
3152
|
+
resyncInFlight = operation;
|
|
3153
|
+
try {
|
|
3154
|
+
return await operation;
|
|
2753
3155
|
}
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
if (cfg.enabled === false)
|
|
2758
|
-
continue;
|
|
2759
|
-
if (!agentRegistry.agents.has(cfg.aid))
|
|
2760
|
-
continue;
|
|
2761
|
-
// 只有磁盘上存在且运行时也存在的才 reload
|
|
2762
|
-
try {
|
|
2763
|
-
await agentRegistry.reload(cfg.aid, hooks);
|
|
2764
|
-
const runtimeAgent = agentRegistry.get(cfg.aid);
|
|
2765
|
-
if (runtimeAgent)
|
|
2766
|
-
await startTriggerScheduler(runtimeAgent);
|
|
2767
|
-
results.push(`↻ ${cfg.aid} (reloaded)`);
|
|
2768
|
-
}
|
|
2769
|
-
catch (e) {
|
|
2770
|
-
results.push(`⚠ ${cfg.aid}: ${e?.message || e}`);
|
|
2771
|
-
}
|
|
3156
|
+
finally {
|
|
3157
|
+
if (resyncInFlight === operation)
|
|
3158
|
+
resyncInFlight = undefined;
|
|
2772
3159
|
}
|
|
2773
|
-
// 重建 channel index(kit 缓存已在本轮开头清过)
|
|
2774
|
-
agentRegistry.channelIndex.clear();
|
|
2775
|
-
agentRegistry.buildChannelIndex();
|
|
2776
|
-
logger.info(`[Resync] Done: ${results.length} agent(s) processed`);
|
|
2777
|
-
return results;
|
|
2778
3160
|
};
|
|
2779
3161
|
ipcServer.setStatsProvider(() => statsCollector.getSnapshot());
|
|
2780
3162
|
ipcServer.setAgentStatsProvider(() => agentRegistry.list().map((agent) => {
|
|
@@ -2825,6 +3207,88 @@ async function main() {
|
|
|
2825
3207
|
return { ok: false, error: `unknown action: ${params.action}` };
|
|
2826
3208
|
}
|
|
2827
3209
|
});
|
|
3210
|
+
async function authorizeManagedAgentOperation(params, operation) {
|
|
3211
|
+
const delegation = agentDelegationRegistry.validate(params.delegationToken, params.sessionId, params.delegationCommandHash);
|
|
3212
|
+
if (!delegation.ok)
|
|
3213
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
3214
|
+
const session = await sessionManager.getSessionById(params.sessionId);
|
|
3215
|
+
if (!session || session.selfAID !== delegation.grant.selfAid) {
|
|
3216
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegation does not match the current session' };
|
|
3217
|
+
}
|
|
3218
|
+
if (params.targetAid && params.targetAid !== delegation.grant.selfAid) {
|
|
3219
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'only the current Agent can be targeted' };
|
|
3220
|
+
}
|
|
3221
|
+
let conversationId;
|
|
3222
|
+
try {
|
|
3223
|
+
conversationId = parsePeerKey(delegation.grant.peerKey).channelId;
|
|
3224
|
+
}
|
|
3225
|
+
catch {
|
|
3226
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'delegation contains an invalid relation key' };
|
|
3227
|
+
}
|
|
3228
|
+
const subject = buildAuthSubject({
|
|
3229
|
+
selfAid: delegation.grant.selfAid,
|
|
3230
|
+
actorId: delegation.grant.actorId,
|
|
3231
|
+
channel: delegation.grant.channel,
|
|
3232
|
+
channelType: delegation.grant.channelType,
|
|
3233
|
+
channelId: conversationId,
|
|
3234
|
+
chatType: delegation.grant.chatType,
|
|
3235
|
+
conversationId,
|
|
3236
|
+
processOwners: daemonCfg.owners ?? [],
|
|
3237
|
+
});
|
|
3238
|
+
if (!isManagementRole(subject.role)) {
|
|
3239
|
+
return { ok: false, code: 'ROLE_ACCESS_DENIED', error: `${operation} requires Agent admin permission` };
|
|
3240
|
+
}
|
|
3241
|
+
const decision = authorizeOperation({
|
|
3242
|
+
source: 'agent-tool',
|
|
3243
|
+
subject,
|
|
3244
|
+
intent: {
|
|
3245
|
+
operation,
|
|
3246
|
+
scope: operation === 'system.status' ? 'process' : 'agent',
|
|
3247
|
+
source: 'agent-tool',
|
|
3248
|
+
args: { self: delegation.grant.selfAid, aid: params.targetAid ?? delegation.grant.selfAid },
|
|
3249
|
+
},
|
|
3250
|
+
auditMetadata: { taskId: delegation.grant.taskId, messageId: delegation.grant.messageId },
|
|
3251
|
+
});
|
|
3252
|
+
return decision.allow
|
|
3253
|
+
? { ok: true, aid: delegation.grant.selfAid }
|
|
3254
|
+
: { ok: false, code: decision.code, error: decision.reason };
|
|
3255
|
+
}
|
|
3256
|
+
ipcServer.setQueueOperationExecutor(async (params) => {
|
|
3257
|
+
const operation = `ec.queue.${params.action ?? 'read'}`;
|
|
3258
|
+
const authorization = await authorizeManagedAgentOperation(params, operation);
|
|
3259
|
+
if (!authorization.ok)
|
|
3260
|
+
return authorization;
|
|
3261
|
+
const handle = agentRegistry.get(authorization.aid);
|
|
3262
|
+
if (!handle?.name)
|
|
3263
|
+
return { ok: false, code: 'AGENT_NOT_FOUND', error: 'current Agent not found' };
|
|
3264
|
+
if (!params.action)
|
|
3265
|
+
return { ok: true, items: messageQueue.getQueueItemsByAgent(handle.name) };
|
|
3266
|
+
if (params.action === 'clear')
|
|
3267
|
+
return { ok: true, cleared: messageQueue.clearByAgent(handle.name) };
|
|
3268
|
+
if (params.action === 'cancel') {
|
|
3269
|
+
if (!params.messageId)
|
|
3270
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: 'missing messageId' };
|
|
3271
|
+
return { ok: true, cancelled: messageQueue.cancelMessageById(handle.name, params.messageId) };
|
|
3272
|
+
}
|
|
3273
|
+
if (!params.sessionKey)
|
|
3274
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: 'missing sessionKey' };
|
|
3275
|
+
const targetSessionId = messageQueue.findSessionIdBySessionKey(params.sessionKey);
|
|
3276
|
+
if (!targetSessionId)
|
|
3277
|
+
return { ok: false, code: 'SESSION_NOT_FOUND', error: `session not found: ${params.sessionKey}` };
|
|
3278
|
+
const targetSession = await sessionManager.getSessionById(targetSessionId);
|
|
3279
|
+
if (!targetSession || targetSession.selfAID !== authorization.aid) {
|
|
3280
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'session does not belong to the current Agent' };
|
|
3281
|
+
}
|
|
3282
|
+
return { ok: true, interrupted: await messageQueue.interruptBySession(targetSessionId) };
|
|
3283
|
+
});
|
|
3284
|
+
ipcServer.setStatusAuthorizationExecutor(async (params) => {
|
|
3285
|
+
const authorization = await authorizeManagedAgentOperation(params, 'system.status');
|
|
3286
|
+
return authorization.ok ? { ok: true } : authorization;
|
|
3287
|
+
});
|
|
3288
|
+
ipcServer.setStorageAuthorizationExecutor(async (params) => {
|
|
3289
|
+
const authorization = await authorizeManagedAgentOperation({ ...params, targetAid: params.aid }, params.operation);
|
|
3290
|
+
return authorization.ok ? { ok: true } : authorization;
|
|
3291
|
+
});
|
|
2828
3292
|
ipcServer.setTriggerExecutor(async (cmd) => {
|
|
2829
3293
|
const schedulerFor = (agentAid) => {
|
|
2830
3294
|
const scheduler = triggerSchedulers.get(agentAid);
|