evolcore 0.0.35 → 0.0.37

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 (224) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +6 -0
  3. package/bin/install-codex-managed-hooks.mjs +20 -3
  4. package/dist/agents/baseagent-availability.js +1 -1
  5. package/dist/agents/baseagent.js +88 -0
  6. package/dist/agents/claude-runner.js +59 -12
  7. package/dist/agents/codex/app-server-client.js +18 -1
  8. package/dist/agents/codex-runner.js +262 -21
  9. package/dist/agents/ecagent-runner.js +47 -11
  10. package/dist/agents/gemini-runner.js +36 -12
  11. package/dist/aun/aid/client.js +4 -3
  12. package/dist/aun/aid/control-aid.js +270 -8
  13. package/dist/aun/aid/identity.js +6 -3
  14. package/dist/aun/aid/ownership-operation.js +58 -0
  15. package/dist/aun/aid/store.js +1 -0
  16. package/dist/aun/group/fs-download.js +67 -2
  17. package/dist/aun/group/history.js +20 -5
  18. package/dist/aun/group/operations.js +8 -2
  19. package/dist/aun/menu/client.js +39 -2
  20. package/dist/aun/menu/transport.js +1 -1
  21. package/dist/aun/msg/history.js +43 -4
  22. package/dist/aun/msg/managed-operation.js +30 -53
  23. package/dist/aun/msg/p2p.js +51 -15
  24. package/dist/aun/outbox.js +4 -0
  25. package/dist/channels/aun.js +260 -73
  26. package/dist/channels/daemon.js +6 -4
  27. package/dist/channels/feishu.js +3 -0
  28. package/dist/cli/agent-command.js +161 -223
  29. package/dist/cli/agent-create.js +190 -223
  30. package/dist/cli/agent.js +3 -1
  31. package/dist/cli/aid-bind-command.js +53 -0
  32. package/dist/cli/aun-commands.js +384 -91
  33. package/dist/cli/bench.js +1 -1
  34. package/dist/cli/cli-argv.js +74 -0
  35. package/dist/cli/daemon-commands.js +105 -74
  36. package/dist/cli/diag.js +1 -1
  37. package/dist/cli/fs-command.js +18 -19
  38. package/dist/cli/getmeta-command.js +159 -6
  39. package/dist/cli/index.js +6 -12
  40. package/dist/cli/init-channel.js +8 -55
  41. package/dist/cli/init.js +125 -218
  42. package/dist/cli/queue-command.js +11 -8
  43. package/dist/cli/restart-monitor.js +177 -85
  44. package/dist/cli/session-command.js +1 -1
  45. package/dist/cli/stats.js +7 -5
  46. package/dist/cli/thread-command.js +103 -30
  47. package/dist/cli/trigger-command.js +1 -1
  48. package/dist/cli/watch-msg-diag.js +1 -0
  49. package/dist/config/access-policy.js +2 -2
  50. package/dist/config/boot-log.js +14 -11
  51. package/dist/config/config-field-policy.js +9 -1
  52. package/dist/config/config-manager.js +176 -90
  53. package/dist/config/config-operation-service.js +7 -14
  54. package/dist/config/contact-book-store.js +142 -26
  55. package/dist/config/contact-book.js +4 -2
  56. package/dist/config/lifecycle.js +1 -1
  57. package/dist/config/owner-config.js +96 -0
  58. package/dist/config/peer-role-resolver.js +12 -18
  59. package/dist/config/providers-config.js +382 -0
  60. package/dist/config/providers-import.js +74 -0
  61. package/dist/config/providers-management.js +328 -0
  62. package/dist/config/providers.js +22 -0
  63. package/dist/config/resolved-config-op.js +17 -14
  64. package/dist/config/snapshot.js +28 -16
  65. package/dist/config-store.js +18 -14
  66. package/dist/core/agent-ipc-registration.js +21 -63
  67. package/dist/core/agent-runtime.js +16 -5
  68. package/dist/core/auth/agent-delegation.js +199 -25
  69. package/dist/core/auth/auth-gateway.js +44 -2
  70. package/dist/core/auth/authorization-denial.js +1 -1
  71. package/dist/core/auth/debug-context.js +2 -1
  72. package/dist/core/auth/delegation-authorization.js +2 -2
  73. package/dist/core/auth/delegation-diagnostics.js +4 -1
  74. package/dist/core/auth/operation-authorizer.js +10 -1
  75. package/dist/core/auth/operation-catalog.js +19 -1
  76. package/dist/core/bootstrap-service.js +43 -20
  77. package/dist/core/channel-loader.js +0 -1
  78. package/dist/core/channel-runtime.js +30 -4
  79. package/dist/core/command/agent-control.js +196 -37
  80. package/dist/core/command/cli-intent-parser.js +60 -4
  81. package/dist/core/command/command-handler.js +212 -60
  82. package/dist/core/command/connect-menu.js +1 -0
  83. package/dist/core/command/diag-service.js +5 -4
  84. package/dist/core/command/file-search.js +368 -0
  85. package/dist/core/command/menu-catalog.js +2 -1
  86. package/dist/core/command/menu-handler.js +292 -107
  87. package/dist/core/command/menu-protocol.js +23 -0
  88. package/dist/core/command/role-menu.js +7 -19
  89. package/dist/core/command/skill-slash.js +130 -25
  90. package/dist/core/command/slash-handler.js +101 -53
  91. package/dist/core/command/topic-slash.js +16 -5
  92. package/dist/core/daemon-bootstrap.js +7 -1
  93. package/dist/core/daemon-control.js +19 -1
  94. package/dist/core/daemon-lifecycle.js +2 -1
  95. package/dist/core/event-catalog.js +2 -0
  96. package/dist/core/evolagent-registry.js +13 -2
  97. package/dist/core/evolagent.js +3 -6
  98. package/dist/core/handoff/ipc-registration.js +2 -2
  99. package/dist/core/handoff/runtime.js +40 -6
  100. package/dist/core/handoff/store.js +1 -0
  101. package/dist/core/inference/text-inference.js +95 -6
  102. package/dist/core/ipc-registration.js +43 -24
  103. package/dist/core/memory/consolidation-context.js +4 -1
  104. package/dist/core/memory/consolidation.js +25 -14
  105. package/dist/core/message/aun-ipc-sender.js +296 -88
  106. package/dist/core/message/aun-request-registry.js +226 -0
  107. package/dist/core/message/create-status.js +33 -4
  108. package/dist/core/message/message-bridge.js +62 -13
  109. package/dist/core/message/message-log.js +0 -1
  110. package/dist/core/message/message-queue.js +4 -1
  111. package/dist/core/message/message-utils.js +8 -1
  112. package/dist/core/message/peer-mode.js +17 -9
  113. package/dist/core/message/response-engine.js +266 -124
  114. package/dist/core/message/retry-scheduler.js +11 -8
  115. package/dist/core/model/catalog-service.js +60 -18
  116. package/dist/core/model/model-catalog.js +112 -9
  117. package/dist/core/model/model-fallback.js +94 -26
  118. package/dist/core/ownership/owner-resolver.js +687 -0
  119. package/dist/core/permission/approval-gateway.js +196 -62
  120. package/dist/core/permission/ec-command-parser.js +138 -25
  121. package/dist/core/permission/filesystem-approval-scope.js +141 -0
  122. package/dist/core/permission/protected-paths.js +14 -1
  123. package/dist/core/permission/sandbox-runtime.js +171 -18
  124. package/dist/core/permission/tool-policy.js +13 -2
  125. package/dist/core/role/runtime-policy.js +3 -4
  126. package/dist/core/session/session-idle-monitor.js +10 -8
  127. package/dist/core/session/session-manager.js +72 -45
  128. package/dist/core/session/session-mapper.js +18 -7
  129. package/dist/core/session/session-renew.js +5 -4
  130. package/dist/core/session/session-title.js +2 -1
  131. package/dist/core/session/session-turn-coordinator.js +4 -2
  132. package/dist/core/skill/index-generator.js +2 -16
  133. package/dist/core/task/task-recovery-service.js +15 -2
  134. package/dist/core/thread/command-parser.js +28 -13
  135. package/dist/core/thread/index.js +1 -0
  136. package/dist/core/thread/ipc-operation.js +11 -8
  137. package/dist/core/thread/thread-operation.js +79 -44
  138. package/dist/core/thread/thread-route.js +54 -6
  139. package/dist/core/thread/thread-service.js +236 -45
  140. package/dist/core/thread/topic-id.js +248 -0
  141. package/dist/eck/kit-renderer.js +1 -1
  142. package/dist/eck/message-renderer.js +12 -2
  143. package/dist/index.js +50 -27
  144. package/dist/ipc.js +59 -6
  145. package/dist/paths.js +1 -0
  146. package/dist/product.js +1 -0
  147. package/dist/response-system/engines/v1/proactive-flow.js +10 -3
  148. package/dist/stats/db.js +8 -0
  149. package/dist/stats/writer.js +26 -4
  150. package/dist/trigger/authorization.js +48 -3
  151. package/dist/trigger/feedback.js +28 -10
  152. package/dist/trigger/ipc-service.js +86 -10
  153. package/dist/trigger/local-files.js +55 -23
  154. package/dist/trigger/scheduler.js +57 -11
  155. package/dist/trigger/system-definitions.js +1 -0
  156. package/dist/trigger/validation.js +44 -15
  157. package/dist/utils/aid-bind.js +17 -297
  158. package/dist/utils/avatar-upload.js +16 -4
  159. package/dist/utils/cross-platform.js +115 -36
  160. package/dist/utils/ecweb-supervisor.js +1 -1
  161. package/dist/utils/ecweb-utils.js +8 -1
  162. package/dist/utils/error-utils.js +44 -6
  163. package/dist/utils/instance-registry.js +12 -2
  164. package/dist/utils/npm-ops.js +102 -27
  165. package/dist/utils/package-version-info.js +72 -27
  166. package/dist/utils/restart-safety.js +50 -0
  167. package/dist/utils/upgrade-jobs.js +304 -0
  168. package/dist/utils/windows-autostart.js +17 -10
  169. package/kits/docs/INDEX.md +5 -0
  170. package/kits/docs/aun/ABILITY_CALLING.md +239 -0
  171. package/kits/docs/evolcore/INDEX.md +14 -3
  172. package/kits/docs/evolcore/agent-create.md +55 -141
  173. package/kits/docs/evolcore/agent.md +38 -16
  174. package/kits/docs/evolcore/aid.md +34 -0
  175. package/kits/docs/evolcore/config.md +20 -1
  176. package/kits/docs/evolcore/event.md +2 -2
  177. package/kits/docs/evolcore/fs.md +18 -2
  178. package/kits/docs/evolcore/group-collaboration.md +13 -246
  179. package/kits/docs/evolcore/group.md +2 -1
  180. package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
  181. package/kits/docs/evolcore/menu-protocol.md +65 -1
  182. package/kits/docs/evolcore/msg.md +64 -2
  183. package/kits/docs/evolcore/skill.md +86 -0
  184. package/kits/docs/evolcore/stats.md +6 -0
  185. package/kits/docs/evolcore/task.md +2 -0
  186. package/kits/docs/evolcore/thread-chain.md +145 -51
  187. package/kits/docs/evolcore/trigger.md +48 -3
  188. package/kits/eck_manifest.json +21 -0
  189. package/kits/eck_message_manifest.json +4 -4
  190. package/kits/migrations/migrate-role-config-v5.mjs +20 -7
  191. package/kits/rules/01-overview.md +2 -1
  192. package/kits/rules/03-identity.md +3 -1
  193. package/kits/rules/04-relation.md +1 -1
  194. package/kits/rules/07-memory-and-thread.md +1 -1
  195. package/kits/schemas/_meta.json +16 -1
  196. package/kits/schemas/agent-config.schema.13.json +44 -2
  197. package/kits/schemas/contact-book.schema.3.json +5 -0
  198. package/kits/schemas/daemon.schema.6.json +3 -1
  199. package/kits/schemas/daemon.schema.8.json +329 -0
  200. package/kits/schemas/defaults.schema.5.json +39 -3
  201. package/kits/schemas/providers.schema.1.json +76 -0
  202. package/kits/schemas/relation-config.schema.9.json +5 -1
  203. package/kits/skills/.switches.json +28 -0
  204. package/kits/skills/aid-ability/SKILL.md +214 -0
  205. package/kits/skills/ec-agent-create/SKILL.md +162 -0
  206. package/kits/skills/ec-group/SKILL.md +2 -1
  207. package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
  208. package/kits/skills/ec-identity/SKILL.md +143 -0
  209. package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
  210. package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
  211. package/kits/skills/ec-trigger/SKILL.md +167 -0
  212. package/kits/skills/ec-trigger/references/examples.md +153 -0
  213. package/kits/skills/thread-ops/SKILL.md +108 -420
  214. package/kits/templates/message-fragments/thread-context.md +1 -2
  215. package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
  216. package/kits/templates/roles/member.json +1 -1
  217. package/kits/templates/roles/visitor.json +1 -0
  218. package/kits/templates/system-fragments/bootstrap.md +1 -0
  219. package/kits/templates/system-fragments/channel.md +2 -1
  220. package/kits/templates/system-fragments/commands.md +5 -4
  221. package/kits/templates/system-fragments/identity.md +2 -0
  222. package/kits/templates/system-fragments/session.md +1 -1
  223. package/kits/templates/system-fragments/venue.md +1 -1
  224. package/package.json +4 -3
