evolcore 0.0.12 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/bin/ec-safe-output.js +26 -7
- package/dist/agents/claude-runner.js +53 -7
- package/dist/agents/codex-app-server-client.js +53 -5
- package/dist/agents/codex-runner.js +104 -59
- package/dist/agents/ecagent-runner.js +4 -1
- package/dist/aun/aid/control-aid.js +36 -13
- package/dist/aun/aid/store.js +13 -7
- package/dist/aun/group-fs-download.js +56 -0
- package/dist/aun/group-identity.js +1 -2
- package/dist/aun/msg/group.js +11 -33
- package/dist/aun/msg/index.js +1 -2
- package/dist/aun/msg/managed-operation.js +116 -55
- package/dist/aun/msg/upload.js +16 -1
- package/dist/aun/outbox.js +31 -2
- package/dist/aun/rpc/client.js +66 -0
- package/dist/aun/rpc/index.js +1 -2
- package/dist/aun/storage/{upload.js → client.js} +11 -2
- package/dist/aun/storage/index.js +1 -2
- package/dist/channels/aun.js +371 -136
- package/dist/cli/agent.js +9 -96
- package/dist/cli/cli-argv.js +10 -0
- package/dist/cli/config.js +9 -17
- package/dist/cli/ctl-command.js +1 -1
- package/dist/cli/daemon-commands.js +39 -24
- package/dist/cli/fs-command.js +19 -10
- package/dist/cli/init.js +77 -8
- package/dist/cli/queue-command.js +42 -0
- package/dist/cli/restart-monitor.js +3 -14
- package/dist/cli/trigger-command.js +7 -0
- package/dist/config/access-policy.js +1 -2
- package/dist/config/builtin-roles.js +5 -0
- package/dist/config/config-manager.js +21 -1
- package/dist/config/peer-role-resolver.js +6 -2
- package/dist/config/role-migration-startup.js +53 -0
- package/dist/config/role-store.js +1 -1
- package/dist/core/auth/agent-delegation.js +1 -1
- package/dist/core/auth/auth-gateway.js +29 -1
- package/dist/core/auth/authorization-audit.js +15 -0
- package/dist/core/auth/operation-authorizer.js +1 -0
- package/dist/core/auth/operation-catalog.js +9 -0
- package/dist/core/bootstrap-messages.js +9 -2
- package/dist/core/bootstrap-service.js +1 -0
- package/dist/core/command/command-handler.js +198 -51
- package/dist/core/command/menu-catalog.js +4 -0
- package/dist/core/command/menu-handler.js +85 -18
- package/dist/core/command/menu-protocol.js +23 -2
- package/dist/core/command/slash-handler.js +58 -10
- package/dist/core/event-catalog.js +6 -0
- package/dist/core/handoff/runtime.js +12 -1
- package/dist/core/handoff/store.js +5 -1
- package/dist/core/interaction-router.js +10 -0
- package/dist/core/message/im-renderer.js +19 -0
- package/dist/core/message/message-bridge.js +63 -11
- package/dist/core/message/message-log.js +2 -0
- package/dist/core/message/message-queue.js +267 -39
- package/dist/core/message/message-utils.js +81 -4
- package/dist/core/message/response-engine.js +107 -41
- package/dist/core/message/stream-debouncer.js +10 -2
- package/dist/core/permission/approval-gateway.js +1 -0
- package/dist/core/permission/ec-command-parser.js +93 -14
- package/dist/core/permission/index.js +1 -1
- package/dist/core/permission/mode.js +15 -0
- package/dist/core/permission/readonly-shell-query.js +148 -8
- package/dist/core/permission/sandbox-runtime.js +21 -0
- package/dist/core/permission/tool-policy.js +249 -27
- package/dist/core/protected-paths.js +107 -2
- package/dist/core/session/session-manager.js +21 -2
- package/dist/core/session/session-mapper.js +20 -1
- package/dist/eck/detect.js +18 -1
- package/dist/eck/group-rules-sync.js +15 -44
- package/dist/index.js +161 -86
- package/dist/ipc.js +16 -3
- package/dist/paths.js +3 -0
- package/dist/trigger/feedback.js +64 -11
- package/dist/trigger/manager.js +21 -1
- package/dist/trigger/parser.js +14 -2
- package/dist/trigger/scheduler.js +1 -0
- package/dist/trigger/validation.js +67 -6
- package/dist/utils/ecweb-supervisor.js +332 -0
- package/dist/utils/error-utils.js +10 -0
- package/kits/docs/evolcore/trigger.md +17 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
- package/kits/schemas/role-config.schema.1.json +1 -0
- package/kits/templates/roles/admin.json +5 -0
- package/kits/templates/roles/member.json +7 -1
- package/kits/templates/roles/visitor.json +2 -0
- package/package.json +1 -1
- package/dist/aun/msg/payload-type.js +0 -27
- package/dist/aun/rpc/caller.js +0 -42
- package/dist/aun/rpc/connection.js +0 -25
- package/dist/aun/storage/manage.js +0 -10
- package/dist/config/access-policy-domain.js +0 -18
- package/dist/config/config-batch-get.js +0 -11
- package/dist/config/owner-policy.js +0 -4
- package/dist/config/role-config-v4-startup.js +0 -32
- package/dist/config/role-config-v5-startup.js +0 -27
- package/dist/core/auth/trigger-authorization.js +0 -15
- package/dist/core/interaction-registration.js +0 -10
- package/dist/core/permission/execution-sandbox.js +0 -16
- package/dist/core/relation/peer-key.js +0 -1
- package/dist/core/session/session-key.js +0 -24
- package/dist/eck/baseagent-caps.js +0 -18
- package/dist/eck/rules-loader.js +0 -28
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseagentRunnerUnavailableError } from '../../agents/runner-types.js';
|
|
2
2
|
import { renderCommandCardAsText } from '../interaction-router.js';
|
|
3
|
-
import { buildEnvelope, sendInteractionPayload } from '../message/message-utils.js';
|
|
3
|
+
import { buildEnvelope, deliveryTargetFromSession, replyContextFromSession, sendInteractionPayload } from '../message/message-utils.js';
|
|
4
4
|
import { resolvePaths, getPackageRoot } from '../../paths.js';
|
|
5
5
|
import { loadDaemonConfig } from '../../config-store.js';
|
|
6
6
|
import { logger } from '../../utils/logger.js';
|
|
@@ -18,6 +18,7 @@ import { displaySessionTitle } from '../session/session-title.js';
|
|
|
18
18
|
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
|
+
import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
|
|
21
22
|
import { hashArgv } from '../auth/authorization-audit.js';
|
|
22
23
|
import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
|
|
23
24
|
import { executeManagedAidOperation, ManagedAidOperationError, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
|
|
@@ -132,6 +133,15 @@ export class CommandHandler {
|
|
|
132
133
|
.filter(key => key.startsWith(prefix))
|
|
133
134
|
.map(key => key.slice(prefix.length));
|
|
134
135
|
}
|
|
136
|
+
/** Return the runner-owned temporary root for a managed session, if any. */
|
|
137
|
+
getManagedSessionTempDir(session) {
|
|
138
|
+
try {
|
|
139
|
+
return this.getAgent(session.channel, session.baseagent).getManagedTempDir?.(session.id);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
135
145
|
formatBaseagentUnavailable(error) {
|
|
136
146
|
const available = error.availableBaseagents.length ? error.availableBaseagents.join(', ') : '(none)';
|
|
137
147
|
return `❌ 当前会话绑定的 baseagent 不可用: ${error.baseagent}\nAgent: ${error.evolagentName}\n可用: ${available}\n请使用 /baseagent 切换到可用后端。`;
|
|
@@ -321,6 +331,7 @@ export class CommandHandler {
|
|
|
321
331
|
channelId: definition.origin.channelId,
|
|
322
332
|
session: definition.origin.session,
|
|
323
333
|
threadId: definition.origin.threadId,
|
|
334
|
+
chatType: definition.origin.delivery?.chatType,
|
|
324
335
|
};
|
|
325
336
|
}
|
|
326
337
|
canAccessTriggerDefinition(definition, peerId, channel, isAdmin) {
|
|
@@ -403,9 +414,6 @@ export class CommandHandler {
|
|
|
403
414
|
}
|
|
404
415
|
const targetChannelType = this.resolveChannelType(feedbackChannel);
|
|
405
416
|
const feedbackChannelId = parsed.targetChannelId ?? peerId;
|
|
406
|
-
if (targetChannelType === 'aun' && parsed.targetChannelId && !parsed.targetChannelId.includes('.')) {
|
|
407
|
-
return { error: `AUN 渠道的 --target-channel-id 必须是 AID 格式(如 user.agentid.pub),收到:"${parsed.targetChannelId}"` };
|
|
408
|
-
}
|
|
409
417
|
const schedulerAid = parsed.agentId
|
|
410
418
|
?? this.getOwningAgent(feedbackChannel)?.aid
|
|
411
419
|
?? tryParseChannelKey(feedbackChannel)?.selfAID
|
|
@@ -414,11 +422,22 @@ export class CommandHandler {
|
|
|
414
422
|
if (!schedulerAid || !scheduler) {
|
|
415
423
|
return { error: `目标 agent 不存在或未就绪:${schedulerAid ?? feedbackChannel}` };
|
|
416
424
|
}
|
|
425
|
+
if (targetChannelType === 'aun' && parsed.targetChannelId && !parsed.targetChatType) {
|
|
426
|
+
return { error: 'AUN 目标必须显式指定 --target-chat-type private 或 group' };
|
|
427
|
+
}
|
|
428
|
+
const targetDelivery = targetChannelType === 'aun'
|
|
429
|
+
? parsed.targetChatType === 'group'
|
|
430
|
+
? { chatType: 'group', groupId: feedbackChannelId }
|
|
431
|
+
: parsed.targetChatType === 'private'
|
|
432
|
+
? { chatType: 'private' }
|
|
433
|
+
: { chatType: 'private' }
|
|
434
|
+
: undefined;
|
|
417
435
|
const target = {
|
|
418
436
|
channelKey: feedbackChannel,
|
|
419
437
|
channelId: feedbackChannelId,
|
|
420
438
|
session: parsed.targetSession,
|
|
421
439
|
threadId: parsed.targetSession === 'thread' ? parsed.targetThreadId : undefined,
|
|
440
|
+
chatType: targetDelivery?.chatType,
|
|
422
441
|
};
|
|
423
442
|
if (target.session === 'thread') {
|
|
424
443
|
const adapter = this.adapters.get(feedbackChannel);
|
|
@@ -439,6 +458,7 @@ export class CommandHandler {
|
|
|
439
458
|
threadId: !originIsGroup ? threadId : undefined,
|
|
440
459
|
peerId,
|
|
441
460
|
sessionKey: activeSession?.sessionKey,
|
|
461
|
+
delivery: { chatType: 'private' },
|
|
442
462
|
};
|
|
443
463
|
if (!isAdmin && parsed.feedbackStrategy === 'target' && !sameFeedbackTarget(target, origin)) {
|
|
444
464
|
return { error: '无权限:非 admin 只能把 trigger 投递到来源会话' };
|
|
@@ -618,7 +638,7 @@ export class CommandHandler {
|
|
|
618
638
|
}
|
|
619
639
|
/** 从 session 提取渠道预构建的回复上下文 */
|
|
620
640
|
getReplyContext(session) {
|
|
621
|
-
return session
|
|
641
|
+
return replyContextFromSession(session);
|
|
622
642
|
}
|
|
623
643
|
/**
|
|
624
644
|
* 发送 CommandCard 卡片。卡片成功返回 null(调用方直接 return),失败返回降级文本。
|
|
@@ -1426,7 +1446,7 @@ export class CommandHandler {
|
|
|
1426
1446
|
* - taskId/chatmode:从 processing_state 和 effective chatmode 注入
|
|
1427
1447
|
*/
|
|
1428
1448
|
buildCtlReplyContext(session) {
|
|
1429
|
-
const ctx = {};
|
|
1449
|
+
const ctx = { delivery: deliveryTargetFromSession(session) };
|
|
1430
1450
|
const meta = session.metadata;
|
|
1431
1451
|
if (meta?.replyContext?.threadId)
|
|
1432
1452
|
ctx.threadId = meta.replyContext.threadId;
|
|
@@ -1454,7 +1474,7 @@ export class CommandHandler {
|
|
|
1454
1474
|
if (encrypted != null)
|
|
1455
1475
|
ctx.metadata.encrypted = encrypted;
|
|
1456
1476
|
}
|
|
1457
|
-
return
|
|
1477
|
+
return ctx;
|
|
1458
1478
|
}
|
|
1459
1479
|
resolveEffectiveChatmodeForSession(session) {
|
|
1460
1480
|
const peerType = session.metadata?.peerType;
|
|
@@ -1509,35 +1529,51 @@ export class CommandHandler {
|
|
|
1509
1529
|
conversationId,
|
|
1510
1530
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
1511
1531
|
});
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
subject,
|
|
1520
|
-
intent: {
|
|
1521
|
-
operation: resolved.command.operationId,
|
|
1522
|
-
scope: resolved.command.commandScope,
|
|
1532
|
+
const executeOne = (fieldArgv) => {
|
|
1533
|
+
const resolved = resolveConfigCommand(fieldArgv, {
|
|
1534
|
+
defaultRelation: { self: grant.selfAid, peerKey: grant.peerKey },
|
|
1535
|
+
});
|
|
1536
|
+
if (!resolved.ok)
|
|
1537
|
+
return { ok: false, code: resolved.code, error: resolved.reason };
|
|
1538
|
+
const decision = authorizeOperation({
|
|
1523
1539
|
source: 'agent-tool',
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1540
|
+
subject,
|
|
1541
|
+
intent: {
|
|
1542
|
+
operation: resolved.command.operationId,
|
|
1543
|
+
scope: resolved.command.commandScope,
|
|
1544
|
+
source: 'agent-tool',
|
|
1545
|
+
args: {
|
|
1546
|
+
...(resolved.command.self ? { self: resolved.command.self } : {}),
|
|
1547
|
+
...(resolved.command.peerKey ? { peer: resolved.command.peerKey, peerKey: resolved.command.peerKey } : {}),
|
|
1548
|
+
...('field' in resolved.command && resolved.command.field ? { field: resolved.command.field } : {}),
|
|
1549
|
+
},
|
|
1550
|
+
dangerous: resolved.command.dangerous,
|
|
1528
1551
|
},
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
const
|
|
1552
|
+
resolvedConfigCommand: resolved.command,
|
|
1553
|
+
auditMetadata: {
|
|
1554
|
+
argvHash: hashArgv(resolved.command.canonicalArgv),
|
|
1555
|
+
taskId: grant.taskId,
|
|
1556
|
+
messageId: grant.messageId,
|
|
1557
|
+
},
|
|
1558
|
+
});
|
|
1559
|
+
if (!decision.allow)
|
|
1560
|
+
return { ok: false, code: decision.code, error: decision.reason };
|
|
1561
|
+
return executeResolvedConfigCommand(resolved.command, { role: subject.role });
|
|
1562
|
+
};
|
|
1563
|
+
const batch = splitConfigBatchGetArgv(argv);
|
|
1564
|
+
if (batch && batch.fields.length > 1) {
|
|
1565
|
+
const results = batch.fields.map(field => {
|
|
1566
|
+
const result = executeOne(['config', 'get', field, ...batch.rest]);
|
|
1567
|
+
if (!result.ok)
|
|
1568
|
+
return { ok: false, field, code: result.code, error: result.error };
|
|
1569
|
+
if (result.subcommand !== 'get') {
|
|
1570
|
+
return { ok: false, field, code: 'UNEXPECTED_RESULT', error: `config get ${field} produced a non-get result` };
|
|
1571
|
+
}
|
|
1572
|
+
return result;
|
|
1573
|
+
});
|
|
1574
|
+
return { ok: true, result: { ok: true, subcommand: 'batch-get', results } };
|
|
1575
|
+
}
|
|
1576
|
+
const result = executeOne(argv);
|
|
1541
1577
|
return result.ok
|
|
1542
1578
|
? { ok: true, result }
|
|
1543
1579
|
: { ok: false, code: result.code, error: result.error, ...(result.data !== undefined ? { data: result.data } : {}) };
|
|
@@ -1747,23 +1783,41 @@ export class CommandHandler {
|
|
|
1747
1783
|
const filePath = String(parsed.command.filePath || '');
|
|
1748
1784
|
const projectPath = path.resolve(session.projectPath);
|
|
1749
1785
|
const resolvedPath = path.resolve(projectPath, filePath);
|
|
1750
|
-
|
|
1751
|
-
|
|
1786
|
+
const managedTempPath = this.getManagedSessionTempDir(session);
|
|
1787
|
+
const allowedPaths = [projectPath, managedTempPath].filter((value) => !!value);
|
|
1788
|
+
if (!filePath || !allowedPaths.some(root => isSameOrDescendant(resolvedPath, root))) {
|
|
1789
|
+
return { ok: false, code: 'INVALID_RULES_PATH', error: 'Rules file must be inside the current session project or managed TMPDIR' };
|
|
1752
1790
|
}
|
|
1753
1791
|
if (isHClassPath(resolvedPath) || isLClassPath(resolvedPath)) {
|
|
1754
1792
|
return { ok: false, code: 'PROTECTED_RULES_PATH', error: 'Rules file cannot be read from a protected EvolCore path' };
|
|
1755
1793
|
}
|
|
1794
|
+
let rulesFd;
|
|
1756
1795
|
try {
|
|
1757
1796
|
const realProjectPath = fs.realpathSync(projectPath);
|
|
1758
1797
|
const realPath = fs.realpathSync(resolvedPath);
|
|
1759
|
-
|
|
1760
|
-
|
|
1798
|
+
let realManagedTempPath;
|
|
1799
|
+
try {
|
|
1800
|
+
realManagedTempPath = managedTempPath ? fs.realpathSync(managedTempPath) : undefined;
|
|
1801
|
+
}
|
|
1802
|
+
catch {
|
|
1803
|
+
// The session may have closed and removed its temporary directory.
|
|
1804
|
+
}
|
|
1805
|
+
const realAllowedPaths = [realProjectPath, realManagedTempPath].filter((value) => !!value);
|
|
1806
|
+
if (!realAllowedPaths.some(root => isSameOrDescendant(realPath, root))) {
|
|
1807
|
+
return { ok: false, code: 'INVALID_RULES_PATH', error: 'Rules file resolves outside the current session project or managed TMPDIR' };
|
|
1761
1808
|
}
|
|
1762
|
-
|
|
1809
|
+
if (isHClassPath(realPath) || isLClassPath(realPath)) {
|
|
1810
|
+
return { ok: false, code: 'PROTECTED_RULES_PATH', error: 'Rules file cannot be read from a protected EvolCore path' };
|
|
1811
|
+
}
|
|
1812
|
+
rulesFd = fs.openSync(realPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
1813
|
+
const stat = fs.fstatSync(rulesFd);
|
|
1763
1814
|
if (!stat.isFile() || stat.size < 1 || stat.size > 4 * 1024) {
|
|
1764
1815
|
return { ok: false, code: 'INVALID_RULES_CONTENT', error: 'Rules file must be a regular file from 1 to 4096 bytes' };
|
|
1765
1816
|
}
|
|
1766
|
-
|
|
1817
|
+
if (realManagedTempPath && isSameOrDescendant(realPath, realManagedTempPath) && stat.nlink > 1) {
|
|
1818
|
+
return { ok: false, code: 'INVALID_RULES_PATH', error: 'Rules file cannot be a hard link' };
|
|
1819
|
+
}
|
|
1820
|
+
rulesContent = new TextDecoder('utf-8', { fatal: true }).decode(fs.readFileSync(rulesFd));
|
|
1767
1821
|
}
|
|
1768
1822
|
catch (error) {
|
|
1769
1823
|
return {
|
|
@@ -1772,6 +1826,10 @@ export class CommandHandler {
|
|
|
1772
1826
|
error: error instanceof Error ? error.message : String(error),
|
|
1773
1827
|
};
|
|
1774
1828
|
}
|
|
1829
|
+
finally {
|
|
1830
|
+
if (rulesFd !== undefined)
|
|
1831
|
+
fs.closeSync(rulesFd);
|
|
1832
|
+
}
|
|
1775
1833
|
}
|
|
1776
1834
|
try {
|
|
1777
1835
|
return { ok: true, result: await executeManagedGroupOperation(parsed.command, rulesContent) };
|
|
@@ -1860,7 +1918,7 @@ export class CommandHandler {
|
|
|
1860
1918
|
};
|
|
1861
1919
|
}
|
|
1862
1920
|
}
|
|
1863
|
-
/** Agent-managed current-group filesystem
|
|
1921
|
+
/** Agent-managed current-group filesystem entrypoint. */
|
|
1864
1922
|
async handleFsOperation(argv, sessionId, delegationToken, delegationCommandHash) {
|
|
1865
1923
|
if (!this.agentDelegationRegistry) {
|
|
1866
1924
|
return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
|
|
@@ -1881,7 +1939,7 @@ export class CommandHandler {
|
|
|
1881
1939
|
return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
|
|
1882
1940
|
}
|
|
1883
1941
|
if (grant.channelType !== 'aun' || grant.chatType !== 'group') {
|
|
1884
|
-
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs
|
|
1942
|
+
return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs operations require the current AUN group relation' };
|
|
1885
1943
|
}
|
|
1886
1944
|
let currentGroup;
|
|
1887
1945
|
try {
|
|
@@ -1904,6 +1962,19 @@ export class CommandHandler {
|
|
|
1904
1962
|
conversationId: currentGroup,
|
|
1905
1963
|
processOwners: loadDaemonConfig().owners ?? [],
|
|
1906
1964
|
});
|
|
1965
|
+
const intentArgs = {
|
|
1966
|
+
self: actorAid,
|
|
1967
|
+
targetId: currentGroup,
|
|
1968
|
+
peer: currentGroup,
|
|
1969
|
+
peerKey: formatPeerKey('aun', currentGroup),
|
|
1970
|
+
path: parsed.command.remotePath,
|
|
1971
|
+
targetPath: parsed.command.remotePath,
|
|
1972
|
+
...(parsed.command.remoteSourcePath ? { sourcePath: parsed.command.remoteSourcePath } : {}),
|
|
1973
|
+
...(parsed.command.localPath ? { localPath: parsed.command.localPath } : {}),
|
|
1974
|
+
...(parsed.command.recursive !== undefined ? { recursive: parsed.command.recursive } : {}),
|
|
1975
|
+
...(parsed.command.parents !== undefined ? { parents: parsed.command.parents } : {}),
|
|
1976
|
+
...(parsed.command.overwrite !== undefined ? { overwrite: parsed.command.overwrite } : {}),
|
|
1977
|
+
};
|
|
1907
1978
|
const decision = authorizeOperation({
|
|
1908
1979
|
source: 'agent-tool',
|
|
1909
1980
|
subject,
|
|
@@ -1911,17 +1982,60 @@ export class CommandHandler {
|
|
|
1911
1982
|
operation: parsed.command.operationId,
|
|
1912
1983
|
scope: 'relation',
|
|
1913
1984
|
source: 'agent-tool',
|
|
1914
|
-
args:
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
},
|
|
1985
|
+
args: intentArgs,
|
|
1986
|
+
},
|
|
1987
|
+
auditMetadata: {
|
|
1988
|
+
argvHash: hashArgv(parsed.command.canonicalArgv), taskId: grant.taskId, messageId: grant.messageId,
|
|
1989
|
+
sessionId, agentAid: actorAid, toolName: 'ec.fs',
|
|
1920
1990
|
},
|
|
1921
|
-
auditMetadata: { argvHash: hashArgv(parsed.command.canonicalArgv), taskId: grant.taskId, messageId: grant.messageId },
|
|
1922
1991
|
});
|
|
1923
1992
|
if (!decision.allow)
|
|
1924
1993
|
return { ok: false, code: decision.code, error: decision.reason };
|
|
1994
|
+
if (parsed.command.kind === 'upload') {
|
|
1995
|
+
const localPath = String(parsed.command.localPath || '').trim();
|
|
1996
|
+
const projectPath = path.resolve(session.projectPath);
|
|
1997
|
+
if (!localPath)
|
|
1998
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: 'Managed upload source is required' };
|
|
1999
|
+
const resolvedPath = path.resolve(projectPath, localPath);
|
|
2000
|
+
const managedTempPath = this.getManagedSessionTempDir(session);
|
|
2001
|
+
const allowedRoots = [projectPath, managedTempPath].filter((value) => !!value);
|
|
2002
|
+
if (!allowedRoots.some(root => isSameOrDescendant(resolvedPath, root))) {
|
|
2003
|
+
return { ok: false, code: 'INVALID_LOCAL_PATH', error: 'Upload source must be inside the current session project or managed TMPDIR' };
|
|
2004
|
+
}
|
|
2005
|
+
if (isHClassPath(resolvedPath) || isLClassPath(resolvedPath)) {
|
|
2006
|
+
return { ok: false, code: 'PROTECTED_LOCAL_PATH', error: 'Upload source cannot be a protected EvolCore path' };
|
|
2007
|
+
}
|
|
2008
|
+
try {
|
|
2009
|
+
const realProjectPath = fs.realpathSync(projectPath);
|
|
2010
|
+
const stat = fs.lstatSync(resolvedPath);
|
|
2011
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
2012
|
+
return { ok: false, code: 'INVALID_LOCAL_FILE', error: 'Upload source must be a regular non-symlink file' };
|
|
2013
|
+
}
|
|
2014
|
+
const realPath = fs.realpathSync(resolvedPath);
|
|
2015
|
+
let realManagedTempPath;
|
|
2016
|
+
try {
|
|
2017
|
+
realManagedTempPath = managedTempPath ? fs.realpathSync(managedTempPath) : undefined;
|
|
2018
|
+
}
|
|
2019
|
+
catch {
|
|
2020
|
+
// A removed or invalid session temp root is not an upload root.
|
|
2021
|
+
}
|
|
2022
|
+
const realAllowedRoots = [realProjectPath, realManagedTempPath].filter((value) => !!value);
|
|
2023
|
+
if (!realAllowedRoots.some(root => isSameOrDescendant(realPath, root))
|
|
2024
|
+
|| isHClassPath(realPath) || isLClassPath(realPath)) {
|
|
2025
|
+
return { ok: false, code: 'PROTECTED_LOCAL_PATH', error: 'Upload source resolves outside the allowed project or managed TMPDIR path' };
|
|
2026
|
+
}
|
|
2027
|
+
if (realManagedTempPath && isSameOrDescendant(realPath, realManagedTempPath) && stat.nlink > 1) {
|
|
2028
|
+
return { ok: false, code: 'INVALID_LOCAL_PATH', error: 'Upload source cannot be a hard link inside managed TMPDIR' };
|
|
2029
|
+
}
|
|
2030
|
+
if (stat.size > 16 * 1024 * 1024) {
|
|
2031
|
+
return { ok: false, code: 'LOCAL_FILE_TOO_LARGE', error: 'Managed upload is limited to 16 MiB' };
|
|
2032
|
+
}
|
|
2033
|
+
parsed.command.localPath = realPath;
|
|
2034
|
+
}
|
|
2035
|
+
catch (error) {
|
|
2036
|
+
return { ok: false, code: 'INVALID_LOCAL_FILE', error: error instanceof Error ? error.message : String(error) };
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
1925
2039
|
try {
|
|
1926
2040
|
return { ok: true, result: await executeManagedFsOperation(parsed.command) };
|
|
1927
2041
|
}
|
|
@@ -2126,6 +2240,14 @@ export class CommandHandler {
|
|
|
2126
2240
|
const showId = args.includes('--showid');
|
|
2127
2241
|
const formatJson = args.includes('--format json');
|
|
2128
2242
|
const full = args.includes('--full');
|
|
2243
|
+
const after = parseQueueTimeArg(this.extractArg(args, '--after'));
|
|
2244
|
+
const before = parseQueueTimeArg(this.extractArg(args, '--before'));
|
|
2245
|
+
if (this.extractArg(args, '--after') && after === undefined)
|
|
2246
|
+
return { ok: false, error: '❌ --after 必须是 epoch 毫秒或 ISO 时间' };
|
|
2247
|
+
if (this.extractArg(args, '--before') && before === undefined)
|
|
2248
|
+
return { ok: false, error: '❌ --before 必须是 epoch 毫秒或 ISO 时间' };
|
|
2249
|
+
if (after !== undefined && before !== undefined && after >= before)
|
|
2250
|
+
return { ok: false, error: '❌ --after 必须早于 --before' };
|
|
2129
2251
|
// 操作分支
|
|
2130
2252
|
if (args.includes('--clear')) {
|
|
2131
2253
|
const count = this.messageQueue.clearBySession(sessionId);
|
|
@@ -2147,13 +2269,19 @@ export class CommandHandler {
|
|
|
2147
2269
|
: { ok: false, error: `❌ 当前无处理中任务` };
|
|
2148
2270
|
}
|
|
2149
2271
|
// 查询
|
|
2150
|
-
const items = this.messageQueue.getQueueItemsBySession(sessionId);
|
|
2272
|
+
const items = this.messageQueue.getQueueItemsBySession(sessionId, { after, before });
|
|
2151
2273
|
if (formatJson) {
|
|
2152
2274
|
return { ok: true, result: JSON.stringify({ items }, null, 2) };
|
|
2153
2275
|
}
|
|
2154
2276
|
return { ok: true, result: renderQueueItemsCtl(items, showId, full) };
|
|
2155
2277
|
}
|
|
2156
2278
|
}
|
|
2279
|
+
function parseQueueTimeArg(value) {
|
|
2280
|
+
if (!value)
|
|
2281
|
+
return undefined;
|
|
2282
|
+
const parsed = /^\d+$/.test(value) ? Number(value) : Date.parse(value);
|
|
2283
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
2284
|
+
}
|
|
2157
2285
|
/**
|
|
2158
2286
|
* ctl 专用渲染:不显示 session 标识列(因为只有一个 session)
|
|
2159
2287
|
*/
|
|
@@ -2176,14 +2304,33 @@ function renderQueueItemsCtl(items, showId, full) {
|
|
|
2176
2304
|
parts.push(` ${name} `);
|
|
2177
2305
|
}
|
|
2178
2306
|
const content = full ? item.preview.replace('...', '') : item.preview;
|
|
2179
|
-
|
|
2307
|
+
const timing = [
|
|
2308
|
+
item.receivedAt != null ? `recv=${new Date(item.receivedAt).toISOString()}` : undefined,
|
|
2309
|
+
item.enqueuedAt != null ? `enq=${new Date(item.enqueuedAt).toISOString()}` : undefined,
|
|
2310
|
+
item.queueWaitMs != null ? `wait=${formatQueueDuration(item.queueWaitMs)}` : undefined,
|
|
2311
|
+
].filter(Boolean).join(' ');
|
|
2312
|
+
parts.push(`${timing ? `${timing} ` : ''}"${content}"`);
|
|
2180
2313
|
lines.push(parts.join(''));
|
|
2181
2314
|
}
|
|
2182
2315
|
return lines.join('\n');
|
|
2183
2316
|
}
|
|
2317
|
+
function formatQueueDuration(ms) {
|
|
2318
|
+
const sec = Math.max(0, Math.round(ms / 1000));
|
|
2319
|
+
return sec >= 60 ? `${Math.floor(sec / 60)}m${sec % 60}s` : `${sec}s`;
|
|
2320
|
+
}
|
|
2184
2321
|
function sameFeedbackTarget(target, origin) {
|
|
2185
2322
|
if (!origin)
|
|
2186
2323
|
return false;
|
|
2324
|
+
const targetChannelType = tryParseChannelKey(target.channelKey)?.type
|
|
2325
|
+
?? target.channelKey.split('#', 1)[0];
|
|
2326
|
+
const targetChatType = target.chatType
|
|
2327
|
+
?? (targetChannelType === 'aun' ? undefined : 'private');
|
|
2328
|
+
if (!targetChatType)
|
|
2329
|
+
return false;
|
|
2330
|
+
const originDelivery = origin.delivery;
|
|
2331
|
+
const originChatType = originDelivery?.chatType ?? 'private';
|
|
2332
|
+
if (targetChatType !== originChatType)
|
|
2333
|
+
return false;
|
|
2187
2334
|
return target.channelKey === origin.channelKey
|
|
2188
2335
|
&& target.channelId === origin.channelId
|
|
2189
2336
|
&& target.session === origin.session
|
|
@@ -131,6 +131,8 @@ export async function resolveMenuCatalogContext(input) {
|
|
|
131
131
|
const rebuilt = buildAuthSubject({
|
|
132
132
|
selfAid: subject.selfAid,
|
|
133
133
|
actorId: subject.actorId,
|
|
134
|
+
requestId: subject.requestId,
|
|
135
|
+
sessionId: subject.sessionId,
|
|
134
136
|
channel: subject.channel,
|
|
135
137
|
channelType: transport,
|
|
136
138
|
channelId: subject.actorId,
|
|
@@ -191,6 +193,8 @@ export async function resolveMenuCatalogContext(input) {
|
|
|
191
193
|
const rebuilt = buildAuthSubject({
|
|
192
194
|
selfAid: subject.selfAid,
|
|
193
195
|
actorId: subject.actorId,
|
|
196
|
+
requestId: subject.requestId,
|
|
197
|
+
sessionId: subject.sessionId,
|
|
194
198
|
channel: subject.channel,
|
|
195
199
|
channelType: transport,
|
|
196
200
|
channelId: canonicalGroupId,
|
|
@@ -6,7 +6,7 @@ import { hasModelSwitcher } from '../../agents/runner-types.js';
|
|
|
6
6
|
import { getCodexEfforts } from '../../agents/codex-runner.js';
|
|
7
7
|
import { execCodexCliSync, resolveCodexCliPath } from '../../utils/codex-cli.js';
|
|
8
8
|
import { resolvePaths, getPackageRoot, daemonControlDir } from '../../paths.js';
|
|
9
|
-
import { buildEnvelope } from '../message/message-utils.js';
|
|
9
|
+
import { buildEnvelope, isDeliveryTarget, isDeliveryTargetForChannel, replyContextFromSession } from '../message/message-utils.js';
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import os from 'os';
|
|
@@ -28,7 +28,7 @@ import { normalizeCliArgv, parseCliIntent, parseLegacyCliCommand, validateCliArg
|
|
|
28
28
|
import { auditCommandAuthorization, hashArgv } from '../auth/authorization-audit.js';
|
|
29
29
|
import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
30
30
|
import { evaluateRoleOperationCapability } from '../auth/operation-authorizer.js';
|
|
31
|
-
import { splitConfigBatchGetArgv } from '../../
|
|
31
|
+
import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
|
|
32
32
|
import { chatmodeFieldForPeer, resolveChatModeForField } from '../message/peer-mode.js';
|
|
33
33
|
import { PUBLIC_PERMISSION_MODES } from '../permission/mode.js';
|
|
34
34
|
import { resolveRuntimePermissionMode, resolveRuntimeStringField, validateRuntimeStringFieldOverride, } from '../role/runtime-policy.js';
|
|
@@ -36,6 +36,7 @@ import { isManagementRole } from '../../config/builtin-roles.js';
|
|
|
36
36
|
import { SYSTEM_CONTROL_CHANNEL } from '../system-channels.js';
|
|
37
37
|
import { logger } from '../../utils/logger.js';
|
|
38
38
|
import { shouldSuppressRealRestart } from '../../utils/restart-safety.js';
|
|
39
|
+
import { AGENT_DELEGATION_TOKEN_ENV } from '../auth/agent-delegation.js';
|
|
39
40
|
import { evolMenuResponseTransportMetadata, menuFailure, menuCommandForName, menuSuccess, normalizeMenuError, validateConfigWriteScope, validateMenuRequest, } from './menu-protocol.js';
|
|
40
41
|
import { roleMenuAction, roleMenuOperation, roleMenuOptions, roleMenuQuery, roleMenuUpdate, } from './role-menu.js';
|
|
41
42
|
import { handleConnectMenu, connectMenuOperation } from './connect-menu.js';
|
|
@@ -1013,8 +1014,13 @@ function buildRelationIntentArgs(params) {
|
|
|
1013
1014
|
return out;
|
|
1014
1015
|
}
|
|
1015
1016
|
function buildMenuAuthSubject(owner, params) {
|
|
1016
|
-
if (params.subject)
|
|
1017
|
-
return
|
|
1017
|
+
if (params.subject) {
|
|
1018
|
+
return {
|
|
1019
|
+
...params.subject,
|
|
1020
|
+
...(params.requestId && !params.subject.requestId ? { requestId: params.requestId } : {}),
|
|
1021
|
+
...(params.session?.id && !params.subject.sessionId ? { sessionId: params.session.id } : {}),
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1018
1024
|
const agent = owner.getOwningAgent?.(params.channel);
|
|
1019
1025
|
const selfAid = agent?.aid || params.session?.selfAID;
|
|
1020
1026
|
const channelType = owner.resolveChannelType?.(params.channel) || params.session?.channelType;
|
|
@@ -1025,6 +1031,8 @@ function buildMenuAuthSubject(owner, params) {
|
|
|
1025
1031
|
return buildAuthSubject({
|
|
1026
1032
|
selfAid,
|
|
1027
1033
|
actorId: params.userId,
|
|
1034
|
+
requestId: params.requestId,
|
|
1035
|
+
sessionId: params.session?.id,
|
|
1028
1036
|
channel: params.channel,
|
|
1029
1037
|
channelType: channelType || params.channel.split('#')[0],
|
|
1030
1038
|
channelId: params.channelId,
|
|
@@ -1114,7 +1122,7 @@ function buildRoleMenuContext(owner, channel, subject) {
|
|
|
1114
1122
|
channel: contactAdapter.channelName,
|
|
1115
1123
|
channelId: applicantAid,
|
|
1116
1124
|
agentName: owningAgent?.name ?? '<unknown>',
|
|
1117
|
-
replyContext: { metadata: evolMenuResponseTransportMetadata() },
|
|
1125
|
+
replyContext: { delivery: { chatType: 'private' }, metadata: evolMenuResponseTransportMetadata() },
|
|
1118
1126
|
}), {
|
|
1119
1127
|
kind: 'custom',
|
|
1120
1128
|
channelType: 'aun',
|
|
@@ -2710,6 +2718,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
2710
2718
|
const subject = buildMenuAuthSubject(this, {
|
|
2711
2719
|
identity: authIdentity,
|
|
2712
2720
|
session: authSession,
|
|
2721
|
+
requestId,
|
|
2713
2722
|
explicitChatType,
|
|
2714
2723
|
channel,
|
|
2715
2724
|
channelId,
|
|
@@ -2927,6 +2936,10 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
2927
2936
|
if (triggerThread !== undefined && triggerThread !== 'per_run' && triggerThread !== 'by_run' && triggerThread !== 'by_trigger') {
|
|
2928
2937
|
return { error: `无效 triggerThread: ${triggerThread}`, code: 'INVALID_ARGS' };
|
|
2929
2938
|
}
|
|
2939
|
+
const targetChatType = args.targetChatType;
|
|
2940
|
+
if (targetChatType !== undefined && targetChatType !== 'private' && targetChatType !== 'group') {
|
|
2941
|
+
return { error: `无效 targetChatType: ${targetChatType}`, code: 'INVALID_ARGS' };
|
|
2942
|
+
}
|
|
2930
2943
|
const parsed = {
|
|
2931
2944
|
scheduleType: args.scheduleType,
|
|
2932
2945
|
scheduleValue: String(args.scheduleValue ?? ''),
|
|
@@ -2937,6 +2950,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
2937
2950
|
name: args.name,
|
|
2938
2951
|
targetChannel: args.targetChannel,
|
|
2939
2952
|
targetChannelId: args.targetChannelId,
|
|
2953
|
+
targetChatType,
|
|
2940
2954
|
targetThreadId: args.targetThreadId,
|
|
2941
2955
|
agentId: args.agentId,
|
|
2942
2956
|
model: args.model,
|
|
@@ -3313,13 +3327,56 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3313
3327
|
}
|
|
3314
3328
|
if (action === 'restart') {
|
|
3315
3329
|
const suppressRealRestart = shouldSuppressRealRestart();
|
|
3330
|
+
let restartReplyContext;
|
|
3331
|
+
try {
|
|
3332
|
+
restartReplyContext = session
|
|
3333
|
+
? replyContextFromSession(session, transportReplyContext)
|
|
3334
|
+
: transportReplyContext;
|
|
3335
|
+
}
|
|
3336
|
+
catch (error) {
|
|
3337
|
+
const code = error?.code;
|
|
3338
|
+
if (channel.split('#', 1)[0].toLowerCase() !== 'aun'
|
|
3339
|
+
|| (code !== 'AUN_OUTBOUND_ROUTE_REQUIRED' && code !== 'AUN_OUTBOUND_ROUTE_MISMATCH')) {
|
|
3340
|
+
throw error;
|
|
3341
|
+
}
|
|
3342
|
+
return {
|
|
3343
|
+
error: '当前 AUN 会话缺少有效的持久化出站路由,请从可信入站上下文重试',
|
|
3344
|
+
code,
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
const restartDelivery = restartReplyContext?.delivery
|
|
3348
|
+
?? (explicitChatType === 'group'
|
|
3349
|
+
? { chatType: 'group', groupId: channelId }
|
|
3350
|
+
: explicitChatType === 'private'
|
|
3351
|
+
? { chatType: 'private' }
|
|
3352
|
+
: undefined);
|
|
3353
|
+
if (channel.split('#', 1)[0].toLowerCase() === 'aun'
|
|
3354
|
+
&& !isDeliveryTargetForChannel(restartDelivery, channelId)) {
|
|
3355
|
+
return {
|
|
3356
|
+
error: '无法确定有效的重启通知出站路由,请从可信会话上下文重试',
|
|
3357
|
+
code: isDeliveryTarget(restartDelivery)
|
|
3358
|
+
? 'AUN_OUTBOUND_ROUTE_MISMATCH'
|
|
3359
|
+
: 'AUN_OUTBOUND_ROUTE_REQUIRED',
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3316
3362
|
if (!suppressRealRestart) {
|
|
3317
|
-
const restartInfo = {
|
|
3363
|
+
const restartInfo = {
|
|
3364
|
+
channel,
|
|
3365
|
+
channelId,
|
|
3366
|
+
timestamp: Date.now(),
|
|
3367
|
+
...(restartDelivery ? { delivery: restartDelivery } : {}),
|
|
3368
|
+
};
|
|
3318
3369
|
const controlDir = daemonControlDir();
|
|
3319
3370
|
fs.mkdirSync(controlDir, { recursive: true });
|
|
3320
3371
|
fs.writeFileSync(path.join(controlDir, 'restart-pending.json'), JSON.stringify(restartInfo));
|
|
3321
|
-
|
|
3372
|
+
// Use the canonical CLI restart path so the restart itself performs
|
|
3373
|
+
// the version check and automatic upgrades before replacing the daemon.
|
|
3374
|
+
// Do not inherit managed-session credentials: otherwise cmdRestart can
|
|
3375
|
+
// delegate back to this daemon and recursively enqueue another restart.
|
|
3376
|
+
spawnDetachedNode([path.join(getPackageRoot(), 'dist', 'cli', 'index.js'), 'restart'], {
|
|
3322
3377
|
EVOLCORE_HOME: resolvePaths().root,
|
|
3378
|
+
EVOLCORE_SESSION_ID: '',
|
|
3379
|
+
[AGENT_DELEGATION_TOKEN_ENV]: '',
|
|
3323
3380
|
});
|
|
3324
3381
|
}
|
|
3325
3382
|
else {
|
|
@@ -3386,6 +3443,17 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3386
3443
|
? (session?.metadata?.groupId || channelId)
|
|
3387
3444
|
: userId;
|
|
3388
3445
|
const cliPeerKey = cliChannelType && cliPeerKeyId ? formatPeerKey(cliChannelType, cliPeerKeyId) : undefined;
|
|
3446
|
+
const subject = buildMenuAuthSubject(this, {
|
|
3447
|
+
identity,
|
|
3448
|
+
session,
|
|
3449
|
+
requestId,
|
|
3450
|
+
explicitChatType,
|
|
3451
|
+
channel,
|
|
3452
|
+
channelId,
|
|
3453
|
+
userId,
|
|
3454
|
+
fromControlChannel: fromControlChannel ?? false,
|
|
3455
|
+
subject: authSubject,
|
|
3456
|
+
});
|
|
3389
3457
|
if (Array.isArray(args?.argv)) {
|
|
3390
3458
|
argv = withDefaultRelationContext(argv, { self: cliSelfAid, peer: cliPeerKey });
|
|
3391
3459
|
}
|
|
@@ -3404,6 +3472,8 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3404
3472
|
await auditCommandAuthorization({
|
|
3405
3473
|
ts: Date.now(), source: 'menu.cli', operation: parsed.intent.operation,
|
|
3406
3474
|
scope: parsed.intent.scope, dangerous: true, actorId: userId,
|
|
3475
|
+
requestId, sessionId: subject.sessionId, agentAid: subject.selfAid,
|
|
3476
|
+
permissionMode: subject.permissionMode,
|
|
3407
3477
|
channel, channelId, role: identity.role, decision: 'deny', code: 'NOT_ALLOWED',
|
|
3408
3478
|
reason: 'Unrecognized CLI command', taskId: requestId, argvHash: hashArgv(intentArgv),
|
|
3409
3479
|
name: 'cli', action: 'exec', args: { argv: [...requestedArgv] },
|
|
@@ -3412,16 +3482,6 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3412
3482
|
}
|
|
3413
3483
|
parsedCommands.push(parsed);
|
|
3414
3484
|
}
|
|
3415
|
-
const subject = buildMenuAuthSubject(this, {
|
|
3416
|
-
identity,
|
|
3417
|
-
session,
|
|
3418
|
-
explicitChatType,
|
|
3419
|
-
channel,
|
|
3420
|
-
channelId,
|
|
3421
|
-
userId,
|
|
3422
|
-
fromControlChannel: fromControlChannel ?? false,
|
|
3423
|
-
subject: authSubject,
|
|
3424
|
-
});
|
|
3425
3485
|
const selfAid = subject.selfAid;
|
|
3426
3486
|
const peerKey = subject.peerKey;
|
|
3427
3487
|
const isDaemonOwner = subject.isDaemonOwner;
|
|
@@ -3446,6 +3506,10 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3446
3506
|
allowExplicitRelationTarget: true,
|
|
3447
3507
|
auditAllowed: false,
|
|
3448
3508
|
auditMetadata: {
|
|
3509
|
+
requestId,
|
|
3510
|
+
sessionId: subject.sessionId,
|
|
3511
|
+
agentAid: subject.selfAid,
|
|
3512
|
+
permissionMode: subject.permissionMode,
|
|
3449
3513
|
taskId: requestId,
|
|
3450
3514
|
argvHash: hashArgv(authArgv),
|
|
3451
3515
|
name: 'cli',
|
|
@@ -3466,6 +3530,8 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3466
3530
|
ts: Date.now(), source: 'menu.cli', operation: parsed.intent.operation,
|
|
3467
3531
|
scope: parsed.intent.scope, dangerous: parsed.intent.dangerous ?? false,
|
|
3468
3532
|
actorId: userId, selfAid, peerKey,
|
|
3533
|
+
requestId, sessionId: subject.sessionId, agentAid: selfAid,
|
|
3534
|
+
permissionMode: subject.permissionMode,
|
|
3469
3535
|
channel, channelId, role: identity.role, isDaemonOwner,
|
|
3470
3536
|
fromControlChannel: fromControlChannel ?? false, decision: 'allow',
|
|
3471
3537
|
matchedRule: decision.command?.matchedRule, taskId: requestId, argvHash: hashArgv(argv),
|
|
@@ -3509,7 +3575,7 @@ export async function execMenuAction(cmd, action, args, channel, channelId, user
|
|
|
3509
3575
|
return { error: '通道不存在', code: 'EXEC_FAILED' };
|
|
3510
3576
|
if (!adapter.capabilities?.file)
|
|
3511
3577
|
return { error: '通道不支持文件发送', code: 'NOT_SUPPORTED' };
|
|
3512
|
-
const sessionReplyContext = session ? this.getReplyContext(session) : undefined;
|
|
3578
|
+
const sessionReplyContext = session ? replyContextFromSession(session, this.getReplyContext(session)) : undefined;
|
|
3513
3579
|
const replyCtx = transportReplyContext
|
|
3514
3580
|
? {
|
|
3515
3581
|
...(sessionReplyContext ?? {}),
|
|
@@ -3593,6 +3659,7 @@ async function execMenuForSystemControl(payload, context) {
|
|
|
3593
3659
|
const trustedSubject = buildAuthSubject({
|
|
3594
3660
|
selfAid: targetAid,
|
|
3595
3661
|
actorId: context.actorAid,
|
|
3662
|
+
requestId: id !== undefined && id !== null && String(id) ? String(id) : undefined,
|
|
3596
3663
|
channel: targetChannel,
|
|
3597
3664
|
channelType: 'aun',
|
|
3598
3665
|
channelId: context.actorAid || SYSTEM_CONTROL_CHANNEL,
|