evolcore 0.0.31 → 0.0.33

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.
Files changed (179) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/bin/install-codex-managed-hooks.mjs +124 -37
  3. package/dist/agents/claude-runner.js +29 -2
  4. package/dist/agents/codex/app-server-client.js +3 -0
  5. package/dist/agents/codex/diagnostics.js +76 -44
  6. package/dist/agents/codex-runner.js +51 -24
  7. package/dist/agents/ecagent-runner.js +9 -13
  8. package/dist/agents/gemini-runner.js +4 -0
  9. package/dist/aun/aid/store.js +0 -81
  10. package/dist/aun/group/fs-download.js +1 -10
  11. package/dist/aun/group/operations.js +0 -1
  12. package/dist/aun/msg/history.js +1 -0
  13. package/dist/aun/msg/managed-operation.js +130 -32
  14. package/dist/aun/msg/p2p.js +0 -1
  15. package/dist/aun/outbox.js +50 -1
  16. package/dist/aun/service-proxy.js +12 -18
  17. package/dist/aun/storage/index.js +29 -0
  18. package/dist/channels/aun.js +38 -137
  19. package/dist/channels/daemon.js +257 -2
  20. package/dist/cli/agent-command.js +64 -31
  21. package/dist/cli/agent-create.js +43 -24
  22. package/dist/cli/agent.js +16 -70
  23. package/dist/cli/aun-commands.js +512 -32
  24. package/dist/cli/contact.js +8 -2
  25. package/dist/cli/ctl-command.js +1 -2
  26. package/dist/cli/daemon-commands.js +82 -13
  27. package/dist/cli/debug-mode.js +154 -0
  28. package/dist/cli/diag.js +2 -2
  29. package/dist/cli/handoff-command.js +18 -5
  30. package/dist/cli/index.js +81 -64
  31. package/dist/cli/init.js +2 -2
  32. package/dist/cli/managed-command-guard.js +5 -0
  33. package/dist/cli/memory-command.js +93 -12
  34. package/dist/cli/model.js +14 -138
  35. package/dist/cli/queue-command.js +51 -0
  36. package/dist/cli/response.js +5 -54
  37. package/dist/cli/session-command.js +194 -0
  38. package/dist/cli/skill-command.js +85 -3
  39. package/dist/cli/stats.js +10 -4
  40. package/dist/cli/task-command.js +1 -0
  41. package/dist/cli/task-context.js +16 -0
  42. package/dist/cli/thread-command.js +620 -0
  43. package/dist/cli/trigger-command.js +76 -50
  44. package/dist/config/builtin-roles.js +9 -7
  45. package/dist/config/config-field-policy.js +2 -3
  46. package/dist/config/config-manager.js +7 -2
  47. package/dist/config/resolved-config-op.js +3 -0
  48. package/dist/config-store.js +15 -0
  49. package/dist/core/agent-ipc-registration.js +65 -22
  50. package/dist/core/auth/agent-delegation.js +42 -2
  51. package/dist/core/auth/debug-context.js +139 -0
  52. package/dist/core/auth/delegation-authorization.js +1 -0
  53. package/dist/core/auth/operation-authorizer.js +24 -1
  54. package/dist/core/auth/operation-catalog.js +44 -3
  55. package/dist/core/background-session/session-cleanup.js +212 -0
  56. package/dist/core/background-session/types.js +10 -0
  57. package/dist/core/bootstrap-messages.js +1 -1
  58. package/dist/core/bootstrap-service.js +6 -3
  59. package/dist/core/command/command-handler.js +224 -45
  60. package/dist/core/command/diag-service.js +5 -1
  61. package/dist/core/command/memory-slash.js +6 -2
  62. package/dist/core/command/menu-catalog.js +48 -1
  63. package/dist/core/command/menu-handler.js +92 -16
  64. package/dist/core/command/menu-protocol.js +24 -1
  65. package/dist/core/command/skill-slash.js +322 -0
  66. package/dist/core/command/slash-gate.js +6 -4
  67. package/dist/core/command/slash-handler.js +193 -13
  68. package/dist/core/command/topic-slash.js +528 -0
  69. package/dist/core/evolagent-registry.js +20 -5
  70. package/dist/core/handoff/ipc-registration.js +38 -0
  71. package/dist/core/handoff/runtime.js +5 -11
  72. package/dist/core/handoff/store.js +5 -0
  73. package/dist/core/ipc-registration.js +210 -9
  74. package/dist/core/memory/consolidation-context.js +49 -16
  75. package/dist/core/memory/consolidation-ops-records.js +35 -0
  76. package/dist/core/memory/consolidation-queue-format.js +21 -0
  77. package/dist/core/memory/consolidation.js +174 -180
  78. package/dist/core/message/aun-ipc-sender.js +207 -8
  79. package/dist/core/message/im-renderer.js +149 -6
  80. package/dist/core/message/message-bridge.js +13 -3
  81. package/dist/core/message/message-log.js +1 -0
  82. package/dist/core/message/message-queue.js +131 -3
  83. package/dist/core/message/peer-mode.js +6 -1
  84. package/dist/core/message/response-engine.js +379 -69
  85. package/dist/core/message/subtask-tool-projection.js +140 -0
  86. package/dist/core/model/catalog-service.js +17 -12
  87. package/dist/core/permission/ec-command-parser.js +126 -7
  88. package/dist/core/permission/sandbox-runtime.js +14 -1
  89. package/dist/core/session/session-idle-monitor.js +249 -22
  90. package/dist/core/session/session-manager.js +54 -9
  91. package/dist/core/skill/command-parser.js +13 -0
  92. package/dist/core/skill/index-generator.js +4 -1
  93. package/dist/core/skill/startup.js +41 -0
  94. package/dist/core/skill/switch-resolver.js +55 -29
  95. package/dist/core/task/task-recovery-service.js +2 -0
  96. package/dist/core/task/task-resume-operation.js +11 -0
  97. package/dist/core/thread/command-parser.js +125 -0
  98. package/dist/core/thread/index.js +1 -0
  99. package/dist/core/thread/ipc-operation.js +95 -0
  100. package/dist/core/thread/thread-context-storage.js +131 -0
  101. package/dist/core/thread/thread-operation.js +258 -0
  102. package/dist/core/thread/thread-route.js +33 -0
  103. package/dist/core/thread/thread-service.js +777 -0
  104. package/dist/core/thread/types.js +1 -0
  105. package/dist/eck/message-renderer.js +35 -0
  106. package/dist/index.js +10 -7
  107. package/dist/ipc.js +234 -17
  108. package/dist/paths.js +23 -0
  109. package/dist/response-system/engines/v1/proactive-flow.js +169 -80
  110. package/dist/trigger/authorization.js +59 -29
  111. package/dist/trigger/feedback.js +7 -1
  112. package/dist/trigger/ipc-service.js +49 -35
  113. package/dist/trigger/parser.js +63 -1
  114. package/dist/trigger/script-sandbox.js +58 -77
  115. package/dist/trigger/validation.js +14 -0
  116. package/dist/utils/error-utils.js +22 -1
  117. package/kits/docs/AUTHORING.md +1 -1
  118. package/kits/docs/channels/aun.md +8 -0
  119. package/kits/docs/context-assembly.md +1 -0
  120. package/kits/docs/evolcore/INDEX.md +3 -2
  121. package/kits/docs/evolcore/agent-create.md +39 -28
  122. package/kits/docs/evolcore/agent.md +19 -4
  123. package/kits/docs/evolcore/ec-capabilities.md +4 -0
  124. package/kits/docs/evolcore/group-collaboration.md +3 -5
  125. package/kits/docs/evolcore/group.md +3 -4
  126. package/kits/docs/evolcore/memory-consolidation.md +1 -1
  127. package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
  128. package/kits/docs/evolcore/menu-protocol.md +7 -0
  129. package/kits/docs/evolcore/msg.md +5 -8
  130. package/kits/docs/evolcore/task.md +2 -0
  131. package/kits/docs/evolcore/thread-chain.md +267 -0
  132. package/kits/docs/evolcore/trigger.md +2 -2
  133. package/kits/docs/prompt-loading-architecture.md +1 -0
  134. package/kits/eck_manifest.json +61 -26
  135. package/kits/eck_message_manifest.json +169 -10
  136. package/kits/rules/01-overview.md +0 -9
  137. package/kits/rules/05-venue.md +1 -1
  138. package/kits/rules/06-channel.md +7 -1
  139. package/kits/rules/07-memory-and-thread.md +202 -0
  140. package/kits/schemas/relation-config.schema.9.json +18 -3
  141. package/kits/schemas/role-config.schema.1.json +6 -6
  142. package/kits/schemas/single-session.schema.1.json +6 -6
  143. package/kits/schemas/single-session.schema.2.json +6 -6
  144. package/kits/schemas/single-session.schema.3.json +6 -6
  145. package/kits/skills/.switches.json +12 -1
  146. package/kits/skills/ec-group/SKILL.md +234 -0
  147. package/kits/skills/ec-skill/SKILL.md +10 -1
  148. package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
  149. package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
  150. package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
  151. package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
  152. package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
  153. package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
  154. package/kits/skills/thread-ops/SKILL.md +492 -0
  155. package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
  156. package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
  157. package/kits/skills/wechat-article-reader/SKILL.md +97 -0
  158. package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
  159. package/kits/templates/bootstrap-welcome.md +7 -7
  160. package/kits/templates/memory-consolidation/forget.md +5 -0
  161. package/kits/templates/memory-consolidation/generalize.md +5 -0
  162. package/kits/templates/memory-consolidation/profile.md +5 -0
  163. package/kits/templates/memory-consolidation/promote.md +5 -0
  164. package/kits/templates/message-fragments/item.md +2 -1
  165. package/kits/templates/message-fragments/thread-context.md +6 -0
  166. package/kits/templates/message-fragments/trigger-session.md +2 -0
  167. package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
  168. package/kits/templates/roles/member.json +2 -0
  169. package/kits/templates/roles/visitor.json +2 -0
  170. package/kits/templates/system-fragments/bootstrap.md +66 -72
  171. package/kits/templates/system-fragments/channel.md +3 -6
  172. package/kits/templates/system-fragments/commands.md +2 -1
  173. package/kits/templates/system-fragments/managed-runtime.md +15 -0
  174. package/kits/templates/system-fragments/reply.md +3 -2
  175. package/kits/templates/system-fragments/session.md +5 -14
  176. package/package.json +3 -1
  177. package/kits/docs/evolcore/group-fs.md +0 -17
  178. package/kits/docs/venues/aun-group.md +0 -45
  179. package/kits/docs/venues/group.md +0 -43
