evolcore 0.0.35 → 0.0.36

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 +31 -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 +134 -48
  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 +109 -34
  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
@@ -7,12 +7,26 @@ import { isValidAunDeliveryForChannel } from './outbound.js';
7
7
  import { isHClassPath } from '../permission/protected-paths.js';
8
8
  import { evaluateOutboundContactAdmission } from './message-admission.js';
9
9
  import { normalizeAunMentionEntries, withAunAttention } from '../../aun/msg/mention-schema.js';
10
- import { buildThreadCheckTrigger, resolveManagedSendArgv } from '../../cli/aun-commands.js';
11
- import { readTriggerLocalFile, resolveTriggerLocalPath } from '../../trigger/local-files.js';
10
+ import { resolveManagedSendArgv } from '../../cli/aun-commands.js';
11
+ import { readTriggerLocalFile, resolveAttachmentLocalPath } from '../../trigger/local-files.js';
12
+ import { TriggerDefinitionManager } from '../../trigger/manager.js';
12
13
  import { sameGroupAid } from '../../aun/group/identity.js';
13
14
  import { resolveThreadRouteTarget } from '../thread/thread-route.js';
15
+ import { isTopicThreadId, parseMainTopicId, safeFormatMainTopicId, sessionLocalTopicId } from '../thread/topic-id.js';
14
16
  import { ThreadService } from '../thread/thread-service.js';