@@ -6,10 +6,14 @@ import { parseMenuClientArgs } from '../../cli/menu-client.js';
6
6
  import { ensureSessionRuntimeDir } from '../../cli/task-context.js';
7
7
  import { renderCommandCardAsText } from '../interaction-router.js';
8
8
  import { buildEnvelope, deliveryTargetFromSession, isInteractionSendAccepted, replyContextFromSession, sendInteractionPayload } from '../message/message-utils.js';
9
+ import { sessionTopicThreadId } from '../thread/topic-id.js';
9
10
  import { resolvePaths, getPackageRoot } from '../../paths.js';
10
11
  import { loadDaemonConfig } from '../../config-store.js';
12
+ import { parseOwnershipCommand, executeOwnershipCommand, ownershipFailure } from '../../aun/aid/ownership-operation.js';
13
+ import { ownerResolver, refreshOwnershipForAuthorization } from '../ownership/owner-resolver.js';
11
14
  import { logger } from '../../utils/logger.js';
12
15
  import { resolvePeerRoleDetail, roleToSessionIdentity } from '../../config/peer-role-resolver.js';
16
+ import { ThreadService } from '../thread/thread-service.js';
13
17
  import { isManagementRole } from '../../config/builtin-roles.js';
14
18
  import { formatPeerKey } from '../relation/peer-identity.js';
