evolcore 0.0.21 → 0.0.22
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 +43 -0
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +3 -1
- package/dist/agents/claude-runner.js +14 -0
- package/dist/agents/codex-app-server-client.js +31 -5
- package/dist/agents/codex-runner.js +926 -121
- package/dist/aun/outbox.js +7 -0
- package/dist/channels/aun.js +209 -35
- package/dist/cli/daemon-commands.js +29 -8
- package/dist/cli/task-context.js +4 -0
- package/dist/cli/trigger-command.js +13 -4
- package/dist/config/config-field-policy.js +3 -0
- package/dist/config/config-manager.js +32 -5
- package/dist/config/contact-book-store.js +25 -3
- package/dist/core/auth/agent-delegation.js +12 -0
- package/dist/core/auth/auth-gateway.js +8 -0
- package/dist/core/auth/authorization-audit.js +66 -6
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +93 -25
- package/dist/core/command/command-handler.js +21 -0
- package/dist/core/command/menu-handler.js +9 -0
- package/dist/core/command/menu-protocol.js +1 -1
- package/dist/core/command/slash-handler.js +41 -18
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +32 -0
- package/dist/core/handoff/runtime.js +23 -3
- package/dist/core/message/im-renderer.js +7 -3
- package/dist/core/message/message-bridge.js +60 -2
- package/dist/core/message/message-log.js +33 -0
- package/dist/core/message/message-queue.js +21 -0
- package/dist/core/message/response-engine.js +172 -41
- package/dist/core/permission/ec-command-parser.js +272 -70
- package/dist/core/permission/protected-paths.js +11 -10
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +46 -5
- package/dist/core/session/session-manager.js +30 -0
- package/dist/core/session/session-renew.js +18 -1
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/index.js +64 -5
- package/dist/ipc.js +97 -17
- package/dist/paths.js +18 -0
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/feedback.js +14 -2
- package/dist/trigger/parser.js +10 -1
- package/dist/trigger/scheduler.js +20 -3
- package/dist/utils/logger.js +9 -4
- package/dist/utils/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/evolcore/INDEX.md +2 -2
- package/kits/docs/evolcore/agent-create.md +146 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/group-collaboration.md +251 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/schemas/_meta.json +3 -2
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/templates/message-fragments/item.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +2 -1
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +2 -2
|
@@ -37,7 +37,7 @@ import { resolveAgentLifecycle } from '../../config/lifecycle.js';
|
|
|
37
37
|
import { authorizationConfigRevision, checkRoleAccess, getFirstStaticAgentOwner, listStaticAgentAdmins, listStaticAgentOwners, resolvePeerRoleDetail, roleToSessionIdentity, } from '../../config/peer-role-resolver.js';
|
|
38
38
|
import { insertUsageEvent, insertContextBreakdown, insertModelCalls } from '../../stats/writer.js';
|
|
39
39
|
import { normalizeUsage } from '../../stats/normalizer.js';
|
|
40
|
-
import { resolvePrices } from '../../stats/price-resolver.js';
|
|
40
|
+
import { resolvePrices, roundCostForOutput } from '../../stats/price-resolver.js';
|
|
41
41
|
import { getBudgetStatus } from '../../stats/budget.js';
|
|
42
42
|
import { formatUsageSubjectKey, getRoleBudgetStatus } from '../../stats/role-budget.js';
|
|
43
43
|
import { snapshot } from './response-snapshot.js';
|
|
@@ -47,7 +47,7 @@ import { registerBuiltinModes } from '../../response-system/modes/index.js';
|
|
|
47
47
|
import { deriveSessionTitle, shouldAutoFillSessionTitle } from '../session/session-title.js';
|
|
48
48
|
import { SessionTurnCoordinator } from '../session/session-turn-coordinator.js';
|
|
49
49
|
import { recordTriggerExecutionAnomaly } from '../../trigger/anomaly-store.js';
|
|
50
|
-
import { classifyToolErrorCode } from '../permission/tool-error-code.js';
|
|
50
|
+
import { classifyToolErrorCode, normalizeToolErrorCode } from '../permission/tool-error-code.js';
|
|
51
51
|
import { normalizeExecutionPermissionMode } from '../permission/mode.js';
|
|
52
52
|
import { buildToolLifecycleEventKey } from '../audit/event-key.js';
|
|
53
53
|
import { isFullAccessEnabled, loadDaemonConfig } from '../../config-store.js';
|
|
@@ -637,20 +637,44 @@ export class ResponseEngine {
|
|
|
637
637
|
// 监听中断事件,标记被中断的 session
|
|
638
638
|
this.eventBus.subscribe('task:interrupted', (event) => {
|
|
639
639
|
if ('sessionId' in event && event.sessionId) {
|
|
640
|
+
const eventIdentity = event;
|
|
641
|
+
const eventTaskId = eventIdentity.taskId;
|
|
642
|
+
const activeTask = this.activeTaskSpans.get(event.sessionId);
|
|
643
|
+
// A delayed interruption from an older task must never invalidate a
|
|
644
|
+
// newer task that has already claimed the same session.
|
|
645
|
+
if (eventTaskId && activeTask && activeTask.taskId !== eventTaskId)
|
|
646
|
+
return;
|
|
640
647
|
const reason = (event.reason || 'new_message');
|
|
641
|
-
this.turnCoordinator.invalidate(event.sessionId, reason);
|
|
648
|
+
const currentTurn = this.turnCoordinator.invalidate(event.sessionId, reason, { taskId: eventTaskId, generation: eventIdentity.generation });
|
|
649
|
+
if ((eventTaskId || eventIdentity.generation !== undefined) && !currentTurn)
|
|
650
|
+
return;
|
|
642
651
|
this.interruptedSessions.set(event.sessionId, reason);
|
|
643
652
|
this.cancelRetryDelays(event.sessionId);
|
|
644
|
-
|
|
653
|
+
// The event can be delayed relative to a newer task on the same
|
|
654
|
+
// session. Revoke only when the producer supplied the matching task;
|
|
655
|
+
// the interrupt path below performs the authoritative task-scoped
|
|
656
|
+
// revocation for legacy events without a taskId.
|
|
657
|
+
const interruptedTaskId = eventTaskId;
|
|
658
|
+
if (interruptedTaskId && activeTask?.taskId === interruptedTaskId) {
|
|
659
|
+
this.agentDelegationRegistry?.revokeTask(event.sessionId, interruptedTaskId);
|
|
660
|
+
}
|
|
645
661
|
}
|
|
646
662
|
});
|
|
647
663
|
this.eventBus.subscribe('task:completed', event => {
|
|
648
|
-
if ('sessionId' in event && event.sessionId)
|
|
649
|
-
|
|
664
|
+
if ('sessionId' in event && event.sessionId) {
|
|
665
|
+
const taskId = event.taskId;
|
|
666
|
+
const active = this.activeTaskSpans.get(event.sessionId);
|
|
667
|
+
if (!taskId || active?.taskId === taskId)
|
|
668
|
+
this.activeTaskSpans.delete(event.sessionId);
|
|
669
|
+
}
|
|
650
670
|
});
|
|
651
671
|
this.eventBus.subscribe('task:error', event => {
|
|
652
|
-
if ('sessionId' in event && event.sessionId)
|
|
653
|
-
|
|
672
|
+
if ('sessionId' in event && event.sessionId) {
|
|
673
|
+
const taskId = event.taskId;
|
|
674
|
+
const active = this.activeTaskSpans.get(event.sessionId);
|
|
675
|
+
if (!taskId || active?.taskId === taskId)
|
|
676
|
+
this.activeTaskSpans.delete(event.sessionId);
|
|
677
|
+
}
|
|
654
678
|
});
|
|
655
679
|
// 初始化响应模式协调器,注册内置模式(interactive/proactive)
|
|
656
680
|
const registry = new ResponseModeRegistry();
|
|
@@ -1274,7 +1298,7 @@ export class ResponseEngine {
|
|
|
1274
1298
|
causation: opts.causation ?? message.causation,
|
|
1275
1299
|
});
|
|
1276
1300
|
}
|
|
1277
|
-
publishTriggerExecutionSkipped(message, reason, causation) {
|
|
1301
|
+
publishTriggerExecutionSkipped(message, reason, causation, interruption) {
|
|
1278
1302
|
const terminal = this.claimTriggerTerminal(message);
|
|
1279
1303
|
if (!terminal)
|
|
1280
1304
|
return;
|
|
@@ -1287,6 +1311,7 @@ export class ResponseEngine {
|
|
|
1287
1311
|
attemptId: trigger.attemptId,
|
|
1288
1312
|
originTriggerId: trigger.triggerId,
|
|
1289
1313
|
reason,
|
|
1314
|
+
...(interruption ?? {}),
|
|
1290
1315
|
targetChannel: message.channel,
|
|
1291
1316
|
targetChannelId: message.channelId,
|
|
1292
1317
|
fireTime: trigger.fireTime,
|
|
@@ -1294,16 +1319,25 @@ export class ResponseEngine {
|
|
|
1294
1319
|
});
|
|
1295
1320
|
}
|
|
1296
1321
|
/** Close the internal daemon conversation before returning from an interrupted Trigger turn. */
|
|
1297
|
-
async publishTriggerExecutionInterrupted(message, adapter, envelope, reason, causation) {
|
|
1322
|
+
async publishTriggerExecutionInterrupted(message, adapter, envelope, reason, causation, generation) {
|
|
1323
|
+
const interruption = reason === 'stale_generation'
|
|
1324
|
+
? {
|
|
1325
|
+
reasonCode: 'stale_generation',
|
|
1326
|
+
decisionSource: 'infrastructure',
|
|
1327
|
+
executionState: 'interrupted',
|
|
1328
|
+
...(generation?.expected !== undefined ? { generation: generation.expected } : {}),
|
|
1329
|
+
...(generation?.current !== undefined ? { currentGeneration: generation.current } : {}),
|
|
1330
|
+
}
|
|
1331
|
+
: undefined;
|
|
1298
1332
|
if (this.isTrustedDaemonTrigger(message)) {
|
|
1299
1333
|
await adapter.send(envelope, {
|
|
1300
1334
|
kind: 'status.interrupted',
|
|
1301
|
-
metadata: { reason },
|
|
1335
|
+
metadata: { reason, ...(interruption ?? {}) },
|
|
1302
1336
|
}).catch(error => {
|
|
1303
1337
|
logger.warn(`[ResponseEngine] Failed to close interrupted Trigger run=${message.triggerMeta?.runId ?? '<unknown>'}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1304
1338
|
});
|
|
1305
1339
|
}
|
|
1306
|
-
this.publishTriggerExecutionSkipped(message, reason, causation);
|
|
1340
|
+
this.publishTriggerExecutionSkipped(message, reason, causation, interruption);
|
|
1307
1341
|
}
|
|
1308
1342
|
publishTriggerExecutionCompleted(message, messageId, durationMs, causation) {
|
|
1309
1343
|
const terminal = this.claimTriggerTerminal(message);
|
|
@@ -2130,6 +2164,8 @@ export class ResponseEngine {
|
|
|
2130
2164
|
this.eventBus.publish({
|
|
2131
2165
|
type: 'task:error',
|
|
2132
2166
|
sessionId: session.id,
|
|
2167
|
+
taskId,
|
|
2168
|
+
generation: turnLease?.generation,
|
|
2133
2169
|
error: timeoutError.message,
|
|
2134
2170
|
errorType,
|
|
2135
2171
|
agentName: agentNameForStats,
|
|
@@ -2255,7 +2291,10 @@ export class ResponseEngine {
|
|
|
2255
2291
|
const contentPreview = formatInboundMessageLogText(message.content);
|
|
2256
2292
|
logger.info(`[${message.channel}] ${message.channelId}: ${contentPreview}${imageInfo}${modeInfo}${e2eeInfo}`);
|
|
2257
2293
|
// 构建 peer 标识(优先 peerName,退化到 peerId / channelId)
|
|
2258
|
-
|
|
2294
|
+
// Group sessions are shared by multiple senders. The current inbound
|
|
2295
|
+
// message is authoritative; session metadata is only a fallback for
|
|
2296
|
+
// legacy paths that do not carry a per-message display name.
|
|
2297
|
+
const peerName = message.peerName ?? session.metadata?.peerName;
|
|
2259
2298
|
const peerId = session.metadata?.peerId ?? message.peerId ?? message.channelId;
|
|
2260
2299
|
const peerShort = peerId ? peerId.split('.')[0].split(':')[0] : '?';
|
|
2261
2300
|
const peerLabel = peerName && peerName !== peerShort ? `${peerShort}(${peerName})` : peerShort;
|
|
@@ -2266,7 +2305,16 @@ export class ResponseEngine {
|
|
|
2266
2305
|
turnLease = await this.turnCoordinator.begin(session, taskId);
|
|
2267
2306
|
// 记录开始处理
|
|
2268
2307
|
const taskEncrypt = message.replyContext?.metadata?.encrypted != null ? !!(message.replyContext.metadata.encrypted) : undefined;
|
|
2269
|
-
this.eventBus.publish({
|
|
2308
|
+
this.eventBus.publish({
|
|
2309
|
+
type: 'task:started',
|
|
2310
|
+
sessionId: session.id,
|
|
2311
|
+
taskId,
|
|
2312
|
+
generation: turnLease.generation,
|
|
2313
|
+
agentName: agentNameForStats,
|
|
2314
|
+
encrypt: taskEncrypt,
|
|
2315
|
+
chatmode,
|
|
2316
|
+
causation: taskCausation,
|
|
2317
|
+
});
|
|
2270
2318
|
this.touchAgentActivity(channelKey);
|
|
2271
2319
|
// Upgrade the channel acknowledgement at task start, before compaction and
|
|
2272
2320
|
// runner setup, so processing feedback remains visible for the whole run.
|
|
@@ -2292,6 +2340,7 @@ export class ResponseEngine {
|
|
|
2292
2340
|
adapter,
|
|
2293
2341
|
envelope,
|
|
2294
2342
|
agentAid: session.selfAID,
|
|
2343
|
+
permissionMode: () => effectivePermissionMode,
|
|
2295
2344
|
flushDelay: (options?.flushDelay ?? this.agentRegistry?.resolveByChannel(channelKey)?.config?.flush_delay ?? 3) * 1000,
|
|
2296
2345
|
suppressActivityItems: isProactive ? false : middleOutputMode !== 'all',
|
|
2297
2346
|
suppressIntermediateText: isProactive ? false : middleOutputMode === 'none',
|
|
@@ -3108,7 +3157,7 @@ export class ResponseEngine {
|
|
|
3108
3157
|
}];
|
|
3109
3158
|
const peerItems = (() => {
|
|
3110
3159
|
if (message.handoffDelivery && this.handoffRuntime) {
|
|
3111
|
-
const items = this.handoffRuntime.buildPromptItems(message);
|
|
3160
|
+
const items = this.handoffRuntime.buildPromptItems(message, peerRole);
|
|
3112
3161
|
if (items.length > 0) {
|
|
3113
3162
|
v2HandoffIds = Array.from(new Set(items.flatMap(item => (item.handoff?.handoffIds?.length
|
|
3114
3163
|
? item.handoff.handoffIds
|
|
@@ -3205,12 +3254,14 @@ export class ResponseEngine {
|
|
|
3205
3254
|
peerName: peerName || undefined,
|
|
3206
3255
|
peerType: message.peerType || session.metadata?.peerType || undefined,
|
|
3207
3256
|
peerRole,
|
|
3257
|
+
permissionMode: effectivePermissionMode,
|
|
3208
3258
|
...(fullAccessAuthorization ? {
|
|
3209
3259
|
processRole: fullAccessAuthorization.processRole,
|
|
3210
3260
|
dataScope: fullAccessAuthorization.dataScope,
|
|
3211
3261
|
authorizedBy: fullAccessAuthorization.authorizedBy,
|
|
3212
3262
|
executionSource: fullAccessAuthorization.source,
|
|
3213
3263
|
} : {}),
|
|
3264
|
+
daemonRuntimeEpoch: this.agentDelegationRegistry?.getRuntimeEpoch(),
|
|
3214
3265
|
threadId: session.threadId || undefined,
|
|
3215
3266
|
sessionRuntimeDir,
|
|
3216
3267
|
runtimeLockDir: ensureRuntimeLockDir(sessionRuntimeDir),
|
|
@@ -3547,7 +3598,10 @@ export class ResponseEngine {
|
|
|
3547
3598
|
}
|
|
3548
3599
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
3549
3600
|
logger.info(`[ResponseEngine] Stale turn stopped before publish: session=${session.id} task=${taskId}`);
|
|
3550
|
-
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation
|
|
3601
|
+
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation, {
|
|
3602
|
+
expected: turnLease?.generation,
|
|
3603
|
+
current: this.turnCoordinator.current(session).generation,
|
|
3604
|
+
});
|
|
3551
3605
|
return;
|
|
3552
3606
|
}
|
|
3553
3607
|
if (!commitDecision.ok) {
|
|
@@ -3559,7 +3613,10 @@ export class ResponseEngine {
|
|
|
3559
3613
|
}
|
|
3560
3614
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
3561
3615
|
logger.info(`[ResponseEngine] Stale turn stopped before protocol rejection: session=${session.id} task=${taskId}`);
|
|
3562
|
-
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation
|
|
3616
|
+
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation, {
|
|
3617
|
+
expected: turnLease?.generation,
|
|
3618
|
+
current: this.turnCoordinator.current(session).generation,
|
|
3619
|
+
});
|
|
3563
3620
|
return;
|
|
3564
3621
|
}
|
|
3565
3622
|
const reason = streamResult.protocolIncompleteReason || commitDecision.reason;
|
|
@@ -3761,7 +3818,10 @@ export class ResponseEngine {
|
|
|
3761
3818
|
if (!commitDecision.ok) {
|
|
3762
3819
|
logger.info(`[ResponseEngine] Turn commit rejected after flush: session=${session.id} task=${taskId} reason=${commitDecision.reason}`);
|
|
3763
3820
|
if (commitDecision.reason === 'stale_generation') {
|
|
3764
|
-
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, commitDecision.reason, taskCausation
|
|
3821
|
+
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, commitDecision.reason, taskCausation, {
|
|
3822
|
+
expected: turnLease?.generation,
|
|
3823
|
+
current: this.turnCoordinator.current(session).generation,
|
|
3824
|
+
});
|
|
3765
3825
|
}
|
|
3766
3826
|
else {
|
|
3767
3827
|
this.publishTriggerExecutionFailure(message, `turn_commit_rejected:${commitDecision.reason}`, {
|
|
@@ -3848,6 +3908,8 @@ export class ResponseEngine {
|
|
|
3848
3908
|
this.eventBus.publish({
|
|
3849
3909
|
type: 'task:error',
|
|
3850
3910
|
sessionId: session.id,
|
|
3911
|
+
taskId,
|
|
3912
|
+
generation: turnLease?.generation,
|
|
3851
3913
|
error: errorSummary,
|
|
3852
3914
|
errorType,
|
|
3853
3915
|
agentName: agentNameForStats,
|
|
@@ -3965,12 +4027,18 @@ export class ResponseEngine {
|
|
|
3965
4027
|
cache_read_tokens: sum.cache_read_tokens,
|
|
3966
4028
|
cache_creation_tokens: sum.cache_creation_tokens,
|
|
3967
4029
|
// 顶层 cost_usd/cost_cny 保持向后兼容 = 网关实际价
|
|
3968
|
-
cost_usd: sum.cost_gateway_usd,
|
|
3969
|
-
cost_cny: sum.cost_gateway_cny,
|
|
4030
|
+
cost_usd: roundCostForOutput(sum.cost_gateway_usd),
|
|
4031
|
+
cost_cny: roundCostForOutput(sum.cost_gateway_cny),
|
|
3970
4032
|
call_count: sum.calls,
|
|
3971
4033
|
cost: {
|
|
3972
|
-
official: {
|
|
3973
|
-
|
|
4034
|
+
official: {
|
|
4035
|
+
usd: roundCostForOutput(sum.cost_official_usd),
|
|
4036
|
+
cny: roundCostForOutput(sum.cost_official_cny),
|
|
4037
|
+
},
|
|
4038
|
+
gateway: {
|
|
4039
|
+
usd: roundCostForOutput(sum.cost_gateway_usd),
|
|
4040
|
+
cny: roundCostForOutput(sum.cost_gateway_cny),
|
|
4041
|
+
},
|
|
3974
4042
|
},
|
|
3975
4043
|
};
|
|
3976
4044
|
}
|
|
@@ -3988,10 +4056,13 @@ export class ResponseEngine {
|
|
|
3988
4056
|
}
|
|
3989
4057
|
else {
|
|
3990
4058
|
// cost 同时给原价(official)与网关实际价(gateway);顶层 cost_usd/cost_cny 保持向后兼容 = 网关价。
|
|
3991
|
-
const gatewayUsd = turnCost.gateway?.usd ?? turnCost.official?.usd ?? 0;
|
|
3992
|
-
const gatewayCny = turnCost.gateway?.cny ?? turnCost.official?.cny ?? 0;
|
|
4059
|
+
const gatewayUsd = roundCostForOutput(turnCost.gateway?.usd ?? turnCost.official?.usd ?? 0);
|
|
4060
|
+
const gatewayCny = roundCostForOutput(turnCost.gateway?.cny ?? turnCost.official?.cny ?? 0);
|
|
3993
4061
|
const turnCostBlock = {
|
|
3994
|
-
official: {
|
|
4062
|
+
official: {
|
|
4063
|
+
usd: roundCostForOutput(turnCost.official?.usd ?? 0),
|
|
4064
|
+
cny: roundCostForOutput(turnCost.official?.cny ?? 0),
|
|
4065
|
+
},
|
|
3995
4066
|
gateway: { usd: gatewayUsd, cny: gatewayCny },
|
|
3996
4067
|
};
|
|
3997
4068
|
// 最后一次访问:本轮可能有多次大模型调用(numTurns>1),整轮的 turnCostBlock 不等于
|
|
@@ -4006,10 +4077,13 @@ export class ResponseEngine {
|
|
|
4006
4077
|
model: lastModel, turns: 1,
|
|
4007
4078
|
});
|
|
4008
4079
|
const lp = resolvePrices(resolveRoot(), lastEvent, agent.getGatewayPricing?.());
|
|
4009
|
-
const lpGwUsd = lp.gateway?.usd ?? lp.official?.usd ?? 0;
|
|
4010
|
-
const lpGwCny = lp.gateway?.cny ?? lp.official?.cny ?? 0;
|
|
4080
|
+
const lpGwUsd = roundCostForOutput(lp.gateway?.usd ?? lp.official?.usd ?? 0);
|
|
4081
|
+
const lpGwCny = roundCostForOutput(lp.gateway?.cny ?? lp.official?.cny ?? 0);
|
|
4011
4082
|
lastModelCall = { ...lastModelCall, cost: {
|
|
4012
|
-
official: {
|
|
4083
|
+
official: {
|
|
4084
|
+
usd: roundCostForOutput(lp.official?.usd ?? 0),
|
|
4085
|
+
cny: roundCostForOutput(lp.official?.cny ?? 0),
|
|
4086
|
+
},
|
|
4013
4087
|
gateway: { usd: lpGwUsd, cny: lpGwCny },
|
|
4014
4088
|
} };
|
|
4015
4089
|
}
|
|
@@ -4057,6 +4131,8 @@ export class ResponseEngine {
|
|
|
4057
4131
|
this.eventBus.publish({
|
|
4058
4132
|
type: 'task:completed',
|
|
4059
4133
|
sessionId: session.id,
|
|
4134
|
+
taskId,
|
|
4135
|
+
generation: turnLease.generation,
|
|
4060
4136
|
channel: message.channel,
|
|
4061
4137
|
channelId: message.channelId,
|
|
4062
4138
|
terminalReason: streamResult.terminalReason,
|
|
@@ -4188,6 +4264,8 @@ export class ResponseEngine {
|
|
|
4188
4264
|
this.eventBus.publish({
|
|
4189
4265
|
type: 'task:error',
|
|
4190
4266
|
sessionId: session.id,
|
|
4267
|
+
taskId,
|
|
4268
|
+
generation: turnLease?.generation,
|
|
4191
4269
|
error: errorMsg,
|
|
4192
4270
|
errorType,
|
|
4193
4271
|
agentName: agentNameForStats,
|
|
@@ -4208,14 +4286,15 @@ export class ResponseEngine {
|
|
|
4208
4286
|
}
|
|
4209
4287
|
// 发送用户友好的错误消息
|
|
4210
4288
|
// 用户主动中断(新消息打断 或 /stop 命令)时静默,不发送错误提示
|
|
4211
|
-
//
|
|
4289
|
+
// 普通渠道可跳过 renderer 已发送的重复错误;daemon trigger 仍必须
|
|
4290
|
+
// 发送结构化终态,否则 DaemonChannel 会一直等到 watchdog。
|
|
4212
4291
|
const retryExhaustedCount = getRetryExhaustedCount(error);
|
|
4213
4292
|
const modelFallbackExhaustedMessage = getModelFallbackExhaustedMessage(error);
|
|
4214
4293
|
const retryInputAlreadySubmitted = wasRetryInputAlreadySubmitted(error);
|
|
4215
4294
|
if (isUserInterrupt) {
|
|
4216
4295
|
logger.info(`[ResponseEngine] User interrupt by new_message, skip sending error message`);
|
|
4217
4296
|
}
|
|
4218
|
-
else if (error?._errorAlreadySent && !retryExhaustedCount && !isTimeout && !isTotalExecutionTimeout) {
|
|
4297
|
+
else if (error?._errorAlreadySent && !daemonTrigger && !retryExhaustedCount && !isTimeout && !isTotalExecutionTimeout) {
|
|
4219
4298
|
logger.info(`[ResponseEngine] Error already sent via renderer, skip sending duplicate message`);
|
|
4220
4299
|
}
|
|
4221
4300
|
else {
|
|
@@ -4633,11 +4712,17 @@ export class ResponseEngine {
|
|
|
4633
4712
|
if (event.type === 'complete') {
|
|
4634
4713
|
event = normalizeCompleteAgentEvent(event);
|
|
4635
4714
|
}
|
|
4636
|
-
if (event.type === 'tool_result' && event.isError
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4715
|
+
if (event.type === 'tool_result' && event.isError) {
|
|
4716
|
+
const classificationMissing = !normalizeToolErrorCode(event.errorCode)
|
|
4717
|
+
&& event.decisionSource !== 'policy'
|
|
4718
|
+
&& event.decisionSource !== 'approval';
|
|
4719
|
+
const errorCode = classifyToolErrorCode({
|
|
4720
|
+
errorCode: event.errorCode,
|
|
4721
|
+
decisionSource: event.decisionSource,
|
|
4722
|
+
});
|
|
4723
|
+
if (event.errorCode !== errorCode) {
|
|
4724
|
+
event = { ...event, errorCode, ...(classificationMissing ? { classificationMissing: true } : {}) };
|
|
4725
|
+
}
|
|
4641
4726
|
}
|
|
4642
4727
|
// 每收到事件重置空闲超时
|
|
4643
4728
|
const toolName = event.type === 'tool_use' ? event.name : undefined;
|
|
@@ -4740,7 +4825,13 @@ export class ResponseEngine {
|
|
|
4740
4825
|
eventDetail = ` tool=${event.name}${desc ? ` desc="${desc}"` : ''}`;
|
|
4741
4826
|
}
|
|
4742
4827
|
else if (event.type === 'tool_result') {
|
|
4743
|
-
|
|
4828
|
+
const decision = event.decision ?? (event.isError ? 'error' : 'allow');
|
|
4829
|
+
const executed = event.executed ?? !event.isError;
|
|
4830
|
+
const executionState = event.executionState
|
|
4831
|
+
?? (decision === 'deny' ? 'blocked' : event.isError ? 'failed' : 'completed');
|
|
4832
|
+
eventDetail = ` tool=${event.name} ok=${decision === 'allow' && executed}`
|
|
4833
|
+
+ ` decision=${decision} executed=${executed} executionState=${executionState}`
|
|
4834
|
+
+ (event.policyCode ? ` policy=${event.policyCode}` : '');
|
|
4744
4835
|
}
|
|
4745
4836
|
const frameworkEvents = new Set(['session_id', 'state_changed', 'status']);
|
|
4746
4837
|
if (frameworkEvents.has(event.type)) {
|
|
@@ -5006,6 +5097,16 @@ export class ResponseEngine {
|
|
|
5006
5097
|
toolName: event.name,
|
|
5007
5098
|
toolInput: event.input || {},
|
|
5008
5099
|
injectToModel: (text) => { agent.injectUserMessage?.(session.id, text); },
|
|
5100
|
+
recordReminder: reminder => {
|
|
5101
|
+
this.eventBus.publish({
|
|
5102
|
+
type: 'runner:proactive-reminder',
|
|
5103
|
+
sessionId: session.id,
|
|
5104
|
+
...reminder,
|
|
5105
|
+
injection: 'requested',
|
|
5106
|
+
timestamp: Date.now(),
|
|
5107
|
+
causation,
|
|
5108
|
+
});
|
|
5109
|
+
},
|
|
5009
5110
|
getQueueLength: () => this.messageQueue?.getQueueLength(session.id) ?? 0,
|
|
5010
5111
|
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, proactiveState?.chatType === 'group' ? 'group' : 'private', proactiveSelfAid || session.selfAID),
|
|
5011
5112
|
logger,
|
|
@@ -5031,6 +5132,7 @@ export class ResponseEngine {
|
|
|
5031
5132
|
sessionId: session.id,
|
|
5032
5133
|
callId: event.callId,
|
|
5033
5134
|
correlationId: event.correlationId,
|
|
5135
|
+
requestId: event.requestId,
|
|
5034
5136
|
});
|
|
5035
5137
|
this.eventBus.publish({
|
|
5036
5138
|
type: 'tool:result',
|
|
@@ -5039,16 +5141,29 @@ export class ResponseEngine {
|
|
|
5039
5141
|
sessionId: session.id,
|
|
5040
5142
|
toolName: event.name,
|
|
5041
5143
|
isError: event.isError,
|
|
5042
|
-
...(event.isError ? {
|
|
5144
|
+
...(event.isError ? {
|
|
5145
|
+
errorCode: classifyToolErrorCode({
|
|
5146
|
+
errorCode: event.errorCode,
|
|
5147
|
+
decisionSource: event.decisionSource,
|
|
5148
|
+
}),
|
|
5149
|
+
...((event.classificationMissing || (!normalizeToolErrorCode(event.errorCode)
|
|
5150
|
+
&& event.decisionSource !== 'policy'
|
|
5151
|
+
&& event.decisionSource !== 'approval')) ? { classificationMissing: true } : {}),
|
|
5152
|
+
} : {}),
|
|
5043
5153
|
...(event.callId ? { callId: event.callId } : {}),
|
|
5044
5154
|
...(event.correlationId || event.callId ? { correlationId: event.correlationId ?? event.callId } : {}),
|
|
5155
|
+
...(event.requestId ? { requestId: event.requestId } : {}),
|
|
5045
5156
|
agentName: lifecycleAgentName,
|
|
5046
5157
|
agentAid: lifecycleAgentAid ?? 'unknown',
|
|
5047
5158
|
permissionMode: lifecyclePermissionMode,
|
|
5048
|
-
decision: event.isError ? 'error' : 'allow',
|
|
5049
|
-
decisionSource: 'runner',
|
|
5050
|
-
|
|
5051
|
-
|
|
5159
|
+
decision: event.decision ?? (event.isError ? 'error' : 'allow'),
|
|
5160
|
+
decisionSource: event.decisionSource ?? 'runner',
|
|
5161
|
+
...(event.policyCode ? { policyCode: event.policyCode } : {}),
|
|
5162
|
+
...(event.reason ? { reason: event.reason } : {}),
|
|
5163
|
+
executed: event.executed ?? (event.decision === 'deny' ? false : true),
|
|
5164
|
+
executionState: event.executionState ?? (event.decision === 'deny'
|
|
5165
|
+
? 'blocked'
|
|
5166
|
+
: event.isError ? 'failed' : 'completed'),
|
|
5052
5167
|
timestamp: Date.now(),
|
|
5053
5168
|
causation,
|
|
5054
5169
|
});
|
|
@@ -5095,7 +5210,23 @@ export class ResponseEngine {
|
|
|
5095
5210
|
result: event.result,
|
|
5096
5211
|
isError: event.isError,
|
|
5097
5212
|
error: event.error,
|
|
5213
|
+
decision: event.decision,
|
|
5214
|
+
decisionSource: event.decisionSource,
|
|
5215
|
+
executed: event.executed,
|
|
5216
|
+
executionState: event.executionState,
|
|
5217
|
+
policyCode: event.policyCode,
|
|
5218
|
+
requestId: event.requestId,
|
|
5098
5219
|
injectToModel: (text) => { agent.injectUserMessage?.(session.id, text); },
|
|
5220
|
+
recordReminder: reminder => {
|
|
5221
|
+
this.eventBus.publish({
|
|
5222
|
+
type: 'runner:proactive-reminder',
|
|
5223
|
+
sessionId: session.id,
|
|
5224
|
+
...reminder,
|
|
5225
|
+
injection: 'requested',
|
|
5226
|
+
timestamp: Date.now(),
|
|
5227
|
+
causation,
|
|
5228
|
+
});
|
|
5229
|
+
},
|
|
5099
5230
|
getQueueLength: () => this.messageQueue?.getQueueLength(session.id) ?? 0,
|
|
5100
5231
|
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, proactiveState?.chatType === 'group' ? 'group' : 'private', proactiveSelfAid || session.selfAID),
|
|
5101
5232
|
logger,
|