@@ -15,6 +15,8 @@ import { sameGroupAid } from '../../aun/group/index.js';
15
15
  import crypto from 'crypto';
16
16
  import path from 'path';
17
17
  import fs from 'fs';
18
+ import { parseDocument } from 'yaml';
19
+ import { agentmdGet } from '../../aun/aid/agentmd.js';
18
20
  import { parseDuration, parseTriggerSet, parseTriggerUpdate } from '../../trigger/parser.js';
19
21
  import { applyTriggerPatch, replaceEditableTriggerDefinition } from '../../trigger/patch.js';
20
22
  import { definitionRevision } from '../../trigger/validation.js';
@@ -25,6 +27,8 @@ import { resolveConfigCommand } from '../../config/resolved-config-op.js';
25
27
  import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommandAsync } from '../../config/config-operation-service.js';
26
28
  import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
27
29
  import { auditCommandAuthorization, auditFullAccessEvent, hashArgv } from '../auth/authorization-audit.js';
30
+ import { ResponseModeRegistry } from '../../response-system/registry.js';
31
+ import { registerBuiltinModes } from '../../response-system/modes/index.js';
28
32
  import { executeResolvedContactCommand, resolveContactCommand } from '../../config/contact-operation-service.js';
29
33
  import { hasTrustedFullAccessDelegation, trustedExecutionIdentityInput } from '../auth/agent-delegation.js';
