evolcore 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/claude-runner.js +35 -2
- package/dist/agents/codex-app-server-client.js +120 -14
- package/dist/agents/codex-runner.js +273 -35
- package/dist/agents/ecagent-runner.js +36 -4
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/channels/aun.js +288 -108
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +56 -8
- package/dist/cli/agent.js +170 -6
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +31 -31
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +30 -2
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +4 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +30 -7
- package/dist/core/auth/operation-catalog.js +187 -25
- package/dist/core/bootstrap-messages.js +18 -0
- package/dist/core/bootstrap-service.js +63 -4
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +1 -1
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +366 -0
- package/dist/core/command/menu-handler.js +147 -35
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +41 -21
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent.js +4 -2
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +236 -78
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +1 -0
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/tool-policy.js +73 -30
- package/dist/core/protected-paths.js +7 -2
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +384 -98
- package/dist/ipc.js +218 -9
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +22 -0
- package/kits/templates/roles/member.json +27 -0
- package/kits/templates/roles/visitor.json +33 -3
- package/kits/templates/system-fragments/bootstrap.md +1 -1
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +2 -2
- package/dist/core/command/evol-menu-version-gate.js +0 -39
package/dist/index.js
CHANGED
|
@@ -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
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,6 +62,7 @@ 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';
|
|
@@ -90,6 +91,7 @@ import { validateModelSelectionForRole } from './core/model/model-permission.js'
|
|
|
90
91
|
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride, } from './core/role/runtime-policy.js';
|
|
91
92
|
import { atomicWriteJson } from './core/session/session-fs-store.js';
|
|
92
93
|
import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from './core/message/message-log.js';
|
|
94
|
+
import { normalizeAunMentionEntries } from './aun/msg/mention-schema.js';
|
|
93
95
|
import { MAIN_PACKAGE_NAME } from './product.js';
|
|
94
96
|
import fs from 'fs';
|
|
95
97
|
import crypto from 'crypto';
|
|
@@ -207,7 +209,7 @@ function originFromActorSession(session, authenticatedPeerId, authenticatedChann
|
|
|
207
209
|
}
|
|
208
210
|
function seedUpgradeCheckTrigger(manager, owner) {
|
|
209
211
|
const now = Date.now();
|
|
210
|
-
const scriptName = 'upgrade-check.
|
|
212
|
+
const scriptName = 'upgrade-check.cjs';
|
|
211
213
|
const definition = normalizeTriggerDefinition({
|
|
212
214
|
$schema_version: 3.1,
|
|
213
215
|
id: '__upgrade-check',
|
|
@@ -234,7 +236,7 @@ function seedUpgradeCheckTrigger(manager, owner) {
|
|
|
234
236
|
type: 'script',
|
|
235
237
|
script: {
|
|
236
238
|
path: scriptName,
|
|
237
|
-
runtime: '
|
|
239
|
+
runtime: 'node',
|
|
238
240
|
timeoutMs: 60_000,
|
|
239
241
|
},
|
|
240
242
|
permissionMode: 'bypass',
|
|
@@ -260,67 +262,107 @@ function seedUpgradeCheckTrigger(manager, owner) {
|
|
|
260
262
|
const cliPath = path.join(packageRoot, 'dist', 'cli', 'index.js');
|
|
261
263
|
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
262
264
|
const devMode = fs.existsSync(path.join(packageRoot, 'src', 'index.ts'));
|
|
263
|
-
fs.writeFileSync(scriptPath,
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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 });
|
|
324
366
|
fs.chmodSync(scriptPath, 0o755);
|
|
325
367
|
atomicWriteJson(manager.definitionPath(definition.id), definition);
|
|
326
368
|
fs.rmSync(manager.activePath(definition.id), { force: true });
|
|
@@ -338,7 +380,9 @@ function removeLegacyAgentUpgradeCheck(manager) {
|
|
|
338
380
|
return;
|
|
339
381
|
const isLegacySystemOrigin = existing.origin?.channelKey === '__system__' || existing.origin?.channelKey === 'daemon';
|
|
340
382
|
const isUpgradeScript = existing.execution.type === 'script'
|
|
341
|
-
&& 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');
|
|
342
386
|
if (!isLegacySystemOrigin || !isUpgradeScript)
|
|
343
387
|
return;
|
|
344
388
|
fs.rmSync(manager.triggerDir('__upgrade-check'), { recursive: true, force: true });
|
|
@@ -396,6 +440,13 @@ async function runBindBootstrapDaemon(daemonCfg) {
|
|
|
396
440
|
await controlChannel.sendMessage(opts.channelId, JSON.stringify(response));
|
|
397
441
|
}
|
|
398
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 { }
|
|
399
450
|
const ipcServer = new IpcServer(resolvePaths().socket, () => ({
|
|
400
451
|
pid: process.pid,
|
|
401
452
|
uptime: Math.round(process.uptime() * 1000),
|
|
@@ -417,6 +468,7 @@ async function runBindBootstrapDaemon(daemonCfg) {
|
|
|
417
468
|
};
|
|
418
469
|
})(),
|
|
419
470
|
}));
|
|
471
|
+
ipcServer.setControlToken(controlToken);
|
|
420
472
|
ipcServer.setBindExecutor({
|
|
421
473
|
begin: (cmd) => bindService.begin(cmd),
|
|
422
474
|
status: (taskId) => bindService.status(taskId),
|
|
@@ -1129,6 +1181,7 @@ async function main() {
|
|
|
1129
1181
|
return { ok: result.ok, message_id: result.message_id, error: result.error };
|
|
1130
1182
|
}, undefined, { queueTtlMs: taskExecutionTtlMs });
|
|
1131
1183
|
responseEngine.setHandoffRuntime(handoffRuntime);
|
|
1184
|
+
cmdHandler.setHandoffRuntime(handoffRuntime);
|
|
1132
1185
|
// Trigger runtime: daemon-level script + feedback scheduler.
|
|
1133
1186
|
const triggerSchedulers = new Map();
|
|
1134
1187
|
const targetSessionLocks = new TargetSessionLockRegistry();
|
|
@@ -1993,7 +2046,7 @@ async function main() {
|
|
|
1993
2046
|
const delegation = agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1994
2047
|
if (!delegation.ok)
|
|
1995
2048
|
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1996
|
-
return cmdHandler.handleCtl(cmd, sessionId);
|
|
2049
|
+
return cmdHandler.handleCtl(cmd, sessionId, delegation.grant);
|
|
1997
2050
|
});
|
|
1998
2051
|
// Register every IPC executor/provider before exposing the endpoint. The
|
|
1999
2052
|
// function declaration is hoisted, while its invocation remains here so a
|
|
@@ -2080,10 +2133,11 @@ async function main() {
|
|
|
2080
2133
|
}
|
|
2081
2134
|
else {
|
|
2082
2135
|
try {
|
|
2083
|
-
const ecwebProc = spawn(
|
|
2136
|
+
const ecwebProc = spawn(process.execPath, [ecwebEntry], {
|
|
2084
2137
|
cwd: ecwebPath,
|
|
2085
2138
|
detached: true,
|
|
2086
2139
|
stdio: 'ignore',
|
|
2140
|
+
windowsHide: platform.isWindows,
|
|
2087
2141
|
env: { ...process.env, EVOLCORE_HOME: resolvePaths().root }
|
|
2088
2142
|
});
|
|
2089
2143
|
ecwebProc.unref();
|
|
@@ -2259,6 +2313,9 @@ async function main() {
|
|
|
2259
2313
|
const recoveryAids = triggerStartupAgents.map(agent => agent.aid);
|
|
2260
2314
|
for (const aid of recoveryAids) {
|
|
2261
2315
|
try {
|
|
2316
|
+
const discarded = await handoffRuntime.discardExpired([aid]);
|
|
2317
|
+
if (discarded > 0)
|
|
2318
|
+
logger.info(`[Handoff] discarded ${discarded} expired handoff(s) for ${aid}`);
|
|
2262
2319
|
const expired = handoffRuntime.expireQueued([aid]);
|
|
2263
2320
|
if (expired > 0)
|
|
2264
2321
|
logger.info(`[Handoff] expired ${expired} stale queued delivery item(s) for ${aid}`);
|
|
@@ -2344,7 +2401,27 @@ async function main() {
|
|
|
2344
2401
|
function configureIpc() {
|
|
2345
2402
|
// M3: direct call (not cast) — wire EvolAgentRegistry into IPC for evolagent.* handlers
|
|
2346
2403
|
ipcServer.setAgentRegistry(agentRegistry);
|
|
2347
|
-
ipcServer.
|
|
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
|
+
}
|
|
2348
2425
|
const aunInstance = channelInstances.find(candidate => {
|
|
2349
2426
|
if (candidate.channelType !== 'aun')
|
|
2350
2427
|
return false;
|
|
@@ -2353,7 +2430,30 @@ async function main() {
|
|
|
2353
2430
|
return owner?.aid === aid;
|
|
2354
2431
|
});
|
|
2355
2432
|
const welcome = aunInstance?.channel;
|
|
2356
|
-
|
|
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);
|
|
2357
2457
|
});
|
|
2358
2458
|
ipcServer.setDingtalkContactBindExecutor({
|
|
2359
2459
|
register: (cmd) => registerPendingDingtalkContactBind(cmd),
|
|
@@ -2378,6 +2478,10 @@ async function main() {
|
|
|
2378
2478
|
ipcServer.setMenuExecutor((payload, auth) => cmdHandler.execMenuForEcweb(payload, auth));
|
|
2379
2479
|
ipcServer.setConfigOperationExecutor((argv, sessionId, delegationToken, delegationCommandHash) => cmdHandler.handleConfigOperation(argv, sessionId, delegationToken, delegationCommandHash));
|
|
2380
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));
|
|
2381
2485
|
cmdHandler.setDaemonStatusProvider(() => {
|
|
2382
2486
|
const aidState = controlChannel?.getAidState?.();
|
|
2383
2487
|
return {
|
|
@@ -2446,30 +2550,82 @@ async function main() {
|
|
|
2446
2550
|
aidStatsCollector.recordOutbound(params.aid, params.toPeer, Buffer.byteLength(params.text || '', 'utf-8'), params.text, false, params.encrypt, params.chatmode, 'send');
|
|
2447
2551
|
});
|
|
2448
2552
|
ipcServer.setTaskRuntimeContextProvider(({ sessionId }) => responseEngine.getTaskRuntimeContext(sessionId));
|
|
2449
|
-
ipcServer.
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
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 };
|
|
2453
2572
|
const runtime = responseEngine.getTaskRuntimeContext(params.sessionId);
|
|
2454
2573
|
const session = await sessionManager.getSessionById(params.sessionId);
|
|
2455
2574
|
const selfAid = runtime?.selfAid || session?.selfAID;
|
|
2456
|
-
if (!selfAid)
|
|
2457
|
-
return { ok: false, code: '
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
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) => {
|
|
2462
2627
|
if (params.sessionId) {
|
|
2463
|
-
|
|
2464
|
-
const session = await sessionManager.getSessionById(params.sessionId);
|
|
2465
|
-
const selfAid = runtime?.selfAid || session?.selfAID;
|
|
2466
|
-
if (!selfAid) {
|
|
2467
|
-
return { ok: false, code: 'HANDOFF_CALL_SESSION_INVALID', error: 'current session is invalid' };
|
|
2468
|
-
}
|
|
2469
|
-
if (params.agent && params.agent !== selfAid) {
|
|
2470
|
-
return { ok: false, code: 'HANDOFF_AGENT_SCOPE_MISMATCH', error: 'agent does not match the current session' };
|
|
2471
|
-
}
|
|
2472
|
-
return { ok: true, aid: selfAid };
|
|
2628
|
+
return resolveManagedHandoffScope({ ...params, sessionId: params.sessionId }, operation);
|
|
2473
2629
|
}
|
|
2474
2630
|
if (!params.agent) {
|
|
2475
2631
|
return { ok: false, code: 'HANDOFF_AGENT_REQUIRED', error: '--agent is required outside a task context' };
|
|
@@ -2477,23 +2633,56 @@ async function main() {
|
|
|
2477
2633
|
if (!agentRegistry.get(params.agent)) {
|
|
2478
2634
|
return { ok: false, code: 'HANDOFF_AGENT_NOT_FOUND', error: `agent not found: ${params.agent}` };
|
|
2479
2635
|
}
|
|
2480
|
-
return { ok: true, aid: params.agent };
|
|
2636
|
+
return { ok: true, aid: params.agent, currentSessionId: undefined, managementRole: true };
|
|
2481
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
|
+
});
|
|
2482
2660
|
ipcServer.setHandoffListExecutor(async (params) => {
|
|
2483
|
-
const scope = await
|
|
2661
|
+
const scope = await resolveHandoffQueryScope(params, 'ec.handoff.list');
|
|
2484
2662
|
if (!scope.ok)
|
|
2485
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
|
+
}
|
|
2486
2667
|
return handoffRuntime.listHandoffs({
|
|
2487
2668
|
selfAid: scope.aid,
|
|
2488
2669
|
state: params.state,
|
|
2489
|
-
sessionId: params.filterSessionId,
|
|
2670
|
+
sessionId: scope.managementRole ? params.filterSessionId : scope.currentSessionId,
|
|
2490
2671
|
limit: params.limit,
|
|
2491
2672
|
});
|
|
2492
2673
|
});
|
|
2493
2674
|
ipcServer.setHandoffTraceExecutor(async (params) => {
|
|
2494
|
-
const scope = await
|
|
2675
|
+
const scope = await resolveHandoffQueryScope(params, 'ec.handoff.trace');
|
|
2495
2676
|
if (!scope.ok)
|
|
2496
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
|
+
}
|
|
2497
2686
|
return handoffRuntime.traceHandoff(scope.aid, params.handoffId, params.limit)
|
|
2498
2687
|
?? { ok: false, code: 'HANDOFF_NOT_FOUND', error: 'handoff not found' };
|
|
2499
2688
|
});
|
|
@@ -2565,12 +2754,27 @@ async function main() {
|
|
|
2565
2754
|
if (!payload) {
|
|
2566
2755
|
return { ok: false, error: 'message payload is required', code: 'INVALID_PAYLOAD' };
|
|
2567
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
|
+
}
|
|
2568
2775
|
if (params.thread && typeof payload.thread_id !== 'string') {
|
|
2569
2776
|
payload = { ...payload, thread_id: params.thread };
|
|
2570
2777
|
}
|
|
2571
|
-
if (params.scope === 'group' && params.mentions?.length) {
|
|
2572
|
-
payload = { ...payload, mentions: params.mentions };
|
|
2573
|
-
}
|
|
2574
2778
|
let targetSessionId;
|
|
2575
2779
|
if (params.originSessionId && params.originMessageId) {
|
|
2576
2780
|
try {
|
|
@@ -2824,7 +3028,7 @@ async function main() {
|
|
|
2824
3028
|
agent.status = 'running';
|
|
2825
3029
|
// 连接
|
|
2826
3030
|
await channelLoader.connectAll(instances, { onConnected: markChannelConnected });
|
|
2827
|
-
await handoffRuntime.recover([aid]);
|
|
3031
|
+
await handoffRuntime.recover([aid], { discardExpired: false });
|
|
2828
3032
|
await ensureTriggerSchedulerStarted(agent);
|
|
2829
3033
|
handoffRuntime.resumeAgent(aid);
|
|
2830
3034
|
resumed = true;
|
|
@@ -3003,6 +3207,88 @@ async function main() {
|
|
|
3003
3207
|
return { ok: false, error: `unknown action: ${params.action}` };
|
|
3004
3208
|
}
|
|
3005
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
|
+
});
|
|
3006
3292
|
ipcServer.setTriggerExecutor(async (cmd) => {
|
|
3007
3293
|
const schedulerFor = (agentAid) => {
|
|
3008
3294
|
const scheduler = triggerSchedulers.get(agentAid);
|