evolcore 0.0.27 → 0.0.29-test.0
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 +38 -15
- package/bin/ec-safe-output.js +270 -54
- package/dist/agents/baseagent-availability.js +37 -1
- package/dist/agents/baseagent.js +29 -14
- package/dist/agents/claude-runner.js +171 -13
- package/dist/agents/codex/app-server-client.js +13 -2
- package/dist/agents/codex/catalog.js +7 -1
- package/dist/agents/codex/diagnostics.js +4 -3
- package/dist/agents/codex-runner.js +444 -106
- package/dist/agents/gemini-runner.js +35 -17
- package/dist/aun/group/history.js +130 -90
- package/dist/aun/group/operations.js +1 -3
- package/dist/aun/menu/client.js +1 -0
- package/dist/aun/menu/transport.js +5 -3
- package/dist/aun/msg/managed-operation.js +70 -0
- package/dist/aun/msg/p2p.js +1 -3
- package/dist/aun/msg/upload.js +2 -2
- package/dist/aun/outbox.js +1 -37
- package/dist/channels/aun.js +262 -86
- package/dist/channels/daemon.js +18 -1
- package/dist/channels/feishu.js +1 -2
- package/dist/cli/agent-command.js +23 -3
- package/dist/cli/agent-create.js +68 -2
- package/dist/cli/agent.js +12 -2
- package/dist/cli/aun-commands.js +87 -13
- package/dist/cli/cli-argv.js +26 -0
- package/dist/cli/daemon-commands.js +116 -149
- package/dist/cli/data-command.js +4 -4
- package/dist/cli/diag.js +1 -1
- package/dist/cli/handoff-command.js +2 -0
- package/dist/cli/index.js +18 -1
- package/dist/cli/managed-command-guard.js +10 -0
- package/dist/cli/memory-command.js +441 -0
- package/dist/cli/menu-client.js +4 -0
- package/dist/cli/restart-monitor.js +127 -23
- package/dist/cli/skill-command.js +41 -0
- package/dist/cli/task-context.js +2 -23
- package/dist/cli/trigger-command.js +9 -1
- package/dist/cli/watch-msg-diag.js +209 -0
- package/dist/config/config-field-policy.js +52 -3
- package/dist/config/config-manager.js +17 -4
- package/dist/config/contact-book-store.js +2 -2
- package/dist/config/mention-mode.js +7 -2
- package/dist/config/merge.js +28 -9
- package/dist/config/resolved-config-op.js +1 -1
- package/dist/config/runtime-env.js +137 -0
- package/dist/config/schema-registry.js +11 -1
- package/dist/config-store.js +10 -10
- package/dist/core/audit/event-key.js +3 -1
- package/dist/core/audit/log-integrity.js +4 -0
- package/dist/core/auth/agent-delegation.js +2 -1
- package/dist/core/auth/auth-gateway.js +13 -6
- package/dist/core/auth/authorization-audit.js +54 -19
- package/dist/core/auth/operation-authorizer.js +17 -0
- package/dist/core/auth/operation-catalog.js +9 -0
- package/dist/core/causation/context.js +5 -1
- package/dist/core/command/agent-control.js +1 -2
- package/dist/core/command/command-handler.js +80 -24
- package/dist/core/command/diag-service.js +292 -27
- package/dist/core/command/memory-slash.js +270 -0
- package/dist/core/command/menu-catalog.js +1 -0
- package/dist/core/command/menu-handler.js +331 -79
- package/dist/core/command/menu-protocol.js +45 -4
- package/dist/core/command/slash-gate.js +10 -10
- package/dist/core/command/slash-handler.js +230 -79
- package/dist/core/data-migration.js +282 -505
- package/dist/core/event-catalog.js +26 -0
- package/dist/core/evolagent-registry.js +6 -0
- package/dist/core/evolagent.js +0 -10
- package/dist/core/handoff/store.js +1 -20
- package/dist/core/inference/text-inference.js +57 -1
- package/dist/core/memory/consolidation-context.js +505 -0
- package/dist/core/memory/consolidation-ops-records.js +124 -0
- package/dist/core/memory/consolidation-trigger.js +394 -0
- package/dist/core/memory/index-manager.js +308 -0
- package/dist/core/memory/launch-consolidation.js +395 -0
- package/dist/core/memory/memory-admin.js +234 -0
- package/dist/core/memory/memory-document.js +100 -0
- package/dist/core/memory/memory-session-policy.js +41 -0
- package/dist/core/memory/memory-signature.js +257 -0
- package/dist/core/memory/memory-writer.js +432 -0
- package/dist/core/memory/staleness.js +117 -0
- package/dist/core/message/auto-mention-gate.js +454 -0
- package/dist/core/message/im-renderer.js +90 -20
- package/dist/core/message/message-bridge.js +333 -23
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +114 -27
- package/dist/core/message/pending-hints.js +329 -161
- package/dist/core/message/response-engine.js +596 -90
- package/dist/core/permission/ec-command-parser.js +1044 -87
- package/dist/core/permission/protected-paths.js +376 -126
- package/dist/core/permission/sandbox-runtime.js +40 -12
- package/dist/core/permission/tool-error-code.js +96 -13
- package/dist/core/permission/tool-policy.js +277 -24
- package/dist/core/role/runtime-policy.js +3 -7
- package/dist/core/session/{runner-task-history.js → runner-event-summary.js} +1 -1
- package/dist/core/session/session-fs-store.js +0 -76
- package/dist/core/session/session-idle-monitor.js +355 -0
- package/dist/core/session/session-manager.js +482 -17
- package/dist/core/session/session-mapper.js +33 -16
- package/dist/core/session/session-renew.js +217 -155
- package/dist/core/session/{session-turns.js → session-transcript.js} +35 -6
- package/dist/core/skill/global-watcher.js +282 -0
- package/dist/core/skill/index-generator.js +84 -0
- package/dist/core/skill/startup.js +73 -0
- package/dist/core/skill/switch-resolver.js +115 -0
- package/dist/core/task/task-activity-store.js +219 -0
- package/dist/eck/kit-renderer.js +30 -3
- package/dist/eck/manifest-engine.js +26 -4
- package/dist/index.js +722 -171
- package/dist/ipc.js +564 -2
- package/dist/paths.js +30 -9
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +116 -15
- package/dist/response-system/modes/single-session/index.js +3 -0
- package/dist/trigger/feedback.js +1 -0
- package/dist/trigger/manager.js +0 -10
- package/dist/trigger/scheduler.js +11 -0
- package/dist/trigger/validation.js +22 -0
- package/dist/utils/atomic-write.js +98 -0
- package/dist/utils/avatar-upload.js +59 -18
- package/dist/utils/cross-platform.js +87 -16
- package/dist/utils/error-dict.json +6 -3
- package/dist/utils/error-utils.js +27 -0
- package/dist/utils/logger.js +32 -10
- package/dist/utils/media-cache.js +84 -0
- package/dist/utils/msg-diag.js +97 -0
- package/dist/utils/npm-ops.js +119 -4
- package/dist/utils/package-version-info.js +244 -0
- package/dist/utils/welcome.js +37 -8
- package/dist/utils/windows-shell-trust.js +37 -0
- package/kits/docs/AUTHORING.md +314 -0
- package/kits/docs/GUIDE.md +2 -0
- package/kits/docs/INDEX.md +2 -0
- package/kits/docs/aun/CHEATSHEET.md +4 -0
- package/kits/docs/aun/SDK.md +60 -0
- package/kits/docs/channels/aun.md +8 -0
- package/kits/docs/context-assembly.md +10 -6
- package/kits/docs/evolcore/INDEX.md +17 -3
- package/kits/docs/evolcore/agent-create.md +24 -0
- package/kits/docs/evolcore/agent.md +5 -2
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/ec-capabilities.md +364 -0
- package/kits/docs/evolcore/group.md +34 -18
- package/kits/docs/evolcore/memory-architecture.md +78 -0
- package/kits/docs/evolcore/memory-consolidation-batches.md +133 -0
- package/kits/docs/evolcore/memory-consolidation.md +263 -0
- package/kits/docs/evolcore/memory-tags.md +63 -0
- package/kits/docs/evolcore/memory.md +110 -0
- package/kits/docs/evolcore/menu-client.md +27 -11
- package/kits/{schemas → docs/evolcore}/menu-exec-schema-commands.md +12 -6
- package/kits/docs/evolcore/menu-protocol.md +213 -0
- package/kits/docs/evolcore/msg.md +2 -0
- package/kits/docs/evolcore/trigger.md +7 -5
- package/kits/docs/path-registry.md +6 -8
- package/kits/docs/prompt-loading-architecture.md +5 -3
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_manifest.json +294 -88
- package/kits/eck_manifest.memory.json +257 -0
- package/kits/rules/03-identity.md +8 -8
- package/kits/rules/05-venue.md +4 -2
- package/kits/rules/06-channel.md +1 -1
- package/kits/schemas/_meta.json +276 -47
- package/kits/schemas/agent-config.schema.12.json +38 -2
- package/kits/schemas/agent-config.schema.13.json +591 -0
- package/kits/schemas/daemon.schema.6.json +9 -9
- package/kits/schemas/defaults.schema.5.json +29 -0
- package/kits/schemas/relation-config.schema.8.json +14 -1
- package/kits/schemas/relation-config.schema.9.json +129 -0
- package/kits/schemas/role-config.schema.1.json +3 -3
- package/kits/skills/.switches.json +3 -0
- package/kits/skills/ec-skill/SKILL.md +217 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +128 -0
- package/kits/skills/ec-skill/scripts/deps.mjs +107 -0
- package/kits/skills/ec-skill/scripts/frontmatter.mjs +104 -0
- package/kits/skills/ec-skill/scripts/skill.mjs +629 -0
- package/kits/skills/ec-skill/scripts/template/SKILL.md.tmpl +30 -0
- package/kits/templates/decision/auto-mention.md +25 -0
- package/kits/templates/decision/session-renew.md +22 -0
- package/kits/templates/memory-consolidation/forget.md +104 -0
- package/kits/templates/memory-consolidation/generalize.md +76 -0
- package/kits/templates/memory-consolidation/notes.md +133 -0
- package/kits/templates/memory-consolidation/profile.md +58 -0
- package/kits/templates/memory-consolidation/promote.md +72 -0
- package/kits/templates/memory-consolidation/workflow.md +75 -0
- package/kits/templates/system-fragments/bootstrap.md +14 -5
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/group-history.md +6 -0
- package/kits/templates/system-fragments/identity.md +0 -3
- package/kits/templates/system-fragments/memory.md +79 -0
- package/kits/templates/system-fragments/reply.md +1 -0
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -6
- package/dist/trigger/legacy-session-history.js +0 -19
- package/kits/templates/system-fragments/group-unread-history.md +0 -7
|
@@ -5,12 +5,15 @@ import os from 'os';
|
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import { BaseagentRunnerUnavailableError, hasCompact, autoCompactWindowForModel, isClaudeContextUsageModel, numericToken, formatAsyncQuestionAnswer, parseAsyncUserQuestions, } from '../../agents/runner-types.js';
|
|
7
7
|
import { loadedBaseagentIds } from '../../agents/baseagent-availability.js';
|
|
8
|
+
import { isDiagnosticSessionActive } from '../command/diag-service.js';
|
|
8
9
|
import { evaluateInboundAdmissionPreflight, evaluateOrdinaryInboundAdmission } from './message-admission.js';
|
|
9
10
|
import { authorizeAccess, authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
10
11
|
import { buildTaskRuntimeEnv, ensureRuntimeLockDir, ensureSessionRuntimeDir } from '../../cli/task-context.js';
|
|
12
|
+
import { memorySessionPolicyHook } from '../memory/memory-session-policy.js';
|
|
11
13
|
import { IMRenderer } from './im-renderer.js';
|
|
14
|
+
import { TaskActivityStore } from '../task/task-activity-store.js';
|
|
12
15
|
import { createSendFileMarkerPattern } from './file-markers.js';
|
|
13
|
-
import { createTextInferenceProvider } from '../inference/text-inference.js';
|
|
16
|
+
import { createDecisionInferenceProvider, createTextInferenceProvider } from '../inference/text-inference.js';
|
|
14
17
|
import { StreamIdleMonitor } from './stream-idle-monitor.js';
|
|
15
18
|
import { logger } from '../../utils/logger.js';
|
|
16
19
|
import { isHostChinese } from '../../utils/locale.js';
|
|
@@ -19,10 +22,10 @@ import { readEvolcoreRuntimeFlavor, tagErrorTextWithEvolcoreVersion } from '../.
|
|
|
19
22
|
import { describeProactiveSendCommandFailure, isExactEvolcoreSendCommandForSession } from '../permission/ec-command-parser.js';
|
|
20
23
|
import { summarizeToolInputForDisplay } from '../../utils/tool-summary.js';
|
|
21
24
|
import { agentDataDir, agentDir as resolveAgentDir, agentMdPath, getPackageRoot, resolveRoot, resolvePaths } from '../../paths.js';
|
|
22
|
-
import { renderKitSections } from '../../eck/kit-renderer.js';
|
|
25
|
+
import { renderKitSections, resolveSessionManifest, DEFAULT_MANIFEST_FILE } from '../../eck/kit-renderer.js';
|
|
23
26
|
import { renderMessageBody } from '../../eck/message-renderer.js';
|
|
24
27
|
import { syncGroupRulesContext } from '../../eck/group-rules-sync.js';
|
|
25
|
-
import {
|
|
28
|
+
import { claimHints, commitHintClaim, releaseHintClaim, peekHints, hintsToSubMessages, composeHintFallback, OBSERVER_HINT_FAILURE_MESSAGES, } from './pending-hints.js';
|
|
26
29
|
import { createRootCausation, deriveCausation, normalizeCausation } from '../causation/context.js';
|
|
27
30
|
import { recordCausationLink, recordCausationSpan } from '../causation/audit.js';
|
|
28
31
|
import { normalizeBaseagent } from '../../agents/baseagent.js';
|
|
@@ -32,6 +35,7 @@ import { buildEnvelope, isInteractionSendAccepted, replyContextFromSession, send
|
|
|
32
35
|
import { isSystemOrServicePeer, resolveChatMode } from './peer-mode.js';
|
|
33
36
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../auth/agent-delegation.js';
|
|
34
37
|
import { hasTrustedPrincipal } from '../auth/authenticated-actor.js';
|
|
38
|
+
import { isValidAid } from '../../aun/aid/identity.js';
|
|
35
39
|
// Re-export 工具函数(向后兼容,让其他模块可以从 response-engine 导入)
|
|
36
40
|
export { buildEnvelope, sendInteractionPayload } from './message-utils.js';
|
|
37
41
|
import { constrainResolvedModelForRole, isModelAllowedForRoleBaseagent } from '../model/model-permission.js';
|
|
@@ -53,10 +57,11 @@ import { registerBuiltinModes } from '../../response-system/modes/index.js';
|
|
|
53
57
|
import { deriveSessionTitle, shouldAutoFillSessionTitle } from '../session/session-title.js';
|
|
54
58
|
import { SessionTurnCoordinator } from '../session/session-turn-coordinator.js';
|
|
55
59
|
import { recordTriggerExecutionAnomaly } from '../../trigger/anomaly-store.js';
|
|
56
|
-
import {
|
|
60
|
+
import { normalizeToolErrorClassification } from '../permission/tool-error-code.js';
|
|
57
61
|
import { normalizeExecutionPermissionMode } from '../permission/mode.js';
|
|
58
62
|
import { buildToolLifecycleEventKey } from '../audit/event-key.js';
|
|
59
|
-
import {
|
|
63
|
+
import { aunMessageTranscriptUuid } from '../session/session-transcript.js';
|
|
64
|
+
import { loadDaemonConfig } from '../../config-store.js';
|
|
60
65
|
import { assertAgentPolicyAuthorization } from '../role/runtime-policy.js';
|
|
61
66
|
import { auditFullAccessEvent } from '../auth/authorization-audit.js';
|
|
62
67
|
import { TaskRecoveryService, isTaskRecoveryCurrent } from '../task/task-recovery-service.js';
|
|
@@ -888,6 +893,13 @@ export class ResponseEngine {
|
|
|
888
893
|
const config = resolveEffective({ self: aid }, { cache: true, expand: true });
|
|
889
894
|
return createTextInferenceProvider(baseagent, config);
|
|
890
895
|
}
|
|
896
|
+
getDecisionInferenceProvider(selfAID) {
|
|
897
|
+
const aid = selfAID;
|
|
898
|
+
if (!aid)
|
|
899
|
+
return undefined;
|
|
900
|
+
const config = resolveEffective({ self: aid }, { cache: true, expand: true });
|
|
901
|
+
return createDecisionInferenceProvider(config.decision_model);
|
|
902
|
+
}
|
|
891
903
|
getAvailableBaseagentsForOwner(evolName) {
|
|
892
904
|
return loadedBaseagentIds(this.agentMap.keys(), evolName);
|
|
893
905
|
}
|
|
@@ -1059,6 +1071,198 @@ export class ResponseEngine {
|
|
|
1059
1071
|
hasChannel: (session) => !!this.resolveChannelInfo(session.metadata?.channelKey || session.channel),
|
|
1060
1072
|
});
|
|
1061
1073
|
}
|
|
1074
|
+
observerHintConsumed;
|
|
1075
|
+
observerHintFailed;
|
|
1076
|
+
observerHintFailureReported = new WeakMap();
|
|
1077
|
+
scheduledObserverHints = new Set();
|
|
1078
|
+
setObserverHintConsumed(fn) {
|
|
1079
|
+
this.observerHintConsumed = fn;
|
|
1080
|
+
}
|
|
1081
|
+
setObserverHintFailed(fn) {
|
|
1082
|
+
this.observerHintFailed = fn;
|
|
1083
|
+
}
|
|
1084
|
+
observerHintStatusHint(hint, requestId) {
|
|
1085
|
+
return requestId && !hint.promoteRequestId ? { ...hint, promoteRequestId: requestId } : hint;
|
|
1086
|
+
}
|
|
1087
|
+
reportObserverHintFailure(message, hint, target, code, requestId, action) {
|
|
1088
|
+
const reported = this.observerHintFailureReported.get(message) ?? new Set();
|
|
1089
|
+
if (reported.has(hint.id))
|
|
1090
|
+
return;
|
|
1091
|
+
reported.add(hint.id);
|
|
1092
|
+
this.observerHintFailureReported.set(message, reported);
|
|
1093
|
+
const statusAction = action ?? (hint.promoteRequestId ? 'promote' : 'add');
|
|
1094
|
+
const statusRequestId = requestId ?? hint.promoteRequestId ?? (statusAction === 'add' ? message.observerHintRequestId : undefined);
|
|
1095
|
+
const statusHint = this.observerHintStatusHint(hint, statusAction === 'promote' ? statusRequestId : undefined);
|
|
1096
|
+
try {
|
|
1097
|
+
this.observerHintFailed?.(statusHint, target, {
|
|
1098
|
+
code, message: OBSERVER_HINT_FAILURE_MESSAGES[code], action: statusAction, requestId: statusRequestId,
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
catch (error) {
|
|
1102
|
+
logger.warn(`[ResponseEngine] observer hint failure status failed id=${hint.id}: ${error}`);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
reportObserverHintFailureForMessage(message, code, requestId) {
|
|
1106
|
+
if (message.source !== 'owner-inject' || !message.observerHintId || !message.selfAID)
|
|
1107
|
+
return;
|
|
1108
|
+
const hint = peekHints(resolvePaths().sessionsDir, 'aun', message.channelId, message.selfAID, message.threadId, (message.chatType || 'private'), undefined, message.channel).find(item => item.id === message.observerHintId);
|
|
1109
|
+
if (!hint)
|
|
1110
|
+
return;
|
|
1111
|
+
this.reportObserverHintFailure(message, hint, {
|
|
1112
|
+
channelKey: message.channel,
|
|
1113
|
+
channelId: message.channelId,
|
|
1114
|
+
chatType: (message.chatType || 'private'),
|
|
1115
|
+
threadId: message.threadId,
|
|
1116
|
+
}, code, requestId, hint.promoteRequestId ? 'promote' : 'add');
|
|
1117
|
+
}
|
|
1118
|
+
observerHintFailureCodeForError(error) {
|
|
1119
|
+
const code = error && typeof error === 'object' && 'code' in error
|
|
1120
|
+
? error.code : undefined;
|
|
1121
|
+
const knownCodes = [
|
|
1122
|
+
'SESSION_NOT_FOUND', 'INTERRUPT_FAILED', 'INTERRUPT_TIMEOUT', 'QUEUE_FAILED',
|
|
1123
|
+
'QUEUE_PERSIST_FAILED', 'RENDER_FAILED', 'RUNNER_START_FAILED',
|
|
1124
|
+
'RUNNER_START_SUPERSEDED', 'HINT_UNAVAILABLE',
|
|
1125
|
+
];
|
|
1126
|
+
if (typeof code === 'string' && knownCodes.includes(code)) {
|
|
1127
|
+
return code;
|
|
1128
|
+
}
|
|
1129
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
1130
|
+
if (text === 'RUNNER_START_SUPERSEDED')
|
|
1131
|
+
return 'RUNNER_START_SUPERSEDED';
|
|
1132
|
+
if (text.includes('interrupt barrier timed out'))
|
|
1133
|
+
return 'INTERRUPT_TIMEOUT';
|
|
1134
|
+
if (text.includes('interrupt'))
|
|
1135
|
+
return 'INTERRUPT_FAILED';
|
|
1136
|
+
if (text.includes('queue is unavailable'))
|
|
1137
|
+
return 'QUEUE_FAILED';
|
|
1138
|
+
if (text.includes('enqueue') || text.includes('queue'))
|
|
1139
|
+
return 'QUEUE_PERSIST_FAILED';
|
|
1140
|
+
if (text.includes('renderMessageBody'))
|
|
1141
|
+
return 'RENDER_FAILED';
|
|
1142
|
+
if (text.includes('target session') || text.includes('SESSION_NOT_FOUND'))
|
|
1143
|
+
return 'SESSION_NOT_FOUND';
|
|
1144
|
+
return 'RUNNER_START_FAILED';
|
|
1145
|
+
}
|
|
1146
|
+
reportObserverHintFailureForClaim(message, session, claim, code) {
|
|
1147
|
+
for (const hint of claim.hints) {
|
|
1148
|
+
this.reportObserverHintFailure(message, hint, {
|
|
1149
|
+
channelKey: session.channel,
|
|
1150
|
+
channelId: claim.channelId,
|
|
1151
|
+
chatType: claim.chatType,
|
|
1152
|
+
threadId: claim.threadId || undefined,
|
|
1153
|
+
}, code, hint.promoteRequestId ?? message.observerHintRequestId, hint.promoteRequestId ? 'promote' : 'add');
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
observerHintScheduleFailure(target, code) {
|
|
1157
|
+
const hint = peekHints(resolvePaths().sessionsDir, 'aun', target.channelId, target.selfAid, target.threadId, target.chatType, undefined, target.channelKey).find(item => item.id === target.hintId);
|
|
1158
|
+
if (!hint)
|
|
1159
|
+
return;
|
|
1160
|
+
const statusAction = hint.promoteRequestId ? 'promote' : 'add';
|
|
1161
|
+
const statusHint = this.observerHintStatusHint(hint, statusAction === 'promote' ? target.requestId : undefined);
|
|
1162
|
+
try {
|
|
1163
|
+
this.observerHintFailed?.(statusHint, {
|
|
1164
|
+
channelKey: target.channelKey,
|
|
1165
|
+
channelId: target.channelId,
|
|
1166
|
+
chatType: target.chatType,
|
|
1167
|
+
threadId: target.threadId,
|
|
1168
|
+
}, {
|
|
1169
|
+
code, message: OBSERVER_HINT_FAILURE_MESSAGES[code], action: statusAction,
|
|
1170
|
+
requestId: target.requestId ?? hint.promoteRequestId,
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
catch (error) {
|
|
1174
|
+
logger.warn(`[ResponseEngine] observer hint schedule failure status failed id=${hint.id}: ${error}`);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
async observerTargetSession(target) {
|
|
1178
|
+
const session = target.threadId
|
|
1179
|
+
? await this.sessionManager.getThreadSession(target.channelKey, target.channelId, target.threadId)
|
|
1180
|
+
: this.sessionManager.getActiveSessionSync(target.channelKey, target.channelId, 'aun', target.selfAid);
|
|
1181
|
+
if (!session || session.deletedAt || session.selfAID !== target.selfAid || session.channel !== target.channelKey
|
|
1182
|
+
|| session.channelId !== target.channelId || session.chatType !== target.chatType
|
|
1183
|
+
|| (session.threadId || '') !== (target.threadId || '')
|
|
1184
|
+
|| !this.sessionManager.hasBoundAgentSession(session)
|
|
1185
|
+
|| (target.chatType === 'group' && (session.metadata?.groupId !== target.channelId
|
|
1186
|
+
|| !session.metadata?.observerLastActorId)))
|
|
1187
|
+
return undefined;
|
|
1188
|
+
return session;
|
|
1189
|
+
}
|
|
1190
|
+
async hasObserverTargetSession(target) {
|
|
1191
|
+
return !!(await this.observerTargetSession(target));
|
|
1192
|
+
}
|
|
1193
|
+
async scheduleObserverHint(target) {
|
|
1194
|
+
const session = await this.observerTargetSession(target);
|
|
1195
|
+
if (!session) {
|
|
1196
|
+
this.observerHintScheduleFailure(target, 'SESSION_NOT_FOUND');
|
|
1197
|
+
return 'SESSION_NOT_FOUND';
|
|
1198
|
+
}
|
|
1199
|
+
if (!this.messageQueue) {
|
|
1200
|
+
this.observerHintScheduleFailure(target, 'QUEUE_FAILED');
|
|
1201
|
+
throw new Error('Observer hint queue is unavailable');
|
|
1202
|
+
}
|
|
1203
|
+
const scheduledKey = `${session.id}\u0000${target.hintId}`;
|
|
1204
|
+
if (this.scheduledObserverHints.has(scheduledKey))
|
|
1205
|
+
return 'duplicate';
|
|
1206
|
+
this.scheduledObserverHints.add(scheduledKey);
|
|
1207
|
+
try {
|
|
1208
|
+
await this.messageQueue.withSessionBarrier(session.id, async (snapshot) => {
|
|
1209
|
+
if (snapshot.active && !snapshot.dispatching) {
|
|
1210
|
+
let timeout;
|
|
1211
|
+
try {
|
|
1212
|
+
try {
|
|
1213
|
+
await Promise.race([
|
|
1214
|
+
this.messageQueue.interruptForPriority(session.id),
|
|
1215
|
+
new Promise((_, reject) => {
|
|
1216
|
+
timeout = setTimeout(() => reject(new Error('Observer hint interrupt barrier timed out')), 30_000);
|
|
1217
|
+
timeout.unref?.();
|
|
1218
|
+
}),
|
|
1219
|
+
]);
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
const code = error instanceof Error
|
|
1223
|
+
&& error.message === 'Observer hint interrupt barrier timed out'
|
|
1224
|
+
? 'INTERRUPT_TIMEOUT' : 'INTERRUPT_FAILED';
|
|
1225
|
+
this.observerHintScheduleFailure(target, code);
|
|
1226
|
+
throw error;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
finally {
|
|
1230
|
+
if (timeout)
|
|
1231
|
+
clearTimeout(timeout);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
const receivedAt = Date.now();
|
|
1235
|
+
const actorId = target.chatType === 'group'
|
|
1236
|
+
? session.metadata.observerLastActorId : target.channelId;
|
|
1237
|
+
try {
|
|
1238
|
+
await this.messageQueue.enqueuePersisted(session.id, {
|
|
1239
|
+
channel: session.channel, channelType: 'aun', channelId: session.channelId,
|
|
1240
|
+
selfAID: session.selfAID, baseagent: session.baseagent, threadId: session.threadId,
|
|
1241
|
+
chatType: target.chatType, groupId: target.chatType === 'group' ? target.channelId : undefined,
|
|
1242
|
+
peerId: actorId, peerName: session.metadata?.observerLastPeerName || session.metadata?.peerName,
|
|
1243
|
+
peerType: session.metadata?.observerLastPeerType || session.metadata?.peerType,
|
|
1244
|
+
replyContext: replyContextFromSession(session),
|
|
1245
|
+
boundSessionId: session.id, content: '', source: 'owner-inject', observerHintId: target.hintId,
|
|
1246
|
+
...(target.requestId ? { observerHintRequestId: target.requestId } : {}),
|
|
1247
|
+
messageId: `owner-hint-${crypto.randomUUID()}`, receivedAt, timestamp: receivedAt,
|
|
1248
|
+
}, session.projectPath, {
|
|
1249
|
+
priority: true, interruptible: false, interruptSamePeer: false,
|
|
1250
|
+
agentName: this.agentRegistry?.resolveByChannel(session.channel)?.name,
|
|
1251
|
+
selfAID: target.selfAid, sessionKeyField: session.sessionKey,
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
catch (error) {
|
|
1255
|
+
this.observerHintScheduleFailure(target, 'QUEUE_PERSIST_FAILED');
|
|
1256
|
+
throw error;
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
return 'queued';
|
|
1260
|
+
}
|
|
1261
|
+
catch (error) {
|
|
1262
|
+
this.scheduledObserverHints.delete(scheduledKey);
|
|
1263
|
+
throw error;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1062
1266
|
async recoverTask(request) {
|
|
1063
1267
|
if (!this.taskRecoveryService) {
|
|
1064
1268
|
return { ok: false, status: 'not_recoverable', sessionId: request.sessionId, message: '消息队列尚未初始化' };
|
|
@@ -1159,7 +1363,7 @@ export class ResponseEngine {
|
|
|
1159
1363
|
else
|
|
1160
1364
|
await this.interruptSession(sessionId, error instanceof OrphanedTargetSessionError ? 'execution_error' : 'timeout');
|
|
1161
1365
|
}
|
|
1162
|
-
async interruptSession(sessionId, reason) {
|
|
1366
|
+
async interruptSession(sessionId, reason, strict = false) {
|
|
1163
1367
|
this.clearPauseSession(sessionId);
|
|
1164
1368
|
// A retry delay has no active runner stream to abort. Wake it before any
|
|
1165
1369
|
// session-store I/O so /stop and a newer message take effect immediately.
|
|
@@ -1239,6 +1443,8 @@ export class ResponseEngine {
|
|
|
1239
1443
|
})();
|
|
1240
1444
|
const runnerInterrupt = agent.interrupt(sessionId).catch(error => {
|
|
1241
1445
|
logger.debug(`[ResponseEngine] interrupt barrier runner failure: session=${sessionId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1446
|
+
if (strict)
|
|
1447
|
+
throw error;
|
|
1242
1448
|
});
|
|
1243
1449
|
await Promise.all([terminalStatus, runnerInterrupt]);
|
|
1244
1450
|
}
|
|
@@ -1393,32 +1599,30 @@ export class ResponseEngine {
|
|
|
1393
1599
|
const globalCm = agent.config?.chatmode ?? this.globalSettings.chatmode;
|
|
1394
1600
|
return agent.getContext(channelName, chatType, globalCm);
|
|
1395
1601
|
}
|
|
1396
|
-
/**
|
|
1397
|
-
* 观察者插话(v0.3):消费当前 (对端, thread) 的待用提示,转成 owner-hint SubMessage。
|
|
1398
|
-
* 一次性语义:consumeHints 回放算有效集后清该 thread(其它 thread 残留则保留,否则删文件)。
|
|
1399
|
-
* 仅 aun 渠道(pending-hints 落在 sessions/aun/<self>/<对端>/)。
|
|
1400
|
-
*/
|
|
1602
|
+
/** Claim matching hints for this turn; only commit after the runner accepts its prompt. */
|
|
1401
1603
|
consumeOwnerHints(session, message) {
|
|
1604
|
+
if (message.source !== 'owner-inject' && message.source !== 'user' && message.source !== undefined)
|
|
1605
|
+
return { items: [] };
|
|
1402
1606
|
const channelType = session.channelType || message.channelType || session.channel;
|
|
1403
1607
|
if (channelType !== 'aun')
|
|
1404
|
-
return [];
|
|
1608
|
+
return { items: [] };
|
|
1405
1609
|
const selfAID = session.selfAID || message.selfAID;
|
|
1406
1610
|
if (!selfAID)
|
|
1407
|
-
return [];
|
|
1611
|
+
return { items: [] };
|
|
1408
1612
|
// 会话定位键:私聊=对端 AID,群聊=groupId(均为 session.channelId)。
|
|
1409
1613
|
const peerChannelId = session.channelId;
|
|
1410
1614
|
if (!peerChannelId)
|
|
1411
|
-
return [];
|
|
1615
|
+
return { items: [] };
|
|
1412
1616
|
try {
|
|
1413
|
-
const
|
|
1414
|
-
if (hints.length === 0)
|
|
1415
|
-
return [];
|
|
1416
|
-
logger.info(`[ResponseEngine]
|
|
1417
|
-
return hintsToSubMessages(hints);
|
|
1617
|
+
const claim = claimHints(resolvePaths().sessionsDir, 'aun', peerChannelId, selfAID, session.threadId, (message.chatType || session.chatType || 'private'), message.source === 'owner-inject' ? 'interrupt' : 'next', message.observerHintId, session.channel);
|
|
1618
|
+
if (claim.hints.length === 0)
|
|
1619
|
+
return { items: [] };
|
|
1620
|
+
logger.info(`[ResponseEngine] claimed ${claim.hints.length} owner-hint(s) for ${peerChannelId} thread=${session.threadId || 'main'}`);
|
|
1621
|
+
return { items: hintsToSubMessages(claim.hints), claim };
|
|
1418
1622
|
}
|
|
1419
1623
|
catch (e) {
|
|
1420
1624
|
logger.warn(`[ResponseEngine] consumeOwnerHints failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1421
|
-
return [];
|
|
1625
|
+
return { items: [] };
|
|
1422
1626
|
}
|
|
1423
1627
|
}
|
|
1424
1628
|
/**
|
|
@@ -1594,8 +1798,8 @@ export class ResponseEngine {
|
|
|
1594
1798
|
}
|
|
1595
1799
|
/**
|
|
1596
1800
|
* A fullaccess Trigger may run with no current relation access for its
|
|
1597
|
-
* origin actor, but only after
|
|
1598
|
-
*
|
|
1801
|
+
* origin actor, but only after its persisted owner provenance still
|
|
1802
|
+
* validates. This is intentionally stricter than merely
|
|
1599
1803
|
* checking the message's requested permission mode.
|
|
1600
1804
|
*/
|
|
1601
1805
|
isCurrentFullAccessTrigger(message) {
|
|
@@ -1609,8 +1813,7 @@ export class ResponseEngine {
|
|
|
1609
1813
|
return false;
|
|
1610
1814
|
}
|
|
1611
1815
|
try {
|
|
1612
|
-
return
|
|
1613
|
-
&& new Set(loadDaemonConfig().owners ?? []).has(trigger.authorizedBy);
|
|
1816
|
+
return new Set(loadDaemonConfig().owners ?? []).has(trigger.authorizedBy);
|
|
1614
1817
|
}
|
|
1615
1818
|
catch {
|
|
1616
1819
|
return false;
|
|
@@ -1618,7 +1821,7 @@ export class ResponseEngine {
|
|
|
1618
1821
|
}
|
|
1619
1822
|
/**
|
|
1620
1823
|
* The authorization envelope is immutable for one logical run, but the
|
|
1621
|
-
*
|
|
1824
|
+
* approving owner and runner profile are live policy. Check
|
|
1622
1825
|
* them immediately before every Runner invocation, including retries.
|
|
1623
1826
|
*/
|
|
1624
1827
|
assertCurrentFullAccessExecutionAuthorization(authorization, agent) {
|
|
@@ -1639,9 +1842,6 @@ export class ResponseEngine {
|
|
|
1639
1842
|
else if (authorization.source !== 'fullaccess-command' && authorization.source !== 'trigger') {
|
|
1640
1843
|
throw new Error('fullaccess execution authorization has an unsupported source');
|
|
1641
1844
|
}
|
|
1642
|
-
if (!isFullAccessEnabled()) {
|
|
1643
|
-
throw new Error('fullaccess execution is disabled by daemon.json');
|
|
1644
|
-
}
|
|
1645
1845
|
const currentOwners = new Set(loadDaemonConfig().owners ?? []);
|
|
1646
1846
|
if (!currentOwners.has(authorization.authorizedBy)) {
|
|
1647
1847
|
throw new Error('fullaccess execution authorization is no longer owned');
|
|
@@ -1831,7 +2031,7 @@ export class ResponseEngine {
|
|
|
1831
2031
|
'/model', '/effort', '/agent', '/slist', '/session', '/rename', '/repair', '/fork',
|
|
1832
2032
|
'/stop', '/pause', '/resume', '/clear', '/compact', '/renew', '/del', '/perm', '/file', '/check',
|
|
1833
2033
|
'/s ', '/name ', '/rewind', '/rw', '/rw ', '/activity', '/observable', '/chatmode',
|
|
1834
|
-
'/aid', '/upgrade', '/evolagent', '/pair ',
|
|
2034
|
+
'/aid', '/upgrade', '/evolagent', '/pair ', '/memory',
|
|
1835
2035
|
];
|
|
1836
2036
|
/** 判断消息内容是否为已知命令 */
|
|
1837
2037
|
isKnownCommand(content) {
|
|
@@ -1849,9 +2049,15 @@ export class ResponseEngine {
|
|
|
1849
2049
|
}
|
|
1850
2050
|
catch (error) {
|
|
1851
2051
|
outerFailureReason = error instanceof Error ? error.message : String(error);
|
|
2052
|
+
if (message.source === 'owner-inject') {
|
|
2053
|
+
this.reportObserverHintFailureForMessage(message, this.observerHintFailureCodeForError(error));
|
|
2054
|
+
}
|
|
1852
2055
|
throw error;
|
|
1853
2056
|
}
|
|
1854
2057
|
finally {
|
|
2058
|
+
if (message.source === 'owner-inject' && message.observerHintId && message.boundSessionId) {
|
|
2059
|
+
this.scheduledObserverHints.delete(`${message.boundSessionId}\u0000${message.observerHintId}`);
|
|
2060
|
+
}
|
|
1855
2061
|
if (fallbackFullAccessAudit && !this.fullAccessTerminalMessages.has(message)) {
|
|
1856
2062
|
auditFullAccessEvent({
|
|
1857
2063
|
event: fallbackFullAccessAudit.authorization.source === 'trigger'
|
|
@@ -1878,6 +2084,28 @@ export class ResponseEngine {
|
|
|
1878
2084
|
async processMessageWithMonitors(message) {
|
|
1879
2085
|
const idleMs = (this.globalSettings.idleMonitor?.timeout ?? 120) * 1000;
|
|
1880
2086
|
const totalExecutionMs = this.totalExecutionLimitMs();
|
|
2087
|
+
// A discarded handoff must be dropped before any bound-session lookup.
|
|
2088
|
+
// Restored origin deliveries may point at a session that has already been
|
|
2089
|
+
// replaced; resolving it first would reactivate the stale session path.
|
|
2090
|
+
if (message.handoffDelivery
|
|
2091
|
+
&& this.handoffRuntime
|
|
2092
|
+
&& this.handoffRuntime.isDiscardedDelivery(message)) {
|
|
2093
|
+
logger.info(`[ResponseEngine] Skip stale handoff delivery before session resolution:`
|
|
2094
|
+
+ ` direction=${message.handoffDelivery.direction}`
|
|
2095
|
+
+ ` handoff=${message.handoffDelivery.handoffId}`);
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2098
|
+
// Includes persisted queues, recovery triggers and delayed owner replies.
|
|
2099
|
+
// A closed diagnostic must not activate its old main session again.
|
|
2100
|
+
const diagnosticBoundId = message.boundSessionId ?? message.triggerMeta?.boundSessionId;
|
|
2101
|
+
if (diagnosticBoundId) {
|
|
2102
|
+
const bound = await this.sessionManager.getSessionById(diagnosticBoundId);
|
|
2103
|
+
if (bound && (bound.metadata?.diagnostic || bound.metadata?.diagnosticStatus === 'cancelled')
|
|
2104
|
+
&& !isDiagnosticSessionActive(bound, this.sessionManager.getChatDir(bound))) {
|
|
2105
|
+
logger.info(`[ResponseEngine] Skip closed diagnostic session ${bound.id}`);
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
1881
2109
|
if (message.taskRecovery) {
|
|
1882
2110
|
const current = message.boundSessionId
|
|
1883
2111
|
? await this.sessionManager.getSessionById(message.boundSessionId)
|
|
@@ -1887,17 +2115,16 @@ export class ResponseEngine {
|
|
|
1887
2115
|
return;
|
|
1888
2116
|
}
|
|
1889
2117
|
}
|
|
1890
|
-
if (message.handoffDelivery
|
|
1891
|
-
&& this.handoffRuntime
|
|
1892
|
-
&& this.handoffRuntime.isDiscardedDelivery(message)) {
|
|
1893
|
-
logger.info(`[ResponseEngine] Skip stale handoff delivery before session resolution:`
|
|
1894
|
-
+ ` direction=${message.handoffDelivery.direction}`
|
|
1895
|
-
+ ` handoff=${message.handoffDelivery.handoffId}`);
|
|
1896
|
-
return;
|
|
1897
|
-
}
|
|
1898
2118
|
// 先解析会话,再优先用 session.metadata.channelKey 精确定位实例级 adapter
|
|
1899
2119
|
// message.channel 现在存实例名(channelName),可直接用于精确路由
|
|
1900
2120
|
const { session, absoluteProjectPath } = await this.resolveSession(message);
|
|
2121
|
+
if (message.source === 'owner-inject' && (!message.observerHintId || !peekHints(resolvePaths().sessionsDir, 'aun', session.channelId, session.selfAID || '', session.threadId, session.chatType, 'interrupt', session.channel).some(item => item.id === message.observerHintId))) {
|
|
2122
|
+
this.reportObserverHintFailureForMessage(message, 'HINT_UNAVAILABLE');
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if ((session.metadata?.diagnostic || session.metadata?.diagnosticStatus === 'cancelled')
|
|
2126
|
+
&& !isDiagnosticSessionActive(session, this.sessionManager.getChatDir(session)))
|
|
2127
|
+
return;
|
|
1901
2128
|
const accessChannelKey = this.messageChannelKey(message, session);
|
|
1902
2129
|
const accessChannelInfo = this.resolveChannelInfo(accessChannelKey);
|
|
1903
2130
|
const accessAdapter = accessChannelInfo?.adapter;
|
|
@@ -1986,6 +2213,21 @@ export class ResponseEngine {
|
|
|
1986
2213
|
this.publishTriggerExecutionFailure(message, 'access_denied');
|
|
1987
2214
|
return;
|
|
1988
2215
|
}
|
|
2216
|
+
if (message.chatType === 'group' && (message.source === 'user' || !message.source)) {
|
|
2217
|
+
const actorId = message.items?.filter(item => item.peerId).at(-1)?.peerId || message.peerId;
|
|
2218
|
+
if (actorId) {
|
|
2219
|
+
try {
|
|
2220
|
+
await this.sessionManager.patchSessionMetadata(session.id, {
|
|
2221
|
+
observerLastActorId: actorId,
|
|
2222
|
+
observerLastPeerName: message.peerName,
|
|
2223
|
+
observerLastPeerType: message.peerType,
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
catch (error) {
|
|
2227
|
+
logger.warn(`[ResponseEngine] observer group actor snapshot failed: ${error}`);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
1989
2231
|
// thread(feishu) pending strategy: inject replyContext so first reply creates the thread
|
|
1990
2232
|
if (message.triggerMeta?.pendingThread && message.triggerMeta?.rootMessageId) {
|
|
1991
2233
|
message.replyContext = {
|
|
@@ -2017,6 +2259,7 @@ export class ResponseEngine {
|
|
|
2017
2259
|
catch (error) {
|
|
2018
2260
|
if (error instanceof BaseagentRunnerUnavailableError) {
|
|
2019
2261
|
logger.error(`[ResponseEngine] baseagent mismatch blocked: session=${session.id} channel=${channelKey} requested=${session.baseagent} owner=${error.evolagentName} available=${error.availableBaseagents.join(',') || '<none>'}`);
|
|
2262
|
+
this.reportObserverHintFailureForMessage(message, 'RUNNER_START_FAILED');
|
|
2020
2263
|
await this.sendBaseagentMismatch(channelKey, message.channelId, session, error, message.replyContext);
|
|
2021
2264
|
this.publishTriggerExecutionFailure(message, `baseagent_unavailable:${error.baseagent}`);
|
|
2022
2265
|
return;
|
|
@@ -2213,8 +2456,7 @@ export class ResponseEngine {
|
|
|
2213
2456
|
&& commandAuthorization.authorizedBy.length > 0) {
|
|
2214
2457
|
let currentOwner = false;
|
|
2215
2458
|
try {
|
|
2216
|
-
currentOwner =
|
|
2217
|
-
&& new Set(loadDaemonConfig().owners ?? []).has(commandAuthorization.authorizedBy);
|
|
2459
|
+
currentOwner = new Set(loadDaemonConfig().owners ?? []).has(commandAuthorization.authorizedBy);
|
|
2218
2460
|
if (commandAuthorization.source === 'agent-policy')
|
|
2219
2461
|
assertAgentPolicyAuthorization(commandAuthorization);
|
|
2220
2462
|
}
|
|
@@ -2330,6 +2572,8 @@ export class ResponseEngine {
|
|
|
2330
2572
|
const isBootstrapping = lifecycle === 'bootstrapping';
|
|
2331
2573
|
// Per-method agent name for stats bucketing (agent.name or '<unknown>')
|
|
2332
2574
|
const agentNameForStats = owningAgentForTask?.name ?? taskAgentAid ?? '<unknown>';
|
|
2575
|
+
let ownerHintClaim;
|
|
2576
|
+
let observerHintFailureCode;
|
|
2333
2577
|
if (!channelInfo) {
|
|
2334
2578
|
logger.error(`[ResponseEngine] Unknown channel: ${channelKey}`);
|
|
2335
2579
|
this.publishTriggerExecutionFailure(message, `unknown_channel:${channelKey}`);
|
|
@@ -2384,6 +2628,29 @@ export class ResponseEngine {
|
|
|
2384
2628
|
}
|
|
2385
2629
|
// 为本次任务处理生成唯一 task_id(客户端生成,格式 task-{10hex})
|
|
2386
2630
|
const taskId = `task-${crypto.randomUUID().replace(/-/g, '').slice(0, 10)}`;
|
|
2631
|
+
let taskActivityStatus = 'running';
|
|
2632
|
+
const taskActivityStore = runnerSelfAid ? new TaskActivityStore(runnerSelfAid) : undefined;
|
|
2633
|
+
if (taskActivityStore) {
|
|
2634
|
+
try {
|
|
2635
|
+
const activityChatType = chatType === 'group' ? 'group' : 'private';
|
|
2636
|
+
const activityChannelId = activityChatType === 'group'
|
|
2637
|
+
? (message.groupId || session.metadata?.groupId || message.channelId)
|
|
2638
|
+
: (message.peerId || message.channelId);
|
|
2639
|
+
taskActivityStore.createTask({
|
|
2640
|
+
taskId,
|
|
2641
|
+
sessionId: session.id,
|
|
2642
|
+
channel: message.channelType || session.channelType || message.channel,
|
|
2643
|
+
channelId: activityChannelId,
|
|
2644
|
+
chatType: activityChatType,
|
|
2645
|
+
...(activityChatType === 'group' ? { groupId: activityChannelId } : {}),
|
|
2646
|
+
...(activityChatType === 'private' ? { peerId: activityChannelId } : {}),
|
|
2647
|
+
taskStatus: taskActivityStatus,
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
catch (error) {
|
|
2651
|
+
logger.warn(`[ResponseEngine] task activity store initialization failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2387
2654
|
const taskPauseController = this.getPauseController(session.id);
|
|
2388
2655
|
let turnLease;
|
|
2389
2656
|
const inputCausation = normalizeCausation(message.causation) ?? createRootCausation();
|
|
@@ -2479,10 +2746,12 @@ export class ResponseEngine {
|
|
|
2479
2746
|
// this inbound actor. Do not fall back to a stale session owner/admin
|
|
2480
2747
|
// after an alias or Agent-role revocation; only use the persisted session
|
|
2481
2748
|
// identity when role resolution itself was unavailable.
|
|
2482
|
-
const peerRole =
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2749
|
+
const peerRole = message.autoMentionBatch && message.items?.some(item => item.peerRole && item.peerRole !== peerRoleDetail?.effectiveRole)
|
|
2750
|
+
? 'visitor'
|
|
2751
|
+
: authorizationIdentity?.role
|
|
2752
|
+
?? (peerRoleDetail
|
|
2753
|
+
? (peerRoleDetail.effectiveRole ?? 'none')
|
|
2754
|
+
: (session.identity?.role ?? 'none'));
|
|
2486
2755
|
const effectiveAgentConfig = (() => {
|
|
2487
2756
|
try {
|
|
2488
2757
|
return resolveEffective({ self: selfAid || undefined, peerKey }, { cache: true });
|
|
@@ -2565,6 +2834,9 @@ export class ResponseEngine {
|
|
|
2565
2834
|
outputState.middleOutputMode = middleOutputMode;
|
|
2566
2835
|
// 诊断日志:记录 inbound message_id 和生成的 task_id 的对应关系
|
|
2567
2836
|
logger.info(`[ResponseEngine] Task created: inboundMsgId=${message.messageId ?? 'none'} taskId=${taskId} sessionId=${session.id} chatmode=${chatmode} mode=${resolvedMode?.mode.id ?? 'override/fallback'}`);
|
|
2837
|
+
{
|
|
2838
|
+
import('../../utils/msg-diag.js').then(m => m.diagLog('task_created', { aid: session.selfAID ?? '', msgId: message.messageId ?? '', taskId, text: message.content ?? '' })).catch(() => { });
|
|
2839
|
+
}
|
|
2568
2840
|
// 构建带 taskId/chatmode 的 ReplyContext(本次任务所有出站消息共用)
|
|
2569
2841
|
const taskReplyContext = () => {
|
|
2570
2842
|
const base = this.getReplyContext(message);
|
|
@@ -2588,6 +2860,19 @@ export class ResponseEngine {
|
|
|
2588
2860
|
},
|
|
2589
2861
|
modeConfig: resolvedMode.context.modeConfig,
|
|
2590
2862
|
state: modeState,
|
|
2863
|
+
sessionState: resolvedMode.context.sessionState,
|
|
2864
|
+
continuation: message.taskRecovery
|
|
2865
|
+
? {
|
|
2866
|
+
kind: 'taskRecovery',
|
|
2867
|
+
taskId: message.taskRecovery.taskId,
|
|
2868
|
+
generation: message.taskRecovery.generation,
|
|
2869
|
+
groupId: message.groupId,
|
|
2870
|
+
threadId: message.threadId,
|
|
2871
|
+
}
|
|
2872
|
+
: message.restartResume?.submitted
|
|
2873
|
+
? { kind: 'restartResume', groupId: message.groupId, threadId: message.threadId }
|
|
2874
|
+
: undefined,
|
|
2875
|
+
previousTurnState: this.turnCoordinator.current(session),
|
|
2591
2876
|
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, chatType, selfAid),
|
|
2592
2877
|
describeSendCommandFailure: describeProactiveSendCommandFailure,
|
|
2593
2878
|
logger,
|
|
@@ -2824,7 +3109,18 @@ export class ResponseEngine {
|
|
|
2824
3109
|
this.publishTriggerExecutionSkipped(message, 'recovery_stale');
|
|
2825
3110
|
return;
|
|
2826
3111
|
}
|
|
2827
|
-
|
|
3112
|
+
const sourceMessageId = message.sourceMessageIds === undefined
|
|
3113
|
+
? message.messageId
|
|
3114
|
+
: message.sourceMessageIds.length === 1 ? message.sourceMessageIds[0] : undefined;
|
|
3115
|
+
const inputId = normalizeBaseagent(agent.name).canonical === 'claude'
|
|
3116
|
+
&& configChatType === 'private'
|
|
3117
|
+
&& (message.channelType ?? session.channelType) === 'aun'
|
|
3118
|
+
&& message.source !== 'trigger'
|
|
3119
|
+
&& !message.taskRecovery
|
|
3120
|
+
&& sourceMessageId
|
|
3121
|
+
? aunMessageTranscriptUuid(sourceMessageId)
|
|
3122
|
+
: undefined;
|
|
3123
|
+
turnLease = await this.turnCoordinator.begin(session, taskId, inputId, configActorId ? {
|
|
2828
3124
|
peerId: configActorId,
|
|
2829
3125
|
encrypted: message.encrypted ?? message.replyContext?.metadata?.encrypted,
|
|
2830
3126
|
baseagent: session.baseagent,
|
|
@@ -2834,7 +3130,11 @@ export class ResponseEngine {
|
|
|
2834
3130
|
triggerAttemptId: message.triggerMeta?.attemptId,
|
|
2835
3131
|
} : undefined);
|
|
2836
3132
|
// 记录开始处理
|
|
2837
|
-
const taskEncrypt =
|
|
3133
|
+
const taskEncrypt = typeof message.encrypted === 'boolean'
|
|
3134
|
+
? message.encrypted
|
|
3135
|
+
: message.replyContext?.metadata?.encrypted != null
|
|
3136
|
+
? !!message.replyContext.metadata.encrypted
|
|
3137
|
+
: undefined;
|
|
2838
3138
|
this.eventBus.publish({
|
|
2839
3139
|
type: 'task:started',
|
|
2840
3140
|
sessionId: session.id,
|
|
@@ -2866,6 +3166,9 @@ export class ResponseEngine {
|
|
|
2866
3166
|
});
|
|
2867
3167
|
// 创建 IMRenderer(统一 interactive/proactive 两条路径)
|
|
2868
3168
|
let firstReply = true;
|
|
3169
|
+
// 出站 debug.model:每次 runQuery 前更新(含备用模型切换)。单独声明是因为
|
|
3170
|
+
// send 闭包早于下方 modelOverride/agentModel 的 let 声明,直接引用会撞 TDZ。
|
|
3171
|
+
let debugRunnerModel;
|
|
2869
3172
|
const renderer = new IMRenderer({
|
|
2870
3173
|
adapter,
|
|
2871
3174
|
envelope,
|
|
@@ -2874,8 +3177,32 @@ export class ResponseEngine {
|
|
|
2874
3177
|
flushDelay: (options?.flushDelay ?? this.agentRegistry?.resolveByChannel(channelKey)?.config?.flush_delay ?? 3) * 1000,
|
|
2875
3178
|
suppressActivityItems: isProactive ? false : middleOutputMode !== 'all',
|
|
2876
3179
|
suppressIntermediateText: isProactive ? false : middleOutputMode === 'none',
|
|
3180
|
+
deferTextUntilBoundary: (message.channelType ?? session.channelType) === 'aun' && session.chatType === 'private',
|
|
2877
3181
|
operationalNoticesAsText: !isProactive && middleOutputMode !== 'none',
|
|
2878
3182
|
fileMarkerPattern: options?.fileMarkerPattern,
|
|
3183
|
+
recordActivity: item => {
|
|
3184
|
+
if (!taskActivityStore || (turnLease && !this.turnCoordinator.canPublish(turnLease)))
|
|
3185
|
+
return;
|
|
3186
|
+
try {
|
|
3187
|
+
const activityChatType = chatType === 'group' ? 'group' : 'private';
|
|
3188
|
+
const activityChannelId = activityChatType === 'group'
|
|
3189
|
+
? (message.groupId || session.metadata?.groupId || message.channelId)
|
|
3190
|
+
: (message.peerId || message.channelId);
|
|
3191
|
+
taskActivityStore.append({
|
|
3192
|
+
taskId,
|
|
3193
|
+
sessionId: session.id,
|
|
3194
|
+
channel: message.channelType || session.channelType || message.channel,
|
|
3195
|
+
channelId: activityChannelId,
|
|
3196
|
+
chatType: activityChatType,
|
|
3197
|
+
...(activityChatType === 'group' ? { groupId: activityChannelId } : {}),
|
|
3198
|
+
...(activityChatType === 'private' ? { peerId: activityChannelId } : {}),
|
|
3199
|
+
item,
|
|
3200
|
+
});
|
|
3201
|
+
}
|
|
3202
|
+
catch (error) {
|
|
3203
|
+
logger.warn(`[ResponseEngine] task activity record failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3204
|
+
}
|
|
3205
|
+
},
|
|
2879
3206
|
send: async (payload) => {
|
|
2880
3207
|
if (turnLease && !this.turnCoordinator.canPublish(turnLease)) {
|
|
2881
3208
|
snapshot.pushOutbound(session.id, taskId, { kind: payload.kind, decision: 'suppressed-stale-turn' });
|
|
@@ -2907,8 +3234,13 @@ export class ResponseEngine {
|
|
|
2907
3234
|
}
|
|
2908
3235
|
opts.sessionId = session.id;
|
|
2909
3236
|
if (payload.kind === 'result.text' && payload.isFinal) {
|
|
2910
|
-
opts.
|
|
3237
|
+
opts.ext = { ...opts.ext, isFinal: true };
|
|
2911
3238
|
}
|
|
3239
|
+
opts.debug = {
|
|
3240
|
+
sessionId: session.id,
|
|
3241
|
+
...(session.agentSessionId ? { agentSessionId: session.agentSessionId } : {}),
|
|
3242
|
+
...(debugRunnerModel ? { model: debugRunnerModel } : {}),
|
|
3243
|
+
};
|
|
2912
3244
|
opts.metadata = withTaskMetadata({
|
|
2913
3245
|
...(opts.metadata ?? {}),
|
|
2914
3246
|
...(payload.kind === 'result.text' ? payload.metadata : {}),
|
|
@@ -3080,7 +3412,12 @@ export class ResponseEngine {
|
|
|
3080
3412
|
const runModeConfig = resolvedMode?.mode.configureRun && modeProcessCtx
|
|
3081
3413
|
? resolvedMode.mode.configureRun(modeProcessCtx)
|
|
3082
3414
|
: undefined;
|
|
3083
|
-
const
|
|
3415
|
+
const modePolicyHook = runModeConfig?.policyHook;
|
|
3416
|
+
// 整理会话:写工具全拦、Bash 只放行 ec memory create|update|archive,先于模式策略判定
|
|
3417
|
+
const isMemorySession = (session.sessionType ?? session.metadata?.sessionType) === 'memory';
|
|
3418
|
+
const pureSessionPolicyHook = isMemorySession
|
|
3419
|
+
? (toolName, toolInput) => memorySessionPolicyHook(toolName, toolInput) ?? modePolicyHook?.(toolName, toolInput)
|
|
3420
|
+
: modePolicyHook;
|
|
3084
3421
|
// 设置权限审批的交互上下文(支持交互卡片)
|
|
3085
3422
|
const permissionContext = {
|
|
3086
3423
|
sendPrompt: permissionPromptForOrigin,
|
|
@@ -3203,6 +3540,9 @@ export class ResponseEngine {
|
|
|
3203
3540
|
const stBefore = modeState.get('proactive');
|
|
3204
3541
|
const wasFirstPending = stBefore && !stBefore.firstToolDone;
|
|
3205
3542
|
const result = pluginHook(toolName, toolInput);
|
|
3543
|
+
// 整理会话的拦截原因已经写清楚怎么改,不套 proactive 违规提醒
|
|
3544
|
+
if (result?.block && result.policyCode?.startsWith('memory_session_'))
|
|
3545
|
+
return result;
|
|
3206
3546
|
if (result?.block) {
|
|
3207
3547
|
// 拦截事件(首工具非表态 / 工具进展汇报未完成)
|
|
3208
3548
|
snapshot.set(session.id, taskId, { policyHook: { triggered: true, blocked: true, toolName } });
|
|
@@ -3264,10 +3604,9 @@ export class ResponseEngine {
|
|
|
3264
3604
|
if (!isProactive && channelInfo.adapter.capabilities?.file)
|
|
3265
3605
|
capParts.push('文件发送');
|
|
3266
3606
|
// Personal state is exposed to the manifest as vars. The manifest owns
|
|
3267
|
-
// loading persona.md
|
|
3607
|
+
// loading persona.md into the system prompt.
|
|
3268
3608
|
const owningAgent = owningAgentForTask;
|
|
3269
3609
|
const persona = isActive ? (owningAgent?.getPersona?.() || undefined) : undefined;
|
|
3270
|
-
const working = isActive ? (owningAgent?.getWorkingMemory?.() || undefined) : undefined;
|
|
3271
3610
|
// 计算 peerKey:群聊固定按 groupId/channelId,私聊按发送者 peerId。
|
|
3272
3611
|
// 这样单条和积压合并批次不会因队列状态不同而切换关系级配置。
|
|
3273
3612
|
const normalizedBaseagent = normalizeBaseagent(agent.name);
|
|
@@ -3609,6 +3948,10 @@ export class ResponseEngine {
|
|
|
3609
3948
|
},
|
|
3610
3949
|
}).allow;
|
|
3611
3950
|
};
|
|
3951
|
+
// 会话原型与整理模式:必须在 kitCtx 构造之前读出,manifest 的 when 条件
|
|
3952
|
+
// 依赖它们选段(缺了会 fail-closed —— 模式手册全不加载,且不报错)。
|
|
3953
|
+
const renderedSessionType = session.sessionType ?? session.metadata?.sessionType ?? 'main';
|
|
3954
|
+
const memoryMode = session.metadata?.memoryMode;
|
|
3612
3955
|
// Kit renderer: 组装上下文
|
|
3613
3956
|
const pkgRoot = getPackageRoot();
|
|
3614
3957
|
const kitCtx = {
|
|
@@ -3626,16 +3969,36 @@ export class ResponseEngine {
|
|
|
3626
3969
|
// evolcore 运行模式:dev=源码仓库 | install=全局安装包
|
|
3627
3970
|
evolcoreMode: readEvolcoreRuntimeFlavor(),
|
|
3628
3971
|
// 路径变量(用于 manifest 路径展开,resolvePath 用 ctx.vars 取真值)
|
|
3972
|
+
// AGENT_DIR 是 manifest 的合法别名(buildPathMappings 有注册),
|
|
3973
|
+
// 缺了它引用 $AGENT_DIR 的段(如技能索引)会整段静默丢弃。
|
|
3974
|
+
AGENT_DIR: selfAid ? path.join(resolveRoot(), 'agents', selfAid) : undefined,
|
|
3629
3975
|
PERSONAL_DIR: selfAid ? path.join(resolveRoot(), 'agents', selfAid, 'personal') : undefined,
|
|
3630
3976
|
RELATIONS_DIR: selfAid ? path.join(resolveRoot(), 'agents', selfAid, 'relations') : undefined,
|
|
3631
3977
|
VENUES_DIR: selfAid ? path.join(resolveRoot(), 'agents', selfAid, 'venues') : undefined,
|
|
3632
3978
|
selfAid: selfAid || undefined,
|
|
3979
|
+
// ownerAid:当前 agent 的第一个 owner AID,用于 owner 作用域记忆路径展开。
|
|
3980
|
+
// 来源:getFirstStaticAgentOwner 读 config.json owners[0];AID 格式不合法时置 undefined。
|
|
3981
|
+
ownerAid: (() => {
|
|
3982
|
+
const raw = selfAid ? getFirstStaticAgentOwner(selfAid) : undefined;
|
|
3983
|
+
if (!raw)
|
|
3984
|
+
return undefined;
|
|
3985
|
+
return isValidAid(raw) ? raw : undefined;
|
|
3986
|
+
})(),
|
|
3987
|
+
// 记忆系统开关变量(来自 effectiveAgentConfig.memory,默认全部启用)。
|
|
3988
|
+
// enableRelation 是总开关:relation 是所有记忆的唯一出生地。
|
|
3989
|
+
// memory.enableRelation 在 relation-config schema 里声明了 x-merge-logic: AND,
|
|
3990
|
+
// mergeLayers 会自动处理 agent 级与 relation 级的 AND 合并,无需手动读两层。
|
|
3991
|
+
memoryEnableRelation: effectiveAgentConfig?.memory?.enableRelation ?? true,
|
|
3992
|
+
memoryEnableAgent: effectiveAgentConfig?.memory?.enableAgent ?? true,
|
|
3993
|
+
memoryEnableOwner: effectiveAgentConfig?.memory?.enableOwner ?? true,
|
|
3994
|
+
// 会话原型与整理模式(manifest 的 when 条件靠它们选段)
|
|
3995
|
+
sessionType: renderedSessionType,
|
|
3996
|
+
memoryMode,
|
|
3633
3997
|
selfName: selfName || undefined,
|
|
3634
3998
|
agentMdPath: selfAid ? agentMdPath(selfAid) : undefined,
|
|
3635
3999
|
lifecycle,
|
|
3636
4000
|
isBootstrapping,
|
|
3637
4001
|
hasPersona: !!persona,
|
|
3638
|
-
hasWorkingMemory: !!working,
|
|
3639
4002
|
peerId: peerIdRaw || undefined,
|
|
3640
4003
|
peerKey,
|
|
3641
4004
|
peerName: peerName || undefined,
|
|
@@ -3686,9 +4049,10 @@ export class ResponseEngine {
|
|
|
3686
4049
|
// 默认值不在此硬编码,出厂默认由模式 schema 声明、经 buildState 落入 proactive.*。
|
|
3687
4050
|
proactivePreTool1stMsgChk: proactive?.preTool1stMsgChk ?? false,
|
|
3688
4051
|
proactiveToolUseReminder: proactive?.toolUseReminder ?? false,
|
|
3689
|
-
|
|
4052
|
+
// 明确续跑已表态时,提示词和运行时门禁都不再要求重复开场。
|
|
4053
|
+
proactiveFirstSendRequired: proactive?.firstSendRequired === true && !proactive.firstToolDone,
|
|
3690
4054
|
proactiveToolReportRequired: proactive?.toolReportRequired ?? false,
|
|
3691
|
-
proactiveToolReportInterval: proactive?.toolReportInterval ??
|
|
4055
|
+
proactiveToolReportInterval: proactive?.toolReportInterval ?? 20,
|
|
3692
4056
|
proactiveSendTargetLabel: chatType === 'group' ? '群里' : '对方',
|
|
3693
4057
|
readonly: effectivePermissionMode === 'readonly',
|
|
3694
4058
|
baseAgent: normalizedBaseagent.canonical,
|
|
@@ -3704,15 +4068,20 @@ export class ResponseEngine {
|
|
|
3704
4068
|
},
|
|
3705
4069
|
sessionId: session.id,
|
|
3706
4070
|
};
|
|
3707
|
-
//
|
|
3708
|
-
|
|
4071
|
+
// 确保技能索引是最新的(懒加载机制)
|
|
4072
|
+
if (selfAid) {
|
|
4073
|
+
const { ensureSkillIndexUpToDate } = await import('../skill/startup.js');
|
|
4074
|
+
ensureSkillIndexUpToDate(resolveAgentDir(selfAid), selfAid);
|
|
4075
|
+
}
|
|
4076
|
+
// 按会话原型(sessionType)选 manifest 文件:config.sessionManifests 覆盖 → 内置映射 → 主 manifest。
|
|
4077
|
+
const sessionType = renderedSessionType;
|
|
3709
4078
|
const sessionManifests = this.agentRegistry?.resolveByChannel(channelKey)?.config?.sessionManifests;
|
|
3710
4079
|
// Bootstrap always uses the lifecycle-gated main manifest. A custom
|
|
3711
4080
|
// session manifest (for example the auxiliary manifest with `always`
|
|
3712
4081
|
// sections) must not reintroduce normal rules or command context.
|
|
3713
4082
|
const manifestFile = isBootstrapping
|
|
3714
|
-
?
|
|
3715
|
-
:
|
|
4083
|
+
? DEFAULT_MANIFEST_FILE
|
|
4084
|
+
: resolveSessionManifest(sessionType, sessionManifests);
|
|
3716
4085
|
const kitContext = renderKitSections(kitCtx, manifestFile);
|
|
3717
4086
|
effectiveSystemPrompt = [options?.systemPromptAppend, kitContext].filter(Boolean).join('\n') || undefined;
|
|
3718
4087
|
// ── Stats: context_breakdown 旁路采集(各段估算 token 数,字符数/4 近似) ──
|
|
@@ -3747,9 +4116,9 @@ export class ResponseEngine {
|
|
|
3747
4116
|
catch { /* non-fatal */ }
|
|
3748
4117
|
// 消息渲染层:用 message manifest 逐条渲染(时间 + 群聊发送者),组装成最终正文。
|
|
3749
4118
|
// 单条消息构造单元素 items;批量合并的消息 message.items 已由队列填充。
|
|
3750
|
-
const hasContent = message.content.trim() || (message.items && message.items.length > 0);
|
|
4119
|
+
const hasContent = !!message.observerHintId || message.content.trim() || !!message.images?.length || (message.items && message.items.length > 0);
|
|
3751
4120
|
if (hasContent) {
|
|
3752
|
-
const rawPeerItems = message.items && message.items.length > 0
|
|
4121
|
+
const rawPeerItems = message.source === 'owner-inject' ? [] : message.items && message.items.length > 0
|
|
3753
4122
|
? message.items
|
|
3754
4123
|
: [{
|
|
3755
4124
|
peerId: message.peerId, peerName: peerName || undefined,
|
|
@@ -3776,10 +4145,17 @@ export class ResponseEngine {
|
|
|
3776
4145
|
}
|
|
3777
4146
|
return rawPeerItems;
|
|
3778
4147
|
})();
|
|
3779
|
-
//
|
|
3780
|
-
//
|
|
3781
|
-
|
|
3782
|
-
|
|
4148
|
+
// 将匹配的 pending hint 暂时 claim,排在真实消息之前;渲染或启动
|
|
4149
|
+
// 失败时释放 claim,只有 runner 接受 prompt 后才持久化 consumed。
|
|
4150
|
+
const hintResult = this.consumeOwnerHints(session, message);
|
|
4151
|
+
ownerHintClaim = hintResult.claim;
|
|
4152
|
+
const hintItems = hintResult.items;
|
|
4153
|
+
if (message.source === 'owner-inject' && hintItems.length === 0) {
|
|
4154
|
+
if (turnLease)
|
|
4155
|
+
await this.turnCoordinator.finish(session, turnLease, 'cancelled', 'owner_hint_unavailable');
|
|
4156
|
+
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
3783
4159
|
const renderItems = hintItems.length > 0 ? [...hintItems, ...peerItems] : peerItems;
|
|
3784
4160
|
const fallbackContent = (() => {
|
|
3785
4161
|
if (!message.restartResume?.submitted || peerItems.length === 0)
|
|
@@ -3820,8 +4196,16 @@ export class ResponseEngine {
|
|
|
3820
4196
|
effectivePrompt = wrapPrompt(composeHintFallback(hintItems, fallbackContent));
|
|
3821
4197
|
}
|
|
3822
4198
|
catch (e) {
|
|
3823
|
-
logger.warn(`[ResponseEngine] renderMessageBody failed
|
|
3824
|
-
|
|
4199
|
+
logger.warn(`[ResponseEngine] renderMessageBody failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
4200
|
+
if (ownerHintClaim) {
|
|
4201
|
+
observerHintFailureCode = 'RENDER_FAILED';
|
|
4202
|
+
this.reportObserverHintFailureForClaim(message, session, ownerHintClaim, observerHintFailureCode);
|
|
4203
|
+
releaseHintClaim(ownerHintClaim);
|
|
4204
|
+
ownerHintClaim = undefined;
|
|
4205
|
+
}
|
|
4206
|
+
if (message.source === 'owner-inject')
|
|
4207
|
+
throw e;
|
|
4208
|
+
effectivePrompt = wrapPrompt(fallbackContent);
|
|
3825
4209
|
}
|
|
3826
4210
|
if (v2HandoffIds.length > 0 && renderResult?.body.trim())
|
|
3827
4211
|
handoffPromptRendered = true;
|
|
@@ -3835,6 +4219,12 @@ export class ResponseEngine {
|
|
|
3835
4219
|
// 空消息防护:在 agent 调用之前检查 prompt 是否为空
|
|
3836
4220
|
// 防止空消息(或纯空格消息)浪费 API 调用
|
|
3837
4221
|
if (!effectivePrompt.trim()) {
|
|
4222
|
+
if (ownerHintClaim) {
|
|
4223
|
+
observerHintFailureCode = 'HINT_UNAVAILABLE';
|
|
4224
|
+
this.reportObserverHintFailureForClaim(message, session, ownerHintClaim, observerHintFailureCode);
|
|
4225
|
+
releaseHintClaim(ownerHintClaim);
|
|
4226
|
+
ownerHintClaim = undefined;
|
|
4227
|
+
}
|
|
3838
4228
|
logger.info(`[ResponseEngine] Skip agent call: empty prompt after render. session=${session.id} task=${taskId}`);
|
|
3839
4229
|
if (turnLease)
|
|
3840
4230
|
await this.turnCoordinator.finish(session, turnLease, 'cancelled', 'empty_prompt');
|
|
@@ -3870,9 +4260,18 @@ export class ResponseEngine {
|
|
|
3870
4260
|
chatType: configChatType,
|
|
3871
4261
|
selfAid,
|
|
3872
4262
|
peerId: configActorId || undefined,
|
|
4263
|
+
peerKey,
|
|
3873
4264
|
peerName: peerName || undefined,
|
|
3874
4265
|
peerType: message.peerType || session.metadata?.peerType || undefined,
|
|
3875
4266
|
peerRole,
|
|
4267
|
+
// 会话原型与整理模式:ipc 侧靠 sessionType 拦非整理会话的写命令,
|
|
4268
|
+
// 靠 memoryMode 校验 --scope 是否与本模式匹配。
|
|
4269
|
+
sessionType: renderedSessionType,
|
|
4270
|
+
memoryMode: session.metadata?.memoryMode ?? undefined,
|
|
4271
|
+
memoryScope: session.metadata?.memoryScope ?? undefined,
|
|
4272
|
+
consolidationBatchDir: session.metadata?.consolidationBatchDir
|
|
4273
|
+
?? session.metadata?.consolidationLogBase
|
|
4274
|
+
?? undefined,
|
|
3876
4275
|
permissionMode: effectivePermissionMode,
|
|
3877
4276
|
...(fullAccessAuthorization ? {
|
|
3878
4277
|
processRole: fullAccessAuthorization.processRole,
|
|
@@ -3932,6 +4331,7 @@ export class ResponseEngine {
|
|
|
3932
4331
|
let attemptTimeout;
|
|
3933
4332
|
try {
|
|
3934
4333
|
logger.info(`[ResponseEngine] agent.runQuery start: agent=${agent.name} session=${session.id} task=${taskId} attempt=${runAttempt} apiRetries=${retryScheduler.apiRetryAttemptsMade} agentSessionId=${runAgentSessionId ?? 'none'} topicBinding=${session.threadId ? 'authoritative' : 'legacy'}`);
|
|
4334
|
+
import('../../utils/msg-diag.js').then(m => m.diagLog('task_start', { aid: session.selfAID ?? '', msgId: message.messageId ?? '', taskId, sessionId: runAgentSessionId ?? undefined, text: message.content ?? '' })).catch(() => { });
|
|
3935
4335
|
if (fullAccessAuthorization) {
|
|
3936
4336
|
this.assertCurrentFullAccessExecutionAuthorization(fullAccessAuthorization, agent);
|
|
3937
4337
|
if (fullAccessAuditStartedAt === undefined) {
|
|
@@ -3984,13 +4384,41 @@ export class ResponseEngine {
|
|
|
3984
4384
|
modelOverride = { ...modelOverride,
|
|
3985
4385
|
enableRequestUserInput: effectiveAgentConfig?.baseagents?.codex?.enableRequestUserInput ?? true };
|
|
3986
4386
|
}
|
|
3987
|
-
const
|
|
4387
|
+
const sourceImages = renderResult?.images.length ? renderResult.images : message.images;
|
|
4388
|
+
const runnerImages = sourceImages?.map(image => ({
|
|
4389
|
+
data: image.data,
|
|
4390
|
+
mimeType: image.mimeType,
|
|
4391
|
+
}));
|
|
4392
|
+
debugRunnerModel = modelOverride?.model || agentModel;
|
|
4393
|
+
const stream = await agent.runQuery(session.id, effectivePrompt, absoluteProjectPath, runAgentSessionId, runnerImages, effectiveSystemPrompt, this.sessionManager, modelOverride, runtimeEnv);
|
|
3988
4394
|
// The turn may be superseded while the runner is still creating its
|
|
3989
4395
|
// backend query. Replay the interrupt after runQuery resolves, when
|
|
3990
4396
|
// every runner is required to have an addressable cancellation handle.
|
|
3991
4397
|
if (await this.cancelStaleRunnerStart(agent, session.id, streamKey, turnLease, taskId)) {
|
|
3992
4398
|
throw new Error('RUNNER_START_SUPERSEDED');
|
|
3993
4399
|
}
|
|
4400
|
+
if (ownerHintClaim) {
|
|
4401
|
+
const accepted = ownerHintClaim;
|
|
4402
|
+
if (commitHintClaim(accepted)) {
|
|
4403
|
+
for (const hint of accepted.hints) {
|
|
4404
|
+
try {
|
|
4405
|
+
this.observerHintConsumed?.(hint, {
|
|
4406
|
+
channelKey: session.channel, channelId: accepted.channelId,
|
|
4407
|
+
chatType: accepted.chatType, threadId: accepted.threadId || undefined,
|
|
4408
|
+
}, {
|
|
4409
|
+
action: hint.promoteRequestId ? 'promote' : 'add',
|
|
4410
|
+
requestId: hint.promoteRequestId ?? message.observerHintRequestId ?? (hint.mode === 'interrupt' ? hint.id : undefined),
|
|
4411
|
+
});
|
|
4412
|
+
}
|
|
4413
|
+
catch (error) {
|
|
4414
|
+
logger.warn(`[ResponseEngine] observer hint status failed id=${hint.id}: ${error}`);
|
|
4415
|
+
}
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
else
|
|
4419
|
+
releaseHintClaim(accepted);
|
|
4420
|
+
ownerHintClaim = undefined;
|
|
4421
|
+
}
|
|
3994
4422
|
agent.registerStream(streamKey, stream);
|
|
3995
4423
|
streamRegistered = true;
|
|
3996
4424
|
if (shouldTimeoutRetryAttempt) {
|
|
@@ -4000,7 +4428,11 @@ export class ResponseEngine {
|
|
|
4000
4428
|
const processAttempt = this.processEventStream(stream, session, agent, renderer, (eventType, toolName) => {
|
|
4001
4429
|
resetTimer(eventType, toolName);
|
|
4002
4430
|
attemptTimeout?.reset();
|
|
4003
|
-
}, shouldSuppress, proactive, resolvedMode ? {
|
|
4431
|
+
}, shouldSuppress, proactive, resolvedMode ? {
|
|
4432
|
+
mode: resolvedMode.mode,
|
|
4433
|
+
state: modeState,
|
|
4434
|
+
sessionState: resolvedMode.context.sessionState,
|
|
4435
|
+
} : undefined, taskCausation, turnLease, effectivePermissionMode, selfAid, async (request) => {
|
|
4004
4436
|
if (!this.asyncQuestions)
|
|
4005
4437
|
throw new Error('Async question service is unavailable');
|
|
4006
4438
|
if (permissionContext.approvalInteractionPolicy === 'deny') {
|
|
@@ -4166,6 +4598,7 @@ export class ResponseEngine {
|
|
|
4166
4598
|
}
|
|
4167
4599
|
}
|
|
4168
4600
|
catch (error) {
|
|
4601
|
+
observerHintFailureCode = this.observerHintFailureCodeForError(error);
|
|
4169
4602
|
if (classifyError(error) === ErrorType.CONTEXT_TOO_LONG && session.agentSessionId && canCompactAgent(agent)) {
|
|
4170
4603
|
streamResult = await this.recoverFromContextLimit(streamResult, {
|
|
4171
4604
|
streamKey,
|
|
@@ -4420,6 +4853,11 @@ export class ResponseEngine {
|
|
|
4420
4853
|
// 最终回复文本:suppressed 模式或无 text 事件时需要兜底添加
|
|
4421
4854
|
const finalReplyText = streamResult.lastReplyText || streamResult.fullText;
|
|
4422
4855
|
const visibleFinalReplyText = stripContextLimitErrorLines(finalReplyText);
|
|
4856
|
+
const isDiagnosticFallback = streamResult.isError && !streamResult.hasReceivedText;
|
|
4857
|
+
const isTriggerNoop = message.source === 'trigger'
|
|
4858
|
+
&& typeof message.triggerMeta?.noopSentinel === 'string'
|
|
4859
|
+
&& message.triggerMeta.noopSentinel.length > 0
|
|
4860
|
+
&& visibleFinalReplyText.trim() === message.triggerMeta.noopSentinel;
|
|
4423
4861
|
if (!this.turnCoordinator.isCurrent(turnLease)) {
|
|
4424
4862
|
agent.cleanupStream(streamKey);
|
|
4425
4863
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
@@ -4431,7 +4869,7 @@ export class ResponseEngine {
|
|
|
4431
4869
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_turn', taskCausation);
|
|
4432
4870
|
return;
|
|
4433
4871
|
}
|
|
4434
|
-
if (visibleFinalReplyText) {
|
|
4872
|
+
if (visibleFinalReplyText && !isDiagnosticFallback && !isTriggerNoop) {
|
|
4435
4873
|
// `hasReceivedText` 是任务级计数。工具调用前的文字已经显示,并不表示最后一轮
|
|
4436
4874
|
// (通常来自 complete.result)的文字也已显示;用本轮投影标记避免终态丢失。
|
|
4437
4875
|
if (shouldSuppress() || !streamResult.hasProjectedCurrentReplyText) {
|
|
@@ -4447,6 +4885,7 @@ export class ResponseEngine {
|
|
|
4447
4885
|
}
|
|
4448
4886
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
4449
4887
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared task=${taskId}`);
|
|
4888
|
+
import('../../utils/msg-diag.js').then(m => m.diagLog('task_done', { aid: session.selfAID ?? '', msgId: message.messageId ?? '', taskId, text: message.content ?? '' })).catch(() => { });
|
|
4450
4889
|
// 降级模型回复末尾追加标记(代码层硬注入,不依赖模型输出)
|
|
4451
4890
|
if (usedFallback) {
|
|
4452
4891
|
const curFbState = this.modelFallbackMap.get(session.id);
|
|
@@ -4518,6 +4957,7 @@ export class ResponseEngine {
|
|
|
4518
4957
|
// 这不是真正的失败,不应把诊断串暴露给用户,也不计入错误统计。
|
|
4519
4958
|
const isExpectedInterrupt = isNonErrorInterruptReason(interruptReason)
|
|
4520
4959
|
|| isExpectedTerminalInterrupt(streamResult.terminalReason);
|
|
4960
|
+
taskActivityStatus = isExpectedInterrupt ? 'interrupted' : 'error';
|
|
4521
4961
|
if (!isExpectedInterrupt && !permissionTerminalFailure) {
|
|
4522
4962
|
const terminalMetadata = {
|
|
4523
4963
|
errorType: rawSubtype,
|
|
@@ -4607,6 +5047,7 @@ export class ResponseEngine {
|
|
|
4607
5047
|
}
|
|
4608
5048
|
else {
|
|
4609
5049
|
// 真正的成功
|
|
5050
|
+
taskActivityStatus = 'completed';
|
|
4610
5051
|
if (!this.turnCoordinator.canPublish(turnLease)) {
|
|
4611
5052
|
logger.info(`[ResponseEngine] Suppressing stale success path: session=${session.id} task=${taskId}`);
|
|
4612
5053
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_turn', taskCausation);
|
|
@@ -4809,6 +5250,7 @@ export class ResponseEngine {
|
|
|
4809
5250
|
durationMs: Date.now() - startTime,
|
|
4810
5251
|
agentName: agentNameForStats,
|
|
4811
5252
|
numTurns: streamResult.numTurns,
|
|
5253
|
+
encrypted: message.encrypted ?? message.replyContext?.metadata?.encrypted,
|
|
4812
5254
|
timestamp: Date.now(),
|
|
4813
5255
|
causation: taskCausation,
|
|
4814
5256
|
});
|
|
@@ -4847,6 +5289,15 @@ export class ResponseEngine {
|
|
|
4847
5289
|
});
|
|
4848
5290
|
}
|
|
4849
5291
|
catch (error) {
|
|
5292
|
+
if (error instanceof Error && (error.message === 'TOTAL_EXECUTION_TIMEOUT' || error.message === 'SDK_TIMEOUT')) {
|
|
5293
|
+
taskActivityStatus = 'timeout';
|
|
5294
|
+
}
|
|
5295
|
+
else if (isNonErrorInterruptReason(this.interruptedSessions.get(session.id))) {
|
|
5296
|
+
taskActivityStatus = 'interrupted';
|
|
5297
|
+
}
|
|
5298
|
+
else {
|
|
5299
|
+
taskActivityStatus = 'error';
|
|
5300
|
+
}
|
|
4850
5301
|
fullAccessExecutionFailed = true;
|
|
4851
5302
|
fullAccessExecutionFailureReason = error instanceof Error ? error.message : String(error);
|
|
4852
5303
|
const authoritativeTimeoutError = timeoutControl?.claim();
|
|
@@ -5064,6 +5515,37 @@ export class ResponseEngine {
|
|
|
5064
5515
|
}
|
|
5065
5516
|
}
|
|
5066
5517
|
finally {
|
|
5518
|
+
if (taskActivityStore) {
|
|
5519
|
+
try {
|
|
5520
|
+
taskActivityStore.updateStatus(taskId, taskActivityStatus);
|
|
5521
|
+
}
|
|
5522
|
+
catch (error) {
|
|
5523
|
+
logger.warn(`[ResponseEngine] task activity status update failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
if (turnLease && resolvedMode?.mode.onTurnEnd) {
|
|
5527
|
+
try {
|
|
5528
|
+
const current = this.turnCoordinator.current(session);
|
|
5529
|
+
await resolvedMode.mode.onTurnEnd({
|
|
5530
|
+
session,
|
|
5531
|
+
state: modeState,
|
|
5532
|
+
sessionState: resolvedMode.context.sessionState,
|
|
5533
|
+
turn: turnLease,
|
|
5534
|
+
terminal: current.active ? undefined : current.lastTerminal,
|
|
5535
|
+
logger,
|
|
5536
|
+
});
|
|
5537
|
+
}
|
|
5538
|
+
catch (error) {
|
|
5539
|
+
logger.warn(`[ResponseEngine] onTurnEnd failed: session=${session.id} task=${taskId} error=${String(error)}`);
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5542
|
+
if (ownerHintClaim) {
|
|
5543
|
+
// A runner error, interruption, or pre-run failure leaves the durable
|
|
5544
|
+
// hint pending for the next attempt.
|
|
5545
|
+
this.reportObserverHintFailureForClaim(message, session, ownerHintClaim, observerHintFailureCode ?? 'RUNNER_START_FAILED');
|
|
5546
|
+
releaseHintClaim(ownerHintClaim);
|
|
5547
|
+
ownerHintClaim = undefined;
|
|
5548
|
+
}
|
|
5067
5549
|
if (fullAccessAuthorization) {
|
|
5068
5550
|
const startedAt = fullAccessAuditStartedAt;
|
|
5069
5551
|
const executed = fullAccessRunnerInvoked;
|
|
@@ -5212,6 +5694,30 @@ export class ResponseEngine {
|
|
|
5212
5694
|
* 解析会话和项目路径
|
|
5213
5695
|
*/
|
|
5214
5696
|
async resolveSession(message) {
|
|
5697
|
+
if (message.source === 'owner-inject') {
|
|
5698
|
+
const bound = message.boundSessionId
|
|
5699
|
+
? await this.sessionManager.getSessionById(message.boundSessionId) : undefined;
|
|
5700
|
+
if (!message.observerHintId || !bound || bound.deletedAt || !bound.agentSessionId
|
|
5701
|
+
|| bound.channel !== message.channel || bound.channelId !== message.channelId
|
|
5702
|
+
|| bound.selfAID !== message.selfAID || bound.chatType !== message.chatType
|
|
5703
|
+
|| (bound.threadId || '') !== (message.threadId || '')) {
|
|
5704
|
+
throw new Error('Observer hint target session is no longer available');
|
|
5705
|
+
}
|
|
5706
|
+
// An enqueued priority hint is bound to exactly the session selected at
|
|
5707
|
+
// submission. A later /new, /renew, thread switch or missing provider
|
|
5708
|
+
// file must never silently resume a stale/different backend session.
|
|
5709
|
+
const current = await this.observerTargetSession({
|
|
5710
|
+
channelKey: bound.channel, channelId: bound.channelId,
|
|
5711
|
+
chatType: bound.chatType, threadId: bound.threadId,
|
|
5712
|
+
selfAid: bound.selfAID,
|
|
5713
|
+
});
|
|
5714
|
+
if (current?.id !== bound.id || current.agentSessionId !== bound.agentSessionId) {
|
|
5715
|
+
throw new Error('Observer hint target session is no longer available');
|
|
5716
|
+
}
|
|
5717
|
+
bound.identity = this.inferDirectSessionIdentity(message, this.agentRegistry?.resolveByChannel(bound.channel));
|
|
5718
|
+
return { session: bound, absoluteProjectPath: path.isAbsolute(bound.projectPath)
|
|
5719
|
+
? bound.projectPath : path.resolve(process.cwd(), bound.projectPath) };
|
|
5720
|
+
}
|
|
5215
5721
|
// 话题会话创建时写入创建者和 replyContext(threadId 路由);主会话不写(避免群聊覆盖)
|
|
5216
5722
|
const metadata = message.threadId
|
|
5217
5723
|
? {
|
|
@@ -5245,7 +5751,7 @@ export class ResponseEngine {
|
|
|
5245
5751
|
// valid mapped owner/admin to role=none at the execution boundary.
|
|
5246
5752
|
if (resolvedIdentity)
|
|
5247
5753
|
bound.identity = resolvedIdentity;
|
|
5248
|
-
if (bound.threadId) {
|
|
5754
|
+
if (bound.threadId || bound.metadata?.diagnostic) {
|
|
5249
5755
|
const absoluteProjectPath = path.isAbsolute(bound.projectPath)
|
|
5250
5756
|
? bound.projectPath : path.resolve(process.cwd(), bound.projectPath);
|
|
5251
5757
|
return { session: bound, absoluteProjectPath };
|
|
@@ -5271,7 +5777,7 @@ export class ResponseEngine {
|
|
|
5271
5777
|
logger.warn(`[ResponseEngine] Bound session ${boundSessionId} not found, falling back to latest`);
|
|
5272
5778
|
}
|
|
5273
5779
|
}
|
|
5274
|
-
const session = await this.sessionManager.getOrCreateSession(message.channel, message.channelId, projectPath, message.threadId, metadata, message.topicName, message.peerId, message.chatType, resolvedBaseagent, message.selfAID, message.channelType, message.peerType, resolvedIdentity, message.lable);
|
|
5780
|
+
const session = await this.sessionManager.getOrCreateSession(message.channel, message.channelId, projectPath, message.threadId, metadata, message.topicName, message.peerId, message.chatType, resolvedBaseagent, message.selfAID, message.channelType, message.peerType, resolvedIdentity, message.label ?? message.lable, message.topicStatus);
|
|
5275
5781
|
this.ensureSessionBaseagent(session, resolvedBaseagent);
|
|
5276
5782
|
// 群名解析:群会话首次取群显示名(group.get),缓存到 metadata,供信封渲染。
|
|
5277
5783
|
// 渠道私有方法 getGroupName 自带进程缓存 + 容错;取不到不阻塞(groupName 保持空,模板回退 groupId)。
|
|
@@ -5398,16 +5904,14 @@ export class ResponseEngine {
|
|
|
5398
5904
|
event = normalizeCompleteAgentEvent(event);
|
|
5399
5905
|
}
|
|
5400
5906
|
if (event.type === 'tool_result' && event.isError) {
|
|
5401
|
-
const
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
event = { ...event, errorCode, ...(classificationMissing ? { classificationMissing: true } : {}) };
|
|
5410
|
-
}
|
|
5907
|
+
const classification = normalizeToolErrorClassification(event);
|
|
5908
|
+
event = {
|
|
5909
|
+
...event,
|
|
5910
|
+
errorCode: classification.errorCode,
|
|
5911
|
+
errorDetailCode: classification.errorDetailCode,
|
|
5912
|
+
classificationMissing: classification.classificationMissing || event.classificationMissing,
|
|
5913
|
+
classificationConflict: classification.classificationConflict || event.classificationConflict,
|
|
5914
|
+
};
|
|
5411
5915
|
}
|
|
5412
5916
|
// 每收到事件重置空闲超时
|
|
5413
5917
|
const toolName = event.type === 'tool_use' ? event.name : undefined;
|
|
@@ -5705,7 +6209,7 @@ export class ResponseEngine {
|
|
|
5705
6209
|
this.eventBus.publish({ type: 'message:text', sessionId: session.id, text: visibleText, isFinal: false });
|
|
5706
6210
|
}
|
|
5707
6211
|
if (visibleText && !shouldSuppress()) {
|
|
5708
|
-
await renderer.addText(visibleText, event.outputTokens, event.turn, event.assistantUuid);
|
|
6212
|
+
await renderer.addText(visibleText, event.outputTokens, event.turn, event.assistantUuid, event.phase);
|
|
5709
6213
|
hasProjectedCurrentReplyText = true;
|
|
5710
6214
|
}
|
|
5711
6215
|
}
|
|
@@ -5785,7 +6289,7 @@ export class ResponseEngine {
|
|
|
5785
6289
|
if (!shouldSuppress()) {
|
|
5786
6290
|
if (event.callId)
|
|
5787
6291
|
toolDescByCallId.set(event.callId, desc);
|
|
5788
|
-
renderer.addToolCall(event.name, event.input, event.callId, desc, event.turn, event.outputTokens);
|
|
6292
|
+
renderer.addToolCall(event.name, event.input, event.callId, desc, event.turn, event.outputTokens, event.assistantUuid);
|
|
5789
6293
|
}
|
|
5790
6294
|
if (event.callId && isCtlQueueReadCommand(event.name, event.input || {})) {
|
|
5791
6295
|
ctlQueueReadCallIds.add(event.callId);
|
|
@@ -5796,6 +6300,8 @@ export class ResponseEngine {
|
|
|
5796
6300
|
modeHooks.mode.onToolUse({
|
|
5797
6301
|
session,
|
|
5798
6302
|
state: modeHooks.state,
|
|
6303
|
+
sessionState: modeHooks.sessionState,
|
|
6304
|
+
turn: turnLease,
|
|
5799
6305
|
toolName: event.name,
|
|
5800
6306
|
toolInput: event.input || {},
|
|
5801
6307
|
injectToModel: (text) => { agent.injectUserMessage?.(session.id, text); },
|
|
@@ -5844,14 +6350,12 @@ export class ResponseEngine {
|
|
|
5844
6350
|
toolName: event.name,
|
|
5845
6351
|
isError: event.isError,
|
|
5846
6352
|
...(event.isError ? {
|
|
5847
|
-
errorCode:
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
}),
|
|
5851
|
-
...((event.classificationMissing || (!normalizeToolErrorCode(event.errorCode)
|
|
5852
|
-
&& event.decisionSource !== 'policy'
|
|
5853
|
-
&& event.decisionSource !== 'approval')) ? { classificationMissing: true } : {}),
|
|
6353
|
+
errorCode: event.errorCode,
|
|
6354
|
+
...(event.errorDetailCode ? { errorDetailCode: event.errorDetailCode } : {}),
|
|
6355
|
+
...(event.classificationMissing ? { classificationMissing: true } : {}),
|
|
6356
|
+
...(event.classificationConflict ? { classificationConflict: true } : {}),
|
|
5854
6357
|
} : {}),
|
|
6358
|
+
...(event.exitCode !== undefined ? { exitCode: event.exitCode } : {}),
|
|
5855
6359
|
...(event.callId ? { callId: event.callId } : {}),
|
|
5856
6360
|
...(event.correlationId || event.callId ? { correlationId: event.correlationId ?? event.callId } : {}),
|
|
5857
6361
|
...(event.requestId ? { requestId: event.requestId } : {}),
|
|
@@ -5907,6 +6411,8 @@ export class ResponseEngine {
|
|
|
5907
6411
|
await modeHooks.mode.onToolResult({
|
|
5908
6412
|
session,
|
|
5909
6413
|
state: modeHooks.state,
|
|
6414
|
+
sessionState: modeHooks.sessionState,
|
|
6415
|
+
turn: turnLease,
|
|
5910
6416
|
toolName: original.name,
|
|
5911
6417
|
toolInput: original.input,
|
|
5912
6418
|
result: event.result,
|
|
@@ -5941,10 +6447,10 @@ export class ResponseEngine {
|
|
|
5941
6447
|
let errorMsg = event.error || (typeof event.result === 'string' ? event.result : JSON.stringify(event.result)) || '\u6267\u884c\u5931\u8d25';
|
|
5942
6448
|
// 移除 XML 风格的错误标签
|
|
5943
6449
|
errorMsg = errorMsg.replace(/<tool_use_error>(.*?)<\/tool_use_error>/gs, '$1');
|
|
5944
|
-
renderer.addToolResult(event.name || '\u5de5\u5177', false, undefined, errorMsg, event.callId, event.durationMs, cachedDesc, event);
|
|
6450
|
+
renderer.addToolResult(event.name || '\u5de5\u5177', false, undefined, errorMsg, event.callId, event.durationMs, cachedDesc, event, event.assistantUuid);
|
|
5945
6451
|
}
|
|
5946
6452
|
else if (!event.isError && !shouldSuppress()) {
|
|
5947
|
-
renderer.addToolResult(event.name || '\u5de5\u5177', true, event.result, undefined, event.callId, event.durationMs, cachedDesc);
|
|
6453
|
+
renderer.addToolResult(event.name || '\u5de5\u5177', true, event.result, undefined, event.callId, event.durationMs, cachedDesc, undefined, event.assistantUuid);
|
|
5948
6454
|
}
|
|
5949
6455
|
}
|
|
5950
6456
|
// 非终态运行时告警走 status notify,不进入 activity/thought 历史。
|