30
34
  import { executeManagedAidOperation, ManagedAidOperationError, managedAidShowViewForRole, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
@@ -41,6 +45,7 @@ import { handleSlashCommand } from './slash-handler.js';
41
45
  import { resolveChatMode } from '../message/peer-mode.js';
42
46
  import { execMenuAction as menuExecMenuAction, execMenuForEcweb as menuExecMenuForEcweb, execMenuForControl as menuExecMenuForControl, execMenuQuery as menuExecMenuQuery, execMenuUpdate as menuExecMenuUpdate, getMenuItems as menuGetMenuItems, getSubMenuItems as menuGetSubMenuItems, buildTopicForkSourcePayload, } from './menu-handler.js';
43
47
  import { getMenuCatalog, execMenuValueBatch, validateMenuValueBatchArgs } from './menu-catalog.js';
48
+ import { resolveCurrentSessionLoopTarget } from '../session/session-idle-monitor.js';
44
49
  export { isProcessLevelOwner } from './menu-handler.js';
45
50
  const CLI_EXEC_TIMEOUT_MS = 15_000;
46
51
  const CLI_EXEC_MAX_OUTPUT = 128 * 1024;
@@ -126,6 +131,8 @@ export class CommandHandler {
126
131
  primaryRunnerKey;
127
132
  agentRegistry;
128
133
  memoryConsolidationLauncher;
134
+ /** /skill refresh 的 daemon 侧入口:重建指定 agent 的技能索引 */
135
+ skillIndexRefresher;
129
136
  agentApplicationService;
130
137
  triggerSchedulerResolver;
131
138
  triggerSchedulerFallback;
@@ -141,9 +148,10 @@ export class CommandHandler {
141
148
  diagService;
142
149
  setDiagService(service) { this.diagService = service; }
143
150
  async handleMenuClientOperation(argv, sessionId, delegationToken, delegationCommandHash, send) {
144
- const validation = this.agentDelegationRegistry?.validate(delegationToken, sessionId, delegationCommandHash);
145
- if (!validation?.ok)
146
- return { ok: false, code: validation?.code ?? 'DELEGATION_REQUIRED', error: validation?.reason ?? 'Task delegation is required' };
151
+ const validation = this.agentDelegationRegistry?.validate(delegationToken, sessionId, delegationCommandHash)
152
+ ?? { ok: false, code: 'DELEGATION_REQUIRED', reason: 'Task delegation registry is not configured', diagnosticCause: 'missing_token' };
153
+ if (!validation.ok)
154
+ return { ok: false, code: validation.code ?? 'DELEGATION_REQUIRED', error: validation.reason ?? 'Task delegation is required' };
147
155
  const grant = validation.grant;
148
156
  const session = await this.sessionManager.getSessionById(sessionId);
149
157
  if (!session || session.selfAID !== grant.selfAid || argv[0] !== 'request' || argv[1] !== grant.selfAid)
@@ -537,17 +545,21 @@ export class CommandHandler {
537
545
  reason: input.reason,
538
546
  });
539
547
  }
540
- triggerRoleContext(selfAid, channel, peerId, chatType) {
548
+ triggerRoleContext(selfAid, channel, peerId, chatType, conversationId) {
541
549
  const owning = this.getOwningAgent(channel);
542
550
  if (!owning || owning.aid !== selfAid)
543
551
  return {};
552
+ const resolvedChatType = chatType === 'group' ? 'group' : 'private';
553
+ const resolvedConversationId = resolvedChatType === 'group'
554
+ ? conversationId || peerId
555
+ : peerId;
544
556
  const role = resolvePeerRoleDetail({
545
557
  selfAid,
546
558
  channelKey: channel,
547
559
  channelType: this.resolveChannelType(channel),
548
- chatType: chatType === 'group' ? 'group' : 'private',
560
+ chatType: resolvedChatType,
549
561
  actorId: peerId,
550
- conversationId: peerId,
562
+ conversationId: resolvedConversationId,
551
563
  }).effectiveRole;
552
564
  return { role: role || 'none', baseagent: owning.baseagent };
553
565
  }
@@ -592,12 +604,18 @@ export class CommandHandler {
592
604
  const now = Date.now();
593
605
  const id = `trig_${now}_${crypto.randomBytes(4).toString('hex')}`;
594
606
  const name = parsed.name ?? `trigger-${now.toString(36)}`;
607
+ const originChatType = chatType === 'group' ? 'group' : 'private';
608
+ const originIsGroup = originChatType === 'group';
609
+ const originConversationId = originIsGroup ? channelId : peerId;
595
610
  const feedbackChannel = parsed.targetChannel ?? channel;
611
+ const hasExplicitTarget = parsed.targetChannel !== undefined
612
+ || parsed.targetChannelId !== undefined
613
+ || parsed.targetChatType !== undefined;
596
614
  if (parsed.targetChannel && !this.adapters.has(parsed.targetChannel)) {
597
615
  return { error: `目标渠道不存在或未启用:${parsed.targetChannel}` };
598
616
  }
599
617
  const targetChannelType = this.resolveChannelType(feedbackChannel);
600
- const feedbackChannelId = parsed.targetChannelId ?? peerId;
618
+ const feedbackChannelId = parsed.targetChannelId ?? originConversationId;
601
619
  const schedulerAid = parsed.agentId
602
620
  ?? this.getOwningAgent(feedbackChannel)?.aid
603
621
  ?? tryParseChannelKey(feedbackChannel)?.selfAID
@@ -609,19 +627,12 @@ export class CommandHandler {
609
627
  if (targetChannelType === 'aun' && parsed.targetChannelId && !parsed.targetChatType) {
610
628
  return { error: 'AUN 目标必须显式指定 --target-chat-type private 或 group' };
611
629
  }
612
- const targetDelivery = targetChannelType === 'aun'
613
- ? parsed.targetChatType === 'group'
614
- ? { chatType: 'group', groupId: feedbackChannelId }
615
- : parsed.targetChatType === 'private'
616
- ? { chatType: 'private' }
617
- : { chatType: 'private' }
618
- : undefined;
619
630
  const target = {
620
631
  channelKey: feedbackChannel,
621
632
  channelId: feedbackChannelId,
622
633
  session: parsed.targetSession,
623
634
  threadId: parsed.targetSession === 'thread' ? parsed.targetThreadId : undefined,
624
- chatType: targetDelivery?.chatType,
635
+ chatType: parsed.targetChatType ?? (hasExplicitTarget ? undefined : originChatType),
625
636
  };
626
637
  if (target.session === 'thread') {
627
638
  const adapter = this.adapters.get(feedbackChannel);
@@ -630,19 +641,20 @@ export class CommandHandler {
630
641
  if (!target.threadId)
631
642
  return { error: '--target-session thread 需要 --target-thread-id' };
632
643
  }
633
- const originIsGroup = chatType === 'group';
634
644
  const activeSession = originIsGroup
635
645
  ? undefined
636
646
  : this.sessionManager.getActiveSessionSync(channel, peerId, this.resolveChannelType(channel), schedulerAid);
637
647
  const origin = {
638
648
  channelKey: channel,
639
649
  channelType: this.resolveChannelType(channel),
640
- channelId: peerId,
641
- session: !originIsGroup && threadId ? 'thread' : 'main',
642
- threadId: !originIsGroup ? threadId : undefined,
650
+ channelId: originConversationId,
651
+ session: threadId ? 'thread' : 'main',
652
+ threadId,
643
653
  peerId,
644
654
  sessionKey: activeSession?.sessionKey,
645
- delivery: { chatType: 'private' },
655
+ delivery: originIsGroup
656
+ ? { chatType: 'group', groupId: originConversationId }
657
+ : { chatType: 'private' },
646
658
  };
647
659
  if (!isAdmin && parsed.feedbackStrategy === 'target' && !sameFeedbackTarget(target, origin)) {
648
660
  return { error: '无权限:非 admin 只能把 trigger 投递到来源会话' };
@@ -664,7 +676,7 @@ export class CommandHandler {
664
676
  timeoutMs: parsed.scriptTimeoutMs ?? 30_000,
665
677
  } : undefined;
666
678
  const permissionMode = this.triggerPermissionFromParsed(parsed);
667
- const triggerRole = this.triggerRoleContext(schedulerAid, channel, peerId, chatType);
679
+ const triggerRole = this.triggerRoleContext(schedulerAid, channel, peerId, originChatType, originConversationId);
668
680
  const permissionError = this.validateTriggerPermissionModeForRole(permissionMode, isAdmin, triggerRole.role, schedulerAid, isDaemonOwner, canApprovePersistentFullAccess);
669
681
  if (permissionError)
670
682
  return { error: permissionError };
@@ -929,10 +941,21 @@ export class CommandHandler {
929
941
  setStatsCollector(collector) {
930
942
  this.statsCollector = collector;
931
943
  }
932
- /** 注入记忆整理入口(daemon 的统一整理队列),供 /memory consolidate 使用。返回 null 表示已入队。 */
944
+ /**
945
+ * 注入记忆整理入口(daemon 的统一整理队列),供 /memory consolidate 使用。
946
+ * 已入队时返回带 queued 的占位结果;返回 null 表示拉起失败。
947
+ */
933
948
  setMemoryConsolidationLauncher(launcher) {
934
949
  this.memoryConsolidationLauncher = launcher;
935
950
  }
951
+ /** 注入 /skill refresh 的索引重建入口(daemon 启动时调用)。 */
952
+ setSkillIndexRefresher(refresher) {
953
+ this.skillIndexRefresher = refresher;
954
+ }
955
+ /** 供 slash-handler 读取。 */
956
+ getSkillIndexRefresher() {
957
+ return this.skillIndexRefresher;
958
+ }
936
959
  registerAdapter(adapter) {
937
960
  this.adapters.set(adapter.channelName, adapter);
938
961
  }
@@ -1072,7 +1095,7 @@ export class CommandHandler {
1072
1095
  getMenuItems(role, chatType = 'private', scope = 'agent', authSubject) {
1073
1096
  return menuGetMenuItems.call(this, role, chatType, scope, authSubject);
1074
1097
  }
1075
- async getMenuCatalog(channel, channelId, subject, request, channelScope = 'agent', paircodeAllowed) {
1098
+ async getMenuCatalog(channel, channelId, subject, request, channelScope = 'agent', paircodeAllowed, transportReplyContext) {
1076
1099
  return await getMenuCatalog({
1077
1100
  subject,
1078
1101
  channelType: this.resolveChannelType(channel),
@@ -1080,6 +1103,19 @@ export class CommandHandler {
1080
1103
  channelId,
1081
1104
  request,
1082
1105
  ...(paircodeAllowed === undefined ? {} : { paircodeAllowed }),
1106
+ resolveLoopTarget: async (context) => {
1107
+ const target = await resolveCurrentSessionLoopTarget(this.sessionManager, {
1108
+ channel,
1109
+ channelId: context.channelId,
1110
+ selfAid: context.self ?? '',
1111
+ ...(transportReplyContext?.threadId && context.channelId === channelId
1112
+ ? { threadId: transportReplyContext.threadId }
1113
+ : {}),
1114
+ });
1115
+ if (target.peerKey !== context.peerKey)
1116
+ throw { code: 'INVALID_CONTEXT', message: '当前会话关系不匹配' };
1117
+ return { level: target.level, sessionId: target.session.id };
1118
+ },
1083
1119
  });
1084
1120
  }
1085
1121
  async execMenuValueBatch(channel, channelId, subject, args, request, channelScope = 'agent', transportReplyContext) {
@@ -1573,7 +1609,11 @@ export class CommandHandler {
1573
1609
  if (!isAdmin && updated.execution.type === 'script') {
1574
1610
  return { ok: false, error: '无权限:只有 owner/admin 可以修改 script trigger' };
1575
1611
  }
1576
- const triggerRole = this.triggerRoleContext(definition.agentAid, definition.origin?.channelKey || channel, definition.origin?.peerId || peerId);
1612
+ const originChatType = definition.origin?.delivery?.chatType;
1613
+ const originConversationId = originChatType === 'group'
1614
+ ? definition.origin?.channelId
1615
+ : definition.origin?.peerId;
1616
+ const triggerRole = this.triggerRoleContext(definition.agentAid, definition.origin?.channelKey || channel, definition.origin?.peerId || peerId, originChatType, originConversationId);
1577
1617
  const permissionError = this.validateTriggerPermissionModeForRole(updated.execution.permissionMode, isAdmin, triggerRole.role, definition.agentAid, isDaemonOwner, canApprovePersistentFullAccess);
1578
1618
  if (permissionError) {
1579
1619
  return {
@@ -1853,10 +1893,23 @@ export class CommandHandler {
1853
1893
  const delegation = this.agentDelegationRegistry.validate(delegationToken, sessionId, delegationCommandHash);
1854
1894
  if (!delegation.ok)
1855
1895
  return { ok: false, code: delegation.code, error: delegation.reason };
1896
+ return this.executeConfigOperation(argv, sessionId, delegation.grant);
1897
+ }
1898
+ async executeConfigOperation(argv, sessionId, grant) {
1856
1899
  const session = await this.sessionManager.getSessionById(sessionId);
1857
1900
  if (!session)
1858
1901
  return { ok: false, code: 'INVALID_SESSION', error: 'Invalid session' };
1859
- const grant = delegation.grant;
1902
+ if (session.selfAID !== grant.selfAid || grant.sessionId !== sessionId) {
1903
+ return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation does not match the current session' };
1904
+ }
1905
+ if (argv[0] === 'agent') {
1906
+ const tail = argv.at(-2) === '--format' && argv.at(-1) === 'json' ? argv.slice(0, -2) : argv;
1907
+ const sub = tail[1];
1908
+ if ((sub !== 'get' && sub !== 'set') || tail.length !== (sub === 'get' ? 4 : 5)) {
1909
+ return { ok: false, code: 'INVALID_ARGS', error: 'Expected agent get/set <aid> <key> [value] [--format json]' };
1910
+ }
1911
+ argv = ['config', sub, ...tail.slice(3), '--self', tail[2]];
1912
+ }
1860
1913
  let conversationId;
1861
1914
  try {
1862
1915
  conversationId = parsePeerKey(grant.peerKey).channelId;
@@ -2549,39 +2602,76 @@ export class CommandHandler {
2549
2602
  if (sessionSelfAid && sessionSelfAid !== actorAid) {
2550
2603
  return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
2551
2604
  }
2552
- if (!hasTrustedFullAccessDelegation(grant) && (grant.channelType !== 'aun' || grant.chatType !== 'group')) {
2553
- return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs operations require the current AUN group relation' };
2554
- }
2605
+ const isPersonalTarget = parsed.command.targetAid === actorAid;
2555
2606
  let currentGroup;
2556
- try {
2557
- currentGroup = parsePeerKey(grant.peerKey).channelId;
2558
- }
2559
- catch {
2560
- return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
2607
+ if (isPersonalTarget) {
2608
+ if (grant.issuedRole !== 'owner' && !hasTrustedFullAccessDelegation(grant)) {
2609
+ return { ok: false, code: 'NO_PERMISSION', error: 'Managed personal storage requires the current Agent owner relation' };
2610
+ }
2611
+ if (parsed.command.actorAid && parsed.command.actorAid !== actorAid) {
2612
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent can access its personal storage' };
2613
+ }
2561
2614
  }
2562
- if (!hasTrustedFullAccessDelegation(grant)
2563
- && ((parsed.command.actorAid && parsed.command.actorAid !== actorAid) || !sameGroupAid(parsed.command.groupId, currentGroup))) {
2564
- return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN group can be targeted' };
2615
+ else {
2616
+ const trustedFullAccess = hasTrustedFullAccessDelegation(grant);
2617
+ if (trustedFullAccess) {
2618
+ let targetType;
2619
+ try {
2620
+ const card = await agentmdGet(parsed.command.targetAid, { withVerification: true, remoteOnly: true });
2621
+ if (card.verification.status !== 'verified') {
2622
+ return { ok: false, code: 'INVALID_TARGET', error: 'Target AID agent.md verification did not succeed' };
2623
+ }
2624
+ const frontmatter = card.content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];
2625
+ const document = frontmatter ? parseDocument(frontmatter) : undefined;
2626
+ targetType = document && document.errors.length === 0
2627
+ ? String(document.toJS()?.type ?? '').trim().toLowerCase()
2628
+ : undefined;
2629
+ }
2630
+ catch {
2631
+ return { ok: false, code: 'INVALID_TARGET', error: 'Target AID agent.md could not be verified' };
2632
+ }
2633
+ if (targetType !== 'group') {
2634
+ return { ok: false, code: 'INVALID_TARGET', error: 'Managed group storage requires a verified group AID' };
2635
+ }
2636
+ }
2637
+ if (!hasTrustedFullAccessDelegation(grant) && (grant.channelType !== 'aun' || grant.chatType !== 'group')) {
2638
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed group fs operations require the current AUN group relation' };
2639
+ }
2640
+ try {
2641
+ currentGroup = parsePeerKey(grant.peerKey).channelId;
2642
+ }
2643
+ catch {
2644
+ return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
2645
+ }
2646
+ if (parsed.command.actorAid && parsed.command.actorAid !== actorAid) {
2647
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs cannot switch the execution identity with --as' };
2648
+ }
2649
+ if (!hasTrustedFullAccessDelegation(grant)
2650
+ && !sameGroupAid(parsed.command.targetAid, currentGroup)) {
2651
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current AUN group can be targeted' };
2652
+ }
2565
2653
  }
2566
- parsed.command.actorAid = hasTrustedFullAccessDelegation(grant)
2567
- ? (parsed.command.actorAid ?? actorAid)
2654
+ const resolvedCommand = parsed.command;
2655
+ resolvedCommand.storageBackend = isPersonalTarget ? 'personal' : 'group';
2656
+ resolvedCommand.actorAid = hasTrustedFullAccessDelegation(grant) && !isPersonalTarget
2657
+ ? (resolvedCommand.actorAid ?? actorAid)
2568
2658
  : actorAid;
2569
2659
  const subject = buildAuthSubject({
2570
2660
  selfAid: grant.selfAid,
2571
2661
  actorId: grant.actorId,
2572
2662
  channel: grant.channel,
2573
2663
  channelType: grant.channelType,
2574
- channelId: currentGroup,
2664
+ channelId: currentGroup ?? actorAid,
2575
2665
  chatType: grant.chatType,
2576
- conversationId: currentGroup,
2666
+ conversationId: currentGroup ?? actorAid,
2577
2667
  processOwners: loadDaemonConfig().owners ?? [],
2578
2668
  ...trustedExecutionIdentityInput(grant),
2579
2669
  });
2580
2670
  const intentArgs = {
2581
2671
  self: actorAid,
2582
- targetId: currentGroup,
2583
- peer: currentGroup,
2584
- peerKey: formatPeerKey('aun', currentGroup),
2672
+ targetId: parsed.command.targetAid,
2673
+ peer: currentGroup ?? actorAid,
2674
+ peerKey: formatPeerKey('aun', currentGroup ?? actorAid),
2585
2675
  path: parsed.command.remotePath,
2586
2676
  targetPath: parsed.command.remotePath,
2587
2677
  ...(parsed.command.remoteSourcePath ? { sourcePath: parsed.command.remoteSourcePath } : {}),
@@ -2598,7 +2688,7 @@ export class CommandHandler {
2598
2688
  subject,
2599
2689
  intent: {
2600
2690
  operation: parsed.command.operationId,
2601
- scope: 'relation',
2691
+ scope: isPersonalTarget ? 'agent' : 'relation',
2602
2692
  source: 'agent-tool',
2603
2693
  args: intentArgs,
2604
2694
  },
@@ -2655,7 +2745,7 @@ export class CommandHandler {
2655
2745
  }
2656
2746
  }
2657
2747
  try {
2658
- return { ok: true, result: await executeManagedFsOperation(parsed.command) };
2748
+ return { ok: true, result: await executeManagedFsOperation(resolvedCommand) };
2659
2749
  }
2660
2750
  catch (error) {
2661
2751
  return {
@@ -2668,6 +2758,95 @@ export class CommandHandler {
2668
2758
  setAgentDelegationRegistry(registry) {
2669
2759
  this.agentDelegationRegistry = registry;
2670
2760
  }
2761
+ async handleManagedCliOperation(argv, sessionId, grant) {
2762
+ const invalid = (error) => ({ ok: false, code: 'INVALID_ARGUMENT', error });
2763
+ if (argv[0] === 'ctl') {
2764
+ if (!argv[1] || /\s/.test(argv[1]))
2765
+ return invalid('Expected a ctl subcommand');
2766
+ return this.handleCtl('/' + argv.slice(1).join(' '), sessionId, grant);
2767
+ }
2768
+ if (argv[0] !== 'model' && argv[0] !== 'response')
2769
+ return invalid('Expected a model or response command');
2770
+ if (argv.includes('--self') || argv.includes('--peer') || argv.includes('--role')) {
2771
+ return { ok: false, code: 'NOT_ALLOWED', error: 'Managed commands always use the authenticated current relation' };
2772
+ }
2773
+ const args = argv.slice(2);
2774
+ const formatIndex = args.indexOf('--format');
2775
+ if (formatIndex >= 0) {
2776
+ if (args[formatIndex + 1] !== 'json' || args.lastIndexOf('--format') !== formatIndex)
2777
+ return invalid('--format must be json');
2778
+ args.splice(formatIndex, 2);
2779
+ }
2780
+ const sub = argv[1];
2781
+ if (argv[0] === 'model') {
2782
+ let command;
2783
+ if (['list', 'current', 'reset', 'check'].includes(sub) && args.length === 0) {
2784
+ command = `/model ${sub}`;
2785
+ }
2786
+ else if ((sub === 'info' || sub === 'effort') && args.length === 1 && args[0] && !/\s|^--/.test(args[0])) {
2787
+ command = sub === 'info' ? `/model info ${args[0]}` : `/effort ${args[0]}`;
2788
+ }
2789
+ else if (sub === 'use' && args[0] && !/\s|^--/.test(args[0])
2790
+ && !['list', 'current', 'reset', 'check', 'info'].includes(args[0])
2791
+ && (args.length === 1 || (args.length === 3 && args[1] === '--effort' && args[2] && !/\s|^--/.test(args[2])))) {
2792
+ command = `/model ${args[0]}${args.length === 3 ? ` ${args[2]}` : ''}`;
2793
+ }
2794
+ else
2795
+ return invalid('Invalid managed model arguments');
2796
+ return this.handleCtl(command, sessionId, grant);
2797
+ }
2798
+ const execute = (fieldArgs) => this.executeConfigOperation(['config', ...fieldArgs], sessionId, grant);
2799
+ const failure = (response) => ({
2800
+ ok: false, code: response.code, error: response.error, data: response.data,
2801
+ });
2802
+ const emit = (data, result) => ({ ok: true, data, result });
2803
+ if (sub === 'set' && args.length === 1) {
2804
+ const registry = new ResponseModeRegistry();
2805
+ registerBuiltinModes(registry);
2806
+ if (!registry.has(args[0]))
2807
+ return { ok: false, code: 'UNKNOWN_MODE', error: `Unknown mode: ${args[0]}` };
2808
+ const result = await execute(['set', 'responseMode', args[0]]);
2809
+ if (!result.ok)
2810
+ return failure(result);
2811
+ return emit({ ok: true, scope: 'relation', mode: args[0] }, `✓ 已设置响应模式为 ${args[0]}(作用域: relation)`);
2812
+ }
2813
+ if (sub === 'reset' && args.length === 0) {
2814
+ const result = await execute(['unset', 'responseMode']);
2815
+ if (!result.ok)
2816
+ return failure(result);
2817
+ return emit({ ok: true, scope: 'relation' }, '✓ 已清除当前关系的响应模式设置');
2818
+ }
2819
+ if (sub === 'current' && args.length === 0) {
2820
+ const modeResult = await execute(['get', 'responseMode']);
2821
+ if (!modeResult.ok)
2822
+ return failure(modeResult);
2823
+ const paramsResult = await execute(['get', 'responseModeParams']);
2824
+ if (!paramsResult.ok)
2825
+ return failure(paramsResult);
2826
+ if (!modeResult.result?.ok || modeResult.result.subcommand !== 'get'
2827
+ || !paramsResult.result?.ok || paramsResult.result.subcommand !== 'get')
2828
+ return invalid('Unexpected config result');
2829
+ const configuredMode = typeof modeResult.result.value === 'string' ? modeResult.result.value : undefined;
2830
+ const registry = new ResponseModeRegistry();
2831
+ registerBuiltinModes(registry);
2832
+ const mode = configuredMode ?? registry.getPreferredId();
2833
+ return emit({ ok: true, scope: 'relation', responseMode: mode ?? null,
2834
+ source: configuredMode ? 'config' : 'preferred', sourceTarget: modeResult.result.source?.target ?? null,
2835
+ responseModeParams: paramsResult.result.value ?? null }, `当前响应模式: ${mode ?? '(无)'}`);
2836
+ }
2837
+ if (sub === 'config' && (args.length === 0 || (args.length === 1 && !args[0].startsWith('--') && args[0] !== 'set'))) {
2838
+ const result = await execute(['get', 'responseModeParams']);
2839
+ if (!result.ok)
2840
+ return failure(result);
2841
+ if (!result.result?.ok || result.result.subcommand !== 'get')
2842
+ return invalid('Unexpected config result');
2843
+ const params = (result.result.value ?? {});
2844
+ return args.length === 1
2845
+ ? emit({ ok: true, mode: args[0], config: params[args[0]] ?? {} }, `${args[0]} 配置: ${JSON.stringify(params[args[0]] ?? {}, null, 2)}`)
2846
+ : emit({ ok: true, responseModeParams: params }, `所有模式配置: ${JSON.stringify(params, null, 2)}`);
2847
+ }
2848
+ return { ok: false, code: 'NOT_ALLOWED', error: `response ${sub} is not available in a managed task` };
2849
+ }
2671
2850
  /**
2672
2851
  * Agent ctl 入口:通过 IPC 接收 Agent 自主管理指令
2673
2852
  * 复用现有 slash cmd 逻辑,权限继承 session 用户角色
@@ -19,13 +19,17 @@ export async function executeDiagIpc(service, registry, owners, cmd) {
19
19
  try {
20
20
  if (!Array.isArray(cmd.argv) || cmd.argv.some((v) => typeof v !== 'string'))
21
21
  throw new Error('Invalid diag argv');
22
- return await service.execute(parseDiagArgs(cmd.argv));
22
+ if (cmd.argv[0] !== 'diag')
23
+ throw new Error('Expected a diag command');
24
+ return await service.execute(parseDiagArgs(cmd.argv.slice(1)));
23
25
  }
24
26
  catch (error) {
25
27
  return { ok: false, code: error.code ?? 'INVALID_ARGUMENT', message: error.message };
26
28
  }
27
29
  }
28
30
  export function parseDiagArgs(args) {
31
+ if (args.at(-2) === '--format' && args.at(-1) === 'json')
32
+ args = args.slice(0, -2);
29
33
  const words = [];
30
34
  let ownerAid;
31
35
  let literal = false;
@@ -8,6 +8,7 @@
8
8
  * 不支持跨关系浏览:relation 永远是发起命令的这段关系。
9
9
  */
10
10
  import { CONSOLIDATION_MODES, MODE_SCOPE } from '../memory/consolidation-context.js';
11
+ import { formatQueueDetails } from '../memory/consolidation-queue-format.js';
11
12
  import { MEMORY_SCOPES, isMemoryScope, memoryOverview, listMemoryFiles, formatMemoryFiles, readMemoryIndex, showMemoryFile, formatPendingNotes, showPendingNote, reindexScope, dropNote, archiveMemoryFile, restoreMemoryFile, } from '../memory/memory-admin.js';
12
13
  const USAGE = [
13
14
  '用法:',
@@ -263,8 +264,11 @@ async function consolidate(ctx, target, rawMode, rawScope, needScope, isPayload)
263
264
  ...(scopeOverride ? { scopeOverride } : {}),
264
265
  });
265
266
  const label = `${mode}${scopeOverride ? `(${scopeOverride})` : ''}`;
266
- if (!launched || launched.queued) {
267
- return result(`⏳ 整理请求已入队(${label}),前一个整理会话结束后自动开始`);
267
+ if (!launched)
268
+ return error(`拉起整理会话失败(${label}),详见 daemon 日志`);
269
+ if (launched.queued) {
270
+ return result(`⏳ 整理请求已入队(${label}),前一个整理会话结束后自动开始\n` +
271
+ formatQueueDetails(launched.queued).join('\n'));
268
272
  }
269
273
  return result(`✅ 已拉起整理会话(${label})${launched.executionSessionId ? `\n会话: ${launched.executionSessionId}` : ''}\n完成后会在当前对话反馈结果`);
270
274
  }
@@ -7,6 +7,7 @@ import { ConfigTarget, read as readConfig } from '../../config/config-manager.js
7
7
  import { readRolesConfig } from '../../config/roles.js';
8
8
  import { normalizeMenuError } from './menu-protocol.js';
9
9
  import { isValidAid } from '../../aun/aid/validation.js';
10
+ import { DEFAULT_SESSION_LOOP_SECONDS, MAX_SESSION_LOOP_SECONDS, MIN_SESSION_LOOP_SECONDS } from '../session/session-idle-monitor.js';
10
11
  const DEFAULT_DIRECTORY = { getGroup: groupInfo, getMembers: groupMembers };
11
12
  const HOST_BASEAGENTS = ['claude', 'codex', 'ecagent', 'gemini'];
12
13
  export function validateMenuValueBatchArgs(args) {
@@ -65,6 +66,19 @@ const relation = (id, name, label, order, cmd, operation, extra) => ({
65
66
  ...extra,
66
67
  });
67
68
  const ENTRIES = [
69
+ {
70
+ id: 'session.loop',
71
+ type: 'setting',
72
+ contexts: ['private_relation', 'group_relation'],
73
+ name: 'session',
74
+ label: '当前会话闲置触发',
75
+ section: 'relation_behavior',
76
+ order: 5,
77
+ query: { cmd: '/session', args: { view: 'loop' }, operation: 'session.loop.current', scope: 'relation' },
78
+ update: { cmd: '/session', args: { resource: 'loop' }, operation: 'session.loop.update', scope: 'relation' },
79
+ valueSchema: { type: 'boolean' },
80
+ control: 'toggle-with-number',
81
+ },
68
82
  relation('model.relation', 'model', '模型', 10, '/model', 'model.current', { options: { cmd: '/model', operation: 'model.list', scope: 'relation', mode: 'dynamic' }, update: { cmd: '/model', operation: 'model.use', scope: 'relation' }, control: 'select' }),
69
83
  relation('effort.relation', 'effort', '推理强度', 20, '/effort', 'model.effort.current', { options: { cmd: '/effort', operation: 'model.effort.current', scope: 'relation', mode: 'dynamic', dependsOn: ['model.relation'] }, update: { cmd: '/effort', operation: 'model.effort', scope: 'relation' }, control: 'select' }),
70
84
  relation('chatmode.relation', 'chatmode', '对话模式', 30, '/chatmode', 'chatmode.current', { options: { cmd: '/chatmode', operation: 'chatmode.current', scope: 'relation', mode: 'static', items: [{ value: 'interactive', label: '交互模式' }, { value: 'proactive', label: '主动模式' }] }, update: { cmd: '/chatmode', operation: 'chatmode.update', scope: 'relation' }, control: 'select' }),
@@ -272,6 +286,9 @@ export function bindMenuExecutionContextArgs(args, context) {
272
286
  if (!args && context.kind !== 'group_relation')
273
287
  return args;
274
288
  const { context: _context, ...bound } = args ?? {};
289
+ const isSessionLoop = bound.resource === 'loop' || bound.view === 'loop';
290
+ if (isSessionLoop)
291
+ return bound;
275
292
  if (context.kind === 'private_relation' || context.kind === 'group_relation') {
276
293
  if (bound.scope !== 'role')
277
294
  bound.scope = 'relation';
@@ -356,6 +373,12 @@ async function operationView(binding, context, options = {}) {
356
373
  function wireBinding(binding, context) {
357
374
  if (!binding || binding.supported === false)
358
375
  return undefined;
376
+ if (binding.operation.startsWith('session.loop.'))
377
+ return {
378
+ args: context.kind === 'group_relation'
379
+ ? { ...(binding.args ?? {}), context: { kind: context.kind, ...(context.groupId ? { groupId: context.groupId } : {}) } }
380
+ : binding.args ?? {},
381
+ };
359
382
  return {
360
383
  args: context.kind === 'group_relation'
361
384
  ? {
@@ -379,6 +402,27 @@ export async function getMenuCatalog(input) {
379
402
  operations[key] = view;
380
403
  }
381
404
  entries.push({ id: entry.id, type: entry.type, name: entry.name, label: entry.label, section: entry.section, order: entry.order, target: { scope: context.kind === 'agent' ? 'agent' : context.kind === 'process' ? 'process' : 'relation', ...(context.self ? { self: context.self } : {}), ...(context.peerKey ? { peerKey: context.peerKey } : {}) }, valueSchema: entry.valueSchema ?? { type: 'object' }, operations, wire: { name: entry.name, operations: { ...(wireBinding(entry.query, context) ? { query: wireBinding(entry.query, context) } : {}), ...(wireBinding(entry.options, context) ? { options: wireBinding(entry.options, context) } : {}), ...(wireBinding(entry.update, context) ? { update: wireBinding(entry.update, context) } : {}), ...(wireBinding(entry.action, context) ? { action: wireBinding(entry.action, context) } : {}) } }, ui: { control: entry.control ?? 'action' } });
405
+ if (entry.id === 'session.loop') {
406
+ const loopEntry = entries[entries.length - 1];
407
+ let target = { level: 'relation-main' };
408
+ if (input.resolveLoopTarget) {
409
+ try {
410
+ target = await input.resolveLoopTarget(context);
411
+ }
412
+ catch (error) {
413
+ const reason = normalizeMenuError(error);
414
+ for (const operation of Object.values(operations)) {
415
+ Object.assign(operation, { allowed: false, reason });
416
+ }
417
+ }
418
+ }
419
+ loopEntry.target = target;
420
+ loopEntry.label = target.level === 'topic-session' ? '当前话题闲置触发' : '关系主会话闲置触发';
421
+ loopEntry.ui = {
422
+ control: entry.control,
423
+ idleSeconds: { type: 'integer', minimum: MIN_SESSION_LOOP_SECONDS, maximum: MAX_SESSION_LOOP_SECONDS, default: DEFAULT_SESSION_LOOP_SECONDS, unit: 'seconds' },
424
+ };
425
+ }
382
426
  }
383
427
  const policyRevision = hash({ role: context.subject.role, self: context.subject.selfAid, peerKey: context.subject.peerKey, groupMembershipRole: context.groupMembershipRole, baselineRole: context.baselineRole, paircodeAllowed, roles: readRolesConfig(context.subject.selfAid) });
384
428
  const data = { $schema_version: 1, kind: 'menu.catalog', complete: true, catalogRevision: hash({ entries, context: context.contextKey, policyRevision }), policyRevision, generatedAt: Date.now(), context: { contextKey: context.contextKey, kind: context.kind, channelScope: context.channelScope, transport: context.transport, chatType: context.chatType, ...(context.self ? { self: context.self } : {}), ...(context.actor ? { actor: context.actor } : {}), ...(context.groupId ? { groupId: context.groupId } : {}), ...(context.peerKey ? { peerKey: context.peerKey } : {}), requesterRole: context.requesterRole, ...(context.groupMembershipRole ? { groupMembershipRole: context.groupMembershipRole } : {}), ...(context.baselineRole !== undefined ? { baselineRole: context.baselineRole } : {}) }, features: { batchValuesV1: true, verifiedExplicitGroupContext: context.kind === 'group_relation' }, entries };
@@ -420,7 +464,10 @@ export async function execMenuValueBatch(input) {
420
464
  if (allowed?.supported === false || !(allowed?.allowed))
421
465
  return { itemId, entryId, ok: false, error: allowed?.reason ?? { code: 'NOT_SUPPORTED', message: 'operation is not supported or denied' } };
422
466
  try {
423
- const result = await input.execQuery(entry.query.cmd, context.channelId, bindingArgs(entry.query, context), context);
467
+ const queryArgs = entry.id === 'session.loop'
468
+ ? { ...(entry.query.args ?? {}) }
469
+ : bindingArgs(entry.query, context);
470
+ const result = await input.execQuery(entry.query.cmd, context.channelId, queryArgs, context);
424
471
  return !('error' in result) ? { itemId, entryId, ok: true, data: result.data } : { itemId, entryId, ok: false, error: { code: normalizeMenuError({ code: result.code, message: result.error, data: result.data }).code, message: result.error, ...(result.data !== undefined ? { data: result.data } : {}) } };
425
472
  }
426
473
  catch (error) {