15
- export function createAunHandoffSender(sessionManager, channelInstances) {
17
+ import { getAunRequestRegistry, requestTtlSeconds } from './aun-request-registry.js';
18
+ /** payload.debug 只是诊断字段:取不到时照常发送,不带 debug。 */
19
+ async function resolveOutboundDebug(resolver, sessionId) {
20
+ if (!resolver || !sessionId)
21
+ return undefined;
22
+ try {
23
+ return await resolver(sessionId);
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
29
+ export function createAunHandoffSender(sessionManager, channelInstances, getOutboundDebug) {
16
30
  return async (handoff) => {
17
31
  const targetSession = await sessionManager.getSessionById(handoff.target_session_id);
18
32
  if (!targetSession)
@@ -28,7 +42,8 @@ export function createAunHandoffSender(sessionManager, channelInstances) {
28
42
  const log = {
29
43
  content: classified.content,
30
44
  sessionId: targetSession.id,
31
- threadId: targetSession.threadId || '',
45
+ // 消息日志用本端身份(含主话题规范 id),不上线。
46
+ threadId: sessionLocalTopicId(targetSession) ?? '',
32
47
  msgType: classified.msgType,
33
48
  payloadType: classified.payloadType,
34
49
  payloadSummary: classified.payloadSummary,
@@ -39,6 +54,8 @@ export function createAunHandoffSender(sessionManager, channelInstances) {
39
54
  if (!isValidAunDeliveryForChannel(delivery, targetSession.channelId)) {
40
55
  return { ok: false, error: 'handoff is missing a valid immutable AUN delivery route' };
41
56
  }
57
+ // debug 标识写这条消息的会话(发起方),与回复路径一致。
58
+ const debug = await resolveOutboundDebug(getOutboundDebug, handoff.origin_session_id);
42
59
  const result = delivery.chatType === 'group'
43
60
  ? typeof channel.sendDaemonGroupMsg === 'function'
44
61
  ? await channel.sendDaemonGroupMsg({
@@ -46,6 +63,7 @@ export function createAunHandoffSender(sessionManager, channelInstances) {
46
63
  payload: { ...handoff.request.payload, [AUN_HANDOFF_MARKER_FIELD]: handoff.handoff_id },
47
64
  encrypt: handoff.request.encrypt,
48
65
  causation: handoff.causation,
66
+ debug,
49
67
  log,
50
68
  })
51
69
  : { ok: false, error: 'AUN channel does not support daemon group sends' }
@@ -55,6 +73,7 @@ export function createAunHandoffSender(sessionManager, channelInstances) {
55
73
  payload: handoff.request.payload,
56
74
  encrypt: handoff.request.encrypt,
57
75
  causation: handoff.causation,
76
+ debug,
58
77
  log,
59
78
  })
60
79
  : { ok: false, error: 'AUN channel does not support daemon private sends' };
@@ -64,70 +83,94 @@ export function createAunHandoffSender(sessionManager, channelInstances) {
64
83
  async function applyManagedCrossThreadSendEffects(input) {
65
84
  const originSessionId = input.runtime?.sessionId;
66
85
  if (!originSessionId)
67
- return undefined;
68
- const originThreadId = input.runtime?.threadId || 'main';
86
+ return { triggerError: 'origin session context unavailable' };
69
87
  const originPeer = input.runtime?.channelId || input.targetChannelId;
70
- const originChatType = input.runtime?.chatType === 'group' ? 'group' : 'private';
71
- const originChannelKey = input.runtime?.channelKey;
72
- const originChannelId = input.runtime?.channelId;
73
- const originPeerId = input.runtime?.peerId || (originChatType === 'private' ? originChannelId : undefined);
74
- const origin = originChannelKey && originChannelId && originPeerId
75
- ? {
76
- channelKey: originChannelKey,
77
- channelType: input.runtime?.channel || 'aun',
78
- channelId: originChannelId,
79
- session: input.runtime?.threadId ? 'thread' : 'main',
80
- ...(input.runtime?.threadId ? { threadId: input.runtime.threadId } : {}),
81
- peerId: originPeerId,
82
- delivery: originChatType === 'group'
83
- ? { chatType: 'group', groupId: originChannelId }
84
- : { chatType: 'private' },
85
- }
86
- : undefined;
88
+ // 来源话题 id:话题用原 id;主话题用"本端+对端"拼出的规范 id(展示与持久化用)。
89
+ const originThreadId = isTopicThreadId(input.runtime?.threadId)
90
+ ? input.runtime.threadId
91
+ : (safeFormatMainTopicId(input.selfAid, originPeer) ?? '');
92
+ // origin 指向复查目标对端(A),而非触发本次发送的会话对端(C)。
93
+ // authorizeTriggerExecution 根据 origin 对端的角色决定 trigger 能否执行;
94
+ // B 周期性地向 A 发消息,应检查 A 对 B 的角色,而不是 C 对 B 的角色。
95
+ const targetChatType = input.feedbackChatType ?? 'private';
96
+ const origin = {
97
+ channelKey: formatChannelKey({ type: 'aun', selfAID: input.selfAid, name: 'main' }),
98
+ channelType: 'aun',
99
+ channelId: input.targetChannelId,
100
+ session: isTopicThreadId(input.targetThreadId) ? 'thread' : 'main',
101
+ ...(isTopicThreadId(input.targetThreadId) ? { threadId: input.targetThreadId } : {}),
102
+ peerId: input.targetChannelId,
103
+ delivery: targetChatType === 'group'
104
+ ? { chatType: 'group', groupId: input.targetChannelId }
105
+ : { chatType: 'private' },
106
+ };
87
107
  const triggerSeed = `${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
88
108
  const triggerId = `trig_${triggerSeed}`;
89
- const triggerName = `check-${input.targetThreadId}-from-${originThreadId}-${triggerSeed.split('_')[1]}`;
90
- const prompt = [
91
- `【Trigger 复查】话题 ${input.targetThreadId}(对端 ${input.targetChannelId})`,
92
- `这是周期复查:每 ${input.checkEveryLabel} 唤醒一次,直到你显式结束它。`,
93
- '',
94
- input.triggerPromptExtra
95
- || `请检查该话题的最新进度,根据情况:继续等待、调整周期(ec trigger update ${triggerId} --every <时长>)或结束复查(ec trigger cancel ${triggerId})。`,
96
- '',
97
- '参考命令:',
98
- ` ec thread history ${input.targetChannelId}/${input.targetThreadId} --limit 10`,
99
- ` ec thread history ${originPeer}/${originThreadId} --after ${Date.now()}`,
100
- ` ec trigger cancel ${triggerId}`,
101
- ` ec trigger update ${triggerId} --every <时长>`,
102
- `Trigger id:${triggerId}`,
103
- `Trigger 名称:${triggerName}`,
104
- ].join('\n');
109
+ const triggerName = `check-${targetTopicLabel(input.targetThreadId)}-from-${targetTopicLabel(originThreadId)}-${triggerSeed.split('_')[1]}`;
110
+ // 话题名:从索引取,取不到时降级。
111
+ const originEntry = isTopicThreadId(originThreadId)
112
+ ? ThreadService.findThreadById(input.selfAid, originThreadId)
113
+ : ThreadService.findMainSessionEntry(input.selfAid, originPeer);
114
+ const targetEntry = isTopicThreadId(input.targetThreadId)
115
+ ? ThreadService.findThreadById(input.selfAid, input.targetThreadId)
116
+ : ThreadService.findMainSessionEntry(input.selfAid, input.targetChannelId);
117
+ const originThreadName = originEntry?.name || (originThreadId || originPeer);
118
+ const originPeerName = originEntry?.peerName || originPeer;
119
+ const targetThreadName = targetEntry?.name || input.targetThreadId;
120
+ const targetPeerName = targetEntry?.peerName || input.targetChannelId;
121
+ const { buildCrossThreadReviewPrompt } = await import('../../cli/aun-commands.js');
122
+ const prompt = buildCrossThreadReviewPrompt({
123
+ triggerId,
124
+ originThreadId,
125
+ originThreadName,
126
+ originPeerName,
127
+ targetThreadName,
128
+ targetPeerName,
129
+ checkEvery: input.checkEveryLabel,
130
+ firstSentAt: input.timestamp,
131
+ originalText: input.contentSummary,
132
+ extra: input.triggerPromptExtra,
133
+ });
105
134
  let createdTriggerId;
135
+ let triggerError;
106
136
  try {
107
- if (!origin)
108
- throw new Error('managed task runtime context is missing a trusted Trigger origin');
109
137
  const scheduler = input.triggerSchedulers.get(input.selfAid);
110
138
  if (!scheduler)
111
139
  throw new Error(`trigger scheduler not found for agent: ${input.selfAid}`);
112
- const definition = buildThreadCheckTrigger({
113
- agentAid: input.selfAid,
114
- id: triggerId,
115
- name: triggerName,
116
- prompt,
117
- everyMs: input.durationMs,
118
- checkEvery: input.checkEveryLabel,
119
- channelKey: formatChannelKey({ type: 'aun', selfAID: input.selfAid, name: 'main' }),
120
- targetChannelId: input.targetChannelId,
121
- targetThreadId: input.targetThreadId,
122
- originThreadId,
123
- originPeer,
124
- origin,
125
- feedbackChatType: input.feedbackChatType,
126
- });
140
+ const { buildThreadCheckTrigger } = await import('../../cli/aun-commands.js');
141
+ // 触发本次发送的会话对端(C):originPeer 已按 chatType 取对端 AID(私聊 peerId、群聊群 AID)。
142
+ // 即使 origin 已改指向 A,C 也需要保留操作权(cancel/delete/list),
143
+ // 所以显式写入 operatorPeers = [C, A]。
144
+ const operatorPeers = [...new Set([originPeer, input.targetChannelId].filter((p) => !!p))];
145
+ const definition = {
146
+ ...buildThreadCheckTrigger({
147
+ agentAid: input.selfAid,
148
+ id: triggerId,
149
+ name: triggerName,
150
+ prompt,
151
+ everyMs: input.durationMs,
152
+ checkEvery: input.checkEveryLabel,
153
+ channelKey: formatChannelKey({ type: 'aun', selfAID: input.selfAid, name: 'main' }),
154
+ targetChannelId: input.targetChannelId,
155
+ targetThreadId: input.targetThreadId,
156
+ originThreadId,
157
+ originPeer,
158
+ origin,
159
+ feedbackChatType: input.feedbackChatType,
160
+ originalText: input.contentSummary,
161
+ firstSentAt: input.timestamp,
162
+ originThreadName,
163
+ targetThreadName,
164
+ }),
165
+ operatorPeers,
166
+ };
127
167
  createdTriggerId = scheduler.create(definition, [], { enable: true }).id;
128
168
  }
129
169
  catch (error) {
130
- console.warn(`⚠ Trigger 创建失败: ${error instanceof Error ? error.message : String(error)}(inbox 仍会写入)`);
170
+ // 失败原因随发送回执返回给调用方:只打 daemon 日志时,CLI 照样显示"已投递",
171
+ // 复查静默缺失无人察觉(2026-10-20 firstSentAt 类型错误即如此)。
172
+ triggerError = error instanceof Error ? error.message : String(error);
173
+ console.warn(`⚠ Trigger 创建失败: ${triggerError}(inbox 仍会写入)`);
131
174
  }
132
175
  try {
133
176
  ThreadService.appendInbox(input.selfAid, {
@@ -144,9 +187,58 @@ async function applyManagedCrossThreadSendEffects(input) {
144
187
  catch (error) {
145
188
  console.warn(`⚠ 话题 inbox 写入失败: ${error instanceof Error ? error.message : String(error)}`);
146
189
  }
147
- return createdTriggerId;
190
+ return { triggerId: createdTriggerId, triggerError };
191
+ }
192
+ /**
193
+ * Trigger 名称里的话题片段。
194
+ *
195
+ * 名称只用于人读与日志,不参与定位;身份在 labels 的 originThreadId /
196
+ * targetThreadId 里。主话题 id 含两个完整 AID 与冒号,塞进名称又长又难读,
197
+ * 统一缩成 `main`。
198
+ */
199
+ function targetTopicLabel(topicId) {
200
+ if (!topicId)
201
+ return 'unknown';
202
+ const parsed = parseMainTopicId(topicId);
203
+ if (!parsed)
204
+ return topicId;
205
+ return 'main';
148
206
  }
149
- export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime, triggerSchedulers, }) {
207
+ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime, triggerSchedulers, sessionManager, }) {
208
+ ipcServer.setAunRequestGetExecutor(async (params) => {
209
+ if (!params.sessionId || !params.delegationToken) {
210
+ return { ok: false, code: 'DELEGATION_REQUIRED', error: 'Request queries require an active delegated session' };
211
+ }
212
+ const authorization = agentDelegationRegistry.validateForContext(params.delegationToken, params.sessionId, params.delegationCommandHash);
213
+ if (!authorization.ok)
214
+ return { ok: false, code: authorization.code, error: authorization.reason };
215
+ const runtime = responseEngine.getTaskRuntimeContext(params.sessionId);
216
+ if (!runtime || runtime.selfAid !== params.aid || authorization.grant.selfAid !== params.aid
217
+ || runtime.peerKey !== authorization.grant.peerKey) {
218
+ return { ok: false, code: 'SCOPE_MISMATCH', error: 'Request status is limited to the delegated session' };
219
+ }
220
+ const request = getAunRequestRegistry().get(params.aid, params.requestId);
221
+ if (!request?.origin || request.origin.sessionId !== params.sessionId
222
+ || request.origin.peerKey !== authorization.grant.peerKey || request.origin.threadId !== runtime.threadId) {
223
+ return { ok: false, code: 'REQUEST_NOT_FOUND', error: 'Request is not available in this session' };
224
+ }
225
+ const targetRoute = request.origin.targetThreadId
226
+ ? resolveThreadRouteTarget(params.aid, request.origin.targetThreadId, 'msg', request.targetAid)
227
+ : undefined;
228
+ if (request.origin.targetThreadId && !targetRoute) {
229
+ return { ok: false, code: 'REQUEST_NOT_FOUND', error: 'Request target topic is no longer available' };
230
+ }
231
+ const permitted = await authorizeDelegatedAunMsgSend(agentDelegationRegistry, {
232
+ aid: params.aid, to: request.targetAid, scope: 'msg', action: 'history',
233
+ sessionId: params.sessionId, messageId: runtime.messageId,
234
+ delegationToken: params.delegationToken, delegationCommandHash: params.delegationCommandHash,
235
+ threadRouted: !!targetRoute,
236
+ });
237
+ if (!permitted.ok)
238
+ return { ok: false, code: permitted.code, error: permitted.reason };
239
+ return request ? { ok: true, request: request }
240
+ : { ok: false, code: 'REQUEST_NOT_FOUND', error: `request not found: ${params.requestId}` };
241
+ });
150
242
  const sender = async (params) => {
151
243
  let textFile;
152
244
  try {
@@ -162,18 +254,56 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
162
254
  catch (error) {
163
255
  return { ok: false, code: 'INVALID_ARGUMENT', error: error instanceof Error ? error.message : String(error) };
164
256
  }
165
- const runtime = params.originSessionId ? responseEngine.getTaskRuntimeContext(params.originSessionId) : undefined;
257
+ // 活跃任务走内存上下文;调试会话(--as-session)和已结束的会话没有内存记录,
258
+ // 这时从会话本身重建,否则跨话题复查会静默不创建(getTaskRuntimeContext 返回 null)。
259
+ let runtime = params.originSessionId ? responseEngine.getTaskRuntimeContext(params.originSessionId) : undefined;
260
+ if (!runtime && params.originSessionId && sessionManager) {
261
+ try {
262
+ const session = await sessionManager.getSessionById(params.originSessionId);
263
+ if (session) {
264
+ const { buildDebugTaskContext } = await import('../auth/debug-context.js');
265
+ const { ensureSessionRuntimeDir } = await import('../../cli/task-context.js');
266
+ runtime = buildDebugTaskContext({
267
+ session,
268
+ getTrigger: (agentAid, triggerId) => new TriggerDefinitionManager(agentAid).get(triggerId),
269
+ sessionRuntimeDir: ensureSessionRuntimeDir(session.id),
270
+ });
271
+ }
272
+ }
273
+ catch {
274
+ // 重建失败时保持 undefined:不影响消息发送,只是不创建复查
275
+ }
276
+ }
166
277
  const runtimeCausation = runtime?.causation;
167
- const threadRoute = params.thread
168
- ? resolveThreadRouteTarget(params.aid, params.thread, params.scope, params.to)
278
+ // 话题引用解析:`threadRef` 是调用方写的原值(可能是主话题 id),
279
+ // `threadRoute` 非空即"这是跨话题发送"——与话题 id 是否为空无关。
280
+ const threadRef = params.thread;
281
+ const threadRoute = threadRef
282
+ ? resolveThreadRouteTarget(params.aid, threadRef, params.scope, params.to)
169
283
  : undefined;
170
- if (params.thread && !threadRoute) {
171
- return { ok: false, code: 'TOPIC_NOT_FOUND', error: `话题不存在或不属于目标对端: ${params.thread}` };
284
+ if (threadRef && !threadRoute) {
285
+ return { ok: false, code: 'TOPIC_NOT_FOUND', error: `话题不存在或不属于目标对端: ${threadRef}` };
172
286
  }
287
+ const threadRouted = !!threadRoute;
288
+ // 规范目标 id:话题保留原 id;主话题用双方拼出的规范形态(用于 Trigger 标签与通知)。
289
+ const targetTopicId = threadRoute
290
+ ? (isTopicThreadId(threadRef)
291
+ ? threadRef
292
+ : (safeFormatMainTopicId(params.aid, threadRoute.peer) ?? threadRef))
293
+ : undefined;
173
294
  if (params.argv !== undefined && !params.delegationCommandHash) {
174
- return { ok: false, code: 'INVALID_DELEGATION', error: 'Managed AUN sends require an approved command hash' };
295
+ return {
296
+ ok: false,
297
+ code: 'INVALID_DELEGATION',
298
+ detail_code: 'APPROVED_COMMAND_UNAVAILABLE',
299
+ error: 'Managed AUN sends require an approved command hash',
300
+ retryable: false,
301
+ sent: false,
302
+ };
175
303
  }
176
- const delegation = authorizeDelegatedAunMsgSend(agentDelegationRegistry, {
304
+ const isSystemEvent = params.trustedSystemEvent === true
305
+ && params.payload?.type === 'event' && params.payload.event === 'topic.renamed';
306
+ const delegation = await authorizeDelegatedAunMsgSend(agentDelegationRegistry, {
177
307
  delegationToken: params.delegationToken,
178
308
  delegationCommandHash: params.delegationCommandHash,
179
309
  sessionId: params.originSessionId,
@@ -183,13 +313,23 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
183
313
  scope: params.scope,
184
314
  action: params.file ? 'file' : 'send',
185
315
  threadRouted: !!threadRoute,
316
+ isSystemEvent,
186
317
  });
187
318
  if (!delegation.ok) {
188
- return { ok: false, error: delegation.reason, code: delegation.code };
319
+ return {
320
+ ok: false,
321
+ error: delegation.reason,
322
+ code: delegation.code,
323
+ ...(delegation.detailCode ? { detail_code: delegation.detailCode } : {}),
324
+ ...(delegation.retryable === false ? { retryable: false } : {}),
325
+ ...(delegation.sent === false ? { sent: false } : {}),
326
+ };
189
327
  }
190
328
  try {
191
- const fileScope = runtime?.projectPath ? { selfAid: delegation.grant.selfAid, projectPath: runtime.projectPath,
192
- sessionRuntimeDir: delegation.grant.sessionRuntimeDir, cwd: runtime.projectPath } : undefined;
329
+ const fileScope = runtime?.projectPath && delegation.grant
330
+ ? { selfAid: delegation.grant.selfAid, projectPath: runtime.projectPath,
331
+ sessionRuntimeDir: delegation.grant.sessionRuntimeDir, cwd: runtime.projectPath }
332
+ : undefined;
193
333
  if ((textFile || params.file) && !fileScope)
194
334
  throw new Error('Current task project context is unavailable');
195
335
  if (textFile) {
@@ -198,14 +338,15 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
198
338
  throw new Error('Message file must contain nonempty UTF-8 text');
199
339
  params.payload = { type: 'text', text };
200
340
  }
201
- if (params.file)
202
- params.file = { ...params.file, filePath: resolveTriggerLocalPath(params.file.filePath, fileScope) };
203
- if (params.payload)
341
+ if (params.file) {
342
+ params.file = { ...params.file, filePath: resolveAttachmentLocalPath(params.file.filePath, fileScope) };
343
+ }
344
+ if (params.payload && params.rawPayload !== true)
204
345
  params.payload = withAunAttention(params.payload);
205
346
  const currentRelation = runtime?.channel === 'aun' && runtime.selfAid === params.aid
206
347
  && runtime.chatType === (params.scope === 'group' ? 'group' : 'private')
207
348
  && !!runtime.channelId && (params.scope === 'group' ? sameGroupAid(runtime.channelId, params.to) : runtime.channelId === params.to);
208
- params.thread = typeof params.payload?.thread_id === 'string' && params.payload.thread_id.trim()
349
+ params.thread = params.rawPayload === true ? params.thread : typeof params.payload?.thread_id === 'string' && params.payload.thread_id.trim()
209
350
  ? params.payload.thread_id.trim() : params.thread ?? (currentRelation ? runtime?.threadId : undefined);
210
351
  }
211
352
  catch (error) {
@@ -286,7 +427,7 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
286
427
  };
287
428
  }
288
429
  try {
289
- if (Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
430
+ if (params.rawPayload !== true && Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
290
431
  payload = { ...payload, mentions: normalizeAunMentionEntries(payload.mentions) };
291
432
  }
292
433
  if (params.scope === 'group' && params.mentions !== undefined) {
@@ -303,11 +444,43 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
303
444
  code: error?.code ?? 'INVALID_MENTION_SCHEMA',
304
445
  };
305
446
  }
306
- if (params.thread && typeof payload.thread_id !== 'string') {
447
+ if (params.rawPayload !== true && isTopicThreadId(params.thread) && typeof payload.thread_id !== 'string') {
307
448
  payload = { ...payload, thread_id: params.thread };
308
449
  }
450
+ let preparedRequest;
451
+ let requestTtl;
452
+ if (params.scope === 'msg' && params.returnPolicy === 'required') {
453
+ try {
454
+ requestTtl = requestTtlSeconds(params.timeoutMs);
455
+ }
456
+ catch (error) {
457
+ return {
458
+ ok: false,
459
+ code: 'INVALID_REQUEST_TTL',
460
+ error: error instanceof Error ? error.message : String(error),
461
+ };
462
+ }
463
+ try {
464
+ preparedRequest = getAunRequestRegistry().prepare({
465
+ selfAid: params.aid,
466
+ targetAid: params.to,
467
+ payload,
468
+ timeoutMs: params.timeoutMs ?? 60_000,
469
+ ...(runtime && delegation.grant ? { origin: {
470
+ sessionId: params.originSessionId, peerKey: delegation.grant.peerKey,
471
+ threadId: runtime.threadId, targetThreadId: threadRoute ? params.thread : undefined,
472
+ } } : {}),
473
+ });
474
+ }
475
+ catch (error) {
476
+ return { ok: false, code: error?.code ?? 'REQUEST_REGISTRATION_FAILED',
477
+ error: error instanceof Error ? error.message : String(error) };
478
+ }
479
+ payload = preparedRequest.payload;
480
+ getAunRequestRegistry().markSending(params.aid, preparedRequest.requestId);
481
+ }
309
482
  let targetSessionId;
310
- if (params.originSessionId && params.originMessageId) {
483
+ if (params.originSessionId && params.originMessageId && params.returnPolicy !== 'required') {
311
484
  try {
312
485
  const created = await handoffRuntime.createOutbound({
313
486
  selfAid: params.aid,
@@ -320,6 +493,9 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
320
493
  targetChatType: params.scope === 'group' ? 'group' : 'private',
321
494
  explicitReturnPolicy: params.returnPolicy,
322
495
  handoffContext: params.handoffContext,
496
+ // 跨话题发送的目标侧上下文由话题链(thread-inbox)负责注入;
497
+ // handoff 只管投递,回复时不再拼"本地发言"段。
498
+ threadRouted: threadRouted && params.checkEveryMs !== undefined,
323
499
  originAuthorization: {
324
500
  actorId: delegation.grant.actorId,
325
501
  channelKey: delegation.grant.channel,
@@ -339,24 +515,28 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
339
515
  handoff_id: created.handoff.handoff_id,
340
516
  target_session_id: created.targetSession.id,
341
517
  };
342
- if (params.thread && params.checkEveryMs !== undefined && response.message_id && threadRoute) {
343
- const triggerId = await applyManagedCrossThreadSendEffects({
518
+ // 排队未投递(无 message_id)时也要写 inbox + 建复查:handoff 已不再
519
+ // 承载目标侧上下文,跳过这里就会丢上下文。
520
+ if (threadRouted && params.checkEveryMs !== undefined && threadRoute) {
521
+ const effects = await applyManagedCrossThreadSendEffects({
344
522
  triggerSchedulers,
345
523
  runtime,
346
524
  selfAid: params.aid,
347
- targetThreadId: params.thread,
525
+ targetThreadId: targetTopicId ?? params.thread ?? "",
348
526
  targetSessionId: threadRoute.sessionId,
349
527
  targetChannelId: params.to,
350
528
  feedbackChatType: params.scope === 'group' ? 'group' : 'private',
351
- messageId: response.message_id,
529
+ messageId: response.message_id ?? created.handoff.handoff_id,
352
530
  contentSummary: params.log?.content ?? classifyAunPayloadForLog(payload).content,
353
531
  timestamp: response.timestamp ?? Date.now(),
354
532
  durationMs: params.checkEveryMs,
355
533
  checkEveryLabel: params.checkEvery ?? '5m',
356
534
  triggerPromptExtra: params.triggerPromptExtra,
357
535
  });
358
- if (triggerId)
359
- response.trigger_id = triggerId;
536
+ if (effects.triggerId)
537
+ response.trigger_id = effects.triggerId;
538
+ if (effects.triggerError)
539
+ response.trigger_error = effects.triggerError;
360
540
  }
361
541
  return response;
362
542
  }
@@ -376,6 +556,7 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
376
556
  };
377
557
  }
378
558
  }
559
+ const debug = await resolveOutboundDebug(sessionId => responseEngine.getOutboundDebug(sessionId), params.originSessionId);
379
560
  if (params.scope === 'group') {
380
561
  if (typeof ch.sendDaemonGroupMsg !== 'function') {
381
562
  return { ok: false, error: 'AUN channel does not support daemon group sends', code: 'AUN_GROUP_UNSUPPORTED' };
@@ -385,10 +566,11 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
385
566
  payload,
386
567
  mentions: params.mentions,
387
568
  encrypt,
569
+ debug,
388
570
  log: params.log ? { ...params.log, sessionId: targetSessionId ?? params.log.sessionId } : undefined,
389
571
  });
390
572
  if (params.thread && params.checkEveryMs !== undefined && sent.ok && sent.message_id && threadRoute) {
391
- const triggerId = await applyManagedCrossThreadSendEffects({
573
+ const effects = await applyManagedCrossThreadSendEffects({
392
574
  triggerSchedulers,
393
575
  runtime,
394
576
  selfAid: params.aid,
@@ -403,8 +585,10 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
403
585
  checkEveryLabel: params.checkEvery ?? '5m',
404
586
  triggerPromptExtra: params.triggerPromptExtra,
405
587
  });
406
- if (triggerId)
407
- sent.trigger_id = triggerId;
588
+ if (effects.triggerId)
589
+ sent.trigger_id = effects.triggerId;
590
+ if (effects.triggerError)
591
+ sent.trigger_error = effects.triggerError;
408
592
  }
409
593
  return sent;
410
594
  }
@@ -414,12 +598,34 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
414
598
  const sent = await ch.sendDaemonMsg({
415
599
  to: params.to,
416
600
  payload,
601
+ rawPayload: params.rawPayload,
602
+ ttl: requestTtl,
417
603
  encrypt,
418
604
  causation: runtimeCausation,
605
+ debug,
419
606
  log: params.log ? { ...params.log, sessionId: targetSessionId ?? params.log.sessionId } : undefined,
420
607
  });
608
+ if (preparedRequest) {
609
+ if (!sent.ok || !sent.message_id) {
610
+ const transportError = sent.error || 'transport send failed';
611
+ const request = getAunRequestRegistry().markFailed(params.aid, preparedRequest.requestId, transportError);
612
+ if (request?.state === 'completed') {
613
+ return { ok: true, request_id: preparedRequest.requestId, request_state: request.state,
614
+ response: request.responsePayload, transport_error: transportError, retryable: false, sent: true };
615
+ }
616
+ return { ...sent, ok: false, request_id: preparedRequest.requestId, request_state: request?.state ?? 'failed' };
617
+ }
618
+ getAunRequestRegistry().markTransportAccepted(params.aid, preparedRequest.requestId, sent.message_id, sent.status);
619
+ const completed = await getAunRequestRegistry().wait(params.aid, preparedRequest.requestId, params.timeoutMs ?? 60_000);
620
+ return {
621
+ ...sent,
622
+ request_id: preparedRequest.requestId,
623
+ request_state: completed?.state ?? 'unknown',
624
+ ...(completed?.responsePayload ? { response: completed.responsePayload } : {}),
625
+ };
626
+ }
421
627
  if (params.thread && params.checkEveryMs !== undefined && sent.ok && sent.message_id && threadRoute) {
422
- const triggerId = await applyManagedCrossThreadSendEffects({
628
+ const effects = await applyManagedCrossThreadSendEffects({
423
629
  triggerSchedulers,
424
630
  runtime,
425
631
  selfAid: params.aid,
@@ -434,8 +640,10 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
434
640
  checkEveryLabel: params.checkEvery ?? '5m',
435
641
  triggerPromptExtra: params.triggerPromptExtra,
436
642
  });
437
- if (triggerId)
438
- sent.trigger_id = triggerId;
643
+ if (effects.triggerId)
644
+ sent.trigger_id = effects.triggerId;
645
+ if (effects.triggerError)
646
+ sent.trigger_error = effects.triggerError;
439
647
  }
440
648
  return sent;
441
649
  };