evolcore 0.0.25 → 0.0.27
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 +632 -565
- package/LICENSE +21 -21
- package/README.md +455 -455
- package/bin/codex-managed-acl.ps1 +203 -160
- package/bin/codex-managed-hook.mjs +208 -208
- package/bin/ec-safe-output.js +327 -327
- package/bin/ec.js +40 -29
- package/bin/install-codex-managed-hooks.mjs +582 -555
- package/dist/agents/baseagent-availability.js +68 -0
- package/dist/agents/baseagent.js +96 -46
- package/dist/agents/claude-runner.js +172 -33
- package/dist/agents/codex/app-server-client.js +76 -31
- package/dist/agents/codex/catalog.js +1 -1
- package/dist/agents/codex/diagnostics.js +34 -1
- package/dist/agents/codex-runner.js +434 -446
- package/dist/agents/ecagent-runner.js +174 -34
- package/dist/agents/gemini-runner.js +8 -7
- package/dist/agents/runner-types.js +75 -19
- package/dist/aun/aid/agentmd.js +151 -36
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/control-aid.js +180 -1
- package/dist/aun/aid/identity.js +4 -4
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +17 -13
- 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} +162 -39
- package/dist/aun/{menu-client.js → menu/client.js} +85 -6
- 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 +85 -19
- package/dist/aun/msg/mention-schema.js +51 -0
- package/dist/aun/msg/p2p.js +19 -9
- package/dist/aun/msg/upload.js +43 -80
- 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 +842 -318
- package/dist/channels/daemon.js +10 -6
- package/dist/cli/agent-command.js +212 -89
- package/dist/cli/agent-create.js +626 -11
- package/dist/cli/agent.js +125 -13
- package/dist/cli/aun-commands.js +487 -337
- package/dist/cli/bench.js +20 -20
- package/dist/cli/config.js +55 -33
- package/dist/cli/contact.js +72 -15
- package/dist/cli/ctl-command.js +24 -24
- package/dist/cli/daemon-commands.js +140 -63
- package/dist/cli/diag.js +31 -0
- package/dist/cli/fs-command.js +70 -70
- package/dist/cli/handoff-command.js +46 -14
- package/dist/cli/index.js +91 -79
- package/dist/cli/init.js +34 -1
- package/dist/cli/link-rules.js +21 -21
- package/dist/cli/managed-command-guard.js +5 -1
- package/dist/cli/menu-client.js +51 -32
- package/dist/cli/model.js +3 -3
- package/dist/cli/net-check.js +22 -22
- package/dist/cli/queue-command.js +31 -31
- package/dist/cli/response.js +32 -32
- package/dist/cli/restart-monitor.js +103 -42
- package/dist/cli/stats.js +51 -51
- package/dist/cli/task-command.js +59 -0
- package/dist/cli/task-context.js +97 -49
- package/dist/cli/trigger-command.js +74 -64
- package/dist/config/builtin-roles.js +11 -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 +160 -0
- package/dist/config/contact-book-store.js +1 -1
- package/dist/config/contact-book.js +1 -1
- package/dist/config/contact-operation-service.js +97 -8
- package/dist/config/peer-role-resolver.js +1 -1
- package/dist/config/resolved-config-op.js +30 -7
- package/dist/config-store.js +1 -1
- package/dist/core/agent-application-service.js +7 -1
- 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 +53 -49
- package/dist/core/auth/operation-authorizer.js +90 -7
- package/dist/core/auth/operation-catalog.js +145 -4
- package/dist/core/auth/remote-access-store.js +155 -10
- package/dist/core/bootstrap-messages.js +66 -7
- package/dist/core/bootstrap-service.js +111 -20
- 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 +159 -65
- package/dist/core/command/data-root-resolver.js +273 -0
- package/dist/core/command/diag-service.js +118 -0
- package/dist/core/command/group-menu.js +7 -2
- package/dist/core/command/menu-catalog.js +46 -7
- package/dist/core/command/menu-handler.js +392 -100
- package/dist/core/command/menu-protocol.js +45 -6
- package/dist/core/command/menu-token-store.js +41 -8
- package/dist/core/command/role-menu.js +2 -2
- package/dist/core/command/slash-gate.js +34 -4
- package/dist/core/command/slash-handler.js +123 -43
- 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/handoff/runtime.js +205 -27
- package/dist/core/handoff/store.js +193 -4
- package/dist/core/interaction-router.js +22 -8
- package/dist/core/message/im-renderer.js +146 -34
- package/dist/core/message/message-bridge.js +163 -20
- package/dist/core/message/message-log.js +5 -3
- package/dist/core/message/message-queue.js +99 -41
- package/dist/core/message/message-utils.js +1 -0
- package/dist/core/message/response-engine.js +755 -144
- package/dist/core/model/catalog-service.js +79 -49
- package/dist/core/model/context-window-suffix.js +33 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-permission.js +12 -3
- package/dist/core/permission/approval-gateway.js +36 -3
- package/dist/core/permission/ec-command-parser.js +112 -41
- package/dist/core/permission/protected-paths.js +134 -16
- package/dist/core/permission/sandbox-runtime.js +108 -17
- package/dist/core/permission/tool-error-code.js +15 -0
- package/dist/core/permission/tool-policy.js +9 -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 +34 -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 +588 -217
- package/dist/ipc.js +193 -15
- package/dist/paths.js +17 -4
- package/dist/product.js +2 -0
- package/dist/response-system/engines/v1/proactive-flow.js +59 -2
- package/dist/stats/billing.js +41 -15
- package/dist/stats/db.js +159 -159
- package/dist/stats/eck-vars.js +9 -9
- package/dist/stats/price-resolver.js +28 -10
- package/dist/stats/query.js +139 -139
- package/dist/stats/role-budget.js +6 -6
- package/dist/stats/writer.js +51 -51
- 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/cross-platform.js +30 -1
- package/dist/utils/ecweb-utils.js +1 -0
- package/dist/utils/error-dict.json +160 -160
- package/dist/utils/error-utils.js +61 -2
- package/dist/utils/evolcore-version.js +129 -0
- package/dist/utils/logger.js +26 -14
- package/dist/utils/macos-autostart.js +22 -22
- package/dist/utils/model-prices.jsonl +23 -23
- package/dist/utils/npm-ops.js +176 -35
- package/dist/utils/tool-summary.js +59 -3
- package/dist/utils/welcome.js +196 -23
- package/ecagent/LICENSE +21 -21
- package/ecagent/NOTICE +11 -11
- package/ecagent/README.md +61 -61
- package/ecagent/dist/harness/agent-harness.js +3 -0
- package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
- package/ecagent/dist/harness/compaction/compaction.js +83 -83
- package/ecagent/dist/harness/messages.js +7 -7
- package/ecagent/dist/harness/session/jsonl-storage.js +21 -2
- package/kits/docs/GUIDE.md +20 -20
- package/kits/docs/INDEX.md +60 -66
- package/kits/docs/aun/CHEATSHEET.md +19 -19
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
- package/kits/docs/channels/aun.md +69 -56
- package/kits/docs/channels/feishu.md +56 -56
- package/kits/docs/context-assembly.md +379 -367
- package/kits/docs/eck_templates/GUIDE.template.md +22 -22
- package/kits/docs/eck_templates/INDEX.template.md +28 -28
- package/kits/docs/eck_templates/path-registry.template.md +33 -33
- package/kits/docs/eck_templates/runtime.template.md +19 -19
- package/kits/docs/evolcore/INDEX.md +76 -71
- package/kits/docs/evolcore/agent-create.md +202 -165
- package/kits/docs/evolcore/agent.md +99 -67
- package/kits/docs/evolcore/aid.md +68 -60
- package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
- package/kits/docs/evolcore/config.md +241 -195
- package/kits/docs/evolcore/contact.md +75 -68
- package/kits/docs/evolcore/ctl.md +46 -46
- package/kits/docs/evolcore/event.md +216 -216
- package/kits/docs/evolcore/fs-architecture.md +1215 -1215
- package/kits/docs/evolcore/fs.md +110 -110
- package/kits/docs/evolcore/group-collaboration.md +244 -244
- package/kits/docs/evolcore/group-fs.md +17 -17
- package/kits/docs/evolcore/group-rules.md +251 -251
- package/kits/docs/evolcore/group.md +267 -152
- package/kits/docs/evolcore/menu-client.md +58 -34
- package/kits/docs/evolcore/model.md +50 -50
- package/kits/docs/evolcore/msg.md +169 -165
- package/kits/docs/evolcore/response.md +75 -75
- package/kits/docs/evolcore/rpc.md +37 -37
- package/kits/docs/evolcore/self-summary.md +29 -29
- package/kits/docs/evolcore/stats.md +83 -83
- package/kits/docs/evolcore/storage.md +53 -50
- package/kits/docs/evolcore/task.md +14 -0
- package/kits/docs/evolcore/trigger.md +623 -575
- package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
- package/kits/docs/identity/PATH_OPS.md +16 -16
- package/kits/docs/identity/ROLE_DETAIL.md +25 -25
- package/kits/docs/identity/identity-tools.md +26 -26
- package/kits/docs/path-registry.md +62 -62
- package/kits/docs/prompt-loading-architecture.md +269 -269
- package/kits/docs/venues/aun-group.md +45 -45
- package/kits/docs/venues/aun-private.md +10 -10
- package/kits/docs/venues/client-desktop.md +10 -10
- package/kits/docs/venues/client-mobile.md +10 -10
- package/kits/docs/venues/feishu-group.md +13 -13
- package/kits/docs/venues/feishu-private.md +9 -9
- package/kits/docs/venues/group.md +42 -25
- package/kits/docs/venues/private.md +10 -10
- package/kits/eck_manifest.auxiliary.json +43 -43
- package/kits/eck_manifest.json +217 -190
- package/kits/eck_message_manifest.json +74 -74
- package/kits/migrations/README-role-config-v4.md +32 -32
- package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
- package/kits/migrations/migrate-role-config-v4.mjs +614 -614
- package/kits/migrations/migrate-role-config-v5.mjs +365 -365
- package/kits/migrations/rename-config-file.mjs +99 -99
- package/kits/rules/01-overview.md +166 -162
- package/kits/rules/02-navigation.md +96 -96
- package/kits/rules/03-identity.md +36 -36
- package/kits/rules/04-relation.md +76 -75
- package/kits/rules/05-venue.md +60 -60
- package/kits/rules/06-channel.md +65 -63
- package/kits/schemas/_meta.json +53 -53
- package/kits/schemas/agent-config.schema.1.json +177 -177
- package/kits/schemas/agent-config.schema.10.json +395 -395
- package/kits/schemas/agent-config.schema.11.json +421 -421
- package/kits/schemas/agent-config.schema.12.json +451 -427
- package/kits/schemas/agent-config.schema.2.json +239 -239
- package/kits/schemas/agent-config.schema.3.json +119 -119
- package/kits/schemas/agent-config.schema.4.json +208 -208
- package/kits/schemas/agent-config.schema.5.json +326 -326
- package/kits/schemas/agent-config.schema.6.json +322 -322
- package/kits/schemas/agent-config.schema.7.json +303 -303
- package/kits/schemas/agent-config.schema.8.json +304 -304
- package/kits/schemas/agent-config.schema.9.json +364 -364
- package/kits/schemas/contact-book.schema.1.json +36 -36
- package/kits/schemas/contact-book.schema.2.json +43 -43
- package/kits/schemas/contact-book.schema.3.json +67 -67
- package/kits/schemas/daemon.schema.1.json +91 -91
- package/kits/schemas/daemon.schema.2.json +101 -101
- package/kits/schemas/daemon.schema.3.json +123 -123
- package/kits/schemas/daemon.schema.4.json +136 -136
- package/kits/schemas/daemon.schema.5.json +135 -135
- package/kits/schemas/daemon.schema.6.json +131 -131
- package/kits/schemas/defaults.schema.1.json +81 -81
- package/kits/schemas/defaults.schema.2.json +85 -85
- package/kits/schemas/defaults.schema.3.json +73 -73
- package/kits/schemas/defaults.schema.4.json +107 -107
- package/kits/schemas/defaults.schema.5.json +119 -119
- package/kits/schemas/menu-exec-schema-commands.md +208 -208
- package/kits/schemas/migrations/README.md +30 -30
- package/kits/schemas/relation-config.schema.1.json +158 -158
- package/kits/schemas/relation-config.schema.2.json +73 -73
- package/kits/schemas/relation-config.schema.3.json +50 -50
- package/kits/schemas/relation-config.schema.4.json +47 -47
- package/kits/schemas/relation-config.schema.5.json +47 -47
- package/kits/schemas/relation-config.schema.6.json +46 -46
- package/kits/schemas/relation-config.schema.7.json +59 -59
- package/kits/schemas/relation-config.schema.8.json +103 -103
- package/kits/schemas/role-config.schema.1.json +215 -201
- package/kits/schemas/role-registry.schema.1.json +35 -35
- package/kits/schemas/single-session.schema.1.json +31 -31
- package/kits/schemas/single-session.schema.2.json +57 -57
- package/kits/schemas/single-session.schema.3.json +32 -32
- package/kits/templates/bootstrap-welcome.md +17 -17
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
- package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
- package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
- package/kits/templates/message-fragments/inject-default.md +2 -2
- package/kits/templates/message-fragments/item.md +2 -2
- package/kits/templates/roles/admin.json +92 -92
- package/kits/templates/roles/member.json +101 -96
- package/kits/templates/roles/owner.json +9 -9
- package/kits/templates/roles/visitor.json +90 -85
- package/kits/templates/system-fragments/baseagent.md +14 -14
- package/kits/templates/system-fragments/bootstrap.md +53 -34
- package/kits/templates/system-fragments/channel.md +28 -64
- package/kits/templates/system-fragments/commands.md +36 -28
- package/kits/templates/system-fragments/group-unread-history.md +7 -0
- package/kits/templates/system-fragments/identity.md +13 -13
- package/kits/templates/system-fragments/relation.md +19 -19
- package/kits/templates/system-fragments/reply.md +56 -0
- package/kits/templates/system-fragments/session.md +59 -59
- package/kits/templates/system-fragments/venue.md +31 -31
- package/package.json +104 -98
- 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,7 +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
|
+
import { tagErrorTextWithEvolcoreVersion } from '../../utils/evolcore-version.js';
|
|
4
5
|
import { DEFAULT_FLUSH_DELAY_MS } from '../../types.js';
|
|
6
|
+
import { classifyAunToolErrorCode } from '../permission/tool-error-code.js';
|
|
5
7
|
export class IMRenderer {
|
|
6
8
|
opts;
|
|
7
9
|
itemsQueue = [];
|
|
@@ -18,6 +20,7 @@ export class IMRenderer {
|
|
|
18
20
|
hasEmittedText = false;
|
|
19
21
|
/** 自增 callId 兜底(runner 没提供时用) */
|
|
20
22
|
syntheticCallSeq = 0;
|
|
23
|
+
currentAssistantUuid;
|
|
21
24
|
constructor(opts) {
|
|
22
25
|
this.opts = opts;
|
|
23
26
|
}
|
|
@@ -60,12 +63,12 @@ export class IMRenderer {
|
|
|
60
63
|
// interactive 模式由 MessageProcessor 显式调 addText/addToolCall/... 推入 items
|
|
61
64
|
}
|
|
62
65
|
/** 强制刷新所有 pending 事件 */
|
|
63
|
-
async flush(isFinal) {
|
|
66
|
+
async flush(isFinal, attention) {
|
|
64
67
|
if (this.opts.envelope.chatmode === 'proactive') {
|
|
65
68
|
// proactive 是 fire-and-forget,无 pending buffer
|
|
66
69
|
return;
|
|
67
70
|
}
|
|
68
|
-
return this.flushInternal(isFinal);
|
|
71
|
+
return this.flushInternal(isFinal, attention);
|
|
69
72
|
}
|
|
70
73
|
/** 仅 flush activities,保留 textBuffer(用于中间 complete 事件) */
|
|
71
74
|
async flushActivitiesOnly() {
|
|
@@ -79,9 +82,9 @@ export class IMRenderer {
|
|
|
79
82
|
}
|
|
80
83
|
/** 是否有会抑制兜底错误消息的正文/过程内容;lifecycle 边界不算。 */
|
|
81
84
|
hasNonLifecycleContent() {
|
|
82
|
-
return this.textBuffer.length > 0 || this.itemsQueue.some(it => {
|
|
85
|
+
return this.textBuffer.trim().length > 0 || this.itemsQueue.some(it => {
|
|
83
86
|
if (it.kind === 'text')
|
|
84
|
-
return it.text.length > 0;
|
|
87
|
+
return it.text.trim().length > 0;
|
|
85
88
|
return it.kind !== 'started' && it.kind !== 'completed';
|
|
86
89
|
});
|
|
87
90
|
}
|
|
@@ -95,6 +98,21 @@ export class IMRenderer {
|
|
|
95
98
|
return;
|
|
96
99
|
if (this.textBuffer.length === 0)
|
|
97
100
|
return;
|
|
101
|
+
// 文件标记过滤后若只剩空白,应立即消费并告警;尤其避免 final-only
|
|
102
|
+
// 模式把工具调用前的空白继续带到最终回复。
|
|
103
|
+
const rawText = this.opts.fileMarkerPattern
|
|
104
|
+
? this.textBuffer.replace(this.opts.fileMarkerPattern, '')
|
|
105
|
+
: this.textBuffer;
|
|
106
|
+
if (!rawText.trim()) {
|
|
107
|
+
if (this.timer) {
|
|
108
|
+
clearTimeout(this.timer);
|
|
109
|
+
this.timer = undefined;
|
|
110
|
+
}
|
|
111
|
+
this.textBuffer = '';
|
|
112
|
+
this.itemsQueue = this.itemsQueue.filter(it => it.kind !== 'text');
|
|
113
|
+
this.hasSendableText(rawText, 'flushText');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
98
116
|
if (this.suppressIntermediateText()) {
|
|
99
117
|
this.itemsQueue = this.itemsQueue.filter(it => it.kind !== 'text');
|
|
100
118
|
return;
|
|
@@ -103,16 +121,15 @@ export class IMRenderer {
|
|
|
103
121
|
clearTimeout(this.timer);
|
|
104
122
|
this.timer = undefined;
|
|
105
123
|
}
|
|
106
|
-
// 文件标记过滤:tool_use 前的文本块也不能把 marker 暴露给用户
|
|
107
|
-
const rawText = this.opts.fileMarkerPattern
|
|
108
|
-
? this.textBuffer.replace(this.opts.fileMarkerPattern, '')
|
|
109
|
-
: this.textBuffer;
|
|
110
124
|
this.textBuffer = '';
|
|
111
|
-
|
|
112
|
-
return;
|
|
113
|
-
// 清掉 itemsQueue 中的 text items(已发出)
|
|
125
|
+
// 清掉已消费的 text items,包括被拦截的纯空白正文。
|
|
114
126
|
this.itemsQueue = this.itemsQueue.filter(it => it.kind !== 'text');
|
|
115
|
-
const payload = {
|
|
127
|
+
const payload = {
|
|
128
|
+
kind: 'result.text',
|
|
129
|
+
text: rawText,
|
|
130
|
+
isFinal: false,
|
|
131
|
+
...this.forkAnchorMetadata(),
|
|
132
|
+
};
|
|
116
133
|
this.sentContent = true;
|
|
117
134
|
try {
|
|
118
135
|
await this.sendPayload(payload);
|
|
@@ -158,12 +175,18 @@ export class IMRenderer {
|
|
|
158
175
|
}
|
|
159
176
|
// ── 文本/活动注入(替代 StreamFlusher.addText/addActivity)──
|
|
160
177
|
/** 添加文本片段(流式 text) */
|
|
161
|
-
addText(text, outputTokens, turn) {
|
|
178
|
+
async addText(text, outputTokens, turn, assistantUuid) {
|
|
162
179
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
163
180
|
return;
|
|
181
|
+
if (this.textBuffer && this.currentAssistantUuid !== assistantUuid && !this.suppressIntermediateText()) {
|
|
182
|
+
await this.flushText();
|
|
183
|
+
}
|
|
184
|
+
// Do not carry the previous item's anchor onto unanchored output.
|
|
185
|
+
this.currentAssistantUuid = assistantUuid;
|
|
164
186
|
this.emitProgress('text', outputTokens, turn);
|
|
165
187
|
if (!text)
|
|
166
188
|
return;
|
|
189
|
+
// 保留流式片段之间的空格/换行;仅在发送完整正文时判定是否纯空白。
|
|
167
190
|
// 同一窗口内连续 text delta 合并到最后一个 text item
|
|
168
191
|
const last = this.itemsQueue[this.itemsQueue.length - 1];
|
|
169
192
|
if (last && last.kind === 'text') {
|
|
@@ -184,18 +207,19 @@ export class IMRenderer {
|
|
|
184
207
|
this.emitProgress('tool_call', outputTokens, turn, { toolName: name, callId });
|
|
185
208
|
if (this.suppressActivityItems())
|
|
186
209
|
return;
|
|
210
|
+
const displayInput = toolInputForDisplay(name, input);
|
|
187
211
|
this.itemsQueue.push({
|
|
188
212
|
kind: 'tool_call',
|
|
189
213
|
call_id: callId || this.synthCallId(),
|
|
190
214
|
name,
|
|
191
|
-
arguments:
|
|
192
|
-
text: descText,
|
|
215
|
+
arguments: displayInput,
|
|
216
|
+
text: displayInput !== input ? summarizeToolInput(name, displayInput || {}) : descText,
|
|
193
217
|
});
|
|
194
218
|
this.messageTimestamps.push(Date.now());
|
|
195
219
|
this.scheduleFlush();
|
|
196
220
|
}
|
|
197
221
|
/** 添加工具结果 */
|
|
198
|
-
addToolResult(name, ok, result, error, callId, durationMs, descText) {
|
|
222
|
+
addToolResult(name, ok, result, error, callId, durationMs, descText, classification) {
|
|
199
223
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
200
224
|
return;
|
|
201
225
|
this.emitProgress('tool_result', undefined, undefined, { toolName: name, callId, ok, durationMs });
|
|
@@ -208,6 +232,7 @@ export class IMRenderer {
|
|
|
208
232
|
ok,
|
|
209
233
|
...(result !== undefined && { result }),
|
|
210
234
|
...(error !== undefined && { error }),
|
|
235
|
+
...(!ok && { error_code: classifyAunToolErrorCode(classification ?? {}) }),
|
|
211
236
|
...(durationMs !== undefined && { duration_ms: durationMs }),
|
|
212
237
|
...(descText !== undefined && { text: descText }),
|
|
213
238
|
});
|
|
@@ -249,6 +274,19 @@ export class IMRenderer {
|
|
|
249
274
|
logger.warn(`[IMRenderer] status notice send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
250
275
|
}
|
|
251
276
|
}
|
|
277
|
+
/** Notify the client that a new model text block has started. */
|
|
278
|
+
async sendTextStart(contentIndex, turn) {
|
|
279
|
+
const payload = {
|
|
280
|
+
kind: 'status.progress',
|
|
281
|
+
metadata: { activityType: 'text', kind: 'text_start', contentIndex, turn },
|
|
282
|
+
};
|
|
283
|
+
try {
|
|
284
|
+
await this.opts.send(payload);
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
logger.debug(`[IMRenderer] text_start status send failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
252
290
|
/** 兼容旧调用方:发送 warning 级别的任务 notice。 */
|
|
253
291
|
async sendWarningStatus(message, subtype, details) {
|
|
254
292
|
return this.sendStatusNotice(message, 'warn', subtype, details);
|
|
@@ -302,9 +340,15 @@ export class IMRenderer {
|
|
|
302
340
|
async sendOperationalNoticeAsText(text) {
|
|
303
341
|
if (!this.opts.operationalNoticesAsText || this.opts.envelope.chatmode === 'proactive' || !text)
|
|
304
342
|
return false;
|
|
343
|
+
if (!this.hasSendableText(text, 'operational-notice'))
|
|
344
|
+
return false;
|
|
305
345
|
// 保持与流式正文的到达顺序:先发出已缓冲的正文,再投影运行状态。
|
|
306
346
|
await this.flushText();
|
|
307
|
-
const payload = {
|
|
347
|
+
const payload = {
|
|
348
|
+
kind: 'result.text',
|
|
349
|
+
text,
|
|
350
|
+
isFinal: false,
|
|
351
|
+
};
|
|
308
352
|
this.sentContent = true;
|
|
309
353
|
await this.sendPayload(payload);
|
|
310
354
|
this.lastFlush = Date.now();
|
|
@@ -315,6 +359,17 @@ export class IMRenderer {
|
|
|
315
359
|
synthCallId() {
|
|
316
360
|
return `synth-${this.opts.envelope.taskId}-${++this.syntheticCallSeq}`;
|
|
317
361
|
}
|
|
362
|
+
/** Reject blank bodies before sending, without trimming meaningful formatting. */
|
|
363
|
+
hasSendableText(text, source) {
|
|
364
|
+
if (text.trim())
|
|
365
|
+
return true;
|
|
366
|
+
// An unused buffer is normal. A nonempty whitespace body is diagnostic evidence.
|
|
367
|
+
if (text.length > 0) {
|
|
368
|
+
const { envelope, agentAid } = this.opts;
|
|
369
|
+
logger.warn(`[IMRenderer] Attempted to send empty message, skipping: code=EMPTY_MESSAGE source=${source} task=${envelope.taskId} session=${envelope.sessionId ?? '<none>'} agent=${agentAid ?? envelope.agentName} textLength=${text.length} text=${JSON.stringify(text.slice(0, 80))}`);
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
318
373
|
/** Convert a structured failed receipt into the same error shape as a rejected sender. */
|
|
319
374
|
receiptError(receipt) {
|
|
320
375
|
return Object.assign(new Error(receipt.error), {
|
|
@@ -331,6 +386,18 @@ export class IMRenderer {
|
|
|
331
386
|
: undefined;
|
|
332
387
|
return value.code === 'OUTBOX_FULL' || cause?.code === 'OUTBOX_FULL';
|
|
333
388
|
}
|
|
389
|
+
isPermanentActivityError(error) {
|
|
390
|
+
const value = error && typeof error === 'object' ? error : {};
|
|
391
|
+
const cause = value.cause && typeof value.cause === 'object'
|
|
392
|
+
? value.cause
|
|
393
|
+
: undefined;
|
|
394
|
+
const code = String(value.code ?? cause?.code ?? '');
|
|
395
|
+
return value.permanent === true
|
|
396
|
+
|| value.status === 'failed'
|
|
397
|
+
|| code === 'AUN_SEND_FAILED'
|
|
398
|
+
|| code === '-1'
|
|
399
|
+
|| /payload is too large/i.test(String(value.message ?? value.error ?? ''));
|
|
400
|
+
}
|
|
334
401
|
/**
|
|
335
402
|
* Serialize durable sends while keeping the chain usable after a rejected
|
|
336
403
|
* operation. Activity queue saturation is best-effort: it is logged, but
|
|
@@ -344,9 +411,19 @@ export class IMRenderer {
|
|
|
344
411
|
}
|
|
345
412
|
});
|
|
346
413
|
const handled = operation.catch(error => {
|
|
347
|
-
if (payload.kind === 'activity.batch'
|
|
348
|
-
|
|
349
|
-
|
|
414
|
+
if (payload.kind === 'activity.batch') {
|
|
415
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
416
|
+
if (this.isOutboxFull(error)) {
|
|
417
|
+
logger.warn(`[IMRenderer] activity outbox full; continuing without activity batch: ${detail}`);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (this.isPermanentActivityError(error)) {
|
|
421
|
+
// Activity is best-effort. A permanent structured-send failure (for
|
|
422
|
+
// example an AUN payload-size rejection) must not requeue the same
|
|
423
|
+
// item indefinitely or prevent the final result.text from flushing.
|
|
424
|
+
logger.warn(`[IMRenderer] activity delivery failed; dropping activity batch and continuing: ${detail}`);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
350
427
|
}
|
|
351
428
|
throw error;
|
|
352
429
|
});
|
|
@@ -424,7 +501,7 @@ export class IMRenderer {
|
|
|
424
501
|
* 完整 flush:把 itemsQueue 里所有 items 打包成 activity.batch 发送。
|
|
425
502
|
* 如果 isFinal=true,还会在 batch 之后单独发一条 result.text 作为最终回复。
|
|
426
503
|
*/
|
|
427
|
-
async flushInternal(isFinal) {
|
|
504
|
+
async flushInternal(isFinal, attention) {
|
|
428
505
|
if (this.timer) {
|
|
429
506
|
clearTimeout(this.timer);
|
|
430
507
|
this.timer = undefined;
|
|
@@ -432,26 +509,30 @@ export class IMRenderer {
|
|
|
432
509
|
// 文件标记过滤:marker 任何时候都不该出现在用户文本里,必须在 *每次* flush
|
|
433
510
|
// (含非最终的定时 flush)执行。否则文本块若滞留 buffer 后被定时器触发,会走
|
|
434
511
|
// 下方非-final 的 result.text 路径,把 [SEND_FILE:...] 原样发给用户。
|
|
435
|
-
//
|
|
512
|
+
// 先保留空白,以便空正文告警仍能记录原长度。
|
|
436
513
|
if (this.opts.fileMarkerPattern) {
|
|
437
|
-
|
|
438
|
-
const replaced = s.replace(this.opts.fileMarkerPattern, '');
|
|
439
|
-
return isFinal ? replaced.trim() : replaced;
|
|
440
|
-
};
|
|
441
|
-
this.textBuffer = strip(this.textBuffer);
|
|
514
|
+
this.textBuffer = this.textBuffer.replace(this.opts.fileMarkerPattern, '');
|
|
442
515
|
for (const item of this.itemsQueue) {
|
|
443
516
|
if (item.kind === 'text')
|
|
444
517
|
item.text = item.text.replace(this.opts.fileMarkerPattern, '');
|
|
445
518
|
}
|
|
446
519
|
}
|
|
520
|
+
if (!this.hasSendableText(this.textBuffer, isFinal ? 'final' : 'flush')) {
|
|
521
|
+
this.textBuffer = '';
|
|
522
|
+
}
|
|
523
|
+
else if (isFinal && this.opts.fileMarkerPattern) {
|
|
524
|
+
// 保持既有最终文件标记过滤语义;中间文本的格式原样保留。
|
|
525
|
+
this.textBuffer = this.textBuffer.trim();
|
|
526
|
+
}
|
|
447
527
|
// 清掉空 text items
|
|
448
528
|
const items = this.itemsQueue.filter(it => {
|
|
449
529
|
if (it.kind === 'text')
|
|
450
|
-
return it.text.length > 0;
|
|
530
|
+
return it.text.trim().length > 0;
|
|
451
531
|
return true;
|
|
452
532
|
});
|
|
453
533
|
this.itemsQueue = [];
|
|
454
534
|
const finalText = isFinal ? this.textBuffer : '';
|
|
535
|
+
const finalAnchor = this.forkAnchorMetadata();
|
|
455
536
|
if (isFinal)
|
|
456
537
|
this.textBuffer = '';
|
|
457
538
|
// 1. interactive 模式下:不发 text items(由 result.text 统一发送最终文本)
|
|
@@ -483,7 +564,12 @@ export class IMRenderer {
|
|
|
483
564
|
return;
|
|
484
565
|
const text = this.textBuffer;
|
|
485
566
|
this.textBuffer = '';
|
|
486
|
-
const payload = {
|
|
567
|
+
const payload = {
|
|
568
|
+
kind: 'result.text',
|
|
569
|
+
text,
|
|
570
|
+
isFinal: false,
|
|
571
|
+
...this.forkAnchorMetadata(),
|
|
572
|
+
};
|
|
487
573
|
this.sentContent = true;
|
|
488
574
|
try {
|
|
489
575
|
await this.sendPayload(payload);
|
|
@@ -497,7 +583,14 @@ export class IMRenderer {
|
|
|
497
583
|
}
|
|
498
584
|
// 2. isFinal=true 时单独发最终回复文本
|
|
499
585
|
if (isFinal && finalText.length > 0) {
|
|
500
|
-
const payload = {
|
|
586
|
+
const payload = {
|
|
587
|
+
kind: 'result.text',
|
|
588
|
+
text: finalText,
|
|
589
|
+
isFinal: true,
|
|
590
|
+
...finalAnchor,
|
|
591
|
+
};
|
|
592
|
+
if (attention !== undefined)
|
|
593
|
+
payload.attention = attention;
|
|
501
594
|
this.sentContent = true;
|
|
502
595
|
try {
|
|
503
596
|
await this.sendPayload(payload);
|
|
@@ -510,6 +603,15 @@ export class IMRenderer {
|
|
|
510
603
|
this.flushCount++;
|
|
511
604
|
}
|
|
512
605
|
}
|
|
606
|
+
forkAnchorMetadata() {
|
|
607
|
+
const assistantMessageId = this.currentAssistantUuid?.trim();
|
|
608
|
+
if (!assistantMessageId)
|
|
609
|
+
return {};
|
|
610
|
+
const anchor = {
|
|
611
|
+
assistantMessageId,
|
|
612
|
+
};
|
|
613
|
+
return { metadata: { forkAnchor: anchor } };
|
|
614
|
+
}
|
|
513
615
|
// ── 内部:status.progress 发送 ──
|
|
514
616
|
emitProgress(activityType, outputTokens, turn, extra) {
|
|
515
617
|
if (this.suppressActivityItems())
|
|
@@ -543,6 +645,15 @@ export class IMRenderer {
|
|
|
543
645
|
const item = this.mapEventToItem(event);
|
|
544
646
|
if (!item)
|
|
545
647
|
return;
|
|
648
|
+
// Proactive output is optional thought content. Blank runner blocks are
|
|
649
|
+
// normal; keep diagnostics without reporting a failed message send.
|
|
650
|
+
if ((item.kind === 'text' || (item.kind === 'summary' && !item.is_error)) && !item.text.trim()) {
|
|
651
|
+
const { envelope, agentAid } = this.opts;
|
|
652
|
+
logger.debug(`[IMRenderer] Skipping blank proactive thought: kind=${item.kind}`
|
|
653
|
+
+ ` task=${envelope.taskId} session=${envelope.sessionId ?? '<none>'}`
|
|
654
|
+
+ ` agent=${agentAid ?? envelope.agentName} textLength=${item.text.length}`);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
546
657
|
if (item.kind === 'text') {
|
|
547
658
|
this.hasEmittedText = true;
|
|
548
659
|
this.allText += item.text;
|
|
@@ -571,7 +682,7 @@ export class IMRenderer {
|
|
|
571
682
|
return null;
|
|
572
683
|
return { kind: 'text', text: event.text };
|
|
573
684
|
case 'tool_use': {
|
|
574
|
-
const desc =
|
|
685
|
+
const desc = summarizeToolInputForDisplay(event.name, event.input || {});
|
|
575
686
|
const item = {
|
|
576
687
|
kind: 'tool_call',
|
|
577
688
|
call_id: event.callId || this.synthCallId(),
|
|
@@ -589,6 +700,7 @@ export class IMRenderer {
|
|
|
589
700
|
call_id: event.callId || this.synthCallId(),
|
|
590
701
|
name: event.name,
|
|
591
702
|
ok: false,
|
|
703
|
+
error_code: classifyAunToolErrorCode(event),
|
|
592
704
|
error: event.error || (typeof event.result === 'string' ? event.result : '执行失败'),
|
|
593
705
|
...(event.durationMs !== undefined && { duration_ms: event.durationMs }),
|
|
594
706
|
};
|
|
@@ -633,7 +745,7 @@ export class IMRenderer {
|
|
|
633
745
|
if (isContextTooLongText(event.error || ''))
|
|
634
746
|
return null;
|
|
635
747
|
const text = getErrorMessage(new Error(event.error || '任务执行失败'), undefined, false);
|
|
636
|
-
return { kind: 'notice', text, severity: 'warn' };
|
|
748
|
+
return { kind: 'notice', text: tagErrorTextWithEvolcoreVersion(text), severity: 'warn' };
|
|
637
749
|
}
|
|
638
750
|
case 'complete': {
|
|
639
751
|
if (event.queryFinal === false)
|
|
@@ -650,7 +762,7 @@ export class IMRenderer {
|
|
|
650
762
|
const text = getErrorMessage(new Error(errText), event.terminalReason, false);
|
|
651
763
|
const item = {
|
|
652
764
|
kind: 'summary',
|
|
653
|
-
text,
|
|
765
|
+
text: tagErrorTextWithEvolcoreVersion(text),
|
|
654
766
|
is_error: true,
|
|
655
767
|
};
|
|
656
768
|
if (event.subtype !== undefined)
|