evolcore 0.0.2 → 0.0.4
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 +93 -778
- package/README.md +45 -10
- package/bin/ec-safe-output.js +89 -24
- package/dist/agents/baseagent.js +46 -0
- package/dist/agents/claude-runner.js +198 -48
- package/dist/agents/codex-app-server-client.js +68 -0
- package/dist/agents/codex-runner.js +203 -8
- package/dist/agents/runner-types.js +2 -2
- package/dist/aun/aid/agentmd.js +79 -0
- package/dist/aun/aid/encryption-seed-policy.js +29 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +2 -5
- package/dist/aun/outbox.js +8 -0
- package/dist/channels/aun.js +241 -116
- package/dist/channels/daemon.js +18 -4
- package/dist/channels/dingtalk.js +1005 -260
- package/dist/channels/feishu.js +184 -56
- package/dist/channels/qqbot.js +23 -1
- package/dist/channels/wechat.js +303 -155
- package/dist/channels/wecom.js +1132 -164
- package/dist/cli/agent-command.js +2 -1
- package/dist/cli/aun-commands.js +98 -43
- package/dist/cli/bench.js +2 -2
- package/dist/cli/contact.js +71 -0
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +77 -214
- package/dist/cli/handoff-command.js +4 -3
- package/dist/cli/help.js +9 -5
- package/dist/cli/index.js +132 -116
- package/dist/cli/init-channel.js +242 -129
- package/dist/cli/init.js +70 -31
- package/dist/cli/model.js +2 -1
- package/dist/cli/net-check.js +2 -2
- package/dist/cli/queue-command.js +30 -6
- package/dist/cli/raw-key-input.js +25 -0
- package/dist/cli/response.js +5 -6
- package/dist/cli/restart-monitor.js +25 -1
- package/dist/cli/stats.js +6 -4
- package/dist/cli/trigger-command.js +146 -25
- package/dist/cli/version.js +6 -1
- package/dist/config/access-policy-domain.js +38 -0
- package/dist/config/access-policy.js +134 -0
- package/dist/config/builtin-role-templates.js +42 -17
- package/dist/config/builtin-roles.js +31 -6
- package/dist/config/config-field-policy.js +17 -5
- package/dist/config/config-manager.js +409 -29
- package/dist/config/config-operation-service.js +35 -38
- package/dist/config/contact-alias.js +68 -0
- package/dist/config/contact-bind-code.js +274 -0
- package/dist/config/contact-book-store.js +622 -0
- package/dist/config/contact-book-v2-startup.js +35 -0
- package/dist/config/contact-book.js +185 -300
- package/dist/config/contact-operation-service.js +116 -0
- package/dist/config/contact-request-service.js +331 -0
- package/dist/config/peer-role-resolver.js +136 -55
- package/dist/config/role-ranks.js +18 -0
- package/dist/config/role-service.js +16 -19
- package/dist/config/role-store.js +16 -5
- package/dist/config/roles.js +10 -1
- package/dist/config/schema-registry.js +49 -24
- package/dist/config-store.js +14 -4
- package/dist/core/auth/agent-delegation.js +105 -11
- package/dist/core/auth/authorization-audit.js +16 -1
- package/dist/core/auth/operation-authorizer.js +10 -0
- package/dist/core/auth/operation-catalog.js +105 -1
- package/dist/core/auth/trigger-authorization.js +15 -0
- package/dist/core/bootstrap-service.js +2 -9
- package/dist/core/channel-loader.js +6 -2
- package/dist/core/command/command-handler.js +113 -21
- package/dist/core/command/connect-menu.js +599 -0
- package/dist/core/command/evol-menu-version-gate.js +38 -0
- package/dist/core/command/group-menu.js +421 -0
- package/dist/core/command/menu-handler.js +382 -100
- package/dist/core/command/menu-protocol.js +8 -2
- package/dist/core/command/menu-token-store.js +102 -0
- package/dist/core/command/role-menu.js +128 -29
- package/dist/core/command/slash-gate.js +1 -1
- package/dist/core/command/slash-handler.js +186 -49
- package/dist/core/daemon-file-cache.js +40 -6
- package/dist/core/event-catalog.js +99 -0
- package/dist/core/evolagent-registry.js +4 -40
- package/dist/core/evolagent.js +20 -9
- package/dist/core/handoff/runtime.js +162 -37
- package/dist/core/handoff/store.js +46 -2
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +16 -74
- package/dist/core/message/file-markers.js +7 -0
- package/dist/core/message/im-renderer.js +20 -14
- package/dist/core/message/inbound-admission.js +124 -0
- package/dist/core/message/message-bridge.js +670 -57
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +195 -10
- package/dist/core/message/message-utils.js +8 -2
- package/dist/core/message/peer-mode.js +7 -8
- package/dist/core/message/response-engine.js +642 -96
- package/dist/core/message/send-receipt.js +24 -0
- package/dist/core/message/stream-debouncer.js +11 -2
- package/dist/core/permission/approval-gateway.js +17 -10
- package/dist/core/permission/ec-command-parser.js +101 -46
- package/dist/core/permission/tool-policy.js +115 -38
- package/dist/core/protected-paths.js +38 -11
- package/dist/core/session/session-fs-store.js +44 -3
- package/dist/core/session/session-manager.js +86 -8
- package/dist/core/session/session-mapper.js +2 -0
- package/dist/core/session/session-renew.js +125 -69
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/eck/kit-renderer.js +12 -1
- package/dist/eck/message-renderer.js +79 -1
- package/dist/index.js +285 -94
- package/dist/ipc.js +114 -8
- package/dist/paths.js +3 -0
- package/dist/response-system/config-resolver.js +29 -0
- package/dist/response-system/coordinator.js +8 -32
- package/dist/response-system/engines/v1/proactive-flow.js +1 -1
- package/dist/response-system/index.js +1 -0
- package/dist/response-system/modes/single-session/index.js +7 -4
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/manager.js +3 -0
- package/dist/trigger/parser.js +126 -11
- package/dist/trigger/patch.js +12 -2
- package/dist/trigger/scheduler.js +444 -40
- package/dist/trigger/validation.js +13 -0
- package/dist/utils/aid-bind.js +43 -29
- package/dist/utils/error-dict.json +7 -0
- package/dist/utils/evolcore-version.js +21 -0
- package/dist/utils/instance-registry.js +14 -7
- package/dist/utils/log-writer.js +46 -0
- package/dist/utils/logger.js +2 -2
- package/dist/utils/media-cache.js +4 -1
- package/dist/utils/model-prices.jsonl +6 -3
- package/dist/utils/restart-safety.js +31 -0
- package/dist/utils/stable-semver.js +21 -0
- package/dist/utils/stats.js +33 -9
- package/dist/utils/system-memory.js +62 -0
- package/kits/docs/INDEX.md +2 -1
- package/kits/docs/channels/aun.md +4 -13
- package/kits/docs/evolcore/INDEX.md +5 -3
- package/kits/docs/evolcore/agent.md +9 -1
- package/kits/docs/evolcore/aid.md +5 -2
- package/kits/docs/evolcore/config.md +47 -2
- package/kits/docs/evolcore/contact.md +61 -0
- package/kits/docs/evolcore/fs.md +9 -0
- package/kits/docs/evolcore/group-rules.md +46 -4
- package/kits/docs/evolcore/group.md +15 -6
- package/kits/docs/evolcore/model.md +4 -1
- package/kits/docs/evolcore/msg.md +12 -6
- package/kits/docs/evolcore/response.md +16 -21
- package/kits/docs/evolcore/rpc.md +2 -0
- package/kits/docs/evolcore/stats.md +15 -2
- package/kits/docs/evolcore/storage.md +1 -0
- package/kits/docs/evolcore/trigger.md +42 -10
- package/kits/docs/path-registry.md +36 -17
- package/kits/eck_manifest.json +12 -0
- package/kits/eck_message_manifest.json +12 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +754 -0
- package/kits/rules/01-overview.md +17 -7
- package/kits/rules/02-navigation.md +38 -18
- package/kits/rules/03-identity.md +28 -24
- package/kits/rules/04-relation.md +44 -28
- package/kits/rules/05-venue.md +31 -15
- package/kits/rules/06-channel.md +11 -7
- package/kits/schemas/_meta.json +21 -7
- package/kits/schemas/agent-config.schema.6.json +322 -0
- package/kits/schemas/agent-config.schema.7.json +303 -0
- package/kits/schemas/agent-config.schema.8.json +304 -0
- package/kits/schemas/agent-config.schema.9.json +374 -0
- package/kits/schemas/contact-book.schema.2.json +43 -0
- package/kits/schemas/contact-book.schema.3.json +67 -0
- package/kits/schemas/daemon.schema.1.json +3 -2
- package/kits/schemas/daemon.schema.2.json +101 -0
- package/kits/schemas/daemon.schema.3.json +123 -0
- package/kits/schemas/defaults.schema.2.json +85 -0
- package/kits/schemas/defaults.schema.3.json +73 -0
- package/kits/schemas/relation-config.schema.5.json +47 -0
- package/kits/schemas/relation-config.schema.6.json +46 -0
- package/kits/schemas/relation-config.schema.7.json +59 -0
- package/kits/schemas/role-config.schema.1.json +1 -0
- package/kits/schemas/role-registry.schema.1.json +2 -2
- package/kits/schemas/single-session.schema.2.json +57 -0
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
- package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
- package/kits/templates/roles/admin.json +47 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/owner.json +1 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/channel.md +12 -2
- package/kits/templates/system-fragments/commands.md +3 -1
- package/kits/templates/system-fragments/identity.md +3 -1
- package/kits/templates/system-fragments/relation.md +1 -1
- package/kits/templates/system-fragments/session.md +6 -2
- package/package.json +7 -5
- package/MIGRATION-0.5.0.md +0 -378
- package/ROLE_ACCESS_CONTROL.md +0 -174
- package/assets/brand/evolcore/README.md +0 -19
- package/assets/brand/evolcore/evolcore-app-icon.png +0 -0
- package/assets/brand/evolcore/evolcore-app-icon.svg +0 -13
- package/assets/brand/evolcore/evolcore-brand-board.png +0 -0
- package/assets/brand/evolcore/evolcore-brand-board.svg +0 -126
- package/assets/brand/evolcore/evolcore-logo-kit.zip +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.png +0 -0
- package/assets/brand/evolcore/evolcore-logo-reverse.svg +0 -14
- package/assets/brand/evolcore/evolcore-logo.png +0 -0
- package/assets/brand/evolcore/evolcore-logo.svg +0 -14
- package/assets/brand/evolcore/evolcore-mark.png +0 -0
- package/assets/brand/evolcore/evolcore-mark.svg +0 -10
|
@@ -2,9 +2,11 @@ import crypto from 'crypto';
|
|
|
2
2
|
import { authorizeOperation, buildAuthSubject } from './auth-gateway.js';
|
|
3
3
|
import { formatPeerKey, parsePeerKey } from '../relation/peer-identity.js';
|
|
4
4
|
export const AGENT_DELEGATION_TOKEN_ENV = 'EVOLCORE_DELEGATION_TOKEN';
|
|
5
|
+
export const AGENT_DELEGATION_COMMAND_TTL_MS = 60_000;
|
|
5
6
|
export class AgentDelegationRegistry {
|
|
6
7
|
grantsByHash = new Map();
|
|
7
8
|
activeHashBySession = new Map();
|
|
9
|
+
approvedCommands = new Map();
|
|
8
10
|
issue(input) {
|
|
9
11
|
this.revokeSession(input.sessionId);
|
|
10
12
|
const token = crypto.randomBytes(32).toString('base64url');
|
|
@@ -13,7 +15,7 @@ export class AgentDelegationRegistry {
|
|
|
13
15
|
this.activeHashBySession.set(input.sessionId, tokenHash);
|
|
14
16
|
return token;
|
|
15
17
|
}
|
|
16
|
-
validate(token, sessionId) {
|
|
18
|
+
validate(token, sessionId, commandHash) {
|
|
17
19
|
if (!token) {
|
|
18
20
|
return {
|
|
19
21
|
ok: false,
|
|
@@ -23,16 +25,49 @@ export class AgentDelegationRegistry {
|
|
|
23
25
|
}
|
|
24
26
|
const tokenHash = hashDelegationToken(token);
|
|
25
27
|
const grant = this.grantsByHash.get(tokenHash);
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
ok: false,
|
|
31
|
-
code: 'INVALID_DELEGATION',
|
|
32
|
-
reason: 'The task delegation token is invalid, revoked, or belongs to another session',
|
|
33
|
-
};
|
|
28
|
+
if (grant
|
|
29
|
+
&& grant.sessionId === sessionId
|
|
30
|
+
&& this.activeHashBySession.get(sessionId) === tokenHash) {
|
|
31
|
+
return { ok: true, grant };
|
|
34
32
|
}
|
|
35
|
-
|
|
33
|
+
const approved = commandHash
|
|
34
|
+
? this.consumeApprovedCommand(tokenHash, sessionId, commandHash)
|
|
35
|
+
: undefined;
|
|
36
|
+
if (approved)
|
|
37
|
+
return { ok: true, grant: approved };
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
code: 'INVALID_DELEGATION',
|
|
41
|
+
reason: 'The task delegation token is invalid, revoked, or belongs to another session',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
armApprovedCommand(input) {
|
|
45
|
+
const activeTokenHash = this.activeHashBySession.get(input.sessionId);
|
|
46
|
+
if (!activeTokenHash)
|
|
47
|
+
return false;
|
|
48
|
+
const grant = activeTokenHash ? this.grantsByHash.get(activeTokenHash) : undefined;
|
|
49
|
+
if (!grant || grant.taskId !== input.taskId)
|
|
50
|
+
return false;
|
|
51
|
+
if (!input.carrierToken || !isDelegationCommandHash(input.commandHash))
|
|
52
|
+
return false;
|
|
53
|
+
this.deleteExpiredApprovedCommands();
|
|
54
|
+
const carrierTokenHash = hashDelegationToken(input.carrierToken);
|
|
55
|
+
const key = approvedCommandKey(carrierTokenHash, input.sessionId, input.commandHash);
|
|
56
|
+
const existing = this.approvedCommands.get(key);
|
|
57
|
+
const ttlMs = Math.max(1, Math.min(input.ttlMs ?? AGENT_DELEGATION_COMMAND_TTL_MS, AGENT_DELEGATION_COMMAND_TTL_MS));
|
|
58
|
+
this.approvedCommands.set(key, {
|
|
59
|
+
sessionId: input.sessionId,
|
|
60
|
+
taskId: input.taskId,
|
|
61
|
+
activeTokenHash,
|
|
62
|
+
commandHash: input.commandHash,
|
|
63
|
+
expiresAt: Date.now() + ttlMs,
|
|
64
|
+
remainingUses: existing
|
|
65
|
+
&& existing.activeTokenHash === activeTokenHash
|
|
66
|
+
&& existing.expiresAt > Date.now()
|
|
67
|
+
? existing.remainingUses + 1
|
|
68
|
+
: 1,
|
|
69
|
+
});
|
|
70
|
+
return true;
|
|
36
71
|
}
|
|
37
72
|
revokeTask(sessionId, taskId) {
|
|
38
73
|
const tokenHash = this.activeHashBySession.get(sessionId);
|
|
@@ -43,19 +78,59 @@ export class AgentDelegationRegistry {
|
|
|
43
78
|
return;
|
|
44
79
|
this.grantsByHash.delete(tokenHash);
|
|
45
80
|
this.activeHashBySession.delete(sessionId);
|
|
81
|
+
this.deleteApprovedCommandsForSession(sessionId);
|
|
46
82
|
}
|
|
47
83
|
revokeSession(sessionId) {
|
|
48
84
|
const tokenHash = this.activeHashBySession.get(sessionId);
|
|
49
85
|
if (tokenHash)
|
|
50
86
|
this.grantsByHash.delete(tokenHash);
|
|
51
87
|
this.activeHashBySession.delete(sessionId);
|
|
88
|
+
this.deleteApprovedCommandsForSession(sessionId);
|
|
89
|
+
}
|
|
90
|
+
consumeApprovedCommand(carrierTokenHash, sessionId, commandHash) {
|
|
91
|
+
if (!isDelegationCommandHash(commandHash))
|
|
92
|
+
return undefined;
|
|
93
|
+
const key = approvedCommandKey(carrierTokenHash, sessionId, commandHash);
|
|
94
|
+
const approved = this.approvedCommands.get(key);
|
|
95
|
+
if (!approved)
|
|
96
|
+
return undefined;
|
|
97
|
+
if (approved.expiresAt <= Date.now()) {
|
|
98
|
+
this.approvedCommands.delete(key);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
const activeTokenHash = this.activeHashBySession.get(sessionId);
|
|
102
|
+
const grant = activeTokenHash ? this.grantsByHash.get(activeTokenHash) : undefined;
|
|
103
|
+
if (!grant
|
|
104
|
+
|| activeTokenHash !== approved.activeTokenHash
|
|
105
|
+
|| grant.taskId !== approved.taskId) {
|
|
106
|
+
this.approvedCommands.delete(key);
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
if (approved.remainingUses <= 1)
|
|
110
|
+
this.approvedCommands.delete(key);
|
|
111
|
+
else
|
|
112
|
+
approved.remainingUses--;
|
|
113
|
+
return grant;
|
|
114
|
+
}
|
|
115
|
+
deleteExpiredApprovedCommands() {
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
for (const [key, approved] of this.approvedCommands) {
|
|
118
|
+
if (approved.expiresAt <= now)
|
|
119
|
+
this.approvedCommands.delete(key);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
deleteApprovedCommandsForSession(sessionId) {
|
|
123
|
+
for (const [key, approved] of this.approvedCommands) {
|
|
124
|
+
if (approved.sessionId === sessionId)
|
|
125
|
+
this.approvedCommands.delete(key);
|
|
126
|
+
}
|
|
52
127
|
}
|
|
53
128
|
}
|
|
54
129
|
export function authorizeDelegatedAunMsgSend(registry, input) {
|
|
55
130
|
if (!input.sessionId) {
|
|
56
131
|
return { ok: false, code: 'DELEGATION_REQUIRED', reason: 'Origin session is required' };
|
|
57
132
|
}
|
|
58
|
-
const validation = registry.validate(input.delegationToken, input.sessionId);
|
|
133
|
+
const validation = registry.validate(input.delegationToken, input.sessionId, input.delegationCommandHash);
|
|
59
134
|
if (!validation.ok)
|
|
60
135
|
return validation;
|
|
61
136
|
const grant = validation.grant;
|
|
@@ -109,3 +184,22 @@ export function authorizeDelegatedAunMsgSend(registry, input) {
|
|
|
109
184
|
function hashDelegationToken(token) {
|
|
110
185
|
return crypto.createHash('sha256').update(token).digest('hex');
|
|
111
186
|
}
|
|
187
|
+
function approvedCommandKey(carrierTokenHash, sessionId, commandHash) {
|
|
188
|
+
return JSON.stringify([carrierTokenHash, sessionId, commandHash]);
|
|
189
|
+
}
|
|
190
|
+
function isDelegationCommandHash(value) {
|
|
191
|
+
return /^[a-f0-9]{64}$/.test(value);
|
|
192
|
+
}
|
|
193
|
+
export function hashDelegatedCommandArgv(argv) {
|
|
194
|
+
if (argv.length === 0 || argv.some(value => typeof value !== 'string' || value.includes('\0')))
|
|
195
|
+
return undefined;
|
|
196
|
+
return crypto.createHash('sha256')
|
|
197
|
+
.update('evolcore-agent-delegation-command-v1\0')
|
|
198
|
+
.update(JSON.stringify(argv))
|
|
199
|
+
.digest('hex');
|
|
200
|
+
}
|
|
201
|
+
export function hashCurrentDelegatedCommand(processArgv = process.argv) {
|
|
202
|
+
if (processArgv.length < 3)
|
|
203
|
+
return undefined;
|
|
204
|
+
return hashDelegatedCommandArgv(['ec', ...processArgv.slice(2)]);
|
|
205
|
+
}
|
|
@@ -21,6 +21,9 @@ function buildAuditRecord(event) {
|
|
|
21
21
|
operation: event.operation,
|
|
22
22
|
scope: event.scope,
|
|
23
23
|
dangerous: event.dangerous,
|
|
24
|
+
name: event.name,
|
|
25
|
+
action: event.action,
|
|
26
|
+
args: event.args?.argv ? { argv: [...event.args.argv] } : undefined,
|
|
24
27
|
actorId: redactIdentifier(event.actorId),
|
|
25
28
|
selfAid: redactIdentifier(event.selfAid),
|
|
26
29
|
peerKey: redactIdentifier(event.peerKey),
|
|
@@ -47,11 +50,23 @@ function redactIdentifier(value) {
|
|
|
47
50
|
return crypto.createHash('sha256').update(value).digest('hex').slice(0, 12);
|
|
48
51
|
}
|
|
49
52
|
function logAuditEvent(record) {
|
|
50
|
-
|
|
53
|
+
// A role-management snapshot fans out into several successful read checks.
|
|
54
|
+
// Keep those available for verbose ECWeb diagnostics without flooding INFO.
|
|
55
|
+
const routineEcwebRoleRead = record.source === 'ecweb'
|
|
56
|
+
&& !record.dangerous
|
|
57
|
+
&& (record.operation === 'role.policy.read' || record.operation === 'role.relation.read');
|
|
58
|
+
const level = record.decision === 'deny'
|
|
59
|
+
? 'warn'
|
|
60
|
+
: routineEcwebRoleRead
|
|
61
|
+
? 'debug'
|
|
62
|
+
: 'info';
|
|
51
63
|
const marker = record.decision === 'deny' ? 'DENY' : record.dangerous ? 'DANGEROUS_ALLOW' : 'ALLOW';
|
|
52
64
|
const message = [
|
|
53
65
|
`[CommandAudit:${marker}]`,
|
|
54
66
|
`operation=${record.operation}`,
|
|
67
|
+
record.name ? `name=${record.name}` : null,
|
|
68
|
+
record.action ? `action=${record.action}` : null,
|
|
69
|
+
record.args?.argv ? `args.argv=${JSON.stringify(record.args.argv)}` : null,
|
|
55
70
|
`role=${record.role}`,
|
|
56
71
|
`actor=${record.actorId || 'unknown'}`,
|
|
57
72
|
record.taskId ? `task=${record.taskId}` : null,
|
|
@@ -18,6 +18,8 @@ export const USER_PLANE_CAPABILITY_CEILING = {
|
|
|
18
18
|
'chatmode.update',
|
|
19
19
|
'mentionmode.current',
|
|
20
20
|
'mentionmode.update',
|
|
21
|
+
'group.mentionmode.current',
|
|
22
|
+
'group.rulespolicy.current',
|
|
21
23
|
'session.list',
|
|
22
24
|
'session.create',
|
|
23
25
|
'session.rename',
|
|
@@ -55,6 +57,8 @@ export const USER_PLANE_CAPABILITY_CEILING = {
|
|
|
55
57
|
]),
|
|
56
58
|
denyNamespaces: new Set([
|
|
57
59
|
'role',
|
|
60
|
+
'connect',
|
|
61
|
+
'contact',
|
|
58
62
|
'agent',
|
|
59
63
|
'system',
|
|
60
64
|
'storage',
|
|
@@ -413,6 +417,12 @@ function checkConfigFieldPolicy(ctx, policy, resolvedConfigOp) {
|
|
|
413
417
|
if (!resolvedConfigOp.route || (resolvedConfigOp.fieldRule.class !== 'safe-scalar' && resolvedConfigOp.fieldRule.class !== 'safe-readonly-object')) {
|
|
414
418
|
return { ok: false, reason: `Config field ${field} is not available to user roles` };
|
|
415
419
|
}
|
|
420
|
+
if (policy === 'role-overridable-write'
|
|
421
|
+
&& resolvedConfigOp.fieldRule.access === 'owner-only'
|
|
422
|
+
&& ctx.role !== 'owner'
|
|
423
|
+
&& !ctx.isDaemonOwner) {
|
|
424
|
+
return { ok: false, reason: `Config field ${field} can only be changed by an owner` };
|
|
425
|
+
}
|
|
416
426
|
const roleDef = getRoleDefinition(ctx.role, ctx.selfAid);
|
|
417
427
|
const fieldPermission = roleDef
|
|
418
428
|
? resolveRoleFieldPermission(roleDef.permissions || {}, field)
|
|
@@ -133,6 +133,46 @@ const OPERATIONS = [
|
|
|
133
133
|
description: 'Update group mention mode',
|
|
134
134
|
sources: ['slash', 'menu'],
|
|
135
135
|
},
|
|
136
|
+
{
|
|
137
|
+
id: 'group.list',
|
|
138
|
+
category: 'read',
|
|
139
|
+
dangerous: false,
|
|
140
|
+
defaultScopes: ['agent'],
|
|
141
|
+
description: 'List joined AUN groups',
|
|
142
|
+
sources: ['menu'],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'group.mentionmode.current',
|
|
146
|
+
category: 'read',
|
|
147
|
+
dangerous: false,
|
|
148
|
+
defaultScopes: ['relation', 'agent'],
|
|
149
|
+
description: 'Read a group mention mode',
|
|
150
|
+
sources: ['menu'],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: 'group.mentionmode.update',
|
|
154
|
+
category: 'write-own',
|
|
155
|
+
dangerous: false,
|
|
156
|
+
defaultScopes: ['relation', 'agent'],
|
|
157
|
+
description: 'Update a group mention mode',
|
|
158
|
+
sources: ['menu'],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: 'group.rulespolicy.current',
|
|
162
|
+
category: 'read',
|
|
163
|
+
dangerous: false,
|
|
164
|
+
defaultScopes: ['relation', 'agent'],
|
|
165
|
+
description: 'Read a group rules loading policy',
|
|
166
|
+
sources: ['menu'],
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'group.rulespolicy.update',
|
|
170
|
+
category: 'write-own',
|
|
171
|
+
dangerous: false,
|
|
172
|
+
defaultScopes: ['relation', 'agent'],
|
|
173
|
+
description: 'Update a group rules loading policy',
|
|
174
|
+
sources: ['menu'],
|
|
175
|
+
},
|
|
136
176
|
{
|
|
137
177
|
id: 'session.list',
|
|
138
178
|
category: 'read',
|
|
@@ -180,7 +220,7 @@ const OPERATIONS = [
|
|
|
180
220
|
dangerous: false,
|
|
181
221
|
defaultScopes: ['filesystem'],
|
|
182
222
|
description: '获取文件内容(需路径沙箱检查)',
|
|
183
|
-
sources: ['menu'],
|
|
223
|
+
sources: ['slash', 'menu'],
|
|
184
224
|
},
|
|
185
225
|
// ── Trigger Operations ──
|
|
186
226
|
{
|
|
@@ -304,6 +344,70 @@ const OPERATIONS = [
|
|
|
304
344
|
description: 'Modify role definitions, command permissions, default roles, or usage limits',
|
|
305
345
|
sources: ['menu', 'ecweb', 'control'],
|
|
306
346
|
},
|
|
347
|
+
{
|
|
348
|
+
id: 'contact.read',
|
|
349
|
+
category: 'read',
|
|
350
|
+
dangerous: false,
|
|
351
|
+
defaultScopes: ['agent'],
|
|
352
|
+
description: 'Read the Agent contact block list',
|
|
353
|
+
sources: ['agent-tool'],
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
id: 'contact.block',
|
|
357
|
+
category: 'write-agent',
|
|
358
|
+
dangerous: false,
|
|
359
|
+
defaultScopes: ['agent'],
|
|
360
|
+
description: 'Block or unblock a canonical contact across bound private channels',
|
|
361
|
+
sources: ['agent-tool'],
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: 'connect.read',
|
|
365
|
+
category: 'read',
|
|
366
|
+
dangerous: false,
|
|
367
|
+
defaultScopes: ['agent'],
|
|
368
|
+
description: 'Read contacts, groups, assignments, or contact details',
|
|
369
|
+
sources: ['menu', 'ecweb', 'control'],
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: 'connect.assign',
|
|
373
|
+
category: 'write-agent',
|
|
374
|
+
dangerous: false,
|
|
375
|
+
defaultScopes: ['agent'],
|
|
376
|
+
description: 'Assign a role to a relation',
|
|
377
|
+
sources: ['menu', 'ecweb', 'control'],
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: 'connect.revoke',
|
|
381
|
+
category: 'write-agent',
|
|
382
|
+
dangerous: false,
|
|
383
|
+
defaultScopes: ['agent'],
|
|
384
|
+
description: 'Revoke a role assignment from a relation',
|
|
385
|
+
sources: ['menu', 'ecweb', 'control'],
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: 'connect.admin',
|
|
389
|
+
category: 'write-agent',
|
|
390
|
+
dangerous: false,
|
|
391
|
+
defaultScopes: ['agent'],
|
|
392
|
+
description: 'Grant or revoke admin status',
|
|
393
|
+
sources: ['menu', 'ecweb', 'control'],
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
id: 'connect.write',
|
|
397
|
+
category: 'write-agent',
|
|
398
|
+
dangerous: false,
|
|
399
|
+
defaultScopes: ['agent'],
|
|
400
|
+
description: 'Manage contacts, contact requests, and allowed group channels',
|
|
401
|
+
sources: ['menu', 'ecweb', 'control'],
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
id: 'connect.access.write',
|
|
405
|
+
category: 'write-agent',
|
|
406
|
+
dangerous: false,
|
|
407
|
+
defaultScopes: ['agent'],
|
|
408
|
+
description: 'Manage owner-only Agent access policy and request limit settings',
|
|
409
|
+
sources: ['menu', 'ecweb', 'control'],
|
|
410
|
+
},
|
|
307
411
|
// Agent Operations
|
|
308
412
|
{
|
|
309
413
|
id: 'agent.list',
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const CROSS_AGENT_READ_OPERATIONS = new Set([
|
|
2
|
+
'trigger.list',
|
|
3
|
+
'trigger.show',
|
|
4
|
+
'trigger.history',
|
|
5
|
+
]);
|
|
6
|
+
export function isCrossAgentTriggerOperationAllowed(input) {
|
|
7
|
+
if (input.control || input.taskAgentAid === input.targetAgentAid)
|
|
8
|
+
return true;
|
|
9
|
+
if (!input.taskAgentAid || !input.targetManagement)
|
|
10
|
+
return false;
|
|
11
|
+
return CROSS_AGENT_READ_OPERATIONS.has(input.operation);
|
|
12
|
+
}
|
|
13
|
+
export function isCrossAgentTriggerReadOperation(operation) {
|
|
14
|
+
return CROSS_AGENT_READ_OPERATIONS.has(operation);
|
|
15
|
+
}
|
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { randomBytes } from 'crypto';
|
|
4
4
|
import { kitsTemplatesDir, agentMdPath } from '../paths.js';
|
|
5
|
+
import { resolveAgentDisplayName } from '../aun/aid/agentmd.js';
|
|
5
6
|
import { logger } from '../utils/logger.js';
|
|
6
7
|
import { loadAgent, saveAgent } from '../config-store.js';
|
|
7
8
|
import { normalizeAgentLifecycle, withLifecycleForWrite } from '../config/lifecycle.js';
|
|
@@ -130,15 +131,7 @@ export class BootstrapService {
|
|
|
130
131
|
return renderTemplate(template, vars).trim();
|
|
131
132
|
}
|
|
132
133
|
resolveAgentDisplayName(aid) {
|
|
133
|
-
|
|
134
|
-
const content = fs.readFileSync(agentMdPath(aid), 'utf-8');
|
|
135
|
-
const fm = content.match(/^---\n([\s\S]*?)\n---/)?.[1] || '';
|
|
136
|
-
const name = fm.match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1]?.trim();
|
|
137
|
-
if (name)
|
|
138
|
-
return name;
|
|
139
|
-
}
|
|
140
|
-
catch { }
|
|
141
|
-
return aid.split('.')[0];
|
|
134
|
+
return resolveAgentDisplayName(aid) || aid.split('.')[0];
|
|
142
135
|
}
|
|
143
136
|
resolveBaseagent(agent, aid) {
|
|
144
137
|
try {
|
|
@@ -26,7 +26,11 @@ export function showActivitiesPolicy(mode, chatType, _identity) {
|
|
|
26
26
|
}
|
|
27
27
|
// ── ChannelLoader ──────────────────────────────────────────────────────────
|
|
28
28
|
export class ChannelLoader {
|
|
29
|
+
processDebug;
|
|
29
30
|
plugins = new Map();
|
|
31
|
+
constructor(processDebug) {
|
|
32
|
+
this.processDebug = processDebug;
|
|
33
|
+
}
|
|
30
34
|
register(plugin) {
|
|
31
35
|
if (this.plugins.has(plugin.name)) {
|
|
32
36
|
throw new Error(`Channel plugin '${plugin.name}' already registered`);
|
|
@@ -46,7 +50,7 @@ export class ChannelLoader {
|
|
|
46
50
|
const ctx = {
|
|
47
51
|
agentName: agent.aid,
|
|
48
52
|
defaultProjectPath: agent.config.projects?.defaultPath ?? process.cwd(),
|
|
49
|
-
debug:
|
|
53
|
+
debug: this.processDebug,
|
|
50
54
|
};
|
|
51
55
|
// Build the full list of config instances to create.
|
|
52
56
|
// AUN is synthesised from agent.aid; any explicit aun entry in channels[] is skipped.
|
|
@@ -252,7 +256,7 @@ export function buildReloadHooks(deps) {
|
|
|
252
256
|
const ctx = {
|
|
253
257
|
agentName: agent.aid ?? agent.config?.aid,
|
|
254
258
|
defaultProjectPath: agent.config?.projects?.defaultPath ?? process.cwd(),
|
|
255
|
-
debug:
|
|
259
|
+
debug: channelLoader.processDebug,
|
|
256
260
|
};
|
|
257
261
|
const plugin = channelLoader.getPlugin(cfgInst.type);
|
|
258
262
|
if (!plugin)
|
|
@@ -18,6 +18,7 @@ import { buildSessionTurnList } from '../session/session-turns.js';
|
|
|
18
18
|
import { resolveConfigCommand } from '../../config/resolved-config-op.js';
|
|
19
19
|
import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommand } from '../../config/config-operation-service.js';
|
|
20
20
|
import { hashArgv } from '../auth/authorization-audit.js';
|
|
21
|
+
import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
|
|
21
22
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
22
23
|
import { validateModelSelectionForRole } from '../model/model-permission.js';
|
|
23
24
|
import { constrainRuntimePermissionMode, validateRuntimeStringFieldOverride } from '../role/runtime-policy.js';
|
|
@@ -353,10 +354,13 @@ export class CommandHandler {
|
|
|
353
354
|
validateTriggerRuntimeFields(definition, role, baseagent) {
|
|
354
355
|
if (!role)
|
|
355
356
|
return undefined;
|
|
357
|
+
const executionBaseagent = definition.execution.type === 'trigger_session'
|
|
358
|
+
? definition.execution.baseagent ?? baseagent
|
|
359
|
+
: baseagent;
|
|
356
360
|
if (definition.execution.model) {
|
|
357
361
|
const modelDecision = validateModelSelectionForRole({
|
|
358
362
|
role,
|
|
359
|
-
baseagent,
|
|
363
|
+
baseagent: executionBaseagent,
|
|
360
364
|
requestedModel: definition.execution.model,
|
|
361
365
|
selfAid: definition.agentAid,
|
|
362
366
|
});
|
|
@@ -364,7 +368,7 @@ export class CommandHandler {
|
|
|
364
368
|
return modelDecision.message || '当前角色不允许使用该 Trigger 模型';
|
|
365
369
|
}
|
|
366
370
|
if (definition.execution.effort) {
|
|
367
|
-
const base =
|
|
371
|
+
const base = executionBaseagent || 'claude';
|
|
368
372
|
const effortDecision = validateRuntimeStringFieldOverride({
|
|
369
373
|
selfAid: definition.agentAid,
|
|
370
374
|
role,
|
|
@@ -447,6 +451,29 @@ export class CommandHandler {
|
|
|
447
451
|
const permissionError = this.validateTriggerPermissionModeForRole(permissionMode, isAdmin, triggerRole.role, schedulerAid);
|
|
448
452
|
if (permissionError)
|
|
449
453
|
return { error: permissionError };
|
|
454
|
+
if (parsed.baseagent !== undefined) {
|
|
455
|
+
if (typeof parsed.baseagent !== 'string' || !parsed.baseagent.trim()) {
|
|
456
|
+
return { error: 'Trigger baseagent 不能为空' };
|
|
457
|
+
}
|
|
458
|
+
if (parsed.executionType !== 'trigger_session') {
|
|
459
|
+
return { error: 'Trigger baseagent 仅适用于 trigger_session' };
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const triggerOwner = this.agentRegistry?.get?.(schedulerAid) ?? this.getOwningAgent(channel);
|
|
463
|
+
const executionBaseagent = parsed.executionType === 'trigger_session'
|
|
464
|
+
? parsed.baseagent?.trim() ?? triggerOwner?.baseagent ?? triggerRole.baseagent
|
|
465
|
+
: undefined;
|
|
466
|
+
if (parsed.executionType === 'trigger_session') {
|
|
467
|
+
if (!executionBaseagent)
|
|
468
|
+
return { error: '无法确定 Trigger 会话的 baseagent' };
|
|
469
|
+
const ownerName = triggerOwner?.name || '<unknown>';
|
|
470
|
+
const available = this.getAvailableBaseagentsForOwner(ownerName);
|
|
471
|
+
if (!available.includes(executionBaseagent)) {
|
|
472
|
+
return {
|
|
473
|
+
error: `Trigger baseagent 不可用:${executionBaseagent}(可用:${available.join(' / ') || '无'})`,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
}
|
|
450
477
|
const definition = {
|
|
451
478
|
$schema_version: 3.1,
|
|
452
479
|
id,
|
|
@@ -460,7 +487,10 @@ export class CommandHandler {
|
|
|
460
487
|
execution: {
|
|
461
488
|
type: parsed.executionType,
|
|
462
489
|
...(script ? { script } : { prompt: parsed.prompt || '' }),
|
|
463
|
-
...(parsed.executionType === 'trigger_session' ? {
|
|
490
|
+
...(parsed.executionType === 'trigger_session' ? {
|
|
491
|
+
thread: parsed.triggerThread ?? 'by_trigger',
|
|
492
|
+
baseagent: executionBaseagent,
|
|
493
|
+
} : {}),
|
|
464
494
|
model: parsed.model,
|
|
465
495
|
effort: parsed.effort,
|
|
466
496
|
...(permissionMode !== undefined ? { permissionMode } : {}),
|
|
@@ -784,12 +814,9 @@ export class CommandHandler {
|
|
|
784
814
|
item.lastActive = s.updatedAt;
|
|
785
815
|
return item;
|
|
786
816
|
}
|
|
787
|
-
/**
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
*/
|
|
791
|
-
getMenuItems(role, chatType = 'private', scope = 'agent') {
|
|
792
|
-
return menuGetMenuItems.call(this, role, chatType, scope);
|
|
817
|
+
/** 返回结构化命令菜单,已迁移的命令按当前授权主体过滤。 */
|
|
818
|
+
getMenuItems(role, chatType = 'private', scope = 'agent', authSubject) {
|
|
819
|
+
return menuGetMenuItems.call(this, role, chatType, scope, authSubject);
|
|
793
820
|
}
|
|
794
821
|
/** 动态子菜单:根据 cmd 路径返回选项列表(供 menu.query + cmd 使用) */
|
|
795
822
|
async getSubMenuItems(cmd, channel, channelId, userId, args, overrideIdentity, explicitChatType, fromControlChannel = false, authSubject, source = 'menu') {
|
|
@@ -813,8 +840,8 @@ export class CommandHandler {
|
|
|
813
840
|
return await menuExecMenuUpdate.call(this, cmd, value, channel, channelId, userId, overrideIdentity, fromControlChannel, args, authSubject, source);
|
|
814
841
|
}
|
|
815
842
|
/** menu.action — 触发动词。 */
|
|
816
|
-
async execMenuAction(cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel = false, authSubject, source = 'menu') {
|
|
817
|
-
return await menuExecMenuAction.call(this, cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel, authSubject, source);
|
|
843
|
+
async execMenuAction(cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel = false, authSubject, source = 'menu', transportReplyContext, deferSideEffect) {
|
|
844
|
+
return await menuExecMenuAction.call(this, cmd, action, args, channel, channelId, userId, overrideIdentity, explicitChatType, requestId, fromControlChannel, authSubject, source, transportReplyContext, deferSideEffect);
|
|
818
845
|
}
|
|
819
846
|
/** ECWeb 专用入口:身份只取可信 IPC 信封。 */
|
|
820
847
|
async execMenuForEcweb(payload, auth) {
|
|
@@ -955,9 +982,9 @@ export class CommandHandler {
|
|
|
955
982
|
/**
|
|
956
983
|
* 主命令处理入口
|
|
957
984
|
*/
|
|
958
|
-
async handle(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject) {
|
|
985
|
+
async handle(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext) {
|
|
959
986
|
try {
|
|
960
|
-
const result = await this._handleInternal(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject);
|
|
987
|
+
const result = await this._handleInternal(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext);
|
|
961
988
|
return result;
|
|
962
989
|
}
|
|
963
990
|
catch (error) {
|
|
@@ -968,8 +995,8 @@ export class CommandHandler {
|
|
|
968
995
|
throw error;
|
|
969
996
|
}
|
|
970
997
|
}
|
|
971
|
-
async _handleInternal(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject) {
|
|
972
|
-
return await handleSlashCommand.call(this, content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject);
|
|
998
|
+
async _handleInternal(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext) {
|
|
999
|
+
return await handleSlashCommand.call(this, content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext);
|
|
973
1000
|
}
|
|
974
1001
|
async handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId) {
|
|
975
1002
|
if (content === '/trigger') {
|
|
@@ -1117,8 +1144,13 @@ export class CommandHandler {
|
|
|
1117
1144
|
const nextStr = updated.trigger.nextFireAt ? new Date(updated.trigger.nextFireAt).toLocaleString() : '未计算';
|
|
1118
1145
|
return `✅ 触发器已更新:**${updated.trigger.name}**\n下次触发:${nextStr}`;
|
|
1119
1146
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1147
|
+
const createSubcommand = sub.startsWith('create ')
|
|
1148
|
+
? 'create'
|
|
1149
|
+
: sub.startsWith('set ')
|
|
1150
|
+
? 'set'
|
|
1151
|
+
: undefined;
|
|
1152
|
+
if (createSubcommand) {
|
|
1153
|
+
const args = sub.slice(`${createSubcommand} `.length);
|
|
1122
1154
|
const result = parseTriggerSet(args);
|
|
1123
1155
|
if (!result.ok)
|
|
1124
1156
|
return `❌ ${result.error}`;
|
|
@@ -1131,9 +1163,9 @@ export class CommandHandler {
|
|
|
1131
1163
|
return `❌ 未知子命令。用法:
|
|
1132
1164
|
/trigger — 查看活跃触发器
|
|
1133
1165
|
/trigger list [--all] — 查看所有触发器
|
|
1134
|
-
/trigger
|
|
1166
|
+
/trigger create <参数> — 创建触发器(set 为兼容别名)
|
|
1135
1167
|
/trigger update <名称|ID> <参数> — 修改触发器
|
|
1136
|
-
常用参数:--delay/--at/--cron/--every、--prompt、--model、--effort、--permission(省略则继承;update 可用 inherit 清除覆盖)
|
|
1168
|
+
常用参数:--exec script|trigger-session|target-session(create 必填)、--delay/--at/--cron/--every、--prompt、--model、--effort、--permission(省略则继承;update 可用 inherit 清除覆盖)
|
|
1137
1169
|
/trigger enable <名称|ID> — 启用触发器
|
|
1138
1170
|
/trigger disable <名称|ID> — 暂停触发器
|
|
1139
1171
|
/trigger show <名称|ID> — 查看触发器详情
|
|
@@ -1416,11 +1448,11 @@ export class CommandHandler {
|
|
|
1416
1448
|
* Agent managed CLI config entrypoint. Identity and relation are resolved
|
|
1417
1449
|
* from the daemon-owned session rather than caller-provided flags.
|
|
1418
1450
|
*/
|
|
1419
|
-
async handleConfigOperation(argv, sessionId, delegationToken) {
|
|
1451
|
+
async handleConfigOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1420
1452
|
if (!this.agentDelegationRegistry) {
|
|
1421
1453
|
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
1422
1454
|
}
|
|
1423
|
-
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId);
|
|
1455
|
+
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1424
1456
|
if (!delegation.ok)
|
|
1425
1457
|
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1426
1458
|
const session = await this.sessionManager.getSessionById(sessionId);
|
|
@@ -1477,6 +1509,66 @@ export class CommandHandler {
|
|
|
1477
1509
|
? { ok: true, result }
|
|
1478
1510
|
: { ok: false, code: result.code, error: result.error, ...(result.data !== undefined ? { data: result.data } : {}) };
|
|
1479
1511
|
}
|
|
1512
|
+
/** Agent-managed contact CLI entrypoint. The daemon owns actor resolution and authorization. */
|
|
1513
|
+
async handleContactOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1514
|
+
if (!this.agentDelegationRegistry) {
|
|
1515
|
+
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
1516
|
+
}
|
|
1517
|
+
const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
|
|
1518
|
+
if (!delegation.ok)
|
|
1519
|
+
return { ok: false, code: delegation.code, error: delegation.reason };
|
|
1520
|
+
const session = await this.sessionManager.getSessionById(sessionId);
|
|
1521
|
+
if (!session)
|
|
1522
|
+
return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
|
|
1523
|
+
const grant = delegation.grant;
|
|
1524
|
+
let conversationId;
|
|
1525
|
+
try {
|
|
1526
|
+
conversationId = parsePeerKey(grant.peerKey).channelId;
|
|
1527
|
+
}
|
|
1528
|
+
catch {
|
|
1529
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1530
|
+
}
|
|
1531
|
+
const resolved = resolveContactCommand(argv);
|
|
1532
|
+
if (!resolved.ok)
|
|
1533
|
+
return { ok: false, code: resolved.code, error: resolved.reason };
|
|
1534
|
+
if (resolved.command.selfAid !== grant.selfAid) {
|
|
1535
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent contact book can be targeted' };
|
|
1536
|
+
}
|
|
1537
|
+
const subject = buildAuthSubject({
|
|
1538
|
+
selfAid: grant.selfAid,
|
|
1539
|
+
actorId: grant.actorId,
|
|
1540
|
+
channel: grant.channel,
|
|
1541
|
+
channelType: grant.channelType,
|
|
1542
|
+
channelId: conversationId,
|
|
1543
|
+
chatType: grant.chatType,
|
|
1544
|
+
conversationId,
|
|
1545
|
+
processOwners: loadDaemonConfig().owners ?? [],
|
|
1546
|
+
});
|
|
1547
|
+
const decision = authorizeOperation({
|
|
1548
|
+
source: 'agent-tool',
|
|
1549
|
+
subject,
|
|
1550
|
+
intent: {
|
|
1551
|
+
operation: resolved.command.operationId,
|
|
1552
|
+
scope: 'agent',
|
|
1553
|
+
source: 'agent-tool',
|
|
1554
|
+
args: {
|
|
1555
|
+
self: resolved.command.selfAid,
|
|
1556
|
+
...('primaryId' in resolved.command ? { primaryId: resolved.command.primaryId } : {}),
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
auditMetadata: {
|
|
1560
|
+
argvHash: hashArgv(resolved.command.canonicalArgv),
|
|
1561
|
+
taskId: grant.taskId,
|
|
1562
|
+
messageId: grant.messageId,
|
|
1563
|
+
},
|
|
1564
|
+
});
|
|
1565
|
+
if (!decision.allow)
|
|
1566
|
+
return { ok: false, code: decision.code, error: decision.reason };
|
|
1567
|
+
const result = await executeResolvedContactCommand(resolved.command, grant.actorId);
|
|
1568
|
+
return result.ok
|
|
1569
|
+
? { ok: true, result }
|
|
1570
|
+
: { ok: false, code: result.code, error: result.error, ...(result.data ? { data: result.data } : {}) };
|
|
1571
|
+
}
|
|
1480
1572
|
setAgentDelegationRegistry(registry) {
|
|
1481
1573
|
this.agentDelegationRegistry = registry;
|
|
1482
1574
|
}
|