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
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
1
|
import os from 'os';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { execCodexCliSync } from '../../../utils/codex-cli.js';
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
if (!fs.existsSync(dir))
|
|
8
|
-
return [];
|
|
9
|
-
return fs.readdirSync(dir, { withFileTypes: true })
|
|
10
|
-
.filter(entry => entry.isDirectory())
|
|
11
|
-
.map(entry => entry.name);
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
4
|
+
import { discoverSkillDirectories } from '../skill-discovery.js';
|
|
17
5
|
function discoverSkills(ctx) {
|
|
18
6
|
const roots = [
|
|
19
7
|
{ dir: path.join(ctx.projectPath, '.codex', 'skills'), source: 'project' },
|
|
@@ -23,11 +11,12 @@ function discoverSkills(ctx) {
|
|
|
23
11
|
const seen = new Set();
|
|
24
12
|
const out = [];
|
|
25
13
|
for (const root of roots) {
|
|
26
|
-
for (const
|
|
14
|
+
for (const skill of discoverSkillDirectories(root.dir)) {
|
|
15
|
+
const { id } = skill;
|
|
27
16
|
if (seen.has(id))
|
|
28
17
|
continue;
|
|
29
18
|
seen.add(id);
|
|
30
|
-
out.push({ id, label: id, source: root.source });
|
|
19
|
+
out.push({ id, label: id, desc: skill.description, source: root.source });
|
|
31
20
|
}
|
|
32
21
|
}
|
|
33
22
|
return out;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { parseDocument } from 'yaml';
|
|
4
|
+
function readSkillMetadata(skillDir) {
|
|
5
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
6
|
+
try {
|
|
7
|
+
if (!fs.statSync(skillFile).isFile())
|
|
8
|
+
return undefined;
|
|
9
|
+
const text = fs.readFileSync(skillFile, 'utf-8');
|
|
10
|
+
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
11
|
+
if (!match)
|
|
12
|
+
return undefined;
|
|
13
|
+
const document = parseDocument(match[1]);
|
|
14
|
+
if (document.errors.length > 0)
|
|
15
|
+
return undefined;
|
|
16
|
+
const metadata = document.toJS();
|
|
17
|
+
if (typeof metadata?.name !== 'string' || !metadata.name.trim())
|
|
18
|
+
return undefined;
|
|
19
|
+
if (typeof metadata.description !== 'string' || !metadata.description.trim())
|
|
20
|
+
return undefined;
|
|
21
|
+
return {
|
|
22
|
+
id: metadata.name.trim(),
|
|
23
|
+
description: metadata.description.trim(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Return only directories that satisfy the Codex/Claude skill file contract. */
|
|
31
|
+
export function discoverSkillDirectories(root) {
|
|
32
|
+
try {
|
|
33
|
+
if (!fs.existsSync(root))
|
|
34
|
+
return [];
|
|
35
|
+
return fs.readdirSync(root, { withFileTypes: true })
|
|
36
|
+
.filter(entry => !entry.name.startsWith('.'))
|
|
37
|
+
.filter(entry => entry.isDirectory() || entry.isSymbolicLink())
|
|
38
|
+
.map(entry => {
|
|
39
|
+
const dir = path.join(root, entry.name);
|
|
40
|
+
try {
|
|
41
|
+
if (!fs.statSync(dir).isDirectory())
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const metadata = readSkillMetadata(dir);
|
|
48
|
+
return metadata;
|
|
49
|
+
})
|
|
50
|
+
.filter((entry) => Boolean(entry));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -196,7 +196,7 @@ export function buildReloadHooks(deps) {
|
|
|
196
196
|
if (!handoffRuntime)
|
|
197
197
|
return;
|
|
198
198
|
try {
|
|
199
|
-
await handoffRuntime.recover([aid]);
|
|
199
|
+
await handoffRuntime.recover([aid], { discardExpired: false });
|
|
200
200
|
}
|
|
201
201
|
finally {
|
|
202
202
|
handoffRuntime.resumeAgent(aid);
|
|
@@ -33,6 +33,15 @@ export function parseCliIntent(inputArgv, source = 'menu.cli', options = {}) {
|
|
|
33
33
|
return parseAidCommand(argv, source);
|
|
34
34
|
if (cmd === 'storage')
|
|
35
35
|
return parseStorageCommand(argv, source);
|
|
36
|
+
if (cmd === 'version') {
|
|
37
|
+
const parsed = parseArgs(argv.slice(1));
|
|
38
|
+
if (parsed.positionals.length > 0)
|
|
39
|
+
return raw(source, argv, 'Unexpected version positional args');
|
|
40
|
+
const unsupported = Object.keys(parsed.args).filter(key => key !== 'format' && key !== 'json');
|
|
41
|
+
if (unsupported.length > 0)
|
|
42
|
+
return raw(source, argv, `Unsupported version option: --${unsupported[0]}`);
|
|
43
|
+
return recognized('ec.version', 'relation', source, parsed.args, argv);
|
|
44
|
+
}
|
|
36
45
|
if (cmd === 'status') {
|
|
37
46
|
return recognized('system.status', 'process', source, parseArgs(argv.slice(1)), argv);
|
|
38
47
|
}
|
|
@@ -9,6 +9,7 @@ import { isManagementRole } from '../../config/builtin-roles.js';
|
|
|
9
9
|
import { formatPeerKey } from '../relation/peer-identity.js';
|
|
10
10
|
import crypto from 'crypto';
|
|
11
11
|
import path from 'path';
|
|
12
|
+
import fs from 'fs';
|
|
12
13
|
import { parseDuration, parseTriggerSet, parseTriggerUpdate } from '../../trigger/parser.js';
|
|
13
14
|
import { applyTriggerPatch, replaceEditableTriggerDefinition } from '../../trigger/patch.js';
|
|
14
15
|
import { definitionRevision } from '../../trigger/validation.js';
|
|
@@ -19,14 +20,19 @@ import { resolveConfigCommand } from '../../config/resolved-config-op.js';
|
|
|
19
20
|
import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommand } from '../../config/config-operation-service.js';
|
|
20
21
|
import { hashArgv } from '../auth/authorization-audit.js';
|
|
21
22
|
import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
|
|
23
|
+
import { executeManagedAidOperation, ManagedAidOperationError, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
|
|
24
|
+
import { executeManagedGroupOperation, isManagedGroupRelationRead, ManagedGroupOperationError, resolveManagedGroupOperation, executeManagedMsgOperation, ManagedMsgOperationError, resolveManagedMsgOperation, executeManagedFsOperation, ManagedFsOperationError, resolveManagedFsOperation, } from '../../aun/msg/managed-operation.js';
|
|
22
25
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
26
|
+
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../protected-paths.js';
|
|
23
27
|
import { validateModelSelectionForRole } from '../model/model-permission.js';
|
|
28
|
+
import { getModelInfo } from '../model/model-catalog.js';
|
|
24
29
|
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride } from '../role/runtime-policy.js';
|
|
25
30
|
import { parsePeerKey } from '../relation/peer-identity.js';
|
|
26
31
|
import { isQuickCommand } from './slash-gate.js';
|
|
27
32
|
import { handleSlashCommand } from './slash-handler.js';
|
|
28
33
|
import { resolveChatMode } from '../message/peer-mode.js';
|
|
29
34
|
import { execMenuAction as menuExecMenuAction, execMenuForEcweb as menuExecMenuForEcweb, execMenuForControl as menuExecMenuForControl, execMenuQuery as menuExecMenuQuery, execMenuUpdate as menuExecMenuUpdate, getMenuItems as menuGetMenuItems, getSubMenuItems as menuGetSubMenuItems, } from './menu-handler.js';
|
|
35
|
+
import { getMenuCatalog, execMenuValueBatch, validateMenuValueBatchArgs } from './menu-catalog.js';
|
|
30
36
|
export { isProcessLevelOwner } from './menu-handler.js';
|
|
31
37
|
const CLI_EXEC_TIMEOUT_MS = 15_000;
|
|
32
38
|
const CLI_EXEC_MAX_OUTPUT = 128 * 1024;
|
|
@@ -93,6 +99,7 @@ export class CommandHandler {
|
|
|
93
99
|
triggerSchedulerFallbackAid;
|
|
94
100
|
daemonStatusProvider;
|
|
95
101
|
agentDelegationRegistry;
|
|
102
|
+
handoffRuntime;
|
|
96
103
|
/**
|
|
97
104
|
* Get the runner for a (channel, baseagent) pair.
|
|
98
105
|
*
|
|
@@ -675,6 +682,9 @@ export class CommandHandler {
|
|
|
675
682
|
setMessageQueue(messageQueue) {
|
|
676
683
|
this.messageQueue = messageQueue;
|
|
677
684
|
}
|
|
685
|
+
setHandoffRuntime(handoffRuntime) {
|
|
686
|
+
this.handoffRuntime = handoffRuntime;
|
|
687
|
+
}
|
|
678
688
|
setPermissionGateway(gateway) {
|
|
679
689
|
this.permissionGateway = gateway;
|
|
680
690
|
}
|
|
@@ -818,6 +828,29 @@ export class CommandHandler {
|
|
|
818
828
|
getMenuItems(role, chatType = 'private', scope = 'agent', authSubject) {
|
|
819
829
|
return menuGetMenuItems.call(this, role, chatType, scope, authSubject);
|
|
820
830
|
}
|
|
831
|
+
async getMenuCatalog(channel, channelId, subject, request, channelScope = 'agent') {
|
|
832
|
+
return await getMenuCatalog({
|
|
833
|
+
subject,
|
|
834
|
+
channelType: this.resolveChannelType(channel),
|
|
835
|
+
channelScope,
|
|
836
|
+
channelId,
|
|
837
|
+
request,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
async execMenuValueBatch(channel, channelId, subject, args, request, channelScope = 'agent') {
|
|
841
|
+
validateMenuValueBatchArgs(args);
|
|
842
|
+
return await execMenuValueBatch({
|
|
843
|
+
subject,
|
|
844
|
+
channelType: this.resolveChannelType(channel),
|
|
845
|
+
channelScope,
|
|
846
|
+
channelId,
|
|
847
|
+
request,
|
|
848
|
+
items: args?.items,
|
|
849
|
+
schemaVersion: args?.schemaVersion,
|
|
850
|
+
consistency: args?.consistency,
|
|
851
|
+
execQuery: async (cmd, resolvedChannelId, queryArgs, context) => this.execMenuQuery(cmd, channel, resolvedChannelId, context.subject.actorId, queryArgs, context.subject.chatType, context.subject.fromControlChannel, context.subject.identity, context.subject, channelScope === 'control' ? 'control' : 'menu'),
|
|
852
|
+
});
|
|
853
|
+
}
|
|
821
854
|
/** 动态子菜单:根据 cmd 路径返回选项列表(供 menu.query + cmd 使用) */
|
|
822
855
|
async getSubMenuItems(cmd, channel, channelId, userId, args, overrideIdentity, explicitChatType, fromControlChannel = false, authSubject, source = 'menu') {
|
|
823
856
|
return await menuGetSubMenuItems.call(this, cmd, channel, channelId, userId, args, overrideIdentity, explicitChatType, fromControlChannel, authSubject, source);
|
|
@@ -1569,6 +1602,337 @@ export class CommandHandler {
|
|
|
1569
1602
|
? { ok: true, result }
|
|
1570
1603
|
: { ok: false, code: result.code, error: result.error, ...(result.data ? { data: result.data } : {}) };
|
|
1571
1604
|
}
|
|
1605
|
+
/** Agent-managed AID CLI entrypoint. Private key and CA access stay inside the daemon. */
|
|
1606
|
+
async handleAidOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1607
|
+
if (!this.agentDelegationRegistry) {
|
|
1608
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
1609
|
+
}
|
|
1610
|
+
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1611
|
+
if (!delegation.ok)
|
|
1612
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1613
|
+
const session = await this.sessionManager.getSessionById(sessionId);
|
|
1614
|
+
if (!session)
|
|
1615
|
+
return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
|
|
1616
|
+
const parsed = resolveManagedAidOperation(argv);
|
|
1617
|
+
if (!parsed.ok)
|
|
1618
|
+
return { ok: false, code: parsed.code, error: parsed.reason };
|
|
1619
|
+
const grantSelfAid = delegation.grant.selfAid.replace(/^@/, '');
|
|
1620
|
+
const sessionSelfAid = session.selfAID?.replace(/^@/, '');
|
|
1621
|
+
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1622
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1623
|
+
}
|
|
1624
|
+
if (parsed.command.aid !== grantSelfAid) {
|
|
1625
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can be targeted' };
|
|
1626
|
+
}
|
|
1627
|
+
let conversationId;
|
|
1628
|
+
try {
|
|
1629
|
+
conversationId = parsePeerKey(delegation.grant.peerKey).channelId;
|
|
1630
|
+
}
|
|
1631
|
+
catch {
|
|
1632
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1633
|
+
}
|
|
1634
|
+
const subject = buildAuthSubject({
|
|
1635
|
+
selfAid: delegation.grant.selfAid,
|
|
1636
|
+
actorId: delegation.grant.actorId,
|
|
1637
|
+
channel: delegation.grant.channel,
|
|
1638
|
+
channelType: delegation.grant.channelType,
|
|
1639
|
+
channelId: conversationId,
|
|
1640
|
+
chatType: delegation.grant.chatType,
|
|
1641
|
+
conversationId,
|
|
1642
|
+
processOwners: loadDaemonConfig().owners ?? [],
|
|
1643
|
+
});
|
|
1644
|
+
const managementRole = isManagementRole(subject.role);
|
|
1645
|
+
const decision = authorizeOperation({
|
|
1646
|
+
source: 'agent-tool',
|
|
1647
|
+
subject,
|
|
1648
|
+
intent: {
|
|
1649
|
+
operation: parsed.command.operationId,
|
|
1650
|
+
scope: managementRole ? 'agent' : 'relation',
|
|
1651
|
+
source: 'agent-tool',
|
|
1652
|
+
args: { self: parsed.command.aid, targetId: parsed.command.aid },
|
|
1653
|
+
},
|
|
1654
|
+
auditMetadata: {
|
|
1655
|
+
argvHash: hashArgv(parsed.command.canonicalArgv),
|
|
1656
|
+
taskId: delegation.grant.taskId,
|
|
1657
|
+
messageId: delegation.grant.messageId,
|
|
1658
|
+
},
|
|
1659
|
+
});
|
|
1660
|
+
if (!decision.allow)
|
|
1661
|
+
return { ok: false, code: decision.code, error: decision.reason };
|
|
1662
|
+
try {
|
|
1663
|
+
return { ok: true, result: await executeManagedAidOperation(parsed.command) };
|
|
1664
|
+
}
|
|
1665
|
+
catch (error) {
|
|
1666
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1667
|
+
return {
|
|
1668
|
+
ok: false,
|
|
1669
|
+
code: error instanceof ManagedAidOperationError ? error.code : 'AID_OPERATION_FAILED',
|
|
1670
|
+
error: message,
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
/** Agent-managed group CLI entrypoint. AUN private material remains daemon-owned. */
|
|
1675
|
+
async handleGroupOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1676
|
+
if (!this.agentDelegationRegistry) {
|
|
1677
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
1678
|
+
}
|
|
1679
|
+
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1680
|
+
if (!delegation.ok)
|
|
1681
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1682
|
+
const session = await this.sessionManager.getSessionById(sessionId);
|
|
1683
|
+
if (!session)
|
|
1684
|
+
return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
|
|
1685
|
+
const parsed = resolveManagedGroupOperation(argv);
|
|
1686
|
+
if (!parsed.ok)
|
|
1687
|
+
return { ok: false, code: parsed.code, error: parsed.reason };
|
|
1688
|
+
const grant = delegation.grant;
|
|
1689
|
+
const grantSelfAid = grant.selfAid.replace(/^@/, '');
|
|
1690
|
+
const sessionSelfAid = session.selfAID?.replace(/^@/, '');
|
|
1691
|
+
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1692
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1693
|
+
}
|
|
1694
|
+
if (parsed.command.from !== grantSelfAid) {
|
|
1695
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can perform managed group operations' };
|
|
1696
|
+
}
|
|
1697
|
+
let conversationId;
|
|
1698
|
+
try {
|
|
1699
|
+
conversationId = parsePeerKey(grant.peerKey).channelId;
|
|
1700
|
+
}
|
|
1701
|
+
catch {
|
|
1702
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1703
|
+
}
|
|
1704
|
+
const subject = buildAuthSubject({
|
|
1705
|
+
selfAid: grant.selfAid,
|
|
1706
|
+
actorId: grant.actorId,
|
|
1707
|
+
channel: grant.channel,
|
|
1708
|
+
channelType: grant.channelType,
|
|
1709
|
+
channelId: conversationId,
|
|
1710
|
+
chatType: grant.chatType,
|
|
1711
|
+
conversationId,
|
|
1712
|
+
processOwners: loadDaemonConfig().owners ?? [],
|
|
1713
|
+
});
|
|
1714
|
+
const managementRole = isManagementRole(subject.role);
|
|
1715
|
+
if (!managementRole && (grant.channelType !== 'aun' || !isManagedGroupRelationRead(parsed.command))) {
|
|
1716
|
+
return { ok: false, code: 'ROLE_ACCESS_DENIED', error: `Role ${subject.role} cannot manage Agent group membership or settings` };
|
|
1717
|
+
}
|
|
1718
|
+
if ((parsed.command.kind === 'dissolve' || parsed.command.kind === 'owner') && subject.role !== 'owner') {
|
|
1719
|
+
return { ok: false, code: 'ROLE_ACCESS_DENIED', error: `${parsed.command.kind} requires Agent owner permission` };
|
|
1720
|
+
}
|
|
1721
|
+
const decision = authorizeOperation({
|
|
1722
|
+
source: 'agent-tool',
|
|
1723
|
+
subject,
|
|
1724
|
+
intent: {
|
|
1725
|
+
operation: parsed.command.operationId,
|
|
1726
|
+
scope: managementRole ? 'agent' : 'relation',
|
|
1727
|
+
source: 'agent-tool',
|
|
1728
|
+
args: {
|
|
1729
|
+
self: parsed.command.from,
|
|
1730
|
+
...(parsed.command.groupId ? {
|
|
1731
|
+
targetId: parsed.command.groupId,
|
|
1732
|
+
peer: parsed.command.groupId,
|
|
1733
|
+
peerKey: formatPeerKey(grant.channelType, parsed.command.groupId),
|
|
1734
|
+
} : {}),
|
|
1735
|
+
},
|
|
1736
|
+
},
|
|
1737
|
+
auditMetadata: {
|
|
1738
|
+
argvHash: hashArgv(parsed.command.canonicalArgv),
|
|
1739
|
+
taskId: grant.taskId,
|
|
1740
|
+
messageId: grant.messageId,
|
|
1741
|
+
},
|
|
1742
|
+
});
|
|
1743
|
+
if (!decision.allow)
|
|
1744
|
+
return { ok: false, code: decision.code, error: decision.reason };
|
|
1745
|
+
let rulesContent;
|
|
1746
|
+
if (parsed.command.kind === 'rules.set') {
|
|
1747
|
+
const filePath = String(parsed.command.filePath || '');
|
|
1748
|
+
const projectPath = path.resolve(session.projectPath);
|
|
1749
|
+
const resolvedPath = path.resolve(projectPath, filePath);
|
|
1750
|
+
if (!filePath || !isSameOrDescendant(resolvedPath, projectPath)) {
|
|
1751
|
+
return { ok: false, code: 'INVALID_RULES_PATH', error: 'Rules file must be inside the current session project' };
|
|
1752
|
+
}
|
|
1753
|
+
if (isHClassPath(resolvedPath) || isLClassPath(resolvedPath)) {
|
|
1754
|
+
return { ok: false, code: 'PROTECTED_RULES_PATH', error: 'Rules file cannot be read from a protected EvolCore path' };
|
|
1755
|
+
}
|
|
1756
|
+
try {
|
|
1757
|
+
const realProjectPath = fs.realpathSync(projectPath);
|
|
1758
|
+
const realPath = fs.realpathSync(resolvedPath);
|
|
1759
|
+
if (!isSameOrDescendant(realPath, realProjectPath)) {
|
|
1760
|
+
return { ok: false, code: 'INVALID_RULES_PATH', error: 'Rules file resolves outside the current session project' };
|
|
1761
|
+
}
|
|
1762
|
+
const stat = fs.statSync(realPath);
|
|
1763
|
+
if (!stat.isFile() || stat.size < 1 || stat.size > 4 * 1024) {
|
|
1764
|
+
return { ok: false, code: 'INVALID_RULES_CONTENT', error: 'Rules file must be a regular file from 1 to 4096 bytes' };
|
|
1765
|
+
}
|
|
1766
|
+
rulesContent = new TextDecoder('utf-8', { fatal: true }).decode(fs.readFileSync(realPath));
|
|
1767
|
+
}
|
|
1768
|
+
catch (error) {
|
|
1769
|
+
return {
|
|
1770
|
+
ok: false,
|
|
1771
|
+
code: 'INVALID_RULES_CONTENT',
|
|
1772
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
try {
|
|
1777
|
+
return { ok: true, result: await executeManagedGroupOperation(parsed.command, rulesContent) };
|
|
1778
|
+
}
|
|
1779
|
+
catch (error) {
|
|
1780
|
+
return {
|
|
1781
|
+
ok: false,
|
|
1782
|
+
code: error instanceof ManagedGroupOperationError ? error.code : 'GROUP_OPERATION_FAILED',
|
|
1783
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
/** Agent-managed private msg read entrypoint. AUN credentials and history roots remain daemon-owned. */
|
|
1788
|
+
async handleMsgOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1789
|
+
if (!this.agentDelegationRegistry) {
|
|
1790
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
1791
|
+
}
|
|
1792
|
+
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1793
|
+
if (!delegation.ok)
|
|
1794
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1795
|
+
const session = await this.sessionManager.getSessionById(sessionId);
|
|
1796
|
+
if (!session)
|
|
1797
|
+
return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
|
|
1798
|
+
const parsed = resolveManagedMsgOperation(argv);
|
|
1799
|
+
if (!parsed.ok)
|
|
1800
|
+
return { ok: false, code: parsed.code, error: parsed.reason };
|
|
1801
|
+
const grant = delegation.grant;
|
|
1802
|
+
const grantSelfAid = grant.selfAid.replace(/^@/, '');
|
|
1803
|
+
const sessionSelfAid = session.selfAID?.replace(/^@/, '');
|
|
1804
|
+
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1805
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1806
|
+
}
|
|
1807
|
+
if (grant.channelType !== 'aun' || grant.chatType !== 'private') {
|
|
1808
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed msg reads require the current AUN private relation' };
|
|
1809
|
+
}
|
|
1810
|
+
let peerAid;
|
|
1811
|
+
try {
|
|
1812
|
+
peerAid = parsePeerKey(grant.peerKey).channelId.replace(/^@/, '');
|
|
1813
|
+
}
|
|
1814
|
+
catch {
|
|
1815
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1816
|
+
}
|
|
1817
|
+
if (parsed.command.from !== grantSelfAid || parsed.command.target !== peerAid) {
|
|
1818
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN private relation can be targeted' };
|
|
1819
|
+
}
|
|
1820
|
+
const subject = buildAuthSubject({
|
|
1821
|
+
selfAid: grant.selfAid,
|
|
1822
|
+
actorId: grant.actorId,
|
|
1823
|
+
channel: grant.channel,
|
|
1824
|
+
channelType: grant.channelType,
|
|
1825
|
+
channelId: peerAid,
|
|
1826
|
+
chatType: grant.chatType,
|
|
1827
|
+
conversationId: peerAid,
|
|
1828
|
+
processOwners: loadDaemonConfig().owners ?? [],
|
|
1829
|
+
});
|
|
1830
|
+
const decision = authorizeOperation({
|
|
1831
|
+
source: 'agent-tool',
|
|
1832
|
+
subject,
|
|
1833
|
+
intent: {
|
|
1834
|
+
operation: parsed.command.operationId,
|
|
1835
|
+
scope: 'relation',
|
|
1836
|
+
source: 'agent-tool',
|
|
1837
|
+
args: {
|
|
1838
|
+
self: parsed.command.from,
|
|
1839
|
+
targetId: parsed.command.target,
|
|
1840
|
+
peer: parsed.command.target,
|
|
1841
|
+
peerKey: formatPeerKey('aun', parsed.command.target),
|
|
1842
|
+
},
|
|
1843
|
+
},
|
|
1844
|
+
auditMetadata: {
|
|
1845
|
+
argvHash: hashArgv(parsed.command.canonicalArgv),
|
|
1846
|
+
taskId: grant.taskId,
|
|
1847
|
+
messageId: grant.messageId,
|
|
1848
|
+
},
|
|
1849
|
+
});
|
|
1850
|
+
if (!decision.allow)
|
|
1851
|
+
return { ok: false, code: decision.code, error: decision.reason };
|
|
1852
|
+
try {
|
|
1853
|
+
return { ok: true, result: await executeManagedMsgOperation(parsed.command) };
|
|
1854
|
+
}
|
|
1855
|
+
catch (error) {
|
|
1856
|
+
return {
|
|
1857
|
+
ok: false,
|
|
1858
|
+
code: error instanceof ManagedMsgOperationError ? error.code : 'MSG_OPERATION_FAILED',
|
|
1859
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
/** Agent-managed current-group filesystem read entrypoint. */
|
|
1864
|
+
async handleFsOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1865
|
+
if (!this.agentDelegationRegistry) {
|
|
1866
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
1867
|
+
}
|
|
1868
|
+
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1869
|
+
if (!delegation.ok)
|
|
1870
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1871
|
+
const session = await this.sessionManager.getSessionById(sessionId);
|
|
1872
|
+
if (!session)
|
|
1873
|
+
return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
|
|
1874
|
+
const parsed = resolveManagedFsOperation(argv);
|
|
1875
|
+
if (!parsed.ok)
|
|
1876
|
+
return { ok: false, code: parsed.code, error: parsed.reason };
|
|
1877
|
+
const grant = delegation.grant;
|
|
1878
|
+
const actorAid = grant.selfAid.replace(/^@/, '');
|
|
1879
|
+
const sessionSelfAid = session.selfAID?.replace(/^@/, '');
|
|
1880
|
+
if (sessionSelfAid && sessionSelfAid !== actorAid) {
|
|
1881
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1882
|
+
}
|
|
1883
|
+
if (grant.channelType !== 'aun' || grant.chatType !== 'group') {
|
|
1884
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs reads require the current AUN group relation' };
|
|
1885
|
+
}
|
|
1886
|
+
let currentGroup;
|
|
1887
|
+
try {
|
|
1888
|
+
currentGroup = parsePeerKey(grant.peerKey).channelId;
|
|
1889
|
+
}
|
|
1890
|
+
catch {
|
|
1891
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1892
|
+
}
|
|
1893
|
+
if ((parsed.command.actorAid && parsed.command.actorAid !== actorAid) || parsed.command.groupId !== currentGroup) {
|
|
1894
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN group can be targeted' };
|
|
1895
|
+
}
|
|
1896
|
+
parsed.command.actorAid = actorAid;
|
|
1897
|
+
const subject = buildAuthSubject({
|
|
1898
|
+
selfAid: grant.selfAid,
|
|
1899
|
+
actorId: grant.actorId,
|
|
1900
|
+
channel: grant.channel,
|
|
1901
|
+
channelType: grant.channelType,
|
|
1902
|
+
channelId: currentGroup,
|
|
1903
|
+
chatType: grant.chatType,
|
|
1904
|
+
conversationId: currentGroup,
|
|
1905
|
+
processOwners: loadDaemonConfig().owners ?? [],
|
|
1906
|
+
});
|
|
1907
|
+
const decision = authorizeOperation({
|
|
1908
|
+
source: 'agent-tool',
|
|
1909
|
+
subject,
|
|
1910
|
+
intent: {
|
|
1911
|
+
operation: parsed.command.operationId,
|
|
1912
|
+
scope: 'relation',
|
|
1913
|
+
source: 'agent-tool',
|
|
1914
|
+
args: {
|
|
1915
|
+
self: actorAid,
|
|
1916
|
+
targetId: currentGroup,
|
|
1917
|
+
peer: currentGroup,
|
|
1918
|
+
peerKey: formatPeerKey('aun', currentGroup),
|
|
1919
|
+
},
|
|
1920
|
+
},
|
|
1921
|
+
auditMetadata: { argvHash: hashArgv(parsed.command.canonicalArgv), taskId: grant.taskId, messageId: grant.messageId },
|
|
1922
|
+
});
|
|
1923
|
+
if (!decision.allow)
|
|
1924
|
+
return { ok: false, code: decision.code, error: decision.reason };
|
|
1925
|
+
try {
|
|
1926
|
+
return { ok: true, result: await executeManagedFsOperation(parsed.command) };
|
|
1927
|
+
}
|
|
1928
|
+
catch (error) {
|
|
1929
|
+
return {
|
|
1930
|
+
ok: false,
|
|
1931
|
+
code: error instanceof ManagedFsOperationError ? error.code : 'FS_OPERATION_FAILED',
|
|
1932
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1572
1936
|
setAgentDelegationRegistry(registry) {
|
|
1573
1937
|
this.agentDelegationRegistry = registry;
|
|
1574
1938
|
}
|
|
@@ -1576,7 +1940,7 @@ export class CommandHandler {
|
|
|
1576
1940
|
* Agent ctl 入口:通过 IPC 接收 Agent 自主管理指令
|
|
1577
1941
|
* 复用现有 slash cmd 逻辑,权限继承 session 用户角色
|
|
1578
1942
|
*/
|
|
1579
|
-
async handleCtl(cmd, sessionId) {
|
|
1943
|
+
async handleCtl(cmd, sessionId, grant) {
|
|
1580
1944
|
logger.info(`[ctl] cmd="${cmd}" sessionId=${sessionId}`);
|
|
1581
1945
|
// 1. 白名单检查
|
|
1582
1946
|
const inputCmd = cmd.split(' ')[0];
|
|
@@ -1588,9 +1952,56 @@ export class CommandHandler {
|
|
|
1588
1952
|
if (!session) {
|
|
1589
1953
|
return { ok: false, error: '无效的 session' };
|
|
1590
1954
|
}
|
|
1955
|
+
if (!grant || grant.sessionId !== sessionId) {
|
|
1956
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'An active task delegation grant is required' };
|
|
1957
|
+
}
|
|
1958
|
+
const sessionSelfAid = session.selfAID?.replace(/^@/, '');
|
|
1959
|
+
const grantSelfAid = grant.selfAid.replace(/^@/, '');
|
|
1960
|
+
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1961
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1962
|
+
}
|
|
1963
|
+
let conversationId;
|
|
1964
|
+
try {
|
|
1965
|
+
conversationId = parsePeerKey(grant.peerKey).channelId;
|
|
1966
|
+
}
|
|
1967
|
+
catch {
|
|
1968
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1969
|
+
}
|
|
1970
|
+
const ctlSubject = buildAuthSubject({
|
|
1971
|
+
selfAid: grant.selfAid,
|
|
1972
|
+
actorId: grant.actorId,
|
|
1973
|
+
channel: grant.channel,
|
|
1974
|
+
channelType: grant.channelType,
|
|
1975
|
+
channelId: conversationId,
|
|
1976
|
+
chatType: grant.chatType,
|
|
1977
|
+
conversationId,
|
|
1978
|
+
processOwners: loadDaemonConfig().owners ?? [],
|
|
1979
|
+
});
|
|
1980
|
+
const ctlIntent = this.resolveCtlIntent(cmd, grantSelfAid, grant.peerKey);
|
|
1981
|
+
if (!ctlIntent)
|
|
1982
|
+
return { ok: false, code: 'NOT_ALLOWED', error: `不允许的指令: ${inputCmd}` };
|
|
1983
|
+
const ctlDecision = authorizeOperation({
|
|
1984
|
+
source: 'agent-tool',
|
|
1985
|
+
subject: ctlSubject,
|
|
1986
|
+
intent: ctlIntent,
|
|
1987
|
+
auditMetadata: {
|
|
1988
|
+
argvHash: hashArgv(['ctl', ...cmd.slice(1).split(/\s+/).filter(Boolean)]),
|
|
1989
|
+
taskId: grant.taskId,
|
|
1990
|
+
messageId: grant.messageId,
|
|
1991
|
+
},
|
|
1992
|
+
});
|
|
1993
|
+
if (!ctlDecision.allow)
|
|
1994
|
+
return { ok: false, code: ctlDecision.code, error: ctlDecision.reason };
|
|
1995
|
+
if (cmd.startsWith('/model info ')) {
|
|
1996
|
+
const modelId = cmd.slice('/model info '.length).trim();
|
|
1997
|
+
if (!modelId || /\s/.test(modelId))
|
|
1998
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: 'model info requires one model id' };
|
|
1999
|
+
const info = await getModelInfo(modelId, grantSelfAid, session.baseagent);
|
|
2000
|
+
return { ok: true, result: JSON.stringify(info, null, 2), data: { ok: true, ...info } };
|
|
2001
|
+
}
|
|
1591
2002
|
// 3. 从 session.metadata.peerId 获取 userId(用于权限判断)
|
|
1592
2003
|
const userId = session.metadata?.peerId;
|
|
1593
|
-
const ctlIdentity =
|
|
2004
|
+
const ctlIdentity = ctlSubject.identity;
|
|
1594
2005
|
// 4. /send 文本消息:直接通过 adapter 主动发送,不走 handle()
|
|
1595
2006
|
if (cmd.startsWith('/send ') || cmd === '/send') {
|
|
1596
2007
|
// 解析 --encrypt 标志和消息文本
|
|
@@ -1625,9 +2036,9 @@ export class CommandHandler {
|
|
|
1625
2036
|
const parts = sendArgs.split(/\s+/);
|
|
1626
2037
|
const filePath = parts[parts.length - 1];
|
|
1627
2038
|
if (filePath) {
|
|
1628
|
-
const resolved = path.resolve(session.projectPath, filePath)
|
|
1629
|
-
const
|
|
1630
|
-
if (
|
|
2039
|
+
const resolved = path.resolve(session.projectPath, filePath);
|
|
2040
|
+
const relative = path.relative(path.resolve(session.projectPath), resolved);
|
|
2041
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
1631
2042
|
return { ok: false, error: '路径越界:只能发送项目目录下的文件' };
|
|
1632
2043
|
}
|
|
1633
2044
|
}
|
|
@@ -1640,7 +2051,7 @@ export class CommandHandler {
|
|
|
1640
2051
|
// 6. 调用现有 handle(),不传 sendMessage 回调(结果直接返回)
|
|
1641
2052
|
try {
|
|
1642
2053
|
const result = await this._handleInternal(cmd, session.channel, session.channelId, undefined, // 不发送消息
|
|
1643
|
-
userId, session.threadId || undefined, session.chatType, undefined, undefined, session.selfAID, ctlIdentity);
|
|
2054
|
+
userId, session.threadId || undefined, session.chatType, undefined, undefined, session.selfAID, ctlIdentity, ctlSubject);
|
|
1644
2055
|
const text = typeof result === 'string' ? result : (result && 'text' in result ? result.text : '(无输出)');
|
|
1645
2056
|
if (result && typeof result === 'object' && 'kind' in result && result.kind === 'command.error') {
|
|
1646
2057
|
return { ok: false, error: text || '执行失败' };
|
|
@@ -1651,6 +2062,56 @@ export class CommandHandler {
|
|
|
1651
2062
|
return { ok: false, error: err.message };
|
|
1652
2063
|
}
|
|
1653
2064
|
}
|
|
2065
|
+
resolveCtlIntent(cmd, selfAid, peerKey) {
|
|
2066
|
+
const [name = '', ...args] = cmd.trim().slice(1).trim().split(/\s+/);
|
|
2067
|
+
const relationArgs = { self: selfAid, peer: peerKey, peerKey };
|
|
2068
|
+
const intent = (operation, extra = {}) => ({
|
|
2069
|
+
operation,
|
|
2070
|
+
scope: 'relation',
|
|
2071
|
+
source: 'agent-tool',
|
|
2072
|
+
args: { ...relationArgs, ...extra },
|
|
2073
|
+
});
|
|
2074
|
+
if (name === 'send')
|
|
2075
|
+
return intent('ec.ctl.send');
|
|
2076
|
+
if (name === 'file')
|
|
2077
|
+
return intent('ec.ctl.file');
|
|
2078
|
+
if (name === 'status')
|
|
2079
|
+
return intent('session.current');
|
|
2080
|
+
if (name === 'pwd')
|
|
2081
|
+
return intent('project.current');
|
|
2082
|
+
if (name === 'name' || name === 'rename')
|
|
2083
|
+
return intent('session.rename');
|
|
2084
|
+
if (name === 'queue') {
|
|
2085
|
+
const action = args.includes('--clear') ? 'clear'
|
|
2086
|
+
: args.includes('--cancel') ? 'cancel'
|
|
2087
|
+
: args.includes('--interrupt') ? 'interrupt'
|
|
2088
|
+
: 'read';
|
|
2089
|
+
return intent(`ec.ctl.queue.${action}`);
|
|
2090
|
+
}
|
|
2091
|
+
if (name === 'model') {
|
|
2092
|
+
if (args.length === 0 || args[0] === 'list')
|
|
2093
|
+
return intent('model.list');
|
|
2094
|
+
if (args[0] === 'current')
|
|
2095
|
+
return intent('model.current');
|
|
2096
|
+
if (args[0] === 'info')
|
|
2097
|
+
return intent('model.info', { model: args[1] });
|
|
2098
|
+
if (args[0] === 'reset')
|
|
2099
|
+
return intent('model.reset');
|
|
2100
|
+
if (args[0] === 'check') {
|
|
2101
|
+
return { operation: 'model.check', scope: 'agent', source: 'agent-tool', args: { self: selfAid } };
|
|
2102
|
+
}
|
|
2103
|
+
return intent('model.use', { model: args[0] });
|
|
2104
|
+
}
|
|
2105
|
+
if (name === 'effort') {
|
|
2106
|
+
return args.length === 0
|
|
2107
|
+
? intent('model.effort.current')
|
|
2108
|
+
: intent('model.effort', { effort: args[0] });
|
|
2109
|
+
}
|
|
2110
|
+
if (name === 'restart') {
|
|
2111
|
+
return { operation: 'system.restart', scope: 'process', source: 'agent-tool', args: { self: selfAid } };
|
|
2112
|
+
}
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
1654
2115
|
// ── Queue command ──
|
|
1655
2116
|
/** 提取命名参数值(如 --cancel msg123 → "msg123") */
|
|
1656
2117
|
extractArg(args, flag) {
|