evolcore 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +58 -9
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +379 -108
- package/dist/agents/codex-app-server-client.js +10 -2
- package/dist/agents/codex-runner.js +402 -135
- package/dist/agents/ecagent-runner.js +171 -61
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +182 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +409 -88
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +69 -115
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +46 -0
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +16 -5
- package/dist/config/config-manager.js +135 -17
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +31 -1
- package/dist/core/auth/auth-gateway.js +33 -4
- package/dist/core/auth/authorization-audit.js +150 -2
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-service.js +6 -2
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +290 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +279 -80
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +350 -32
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +10 -0
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +83 -84
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +124 -10
- package/dist/core/message/message-log.js +14 -7
- package/dist/core/message/message-queue.js +206 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +486 -68
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +410 -54
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-policy.js +575 -21
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +299 -30
- package/dist/core/session/session-renew.js +19 -12
- package/dist/core/session/session-turn-coordinator.js +11 -4
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +253 -46
- package/dist/ipc.js +374 -24
- package/dist/paths.js +64 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +56 -5
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +3 -2
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +21 -2
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/schemas/_meta.json +4 -2
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { BaseagentRunnerUnavailableError } from '../../agents/runner-types.js';
|
|
2
2
|
import { renderCommandCardAsText } from '../interaction-router.js';
|
|
3
|
-
import { buildEnvelope, deliveryTargetFromSession, replyContextFromSession, sendInteractionPayload } from '../message/message-utils.js';
|
|
3
|
+
import { buildEnvelope, deliveryTargetFromSession, isInteractionSendAccepted, replyContextFromSession, sendInteractionPayload } from '../message/message-utils.js';
|
|
4
4
|
import { resolvePaths, getPackageRoot } from '../../paths.js';
|
|
5
|
-
import { loadDaemonConfig } from '../../config-store.js';
|
|
5
|
+
import { isFullAccessEnabled, loadDaemonConfig } from '../../config-store.js';
|
|
6
6
|
import { logger } from '../../utils/logger.js';
|
|
7
7
|
import { resolvePeerRoleDetail, roleToSessionIdentity } from '../../config/peer-role-resolver.js';
|
|
8
8
|
import { isManagementRole } from '../../config/builtin-roles.js';
|
|
@@ -19,12 +19,13 @@ 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 { auditCommandAuthorization, hashArgv } from '../auth/authorization-audit.js';
|
|
22
|
+
import { auditCommandAuthorization, auditFullAccessEvent, hashArgv } from '../auth/authorization-audit.js';
|
|
23
23
|
import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
|
|
24
|
+
import { hasTrustedFullAccessDelegation, trustedExecutionIdentityInput } from '../auth/agent-delegation.js';
|
|
24
25
|
import { executeManagedAidOperation, ManagedAidOperationError, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
|
|
25
26
|
import { executeManagedGroupOperation, isManagedGroupRelationRead, ManagedGroupOperationError, resolveManagedGroupOperation, executeManagedMsgOperation, ManagedMsgOperationError, resolveManagedMsgOperation, executeManagedFsOperation, ManagedFsOperationError, resolveManagedFsOperation, } from '../../aun/msg/managed-operation.js';
|
|
26
27
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
27
|
-
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../protected-paths.js';
|
|
28
|
+
import { isHClassPath, isLClassPath, isSameOrDescendant } from '../permission/protected-paths.js';
|
|
28
29
|
import { validateModelSelectionForRole } from '../model/model-permission.js';
|
|
29
30
|
import { getModelInfo } from '../model/model-catalog.js';
|
|
30
31
|
import { constrainRuntimePermissionMode, resolveRuntimePermissionMode, validateRuntimeStringFieldOverride } from '../role/runtime-policy.js';
|
|
@@ -79,6 +80,20 @@ function writeUserSettings(updates) {
|
|
|
79
80
|
function isAdminRole(role) {
|
|
80
81
|
return isManagementRole(role);
|
|
81
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* A cross-channel history selector is intentionally narrower than ordinary
|
|
85
|
+
* managed reads. The persisted session, rather than a caller-controlled
|
|
86
|
+
* runtime hint, proves that this command belongs to a daemon Trigger.
|
|
87
|
+
*/
|
|
88
|
+
function isTrustedDaemonTriggerSession(session, selfAid) {
|
|
89
|
+
const channelId = String(session.channelId || '');
|
|
90
|
+
return session.channel === 'daemon'
|
|
91
|
+
&& session.channelType === 'daemon'
|
|
92
|
+
&& channelId.startsWith('trigger:')
|
|
93
|
+
&& session.selfAID?.replace(/^@/, '') === selfAid
|
|
94
|
+
&& session.metadata?.channelKey === 'daemon'
|
|
95
|
+
&& session.metadata?.peerId === channelId;
|
|
96
|
+
}
|
|
82
97
|
export class CommandHandler {
|
|
83
98
|
sessionManager;
|
|
84
99
|
messageCache;
|
|
@@ -95,12 +110,14 @@ export class CommandHandler {
|
|
|
95
110
|
agentMap;
|
|
96
111
|
primaryRunnerKey;
|
|
97
112
|
agentRegistry;
|
|
113
|
+
agentApplicationService;
|
|
98
114
|
triggerSchedulerResolver;
|
|
99
115
|
triggerSchedulerFallback;
|
|
100
116
|
triggerSchedulerFallbackAid;
|
|
101
117
|
daemonStatusProvider;
|
|
102
118
|
agentDelegationRegistry;
|
|
103
119
|
handoffRuntime;
|
|
120
|
+
contactRequestSubmitter;
|
|
104
121
|
/**
|
|
105
122
|
* Get the runner for a (channel, baseagent) pair.
|
|
106
123
|
*
|
|
@@ -195,6 +212,10 @@ export class CommandHandler {
|
|
|
195
212
|
setAgentRegistry(registry) {
|
|
196
213
|
this.agentRegistry = registry;
|
|
197
214
|
}
|
|
215
|
+
/** Inject the Core-owned agent application facade after runtime hooks exist. */
|
|
216
|
+
setAgentApplicationService(service) {
|
|
217
|
+
this.agentApplicationService = service;
|
|
218
|
+
}
|
|
198
219
|
setTriggerSchedulerResolver(resolver) {
|
|
199
220
|
this.triggerSchedulerResolver = resolver;
|
|
200
221
|
}
|
|
@@ -342,9 +363,39 @@ export class CommandHandler {
|
|
|
342
363
|
triggerPermissionFromParsed(parsed) {
|
|
343
364
|
return parsed.permissionMode ?? undefined;
|
|
344
365
|
}
|
|
345
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Persist fullaccess approval provenance as the canonical process owner,
|
|
368
|
+
* rather than the transport-specific peer id. A Feishu/other-channel
|
|
369
|
+
* alias may authenticate as a daemon-owner while its raw peer id is not an
|
|
370
|
+
* entry in daemon.json.owners; storing that alias would make the Trigger
|
|
371
|
+
* fail its next scheduler-time owner check.
|
|
372
|
+
*/
|
|
373
|
+
canonicalDaemonOwnerId(selfAid, channel, peerId, chatType) {
|
|
374
|
+
const fallback = String(peerId || '').trim();
|
|
375
|
+
try {
|
|
376
|
+
const detail = resolvePeerRoleDetail({
|
|
377
|
+
selfAid,
|
|
378
|
+
channelKey: channel,
|
|
379
|
+
channelType: this.resolveChannelType(channel),
|
|
380
|
+
chatType: chatType === 'group' ? 'group' : 'private',
|
|
381
|
+
actorId: fallback,
|
|
382
|
+
conversationId: fallback,
|
|
383
|
+
});
|
|
384
|
+
const principal = detail.actor.principalId?.trim();
|
|
385
|
+
return principal || fallback;
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return fallback;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
validateTriggerPermissionModeForRole(mode, isAdmin, role, selfAid, isDaemonOwner = false) {
|
|
346
392
|
if (!mode)
|
|
347
393
|
return undefined;
|
|
394
|
+
if (mode === 'fullaccess') {
|
|
395
|
+
if (!isFullAccessEnabled())
|
|
396
|
+
return 'fullaccess 功能未启用';
|
|
397
|
+
return isDaemonOwner ? undefined : '无权限:只有 daemon-owner 可以配置 fullaccess Trigger';
|
|
398
|
+
}
|
|
348
399
|
if (!role) {
|
|
349
400
|
if (mode === 'bypass' && !isAdmin)
|
|
350
401
|
return '无权限:只有 owner/admin 可以创建或修改 bypass trigger';
|
|
@@ -355,6 +406,19 @@ export class CommandHandler {
|
|
|
355
406
|
return `无权限:角色 ${role} 不能使用 ${mode} trigger permissionMode`;
|
|
356
407
|
return undefined;
|
|
357
408
|
}
|
|
409
|
+
auditFullAccessTriggerConfiguration(input) {
|
|
410
|
+
auditFullAccessEvent({
|
|
411
|
+
event: input.event,
|
|
412
|
+
source: 'trigger',
|
|
413
|
+
actorId: input.actorId,
|
|
414
|
+
processRole: input.isDaemonOwner ? 'daemon-owner' : 'none',
|
|
415
|
+
agentAid: input.definition.agentAid,
|
|
416
|
+
messageId: input.messageId,
|
|
417
|
+
triggerId: input.definition.id,
|
|
418
|
+
authorizedBy: input.definition.authorizedBy,
|
|
419
|
+
reason: input.reason,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
358
422
|
triggerRoleContext(selfAid, channel, peerId, chatType) {
|
|
359
423
|
const owning = this.getOwningAgent(channel);
|
|
360
424
|
if (!owning || owning.aid !== selfAid)
|
|
@@ -404,7 +468,7 @@ export class CommandHandler {
|
|
|
404
468
|
.find(definition => (definition.id === nameOrId || definition.name === nameOrId)
|
|
405
469
|
&& this.canAccessTriggerDefinition(definition, peerId, channel, isAdmin));
|
|
406
470
|
}
|
|
407
|
-
async buildTriggerDefinitionFromParsed(parsed, channel, channelId, peerId, isAdmin, messageId, chatType, threadId) {
|
|
471
|
+
async buildTriggerDefinitionFromParsed(parsed, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
408
472
|
const now = Date.now();
|
|
409
473
|
const id = `trig_${now}_${crypto.randomBytes(4).toString('hex')}`;
|
|
410
474
|
const name = parsed.name ?? `trigger-${now.toString(36)}`;
|
|
@@ -475,9 +539,12 @@ export class CommandHandler {
|
|
|
475
539
|
} : undefined;
|
|
476
540
|
const permissionMode = this.triggerPermissionFromParsed(parsed);
|
|
477
541
|
const triggerRole = this.triggerRoleContext(schedulerAid, channel, peerId, chatType);
|
|
478
|
-
const permissionError = this.validateTriggerPermissionModeForRole(permissionMode, isAdmin, triggerRole.role, schedulerAid);
|
|
542
|
+
const permissionError = this.validateTriggerPermissionModeForRole(permissionMode, isAdmin, triggerRole.role, schedulerAid, isDaemonOwner);
|
|
479
543
|
if (permissionError)
|
|
480
544
|
return { error: permissionError };
|
|
545
|
+
if (permissionMode === 'fullaccess' && !canApprovePersistentFullAccess) {
|
|
546
|
+
return { error: '当前认证通道不能批准持久 fullaccess Trigger' };
|
|
547
|
+
}
|
|
481
548
|
if (parsed.baseagent !== undefined) {
|
|
482
549
|
if (typeof parsed.baseagent !== 'string' || !parsed.baseagent.trim()) {
|
|
483
550
|
return { error: 'Trigger baseagent 不能为空' };
|
|
@@ -501,6 +568,9 @@ export class CommandHandler {
|
|
|
501
568
|
};
|
|
502
569
|
}
|
|
503
570
|
}
|
|
571
|
+
const authorizedBy = permissionMode === 'fullaccess' && isDaemonOwner
|
|
572
|
+
? this.canonicalDaemonOwnerId(schedulerAid, channel, peerId, chatType)
|
|
573
|
+
: undefined;
|
|
504
574
|
const definition = {
|
|
505
575
|
$schema_version: 3.1,
|
|
506
576
|
id,
|
|
@@ -509,6 +579,7 @@ export class CommandHandler {
|
|
|
509
579
|
name,
|
|
510
580
|
createdAt: now,
|
|
511
581
|
updatedAt: now,
|
|
582
|
+
...(authorizedBy ? { authorizedBy } : {}),
|
|
512
583
|
origin,
|
|
513
584
|
source,
|
|
514
585
|
execution: {
|
|
@@ -665,8 +736,8 @@ export class CommandHandler {
|
|
|
665
736
|
replyContext: opts.replyCtx,
|
|
666
737
|
});
|
|
667
738
|
const fallbackText = renderCommandCardAsText(card);
|
|
668
|
-
const
|
|
669
|
-
if (
|
|
739
|
+
const receipt = await sendInteractionPayload(adapter, envelope, opts.interaction, fallbackText, opts.replyCtx);
|
|
740
|
+
if (isInteractionSendAccepted(receipt))
|
|
670
741
|
return null;
|
|
671
742
|
}
|
|
672
743
|
catch (e) {
|
|
@@ -711,6 +782,10 @@ export class CommandHandler {
|
|
|
711
782
|
setInteractionRouter(router) {
|
|
712
783
|
this.interactionRouter = router;
|
|
713
784
|
}
|
|
785
|
+
/** Inject the daemon-owned contact request/card workflow for Agent self-service. */
|
|
786
|
+
setContactRequestSubmitter(submitter) {
|
|
787
|
+
this.contactRequestSubmitter = submitter;
|
|
788
|
+
}
|
|
714
789
|
setStatsCollector(collector) {
|
|
715
790
|
this.statsCollector = collector;
|
|
716
791
|
}
|
|
@@ -1057,7 +1132,7 @@ export class CommandHandler {
|
|
|
1057
1132
|
async _handleInternal(content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext) {
|
|
1058
1133
|
return await handleSlashCommand.call(this, content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext);
|
|
1059
1134
|
}
|
|
1060
|
-
async handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId) {
|
|
1135
|
+
async handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
1061
1136
|
if (content === '/trigger') {
|
|
1062
1137
|
const visible = scheduler
|
|
1063
1138
|
.list()
|
|
@@ -1110,11 +1185,35 @@ export class CommandHandler {
|
|
|
1110
1185
|
const prefix = sub.startsWith('delete ') ? 'delete ' : sub.startsWith('remove ') ? 'remove ' : 'rm ';
|
|
1111
1186
|
const { definition } = extractAndFindTrigger(sub, prefix, 'delete');
|
|
1112
1187
|
const deleted = scheduler.delete(definition.id);
|
|
1188
|
+
if (deleted.execution.permissionMode === 'fullaccess') {
|
|
1189
|
+
this.auditFullAccessTriggerConfiguration({
|
|
1190
|
+
event: 'fullaccess.trigger.deescalated', definition: deleted, actorId: peerId,
|
|
1191
|
+
messageId, isDaemonOwner, reason: 'fullaccess Trigger deleted',
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1113
1194
|
return `✅ 触发器已删除:**${deleted.name}**`;
|
|
1114
1195
|
}
|
|
1115
1196
|
if (sub.startsWith('enable ')) {
|
|
1116
1197
|
const { definition } = extractAndFindTrigger(sub, 'enable ', 'enable');
|
|
1117
|
-
|
|
1198
|
+
if (definition.execution.permissionMode === 'fullaccess' && (!isDaemonOwner || !isFullAccessEnabled())) {
|
|
1199
|
+
return '❌ 只有 daemon-owner 可以启用 fullaccess Trigger,且 daemon.json.fullaccess.enabled 必须为 true';
|
|
1200
|
+
}
|
|
1201
|
+
if (definition.execution.permissionMode === 'fullaccess' && !canApprovePersistentFullAccess) {
|
|
1202
|
+
return '❌ 当前认证通道不能批准持久 fullaccess Trigger';
|
|
1203
|
+
}
|
|
1204
|
+
if (definition.execution.permissionMode === 'fullaccess') {
|
|
1205
|
+
const authorizedBy = this.canonicalDaemonOwnerId(definition.agentAid, definition.origin?.channelKey || channel, peerId);
|
|
1206
|
+
if (!authorizedBy)
|
|
1207
|
+
return '❌ 无法确认 fullaccess Trigger 的批准者';
|
|
1208
|
+
scheduler.update(definition.id, { ...definition, authorizedBy });
|
|
1209
|
+
}
|
|
1210
|
+
const enabled = scheduler.setEnabled(definition.id, true);
|
|
1211
|
+
if (enabled.execution.permissionMode === 'fullaccess') {
|
|
1212
|
+
this.auditFullAccessTriggerConfiguration({
|
|
1213
|
+
event: 'fullaccess.trigger.configured', definition: enabled, actorId: peerId,
|
|
1214
|
+
messageId, isDaemonOwner, reason: 'fullaccess Trigger enabled',
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1118
1217
|
return `✅ 触发器已启用:**${definition.name}**`;
|
|
1119
1218
|
}
|
|
1120
1219
|
if (sub.startsWith('disable ')) {
|
|
@@ -1197,7 +1296,7 @@ export class CommandHandler {
|
|
|
1197
1296
|
const result = parseTriggerUpdate(args);
|
|
1198
1297
|
if (!result.ok)
|
|
1199
1298
|
return `❌ ${result.error}`;
|
|
1200
|
-
const updated = await this.updateTriggerFromPatch(scheduler, result.nameOrId, result.value, channel, channelId, peerId, isAdmin, messageId, threadId);
|
|
1299
|
+
const updated = await this.updateTriggerFromPatch(scheduler, result.nameOrId, result.value, channel, channelId, peerId, isAdmin, messageId, threadId, isDaemonOwner, canApprovePersistentFullAccess);
|
|
1201
1300
|
if (!updated.ok)
|
|
1202
1301
|
return `❌ ${updated.error}`;
|
|
1203
1302
|
const nextStr = updated.trigger.nextFireAt ? new Date(updated.trigger.nextFireAt).toLocaleString() : '未计算';
|
|
@@ -1213,7 +1312,7 @@ export class CommandHandler {
|
|
|
1213
1312
|
const result = parseTriggerSet(args);
|
|
1214
1313
|
if (!result.ok)
|
|
1215
1314
|
return `❌ ${result.error}`;
|
|
1216
|
-
const reg = await this.registerTriggerFromParsed(result.value, channel, channelId, peerId, messageId, chatType, threadId, isAdmin);
|
|
1315
|
+
const reg = await this.registerTriggerFromParsed(result.value, channel, channelId, peerId, messageId, chatType, threadId, isAdmin, isDaemonOwner, canApprovePersistentFullAccess);
|
|
1217
1316
|
if (!reg.ok)
|
|
1218
1317
|
return `❌ ${reg.error}`;
|
|
1219
1318
|
const nextStr = reg.trigger.nextFireAt ? new Date(reg.trigger.nextFireAt).toLocaleString() : '未计算';
|
|
@@ -1232,13 +1331,13 @@ export class CommandHandler {
|
|
|
1232
1331
|
/trigger cancel <名称|ID> — 禁用触发器(disable 的兼容别名)
|
|
1233
1332
|
/trigger delete <名称|ID> — 删除触发器`;
|
|
1234
1333
|
}
|
|
1235
|
-
async handleTrigger(content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId) {
|
|
1334
|
+
async handleTrigger(content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
1236
1335
|
const scheduler = this.getTriggerSchedulerForChannel(channel);
|
|
1237
1336
|
if (!scheduler)
|
|
1238
1337
|
return '⚠️ 触发器功能未启用';
|
|
1239
|
-
return await this.handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId);
|
|
1338
|
+
return await this.handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner, canApprovePersistentFullAccess);
|
|
1240
1339
|
}
|
|
1241
|
-
async updateTriggerFromPatch(scheduler, nameOrId, patch, channel, channelId, peerId, isAdmin, messageId, threadId) {
|
|
1340
|
+
async updateTriggerFromPatch(scheduler, nameOrId, patch, channel, channelId, peerId, isAdmin, messageId, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
1242
1341
|
const definition = this.findTriggerDefinition(scheduler, nameOrId, peerId, channel, isAdmin);
|
|
1243
1342
|
if (!definition) {
|
|
1244
1343
|
return { ok: false, error: isAdmin ? `未找到触发器:${nameOrId}` : `未找到触发器 "${nameOrId}",或无权限修改` };
|
|
@@ -1275,6 +1374,22 @@ export class CommandHandler {
|
|
|
1275
1374
|
catch (err) {
|
|
1276
1375
|
return { ok: false, error: err?.message || String(err) };
|
|
1277
1376
|
}
|
|
1377
|
+
if (updated.execution.permissionMode === 'fullaccess') {
|
|
1378
|
+
if (!isDaemonOwner) {
|
|
1379
|
+
return { ok: false, code: 'FORBIDDEN', error: '无权限:只有 daemon-owner 可以配置 fullaccess Trigger' };
|
|
1380
|
+
}
|
|
1381
|
+
if (!canApprovePersistentFullAccess) {
|
|
1382
|
+
return { ok: false, error: '当前认证通道不能批准持久 fullaccess Trigger', code: 'FORBIDDEN' };
|
|
1383
|
+
}
|
|
1384
|
+
updated = {
|
|
1385
|
+
...updated,
|
|
1386
|
+
authorizedBy: this.canonicalDaemonOwnerId(definition.agentAid, definition.origin?.channelKey || channel, peerId),
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
else {
|
|
1390
|
+
const { authorizedBy: _authorizedBy, ...deescalated } = updated;
|
|
1391
|
+
updated = deescalated;
|
|
1392
|
+
}
|
|
1278
1393
|
const target = this.primaryFeedbackTarget(updated);
|
|
1279
1394
|
if (!isAdmin && target && !sameFeedbackTarget(target, definition.origin)) {
|
|
1280
1395
|
return { ok: false, error: '无权限:非 admin 只能把 trigger 投递到来源会话' };
|
|
@@ -1283,14 +1398,35 @@ export class CommandHandler {
|
|
|
1283
1398
|
return { ok: false, error: '无权限:只有 owner/admin 可以修改 script trigger' };
|
|
1284
1399
|
}
|
|
1285
1400
|
const triggerRole = this.triggerRoleContext(definition.agentAid, definition.origin?.channelKey || channel, definition.origin?.peerId || peerId);
|
|
1286
|
-
const permissionError = this.validateTriggerPermissionModeForRole(updated.execution.permissionMode, isAdmin, triggerRole.role, definition.agentAid);
|
|
1287
|
-
if (permissionError)
|
|
1288
|
-
return {
|
|
1401
|
+
const permissionError = this.validateTriggerPermissionModeForRole(updated.execution.permissionMode, isAdmin, triggerRole.role, definition.agentAid, isDaemonOwner);
|
|
1402
|
+
if (permissionError) {
|
|
1403
|
+
return {
|
|
1404
|
+
ok: false,
|
|
1405
|
+
error: permissionError,
|
|
1406
|
+
code: /fullaccess 功能未启用/.test(permissionError) ? 'NOT_SUPPORTED'
|
|
1407
|
+
: /无权限|只有 daemon-owner/.test(permissionError) ? 'FORBIDDEN'
|
|
1408
|
+
: undefined,
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1289
1411
|
const runtimeFieldError = this.validateTriggerRuntimeFields(updated, triggerRole.role, triggerRole.baseagent);
|
|
1290
1412
|
if (runtimeFieldError)
|
|
1291
1413
|
return { ok: false, error: runtimeFieldError };
|
|
1292
1414
|
try {
|
|
1293
1415
|
const saved = scheduler.update(definition.id, updated);
|
|
1416
|
+
if (saved.execution.permissionMode === 'fullaccess') {
|
|
1417
|
+
this.auditFullAccessTriggerConfiguration({
|
|
1418
|
+
event: 'fullaccess.trigger.configured', definition: saved, actorId: peerId,
|
|
1419
|
+
messageId, isDaemonOwner, reason: definition.execution.permissionMode === 'fullaccess'
|
|
1420
|
+
? 'fullaccess Trigger updated'
|
|
1421
|
+
: 'Trigger elevated to fullaccess',
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
else if (definition.execution.permissionMode === 'fullaccess') {
|
|
1425
|
+
this.auditFullAccessTriggerConfiguration({
|
|
1426
|
+
event: 'fullaccess.trigger.deescalated', definition, actorId: peerId,
|
|
1427
|
+
messageId, isDaemonOwner, reason: 'Trigger permissionMode changed from fullaccess',
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1294
1430
|
return {
|
|
1295
1431
|
ok: true,
|
|
1296
1432
|
trigger: this.definitionToTriggerView(saved, scheduler),
|
|
@@ -1304,12 +1440,18 @@ export class CommandHandler {
|
|
|
1304
1440
|
/** 从已解析的 trigger 参数组装 definition 并注册。文本路径(handleTrigger)与 menu 路径共用。
|
|
1305
1441
|
* parsed 形状 = parseTriggerSet 的 result.value(ParsedTriggerSet)。
|
|
1306
1442
|
* 失败 return { ok:false, error };成功 return { ok:true, trigger }。 */
|
|
1307
|
-
async registerTriggerFromParsed(parsed, channel, channelId, peerId, messageId, chatType, threadId, isAdmin = false) {
|
|
1308
|
-
const built = await this.buildTriggerDefinitionFromParsed(parsed, channel, channelId, peerId, isAdmin, messageId, chatType, threadId);
|
|
1443
|
+
async registerTriggerFromParsed(parsed, channel, channelId, peerId, messageId, chatType, threadId, isAdmin = false, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
|
|
1444
|
+
const built = await this.buildTriggerDefinitionFromParsed(parsed, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner, canApprovePersistentFullAccess);
|
|
1309
1445
|
if ('error' in built)
|
|
1310
1446
|
return { ok: false, error: built.error };
|
|
1311
1447
|
try {
|
|
1312
1448
|
const created = built.scheduler.create(built.definition, [], { enable: true });
|
|
1449
|
+
if (created.execution.permissionMode === 'fullaccess') {
|
|
1450
|
+
this.auditFullAccessTriggerConfiguration({
|
|
1451
|
+
event: 'fullaccess.trigger.configured', definition: created, actorId: peerId,
|
|
1452
|
+
messageId, isDaemonOwner, reason: 'fullaccess Trigger created',
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1313
1455
|
return { ok: true, trigger: this.definitionToTriggerView(created, built.scheduler) };
|
|
1314
1456
|
}
|
|
1315
1457
|
catch (err) {
|
|
@@ -1534,6 +1676,7 @@ export class CommandHandler {
|
|
|
1534
1676
|
chatType: grant.chatType,
|
|
1535
1677
|
conversationId,
|
|
1536
1678
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
1679
|
+
...trustedExecutionIdentityInput(grant),
|
|
1537
1680
|
});
|
|
1538
1681
|
const executeOne = (fieldArgv) => {
|
|
1539
1682
|
const resolved = resolveConfigCommand(fieldArgv, {
|
|
@@ -1606,9 +1749,19 @@ export class CommandHandler {
|
|
|
1606
1749
|
const resolved = resolveContactCommand(argv);
|
|
1607
1750
|
if (!resolved.ok)
|
|
1608
1751
|
return { ok: false, code: resolved.code, error: resolved.reason };
|
|
1609
|
-
if (resolved.command.selfAid !== grant.selfAid) {
|
|
1752
|
+
if (resolved.command.selfAid !== grant.selfAid && !hasTrustedFullAccessDelegation(grant)) {
|
|
1610
1753
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent contact book can be targeted' };
|
|
1611
1754
|
}
|
|
1755
|
+
const selfServiceAdd = resolved.command.kind === 'add';
|
|
1756
|
+
const primaryId = 'primaryId' in resolved.command ? resolved.command.primaryId : undefined;
|
|
1757
|
+
if (selfServiceAdd && !hasTrustedFullAccessDelegation(grant)) {
|
|
1758
|
+
if (grant.channelType !== 'aun' || grant.chatType !== 'private') {
|
|
1759
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'contact add requires the current AUN private relation' };
|
|
1760
|
+
}
|
|
1761
|
+
if (primaryId !== conversationId) {
|
|
1762
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'contact add may only target the current private peer' };
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1612
1765
|
const subject = buildAuthSubject({
|
|
1613
1766
|
selfAid: grant.selfAid,
|
|
1614
1767
|
actorId: grant.actorId,
|
|
@@ -1618,17 +1771,25 @@ export class CommandHandler {
|
|
|
1618
1771
|
chatType: grant.chatType,
|
|
1619
1772
|
conversationId,
|
|
1620
1773
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
1774
|
+
...trustedExecutionIdentityInput(grant),
|
|
1621
1775
|
});
|
|
1622
1776
|
const decision = authorizeOperation({
|
|
1623
1777
|
source: 'agent-tool',
|
|
1624
1778
|
subject,
|
|
1625
1779
|
intent: {
|
|
1626
1780
|
operation: resolved.command.operationId,
|
|
1627
|
-
scope: 'agent',
|
|
1781
|
+
scope: selfServiceAdd ? 'relation' : 'agent',
|
|
1628
1782
|
source: 'agent-tool',
|
|
1629
1783
|
args: {
|
|
1630
1784
|
self: resolved.command.selfAid,
|
|
1631
|
-
...(
|
|
1785
|
+
...(primaryId ? { primaryId } : {}),
|
|
1786
|
+
...(selfServiceAdd
|
|
1787
|
+
? {
|
|
1788
|
+
targetId: primaryId,
|
|
1789
|
+
peer: primaryId,
|
|
1790
|
+
peerKey: `aun#${primaryId}`,
|
|
1791
|
+
}
|
|
1792
|
+
: {}),
|
|
1632
1793
|
},
|
|
1633
1794
|
},
|
|
1634
1795
|
auditMetadata: {
|
|
@@ -1639,6 +1800,47 @@ export class CommandHandler {
|
|
|
1639
1800
|
});
|
|
1640
1801
|
if (!decision.allow)
|
|
1641
1802
|
return { ok: false, code: decision.code, error: decision.reason };
|
|
1803
|
+
if (selfServiceAdd) {
|
|
1804
|
+
if (!this.contactRequestSubmitter) {
|
|
1805
|
+
return { ok: false, code: 'CONTACT_REQUEST_UNAVAILABLE', error: 'Contact request workflow is not configured' };
|
|
1806
|
+
}
|
|
1807
|
+
try {
|
|
1808
|
+
const submission = await this.contactRequestSubmitter({
|
|
1809
|
+
selfAid: grant.selfAid,
|
|
1810
|
+
applicantAid: primaryId,
|
|
1811
|
+
sourceChannelKey: grant.channel,
|
|
1812
|
+
});
|
|
1813
|
+
if (submission.code === 'blocked') {
|
|
1814
|
+
return {
|
|
1815
|
+
ok: false,
|
|
1816
|
+
code: 'CONTACT_BLOCKED',
|
|
1817
|
+
error: `目标联系人 ${primaryId} 已被拉黑。请先解除拉黑:ec contact unblock ${primaryId} --self ${grant.selfAid}`,
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
return {
|
|
1821
|
+
ok: true,
|
|
1822
|
+
result: {
|
|
1823
|
+
ok: true,
|
|
1824
|
+
operation: 'add',
|
|
1825
|
+
selfAid: grant.selfAid,
|
|
1826
|
+
primaryId: primaryId,
|
|
1827
|
+
status: submission.code,
|
|
1828
|
+
changed: submission.code === 'submitted' || submission.code === 'resubmitted',
|
|
1829
|
+
requestId: submission.requestId,
|
|
1830
|
+
expiresAt: submission.expiresAt,
|
|
1831
|
+
contactRevision: submission.contactRevision,
|
|
1832
|
+
reviewCardSent: submission.reviewCardSent === true,
|
|
1833
|
+
},
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
catch (error) {
|
|
1837
|
+
return {
|
|
1838
|
+
ok: false,
|
|
1839
|
+
code: error?.code ?? 'CONTACT_REQUEST_FAILED',
|
|
1840
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1642
1844
|
const result = await executeResolvedContactCommand(resolved.command, grant.actorId);
|
|
1643
1845
|
return result.ok
|
|
1644
1846
|
? { ok: true, result }
|
|
@@ -1663,7 +1865,7 @@ export class CommandHandler {
|
|
|
1663
1865
|
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1664
1866
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1665
1867
|
}
|
|
1666
|
-
if (parsed.command.aid !== grantSelfAid) {
|
|
1868
|
+
if (parsed.command.aid !== grantSelfAid && !hasTrustedFullAccessDelegation(delegation.grant)) {
|
|
1667
1869
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can be targeted' };
|
|
1668
1870
|
}
|
|
1669
1871
|
let conversationId;
|
|
@@ -1682,6 +1884,7 @@ export class CommandHandler {
|
|
|
1682
1884
|
chatType: delegation.grant.chatType,
|
|
1683
1885
|
conversationId,
|
|
1684
1886
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
1887
|
+
...trustedExecutionIdentityInput(delegation.grant),
|
|
1685
1888
|
});
|
|
1686
1889
|
const managementRole = isManagementRole(subject.role);
|
|
1687
1890
|
const decision = authorizeOperation({
|
|
@@ -1733,7 +1936,7 @@ export class CommandHandler {
|
|
|
1733
1936
|
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1734
1937
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1735
1938
|
}
|
|
1736
|
-
if (parsed.command.from !== grantSelfAid) {
|
|
1939
|
+
if (parsed.command.from !== grantSelfAid && !hasTrustedFullAccessDelegation(grant)) {
|
|
1737
1940
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can perform managed group operations' };
|
|
1738
1941
|
}
|
|
1739
1942
|
let conversationId;
|
|
@@ -1752,12 +1955,16 @@ export class CommandHandler {
|
|
|
1752
1955
|
chatType: grant.chatType,
|
|
1753
1956
|
conversationId,
|
|
1754
1957
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
1958
|
+
...trustedExecutionIdentityInput(grant),
|
|
1755
1959
|
});
|
|
1756
|
-
const managementRole = isManagementRole(subject.role);
|
|
1757
|
-
if (
|
|
1960
|
+
const managementRole = isManagementRole(subject.role) || subject.dataScope === 'daemon';
|
|
1961
|
+
if (subject.dataScope !== 'daemon'
|
|
1962
|
+
&& !managementRole
|
|
1963
|
+
&& (grant.channelType !== 'aun' || !isManagedGroupRelationRead(parsed.command))) {
|
|
1758
1964
|
return { ok: false, code: 'ROLE_ACCESS_DENIED', error: `Role ${subject.role} cannot manage Agent group membership or settings` };
|
|
1759
1965
|
}
|
|
1760
|
-
if ((parsed.command.kind === 'dissolve' || parsed.command.kind === 'owner')
|
|
1966
|
+
if ((parsed.command.kind === 'dissolve' || parsed.command.kind === 'owner')
|
|
1967
|
+
&& subject.dataScope !== 'daemon' && subject.role !== 'owner') {
|
|
1761
1968
|
return { ok: false, code: 'ROLE_ACCESS_DENIED', error: `${parsed.command.kind} requires Agent owner permission` };
|
|
1762
1969
|
}
|
|
1763
1970
|
const decision = authorizeOperation({
|
|
@@ -1849,7 +2056,7 @@ export class CommandHandler {
|
|
|
1849
2056
|
};
|
|
1850
2057
|
}
|
|
1851
2058
|
}
|
|
1852
|
-
/** Agent-managed
|
|
2059
|
+
/** Agent-managed msg read entrypoint. AUN credentials and history roots remain daemon-owned. */
|
|
1853
2060
|
async handleMsgOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1854
2061
|
if (!this.agentDelegationRegistry) {
|
|
1855
2062
|
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
@@ -1902,28 +2109,53 @@ export class CommandHandler {
|
|
|
1902
2109
|
if (sessionSelfAid && sessionSelfAid !== grantSelfAid) {
|
|
1903
2110
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1904
2111
|
}
|
|
1905
|
-
if (
|
|
1906
|
-
return { ok: false, code: 'SCOPE_MISMATCH', error: '
|
|
2112
|
+
if (parsed.command.from !== grantSelfAid && !hasTrustedFullAccessDelegation(grant)) {
|
|
2113
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent AID can be targeted' };
|
|
1907
2114
|
}
|
|
1908
|
-
|
|
2115
|
+
const isSelectorHistory = parsed.command.kind === 'history'
|
|
2116
|
+
&& !!parsed.command.channelKey
|
|
2117
|
+
&& !!parsed.command.peerId;
|
|
2118
|
+
let conversationId;
|
|
1909
2119
|
try {
|
|
1910
|
-
|
|
2120
|
+
conversationId = parsePeerKey(grant.peerKey).channelId.replace(/^@/, '');
|
|
1911
2121
|
}
|
|
1912
2122
|
catch {
|
|
1913
2123
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1914
2124
|
}
|
|
1915
|
-
if (
|
|
1916
|
-
|
|
2125
|
+
if (isSelectorHistory) {
|
|
2126
|
+
if (!hasTrustedFullAccessDelegation(grant) && !isTrustedDaemonTriggerSession(session, grantSelfAid)) {
|
|
2127
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Cross-channel history selectors require a trusted daemon Trigger session' };
|
|
2128
|
+
}
|
|
1917
2129
|
}
|
|
2130
|
+
else {
|
|
2131
|
+
if (!hasTrustedFullAccessDelegation(grant) && (grant.channelType !== 'aun' || grant.chatType !== 'private')) {
|
|
2132
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed msg reads require the current AUN private relation' };
|
|
2133
|
+
}
|
|
2134
|
+
if (!hasTrustedFullAccessDelegation(grant) && parsed.command.target !== conversationId) {
|
|
2135
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN private relation can be targeted' };
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
const selectorChannel = isSelectorHistory
|
|
2139
|
+
? tryParseChannelKey(parsed.command.channelKey)
|
|
2140
|
+
: undefined;
|
|
2141
|
+
if (isSelectorHistory && !selectorChannel) {
|
|
2142
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: 'Invalid channel selector' };
|
|
2143
|
+
}
|
|
2144
|
+
const targetId = isSelectorHistory ? parsed.command.peerId : parsed.command.target;
|
|
2145
|
+
const targetPeerKey = isSelectorHistory
|
|
2146
|
+
? formatPeerKey(selectorChannel.type, targetId)
|
|
2147
|
+
: formatPeerKey('aun', targetId);
|
|
1918
2148
|
const subject = buildAuthSubject({
|
|
1919
2149
|
selfAid: grant.selfAid,
|
|
1920
2150
|
actorId: grant.actorId,
|
|
1921
2151
|
channel: grant.channel,
|
|
1922
2152
|
channelType: grant.channelType,
|
|
1923
|
-
channelId:
|
|
2153
|
+
channelId: conversationId,
|
|
1924
2154
|
chatType: grant.chatType,
|
|
1925
|
-
conversationId
|
|
2155
|
+
conversationId,
|
|
2156
|
+
...(isSelectorHistory ? { identity: roleToSessionIdentity(grant.issuedRole) } : {}),
|
|
1926
2157
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
2158
|
+
...trustedExecutionIdentityInput(grant),
|
|
1927
2159
|
});
|
|
1928
2160
|
const decision = authorizeOperation({
|
|
1929
2161
|
source: 'agent-tool',
|
|
@@ -1934,9 +2166,15 @@ export class CommandHandler {
|
|
|
1934
2166
|
source: 'agent-tool',
|
|
1935
2167
|
args: {
|
|
1936
2168
|
self: parsed.command.from,
|
|
1937
|
-
targetId
|
|
1938
|
-
peer:
|
|
1939
|
-
peerKey:
|
|
2169
|
+
targetId,
|
|
2170
|
+
peer: targetId,
|
|
2171
|
+
peerKey: targetPeerKey,
|
|
2172
|
+
...(isSelectorHistory ? {
|
|
2173
|
+
channelKey: parsed.command.channelKey,
|
|
2174
|
+
peerId: parsed.command.peerId,
|
|
2175
|
+
selectorPeerKey: targetPeerKey,
|
|
2176
|
+
triggerSessionId: session.id,
|
|
2177
|
+
} : {}),
|
|
1940
2178
|
...(parsed.command.session ? { session: parsed.command.session } : {}),
|
|
1941
2179
|
...(parsed.command.limit !== undefined ? { limit: parsed.command.limit } : {}),
|
|
1942
2180
|
...(parsed.command.before !== undefined ? { before: parsed.command.before } : {}),
|
|
@@ -1949,6 +2187,9 @@ export class CommandHandler {
|
|
|
1949
2187
|
argvHash: hashArgv(parsed.command.canonicalArgv),
|
|
1950
2188
|
taskId: grant.taskId,
|
|
1951
2189
|
messageId: grant.messageId,
|
|
2190
|
+
sessionId,
|
|
2191
|
+
agentAid: grant.selfAid,
|
|
2192
|
+
toolName: 'ec.msg',
|
|
1952
2193
|
},
|
|
1953
2194
|
});
|
|
1954
2195
|
if (!decision.allow)
|
|
@@ -1984,7 +2225,7 @@ export class CommandHandler {
|
|
|
1984
2225
|
if (sessionSelfAid && sessionSelfAid !== actorAid) {
|
|
1985
2226
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1986
2227
|
}
|
|
1987
|
-
if (grant.channelType !== 'aun' || grant.chatType !== 'group') {
|
|
2228
|
+
if (!hasTrustedFullAccessDelegation(grant) && (grant.channelType !== 'aun' || grant.chatType !== 'group')) {
|
|
1988
2229
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs operations require the current AUN group relation' };
|
|
1989
2230
|
}
|
|
1990
2231
|
let currentGroup;
|
|
@@ -1994,10 +2235,13 @@ export class CommandHandler {
|
|
|
1994
2235
|
catch {
|
|
1995
2236
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
|
|
1996
2237
|
}
|
|
1997
|
-
if ((
|
|
2238
|
+
if (!hasTrustedFullAccessDelegation(grant)
|
|
2239
|
+
&& ((parsed.command.actorAid && parsed.command.actorAid !== actorAid) || parsed.command.groupId !== currentGroup)) {
|
|
1998
2240
|
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN group can be targeted' };
|
|
1999
2241
|
}
|
|
2000
|
-
parsed.command.actorAid =
|
|
2242
|
+
parsed.command.actorAid = hasTrustedFullAccessDelegation(grant)
|
|
2243
|
+
? (parsed.command.actorAid ?? actorAid)
|
|
2244
|
+
: actorAid;
|
|
2001
2245
|
const subject = buildAuthSubject({
|
|
2002
2246
|
selfAid: grant.selfAid,
|
|
2003
2247
|
actorId: grant.actorId,
|
|
@@ -2007,6 +2251,7 @@ export class CommandHandler {
|
|
|
2007
2251
|
chatType: grant.chatType,
|
|
2008
2252
|
conversationId: currentGroup,
|
|
2009
2253
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
2254
|
+
...trustedExecutionIdentityInput(grant),
|
|
2010
2255
|
});
|
|
2011
2256
|
const intentArgs = {
|
|
2012
2257
|
self: actorAid,
|
|
@@ -2139,6 +2384,7 @@ export class CommandHandler {
|
|
|
2139
2384
|
chatType: grant.chatType,
|
|
2140
2385
|
conversationId,
|
|
2141
2386
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
2387
|
+
...trustedExecutionIdentityInput(grant),
|
|
2142
2388
|
});
|
|
2143
2389
|
const ctlIntent = this.resolveCtlIntent(cmd, grantSelfAid, grant.peerKey);
|
|
2144
2390
|
if (!ctlIntent)
|