evolcore 0.0.23 → 0.0.24
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 +50 -0
- package/README.md +3 -1
- package/bin/codex-managed-acl.ps1 +7 -2
- package/bin/codex-managed-hook.mjs +127 -10
- package/bin/ec-safe-output.js +85 -3
- package/bin/install-codex-managed-hooks.mjs +17 -6
- package/dist/agents/baseagent-availability.js +4 -4
- package/dist/agents/baseagent.js +29 -1
- package/dist/agents/claude-runner.js +148 -13
- package/dist/agents/codex/app-server-client.js +18 -8
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +215 -0
- package/dist/agents/codex-runner.js +438 -59
- package/dist/agents/ecagent-runner.js +17 -3
- package/dist/agents/gemini-runner.js +60 -2
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/managed-operation.js +28 -6
- package/dist/aun/aid/store.js +41 -0
- package/dist/aun/msg/managed-operation.js +6 -1
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +4 -5
- package/dist/channels/aun.js +147 -76
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +29 -3
- package/dist/cli/agent-create.js +538 -0
- package/dist/cli/agent.js +26 -675
- package/dist/cli/aun-commands.js +60 -20
- package/dist/cli/ctl-command.js +2 -0
- package/dist/cli/daemon-commands.js +148 -17
- package/dist/cli/index.js +2 -2
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/restart-monitor.js +432 -8
- package/dist/cli/stats.js +110 -0
- package/dist/config/builtin-roles.js +2 -0
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +72 -19
- package/dist/core/auth/operation-authorizer.js +63 -3
- package/dist/core/auth/operation-catalog.js +10 -2
- package/dist/core/auth/remote-access-store.js +3 -2
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +7 -2
- package/dist/core/command/command-handler.js +87 -25
- package/dist/core/command/menu-catalog.js +3 -1
- package/dist/core/command/menu-handler.js +274 -70
- package/dist/core/command/menu-protocol.js +6 -2
- package/dist/core/command/slash-gate.js +1 -3
- package/dist/core/command/slash-handler.js +157 -48
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +3 -0
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +73 -21
- package/dist/core/model/catalog-service.js +3 -1
- package/dist/core/model/model-catalog.js +10 -0
- package/dist/core/model/model-permission.js +4 -1
- package/dist/core/permission/approval-gateway.js +169 -33
- package/dist/core/permission/tool-policy.js +13 -6
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +174 -28
- package/dist/ipc.js +68 -21
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/error-utils.js +11 -4
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +24 -1
- package/dist/utils/windows-autostart.js +1 -1
- package/kits/templates/roles/admin.json +2 -0
- package/package.json +7 -3
- package/AGENTS.md +0 -62
- package/dist/utils/codex-cli.js +0 -43
|
@@ -22,7 +22,7 @@ import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
|
|
|
22
22
|
import { auditCommandAuthorization, auditFullAccessEvent, hashArgv } from '../auth/authorization-audit.js';
|
|
23
23
|
import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
|
|
24
24
|
import { hasTrustedFullAccessDelegation, trustedExecutionIdentityInput } from '../auth/agent-delegation.js';
|
|
25
|
-
import { executeManagedAidOperation, ManagedAidOperationError, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
|
|
25
|
+
import { executeManagedAidOperation, ManagedAidOperationError, managedAidShowViewForRole, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
|
|
26
26
|
import { executeManagedGroupOperation, isManagedGroupRelationRead, ManagedGroupOperationError, resolveManagedGroupOperation, executeManagedMsgOperation, ManagedMsgOperationError, resolveManagedMsgOperation, executeManagedFsOperation, ManagedFsOperationError, resolveManagedFsOperation, } from '../../aun/msg/managed-operation.js';
|
|
27
27
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
28
28
|
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../permission/protected-paths.js';
|
|
@@ -108,6 +108,7 @@ export class CommandHandler {
|
|
|
108
108
|
interactionRouter;
|
|
109
109
|
statsCollector;
|
|
110
110
|
agentMap;
|
|
111
|
+
legacySingleRunnerMode;
|
|
111
112
|
primaryRunnerKey;
|
|
112
113
|
agentRegistry;
|
|
113
114
|
agentApplicationService;
|
|
@@ -131,23 +132,42 @@ export class CommandHandler {
|
|
|
131
132
|
* different backend; that would corrupt session metadata.
|
|
132
133
|
*/
|
|
133
134
|
getAgent(channel, baseagent) {
|
|
134
|
-
|
|
135
|
+
const requestedBaseagent = typeof baseagent === 'string' ? baseagent.trim() : '';
|
|
136
|
+
// Support callers that intentionally provide a single-runner map keyed by
|
|
137
|
+
// the backend name. This exact lookup does not select another agent.
|
|
138
|
+
if (requestedBaseagent && this.agentMap.has(requestedBaseagent)) {
|
|
139
|
+
return this.agentMap.get(requestedBaseagent);
|
|
140
|
+
}
|
|
141
|
+
// A caller that passed one runner explicitly has already selected the
|
|
142
|
+
// backend; this compatibility path does not exist for multi-agent maps.
|
|
143
|
+
if (this.legacySingleRunnerMode && this.agentMap.size === 1
|
|
144
|
+
&& this.agentMap.values().next().value?.name === requestedBaseagent) {
|
|
145
|
+
return this.agentMap.values().next().value;
|
|
146
|
+
}
|
|
147
|
+
if (channel && requestedBaseagent) {
|
|
135
148
|
const owner = this.agentRegistry?.resolveByChannel(channel);
|
|
136
149
|
const evolName = owner?.name || '<unknown>';
|
|
137
|
-
const key = `${evolName}::${
|
|
150
|
+
const key = `${evolName}::${requestedBaseagent}`;
|
|
138
151
|
if (this.agentMap.has(key))
|
|
139
152
|
return this.agentMap.get(key);
|
|
140
153
|
if (owner) {
|
|
141
|
-
throw new BaseagentRunnerUnavailableError(evolName,
|
|
154
|
+
throw new BaseagentRunnerUnavailableError(evolName, requestedBaseagent, this.getAvailableBaseagentsForOwner(evolName));
|
|
142
155
|
}
|
|
143
|
-
const
|
|
144
|
-
.
|
|
145
|
-
if (
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
const matches = [...this.agentMap.entries()]
|
|
157
|
+
.filter(([runnerKey]) => runnerKey.endsWith(`::${requestedBaseagent}`));
|
|
158
|
+
if (matches.length === 1)
|
|
159
|
+
return matches[0][1];
|
|
160
|
+
logger.error(`[RunnerLookup] unavailable channel=${channel ?? '<none>'} baseagent=${requestedBaseagent} owner=${evolName} keys=${[...this.agentMap.keys()].join(',')}`);
|
|
161
|
+
throw new BaseagentRunnerUnavailableError('<unknown>', requestedBaseagent, []);
|
|
162
|
+
}
|
|
163
|
+
if (requestedBaseagent) {
|
|
164
|
+
const matches = [...this.agentMap.entries()]
|
|
165
|
+
.filter(([runnerKey]) => runnerKey.endsWith(`::${requestedBaseagent}`));
|
|
166
|
+
if (matches.length === 1)
|
|
167
|
+
return matches[0][1];
|
|
168
|
+
}
|
|
169
|
+
logger.error(`[RunnerLookup] unavailable channel=${channel ?? '<none>'} baseagent=${requestedBaseagent || '<missing>'} keys=${[...this.agentMap.keys()].join(',')}`);
|
|
170
|
+
throw new BaseagentRunnerUnavailableError('<unknown>', requestedBaseagent || '<missing>', []);
|
|
151
171
|
}
|
|
152
172
|
getAvailableBaseagentsForOwner(evolName) {
|
|
153
173
|
const prefix = `${evolName}::`;
|
|
@@ -205,11 +225,13 @@ export class CommandHandler {
|
|
|
205
225
|
this.eventBus = eventBus;
|
|
206
226
|
if (agentRunnerOrMap instanceof Map) {
|
|
207
227
|
this.agentMap = agentRunnerOrMap;
|
|
228
|
+
this.legacySingleRunnerMode = false;
|
|
208
229
|
this.primaryRunnerKey = primaryRunnerKey || '<unknown>::claude';
|
|
209
230
|
}
|
|
210
231
|
else {
|
|
211
232
|
// 测试 / 单 runner 路径:占位 agent name 用 '<unknown>'
|
|
212
233
|
this.agentMap = new Map([[`<unknown>::${agentRunnerOrMap.name}`, agentRunnerOrMap]]);
|
|
234
|
+
this.legacySingleRunnerMode = true;
|
|
213
235
|
this.primaryRunnerKey = `<unknown>::${agentRunnerOrMap.name}`;
|
|
214
236
|
}
|
|
215
237
|
}
|
|
@@ -424,13 +446,13 @@ export class CommandHandler {
|
|
|
424
446
|
return fallback;
|
|
425
447
|
}
|
|
426
448
|
}
|
|
427
|
-
validateTriggerPermissionModeForRole(mode, isAdmin, role, selfAid, isDaemonOwner = false) {
|
|
449
|
+
validateTriggerPermissionModeForRole(mode, isAdmin, role, selfAid, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
428
450
|
if (!mode)
|
|
429
451
|
return undefined;
|
|
430
452
|
if (mode === 'fullaccess') {
|
|
431
453
|
if (!isFullAccessEnabled())
|
|
432
454
|
return 'fullaccess 功能未启用';
|
|
433
|
-
return
|
|
455
|
+
return canApprovePersistentFullAccess ? undefined : '无权限:只有 daemon-owner 可以配置 fullaccess Trigger';
|
|
434
456
|
}
|
|
435
457
|
if (!role) {
|
|
436
458
|
if (mode === 'bypass' && !isAdmin)
|
|
@@ -486,7 +508,9 @@ export class CommandHandler {
|
|
|
486
508
|
return modelDecision.message || '当前角色不允许使用该 Trigger 模型';
|
|
487
509
|
}
|
|
488
510
|
if (definition.execution.effort) {
|
|
489
|
-
|
|
511
|
+
if (!executionBaseagent)
|
|
512
|
+
return '无法确定 Trigger 会话的 baseagent';
|
|
513
|
+
const base = executionBaseagent;
|
|
490
514
|
const effortDecision = validateRuntimeStringFieldOverride({
|
|
491
515
|
selfAid: definition.agentAid,
|
|
492
516
|
role,
|
|
@@ -575,7 +599,7 @@ export class CommandHandler {
|
|
|
575
599
|
} : undefined;
|
|
576
600
|
const permissionMode = this.triggerPermissionFromParsed(parsed);
|
|
577
601
|
const triggerRole = this.triggerRoleContext(schedulerAid, channel, peerId, chatType);
|
|
578
|
-
const permissionError = this.validateTriggerPermissionModeForRole(permissionMode, isAdmin, triggerRole.role, schedulerAid, isDaemonOwner);
|
|
602
|
+
const permissionError = this.validateTriggerPermissionModeForRole(permissionMode, isAdmin, triggerRole.role, schedulerAid, isDaemonOwner, canApprovePersistentFullAccess);
|
|
579
603
|
if (permissionError)
|
|
580
604
|
return { error: permissionError };
|
|
581
605
|
if (permissionMode === 'fullaccess' && !canApprovePersistentFullAccess) {
|
|
@@ -604,7 +628,7 @@ export class CommandHandler {
|
|
|
604
628
|
};
|
|
605
629
|
}
|
|
606
630
|
}
|
|
607
|
-
const authorizedBy = permissionMode === 'fullaccess' &&
|
|
631
|
+
const authorizedBy = permissionMode === 'fullaccess' && canApprovePersistentFullAccess
|
|
608
632
|
? this.canonicalDaemonOwnerId(schedulerAid, channel, peerId, chatType)
|
|
609
633
|
: undefined;
|
|
610
634
|
const definition = {
|
|
@@ -1000,8 +1024,8 @@ export class CommandHandler {
|
|
|
1000
1024
|
return await menuExecMenuQuery.call(this, cmd, channel, channelId, userId, args, explicitChatType, fromControlChannel, overrideIdentity, authSubject, source);
|
|
1001
1025
|
}
|
|
1002
1026
|
/** menu.update — 写入新值。 */
|
|
1003
|
-
async execMenuUpdate(cmd, value, channel, channelId, userId, overrideIdentity, fromControlChannel = false, args, authSubject, source = 'menu') {
|
|
1004
|
-
return await menuExecMenuUpdate.call(this, cmd, value, channel, channelId, userId, overrideIdentity, fromControlChannel, args, authSubject, source);
|
|
1027
|
+
async execMenuUpdate(cmd, value, channel, channelId, userId, overrideIdentity, fromControlChannel = false, args, authSubject, source = 'menu', transportReplyContext) {
|
|
1028
|
+
return await menuExecMenuUpdate.call(this, cmd, value, channel, channelId, userId, overrideIdentity, fromControlChannel, args, authSubject, source, transportReplyContext);
|
|
1005
1029
|
}
|
|
1006
1030
|
/** menu.action — 触发动词。 */
|
|
1007
1031
|
async execMenuAction(cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel = false, authSubject, source = 'menu', transportReplyContext, deferSideEffect) {
|
|
@@ -1103,7 +1127,7 @@ export class CommandHandler {
|
|
|
1103
1127
|
/** 把 menu.action 委派给已有 slash 命令处理逻辑,把 OutboundPayload 包成结构化结果。 */
|
|
1104
1128
|
async delegateAsAction(action, slashCmd, channel, channelId, userId, opts = {}) {
|
|
1105
1129
|
try {
|
|
1106
|
-
const result = await this._handleInternal(slashCmd, channel, channelId, undefined, userId,
|
|
1130
|
+
const result = await this._handleInternal(slashCmd, channel, channelId, undefined, userId, opts.threadId, opts.chatType, undefined, undefined, undefined, opts.overrideIdentity, opts.authSubject, opts.replyContext);
|
|
1107
1131
|
if (result == null) {
|
|
1108
1132
|
// null / undefined: 命令未识别或前置守卫拦截(如 idle 检查),视为失败
|
|
1109
1133
|
return { error: '命令未执行(可能被前置守卫拦截)', code: 'EXEC_FAILED' };
|
|
@@ -1432,7 +1456,7 @@ export class CommandHandler {
|
|
|
1432
1456
|
return { ok: false, error: err?.message || String(err) };
|
|
1433
1457
|
}
|
|
1434
1458
|
if (updated.execution.permissionMode === 'fullaccess') {
|
|
1435
|
-
if (!isDaemonOwner) {
|
|
1459
|
+
if (!isDaemonOwner && !canApprovePersistentFullAccess) {
|
|
1436
1460
|
return { ok: false, code: 'FORBIDDEN', error: '无权限:只有 daemon-owner 可以配置 fullaccess Trigger' };
|
|
1437
1461
|
}
|
|
1438
1462
|
if (!canApprovePersistentFullAccess) {
|
|
@@ -1455,7 +1479,7 @@ export class CommandHandler {
|
|
|
1455
1479
|
return { ok: false, error: '无权限:只有 owner/admin 可以修改 script trigger' };
|
|
1456
1480
|
}
|
|
1457
1481
|
const triggerRole = this.triggerRoleContext(definition.agentAid, definition.origin?.channelKey || channel, definition.origin?.peerId || peerId);
|
|
1458
|
-
const permissionError = this.validateTriggerPermissionModeForRole(updated.execution.permissionMode, isAdmin, triggerRole.role, definition.agentAid, isDaemonOwner);
|
|
1482
|
+
const permissionError = this.validateTriggerPermissionModeForRole(updated.execution.permissionMode, isAdmin, triggerRole.role, definition.agentAid, isDaemonOwner, canApprovePersistentFullAccess);
|
|
1459
1483
|
if (permissionError) {
|
|
1460
1484
|
return {
|
|
1461
1485
|
ok: false,
|
|
@@ -1922,7 +1946,10 @@ export class CommandHandler {
|
|
|
1922
1946
|
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1923
1947
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1924
1948
|
}
|
|
1925
|
-
|
|
1949
|
+
const requiresSelfAid = parsed.command.kind === 'show' || parsed.command.kind === 'agentmd.put';
|
|
1950
|
+
if (requiresSelfAid
|
|
1951
|
+
&& parsed.command.aid !== grantSelfAid
|
|
1952
|
+
&& !hasTrustedFullAccessDelegation(delegation.grant)) {
|
|
1926
1953
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can be targeted' };
|
|
1927
1954
|
}
|
|
1928
1955
|
let conversationId;
|
|
@@ -1962,7 +1989,12 @@ export class CommandHandler {
|
|
|
1962
1989
|
if (!decision.allow)
|
|
1963
1990
|
return { ok: false, code: decision.code, error: decision.reason };
|
|
1964
1991
|
try {
|
|
1965
|
-
return {
|
|
1992
|
+
return {
|
|
1993
|
+
ok: true,
|
|
1994
|
+
result: await executeManagedAidOperation(parsed.command, {
|
|
1995
|
+
showView: managedAidShowViewForRole(subject.role),
|
|
1996
|
+
}),
|
|
1997
|
+
};
|
|
1966
1998
|
}
|
|
1967
1999
|
catch (error) {
|
|
1968
2000
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2188,7 +2220,14 @@ export class CommandHandler {
|
|
|
2188
2220
|
if (!hasTrustedFullAccessDelegation(grant) && (grant.channelType !== 'aun' || grant.chatType !== 'private')) {
|
|
2189
2221
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed msg reads require the current AUN private relation' };
|
|
2190
2222
|
}
|
|
2191
|
-
|
|
2223
|
+
// A management role may read history for any peer, one peer per query.
|
|
2224
|
+
// `issuedRole` is resolved daemon-side from the persisted relation
|
|
2225
|
+
// config, so it cannot be spoofed by the calling task. Narrower roles
|
|
2226
|
+
// stay pinned here, and authorizeOperation below remains the authority
|
|
2227
|
+
// for every role (member carries currentRelationOnly; visitor is denied).
|
|
2228
|
+
const mayReadCrossPeer = hasTrustedFullAccessDelegation(grant)
|
|
2229
|
+
|| (parsed.command.kind === 'history' && isAdminRole(grant.issuedRole));
|
|
2230
|
+
if (!mayReadCrossPeer && parsed.command.target !== conversationId) {
|
|
2192
2231
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN private relation can be targeted' };
|
|
2193
2232
|
}
|
|
2194
2233
|
}
|
|
@@ -2458,6 +2497,29 @@ export class CommandHandler {
|
|
|
2458
2497
|
});
|
|
2459
2498
|
if (!ctlDecision.allow)
|
|
2460
2499
|
return { ok: false, code: ctlDecision.code, error: ctlDecision.reason };
|
|
2500
|
+
if (cmd.startsWith('/status ') && cmd !== '/status --format json') {
|
|
2501
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: 'status 仅支持可选参数 --format json' };
|
|
2502
|
+
}
|
|
2503
|
+
if (cmd === '/status --format json') {
|
|
2504
|
+
const context = {
|
|
2505
|
+
selfAid: grantSelfAid,
|
|
2506
|
+
sessionId,
|
|
2507
|
+
taskId: grant.taskId,
|
|
2508
|
+
...(grant.messageId ? { messageId: grant.messageId } : {}),
|
|
2509
|
+
baseagent: session.baseagent,
|
|
2510
|
+
channel: grant.channelType,
|
|
2511
|
+
channelKey: grant.channel,
|
|
2512
|
+
channelId: conversationId,
|
|
2513
|
+
chatType: grant.chatType,
|
|
2514
|
+
peerKey: grant.peerKey,
|
|
2515
|
+
peerId: conversationId,
|
|
2516
|
+
peerRole: ctlSubject.role,
|
|
2517
|
+
permissionMode: grant.permissionMode ?? ctlSubject.permissionMode,
|
|
2518
|
+
projectPath: path.resolve(session.projectPath),
|
|
2519
|
+
...(grant.sessionRuntimeDir ? { tmpDir: grant.sessionRuntimeDir } : {}),
|
|
2520
|
+
};
|
|
2521
|
+
return { ok: true, result: JSON.stringify(context, null, 2), data: context };
|
|
2522
|
+
}
|
|
2461
2523
|
if (cmd.startsWith('/model info ')) {
|
|
2462
2524
|
const modelId = cmd.slice('/model info '.length).trim();
|
|
2463
2525
|
if (!modelId || /\s/.test(modelId))
|
|
@@ -83,6 +83,7 @@ const ENTRIES = [
|
|
|
83
83
|
{ id: 'system.monitor', type: 'list', contexts: ['process'], name: 'system', label: '运行监控快照', section: 'process', order: 11, query: { cmd: '/system', args: { view: 'monitor' }, operation: 'system.status', scope: 'process' }, control: 'list' },
|
|
84
84
|
{ id: 'system.logs', type: 'list', contexts: ['process'], name: 'system', label: 'Daemon 日志', section: 'process', order: 12, query: { cmd: '/system', args: { view: 'logs' }, operation: 'system.status', scope: 'process' }, options: { cmd: '/system', args: { option: 'logs' }, operation: 'system.status', scope: 'process', mode: 'dynamic' }, control: 'list' },
|
|
85
85
|
{ id: 'config.rawFile', type: 'list', contexts: ['process'], name: 'config', label: '原始配置文件', section: 'process', order: 13, query: { cmd: '/config', args: { view: 'rawFile' }, operation: 'config.read', scope: 'process' }, options: { cmd: '/config', args: { option: 'rawFile' }, operation: 'config.read', scope: 'process', mode: 'dynamic' }, control: 'list' },
|
|
86
|
+
{ id: 'baseagent.models', type: 'list', contexts: ['process'], name: 'baseagent-models', label: 'Baseagent 模型目录', section: 'process', order: 41, query: { cmd: '/baseagent-models', args: { baseagent: 'claude' }, operation: 'baseagent.models.list', scope: 'process' }, control: 'list' },
|
|
86
87
|
{ id: 'system.restart', type: 'action', contexts: ['process'], name: 'system', label: '重启服务', section: 'process', order: 20, action: { cmd: '/system', action: 'restart', operation: 'system.restart', scope: 'process' } },
|
|
87
88
|
{ id: 'system.upgrade', type: 'action', contexts: ['process'], name: 'system', label: '升级服务', section: 'process', order: 30, action: { cmd: '/system', action: 'upgrade', operation: 'system.upgrade', scope: 'process' } },
|
|
88
89
|
{ id: 'agent.list', type: 'list', contexts: ['process'], name: 'agent', label: 'Agent 列表', section: 'process', order: 40, options: { cmd: '/agent', args: { options: 'all' }, operation: 'agent.list', scope: 'control', mode: 'dynamic' }, control: 'list' },
|
|
@@ -110,7 +111,8 @@ export async function resolveMenuCatalogContext(input) {
|
|
|
110
111
|
throw { code: 'FORBIDDEN', message: 'control catalog context requires a control subject' };
|
|
111
112
|
}
|
|
112
113
|
if (requested === 'process') {
|
|
113
|
-
if (input.channelScope !== 'control' || !subject.fromControlChannel
|
|
114
|
+
if (input.channelScope !== 'control' || !subject.fromControlChannel
|
|
115
|
+
|| (!subject.isDaemonOwner && subject.processRole !== 'daemon-service')) {
|
|
114
116
|
throw {
|
|
115
117
|
code: 'NOT_ALLOWED',
|
|
116
118
|
message: formatAuthorizationDenial('Process context requires a DaemonOwner control subject.', 'DAEMON_OWNER_REQUIRED'),
|