evolcore 0.0.26 → 0.0.28
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 +36 -0
- package/bin/codex-managed-acl.ps1 +43 -0
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/baseagent.js +10 -5
- package/dist/agents/claude-runner.js +123 -22
- package/dist/agents/codex/app-server-client.js +70 -26
- package/dist/agents/codex/catalog.js +7 -1
- package/dist/agents/codex-runner.js +363 -421
- package/dist/agents/ecagent-runner.js +170 -25
- package/dist/agents/gemini-runner.js +6 -0
- package/dist/agents/runner-types.js +50 -2
- package/dist/aun/aid/agentmd.js +73 -31
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +3 -3
- package/dist/aun/group/history.js +303 -0
- package/dist/aun/group/identity.js +43 -0
- package/dist/aun/group/index.js +5 -0
- package/dist/aun/{msg/group.js → group/operations.js} +161 -38
- package/dist/aun/{menu-client.js → menu/client.js} +13 -7
- package/dist/aun/menu/index.js +2 -0
- package/dist/aun/menu/transport.js +49 -0
- package/dist/aun/msg/index.js +2 -1
- package/dist/aun/msg/managed-operation.js +84 -18
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +13 -8
- package/dist/aun/msg/upload.js +43 -88
- package/dist/aun/outbox.js +29 -7
- package/dist/aun/rpc/client.js +1 -0
- package/dist/aun/storage/client.js +39 -50
- package/dist/channels/aun.js +544 -239
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +108 -6
- package/dist/cli/agent-create.js +515 -2
- package/dist/cli/agent.js +109 -8
- package/dist/cli/aun-commands.js +191 -42
- package/dist/cli/config.js +8 -5
- package/dist/cli/contact.js +2 -2
- package/dist/cli/ctl-command.js +2 -2
- package/dist/cli/daemon-commands.js +111 -162
- package/dist/cli/diag.js +3 -3
- package/dist/cli/fs-command.js +3 -3
- package/dist/cli/handoff-command.js +6 -6
- package/dist/cli/index.js +8 -2
- package/dist/cli/init.js +34 -1
- package/dist/cli/managed-command-guard.js +2 -1
- package/dist/cli/menu-client.js +13 -5
- package/dist/cli/model.js +3 -3
- package/dist/cli/queue-command.js +2 -2
- package/dist/cli/response.js +2 -2
- package/dist/cli/restart-monitor.js +81 -19
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +20 -10
- package/dist/config/builtin-roles.js +10 -0
- package/dist/config/config-field-policy.js +11 -0
- package/dist/config/config-manager.js +32 -4
- package/dist/config/config-operation-service.js +20 -5
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +1 -1
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +5 -0
- package/dist/core/audit/event-key.js +17 -2
- package/dist/core/audit/log-integrity.js +34 -8
- package/dist/core/auth/agent-delegation.js +48 -14
- package/dist/core/auth/auth-gateway.js +2 -0
- package/dist/core/auth/authorization-audit.js +34 -38
- package/dist/core/auth/operation-authorizer.js +67 -5
- package/dist/core/auth/operation-catalog.js +110 -0
- package/dist/core/auth/remote-access-store.js +115 -9
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +110 -19
- package/dist/core/command/agent-control.js +88 -7
- package/dist/core/command/agent-storage-menu.js +443 -0
- package/dist/core/command/cli-intent-parser.js +19 -0
- package/dist/core/command/command-handler.js +105 -40
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/diag-service.js +292 -27
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +34 -4
- package/dist/core/command/menu-handler.js +364 -96
- package/dist/core/command/menu-protocol.js +43 -6
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-handler.js +61 -37
- package/dist/core/event-catalog.js +4 -0
- package/dist/core/evolagent-registry.js +28 -2
- package/dist/core/group/invite-service.js +924 -0
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +94 -16
- package/dist/core/message/message-bridge.js +140 -13
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +73 -41
- package/dist/core/message/response-engine.js +729 -118
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +76 -36
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +72 -3
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +8 -8
- package/dist/core/relation/peer-identity.js +57 -6
- package/dist/core/relation/shared-data.js +106 -0
- package/dist/core/role/runtime-policy.js +64 -0
- package/dist/core/runtime-lock.js +10 -26
- package/dist/core/session/runner-task-history.js +170 -0
- package/dist/core/session/session-manager.js +38 -6
- package/dist/core/session/session-mapper.js +41 -0
- package/dist/core/session/session-turn-coordinator.js +37 -7
- package/dist/core/session/session-turns.js +48 -7
- package/dist/core/task/task-recovery-service.js +176 -0
- package/dist/core/task/task-resume-operation.js +32 -0
- package/dist/eck/group-rules-sync.js +5 -3
- package/dist/eck/kit-renderer.js +1 -0
- package/dist/index.js +322 -207
- package/dist/ipc.js +141 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +3 -0
- package/dist/trigger/event-source.js +19 -0
- package/dist/trigger/feedback.js +233 -40
- package/dist/trigger/parser.js +55 -5
- package/dist/trigger/patch.js +9 -0
- package/dist/trigger/scheduler.js +30 -4
- package/dist/trigger/validation.js +20 -3
- package/dist/utils/atomic-write.js +98 -0
- package/dist/utils/cross-platform.js +87 -16
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-utils.js +40 -2
- package/dist/utils/logger.js +23 -13
- package/dist/utils/npm-ops.js +119 -4
- package/dist/utils/package-version-info.js +244 -0
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +174 -1
- package/dist/utils/windows-shell-trust.js +37 -0
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/INDEX.md +0 -6
- package/kits/docs/channels/aun.md +23 -2
- package/kits/docs/channels/feishu.md +1 -1
- package/kits/docs/context-assembly.md +29 -17
- package/kits/docs/evolcore/INDEX.md +8 -3
- package/kits/docs/evolcore/agent-create.md +39 -7
- package/kits/docs/evolcore/agent.md +33 -1
- package/kits/docs/evolcore/aid.md +9 -1
- package/kits/docs/evolcore/config.md +40 -1
- package/kits/docs/evolcore/group-collaboration.md +1 -1
- package/kits/docs/evolcore/group.md +117 -2
- package/kits/docs/evolcore/menu-client.md +16 -0
- package/kits/docs/evolcore/msg.md +3 -0
- package/kits/docs/evolcore/storage.md +3 -0
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +51 -3
- package/kits/docs/venues/group.md +5 -0
- package/kits/eck_manifest.json +27 -0
- package/kits/rules/01-overview.md +11 -7
- package/kits/rules/04-relation.md +2 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +6 -4
- package/kits/schemas/agent-config.schema.12.json +25 -1
- package/kits/schemas/daemon.schema.6.json +2 -2
- package/kits/schemas/defaults.schema.5.json +1 -1
- package/kits/schemas/relation-config.schema.1.json +1 -1
- package/kits/schemas/relation-config.schema.5.json +1 -1
- package/kits/schemas/role-config.schema.1.json +14 -0
- package/kits/templates/roles/member.json +5 -0
- package/kits/templates/roles/visitor.json +5 -0
- package/kits/templates/system-fragments/baseagent.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +26 -9
- package/kits/templates/system-fragments/channel.md +8 -44
- package/kits/templates/system-fragments/commands.md +10 -2
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/reply.md +56 -0
- package/package.json +7 -3
- package/dist/aun/group-identity.js +0 -9
- /package/dist/aun/{group-fs-download.js → group/fs-download.js} +0 -0
- /package/dist/aun/{msg/group-index.js → group/index-rpc.js} +0 -0
|
@@ -46,6 +46,8 @@ export class InteractionRouter {
|
|
|
46
46
|
clearTimeout(existing.timer);
|
|
47
47
|
const isReplacement = !!existing;
|
|
48
48
|
const pending = {
|
|
49
|
+
blocking: opts?.blocking !== false,
|
|
50
|
+
retainOnRejected: opts?.retainOnRejected === true,
|
|
49
51
|
callback,
|
|
50
52
|
sessionId,
|
|
51
53
|
initiatorId: opts?.initiatorId,
|
|
@@ -61,7 +63,8 @@ export class InteractionRouter {
|
|
|
61
63
|
if (this.handlers.get(id) !== pending)
|
|
62
64
|
return;
|
|
63
65
|
this.handlers.delete(id);
|
|
64
|
-
|
|
66
|
+
if (pending.blocking)
|
|
67
|
+
this.decPending(pending.sessionId);
|
|
65
68
|
const cancelReason = opts.timeoutCancelReason ?? 'timeout';
|
|
66
69
|
logger.debug(`[InteractionRouter] Timeout for interaction: ${id}`);
|
|
67
70
|
opts.onTimeout?.();
|
|
@@ -73,7 +76,9 @@ export class InteractionRouter {
|
|
|
73
76
|
}, opts.timeoutMs);
|
|
74
77
|
}
|
|
75
78
|
this.handlers.set(id, pending);
|
|
76
|
-
if (!
|
|
79
|
+
if (existing?.blocking && (!pending.blocking || existing.sessionId !== sessionId))
|
|
80
|
+
this.decPending(existing.sessionId);
|
|
81
|
+
if (pending.blocking && (!isReplacement || !existing?.blocking || existing.sessionId !== sessionId))
|
|
77
82
|
this.incPending(sessionId);
|
|
78
83
|
}
|
|
79
84
|
dispatch(response) {
|
|
@@ -104,10 +109,15 @@ export class InteractionRouter {
|
|
|
104
109
|
// The callback may already have resolved/cancelled this exact handler.
|
|
105
110
|
if (this.handlers.get(response.id) !== handler)
|
|
106
111
|
return accepted;
|
|
112
|
+
if (!accepted && handler.retainOnRejected && rejectionReason === 'backend_rejected') {
|
|
113
|
+
handler.inFlight = false;
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
107
116
|
if (handler.timer)
|
|
108
117
|
clearTimeout(handler.timer);
|
|
109
118
|
this.handlers.delete(response.id);
|
|
110
|
-
|
|
119
|
+
if (handler.blocking)
|
|
120
|
+
this.decPending(handler.sessionId);
|
|
111
121
|
if (!accepted && handler.onCancel) {
|
|
112
122
|
Promise.resolve(handler.onCancel(rejectionReason)).catch(err => {
|
|
113
123
|
logger.error(`[InteractionRouter] Rejection cancellation error for ${response.id}:`, err);
|
|
@@ -153,10 +163,13 @@ export class InteractionRouter {
|
|
|
153
163
|
const pending = [];
|
|
154
164
|
for (const [id, handler] of this.handlers.entries()) {
|
|
155
165
|
if (handler.sessionId === sessionId) {
|
|
166
|
+
if (!handler.blocking && reason === 'new_message')
|
|
167
|
+
continue;
|
|
156
168
|
if (handler.timer)
|
|
157
169
|
clearTimeout(handler.timer);
|
|
158
170
|
this.handlers.delete(id);
|
|
159
|
-
|
|
171
|
+
if (handler.blocking)
|
|
172
|
+
this.decPending(handler.sessionId);
|
|
160
173
|
if (reason && handler.onCancel) {
|
|
161
174
|
pending.push(Promise.resolve(handler.onCancel(reason)).then(() => undefined));
|
|
162
175
|
}
|
|
@@ -170,7 +183,8 @@ export class InteractionRouter {
|
|
|
170
183
|
if (handler.timer)
|
|
171
184
|
clearTimeout(handler.timer);
|
|
172
185
|
this.handlers.delete(id);
|
|
173
|
-
|
|
186
|
+
if (handler.blocking)
|
|
187
|
+
this.decPending(handler.sessionId);
|
|
174
188
|
if (reason && handler.onCancel)
|
|
175
189
|
await handler.onCancel(reason);
|
|
176
190
|
return true;
|
|
@@ -208,10 +222,10 @@ export class InteractionRouter {
|
|
|
208
222
|
return false;
|
|
209
223
|
}
|
|
210
224
|
}
|
|
211
|
-
getPending(sessionId) {
|
|
225
|
+
getPending(sessionId, command) {
|
|
212
226
|
const ids = [];
|
|
213
227
|
for (const [id, handler] of this.handlers.entries()) {
|
|
214
|
-
if (handler.sessionId === sessionId)
|
|
228
|
+
if (handler.sessionId === sessionId && (!command || handler.fallbackCommand === command))
|
|
215
229
|
ids.push(id);
|
|
216
230
|
}
|
|
217
231
|
return ids;
|
|
@@ -281,7 +295,7 @@ export function renderActionAsText(req) {
|
|
|
281
295
|
if (fb.acceptFreeText && fb.freeTextHint) {
|
|
282
296
|
lines.push(` ${fb.freeTextHint}`);
|
|
283
297
|
}
|
|
284
|
-
if (action.allowCustomInput) {
|
|
298
|
+
if (action.allowCustomInput && req.blocking !== false) {
|
|
285
299
|
lines.push(` 或直接输入自定义内容`);
|
|
286
300
|
}
|
|
287
301
|
return lines.join('\n');
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { logger } from '../../utils/logger.js';
|
|
2
|
-
import { summarizeToolInput, toolInputForDisplay } from '../../utils/tool-summary.js';
|
|
2
|
+
import { summarizeToolInput, summarizeToolInputForDisplay, toolInputForDisplay } from '../../utils/tool-summary.js';
|
|
3
3
|
import { getErrorMessage, isContextTooLongText } from '../../utils/error-utils.js';
|
|
4
4
|
import { tagErrorTextWithEvolcoreVersion } from '../../utils/evolcore-version.js';
|
|
5
5
|
import { DEFAULT_FLUSH_DELAY_MS } from '../../types.js';
|
|
6
|
+
import { classifyAunToolErrorCode } from '../permission/tool-error-code.js';
|
|
6
7
|
export class IMRenderer {
|
|
7
8
|
opts;
|
|
8
9
|
itemsQueue = [];
|
|
@@ -19,6 +20,8 @@ export class IMRenderer {
|
|
|
19
20
|
hasEmittedText = false;
|
|
20
21
|
/** 自增 callId 兜底(runner 没提供时用) */
|
|
21
22
|
syntheticCallSeq = 0;
|
|
23
|
+
currentAssistantUuid;
|
|
24
|
+
currentTextPhase;
|
|
22
25
|
constructor(opts) {
|
|
23
26
|
this.opts = opts;
|
|
24
27
|
}
|
|
@@ -61,12 +64,12 @@ export class IMRenderer {
|
|
|
61
64
|
// interactive 模式由 MessageProcessor 显式调 addText/addToolCall/... 推入 items
|
|
62
65
|
}
|
|
63
66
|
/** 强制刷新所有 pending 事件 */
|
|
64
|
-
async flush(isFinal) {
|
|
67
|
+
async flush(isFinal, attention) {
|
|
65
68
|
if (this.opts.envelope.chatmode === 'proactive') {
|
|
66
69
|
// proactive 是 fire-and-forget,无 pending buffer
|
|
67
70
|
return;
|
|
68
71
|
}
|
|
69
|
-
return this.flushInternal(isFinal);
|
|
72
|
+
return this.flushInternal(isFinal, attention);
|
|
70
73
|
}
|
|
71
74
|
/** 仅 flush activities,保留 textBuffer(用于中间 complete 事件) */
|
|
72
75
|
async flushActivitiesOnly() {
|
|
@@ -122,7 +125,12 @@ export class IMRenderer {
|
|
|
122
125
|
this.textBuffer = '';
|
|
123
126
|
// 清掉已消费的 text items,包括被拦截的纯空白正文。
|
|
124
127
|
this.itemsQueue = this.itemsQueue.filter(it => it.kind !== 'text');
|
|
125
|
-
const payload = {
|
|
128
|
+
const payload = {
|
|
129
|
+
kind: 'result.text',
|
|
130
|
+
text: rawText,
|
|
131
|
+
isFinal: false,
|
|
132
|
+
...this.forkAnchorMetadata(),
|
|
133
|
+
};
|
|
126
134
|
this.sentContent = true;
|
|
127
135
|
try {
|
|
128
136
|
await this.sendPayload(payload);
|
|
@@ -168,9 +176,24 @@ export class IMRenderer {
|
|
|
168
176
|
}
|
|
169
177
|
// ── 文本/活动注入(替代 StreamFlusher.addText/addActivity)──
|
|
170
178
|
/** 添加文本片段(流式 text) */
|
|
171
|
-
addText(text, outputTokens, turn) {
|
|
179
|
+
async addText(text, outputTokens, turn, assistantUuid, phase) {
|
|
172
180
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
173
181
|
return;
|
|
182
|
+
const sameAssistant = this.currentAssistantUuid === assistantUuid;
|
|
183
|
+
// In deferred AUN private delivery, only explicit text_start/tool_use
|
|
184
|
+
// handlers are semantic boundaries. assistantUuid is correlation data and
|
|
185
|
+
// may be absent or change without proving that the prior text is final.
|
|
186
|
+
const hadPendingText = this.textBuffer.length > 0;
|
|
187
|
+
if (hadPendingText && !this.opts.deferTextUntilBoundary && !sameAssistant
|
|
188
|
+
&& !this.suppressIntermediateText()) {
|
|
189
|
+
await this.flushText();
|
|
190
|
+
}
|
|
191
|
+
// Do not carry the previous item's anchor onto unanchored output.
|
|
192
|
+
this.currentAssistantUuid = assistantUuid;
|
|
193
|
+
// A mixed buffer has no trustworthy phase, even when its latest chunk is commentary.
|
|
194
|
+
this.currentTextPhase = hadPendingText && this.opts.deferTextUntilBoundary
|
|
195
|
+
? this.currentTextPhase === phase ? phase : undefined
|
|
196
|
+
: phase;
|
|
174
197
|
this.emitProgress('text', outputTokens, turn);
|
|
175
198
|
if (!text)
|
|
176
199
|
return;
|
|
@@ -195,18 +218,19 @@ export class IMRenderer {
|
|
|
195
218
|
this.emitProgress('tool_call', outputTokens, turn, { toolName: name, callId });
|
|
196
219
|
if (this.suppressActivityItems())
|
|
197
220
|
return;
|
|
221
|
+
const displayInput = toolInputForDisplay(name, input);
|
|
198
222
|
this.itemsQueue.push({
|
|
199
223
|
kind: 'tool_call',
|
|
200
224
|
call_id: callId || this.synthCallId(),
|
|
201
225
|
name,
|
|
202
|
-
arguments:
|
|
203
|
-
text: descText,
|
|
226
|
+
arguments: displayInput,
|
|
227
|
+
text: displayInput !== input ? summarizeToolInput(name, displayInput || {}) : descText,
|
|
204
228
|
});
|
|
205
229
|
this.messageTimestamps.push(Date.now());
|
|
206
230
|
this.scheduleFlush();
|
|
207
231
|
}
|
|
208
232
|
/** 添加工具结果 */
|
|
209
|
-
addToolResult(name, ok, result, error, callId, durationMs, descText) {
|
|
233
|
+
addToolResult(name, ok, result, error, callId, durationMs, descText, classification) {
|
|
210
234
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
211
235
|
return;
|
|
212
236
|
this.emitProgress('tool_result', undefined, undefined, { toolName: name, callId, ok, durationMs });
|
|
@@ -219,6 +243,7 @@ export class IMRenderer {
|
|
|
219
243
|
ok,
|
|
220
244
|
...(result !== undefined && { result }),
|
|
221
245
|
...(error !== undefined && { error }),
|
|
246
|
+
...(!ok && { error_code: classifyAunToolErrorCode(classification ?? {}) }),
|
|
222
247
|
...(durationMs !== undefined && { duration_ms: durationMs }),
|
|
223
248
|
...(descText !== undefined && { text: descText }),
|
|
224
249
|
});
|
|
@@ -260,6 +285,21 @@ export class IMRenderer {
|
|
|
260
285
|
logger.warn(`[IMRenderer] status notice send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
261
286
|
}
|
|
262
287
|
}
|
|
288
|
+
/** Notify the client that a new model text block has started. */
|
|
289
|
+
async sendTextStart(contentIndex, turn) {
|
|
290
|
+
if (this.opts.deferTextUntilBoundary)
|
|
291
|
+
await this.flushText();
|
|
292
|
+
const payload = {
|
|
293
|
+
kind: 'status.progress',
|
|
294
|
+
metadata: { activityType: 'text', kind: 'text_start', contentIndex, turn },
|
|
295
|
+
};
|
|
296
|
+
try {
|
|
297
|
+
await this.opts.send(payload);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
logger.debug(`[IMRenderer] text_start status send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
263
303
|
/** 兼容旧调用方:发送 warning 级别的任务 notice。 */
|
|
264
304
|
async sendWarningStatus(message, subtype, details) {
|
|
265
305
|
return this.sendStatusNotice(message, 'warn', subtype, details);
|
|
@@ -317,7 +357,11 @@ export class IMRenderer {
|
|
|
317
357
|
return false;
|
|
318
358
|
// 保持与流式正文的到达顺序:先发出已缓冲的正文,再投影运行状态。
|
|
319
359
|
await this.flushText();
|
|
320
|
-
const payload = {
|
|
360
|
+
const payload = {
|
|
361
|
+
kind: 'result.text',
|
|
362
|
+
text,
|
|
363
|
+
isFinal: false,
|
|
364
|
+
};
|
|
321
365
|
this.sentContent = true;
|
|
322
366
|
await this.sendPayload(payload);
|
|
323
367
|
this.lastFlush = Date.now();
|
|
@@ -422,7 +466,7 @@ export class IMRenderer {
|
|
|
422
466
|
const elapsed = Date.now() - this.lastFlush;
|
|
423
467
|
const delay = Math.max(0, targetDelay - elapsed);
|
|
424
468
|
this.timer = setTimeout(() => {
|
|
425
|
-
void this.flushInternal().catch(error => {
|
|
469
|
+
void this.flushInternal(undefined, undefined, true).catch(error => {
|
|
426
470
|
logger.warn(`[IMRenderer] scheduled flush failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
427
471
|
});
|
|
428
472
|
}, delay);
|
|
@@ -470,7 +514,7 @@ export class IMRenderer {
|
|
|
470
514
|
* 完整 flush:把 itemsQueue 里所有 items 打包成 activity.batch 发送。
|
|
471
515
|
* 如果 isFinal=true,还会在 batch 之后单独发一条 result.text 作为最终回复。
|
|
472
516
|
*/
|
|
473
|
-
async flushInternal(isFinal) {
|
|
517
|
+
async flushInternal(isFinal, attention, scheduled = false) {
|
|
474
518
|
if (this.timer) {
|
|
475
519
|
clearTimeout(this.timer);
|
|
476
520
|
this.timer = undefined;
|
|
@@ -501,6 +545,7 @@ export class IMRenderer {
|
|
|
501
545
|
});
|
|
502
546
|
this.itemsQueue = [];
|
|
503
547
|
const finalText = isFinal ? this.textBuffer : '';
|
|
548
|
+
const finalAnchor = this.forkAnchorMetadata();
|
|
504
549
|
if (isFinal)
|
|
505
550
|
this.textBuffer = '';
|
|
506
551
|
// 1. interactive 模式下:不发 text items(由 result.text 统一发送最终文本)
|
|
@@ -530,9 +575,18 @@ export class IMRenderer {
|
|
|
530
575
|
if (!isFinal && this.textBuffer.length > 0) {
|
|
531
576
|
if (this.suppressIntermediateText())
|
|
532
577
|
return;
|
|
578
|
+
// A timer cannot decide whether this is the final answer. Keep it for
|
|
579
|
+
// an explicit boundary; known commentary can still be sent immediately.
|
|
580
|
+
if (scheduled && this.opts.deferTextUntilBoundary && this.currentTextPhase !== 'commentary')
|
|
581
|
+
return;
|
|
533
582
|
const text = this.textBuffer;
|
|
534
583
|
this.textBuffer = '';
|
|
535
|
-
const payload = {
|
|
584
|
+
const payload = {
|
|
585
|
+
kind: 'result.text',
|
|
586
|
+
text,
|
|
587
|
+
isFinal: false,
|
|
588
|
+
...this.forkAnchorMetadata(),
|
|
589
|
+
};
|
|
536
590
|
this.sentContent = true;
|
|
537
591
|
try {
|
|
538
592
|
await this.sendPayload(payload);
|
|
@@ -546,7 +600,14 @@ export class IMRenderer {
|
|
|
546
600
|
}
|
|
547
601
|
// 2. isFinal=true 时单独发最终回复文本
|
|
548
602
|
if (isFinal && finalText.length > 0) {
|
|
549
|
-
const payload = {
|
|
603
|
+
const payload = {
|
|
604
|
+
kind: 'result.text',
|
|
605
|
+
text: finalText,
|
|
606
|
+
isFinal: true,
|
|
607
|
+
...finalAnchor,
|
|
608
|
+
};
|
|
609
|
+
if (attention !== undefined)
|
|
610
|
+
payload.attention = attention;
|
|
550
611
|
this.sentContent = true;
|
|
551
612
|
try {
|
|
552
613
|
await this.sendPayload(payload);
|
|
@@ -559,6 +620,15 @@ export class IMRenderer {
|
|
|
559
620
|
this.flushCount++;
|
|
560
621
|
}
|
|
561
622
|
}
|
|
623
|
+
forkAnchorMetadata() {
|
|
624
|
+
const assistantMessageId = this.currentAssistantUuid?.trim();
|
|
625
|
+
if (!assistantMessageId)
|
|
626
|
+
return {};
|
|
627
|
+
const anchor = {
|
|
628
|
+
assistantMessageId,
|
|
629
|
+
};
|
|
630
|
+
return { metadata: { forkAnchor: anchor } };
|
|
631
|
+
}
|
|
562
632
|
// ── 内部:status.progress 发送 ──
|
|
563
633
|
emitProgress(activityType, outputTokens, turn, extra) {
|
|
564
634
|
if (this.suppressActivityItems())
|
|
@@ -592,9 +662,16 @@ export class IMRenderer {
|
|
|
592
662
|
const item = this.mapEventToItem(event);
|
|
593
663
|
if (!item)
|
|
594
664
|
return;
|
|
665
|
+
// Proactive output is optional thought content. Blank runner blocks are
|
|
666
|
+
// normal; keep diagnostics without reporting a failed message send.
|
|
667
|
+
if ((item.kind === 'text' || (item.kind === 'summary' && !item.is_error)) && !item.text.trim()) {
|
|
668
|
+
const { envelope, agentAid } = this.opts;
|
|
669
|
+
logger.debug(`[IMRenderer] Skipping blank proactive thought: kind=${item.kind}`
|
|
670
|
+
+ ` task=${envelope.taskId} session=${envelope.sessionId ?? '<none>'}`
|
|
671
|
+
+ ` agent=${agentAid ?? envelope.agentName} textLength=${item.text.length}`);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
595
674
|
if (item.kind === 'text') {
|
|
596
|
-
if (!this.hasSendableText(item.text, 'proactive'))
|
|
597
|
-
return;
|
|
598
675
|
this.hasEmittedText = true;
|
|
599
676
|
this.allText += item.text;
|
|
600
677
|
}
|
|
@@ -622,7 +699,7 @@ export class IMRenderer {
|
|
|
622
699
|
return null;
|
|
623
700
|
return { kind: 'text', text: event.text };
|
|
624
701
|
case 'tool_use': {
|
|
625
|
-
const desc =
|
|
702
|
+
const desc = summarizeToolInputForDisplay(event.name, event.input || {});
|
|
626
703
|
const item = {
|
|
627
704
|
kind: 'tool_call',
|
|
628
705
|
call_id: event.callId || this.synthCallId(),
|
|
@@ -640,6 +717,7 @@ export class IMRenderer {
|
|
|
640
717
|
call_id: event.callId || this.synthCallId(),
|
|
641
718
|
name: event.name,
|
|
642
719
|
ok: false,
|
|
720
|
+
error_code: classifyAunToolErrorCode(event),
|
|
643
721
|
error: event.error || (typeof event.result === 'string' ? event.result : '执行失败'),
|
|
644
722
|
...(event.durationMs !== undefined && { duration_ms: event.durationMs }),
|
|
645
723
|
};
|