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.
- package/CHANGELOG.md +35 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +51 -24
- package/dist/agents/ecagent-runner.js +9 -13
- package/dist/agents/gemini-runner.js +4 -0
- package/dist/aun/aid/store.js +0 -81
- package/dist/aun/group/fs-download.js +1 -10
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/managed-operation.js +130 -32
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/aun/service-proxy.js +12 -18
- package/dist/aun/storage/index.js +29 -0
- package/dist/channels/aun.js +38 -137
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +64 -31
- package/dist/cli/agent-create.js +43 -24
- package/dist/cli/agent.js +16 -70
- package/dist/cli/aun-commands.js +512 -32
- package/dist/cli/contact.js +8 -2
- package/dist/cli/ctl-command.js +1 -2
- package/dist/cli/daemon-commands.js +82 -13
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/diag.js +2 -2
- package/dist/cli/handoff-command.js +18 -5
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +93 -12
- package/dist/cli/model.js +14 -138
- package/dist/cli/queue-command.js +51 -0
- package/dist/cli/response.js +5 -54
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +85 -3
- package/dist/cli/stats.js +10 -4
- package/dist/cli/task-command.js +1 -0
- package/dist/cli/task-context.js +16 -0
- package/dist/cli/thread-command.js +620 -0
- package/dist/cli/trigger-command.js +76 -50
- package/dist/config/builtin-roles.js +9 -7
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/agent-ipc-registration.js +65 -22
- package/dist/core/auth/agent-delegation.js +42 -2
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/delegation-authorization.js +1 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +44 -3
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/command-handler.js +224 -45
- package/dist/core/command/diag-service.js +5 -1
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/ipc-registration.js +38 -0
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +210 -9
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +207 -8
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +131 -3
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +379 -69
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +126 -7
- package/dist/core/permission/sandbox-runtime.js +14 -1
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/task/task-recovery-service.js +2 -0
- package/dist/core/task/task-resume-operation.js +11 -0
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +10 -7
- package/dist/ipc.js +234 -17
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +59 -29
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/ipc-service.js +49 -35
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/script-sandbox.js +58 -77
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/AUTHORING.md +1 -1
- package/kits/docs/channels/aun.md +8 -0
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +3 -2
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/evolcore/trigger.md +2 -2
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/01-overview.md +0 -9
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +7 -1
- package/kits/rules/07-memory-and-thread.md +202 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +66 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +2 -1
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
1
2
|
import { authorizeDelegatedAunMsgSend, } from '../auth/agent-delegation.js';
|
|
2
|
-
import { tryParseChannelKey } from '../channel-loader.js';
|
|
3
|
+
import { formatChannelKey, tryParseChannelKey } from '../channel-loader.js';
|
|
3
4
|
import { AUN_HANDOFF_MARKER_FIELD } from '../../channels/aun.js';
|
|
4
5
|
import { classifyAunPayloadForLog } from './message-log.js';
|
|
5
6
|
import { isValidAunDeliveryForChannel } from './outbound.js';
|
|
6
7
|
import { isHClassPath } from '../permission/protected-paths.js';
|
|
7
8
|
import { evaluateOutboundContactAdmission } from './message-admission.js';
|
|
8
|
-
import { normalizeAunMentionEntries } from '../../aun/msg/mention-schema.js';
|
|
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';
|
|
12
|
+
import { sameGroupAid } from '../../aun/group/identity.js';
|
|
13
|
+
import { resolveThreadRouteTarget } from '../thread/thread-route.js';
|
|
14
|
+
import { ThreadService } from '../thread/thread-service.js';
|
|
9
15
|
export function createAunHandoffSender(sessionManager, channelInstances) {
|
|
10
16
|
return async (handoff) => {
|
|
11
17
|
const targetSession = await sessionManager.getSessionById(handoff.target_session_id);
|
|
@@ -55,11 +61,118 @@ export function createAunHandoffSender(sessionManager, channelInstances) {
|
|
|
55
61
|
return { ok: result.ok, message_id: result.message_id, error: result.error };
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
|
-
|
|
64
|
+
async function applyManagedCrossThreadSendEffects(input) {
|
|
65
|
+
const originSessionId = input.runtime?.sessionId;
|
|
66
|
+
if (!originSessionId)
|
|
67
|
+
return undefined;
|
|
68
|
+
const originThreadId = input.runtime?.threadId || 'main';
|
|
69
|
+
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;
|
|
87
|
+
const triggerSeed = `${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
88
|
+
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');
|
|
105
|
+
let createdTriggerId;
|
|
106
|
+
try {
|
|
107
|
+
if (!origin)
|
|
108
|
+
throw new Error('managed task runtime context is missing a trusted Trigger origin');
|
|
109
|
+
const scheduler = input.triggerSchedulers.get(input.selfAid);
|
|
110
|
+
if (!scheduler)
|
|
111
|
+
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
|
+
});
|
|
127
|
+
createdTriggerId = scheduler.create(definition, [], { enable: true }).id;
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.warn(`⚠ Trigger 创建失败: ${error instanceof Error ? error.message : String(error)}(inbox 仍会写入)`);
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
ThreadService.appendInbox(input.selfAid, {
|
|
134
|
+
messageId: input.messageId,
|
|
135
|
+
content: input.contentSummary,
|
|
136
|
+
timestamp: input.timestamp,
|
|
137
|
+
originThreadId,
|
|
138
|
+
originPeer,
|
|
139
|
+
originSessionId,
|
|
140
|
+
targetSessionId: input.targetSessionId,
|
|
141
|
+
...(createdTriggerId ? { triggerId: createdTriggerId } : {}),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
console.warn(`⚠ 话题 inbox 写入失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
146
|
+
}
|
|
147
|
+
return createdTriggerId;
|
|
148
|
+
}
|
|
149
|
+
export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegationRegistry, responseEngine, handoffRuntime, triggerSchedulers, }) {
|
|
59
150
|
const sender = async (params) => {
|
|
60
|
-
|
|
61
|
-
|
|
151
|
+
let textFile;
|
|
152
|
+
try {
|
|
153
|
+
if (params.argv !== undefined) {
|
|
154
|
+
const command = resolveManagedSendArgv(params.argv);
|
|
155
|
+
textFile = command.textFile;
|
|
156
|
+
params = { ...params, ...command };
|
|
157
|
+
}
|
|
158
|
+
else if (!params.aid || !params.to || !params.scope) {
|
|
159
|
+
return { ok: false, code: 'INVALID_REQUEST', error: 'Managed AUN sends require command parameters' };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return { ok: false, code: 'INVALID_ARGUMENT', error: error instanceof Error ? error.message : String(error) };
|
|
164
|
+
}
|
|
165
|
+
const runtime = params.originSessionId ? responseEngine.getTaskRuntimeContext(params.originSessionId) : undefined;
|
|
166
|
+
const runtimeCausation = runtime?.causation;
|
|
167
|
+
const threadRoute = params.thread
|
|
168
|
+
? resolveThreadRouteTarget(params.aid, params.thread, params.scope, params.to)
|
|
62
169
|
: undefined;
|
|
170
|
+
if (params.thread && !threadRoute) {
|
|
171
|
+
return { ok: false, code: 'TOPIC_NOT_FOUND', error: `话题不存在或不属于目标对端: ${params.thread}` };
|
|
172
|
+
}
|
|
173
|
+
if (params.argv !== undefined && !params.delegationCommandHash) {
|
|
174
|
+
return { ok: false, code: 'INVALID_DELEGATION', error: 'Managed AUN sends require an approved command hash' };
|
|
175
|
+
}
|
|
63
176
|
const delegation = authorizeDelegatedAunMsgSend(agentDelegationRegistry, {
|
|
64
177
|
delegationToken: params.delegationToken,
|
|
65
178
|
delegationCommandHash: params.delegationCommandHash,
|
|
@@ -69,10 +182,35 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
|
|
|
69
182
|
to: params.to,
|
|
70
183
|
scope: params.scope,
|
|
71
184
|
action: params.file ? 'file' : 'send',
|
|
185
|
+
threadRouted: !!threadRoute,
|
|
72
186
|
});
|
|
73
187
|
if (!delegation.ok) {
|
|
74
188
|
return { ok: false, error: delegation.reason, code: delegation.code };
|
|
75
189
|
}
|
|
190
|
+
try {
|
|
191
|
+
const fileScope = runtime?.projectPath ? { selfAid: delegation.grant.selfAid, projectPath: runtime.projectPath,
|
|
192
|
+
sessionRuntimeDir: delegation.grant.sessionRuntimeDir, cwd: runtime.projectPath } : undefined;
|
|
193
|
+
if ((textFile || params.file) && !fileScope)
|
|
194
|
+
throw new Error('Current task project context is unavailable');
|
|
195
|
+
if (textFile) {
|
|
196
|
+
const text = new TextDecoder('utf-8', { fatal: true }).decode(readTriggerLocalFile(textFile, fileScope));
|
|
197
|
+
if (!text || text.includes('\0'))
|
|
198
|
+
throw new Error('Message file must contain nonempty UTF-8 text');
|
|
199
|
+
params.payload = { type: 'text', text };
|
|
200
|
+
}
|
|
201
|
+
if (params.file)
|
|
202
|
+
params.file = { ...params.file, filePath: resolveTriggerLocalPath(params.file.filePath, fileScope) };
|
|
203
|
+
if (params.payload)
|
|
204
|
+
params.payload = withAunAttention(params.payload);
|
|
205
|
+
const currentRelation = runtime?.channel === 'aun' && runtime.selfAid === params.aid
|
|
206
|
+
&& runtime.chatType === (params.scope === 'group' ? 'group' : 'private')
|
|
207
|
+
&& !!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()
|
|
209
|
+
? params.payload.thread_id.trim() : params.thread ?? (currentRelation ? runtime?.threadId : undefined);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
return { ok: false, code: 'INVALID_LOCAL_FILE', error: error instanceof Error ? error.message : String(error) };
|
|
213
|
+
}
|
|
76
214
|
const inst = channelInstances.find((candidate) => {
|
|
77
215
|
if (candidate.channelType !== 'aun')
|
|
78
216
|
return false;
|
|
@@ -170,18 +308,39 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
|
|
|
170
308
|
channelType: delegation.grant.channelType,
|
|
171
309
|
chatType: delegation.grant.chatType,
|
|
172
310
|
peerKey: delegation.grant.peerKey,
|
|
311
|
+
approvalInteractionPolicy: delegation.grant.approvalInteractionPolicy,
|
|
173
312
|
},
|
|
174
313
|
causation: runtimeCausation,
|
|
175
314
|
});
|
|
176
315
|
targetSessionId = created.targetSession.id;
|
|
177
316
|
if (created.crossSession && created.handoff) {
|
|
178
|
-
|
|
317
|
+
const response = {
|
|
179
318
|
ok: true,
|
|
180
319
|
status: created.handoff.state === 'target_sent' ? 'delivered' : 'queued',
|
|
181
320
|
message_id: created.handoff.target_message_id ?? undefined,
|
|
182
321
|
handoff_id: created.handoff.handoff_id,
|
|
183
322
|
target_session_id: created.targetSession.id,
|
|
184
323
|
};
|
|
324
|
+
if (params.thread && params.checkEveryMs !== undefined && response.message_id && threadRoute) {
|
|
325
|
+
const triggerId = await applyManagedCrossThreadSendEffects({
|
|
326
|
+
triggerSchedulers,
|
|
327
|
+
runtime,
|
|
328
|
+
selfAid: params.aid,
|
|
329
|
+
targetThreadId: params.thread,
|
|
330
|
+
targetSessionId: threadRoute.sessionId,
|
|
331
|
+
targetChannelId: params.to,
|
|
332
|
+
feedbackChatType: params.scope === 'group' ? 'group' : 'private',
|
|
333
|
+
messageId: response.message_id,
|
|
334
|
+
contentSummary: params.log?.content ?? classifyAunPayloadForLog(payload).content,
|
|
335
|
+
timestamp: response.timestamp ?? Date.now(),
|
|
336
|
+
durationMs: params.checkEveryMs,
|
|
337
|
+
checkEveryLabel: params.checkEvery ?? '5m',
|
|
338
|
+
triggerPromptExtra: params.triggerPromptExtra,
|
|
339
|
+
});
|
|
340
|
+
if (triggerId)
|
|
341
|
+
response.trigger_id = triggerId;
|
|
342
|
+
}
|
|
343
|
+
return response;
|
|
185
344
|
}
|
|
186
345
|
if (params.scope === 'msg' && !payload.ref_message_id) {
|
|
187
346
|
payload.ref_message_id = params.originMessageId;
|
|
@@ -203,24 +362,64 @@ export function registerAunIpcSender({ ipcServer, channelInstances, agentDelegat
|
|
|
203
362
|
if (typeof ch.sendDaemonGroupMsg !== 'function') {
|
|
204
363
|
return { ok: false, error: 'AUN channel does not support daemon group sends', code: 'AUN_GROUP_UNSUPPORTED' };
|
|
205
364
|
}
|
|
206
|
-
|
|
365
|
+
const sent = await ch.sendDaemonGroupMsg({
|
|
207
366
|
groupId: params.to,
|
|
208
367
|
payload,
|
|
209
368
|
mentions: params.mentions,
|
|
210
369
|
encrypt,
|
|
211
370
|
log: params.log ? { ...params.log, sessionId: targetSessionId ?? params.log.sessionId } : undefined,
|
|
212
371
|
});
|
|
372
|
+
if (params.thread && params.checkEveryMs !== undefined && sent.ok && sent.message_id && threadRoute) {
|
|
373
|
+
const triggerId = await applyManagedCrossThreadSendEffects({
|
|
374
|
+
triggerSchedulers,
|
|
375
|
+
runtime,
|
|
376
|
+
selfAid: params.aid,
|
|
377
|
+
targetThreadId: params.thread,
|
|
378
|
+
targetSessionId: threadRoute.sessionId,
|
|
379
|
+
targetChannelId: params.to,
|
|
380
|
+
feedbackChatType: 'group',
|
|
381
|
+
messageId: sent.message_id,
|
|
382
|
+
contentSummary: params.log?.content ?? classifyAunPayloadForLog(payload).content,
|
|
383
|
+
timestamp: sent.timestamp ?? Date.now(),
|
|
384
|
+
durationMs: params.checkEveryMs,
|
|
385
|
+
checkEveryLabel: params.checkEvery ?? '5m',
|
|
386
|
+
triggerPromptExtra: params.triggerPromptExtra,
|
|
387
|
+
});
|
|
388
|
+
if (triggerId)
|
|
389
|
+
sent.trigger_id = triggerId;
|
|
390
|
+
}
|
|
391
|
+
return sent;
|
|
213
392
|
}
|
|
214
393
|
if (typeof ch.sendDaemonMsg !== 'function') {
|
|
215
394
|
return { ok: false, error: 'AUN channel does not support daemon private sends', code: 'AUN_MSG_UNSUPPORTED' };
|
|
216
395
|
}
|
|
217
|
-
|
|
396
|
+
const sent = await ch.sendDaemonMsg({
|
|
218
397
|
to: params.to,
|
|
219
398
|
payload,
|
|
220
399
|
encrypt,
|
|
221
400
|
causation: runtimeCausation,
|
|
222
401
|
log: params.log ? { ...params.log, sessionId: targetSessionId ?? params.log.sessionId } : undefined,
|
|
223
402
|
});
|
|
403
|
+
if (params.thread && params.checkEveryMs !== undefined && sent.ok && sent.message_id && threadRoute) {
|
|
404
|
+
const triggerId = await applyManagedCrossThreadSendEffects({
|
|
405
|
+
triggerSchedulers,
|
|
406
|
+
runtime,
|
|
407
|
+
selfAid: params.aid,
|
|
408
|
+
targetThreadId: params.thread,
|
|
409
|
+
targetSessionId: threadRoute.sessionId,
|
|
410
|
+
targetChannelId: params.to,
|
|
411
|
+
feedbackChatType: 'private',
|
|
412
|
+
messageId: sent.message_id,
|
|
413
|
+
contentSummary: params.log?.content ?? classifyAunPayloadForLog(payload).content,
|
|
414
|
+
timestamp: sent.timestamp ?? Date.now(),
|
|
415
|
+
durationMs: params.checkEveryMs,
|
|
416
|
+
checkEveryLabel: params.checkEvery ?? '5m',
|
|
417
|
+
triggerPromptExtra: params.triggerPromptExtra,
|
|
418
|
+
});
|
|
419
|
+
if (triggerId)
|
|
420
|
+
sent.trigger_id = triggerId;
|
|
421
|
+
}
|
|
422
|
+
return sent;
|
|
224
423
|
};
|
|
225
424
|
ipcServer.setAunMsgSender(sender);
|
|
226
425
|
}
|
|
@@ -4,6 +4,7 @@ import { getErrorMessage, isContextTooLongText } from '../../utils/error-utils.j
|
|
|
4
4
|
import { tagErrorTextWithEvolcoreVersion } from '../../utils/evolcore-version.js';
|
|
5
5
|
import { DEFAULT_FLUSH_DELAY_MS } from '../../types.js';
|
|
6
6
|
import { classifyAunToolErrorCode, normalizeToolErrorClassification } from '../permission/tool-error-code.js';
|
|
7
|
+
import { foldSubtaskTerminal } from './subtask-tool-projection.js';
|
|
7
8
|
export class IMRenderer {
|
|
8
9
|
opts;
|
|
9
10
|
itemsQueue = [];
|
|
@@ -22,6 +23,16 @@ export class IMRenderer {
|
|
|
22
23
|
syntheticCallSeq = 0;
|
|
23
24
|
currentAssistantUuid;
|
|
24
25
|
currentTextPhase;
|
|
26
|
+
/** 无来源工具时合成的子任务卡片;与 lifecycle 边界一样,不算抑制兜底错误的正文/过程内容。 */
|
|
27
|
+
subtaskItems = new WeakSet();
|
|
28
|
+
/** 本轮已投影的工具调用 call_id → 工具名;子任务只归拢到本轮可见的来源工具卡片。 */
|
|
29
|
+
toolCallNames = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* 归拢到来源工具的子任务:call_id → 状态。
|
|
32
|
+
* running 期间到达的 tool_result(后台占位结果)先扣住,等终态合并后再发;
|
|
33
|
+
* 终态先到(前台)时记下终态,等真实 tool_result 到达时合并。
|
|
34
|
+
*/
|
|
35
|
+
parentSubtasks = new Map();
|
|
25
36
|
constructor(opts) {
|
|
26
37
|
this.opts = opts;
|
|
27
38
|
}
|
|
@@ -66,7 +77,8 @@ export class IMRenderer {
|
|
|
66
77
|
/** 强制刷新所有 pending 事件 */
|
|
67
78
|
async flush(isFinal, attention) {
|
|
68
79
|
if (this.opts.envelope.chatmode === 'proactive') {
|
|
69
|
-
// proactive 是 fire-and-forget,无 pending buffer
|
|
80
|
+
// proactive 是 fire-and-forget,无 pending buffer;只需放出仍被扣住的工具结果
|
|
81
|
+
this.releaseHeldToolResults();
|
|
70
82
|
return;
|
|
71
83
|
}
|
|
72
84
|
return this.flushInternal(isFinal, attention);
|
|
@@ -79,16 +91,24 @@ export class IMRenderer {
|
|
|
79
91
|
}
|
|
80
92
|
/** 是否有 pending 内容 */
|
|
81
93
|
hasContent() {
|
|
82
|
-
return this.textBuffer.length > 0 || this.itemsQueue.some(it => it.kind !== 'text');
|
|
94
|
+
return this.textBuffer.length > 0 || this.itemsQueue.some(it => it.kind !== 'text') || this.hasHeldToolResults();
|
|
83
95
|
}
|
|
84
96
|
/** 是否有会抑制兜底错误消息的正文/过程内容;lifecycle 边界不算。 */
|
|
85
97
|
hasNonLifecycleContent() {
|
|
86
|
-
return this.textBuffer.trim().length > 0 || this.itemsQueue.some(it => {
|
|
98
|
+
return this.textBuffer.trim().length > 0 || this.hasHeldToolResults() || this.itemsQueue.some(it => {
|
|
87
99
|
if (it.kind === 'text')
|
|
88
100
|
return it.text.trim().length > 0;
|
|
101
|
+
if (this.subtaskItems.has(it))
|
|
102
|
+
return false;
|
|
89
103
|
return it.kind !== 'started' && it.kind !== 'completed';
|
|
90
104
|
});
|
|
91
105
|
}
|
|
106
|
+
hasHeldToolResults() {
|
|
107
|
+
for (const state of this.parentSubtasks.values())
|
|
108
|
+
if (state.held)
|
|
109
|
+
return true;
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
92
112
|
/** 是否有待发送的文本 */
|
|
93
113
|
hasTextPending() {
|
|
94
114
|
return this.textBuffer.length > 0;
|
|
@@ -234,6 +254,7 @@ export class IMRenderer {
|
|
|
234
254
|
...(evolCommand ? { evolCommand } : {}),
|
|
235
255
|
...(assistantUuid ? { forkAnchor: { assistantMessageId: assistantUuid } } : {}),
|
|
236
256
|
};
|
|
257
|
+
this.toolCallNames.set(item.call_id, name);
|
|
237
258
|
this.recordActivity(item);
|
|
238
259
|
if (this.suppressActivityItems())
|
|
239
260
|
return;
|
|
@@ -245,7 +266,6 @@ export class IMRenderer {
|
|
|
245
266
|
addToolResult(name, ok, result, error, callId, durationMs, descText, classification, assistantUuid) {
|
|
246
267
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
247
268
|
return;
|
|
248
|
-
this.emitProgress('tool_result', undefined, undefined, { toolName: name, callId, ok, durationMs });
|
|
249
269
|
const normalizedClassification = !ok
|
|
250
270
|
? normalizeToolErrorClassification(classification ?? {})
|
|
251
271
|
: undefined;
|
|
@@ -269,6 +289,22 @@ export class IMRenderer {
|
|
|
269
289
|
...(descText !== undefined && { text: descText }),
|
|
270
290
|
...(assistantUuid ? { forkAnchor: { assistantMessageId: assistantUuid } } : {}),
|
|
271
291
|
};
|
|
292
|
+
const resolved = this.resolveParentToolResult(item);
|
|
293
|
+
if (!resolved)
|
|
294
|
+
return;
|
|
295
|
+
this.deliverToolResult(resolved);
|
|
296
|
+
}
|
|
297
|
+
/** 投递一条工具结果(interactive 入队 / proactive 直发),并记录任务活动。 */
|
|
298
|
+
deliverToolResult(item) {
|
|
299
|
+
if (item.kind !== 'tool_result')
|
|
300
|
+
return;
|
|
301
|
+
if (this.opts.envelope.chatmode === 'proactive') {
|
|
302
|
+
this.emitProactiveItem(item);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
this.emitProgress('tool_result', undefined, undefined, {
|
|
306
|
+
toolName: item.name, callId: item.call_id, ok: item.ok, durationMs: item.duration_ms,
|
|
307
|
+
});
|
|
272
308
|
this.recordActivity(item);
|
|
273
309
|
if (this.suppressActivityItems())
|
|
274
310
|
return;
|
|
@@ -276,6 +312,73 @@ export class IMRenderer {
|
|
|
276
312
|
this.messageTimestamps.push(Date.now());
|
|
277
313
|
this.scheduleFlush();
|
|
278
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* 工具结果与归拢子任务的会合点。
|
|
317
|
+
* - 无归拢子任务:原样放行
|
|
318
|
+
* - 子任务已终态(前台:通知先于真实结果到达):合并终态后放行
|
|
319
|
+
* - 子任务仍在跑(后台:先到的是占位结果):扣住,返回 null,等 endParentSubtask 合并后发出
|
|
320
|
+
*/
|
|
321
|
+
resolveParentToolResult(item) {
|
|
322
|
+
if (item.kind !== 'tool_result')
|
|
323
|
+
return item;
|
|
324
|
+
const state = this.parentSubtasks.get(item.call_id);
|
|
325
|
+
if (!state)
|
|
326
|
+
return item;
|
|
327
|
+
if (state.terminal) {
|
|
328
|
+
this.parentSubtasks.delete(item.call_id);
|
|
329
|
+
return foldSubtaskTerminal(item, state.terminal);
|
|
330
|
+
}
|
|
331
|
+
state.held = item;
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* 子任务开始:若来源工具卡片在本轮可见,登记归拢,不另出卡片。
|
|
336
|
+
* 返回 false 表示无法归拢(无来源工具或卡片不在本轮),调用方应改发独立卡片。
|
|
337
|
+
*/
|
|
338
|
+
beginParentSubtask(toolUseId) {
|
|
339
|
+
if (!toolUseId || !this.toolCallNames.has(toolUseId))
|
|
340
|
+
return false;
|
|
341
|
+
if (!this.parentSubtasks.has(toolUseId))
|
|
342
|
+
this.parentSubtasks.set(toolUseId, {});
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* 子任务终态:合并进来源工具的 tool_result。
|
|
347
|
+
* - 已扣住占位结果(后台):合并后立即发出
|
|
348
|
+
* - 结果尚未到达(前台):记下终态,等结果到达时合并
|
|
349
|
+
* 返回 false 表示本轮没有登记该归拢(卡片已提前释放或不在本轮),调用方应补发一条同 call_id 的终态结果。
|
|
350
|
+
*/
|
|
351
|
+
endParentSubtask(toolUseId, terminal) {
|
|
352
|
+
if (!toolUseId)
|
|
353
|
+
return false;
|
|
354
|
+
const state = this.parentSubtasks.get(toolUseId);
|
|
355
|
+
if (!state)
|
|
356
|
+
return false;
|
|
357
|
+
if (state.held) {
|
|
358
|
+
this.parentSubtasks.delete(toolUseId);
|
|
359
|
+
this.deliverToolResult(foldSubtaskTerminal(state.held, terminal));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
state.terminal = terminal;
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
/** 来源工具的名称(本轮可见时);补发终态结果时用来对齐卡片名。 */
|
|
367
|
+
toolNameForCall(callId) {
|
|
368
|
+
return callId ? this.toolCallNames.get(callId) : undefined;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* 放出仍被扣住的工具结果(不带子任务终态,原样发出)。
|
|
372
|
+
* 轮次结束或出错收尾时调用,避免来源工具卡片停在"执行中"。
|
|
373
|
+
*/
|
|
374
|
+
releaseHeldToolResults() {
|
|
375
|
+
for (const [callId, state] of this.parentSubtasks) {
|
|
376
|
+
if (!state.held)
|
|
377
|
+
continue;
|
|
378
|
+
this.parentSubtasks.delete(callId);
|
|
379
|
+
this.deliverToolResult(state.held);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
279
382
|
/** 添加进度提示 */
|
|
280
383
|
addProgress(text, opts = {}) {
|
|
281
384
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
@@ -348,6 +451,26 @@ export class IMRenderer {
|
|
|
348
451
|
this.messageTimestamps.push(Date.now());
|
|
349
452
|
this.scheduleFlush();
|
|
350
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* 添加子任务活动(由 subtask-tool-projection 构造的 tool_call / tool_result)。
|
|
456
|
+
*
|
|
457
|
+
* 与 addToolCall/addToolResult 不同:不发 status.progress(子任务不是本轮模型正在发起的工具调用),
|
|
458
|
+
* 且 proactive 下照常投影为 thought(mapEventToItem 不处理 task_* 事件,不会重复)。
|
|
459
|
+
* 活动记录与 addToolCall 一致,在抑制前写入,保证任务活动 Menu 历史完整。
|
|
460
|
+
*/
|
|
461
|
+
addSubtaskActivity(item) {
|
|
462
|
+
this.subtaskItems.add(item);
|
|
463
|
+
if (this.opts.envelope.chatmode === 'proactive') {
|
|
464
|
+
this.emitProactiveItem(item);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
this.recordActivity(item);
|
|
468
|
+
if (this.suppressActivityItems())
|
|
469
|
+
return;
|
|
470
|
+
this.itemsQueue.push(item);
|
|
471
|
+
this.messageTimestamps.push(Date.now());
|
|
472
|
+
this.scheduleFlush();
|
|
473
|
+
}
|
|
351
474
|
/**
|
|
352
475
|
* proactive 下放行为 thought 的 notice subtype 白名单——仅"真·终态错误":
|
|
353
476
|
* - context-too-long:上下文超限且无法 auto-compact,任务到此终止
|
|
@@ -553,6 +676,16 @@ export class IMRenderer {
|
|
|
553
676
|
clearTimeout(this.timer);
|
|
554
677
|
this.timer = undefined;
|
|
555
678
|
}
|
|
679
|
+
// 显式 flush(轮次结束、出错收尾)时放出被扣住的后台占位结果;定时 flush 不放,
|
|
680
|
+
// 否则长时间后台任务会在终态到达前就把占位结果发出去。放出的结果随本次 flush 一起发,
|
|
681
|
+
// 不需要它重新挂上的定时器。
|
|
682
|
+
if (!scheduled) {
|
|
683
|
+
this.releaseHeldToolResults();
|
|
684
|
+
if (this.timer) {
|
|
685
|
+
clearTimeout(this.timer);
|
|
686
|
+
this.timer = undefined;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
556
689
|
// 文件标记过滤:marker 任何时候都不该出现在用户文本里,必须在 *每次* flush
|
|
557
690
|
// (含非最终的定时 flush)执行。否则文本块若滞留 buffer 后被定时器触发,会走
|
|
558
691
|
// 下方非-final 的 result.text 路径,把 [SEND_FILE:...] 原样发给用户。
|
|
@@ -717,6 +850,16 @@ export class IMRenderer {
|
|
|
717
850
|
if (item.kind === 'progress') {
|
|
718
851
|
return;
|
|
719
852
|
}
|
|
853
|
+
if (item.kind === 'tool_call') {
|
|
854
|
+
this.toolCallNames.set(item.call_id, item.name);
|
|
855
|
+
}
|
|
856
|
+
else if (item.kind === 'tool_result') {
|
|
857
|
+
const resolved = this.resolveParentToolResult(item);
|
|
858
|
+
if (!resolved)
|
|
859
|
+
return;
|
|
860
|
+
this.emitProactiveItem(resolved);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
720
863
|
this.emitProactiveItem(item);
|
|
721
864
|
}
|
|
722
865
|
/** proactive 模式逐条投影:单个 ThoughtItem 包成 activity.batch[1] 发出(fire-and-forget)。 */
|
|
@@ -814,7 +957,7 @@ export class IMRenderer {
|
|
|
814
957
|
// 上下文过长错误不输出(留给外层 auto-compact 处理)
|
|
815
958
|
if (isContextTooLongText(event.error || ''))
|
|
816
959
|
return null;
|
|
817
|
-
const text = getErrorMessage(new Error(event.error
|
|
960
|
+
const text = getErrorMessage(new Error(event.error), undefined, false);
|
|
818
961
|
return { kind: 'notice', text: tagErrorTextWithEvolcoreVersion(text), severity: 'warn' };
|
|
819
962
|
}
|
|
820
963
|
case 'complete': {
|
|
@@ -828,7 +971,7 @@ export class IMRenderer {
|
|
|
828
971
|
return null;
|
|
829
972
|
}
|
|
830
973
|
if (event.isError) {
|
|
831
|
-
const errText = event.errors?.join('; ') || event.result || '
|
|
974
|
+
const errText = event.errors?.join('; ') || event.result || '';
|
|
832
975
|
const text = getErrorMessage(new Error(errText), event.terminalReason, false);
|
|
833
976
|
const item = {
|
|
834
977
|
kind: 'summary',
|
|
@@ -1895,9 +1895,19 @@ export class MessageBridge {
|
|
|
1895
1895
|
case 'menu.list':
|
|
1896
1896
|
if (args?.view === 'catalog') {
|
|
1897
1897
|
validateMenuCatalogArgs(args);
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
|
|
1898
|
+
const catalogArgs = [
|
|
1899
|
+
channel,
|
|
1900
|
+
msg.channelId,
|
|
1901
|
+
authSubject,
|
|
1902
|
+
args.context,
|
|
1903
|
+
msg.isControlChannel ? 'control' : 'agent',
|
|
1904
|
+
!msg.isControlChannel || (msg.encrypted === true
|
|
1905
|
+
&& msg.protectedHeaders?.auth_method !== 'remote-access-token'
|
|
1906
|
+
&& msg.protectedHeaders?.auth_method !== 'ecweb-token'),
|
|
1907
|
+
];
|
|
1908
|
+
const data = msg.threadId
|
|
1909
|
+
? await this.cmdHandler.getMenuCatalog(...catalogArgs, transportReplyContext)
|
|
1910
|
+
: await this.cmdHandler.getMenuCatalog(...catalogArgs);
|
|
1901
1911
|
return menuSuccess(header, data);
|
|
1902
1912
|
}
|
|
1903
1913
|
if (args?.view !== undefined) {
|