15
19
  import { sameGroupAid } from '../../aun/group/index.js';
@@ -22,12 +26,13 @@ import { parseDuration, parseTriggerSet, parseTriggerUpdate } from '../../trigge
22
26
  import { applyTriggerPatch, replaceEditableTriggerDefinition } from '../../trigger/patch.js';
23
27
  import { definitionRevision } from '../../trigger/validation.js';
24
28
  import { isDaemonEventSessionTrigger, matchTriggerEvent } from '../../trigger/event-source.js';
29
+ import { isTriggerOperator } from '../../trigger/authorization.js';
25
30
  import { tryParseChannelKey } from '../channel-loader.js';
26
31
  import { displaySessionTitle } from '../session/session-title.js';
27
32
  import { buildTranscriptTurns } from '../session/session-transcript.js';
28
33
  import { resolveConfigCommand } from '../../config/resolved-config-op.js';
29
34
  import { AUTHENTICATED_CONFIG_ROLE_ENV, executeResolvedConfigCommandAsync } from '../../config/config-operation-service.js';
30
- import { splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
35
+ import { parseTimeArg, TIME_ARG_FORMATS, splitConfigBatchGetArgv } from '../../cli/cli-argv.js';
31
36
  import { auditCommandAuthorization, auditFullAccessEvent, hashArgv } from '../auth/authorization-audit.js';
32
37
  import { ResponseModeRegistry } from '../../response-system/registry.js';
33
38
  import { registerBuiltinModes } from '../../response-system/modes/index.js';
@@ -36,7 +41,7 @@ import { hasTrustedFullAccessDelegation, trustedExecutionIdentityInput } from '.
36
41
  import { executeManagedAidOperation, ManagedAidOperationError, managedAidShowViewForRole, resolveManagedAidOperation, } from '../../aun/aid/managed-operation.js';
37
42
  import { MAX_AVATAR_SOURCE_BYTES } from '../../utils/avatar-upload.js';
38
43
  import { executeManagedGroupOperation, isManagedGroupRelationRead, isManagedGroupRelationWrite, ManagedGroupOperationError, resolveManagedGroupOperation, executeManagedMsgOperation, ManagedMsgOperationError, resolveManagedMsgOperation, executeManagedFsOperation, ManagedFsOperationError, resolveManagedFsOperation, } from '../../aun/msg/managed-operation.js';
39
- import { authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
44
+ import { authorizeFreshOperation, buildAuthSubject } from '../auth/auth-gateway.js';
40
45
  import { isHClassPath, isLClassPath, isSameOrDescendant } from '../permission/protected-paths.js';
41
46
  import { validateModelSelectionForRole } from '../model/model-permission.js';
42
47
  import { getModelInfo } from '../model/model-catalog.js';
@@ -146,6 +151,7 @@ export class CommandHandler {
146
151
  daemonStatusProvider;
147
152
  daemonMonitorProvider;
148
153
  agentDelegationRegistry;
154
+ threadSystemMsgSender;
149
155
  handoffRuntime;
150
156
  diagService;
151
157
  setDiagService(service) { this.diagService = service; }
@@ -163,7 +169,7 @@ export class CommandHandler {
163
169
  const subject = buildAuthSubject({ selfAid: grant.selfAid, actorId: grant.actorId, channel: grant.channel,
164
170
  channelType: grant.channelType, channelId, chatType: grant.chatType, conversationId: channelId,
165
171
  processOwners: [], ...trustedExecutionIdentityInput(grant) });
166
- const decision = authorizeOperation({ source: 'agent-tool', subject,
172
+ const decision = await authorizeFreshOperation({ source: 'agent-tool', subject,
167
173
  intent: { operation: 'ec.menu.request', scope: 'relation', source: 'agent-tool', args: { self: grant.selfAid, peer: argv[2], targetId: argv[2] } },
168
174
  auditMetadata: { taskId: grant.taskId, sessionId, messageId: grant.messageId } });
169
175
  if (!decision.allow)
@@ -325,7 +331,10 @@ export class CommandHandler {
325
331
  this.daemonTriggerChannel = String(channel || 'daemon').trim() || 'daemon';
326
332
  }
327
333
  /** Used by control-menu routing; daemon owner is not in the Agent registry. */
328
- getDaemonTriggerOwnerAid(channel = 'daemon') {
334
+ getDaemonTriggerChannel() {
335
+ return this.daemonTriggerChannel;
336
+ }
337
+ getDaemonTriggerOwnerAid(channel = this.daemonTriggerChannel) {
329
338
  if (channel !== this.daemonTriggerChannel)
330
339
  return undefined;
331
340
  if (this.daemonTriggerOwnerAid)
@@ -398,7 +407,7 @@ export class CommandHandler {
398
407
  channelName: session.channel,
399
408
  channelId: session.channelId,
400
409
  chatType: session.chatType,
401
- ...(session.threadId ? { threadId: session.threadId } : {}),
410
+ ...(sessionTopicThreadId(session) ? { threadId: sessionTopicThreadId(session) } : {}),
402
411
  peerId: session.metadata?.peerId || session.channelId,
403
412
  };
404
413
  const valid = enabled.some(definition => definition.source.type === 'event'
@@ -513,7 +522,7 @@ export class CommandHandler {
513
522
  primaryFeedbackTarget(definition) {
514
523
  if (definition.feedback.strategy === 'target')
515
524
  return definition.feedback.target;
516
- if (definition.feedback.strategy === 'event_session')
525
+ if (definition.feedback.strategy !== 'origin')
517
526
  return undefined;
518
527
  if (!definition.origin)
519
528
  return undefined;
@@ -525,10 +534,14 @@ export class CommandHandler {
525
534
  chatType: definition.origin.delivery?.chatType,
526
535
  };
527
536
  }
528
- canAccessTriggerDefinition(definition, peerId, channel, isAdmin) {
537
+ /**
538
+ * 与 IPC 侧同一判据:操作主体集合由定义的 operatorPeers 决定(缺省时按
539
+ * origin 对端 + feedback 目标对端现算)。群聊身份是群 AID,调用方传 channelId。
540
+ */
541
+ canAccessTriggerDefinition(definition, actorConversationPeerId, isAdmin) {
529
542
  if (isAdmin)
530
543
  return true;
531
- return definition.origin?.peerId === peerId && definition.origin?.channelKey === channel;
544
+ return isTriggerOperator(definition, actorConversationPeerId);
532
545
  }
533
546
  triggerPermissionFromParsed(parsed) {
534
547
  return parsed.permissionMode ?? undefined;
@@ -537,7 +550,7 @@ export class CommandHandler {
537
550
  * Persist fullaccess approval provenance as the canonical process owner,
538
551
  * rather than the transport-specific peer id. A Feishu/other-channel
539
552
  * alias may authenticate as a daemon-owner while its raw peer id is not an
540
- * entry in daemon.json.owners; storing that alias would make the Trigger
553
+ * the verified Control AID protocol Owner; storing that alias would make the Trigger
541
554
  * fail its next scheduler-time owner check.
542
555
  */
543
556
  canonicalDaemonOwnerId(selfAid, channel, peerId, chatType) {
@@ -636,11 +649,11 @@ export class CommandHandler {
636
649
  }
637
650
  return undefined;
638
651
  }
639
- findTriggerDefinition(scheduler, nameOrId, peerId, channel, isAdmin) {
652
+ findTriggerDefinition(scheduler, nameOrId, actorConversationPeerId, isAdmin) {
640
653
  return scheduler
641
654
  .list({ all: true })
642
655
  .find(definition => (definition.id === nameOrId || definition.name === nameOrId)
643
- && this.canAccessTriggerDefinition(definition, peerId, channel, isAdmin));
656
+ && this.canAccessTriggerDefinition(definition, actorConversationPeerId, isAdmin));
644
657
  }
645
658
  async buildTriggerDefinitionFromParsed(parsed, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
646
659
  const now = Date.now();
@@ -659,6 +672,7 @@ export class CommandHandler {
659
672
  const targetChannelType = this.resolveChannelType(feedbackChannel);
660
673
  const feedbackChannelId = parsed.targetChannelId ?? originConversationId;
661
674
  const schedulerAid = parsed.agentId
675
+ ?? this.getDaemonTriggerOwnerAid(channel)
662
676
  ?? this.getOwningAgent(feedbackChannel)?.aid
663
677
  ?? tryParseChannelKey(feedbackChannel)?.selfAID
664
678
  ?? this.triggerSchedulerFallbackAid;
@@ -733,14 +747,20 @@ export class CommandHandler {
733
747
  return { error: 'Trigger baseagent 仅适用于 trigger_session' };
734
748
  }
735
749
  }
736
- const triggerOwner = this.agentRegistry?.get?.(schedulerAid) ?? this.getOwningAgent(channel);
750
+ const daemonScheduler = schedulerAid === this.getDaemonTriggerOwnerAid(channel);
751
+ const triggerOwner = daemonScheduler
752
+ ? undefined
753
+ : this.agentRegistry?.get?.(schedulerAid) ?? this.getOwningAgent(channel);
737
754
  const executionBaseagent = parsed.executionType === 'trigger_session'
738
755
  ? parsed.baseagent?.trim() ?? triggerOwner?.baseagent ?? triggerRole.baseagent
756
+ ?? (daemonScheduler ? this.parseDefaultBaseagent() : undefined)
739
757
  : undefined;
740
758
  if (parsed.executionType === 'trigger_session') {
741
759
  if (!executionBaseagent)
742
760
  return { error: '无法确定 Trigger 会话的 baseagent' };
743
- const ownerName = triggerOwner?.name || '<unknown>';
761
+ const ownerName = daemonScheduler
762
+ ? this.primaryRunnerKey.split('::')[0]
763
+ : triggerOwner?.name || '<unknown>';
744
764
  const available = this.getAvailableBaseagentsForOwner(ownerName);
745
765
  if (!available.includes(executionBaseagent)) {
746
766
  return {
@@ -1111,9 +1131,9 @@ export class CommandHandler {
1111
1131
  return !!userId && topic.metadata?.peerId === userId;
1112
1132
  }
1113
1133
  buildTopicMenuItem(s) {
1114
- const displayName = displaySessionTitle(s.name, s.threadId || s.id.slice(0, 8));
1134
+ const displayName = displaySessionTitle(s.name, sessionTopicThreadId(s) || s.id.slice(0, 8));
1115
1135
  const item = {
1116
- value: s.threadId,
1136
+ value: sessionTopicThreadId(s) ?? '',
1117
1137
  label: displayName,
1118
1138
  topicLabel: s.label || null,
1119
1139
  topicStatus: s.topicStatus ?? 'pending',
@@ -1385,10 +1405,12 @@ export class CommandHandler {
1385
1405
  return await handleSlashCommand.call(this, content, channel, channelId, sendMessage, userId, threadId, chatType, source, messageId, selfAID, overrideIdentity, authSubject, replyContext, applyGroupSlashPolicy);
1386
1406
  }
1387
1407
  async handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
1408
+ // 操作主体身份:群聊是群 AID(channelId),私聊是对端 AID(peerId)。
1409
+ const actorConversationPeerId = chatType === 'group' ? channelId : peerId;
1388
1410
  if (content === '/trigger') {
1389
1411
  const visible = scheduler
1390
1412
  .list()
1391
- .filter(definition => this.canAccessTriggerDefinition(definition, peerId, channel, isAdmin));
1413
+ .filter(definition => this.canAccessTriggerDefinition(definition, actorConversationPeerId, isAdmin));
1392
1414
  if (visible.length === 0)
1393
1415
  return '📭 当前没有活跃的触发器';
1394
1416
  const lines = visible.map(definition => {
@@ -1403,7 +1425,7 @@ export class CommandHandler {
1403
1425
  const includeDisabled = sub.includes('--all') || sub.includes('all');
1404
1426
  const visible = scheduler
1405
1427
  .list({ all: includeDisabled })
1406
- .filter(definition => this.canAccessTriggerDefinition(definition, peerId, channel, isAdmin));
1428
+ .filter(definition => this.canAccessTriggerDefinition(definition, actorConversationPeerId, isAdmin));
1407
1429
  if (visible.length === 0)
1408
1430
  return '📭 没有触发器记录';
1409
1431
  const lines = visible.map(definition => {
@@ -1419,7 +1441,7 @@ export class CommandHandler {
1419
1441
  const nameOrId = sub.slice(prefix.length).trim();
1420
1442
  if (!nameOrId)
1421
1443
  throw new Error(`❌ 用法:/trigger ${action} <名称>`);
1422
- const definition = this.findTriggerDefinition(scheduler, nameOrId, peerId, channel, isAdmin);
1444
+ const definition = this.findTriggerDefinition(scheduler, nameOrId, actorConversationPeerId, isAdmin);
1423
1445
  if (!definition) {
1424
1446
  throw new Error(isAdmin
1425
1447
  ? `❌ 未找到触发器:${nameOrId}`
@@ -1528,7 +1550,7 @@ export class CommandHandler {
1528
1550
  : args.trim();
1529
1551
  if (!nameOrId)
1530
1552
  return '❌ 用法:/trigger run <名称> [--dry-run]';
1531
- const definition = this.findTriggerDefinition(scheduler, nameOrId, peerId, channel, isAdmin);
1553
+ const definition = this.findTriggerDefinition(scheduler, nameOrId, actorConversationPeerId, isAdmin);
1532
1554
  if (!definition) {
1533
1555
  return isAdmin
1534
1556
  ? `❌ 未找到触发器:${nameOrId}`
@@ -1548,7 +1570,7 @@ export class CommandHandler {
1548
1570
  const result = parseTriggerUpdate(args);
1549
1571
  if (!result.ok)
1550
1572
  return `❌ ${result.error}`;
1551
- const updated = await this.updateTriggerFromPatch(scheduler, result.nameOrId, result.value, channel, channelId, peerId, isAdmin, messageId, threadId, isDaemonOwner, canApprovePersistentFullAccess);
1573
+ const updated = await this.updateTriggerFromPatch(scheduler, result.nameOrId, result.value, channel, channelId, peerId, isAdmin, messageId, threadId, isDaemonOwner, canApprovePersistentFullAccess, chatType);
1552
1574
  if (!updated.ok)
1553
1575
  return `❌ ${updated.error}`;
1554
1576
  const nextStr = updated.trigger.nextFireAt ? new Date(updated.trigger.nextFireAt).toLocaleString() : '未计算';
@@ -1589,8 +1611,9 @@ export class CommandHandler {
1589
1611
  return '⚠️ 触发器功能未启用';
1590
1612
  return await this.handleTriggerCommand(scheduler, content, channel, channelId, peerId, isAdmin, messageId, chatType, threadId, isDaemonOwner, canApprovePersistentFullAccess);
1591
1613
  }
1592
- async updateTriggerFromPatch(scheduler, nameOrId, patch, channel, channelId, peerId, isAdmin, messageId, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner) {
1593
- const definition = this.findTriggerDefinition(scheduler, nameOrId, peerId, channel, isAdmin);
1614
+ async updateTriggerFromPatch(scheduler, nameOrId, patch, channel, channelId, peerId, isAdmin, messageId, threadId, isDaemonOwner = false, canApprovePersistentFullAccess = isDaemonOwner, chatType) {
1615
+ const actorConversationPeerId = chatType === 'group' ? channelId : peerId;
1616
+ const definition = this.findTriggerDefinition(scheduler, nameOrId, actorConversationPeerId, isAdmin);
1594
1617
  if (!definition) {
1595
1618
  return { ok: false, error: isAdmin ? `未找到触发器:${nameOrId}` : `未找到触发器 "${nameOrId}",或无权限修改` };
1596
1619
  }
@@ -1768,7 +1791,7 @@ export class CommandHandler {
1768
1791
  }
1769
1792
  async handleRewind(session, agent, turnNum, mode) {
1770
1793
  try {
1771
- if (session.threadId && turnNum === 1) {
1794
+ if (sessionTopicThreadId(session) && turnNum === 1) {
1772
1795
  return '❌ 话题会话暂不支持 /rewind 1,仅开放 N>1';
1773
1796
  }
1774
1797
  const messages = await agent.getSessionMessages(session.agentSessionId, session.projectPath);
@@ -1811,10 +1834,11 @@ export class CommandHandler {
1811
1834
  const ok = await agent.rollbackSessionTurns(session.agentSessionId, session.projectPath, discarded, rewindTarget.assistantUuid);
1812
1835
  if (!ok)
1813
1836
  return '❌ 对话回退失败';
1814
- if (session.threadId) {
1837
+ const topicThreadId = sessionTopicThreadId(session);
1838
+ if (topicThreadId) {
1815
1839
  await this.sessionManager.clearTopicResumeAt({
1816
1840
  sessionId: session.id,
1817
- expectedThreadId: session.threadId,
1841
+ expectedThreadId: topicThreadId,
1818
1842
  expectedAgentSessionId: session.agentSessionId,
1819
1843
  });
1820
1844
  }
@@ -1825,10 +1849,11 @@ export class CommandHandler {
1825
1849
  }
1826
1850
  }
1827
1851
  else if (keepTarget) {
1828
- if (session.threadId) {
1852
+ const topicThreadId = sessionTopicThreadId(session);
1853
+ if (topicThreadId) {
1829
1854
  await this.sessionManager.setTopicResumeAt({
1830
1855
  sessionId: session.id,
1831
- expectedThreadId: session.threadId,
1856
+ expectedThreadId: topicThreadId,
1832
1857
  expectedAgentSessionId: session.agentSessionId,
1833
1858
  resumeAt: keepTarget.assistantUuid,
1834
1859
  });
@@ -1876,7 +1901,7 @@ export class CommandHandler {
1876
1901
  buildCtlReplyContext(session) {
1877
1902
  const ctx = { delivery: deliveryTargetFromSession(session) };
1878
1903
  const meta = session.metadata;
1879
- const threadId = session.threadId || meta?.replyContext?.threadId;
1904
+ const threadId = sessionTopicThreadId(session) || meta?.replyContext?.threadId;
1880
1905
  if (threadId)
1881
1906
  ctx.threadId = threadId;
1882
1907
  if (meta?.replyContext?.peerId)
@@ -1969,7 +1994,7 @@ export class CommandHandler {
1969
1994
  channelId: conversationId,
1970
1995
  chatType: grant.chatType,
1971
1996
  conversationId,
1972
- processOwners: loadDaemonConfig().owners ?? [],
1997
+ processOwners: ownerResolver.daemonOwners(),
1973
1998
  ...trustedExecutionIdentityInput(grant),
1974
1999
  });
1975
2000
  const executeOne = async (fieldArgv) => {
@@ -1978,7 +2003,7 @@ export class CommandHandler {
1978
2003
  });
1979
2004
  if (!resolved.ok)
1980
2005
  return { ok: false, code: resolved.code, error: resolved.reason };
1981
- const decision = authorizeOperation({
2006
+ const decision = await authorizeFreshOperation({
1982
2007
  source: 'agent-tool',
1983
2008
  subject,
1984
2009
  intent: {
@@ -2079,10 +2104,10 @@ export class CommandHandler {
2079
2104
  channelId: conversationId,
2080
2105
  chatType: grant.chatType,
2081
2106
  conversationId,
2082
- processOwners: loadDaemonConfig().owners ?? [],
2107
+ processOwners: ownerResolver.daemonOwners(),
2083
2108
  ...trustedExecutionIdentityInput(grant),
2084
2109
  });
2085
- const decision = authorizeOperation({
2110
+ const decision = await authorizeFreshOperation({
2086
2111
  source: 'agent-tool',
2087
2112
  subject,
2088
2113
  intent: {
@@ -2156,6 +2181,56 @@ export class CommandHandler {
2156
2181
  : { ok: false, code: result.code, error: result.error, ...(result.data ? { data: result.data } : {}) };
2157
2182
  }
2158
2183
  /** Agent-managed AID CLI entrypoint. Private key and CA access stay inside the daemon. */
2184
+ async handleOwnershipOperation(argv, sessionId, delegationToken, delegationCommandHash) {
2185
+ const delegation = this.agentDelegationRegistry?.validate(delegationToken, sessionId, delegationCommandHash);
2186
+ if (!delegation?.ok)
2187
+ return { ok: false, code: 'INVALID_DELEGATION', error: 'Valid task delegation is required' };
2188
+ const grant = delegation.grant;
2189
+ if (grant.executionIdentity) {
2190
+ return { ok: false, code: 'NOT_ALLOWED', error: 'Fullaccess execution does not grant protocol Owner binding authority' };
2191
+ }
2192
+ const session = await this.sessionManager.getSessionById(sessionId);
2193
+ if (!session || session.selfAID !== grant.selfAid)
2194
+ return { ok: false, code: 'INVALID_SESSION', error: 'Delegation does not match the session identity' };
2195
+ try {
2196
+ const command = parseOwnershipCommand(argv);
2197
+ const targetAid = command.subjectAid ?? grant.selfAid;
2198
+ ownerResolver.assertLocalSubject(targetAid);
2199
+ if (command.action === 'bind' && ['readonly', 'auto'].includes(grant.permissionMode ?? '')) {
2200
+ return { ok: false, code: 'NOT_ALLOWED', error: 'Binding requires a request or bypass task' };
2201
+ }
2202
+ await refreshOwnershipForAuthorization(targetAid);
2203
+ let conversationId;
2204
+ try {
2205
+ conversationId = parsePeerKey(grant.peerKey).channelId;
2206
+ }
2207
+ catch {
2208
+ return { ok: false, code: 'INVALID_DELEGATION', error: 'Invalid relation key' };
2209
+ }
2210
+ const subject = buildAuthSubject({
2211
+ selfAid: targetAid, actorId: grant.actorId, channel: grant.channel, channelType: grant.channelType,
2212
+ channelId: conversationId, chatType: grant.chatType, conversationId,
2213
+ processOwners: ownerResolver.daemonOwners(),
2214
+ });
2215
+ if (targetAid !== grant.selfAid && !subject.isDaemonOwner) {
2216
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'A different local subject requires the current daemon Owner' };
2217
+ }
2218
+ if (!subject.isDaemonOwner && (command.action === 'bind' ? subject.role !== 'owner' : !isManagementRole(subject.role))) {
2219
+ return { ok: false, code: 'OWNER_REQUIRED', error: 'Ownership binding requires the subject Owner or daemon Owner; first daemon binding uses the local CLI' };
2220
+ }
2221
+ const decision = await authorizeFreshOperation({
2222
+ source: 'agent-tool', subject,
2223
+ intent: { operation: command.action === 'bind' ? 'ec.aid.bind' : 'ec.aid.bind.status', scope: 'agent', source: 'agent-tool', args: { self: targetAid, ...(command.action === 'bind' ? { ownerAid: command.ownerAid } : {}) } },
2224
+ auditMetadata: { taskId: grant.taskId, messageId: grant.messageId },
2225
+ });
2226
+ if (!decision.allow)
2227
+ return { ok: false, code: decision.code, error: decision.reason };
2228
+ return { ok: true, state: await executeOwnershipCommand(command, grant.selfAid) };
2229
+ }
2230
+ catch (error) {
2231
+ return ownershipFailure(error);
2232
+ }
2233
+ }
2159
2234
  async handleAidOperation(argv, sessionId, delegationToken, delegationCommandHash) {
2160
2235
  if (!this.agentDelegationRegistry) {
2161
2236
  return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
@@ -2198,11 +2273,11 @@ export class CommandHandler {
2198
2273
  channelId: conversationId,
2199
2274
  chatType: delegation.grant.chatType,
2200
2275
  conversationId,
2201
- processOwners: loadDaemonConfig().owners ?? [],
2276
+ processOwners: ownerResolver.daemonOwners(),
2202
2277
  ...trustedExecutionIdentityInput(delegation.grant),
2203
2278
  });
2204
2279
  const managementRole = isManagementRole(subject.role);
2205
- const decision = authorizeOperation({
2280
+ const decision = await authorizeFreshOperation({
2206
2281
  source: 'agent-tool',
2207
2282
  subject,
2208
2283
  intent: {
@@ -2349,7 +2424,7 @@ export class CommandHandler {
2349
2424
  channelId: conversationId,
2350
2425
  chatType: grant.chatType,
2351
2426
  conversationId,
2352
- processOwners: loadDaemonConfig().owners ?? [],
2427
+ processOwners: ownerResolver.daemonOwners(),
2353
2428
  ...trustedExecutionIdentityInput(grant),
2354
2429
  });
2355
2430
  const managementRole = isManagementRole(subject.role) || subject.dataScope === 'daemon';
@@ -2363,7 +2438,7 @@ export class CommandHandler {
2363
2438
  && subject.dataScope !== 'daemon' && subject.role !== 'owner') {
2364
2439
  return { ok: false, code: 'ROLE_ACCESS_DENIED', error: `${parsed.command.kind} requires Agent owner permission` };
2365
2440
  }
2366
- const decision = authorizeOperation({
2441
+ const decision = await authorizeFreshOperation({
2367
2442
  source: 'agent-tool',
2368
2443
  subject,
2369
2444
  intent: {
@@ -2459,7 +2534,7 @@ export class CommandHandler {
2459
2534
  try {
2460
2535
  return { ok: true, result: await executeManagedGroupOperation(parsed.command, rulesContent, {
2461
2536
  defaultThread: grant.chatType === 'group' && conversationId === parsed.command.groupId
2462
- ? session.threadId || 'main' : 'all',
2537
+ ? sessionTopicThreadId(session) ?? 'main' : 'all',
2463
2538
  }) };
2464
2539
  }
2465
2540
  catch (error) {
@@ -2580,10 +2655,10 @@ export class CommandHandler {
2580
2655
  chatType: grant.chatType,
2581
2656
  conversationId,
2582
2657
  ...(isSelectorHistory ? { identity: roleToSessionIdentity(grant.issuedRole ?? null) } : {}),
2583
- processOwners: loadDaemonConfig().owners ?? [],
2658
+ processOwners: ownerResolver.daemonOwners(),
2584
2659
  ...trustedExecutionIdentityInput(grant),
2585
2660
  });
2586
- const decision = authorizeOperation({
2661
+ const decision = await authorizeFreshOperation({
2587
2662
  source: 'agent-tool',
2588
2663
  subject,
2589
2664
  intent: {
@@ -2632,7 +2707,7 @@ export class CommandHandler {
2632
2707
  };
2633
2708
  }
2634
2709
  }
2635
- /** Agent-managed current-group filesystem entrypoint. */
2710
+ /** Agent-managed filesystem entrypoint for self, current-group, and current-private-peer reads. */
2636
2711
  async handleFsOperation(argv, sessionId, delegationToken, delegationCommandHash) {
2637
2712
  if (!this.agentDelegationRegistry) {
2638
2713
  return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Task delegation is not configured' };
@@ -2652,9 +2727,21 @@ export class CommandHandler {
2652
2727
  if (sessionSelfAid && sessionSelfAid !== actorAid) {
2653
2728
  return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation self agent does not match the active session' };
2654
2729
  }
2655
- const isPersonalTarget = parsed.command.targetAid === actorAid;
2730
+ const isSelfPersonalTarget = parsed.command.targetAid === actorAid;
2731
+ let currentPrivatePeer;
2656
2732
  let currentGroup;
2657
- if (isPersonalTarget) {
2733
+ if (grant.channelType === 'aun' && grant.chatType === 'private') {
2734
+ try {
2735
+ currentPrivatePeer = parsePeerKey(grant.peerKey).channelId;
2736
+ }
2737
+ catch {
2738
+ return { ok: false, code: 'INVALID_DELEGATION', error: 'Delegation contains an invalid relation key' };
2739
+ }
2740
+ }
2741
+ const isCurrentPrivatePeerCat = parsed.command.kind === 'cat'
2742
+ && currentPrivatePeer !== undefined
2743
+ && parsed.command.targetAid === currentPrivatePeer;
2744
+ if (isSelfPersonalTarget) {
2658
2745
  if (grant.issuedRole !== 'owner' && !hasTrustedFullAccessDelegation(grant)) {
2659
2746
  return { ok: false, code: 'NO_PERMISSION', error: 'Managed personal storage requires the current Agent owner relation' };
2660
2747
  }
@@ -2662,6 +2749,11 @@ export class CommandHandler {
2662
2749
  return { ok: false, code: 'SCOPE_MISMATCH', error: 'Only the current Agent can access its personal storage' };
2663
2750
  }
2664
2751
  }
2752
+ else if (isCurrentPrivatePeerCat) {
2753
+ if (parsed.command.actorAid && parsed.command.actorAid !== actorAid) {
2754
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'Managed fs cannot switch the execution identity with --as' };
2755
+ }
2756
+ }
2665
2757
  else {
2666
2758
  const trustedFullAccess = hasTrustedFullAccessDelegation(grant);
2667
2759
  if (trustedFullAccess) {
@@ -2702,26 +2794,27 @@ export class CommandHandler {
2702
2794
  }
2703
2795
  }
2704
2796
  const resolvedCommand = parsed.command;
2705
- resolvedCommand.storageBackend = isPersonalTarget ? 'personal' : 'group';
2706
- resolvedCommand.actorAid = hasTrustedFullAccessDelegation(grant) && !isPersonalTarget
2797
+ resolvedCommand.storageBackend = isSelfPersonalTarget || isCurrentPrivatePeerCat ? 'personal' : 'group';
2798
+ resolvedCommand.actorAid = hasTrustedFullAccessDelegation(grant) && !isSelfPersonalTarget && !isCurrentPrivatePeerCat
2707
2799
  ? (resolvedCommand.actorAid ?? actorAid)
2708
2800
  : actorAid;
2801
+ const relationTarget = currentPrivatePeer ?? currentGroup;
2709
2802
  const subject = buildAuthSubject({
2710
2803
  selfAid: grant.selfAid,
2711
2804
  actorId: grant.actorId,
2712
2805
  channel: grant.channel,
2713
2806
  channelType: grant.channelType,
2714
- channelId: currentGroup ?? actorAid,
2807
+ channelId: relationTarget ?? actorAid,
2715
2808
  chatType: grant.chatType,
2716
- conversationId: currentGroup ?? actorAid,
2717
- processOwners: loadDaemonConfig().owners ?? [],
2809
+ conversationId: relationTarget ?? actorAid,
2810
+ processOwners: ownerResolver.daemonOwners(),
2718
2811
  ...trustedExecutionIdentityInput(grant),
2719
2812
  });
2720
2813
  const intentArgs = {
2721
2814
  self: actorAid,
2722
2815
  targetId: parsed.command.targetAid,
2723
- peer: currentGroup ?? actorAid,
2724
- peerKey: formatPeerKey('aun', currentGroup ?? actorAid),
2816
+ peer: relationTarget ?? actorAid,
2817
+ peerKey: formatPeerKey('aun', relationTarget ?? actorAid),
2725
2818
  path: parsed.command.remotePath,
2726
2819
  targetPath: parsed.command.remotePath,
2727
2820
  ...(parsed.command.remoteSourcePath ? { sourcePath: parsed.command.remoteSourcePath } : {}),
@@ -2733,12 +2826,12 @@ export class CommandHandler {
2733
2826
  ...(parsed.command.aclPerms ? { aclPerms: parsed.command.aclPerms } : {}),
2734
2827
  ...(parsed.command.aclRemove !== undefined ? { aclRemove: parsed.command.aclRemove } : {}),
2735
2828
  };
2736
- const decision = authorizeOperation({
2829
+ const decision = await authorizeFreshOperation({
2737
2830
  source: 'agent-tool',
2738
2831
  subject,
2739
2832
  intent: {
2740
2833
  operation: parsed.command.operationId,
2741
- scope: isPersonalTarget ? 'agent' : 'relation',
2834
+ scope: isSelfPersonalTarget ? 'agent' : 'relation',
2742
2835
  source: 'agent-tool',
2743
2836
  args: intentArgs,
2744
2837
  },
@@ -2808,6 +2901,68 @@ export class CommandHandler {
2808
2901
  setAgentDelegationRegistry(registry) {
2809
2902
  this.agentDelegationRegistry = registry;
2810
2903
  }
2904
+ /** Inject the daemon-owned system-message transport used by thread operations. */
2905
+ setThreadSystemMsgSender(sender) {
2906
+ this.threadSystemMsgSender = sender;
2907
+ }
2908
+ /** Menu actions share the same transport as CLI/thread operations. */
2909
+ getThreadSystemMsgSender() {
2910
+ return this.threadSystemMsgSender;
2911
+ }
2912
+ async executeThreadSummaryCli(input) {
2913
+ const startedAt = Date.now();
2914
+ try {
2915
+ if (!input.selfAid || input.selfAid !== input.authenticatedSelfAid
2916
+ || !input.peer || !input.threadId) {
2917
+ return {
2918
+ data: {
2919
+ exitCode: 1,
2920
+ stdout: JSON.stringify({ code: 'SCOPE_MISMATCH', message: 'Only the current Agent and a concrete topic can be targeted' }),
2921
+ stderr: '',
2922
+ durationMs: Date.now() - startedAt,
2923
+ },
2924
+ };
2925
+ }
2926
+ const service = new ThreadService(this.sessionManager);
2927
+ const topic = await service.showThread({
2928
+ selfAid: input.selfAid,
2929
+ peer: input.peer,
2930
+ threadId: input.threadId,
2931
+ });
2932
+ const summaries = service.logSummary(input.selfAid, topic.sessionId);
2933
+ const result = {
2934
+ thread: {
2935
+ peer: topic.peer,
2936
+ threadId: topic.threadId,
2937
+ name: topic.name,
2938
+ topicStatus: topic.topicStatus,
2939
+ label: topic.label ?? null,
2940
+ },
2941
+ currentSummary: service.showSummary(input.selfAid, topic.sessionId) ?? null,
2942
+ summaries,
2943
+ };
2944
+ return {
2945
+ data: {
2946
+ exitCode: 0,
2947
+ stdout: JSON.stringify(result),
2948
+ stderr: '',
2949
+ durationMs: Date.now() - startedAt,
2950
+ },
2951
+ };
2952
+ }
2953
+ catch (error) {
2954
+ const code = error?.code ?? 'THREAD_READ_FAILED';
2955
+ const message = error instanceof Error ? error.message : String(error);
2956
+ return {
2957
+ data: {
2958
+ exitCode: 1,
2959
+ stdout: JSON.stringify({ code, message }),
2960
+ stderr: '',
2961
+ durationMs: Date.now() - startedAt,
2962
+ },
2963
+ };
2964
+ }
2965
+ }
2811
2966
  async handleManagedCliOperation(argv, sessionId, grant) {
2812
2967
  const invalid = (error) => ({ ok: false, code: 'INVALID_ARGUMENT', error });
2813
2968
  if (argv[0] === 'ctl') {
@@ -2936,13 +3091,13 @@ export class CommandHandler {
2936
3091
  channelId: conversationId,
2937
3092
  chatType: grant.chatType,
2938
3093
  conversationId,
2939
- processOwners: loadDaemonConfig().owners ?? [],
3094
+ processOwners: ownerResolver.daemonOwners(),
2940
3095
  ...trustedExecutionIdentityInput(grant),
2941
3096
  });
2942
3097
  const ctlIntent = this.resolveCtlIntent(cmd, grantSelfAid, grant.peerKey);
2943
3098
  if (!ctlIntent)
2944
3099
  return { ok: false, code: 'NOT_ALLOWED', error: `不允许的指令: ${inputCmd}` };
2945
- const ctlDecision = authorizeOperation({
3100
+ const ctlDecision = await authorizeFreshOperation({
2946
3101
  source: 'agent-tool',
2947
3102
  subject: ctlSubject,
2948
3103
  intent: ctlIntent,
@@ -3045,7 +3200,7 @@ export class CommandHandler {
3045
3200
  // 6. 调用现有 handle(),不传 sendMessage 回调(结果直接返回)
3046
3201
  try {
3047
3202
  const result = await this._handleInternal(cmd, session.channel, session.channelId, undefined, // 不发送消息
3048
- userId, session.threadId || undefined, session.chatType, undefined, undefined, session.selfAID, ctlIdentity, ctlSubject);
3203
+ userId, sessionTopicThreadId(session), session.chatType, undefined, undefined, session.selfAID, ctlIdentity, ctlSubject);
3049
3204
  const text = typeof result === 'string' ? result : (result && 'text' in result ? result.text : '(无输出)');
3050
3205
  if (result && typeof result === 'object' && 'kind' in result && result.kind === 'command.error') {
3051
3206
  return { ok: false, error: text || '执行失败' };
@@ -3204,9 +3359,9 @@ export class CommandHandler {
3204
3359
  const after = parseQueueTimeArg(this.extractArg(args, '--after'));
3205
3360
  const before = parseQueueTimeArg(this.extractArg(args, '--before'));
3206
3361
  if (this.extractArg(args, '--after') && after === undefined)
3207
- return { ok: false, error: '❌ --after 必须是 epoch 毫秒或 ISO 时间' };
3362
+ return { ok: false, error: `❌ --after 不是有效时间(支持 ${TIME_ARG_FORMATS})` };
3208
3363
  if (this.extractArg(args, '--before') && before === undefined)
3209
- return { ok: false, error: '❌ --before 必须是 epoch 毫秒或 ISO 时间' };
3364
+ return { ok: false, error: `❌ --before 不是有效时间(支持 ${TIME_ARG_FORMATS})` };
3210
3365
  if (after !== undefined && before !== undefined && after >= before)
3211
3366
  return { ok: false, error: '❌ --after 必须早于 --before' };
3212
3367
  // 操作分支
@@ -3238,10 +3393,7 @@ export class CommandHandler {
3238
3393
  }
3239
3394
  }
3240
3395
  function parseQueueTimeArg(value) {
3241
- if (!value)
3242
- return undefined;
3243
- const parsed = /^\d+$/.test(value) ? Number(value) : Date.parse(value);
3244
- return Number.isFinite(parsed) ? parsed : undefined;
3396
+ return parseTimeArg(value);
3245
3397
  }
3246
3398
  /**
3247
3399
  * ctl 专用渲染:不显示 session 标识列(因为只有一个 session)
@@ -195,6 +195,7 @@ function contactItem(self, primaryId) {
195
195
  status: view.status,
196
196
  blocked: view.blocked,
197
197
  isOwner: view.isOwner,
198
+ ...(view.managementRole ? { managementRole: view.managementRole } : {}),
198
199
  aliases: view.aliases,
199
200
  ...(pendingRequest ? {
200
201
  pendingRequest: {