evolcore 0.0.17 → 0.0.19
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 +51 -0
- package/bin/codex-managed-hook.mjs +16 -7
- package/bin/install-codex-managed-hooks.mjs +4 -2
- package/dist/agents/claude-runner.js +113 -24
- package/dist/agents/codex-app-server-client.js +6 -1
- package/dist/agents/codex-runner.js +43 -24
- package/dist/agents/ecagent-runner.js +39 -10
- package/dist/agents/gemini-runner.js +90 -19
- package/dist/aun/aid/store.js +36 -0
- package/dist/aun/msg/p2p.js +20 -8
- package/dist/channels/aun.js +159 -21
- package/dist/cli/agent-command.js +67 -6
- package/dist/cli/agent.js +26 -0
- package/dist/cli/command-log.js +23 -4
- package/dist/cli/daemon-commands.js +82 -14
- package/dist/cli/init.js +21 -5
- package/dist/cli/restart-monitor.js +13 -6
- package/dist/cli/watch-logs.js +2 -2
- package/dist/config/builtin-roles.js +5 -1
- package/dist/config/role-ranks.js +4 -0
- package/dist/core/audit/event-key.js +29 -0
- package/dist/core/audit/log-integrity.js +13 -3
- package/dist/core/auth/auth-gateway.js +14 -18
- package/dist/core/auth/authorization-audit.js +110 -3
- package/dist/core/auth/authorization-denial.js +17 -0
- package/dist/core/auth/operation-authorizer.js +143 -18
- package/dist/core/auth/operation-catalog.js +21 -5
- package/dist/core/bootstrap-messages.js +11 -6
- package/dist/core/bootstrap-service.js +26 -4
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +25 -16
- package/dist/core/command/command-handler.js +50 -4
- package/dist/core/command/group-menu.js +1 -1
- package/dist/core/command/menu-catalog.js +32 -7
- package/dist/core/command/menu-handler.js +59 -23
- package/dist/core/command/menu-protocol.js +196 -0
- package/dist/core/command/slash-gate.js +14 -5
- package/dist/core/command/slash-handler.js +81 -99
- package/dist/core/data-migration.js +10 -4
- package/dist/core/event-catalog.js +18 -0
- package/dist/core/message/message-bridge.js +66 -8
- package/dist/core/message/response-engine.js +147 -10
- package/dist/core/permission/ec-command-parser.js +148 -22
- package/dist/core/permission/sandbox-runtime.js +79 -13
- package/dist/core/permission/tool-policy.js +19 -7
- package/dist/index.js +357 -48
- package/dist/ipc.js +81 -5
- package/dist/paths.js +0 -3
- package/dist/utils/atomic-write.js +45 -11
- package/dist/utils/logger.js +27 -0
- package/dist/utils/windows-autostart.js +740 -83
- package/ecagent/dist/harness/agent-harness.d.ts +1 -1
- package/ecagent/dist/harness/agent-harness.js +6 -4
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/group-rules.md +2 -1
- package/kits/docs/identity/ROLE_DETAIL.md +3 -1
- package/kits/docs/path-registry.md +1 -1
- package/kits/eck_manifest.json +25 -16
- package/kits/rules/01-overview.md +5 -5
- package/kits/rules/02-navigation.md +2 -2
- package/kits/rules/03-identity.md +1 -1
- package/kits/rules/04-relation.md +4 -4
- package/kits/rules/05-venue.md +5 -5
- package/kits/templates/bootstrap-welcome.md +3 -1
- package/kits/templates/system-fragments/bootstrap.md +17 -9
- package/package.json +1 -1
|
@@ -11,22 +11,18 @@ import { isValidAid } from '../../aun/aid/validation.js';
|
|
|
11
11
|
const SUPPORTED_AGENT_PATCH_FIELDS = new Set(['aid', 'name', 'avatar', 'active_baseagent', 'baseagents', 'projects', 'owners', 'chatmode', 'channels', 'channelOwners']);
|
|
12
12
|
const HIDDEN_VALUE = '[hidden]';
|
|
13
13
|
const SENSITIVE_CONFIG_KEYS = new Set([
|
|
14
|
-
'apiKey',
|
|
15
14
|
'apikey',
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'
|
|
19
|
-
'clientId',
|
|
20
|
-
'clientSecret',
|
|
21
|
-
'secret',
|
|
22
|
-
'token',
|
|
23
|
-
'accessToken',
|
|
15
|
+
'appid',
|
|
16
|
+
'appsecret',
|
|
17
|
+
'botid',
|
|
24
18
|
'authorization',
|
|
19
|
+
'clientid',
|
|
25
20
|
'credential',
|
|
26
21
|
'credentials',
|
|
27
22
|
'password',
|
|
28
|
-
'
|
|
29
|
-
'
|
|
23
|
+
'privatekey',
|
|
24
|
+
'encryptionseed',
|
|
25
|
+
'aeskey',
|
|
30
26
|
]);
|
|
31
27
|
const HIDDEN_SETTINGS_KEYS = new Set([
|
|
32
28
|
'pathToClaudeCodeExecutable',
|
|
@@ -225,7 +221,7 @@ function sanitizeConfigValue(value) {
|
|
|
225
221
|
if (out.effort === undefined)
|
|
226
222
|
out.effort = sanitizeConfigValue(raw);
|
|
227
223
|
}
|
|
228
|
-
else if (
|
|
224
|
+
else if (isSensitiveConfigKey(key)) {
|
|
229
225
|
out[key] = raw == null || raw === '' ? raw : HIDDEN_VALUE;
|
|
230
226
|
}
|
|
231
227
|
else if (key === 'projects') {
|
|
@@ -237,6 +233,18 @@ function sanitizeConfigValue(value) {
|
|
|
237
233
|
}
|
|
238
234
|
return out;
|
|
239
235
|
}
|
|
236
|
+
function isSensitiveConfigKey(key) {
|
|
237
|
+
const normalized = key.replace(/[-_\s]/g, '').toLowerCase();
|
|
238
|
+
return SENSITIVE_CONFIG_KEYS.has(normalized)
|
|
239
|
+
|| normalized.endsWith('token')
|
|
240
|
+
|| normalized.endsWith('secret')
|
|
241
|
+
|| normalized.endsWith('password')
|
|
242
|
+
|| normalized.endsWith('credential')
|
|
243
|
+
|| normalized.endsWith('credentials')
|
|
244
|
+
|| normalized.endsWith('privatekey')
|
|
245
|
+
|| normalized.endsWith('apikey')
|
|
246
|
+
|| normalized.endsWith('authorization');
|
|
247
|
+
}
|
|
240
248
|
function buildSafeAgentConfig(config) {
|
|
241
249
|
if (!config)
|
|
242
250
|
return null;
|
|
@@ -592,14 +600,15 @@ export async function execAgentQuery(args) {
|
|
|
592
600
|
}
|
|
593
601
|
const config = cfgRead(ConfigTarget.Agent, { self: aid });
|
|
594
602
|
const safeConfig = buildSafeAgentConfig(config);
|
|
603
|
+
const safeAgentConfig = safeConfig ?? {};
|
|
595
604
|
const data = {
|
|
596
605
|
...res,
|
|
597
606
|
config: {
|
|
598
607
|
...(res.config ?? {}),
|
|
599
|
-
active_baseagent:
|
|
600
|
-
projects:
|
|
601
|
-
baseagents:
|
|
602
|
-
owners:
|
|
608
|
+
active_baseagent: safeAgentConfig.active_baseagent ?? res.config?.baseagent ?? null,
|
|
609
|
+
projects: safeAgentConfig.projects ?? {},
|
|
610
|
+
baseagents: safeAgentConfig.baseagents ?? {},
|
|
611
|
+
owners: safeAgentConfig.owners ?? res.config?.owners ?? [],
|
|
603
612
|
},
|
|
604
613
|
safeConfig,
|
|
605
614
|
};
|
|
@@ -19,7 +19,7 @@ import { buildSessionTurnList } from '../session/session-turns.js';
|
|
|
19
19
|
import { resolveConfigCommand } from '../../config/resolved-config-op.js';
|
|
20
20
|
import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommand } from '../../config/config-operation-service.js';
|
|
21
21
|
import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
|
|
22
|
-
import { hashArgv } from '../auth/authorization-audit.js';
|
|
22
|
+
import { auditCommandAuthorization, hashArgv } from '../auth/authorization-audit.js';
|
|
23
23
|
import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
|
|
24
24
|
import { executeManagedAidOperation, ManagedAidOperationError, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
|
|
25
25
|
import { executeManagedGroupOperation, isManagedGroupRelationRead, ManagedGroupOperationError, resolveManagedGroupOperation, executeManagedMsgOperation, ManagedMsgOperationError, resolveManagedMsgOperation, executeManagedFsOperation, ManagedFsOperationError, resolveManagedFsOperation, } from '../../aun/msg/managed-operation.js';
|
|
@@ -27,7 +27,7 @@ import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
|
27
27
|
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../protected-paths.js';
|
|
28
28
|
import { validateModelSelectionForRole } from '../model/model-permission.js';
|
|
29
29
|
import { getModelInfo } from '../model/model-catalog.js';
|
|
30
|
-
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride } from '../role/runtime-policy.js';
|
|
30
|
+
import { constrainRuntimePermissionMode, resolveRuntimePermissionMode, validateRuntimeStringFieldOverride } from '../role/runtime-policy.js';
|
|
31
31
|
import { parsePeerKey } from '../relation/peer-identity.js';
|
|
32
32
|
import { isQuickCommand } from './slash-gate.js';
|
|
33
33
|
import { handleSlashCommand } from './slash-handler.js';
|
|
@@ -1002,7 +1002,13 @@ export class CommandHandler {
|
|
|
1002
1002
|
}
|
|
1003
1003
|
const payload = result;
|
|
1004
1004
|
if (payload.kind === 'command.error') {
|
|
1005
|
-
return {
|
|
1005
|
+
return {
|
|
1006
|
+
error: payload.text || '执行失败',
|
|
1007
|
+
code: payload.reason || 'EXEC_FAILED',
|
|
1008
|
+
...(payload.reasonCode ? {
|
|
1009
|
+
data: { reasonCode: payload.reasonCode, nextStep: payload.nextStep },
|
|
1010
|
+
} : {}),
|
|
1011
|
+
};
|
|
1006
1012
|
}
|
|
1007
1013
|
const data = payload.structured && typeof payload.structured === 'object'
|
|
1008
1014
|
? { ...payload.structured }
|
|
@@ -1771,6 +1777,7 @@ export class CommandHandler {
|
|
|
1771
1777
|
},
|
|
1772
1778
|
},
|
|
1773
1779
|
auditMetadata: {
|
|
1780
|
+
correlationId: grant.taskId,
|
|
1774
1781
|
argvHash: hashArgv(parsed.command.canonicalArgv),
|
|
1775
1782
|
taskId: grant.taskId,
|
|
1776
1783
|
messageId: grant.messageId,
|
|
@@ -1854,8 +1861,41 @@ export class CommandHandler {
|
|
|
1854
1861
|
if (!session)
|
|
1855
1862
|
return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
|
|
1856
1863
|
const parsed = resolveManagedMsgOperation(argv);
|
|
1857
|
-
if (!parsed.ok)
|
|
1864
|
+
if (!parsed.ok) {
|
|
1865
|
+
const grant = delegation.grant;
|
|
1866
|
+
auditCommandAuthorization({
|
|
1867
|
+
ts: Date.now(),
|
|
1868
|
+
source: 'agent-tool',
|
|
1869
|
+
operation: 'ec.msg.managed.parse',
|
|
1870
|
+
scope: 'relation',
|
|
1871
|
+
dangerous: false,
|
|
1872
|
+
decision: 'deny',
|
|
1873
|
+
executed: false,
|
|
1874
|
+
executionState: 'blocked',
|
|
1875
|
+
decisionSource: 'policy',
|
|
1876
|
+
policyCode: parsed.code,
|
|
1877
|
+
code: parsed.code,
|
|
1878
|
+
reason: parsed.reason,
|
|
1879
|
+
correlationId: grant.taskId,
|
|
1880
|
+
requestId: grant.taskId,
|
|
1881
|
+
sessionId,
|
|
1882
|
+
agentAid: grant.selfAid,
|
|
1883
|
+
permissionMode: resolveRuntimePermissionMode({ selfAid: grant.selfAid, role: grant.issuedRole }).effectiveValue,
|
|
1884
|
+
actorId: grant.actorId,
|
|
1885
|
+
selfAid: grant.selfAid,
|
|
1886
|
+
peerKey: grant.peerKey,
|
|
1887
|
+
channel: grant.channel,
|
|
1888
|
+
role: grant.issuedRole,
|
|
1889
|
+
taskId: grant.taskId,
|
|
1890
|
+
messageId: grant.messageId,
|
|
1891
|
+
argvHash: hashArgv(argv),
|
|
1892
|
+
argsSummary: {
|
|
1893
|
+
subcommand: String(argv[1] ?? argv[0] ?? '<missing>'),
|
|
1894
|
+
providedOptions: argv.filter(value => typeof value === 'string' && value.startsWith('--')).join(','),
|
|
1895
|
+
},
|
|
1896
|
+
});
|
|
1858
1897
|
return { ok: false, code: parsed.code, error: parsed.reason };
|
|
1898
|
+
}
|
|
1859
1899
|
const grant = delegation.grant;
|
|
1860
1900
|
const grantSelfAid = grant.selfAid.replace(/^@/, '');
|
|
1861
1901
|
const sessionSelfAid = session.selfAID?.replace(/^@/, '');
|
|
@@ -1897,9 +1937,15 @@ export class CommandHandler {
|
|
|
1897
1937
|
targetId: parsed.command.target,
|
|
1898
1938
|
peer: parsed.command.target,
|
|
1899
1939
|
peerKey: formatPeerKey('aun', parsed.command.target),
|
|
1940
|
+
...(parsed.command.session ? { session: parsed.command.session } : {}),
|
|
1941
|
+
...(parsed.command.limit !== undefined ? { limit: parsed.command.limit } : {}),
|
|
1942
|
+
...(parsed.command.before !== undefined ? { before: parsed.command.before } : {}),
|
|
1943
|
+
...(parsed.command.after !== undefined ? { after: parsed.command.after } : {}),
|
|
1944
|
+
...(parsed.command.direction ? { direction: parsed.command.direction } : {}),
|
|
1900
1945
|
},
|
|
1901
1946
|
},
|
|
1902
1947
|
auditMetadata: {
|
|
1948
|
+
correlationId: grant.taskId,
|
|
1903
1949
|
argvHash: hashArgv(parsed.command.canonicalArgv),
|
|
1904
1950
|
taskId: grant.taskId,
|
|
1905
1951
|
messageId: grant.messageId,
|
|
@@ -225,7 +225,7 @@ function updateSetting(key, target, value, args, context) {
|
|
|
225
225
|
if (!allowed.has(raw)) {
|
|
226
226
|
throw new GroupMenuError('INVALID_ARGUMENT', `Invalid ${key} value: ${raw || '<empty>'}`);
|
|
227
227
|
}
|
|
228
|
-
if (key === 'rulesPolicy' && context.role !== 'owner'
|
|
228
|
+
if (key === 'rulesPolicy' && context.role !== 'owner') {
|
|
229
229
|
throw new GroupMenuError('PERMISSION_DENIED', 'rulesPolicy can only be changed by an Agent owner');
|
|
230
230
|
}
|
|
231
231
|
const expectedRevision = stringArg(args?.expectedRevision);
|
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { groupInfo, groupMembers } from '../../aun/msg/group.js';
|
|
3
3
|
import { formatPeerKey } from '../relation/peer-identity.js';
|
|
4
4
|
import { authorizeAccess, authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
5
|
+
import { authorizationDenialData, formatAuthorizationDenial } from '../auth/authorization-denial.js';
|
|
5
6
|
import { ConfigTarget, read as readConfig } from '../../config/config-manager.js';
|
|
6
7
|
import { readRolesConfig } from '../../config/roles.js';
|
|
7
8
|
import { normalizeMenuError } from './menu-protocol.js';
|
|
@@ -106,8 +107,13 @@ export async function resolveMenuCatalogContext(input) {
|
|
|
106
107
|
throw { code: 'FORBIDDEN', message: 'control catalog context requires a control subject' };
|
|
107
108
|
}
|
|
108
109
|
if (requested === 'process') {
|
|
109
|
-
if (input.channelScope !== 'control' || !subject.fromControlChannel || !subject.isDaemonOwner)
|
|
110
|
-
throw {
|
|
110
|
+
if (input.channelScope !== 'control' || !subject.fromControlChannel || !subject.isDaemonOwner) {
|
|
111
|
+
throw {
|
|
112
|
+
code: 'NOT_ALLOWED',
|
|
113
|
+
message: formatAuthorizationDenial('Process context requires a DaemonOwner control subject.', 'DAEMON_OWNER_REQUIRED'),
|
|
114
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
111
117
|
return makeContext(input, requested, subject, input.channelId);
|
|
112
118
|
}
|
|
113
119
|
if (requested === 'agent') {
|
|
@@ -139,9 +145,18 @@ export async function resolveMenuCatalogContext(input) {
|
|
|
139
145
|
chatType: 'private',
|
|
140
146
|
conversationId: subject.actorId,
|
|
141
147
|
fromControlChannel: subject.fromControlChannel,
|
|
142
|
-
identity: subject.isDaemonOwner ? subject.identity : undefined,
|
|
143
148
|
});
|
|
144
|
-
const
|
|
149
|
+
const processRole = subject.processRole ?? (subject.isDaemonOwner ? 'daemon-owner' : 'none');
|
|
150
|
+
const bound = {
|
|
151
|
+
...rebuilt,
|
|
152
|
+
channel: subject.channel,
|
|
153
|
+
channelId: subject.actorId,
|
|
154
|
+
peerKey: formatPeerKey('aun', subject.actorId),
|
|
155
|
+
processRole,
|
|
156
|
+
isDaemonOwner: processRole === 'daemon-owner',
|
|
157
|
+
fromControlChannel: subject.fromControlChannel,
|
|
158
|
+
allowAccess: processRole !== 'none' || rebuilt.allowAccess,
|
|
159
|
+
};
|
|
145
160
|
const access = authorizeAccess(bound);
|
|
146
161
|
if (!access.allow)
|
|
147
162
|
throw { code: access.code, message: access.reason };
|
|
@@ -201,9 +216,19 @@ export async function resolveMenuCatalogContext(input) {
|
|
|
201
216
|
chatType: 'group',
|
|
202
217
|
conversationId: canonicalGroupId,
|
|
203
218
|
fromControlChannel: subject.fromControlChannel,
|
|
204
|
-
identity: subject.isDaemonOwner ? subject.identity : undefined,
|
|
205
219
|
});
|
|
206
|
-
const
|
|
220
|
+
const processRole = subject.processRole ?? (subject.isDaemonOwner ? 'daemon-owner' : 'none');
|
|
221
|
+
const bound = {
|
|
222
|
+
...rebuilt,
|
|
223
|
+
channel: subject.channel,
|
|
224
|
+
channelId: canonicalGroupId,
|
|
225
|
+
conversationId: canonicalGroupId,
|
|
226
|
+
peerKey,
|
|
227
|
+
processRole,
|
|
228
|
+
isDaemonOwner: processRole === 'daemon-owner',
|
|
229
|
+
fromControlChannel: subject.fromControlChannel,
|
|
230
|
+
allowAccess: processRole !== 'none' || rebuilt.allowAccess,
|
|
231
|
+
};
|
|
207
232
|
const access = authorizeAccess(bound);
|
|
208
233
|
if (!access.allow)
|
|
209
234
|
throw { code: access.code, message: access.reason };
|
|
@@ -278,7 +303,7 @@ async function operationView(binding, context) {
|
|
|
278
303
|
return undefined;
|
|
279
304
|
if (binding.supported === false)
|
|
280
305
|
return { supported: false, allowed: false, reason: { code: 'NOT_SUPPORTED', message: 'operation is not supported' } };
|
|
281
|
-
if (binding.operation === 'group.rulespolicy.update' && context.subject.role !== 'owner'
|
|
306
|
+
if (binding.operation === 'group.rulespolicy.update' && context.subject.role !== 'owner') {
|
|
282
307
|
return { supported: true, allowed: false, reason: { code: 'PERMISSION_DENIED', message: 'rulesPolicy can only be changed by an Agent owner' } };
|
|
283
308
|
}
|
|
284
309
|
const decision = authorizeOperation({ source: 'menu', intent: intentFor(binding, context), subject: context.subject, audit: false });
|
|
@@ -27,6 +27,7 @@ import { isCapabilityType, listCapabilityOptions, queryCapabilityTypes, resolveC
|
|
|
27
27
|
import { normalizeCliArgv, parseCliIntent, parseLegacyCliCommand, validateCliArgv, withDefaultRelationContext } from './cli-intent-parser.js';
|
|
28
28
|
import { auditCommandAuthorization, hashArgv } from '../auth/authorization-audit.js';
|
|
29
29
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
30
|
+
import { authorizationDenialData, formatAuthorizationDenial } from '../auth/authorization-denial.js';
|
|
30
31
|
import { evaluateRoleOperationCapability } from '../auth/operation-authorizer.js';
|
|
31
32
|
import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
|
|
32
33
|
import { chatmodeFieldForPeer, resolveChatModeForField } from '../message/peer-mode.js';
|
|
@@ -854,13 +855,21 @@ function gateControlScope(opts) {
|
|
|
854
855
|
if (fromControlChannel)
|
|
855
856
|
return null;
|
|
856
857
|
if (isProcessLevelAction(cmdBase, action)) {
|
|
857
|
-
return {
|
|
858
|
+
return {
|
|
859
|
+
error: formatAuthorizationDenial('This daemon-level operation is not available from an Agent channel.', 'DAEMON_OWNER_REQUIRED'),
|
|
860
|
+
code: 'NOT_ALLOWED',
|
|
861
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
862
|
+
};
|
|
858
863
|
}
|
|
859
864
|
const targetAid = args?.aid;
|
|
860
865
|
if (targetAid) {
|
|
861
866
|
const currentAgentAid = this.getOwningAgent?.(channel)?.aid;
|
|
862
867
|
if (targetAid !== currentAgentAid) {
|
|
863
|
-
return {
|
|
868
|
+
return {
|
|
869
|
+
error: formatAuthorizationDenial('An Agent channel may only target its current Agent.', 'TARGET_SELF_ONLY'),
|
|
870
|
+
code: 'NOT_ALLOWED',
|
|
871
|
+
data: authorizationDenialData('TARGET_SELF_ONLY'),
|
|
872
|
+
};
|
|
864
873
|
}
|
|
865
874
|
}
|
|
866
875
|
return null;
|
|
@@ -983,8 +992,12 @@ function buildMenuIntent(verb, cmdBase, args, action, value, fromControlChannel
|
|
|
983
992
|
return intent('agent.reload', fromControlChannel ? 'control' : 'agent', { action });
|
|
984
993
|
if (action === 'create')
|
|
985
994
|
return intent('agent.create', 'control', { action });
|
|
986
|
-
if (action === 'delete'
|
|
995
|
+
if (action === 'delete')
|
|
987
996
|
return intent('agent.delete', 'control', { action });
|
|
997
|
+
if (action === 'enable')
|
|
998
|
+
return intent('agent.enable', 'control', { action });
|
|
999
|
+
if (action === 'disable')
|
|
1000
|
+
return intent('agent.disable', 'control', { action });
|
|
988
1001
|
}
|
|
989
1002
|
if (cmdBase === '/trigger') {
|
|
990
1003
|
if (action === 'create' || action === 'set')
|
|
@@ -1088,7 +1101,12 @@ async function authorizeMenuIntent(params) {
|
|
|
1088
1101
|
},
|
|
1089
1102
|
};
|
|
1090
1103
|
}
|
|
1091
|
-
|
|
1104
|
+
const reasonCode = decision.command?.reasonCode;
|
|
1105
|
+
return {
|
|
1106
|
+
error: formatAuthorizationDenial(decision.reason, reasonCode),
|
|
1107
|
+
code: decision.code,
|
|
1108
|
+
...(reasonCode ? { data: authorizationDenialData(reasonCode) } : {}),
|
|
1109
|
+
};
|
|
1092
1110
|
}
|
|
1093
1111
|
return null;
|
|
1094
1112
|
}
|
|
@@ -1136,8 +1154,6 @@ function buildRoleMenuContext(owner, channel, subject) {
|
|
|
1136
1154
|
},
|
|
1137
1155
|
} : {}),
|
|
1138
1156
|
availableBaseagents: Array.from(new Set([
|
|
1139
|
-
...Object.keys(owningAgent?.config?.baseagents ?? {}),
|
|
1140
|
-
...(owningAgent?.baseagent ? [owningAgent.baseagent] : []),
|
|
1141
1157
|
...(owningAgent?.name ? (owner.getAvailableBaseagentsForOwner?.(owningAgent.name) ?? []) : []),
|
|
1142
1158
|
])),
|
|
1143
1159
|
listModels: async (baseagent) => {
|
|
@@ -1187,9 +1203,6 @@ async function executeGroupMenu(owner, params) {
|
|
|
1187
1203
|
return menuResultFailure(error);
|
|
1188
1204
|
}
|
|
1189
1205
|
const authorizationArgs = groupMenuAuthorizationArgs(params.args, params.subject.selfAid, menuIntent.scope === 'relation' ? params.subject.peerKey : undefined);
|
|
1190
|
-
const authorizationSubject = params.subject.isDaemonOwner && params.subject.role !== 'owner'
|
|
1191
|
-
? { ...params.subject, role: 'owner' }
|
|
1192
|
-
: params.subject;
|
|
1193
1206
|
const denied = await authorizeMenuIntent.call(owner, {
|
|
1194
1207
|
intent: {
|
|
1195
1208
|
operation: menuIntent.operation,
|
|
@@ -1198,7 +1211,7 @@ async function executeGroupMenu(owner, params) {
|
|
|
1198
1211
|
args: authorizationArgs,
|
|
1199
1212
|
},
|
|
1200
1213
|
identity: params.identity,
|
|
1201
|
-
subject:
|
|
1214
|
+
subject: params.subject,
|
|
1202
1215
|
session: params.session,
|
|
1203
1216
|
explicitChatType: params.explicitChatType,
|
|
1204
1217
|
channel: params.channel,
|
|
@@ -1541,16 +1554,15 @@ export async function getSubMenuItems(cmd, channel, channelId, userId, args, ove
|
|
|
1541
1554
|
throw { code: res.code, message: res.error };
|
|
1542
1555
|
return res.data.agents.map(ag => ({ value: ag.aid, label: ag.name || ag.aid, desc: ag.status }));
|
|
1543
1556
|
}
|
|
1544
|
-
// agent channel
|
|
1557
|
+
// agent channel:直接读取自身,避免先枚举全部 Agent 再过滤。
|
|
1545
1558
|
const selfAid = this.getOwningAgent?.(channel)?.aid;
|
|
1546
1559
|
if (!selfAid)
|
|
1547
1560
|
throw { code: 'FORBIDDEN', message: '当前 channel 无绑定 agent' };
|
|
1548
|
-
const res = await
|
|
1561
|
+
const res = await execAgentQuery({ aid: selfAid });
|
|
1549
1562
|
if ('error' in res)
|
|
1550
1563
|
throw { code: res.code, message: res.error };
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
.map(ag => ({ value: ag.aid, label: ag.name || ag.aid, desc: ag.status }));
|
|
1564
|
+
const ag = res.data;
|
|
1565
|
+
return [{ value: ag.aid, label: ag.identity?.name || ag.name || ag.aid, desc: ag.status }];
|
|
1554
1566
|
}
|
|
1555
1567
|
if (cmd === '/capability') {
|
|
1556
1568
|
const type = args?.type;
|
|
@@ -1890,7 +1902,7 @@ export async function execMenuQuery(cmd, channel, channelId, userId, args, expli
|
|
|
1890
1902
|
// 授权由 agent.list/agent.show operation 决定;channel 闸门只负责目标范围。
|
|
1891
1903
|
if (cmdBase === '/agent') {
|
|
1892
1904
|
if (fromControlChannel) {
|
|
1893
|
-
return await execAgentQuery(args);
|
|
1905
|
+
return args?.aid ? await execAgentQuery(args) : await execAgentOptions(args);
|
|
1894
1906
|
}
|
|
1895
1907
|
const selfAid = this.getOwningAgent?.(channel)?.aid;
|
|
1896
1908
|
if (!selfAid)
|
|
@@ -2727,7 +2739,11 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
2727
2739
|
subject: authSubject,
|
|
2728
2740
|
});
|
|
2729
2741
|
if (cmdBase === '/system' && fromControlChannel && !subject.isDaemonOwner) {
|
|
2730
|
-
return {
|
|
2742
|
+
return {
|
|
2743
|
+
error: formatAuthorizationDenial('This daemon-level operation requires DaemonOwner.', 'DAEMON_OWNER_REQUIRED'),
|
|
2744
|
+
code: 'NOT_ALLOWED',
|
|
2745
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
2746
|
+
};
|
|
2731
2747
|
}
|
|
2732
2748
|
if (cmdBase === '/role') {
|
|
2733
2749
|
const authorized = await authorizeRoleMenu(this, {
|
|
@@ -2808,7 +2824,11 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
2808
2824
|
if (cmdBase === '/agent') {
|
|
2809
2825
|
if (fromControlChannel) {
|
|
2810
2826
|
if (!subject.isDaemonOwner) {
|
|
2811
|
-
return {
|
|
2827
|
+
return {
|
|
2828
|
+
error: formatAuthorizationDenial('This Agent lifecycle operation requires DaemonOwner.', 'DAEMON_OWNER_REQUIRED'),
|
|
2829
|
+
code: 'NOT_ALLOWED',
|
|
2830
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
2831
|
+
};
|
|
2812
2832
|
}
|
|
2813
2833
|
}
|
|
2814
2834
|
else {
|
|
@@ -3323,7 +3343,11 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3323
3343
|
if (cmdBase === '/system') {
|
|
3324
3344
|
// D1 迁移:进程级鉴权统一查 daemon.json owners,替代各 action 内联的 identity.role 判断
|
|
3325
3345
|
if (!subject.isDaemonOwner) {
|
|
3326
|
-
return {
|
|
3346
|
+
return {
|
|
3347
|
+
error: formatAuthorizationDenial('This daemon-level operation requires DaemonOwner.', 'DAEMON_OWNER_REQUIRED'),
|
|
3348
|
+
code: 'NOT_ALLOWED',
|
|
3349
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
3350
|
+
};
|
|
3327
3351
|
}
|
|
3328
3352
|
if (action === 'restart') {
|
|
3329
3353
|
const suppressRealRestart = shouldSuppressRealRestart();
|
|
@@ -3475,6 +3499,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3475
3499
|
requestId, sessionId: subject.sessionId, agentAid: subject.selfAid,
|
|
3476
3500
|
permissionMode: subject.permissionMode,
|
|
3477
3501
|
channel, channelId, role: identity.role, decision: 'deny', code: 'NOT_ALLOWED',
|
|
3502
|
+
executed: false, executionState: 'blocked',
|
|
3478
3503
|
reason: 'Unrecognized CLI command', taskId: requestId, argvHash: hashArgv(intentArgv),
|
|
3479
3504
|
name: 'cli', action: 'exec', args: { argv: [...requestedArgv] },
|
|
3480
3505
|
});
|
|
@@ -3534,6 +3559,8 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3534
3559
|
permissionMode: subject.permissionMode,
|
|
3535
3560
|
channel, channelId, role: identity.role, isDaemonOwner,
|
|
3536
3561
|
fromControlChannel: fromControlChannel ?? false, decision: 'allow',
|
|
3562
|
+
executed: true,
|
|
3563
|
+
executionState: executionData?.exitCode === undefined || executionData.exitCode === 0 ? 'completed' : 'failed',
|
|
3537
3564
|
matchedRule: decision.command?.matchedRule, taskId: requestId, argvHash: hashArgv(argv),
|
|
3538
3565
|
name: 'cli', action: 'exec',
|
|
3539
3566
|
args: { argv: [...requestedArgv] },
|
|
@@ -3665,7 +3692,6 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3665
3692
|
channelId: context.actorAid || SYSTEM_CONTROL_CHANNEL,
|
|
3666
3693
|
chatType: 'private',
|
|
3667
3694
|
conversationId: context.actorAid || SYSTEM_CONTROL_CHANNEL,
|
|
3668
|
-
identity: context.localDirect ? { role: 'owner', mode: 'interactive' } : undefined,
|
|
3669
3695
|
processOwners: context.owners,
|
|
3670
3696
|
fromControlChannel: true,
|
|
3671
3697
|
});
|
|
@@ -3788,7 +3814,11 @@ export async function execMenuForEcweb(payload, trusted) {
|
|
|
3788
3814
|
const isProcessLevel = isProcessLevelMenu(name, payload?.cmd) || isProcessContextMenu(payload);
|
|
3789
3815
|
const owners = loadDaemonConfig().owners ?? [];
|
|
3790
3816
|
if (isProcessLevel && owners.length === 0) {
|
|
3791
|
-
return {
|
|
3817
|
+
return menuFailure({ id, ...(name ? { name } : {}) }, {
|
|
3818
|
+
code: 'NOT_ALLOWED',
|
|
3819
|
+
message: formatAuthorizationDenial('Configure daemon.json.owners before using process-level operations.', 'DAEMON_OWNER_REQUIRED'),
|
|
3820
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
3821
|
+
});
|
|
3792
3822
|
}
|
|
3793
3823
|
if (!trusted || (!trusted.localDirect && !trusted.actorAid)) {
|
|
3794
3824
|
return menuFailure({ id, ...(name ? { name } : {}) }, {
|
|
@@ -3797,7 +3827,9 @@ export async function execMenuForEcweb(payload, trusted) {
|
|
|
3797
3827
|
data: { $schema_version: 1, kind: 'role_permission_denied', self: payload?.args?.self ?? null },
|
|
3798
3828
|
});
|
|
3799
3829
|
}
|
|
3800
|
-
|
|
3830
|
+
// A verified local ECWeb connection acts through the configured human
|
|
3831
|
+
// DaemonOwner identity; it is not an anonymous daemon service principal.
|
|
3832
|
+
const userId = trusted.actorAid ?? (trusted.localDirect ? (owners[0] || 'local-direct') : undefined);
|
|
3801
3833
|
return execMenuForSystemControl.call(this, payload, {
|
|
3802
3834
|
source: 'ecweb',
|
|
3803
3835
|
actorAid: userId || 'local-direct',
|
|
@@ -3819,7 +3851,11 @@ export async function execMenuForControl(payload, peerId) {
|
|
|
3819
3851
|
const isRoleRequest = name === 'role' || compatCmd === '/role';
|
|
3820
3852
|
const isConnectRequest = name === 'connect' || compatCmd === '/connect';
|
|
3821
3853
|
if (!isRoleRequest && !isConnectRequest && !isProcessLevelOwner(peerId, owners)) {
|
|
3822
|
-
return menuFailure({ id, ...(name ? { name } : {}) }, {
|
|
3854
|
+
return menuFailure({ id, ...(name ? { name } : {}) }, {
|
|
3855
|
+
code: 'NOT_ALLOWED',
|
|
3856
|
+
message: formatAuthorizationDenial('Control-channel process operations require DaemonOwner.', 'DAEMON_OWNER_REQUIRED'),
|
|
3857
|
+
data: authorizationDenialData('DAEMON_OWNER_REQUIRED'),
|
|
3858
|
+
});
|
|
3823
3859
|
}
|
|
3824
3860
|
return execMenuForSystemControl.call(this, payload, {
|
|
3825
3861
|
source: 'control',
|