evolcore 0.0.32 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +23 -19
- package/dist/agents/ecagent-runner.js +5 -13
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/channels/aun.js +28 -2
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +365 -29
- package/dist/cli/contact.js +8 -2
- package/dist/cli/daemon-commands.js +81 -12
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +8 -1
- package/dist/cli/queue-command.js +9 -4
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +78 -2
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +620 -0
- package/dist/cli/trigger-command.js +4 -2
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/auth/agent-delegation.js +25 -1
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +33 -0
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/command-handler.js +60 -24
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +139 -2
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +349 -67
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +126 -7
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +8 -5
- package/dist/ipc.js +89 -10
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/07-memory-and-thread.md +202 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +66 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
|
@@ -11,6 +11,7 @@ import { authorizeAccess, authorizeOperation, buildAuthSubject } from '../auth/a
|
|
|
11
11
|
import { buildTaskRuntimeEnv, ensureRuntimeLockDir, ensureSessionRuntimeDir } from '../../cli/task-context.js';
|
|
12
12
|
import { memorySessionPolicyHook } from '../memory/memory-session-policy.js';
|
|
13
13
|
import { IMRenderer } from './im-renderer.js';
|
|
14
|
+
import { projectSubtaskStarted, projectSubtaskTerminal } from './subtask-tool-projection.js';
|
|
14
15
|
import { TaskActivityStore } from '../task/task-activity-store.js';
|
|
15
16
|
import { createSendFileMarkerPattern } from './file-markers.js';
|
|
16
17
|
import { createDecisionInferenceProvider, createTextInferenceProvider } from '../inference/text-inference.js';
|
|
@@ -19,9 +20,10 @@ import { logger } from '../../utils/logger.js';
|
|
|
19
20
|
import { isHostChinese } from '../../utils/locale.js';
|
|
20
21
|
import { ExecutionTimeoutError, OrphanedTargetSessionError, isExecutionFailure, getErrorMessage, classifyError, ErrorType, ERROR_PREFIX, isInfraError, isAbortTerminalReason, prefixErrorType, isRetryableError, isContextTooLongText } from '../../utils/error-utils.js';
|
|
21
22
|
import { readEvolcoreRuntimeFlavor, tagErrorTextWithEvolcoreVersion } from '../../utils/evolcore-version.js';
|
|
22
|
-
import { describeProactiveSendCommandFailure, isExactEvolcoreSendCommandForSession } from '../permission/ec-command-parser.js';
|
|
23
|
+
import { describeProactiveSendCommandFailure, isExactEvolcoreSendCommandForSession, isThreadRoutedEvolcoreSendCommand } from '../permission/ec-command-parser.js';
|
|
24
|
+
import { resolveThreadRouteTarget } from '../thread/thread-route.js';
|
|
23
25
|
import { summarizeToolInputForDisplay } from '../../utils/tool-summary.js';
|
|
24
|
-
import { agentDataDir, agentDir as resolveAgentDir, agentMdPath, getPackageRoot, resolveRoot, resolvePaths } from '../../paths.js';
|
|
26
|
+
import { agentDataDir, agentDir as resolveAgentDir, agentMdPath, getPackageRoot, resolveRoot, resolvePaths, debugSessionsDir } from '../../paths.js';
|
|
25
27
|
import { renderKitSections, resolveSessionManifest, DEFAULT_MANIFEST_FILE } from '../../eck/kit-renderer.js';
|
|
26
28
|
import { renderMessageBody } from '../../eck/message-renderer.js';
|
|
27
29
|
import { syncGroupRulesContext } from '../../eck/group-rules-sync.js';
|
|
@@ -67,6 +69,10 @@ import { auditFullAccessEvent } from '../auth/authorization-audit.js';
|
|
|
67
69
|
import { TaskRecoveryService, isTaskRecoveryCurrent } from '../task/task-recovery-service.js';
|
|
68
70
|
const QUESTION_TTL_MS = 24 * 60 * 60 * 1000;
|
|
69
71
|
/** Pending async questions outlive the originating turn, but never change its backend binding. */
|
|
72
|
+
/** 带 --thread 的跨话题发送:话题存在于本端索引且目标一致时视为合法发送。 */
|
|
73
|
+
function isThreadRoutedSendForSelf(toolName, toolInput, selfAid) {
|
|
74
|
+
return isThreadRoutedEvolcoreSendCommand(toolName, toolInput, selfAid, (scope, threadId, target) => !!selfAid && !!resolveThreadRouteTarget(selfAid, threadId, scope, target));
|
|
75
|
+
}
|
|
70
76
|
export class AsyncQuestionService {
|
|
71
77
|
deps;
|
|
72
78
|
registered = new Map();
|
|
@@ -673,16 +679,8 @@ function getStreamErrorText(result) {
|
|
|
673
679
|
return parts.join('\n');
|
|
674
680
|
}
|
|
675
681
|
function getStreamErrorMessage(result, includeEmoji = false) {
|
|
676
|
-
const raw = getStreamErrorText(result)
|
|
677
|
-
|
|
678
|
-
const generic = includeEmoji ? '❌ 处理消息时出错,请稍后重试' : '处理消息时出错,请稍后重试';
|
|
679
|
-
// Unknown runner-specific terminal reasons do not have a friendly mapping.
|
|
680
|
-
// Preserve their diagnostic text instead of collapsing it to the generic
|
|
681
|
-
// "处理消息时出错" message.
|
|
682
|
-
if (raw && mapped === generic) {
|
|
683
|
-
return includeEmoji ? `❌ ${raw}` : raw;
|
|
684
|
-
}
|
|
685
|
-
return mapped;
|
|
682
|
+
const raw = getStreamErrorText(result);
|
|
683
|
+
return getErrorMessage(new Error(raw), result.terminalReason, includeEmoji);
|
|
686
684
|
}
|
|
687
685
|
function streamHitContextLimit(result) {
|
|
688
686
|
return result.terminalReason === 'prompt_too_long' ||
|
|
@@ -826,6 +824,8 @@ export class ResponseEngine {
|
|
|
826
824
|
activeMonitors = new Map();
|
|
827
825
|
/** sessionId → 当前正在处理任务的运行时上下文,供 in-task CLI 通过 IPC 查询。 */
|
|
828
826
|
activeTaskRuntimeContexts = new Map();
|
|
827
|
+
/** sessionId → 本轮最近一次 runQuery 请求的模型;仅在任务活跃时对外可见(见 getOutboundDebug)。 */
|
|
828
|
+
activeRunnerModels = new Map();
|
|
829
829
|
/** task.run started 后立即登记,覆盖运行时上下文尚未建立的 shutdown 窗口。 */
|
|
830
830
|
activeTaskSpans = new Map();
|
|
831
831
|
/** sessionId → 尚未收到 runner:task-notification 的子任务。 */
|
|
@@ -1404,6 +1404,7 @@ export class ResponseEngine {
|
|
|
1404
1404
|
this.sessionManager.clearProcessingIfTask(sessionId, taskId);
|
|
1405
1405
|
if (this.activeTaskRuntimeContexts.get(sessionId)?.taskId === taskId) {
|
|
1406
1406
|
this.activeTaskRuntimeContexts.delete(sessionId);
|
|
1407
|
+
this.activeRunnerModels.delete(sessionId);
|
|
1407
1408
|
}
|
|
1408
1409
|
this.agentDelegationRegistry?.revokeTask(sessionId, taskId);
|
|
1409
1410
|
}
|
|
@@ -1482,6 +1483,64 @@ export class ResponseEngine {
|
|
|
1482
1483
|
getTaskRuntimeContext(sessionId) {
|
|
1483
1484
|
return this.activeTaskRuntimeContexts.get(sessionId) ?? null;
|
|
1484
1485
|
}
|
|
1486
|
+
/** debug 模式下写/删 debug-sessions 临时文件 */
|
|
1487
|
+
debugWriteSessionSnapshot(sessionId, ctx) {
|
|
1488
|
+
if (process.env.EVOLCORE_DEBUG_MODE !== '1')
|
|
1489
|
+
return;
|
|
1490
|
+
try {
|
|
1491
|
+
const dir = debugSessionsDir();
|
|
1492
|
+
if (!fs.existsSync(dir))
|
|
1493
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1494
|
+
const snapshot = {
|
|
1495
|
+
schemaVersion: 1,
|
|
1496
|
+
sessionId,
|
|
1497
|
+
selfAid: ctx.selfAid ?? '',
|
|
1498
|
+
channel: ctx.channel ?? '',
|
|
1499
|
+
channelKey: ctx.channelKey ?? '',
|
|
1500
|
+
channelId: ctx.channelId ?? '',
|
|
1501
|
+
peerKey: ctx.peerKey ?? '',
|
|
1502
|
+
chatType: ctx.chatType ?? 'private',
|
|
1503
|
+
threadId: ctx.threadId ?? '',
|
|
1504
|
+
sessionType: ctx.sessionType ?? 'main',
|
|
1505
|
+
baseagent: ctx.baseagent ?? '',
|
|
1506
|
+
writtenAt: Date.now(),
|
|
1507
|
+
};
|
|
1508
|
+
fs.writeFileSync(`${dir}/${sessionId}.json`, JSON.stringify(snapshot), 'utf-8');
|
|
1509
|
+
}
|
|
1510
|
+
catch (err) {
|
|
1511
|
+
logger.debug(`[Debug] failed to write debug session snapshot for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
debugDeleteSessionSnapshot(sessionId) {
|
|
1515
|
+
if (process.env.EVOLCORE_DEBUG_MODE !== '1')
|
|
1516
|
+
return;
|
|
1517
|
+
try {
|
|
1518
|
+
const snapshotPath = `${debugSessionsDir()}/${sessionId}.json`;
|
|
1519
|
+
if (fs.existsSync(snapshotPath))
|
|
1520
|
+
fs.rmSync(snapshotPath, { force: true });
|
|
1521
|
+
}
|
|
1522
|
+
catch (err) {
|
|
1523
|
+
logger.debug(`[Debug] failed to delete debug session snapshot for ${sessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* 出站 payload.debug 的会话侧字段(不含 sentAt,由发送方盖戳)。
|
|
1528
|
+
* 供 agent 经 CLI(ec msg send / ec group send)委托发送的路径使用,
|
|
1529
|
+
* 与 IMRenderer 回复路径写入的 debug 同构。会话不存在返回 undefined。
|
|
1530
|
+
*/
|
|
1531
|
+
async getOutboundDebug(sessionId) {
|
|
1532
|
+
const session = await this.sessionManager.getSessionById(sessionId);
|
|
1533
|
+
if (!session)
|
|
1534
|
+
return undefined;
|
|
1535
|
+
const model = this.activeTaskRuntimeContexts.has(sessionId)
|
|
1536
|
+
? this.activeRunnerModels.get(sessionId)
|
|
1537
|
+
: undefined;
|
|
1538
|
+
return {
|
|
1539
|
+
sessionId: session.id,
|
|
1540
|
+
...(session.agentSessionId ? { agentSessionId: session.agentSessionId } : {}),
|
|
1541
|
+
...(model ? { model } : {}),
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1485
1544
|
setAgentDelegationRegistry(registry) {
|
|
1486
1545
|
this.agentDelegationRegistry = registry;
|
|
1487
1546
|
}
|
|
@@ -1796,6 +1855,71 @@ export class ResponseEngine {
|
|
|
1796
1855
|
&& message.source === 'trigger'
|
|
1797
1856
|
&& !!message.triggerMeta?.triggerId;
|
|
1798
1857
|
}
|
|
1858
|
+
isTrustedBgSession(message) {
|
|
1859
|
+
return message.channel === 'daemon'
|
|
1860
|
+
&& message.source === 'bg-session'
|
|
1861
|
+
&& !!message.bgSessionMeta?.runId;
|
|
1862
|
+
}
|
|
1863
|
+
bgSessionTerminalMessages = new WeakSet();
|
|
1864
|
+
claimBgSessionTerminal(message) {
|
|
1865
|
+
const meta = message.bgSessionMeta;
|
|
1866
|
+
if (message.source !== 'bg-session' || !meta?.runId || this.bgSessionTerminalMessages.has(message)) {
|
|
1867
|
+
return undefined;
|
|
1868
|
+
}
|
|
1869
|
+
this.bgSessionTerminalMessages.add(message);
|
|
1870
|
+
return { meta, runId: meta.runId };
|
|
1871
|
+
}
|
|
1872
|
+
publishBgSessionError(message, error, opts = {}) {
|
|
1873
|
+
const terminal = this.claimBgSessionTerminal(message);
|
|
1874
|
+
if (!terminal)
|
|
1875
|
+
return;
|
|
1876
|
+
const { meta, runId } = terminal;
|
|
1877
|
+
this.eventBus.publish({
|
|
1878
|
+
type: 'bg-session:error',
|
|
1879
|
+
kind: meta.kind,
|
|
1880
|
+
runId,
|
|
1881
|
+
sessionId: meta.boundSessionId ?? '',
|
|
1882
|
+
messageId: opts.messageId ?? message.messageId ?? runId,
|
|
1883
|
+
error,
|
|
1884
|
+
targetChannel: message.channel,
|
|
1885
|
+
targetChannelId: message.channelId,
|
|
1886
|
+
causation: opts.causation ?? message.causation,
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
publishBgSessionInterrupted(message, reason, causation) {
|
|
1890
|
+
const terminal = this.claimBgSessionTerminal(message);
|
|
1891
|
+
if (!terminal)
|
|
1892
|
+
return;
|
|
1893
|
+
const { meta, runId } = terminal;
|
|
1894
|
+
this.eventBus.publish({
|
|
1895
|
+
type: 'bg-session:interrupted',
|
|
1896
|
+
kind: meta.kind,
|
|
1897
|
+
runId,
|
|
1898
|
+
sessionId: meta.boundSessionId ?? '',
|
|
1899
|
+
messageId: message.messageId ?? runId,
|
|
1900
|
+
reason,
|
|
1901
|
+
targetChannel: message.channel,
|
|
1902
|
+
targetChannelId: message.channelId,
|
|
1903
|
+
causation: causation ?? message.causation,
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1906
|
+
publishBgSessionCompleted(message, messageId, durationMs, causation) {
|
|
1907
|
+
const terminal = this.claimBgSessionTerminal(message);
|
|
1908
|
+
if (!terminal)
|
|
1909
|
+
return;
|
|
1910
|
+
const { meta, runId } = terminal;
|
|
1911
|
+
this.eventBus.publish({
|
|
1912
|
+
type: 'bg-session:completed',
|
|
1913
|
+
kind: meta.kind,
|
|
1914
|
+
runId,
|
|
1915
|
+
sessionId: meta.boundSessionId ?? '',
|
|
1916
|
+
messageId,
|
|
1917
|
+
durationMs,
|
|
1918
|
+
targetChannel: message.channel,
|
|
1919
|
+
targetChannelId: message.channelId,
|
|
1920
|
+
causation: causation ?? message.causation,
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1799
1923
|
/**
|
|
1800
1924
|
* A fullaccess Trigger may run with no current relation access for its
|
|
1801
1925
|
* origin actor, but only after its persisted owner provenance still
|
|
@@ -1859,6 +1983,8 @@ export class ResponseEngine {
|
|
|
1859
1983
|
if (!selfAid || !actorId || !channelType || !originChannelKey)
|
|
1860
1984
|
return undefined;
|
|
1861
1985
|
const chatType = message.triggerMeta.originChatType ?? 'private';
|
|
1986
|
+
if (chatType === 'group' && !message.triggerMeta.originConversationId)
|
|
1987
|
+
return undefined;
|
|
1862
1988
|
const conversationId = message.triggerMeta.originConversationId ?? actorId;
|
|
1863
1989
|
const control = message.triggerMeta.originControl === true;
|
|
1864
1990
|
let actor;
|
|
@@ -1889,6 +2015,7 @@ export class ResponseEngine {
|
|
|
1889
2015
|
actorId,
|
|
1890
2016
|
channelType,
|
|
1891
2017
|
originChannelKey,
|
|
2018
|
+
conversationId: chatType === 'group' ? conversationId : actorId,
|
|
1892
2019
|
control,
|
|
1893
2020
|
peerKey: formatPeerKey(channelType, chatType === 'group' ? conversationId : actorId),
|
|
1894
2021
|
};
|
|
@@ -1958,8 +2085,11 @@ export class ResponseEngine {
|
|
|
1958
2085
|
/** Publish a terminal outcome before an early return from Trigger execution. */
|
|
1959
2086
|
publishTriggerExecutionFailure(message, error, opts = {}) {
|
|
1960
2087
|
const terminal = this.claimTriggerTerminal(message);
|
|
1961
|
-
if (!terminal)
|
|
2088
|
+
if (!terminal) {
|
|
2089
|
+
// bg-session 路径:trigger terminal 不匹配时尝试 bg-session
|
|
2090
|
+
this.publishBgSessionError(message, error, { messageId: opts.messageId, causation: opts.causation });
|
|
1962
2091
|
return;
|
|
2092
|
+
}
|
|
1963
2093
|
const { trigger, runId } = terminal;
|
|
1964
2094
|
const attemptId = trigger.attemptId;
|
|
1965
2095
|
this.eventBus.publish({
|
|
@@ -1980,8 +2110,11 @@ export class ResponseEngine {
|
|
|
1980
2110
|
}
|
|
1981
2111
|
publishTriggerExecutionSkipped(message, reason, causation, interruption) {
|
|
1982
2112
|
const terminal = this.claimTriggerTerminal(message);
|
|
1983
|
-
if (!terminal)
|
|
2113
|
+
if (!terminal) {
|
|
2114
|
+
// bg-session 路径
|
|
2115
|
+
this.publishBgSessionInterrupted(message, reason, causation);
|
|
1984
2116
|
return;
|
|
2117
|
+
}
|
|
1985
2118
|
const { trigger, runId } = terminal;
|
|
1986
2119
|
this.eventBus.publish({
|
|
1987
2120
|
type: 'trigger:skipped',
|
|
@@ -2017,6 +2150,14 @@ export class ResponseEngine {
|
|
|
2017
2150
|
logger.warn(`[ResponseEngine] Failed to close interrupted Trigger run=${message.triggerMeta?.runId ?? '<unknown>'}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2018
2151
|
});
|
|
2019
2152
|
}
|
|
2153
|
+
else if (this.isTrustedBgSession(message)) {
|
|
2154
|
+
await adapter.send(envelope, {
|
|
2155
|
+
kind: 'status.interrupted',
|
|
2156
|
+
metadata: { reason },
|
|
2157
|
+
}).catch(error => {
|
|
2158
|
+
logger.warn(`[ResponseEngine] Failed to close interrupted bg-session run=${message.bgSessionMeta?.runId ?? '<unknown>'}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2020
2161
|
this.publishTriggerExecutionSkipped(message, reason, causation, interruption);
|
|
2021
2162
|
}
|
|
2022
2163
|
publishTriggerExecutionCompleted(message, messageId, durationMs, causation) {
|
|
@@ -2888,8 +3029,12 @@ export class ResponseEngine {
|
|
|
2888
3029
|
? { kind: 'restartResume', groupId: message.groupId, threadId: message.threadId }
|
|
2889
3030
|
: undefined,
|
|
2890
3031
|
previousTurnState: this.turnCoordinator.current(session),
|
|
2891
|
-
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, chatType, selfAid),
|
|
2892
|
-
describeSendCommandFailure: describeProactiveSendCommandFailure,
|
|
3032
|
+
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, chatType, selfAid) || isThreadRoutedSendForSelf(toolName, toolInput, selfAid),
|
|
3033
|
+
describeSendCommandFailure: (toolName, toolInput) => describeProactiveSendCommandFailure(toolName, toolInput, {
|
|
3034
|
+
selfAid,
|
|
3035
|
+
channelId: session.channelId,
|
|
3036
|
+
chatType: chatType,
|
|
3037
|
+
}),
|
|
2893
3038
|
logger,
|
|
2894
3039
|
} : null;
|
|
2895
3040
|
if (resolvedMode?.mode.beforeProcess && modeProcessCtx) {
|
|
@@ -2952,6 +3097,7 @@ export class ResponseEngine {
|
|
|
2952
3097
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
2953
3098
|
if (this.activeTaskRuntimeContexts.get(session.id)?.taskId === taskId) {
|
|
2954
3099
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
3100
|
+
this.activeRunnerModels.delete(session.id); // F1修复
|
|
2955
3101
|
}
|
|
2956
3102
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
2957
3103
|
const daemonTrigger = this.isTrustedDaemonTrigger(message);
|
|
@@ -3301,9 +3447,9 @@ export class ResponseEngine {
|
|
|
3301
3447
|
await adapter.send(withEnvelopeReplyContext(envelope, capturedReplyContext), { kind: 'result.text', text, isFinal: false });
|
|
3302
3448
|
};
|
|
3303
3449
|
const authChatType = configChatType;
|
|
3304
|
-
const authConversationId = authChatType === 'group'
|
|
3450
|
+
const authConversationId = triggerExecutionIdentity?.conversationId ?? (authChatType === 'group'
|
|
3305
3451
|
? (session.metadata?.groupId || session.channelId || capturedChannelId)
|
|
3306
|
-
: configActorId;
|
|
3452
|
+
: configActorId);
|
|
3307
3453
|
const authPeerKey = configChannelType && authConversationId
|
|
3308
3454
|
? formatPeerKey(configChannelType, authConversationId)
|
|
3309
3455
|
: undefined;
|
|
@@ -3552,16 +3698,31 @@ export class ResponseEngine {
|
|
|
3552
3698
|
const pluginHook = pureSessionPolicyHook;
|
|
3553
3699
|
if (!pluginHook)
|
|
3554
3700
|
return undefined;
|
|
3555
|
-
return (toolName, toolInput) => {
|
|
3701
|
+
return (toolName, toolInput, meta) => {
|
|
3556
3702
|
const stBefore = modeState.get('proactive');
|
|
3557
3703
|
const wasFirstPending = stBefore && !stBefore.firstToolDone;
|
|
3558
|
-
const result = pluginHook(toolName, toolInput);
|
|
3704
|
+
const result = pluginHook(toolName, toolInput, meta);
|
|
3559
3705
|
// 整理会话的拦截原因已经写清楚怎么改,不套 proactive 违规提醒
|
|
3560
3706
|
if (result?.block && result.policyCode?.startsWith('memory_session_'))
|
|
3561
3707
|
return result;
|
|
3562
3708
|
if (result?.block) {
|
|
3563
3709
|
// 拦截事件(首工具非表态 / 工具进展汇报未完成)
|
|
3564
3710
|
snapshot.set(session.id, taskId, { policyHook: { triggered: true, blocked: true, toolName } });
|
|
3711
|
+
// 问题1/2修复:熔断码表示连续违规已超阈值,停止注入提醒,直接中断任务。
|
|
3712
|
+
// F5修复:向渠道发一条人类可读的错误说明,避免静默中断。
|
|
3713
|
+
if (result.policyCode === 'proactive_circuit_break') {
|
|
3714
|
+
void adapter.send(withEnvelopeReplyContext(envelope, capturedReplyContext), {
|
|
3715
|
+
kind: 'result.text',
|
|
3716
|
+
text: '⚠️ 任务执行遇到循环障碍,已自动中断。请重新描述你的需求。',
|
|
3717
|
+
isFinal: true,
|
|
3718
|
+
}).catch(err => {
|
|
3719
|
+
logger.warn(`[ResponseEngine] circuit break channel send failed: session=${session.id} error=${err instanceof Error ? err.message : String(err)}`);
|
|
3720
|
+
});
|
|
3721
|
+
void agent.interrupt(session.id).catch(error => {
|
|
3722
|
+
logger.debug(`[ResponseEngine] circuit break interrupt failed: session=${session.id} error=${error instanceof Error ? error.message : String(error)}`);
|
|
3723
|
+
});
|
|
3724
|
+
return result;
|
|
3725
|
+
}
|
|
3565
3726
|
const errorMsg = `⚠️ proactive 模式违规:${result.reason ?? '请先发送必要说明'}。请重新执行正确的命令。`;
|
|
3566
3727
|
agent.injectUserMessage?.(session.id, errorMsg);
|
|
3567
3728
|
return result;
|
|
@@ -3605,6 +3766,8 @@ export class ResponseEngine {
|
|
|
3605
3766
|
let previousFallbackState;
|
|
3606
3767
|
let agentModel;
|
|
3607
3768
|
let v2HandoffIds = [];
|
|
3769
|
+
/** T3: 本次注入的 Thread Context 条目(渲染后据此清空 inbox)。 */
|
|
3770
|
+
let threadContextItems = [];
|
|
3608
3771
|
let v2HandoffResultRefs = [];
|
|
3609
3772
|
let v2HandoffDirection;
|
|
3610
3773
|
let handoffPromptRendered = false;
|
|
@@ -4137,6 +4300,16 @@ export class ResponseEngine {
|
|
|
4137
4300
|
const rawPeerItems = message.source === 'owner-inject' ? [] : message.items && message.items.length > 0
|
|
4138
4301
|
? message.items
|
|
4139
4302
|
: [{
|
|
4303
|
+
// T9.5/T10: trigger 消息填充专属 kind + triggerInfo(含 labels),走 msg-trigger-session/thread-check 渲染
|
|
4304
|
+
...(message.source === 'trigger' && message.triggerMeta?.triggerId ? {
|
|
4305
|
+
kind: 'trigger',
|
|
4306
|
+
triggerInfo: {
|
|
4307
|
+
triggerId: message.triggerMeta.triggerId,
|
|
4308
|
+
triggerName: message.triggerMeta.triggerName,
|
|
4309
|
+
fireTime: message.triggerMeta.fireTime,
|
|
4310
|
+
labels: message.triggerMeta.labels,
|
|
4311
|
+
},
|
|
4312
|
+
} : {}),
|
|
4140
4313
|
peerId: message.peerId, peerName: peerName || undefined,
|
|
4141
4314
|
peerType: message.peerType,
|
|
4142
4315
|
peerRole: message.batchRole || session.identity?.role || 'none',
|
|
@@ -4147,6 +4320,34 @@ export class ResponseEngine {
|
|
|
4147
4320
|
mentions: message.mentions,
|
|
4148
4321
|
mentionAids: message.mentionAids,
|
|
4149
4322
|
}];
|
|
4323
|
+
// T3.2: 读取 Thread Context inbox —— 跨话题发送留在本端的注入提醒。
|
|
4324
|
+
// 注入一次后立即删除整个文件(T3.3),不依赖任何消费标记。
|
|
4325
|
+
threadContextItems = [];
|
|
4326
|
+
if (selfAid && message.source !== 'owner-inject') {
|
|
4327
|
+
try {
|
|
4328
|
+
const { ThreadService } = await import('../thread/index.js');
|
|
4329
|
+
const inbox = ThreadService.readInbox(selfAid, session.id);
|
|
4330
|
+
threadContextItems = inbox.map(entry => ({
|
|
4331
|
+
kind: 'thread-context',
|
|
4332
|
+
peerId: entry.originPeer,
|
|
4333
|
+
peerType: 'agent',
|
|
4334
|
+
peerRole: 'none',
|
|
4335
|
+
content: entry.content,
|
|
4336
|
+
timestamp: entry.timestamp,
|
|
4337
|
+
threadContext: {
|
|
4338
|
+
messageId: entry.messageId,
|
|
4339
|
+
originThreadId: entry.originThreadId,
|
|
4340
|
+
originPeer: entry.originPeer,
|
|
4341
|
+
originSessionId: entry.originSessionId,
|
|
4342
|
+
...(entry.triggerId ? { triggerId: entry.triggerId } : {}),
|
|
4343
|
+
gapDetection: entry.gapDetection,
|
|
4344
|
+
},
|
|
4345
|
+
}));
|
|
4346
|
+
}
|
|
4347
|
+
catch (e) {
|
|
4348
|
+
logger.warn(`[ResponseEngine] ThreadContext inbox 读取失败: ${e?.message ?? e}`);
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4150
4351
|
const peerItems = (() => {
|
|
4151
4352
|
if (message.handoffDelivery && this.handoffRuntime) {
|
|
4152
4353
|
const items = this.handoffRuntime.buildPromptItems(message, peerRole);
|
|
@@ -4156,10 +4357,10 @@ export class ResponseEngine {
|
|
|
4156
4357
|
: (item.handoff?.handoffId ? [item.handoff.handoffId] : [])))));
|
|
4157
4358
|
v2HandoffResultRefs = items.flatMap(item => item.handoff?.resultRefs ?? []);
|
|
4158
4359
|
v2HandoffDirection = message.handoffDelivery.direction;
|
|
4159
|
-
return items;
|
|
4360
|
+
return [...threadContextItems, ...items];
|
|
4160
4361
|
}
|
|
4161
4362
|
}
|
|
4162
|
-
return rawPeerItems;
|
|
4363
|
+
return [...threadContextItems, ...rawPeerItems];
|
|
4163
4364
|
})();
|
|
4164
4365
|
// 将匹配的 pending hint 暂时 claim,排在真实消息之前;渲染或启动
|
|
4165
4366
|
// 失败时释放 claim,只有 runner 接受 prompt 后才持久化 consumed。
|
|
@@ -4263,6 +4464,16 @@ export class ResponseEngine {
|
|
|
4263
4464
|
}
|
|
4264
4465
|
}
|
|
4265
4466
|
}
|
|
4467
|
+
// T3.3: 渲染成功、prompt 非空 —— 注入完成,删除 inbox(注入一次语义)。
|
|
4468
|
+
if (threadContextItems.length > 0 && selfAid) {
|
|
4469
|
+
try {
|
|
4470
|
+
const { ThreadService } = await import('../thread/index.js');
|
|
4471
|
+
ThreadService.clearInbox(selfAid, session.id);
|
|
4472
|
+
}
|
|
4473
|
+
catch (e) {
|
|
4474
|
+
logger.warn(`[ResponseEngine] ThreadContext inbox 清理失败: ${e?.message ?? e}`);
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4266
4477
|
// Pass the stable session directory explicitly so sandboxed helper
|
|
4267
4478
|
// commands never fall back to a process, agent, or project directory
|
|
4268
4479
|
// for transient state.
|
|
@@ -4273,6 +4484,10 @@ export class ResponseEngine {
|
|
|
4273
4484
|
messageId: message.messageId,
|
|
4274
4485
|
baseagent: normalizedBaseagent.canonical,
|
|
4275
4486
|
channel: configChannelType,
|
|
4487
|
+
// 完整 channelKey(= adapter.channelName / inst.name,如 `aun#<selfAid>#main`)。
|
|
4488
|
+
// 必须与下面 issue() 的 `channel:` 用同一表达式,否则 debug 路径拿到的
|
|
4489
|
+
// origin.channelKey 会与真实任务路径不一致(见 D5)。
|
|
4490
|
+
channelKey: authorizationIdentity?.originChannelKey ?? message.channel,
|
|
4276
4491
|
channelId: message.channelId,
|
|
4277
4492
|
chatType: configChatType,
|
|
4278
4493
|
selfAid,
|
|
@@ -4304,6 +4519,7 @@ export class ResponseEngine {
|
|
|
4304
4519
|
causation: taskCausation,
|
|
4305
4520
|
};
|
|
4306
4521
|
this.activeTaskRuntimeContexts.set(session.id, taskRuntimeContext);
|
|
4522
|
+
this.debugWriteSessionSnapshot(session.id, taskRuntimeContext);
|
|
4307
4523
|
runtimeEnv = buildTaskRuntimeEnv(taskRuntimeContext);
|
|
4308
4524
|
if (this.agentDelegationRegistry && configActorId && selfAid && peerKey) {
|
|
4309
4525
|
const delegationToken = this.agentDelegationRegistry.issue({
|
|
@@ -4409,6 +4625,10 @@ export class ResponseEngine {
|
|
|
4409
4625
|
mimeType: image.mimeType,
|
|
4410
4626
|
}));
|
|
4411
4627
|
debugRunnerModel = modelOverride?.model || agentModel;
|
|
4628
|
+
if (debugRunnerModel)
|
|
4629
|
+
this.activeRunnerModels.set(session.id, debugRunnerModel);
|
|
4630
|
+
else
|
|
4631
|
+
this.activeRunnerModels.delete(session.id);
|
|
4412
4632
|
const stream = await agent.runQuery(session.id, effectivePrompt, absoluteProjectPath, runAgentSessionId, runnerImages, effectiveSystemPrompt, this.sessionManager, modelOverride, runtimeEnv);
|
|
4413
4633
|
// The turn may be superseded while the runner is still creating its
|
|
4414
4634
|
// backend query. Replay the interrupt after runQuery resolves, when
|
|
@@ -4704,6 +4924,7 @@ export class ResponseEngine {
|
|
|
4704
4924
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
4705
4925
|
if (this.activeTaskRuntimeContexts.get(session.id)?.taskId === taskId) {
|
|
4706
4926
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
4927
|
+
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4707
4928
|
}
|
|
4708
4929
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
4709
4930
|
logger.info(`[ResponseEngine] Stale turn stopped before publish: session=${session.id} task=${taskId}`);
|
|
@@ -4719,6 +4940,7 @@ export class ResponseEngine {
|
|
|
4719
4940
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
4720
4941
|
if (this.activeTaskRuntimeContexts.get(session.id)?.taskId === taskId) {
|
|
4721
4942
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
4943
|
+
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4722
4944
|
}
|
|
4723
4945
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
4724
4946
|
logger.info(`[ResponseEngine] Stale turn stopped before protocol rejection: session=${session.id} task=${taskId}`);
|
|
@@ -4870,9 +5092,10 @@ export class ResponseEngine {
|
|
|
4870
5092
|
});
|
|
4871
5093
|
}
|
|
4872
5094
|
// 最终回复文本:suppressed 模式或无 text 事件时需要兜底添加
|
|
5095
|
+
// 错误诊断串已通过 system.error 单独发出时置 true,下游跳过 result.error 避免重复
|
|
5096
|
+
let sentErrorAsSystemError = false;
|
|
4873
5097
|
const finalReplyText = streamResult.lastReplyText || streamResult.fullText;
|
|
4874
5098
|
const visibleFinalReplyText = stripContextLimitErrorLines(finalReplyText);
|
|
4875
|
-
const isDiagnosticFallback = streamResult.isError && !streamResult.hasReceivedText;
|
|
4876
5099
|
const isTriggerNoop = message.source === 'trigger'
|
|
4877
5100
|
&& typeof message.triggerMeta?.noopSentinel === 'string'
|
|
4878
5101
|
&& message.triggerMeta.noopSentinel.length > 0
|
|
@@ -4882,16 +5105,38 @@ export class ResponseEngine {
|
|
|
4882
5105
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
4883
5106
|
if (this.activeTaskRuntimeContexts.get(session.id)?.taskId === taskId) {
|
|
4884
5107
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5108
|
+
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4885
5109
|
}
|
|
4886
5110
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
4887
5111
|
logger.info(`[ResponseEngine] Stale turn stopped before final flush: session=${session.id} task=${taskId}`);
|
|
4888
5112
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_turn', taskCausation);
|
|
4889
5113
|
return;
|
|
4890
5114
|
}
|
|
4891
|
-
if (visibleFinalReplyText
|
|
5115
|
+
if ((visibleFinalReplyText || streamResult.syntheticDiagnosticText) && !isTriggerNoop) {
|
|
4892
5116
|
// `hasReceivedText` 是任务级计数。工具调用前的文字已经显示,并不表示最后一轮
|
|
4893
5117
|
// (通常来自 complete.result)的文字也已显示;用本轮投影标记避免终态丢失。
|
|
4894
|
-
|
|
5118
|
+
//
|
|
5119
|
+
// 终态路由按"本轮是否以错误收场"(isError)判定,不再按"有没有收到过文本"判定。
|
|
5120
|
+
// 原因:上游会把错误原文当正文送进来——Claude Code 用 `<synthetic>` assistant 表达
|
|
5121
|
+
// 传输/API 故障(runner 已标 synthetic,见上方 text 分支),网关也会把上下文超限错误
|
|
5122
|
+
// 伪装成 text event。这两种情况下 hasReceivedText / lastReplyText 都非空,旧判据
|
|
5123
|
+
// (isError && !lastReplyText)拦不住,错误串会被当成模型的最终回复发出去
|
|
5124
|
+
// (2026-10-11 403「无法连接到积分服务器」案例)。
|
|
5125
|
+
// 以错误收场的轮次一律走 system.error:前端收到的单条消息即可判断来源,
|
|
5126
|
+
// 不需要等后续的 result.error。
|
|
5127
|
+
const errorText = visibleFinalReplyText || streamResult.syntheticDiagnosticText || getStreamErrorMessage(streamResult, false);
|
|
5128
|
+
if (streamResult.isError) {
|
|
5129
|
+
// 用原始诊断文本(含错误码)判断可重试性,不用已过 stripContextLimitErrorLines 的展示文本
|
|
5130
|
+
const recoverable = isRetryableError(new Error(getStreamErrorText(streamResult)));
|
|
5131
|
+
await adapter.send(envelope, {
|
|
5132
|
+
kind: 'system.error',
|
|
5133
|
+
text: errorText,
|
|
5134
|
+
subtype: streamResult.subtype ?? 'api_error',
|
|
5135
|
+
recoverable,
|
|
5136
|
+
}).catch(() => { });
|
|
5137
|
+
sentErrorAsSystemError = true;
|
|
5138
|
+
}
|
|
5139
|
+
else if (visibleFinalReplyText && (shouldSuppress() || !streamResult.hasProjectedCurrentReplyText)) {
|
|
4895
5140
|
await renderer.addText(visibleFinalReplyText, undefined, undefined, streamResult.assistantUuid);
|
|
4896
5141
|
}
|
|
4897
5142
|
}
|
|
@@ -4901,6 +5146,7 @@ export class ResponseEngine {
|
|
|
4901
5146
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
4902
5147
|
if (this.activeTaskRuntimeContexts.get(session.id)?.taskId === taskId) {
|
|
4903
5148
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5149
|
+
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4904
5150
|
}
|
|
4905
5151
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
4906
5152
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared task=${taskId}`);
|
|
@@ -4991,12 +5237,17 @@ export class ResponseEngine {
|
|
|
4991
5237
|
// DaemonChannel settles a trigger when it receives a terminal
|
|
4992
5238
|
// payload. Send the metadata-bearing status first so failed runs do
|
|
4993
5239
|
// not lose their tool counts and recovery diagnostics.
|
|
5240
|
+
// system.error 已在 finalReplyText 分支把错误文本发给了前端,不再重复发 result.error
|
|
4994
5241
|
if (this.isTrustedDaemonTrigger(message)) {
|
|
4995
5242
|
await adapter.send(envelope, { kind: 'status.error', metadata: terminalMetadata }).catch(() => { });
|
|
4996
|
-
|
|
5243
|
+
if (!sentErrorAsSystemError) {
|
|
5244
|
+
await adapter.send(envelope, { kind: 'result.error', text: userErrorSummary, reason: rawSubtype }).catch(() => { });
|
|
5245
|
+
}
|
|
4997
5246
|
}
|
|
4998
5247
|
else {
|
|
4999
|
-
|
|
5248
|
+
if (!sentErrorAsSystemError) {
|
|
5249
|
+
await adapter.send(envelope, { kind: 'result.error', text: userErrorSummary, reason: rawSubtype }).catch(() => { });
|
|
5250
|
+
}
|
|
5000
5251
|
await adapter.send(envelope, { kind: 'status.error', metadata: terminalMetadata }).catch(() => { });
|
|
5001
5252
|
}
|
|
5002
5253
|
this.touchAgentActivity(channelKey);
|
|
@@ -5249,6 +5500,7 @@ export class ResponseEngine {
|
|
|
5249
5500
|
}
|
|
5250
5501
|
if (!interruptReason) {
|
|
5251
5502
|
this.publishTriggerExecutionCompleted(message, messageId, durationMs, taskCausation);
|
|
5503
|
+
this.publishBgSessionCompleted(message, messageId, durationMs, taskCausation);
|
|
5252
5504
|
}
|
|
5253
5505
|
if (!this.turnCoordinator.canPublish(turnLease))
|
|
5254
5506
|
return;
|
|
@@ -5338,6 +5590,7 @@ export class ResponseEngine {
|
|
|
5338
5590
|
this.sessionManager.clearProcessingIfTask(session.id, taskId);
|
|
5339
5591
|
if (this.activeTaskRuntimeContexts.get(session.id)?.taskId === taskId) {
|
|
5340
5592
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5593
|
+
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5341
5594
|
}
|
|
5342
5595
|
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5343
5596
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared (on error) task=${taskId}`);
|
|
@@ -5889,6 +6142,9 @@ export class ResponseEngine {
|
|
|
5889
6142
|
});
|
|
5890
6143
|
// 追踪最后一轮 assistant 回复文本(tool_use 之后的纯文本)
|
|
5891
6144
|
let lastReplyText = '';
|
|
6145
|
+
// 上游合成错误帧的原文(runner 标记 synthetic 的 text)。与正文通道分开存放:
|
|
6146
|
+
// 它只能作为终态错误文案的来源,不得进入 lastReplyText / 投影 / hasReceivedText。
|
|
6147
|
+
let syntheticDiagnosticText = '';
|
|
5892
6148
|
// callId → description 映射,用于 tool_result 回显描述
|
|
5893
6149
|
const toolDescByCallId = new Map();
|
|
5894
6150
|
const toolInputByCallId = new Map();
|
|
@@ -6113,6 +6369,7 @@ export class ResponseEngine {
|
|
|
6113
6369
|
toolUseId: event.toolUseId,
|
|
6114
6370
|
description: event.description,
|
|
6115
6371
|
subagentType: event.subagentType,
|
|
6372
|
+
taskType: event.taskType,
|
|
6116
6373
|
skipTranscript: event.skipTranscript,
|
|
6117
6374
|
});
|
|
6118
6375
|
this.eventBus.publish({
|
|
@@ -6131,15 +6388,21 @@ export class ResponseEngine {
|
|
|
6131
6388
|
causation,
|
|
6132
6389
|
});
|
|
6133
6390
|
if (!event.skipTranscript) {
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6391
|
+
// 子任务就是来源工具调用的执行过程:来源卡片在本轮可见时归拢进它的 tool_result,
|
|
6392
|
+
// 不另出卡片;否则(如 Codex 子智能体无来源工具)退回合成卡片。见 subtask-tool-projection.ts。
|
|
6393
|
+
const folded = renderer.beginParentSubtask(event.toolUseId);
|
|
6394
|
+
tasks.get(event.taskId).projection = folded ? 'parent' : 'synthetic';
|
|
6395
|
+
if (!folded) {
|
|
6396
|
+
renderer.addSubtaskActivity(projectSubtaskStarted({
|
|
6397
|
+
taskId: event.taskId,
|
|
6398
|
+
taskKind: event.taskKind,
|
|
6399
|
+
description: event.description,
|
|
6400
|
+
taskType: event.taskType,
|
|
6401
|
+
subagentType: event.subagentType,
|
|
6402
|
+
toolUseId: event.toolUseId,
|
|
6403
|
+
workflowName: event.workflowName,
|
|
6404
|
+
}));
|
|
6405
|
+
}
|
|
6143
6406
|
}
|
|
6144
6407
|
continue;
|
|
6145
6408
|
}
|
|
@@ -6155,6 +6418,7 @@ export class ResponseEngine {
|
|
|
6155
6418
|
}
|
|
6156
6419
|
if (event.type === 'task_notification') {
|
|
6157
6420
|
const tasks = this.activeRunnerTasks.get(session.id);
|
|
6421
|
+
const started = tasks?.get(event.taskId);
|
|
6158
6422
|
tasks?.delete(event.taskId);
|
|
6159
6423
|
if (tasks?.size === 0)
|
|
6160
6424
|
this.activeRunnerTasks.delete(session.id);
|
|
@@ -6175,19 +6439,26 @@ export class ResponseEngine {
|
|
|
6175
6439
|
causation,
|
|
6176
6440
|
});
|
|
6177
6441
|
if (!event.skipTranscript) {
|
|
6178
|
-
const
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
subagentType
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6442
|
+
const terminal = { status: event.status, summary: event.summary, durationMs: event.usage?.durationMs };
|
|
6443
|
+
const toolUseId = event.toolUseId ?? started?.toolUseId;
|
|
6444
|
+
// 归拢的子任务:终态合并进来源工具的 tool_result(前台等真实结果、后台放出扣住的占位结果)。
|
|
6445
|
+
const merged = started?.projection === 'parent' && renderer.endParentSubtask(toolUseId, terminal);
|
|
6446
|
+
if (!merged) {
|
|
6447
|
+
// 没归拢上:开始时就是合成卡片,则终态配同一张合成卡片;
|
|
6448
|
+
// 否则来源卡片已在本轮之外,补发一条同 call_id 的终态结果更新那张卡片。
|
|
6449
|
+
// taskType/subagentType 只在 task_started 事件上,从已登记的任务元数据补齐。
|
|
6450
|
+
const toParent = started?.projection !== 'synthetic' && !!toolUseId;
|
|
6451
|
+
renderer.addSubtaskActivity(projectSubtaskTerminal({
|
|
6452
|
+
taskId: event.taskId,
|
|
6453
|
+
taskKind: event.taskKind,
|
|
6454
|
+
description: event.description ?? started?.description,
|
|
6455
|
+
taskType: started?.taskType,
|
|
6456
|
+
subagentType: event.subagentType ?? started?.subagentType,
|
|
6457
|
+
toolUseId,
|
|
6458
|
+
...terminal,
|
|
6459
|
+
...(toParent ? { callId: toolUseId, toolName: renderer.toolNameForCall(toolUseId) } : {}),
|
|
6460
|
+
}));
|
|
6461
|
+
}
|
|
6191
6462
|
}
|
|
6192
6463
|
continue;
|
|
6193
6464
|
}
|
|
@@ -6224,21 +6495,30 @@ export class ResponseEngine {
|
|
|
6224
6495
|
if (!isCurrentlyBackground) {
|
|
6225
6496
|
// 流式文本
|
|
6226
6497
|
if (event.type === 'text') {
|
|
6227
|
-
//
|
|
6228
|
-
//
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
madeProgress = true;
|
|
6234
|
-
}
|
|
6235
|
-
lastReplyText += event.text;
|
|
6236
|
-
if (visibleText) {
|
|
6237
|
-
this.eventBus.publish({ type: 'message:text', sessionId: session.id, text: visibleText, isFinal: false });
|
|
6498
|
+
// 上游合成错误帧(Claude Code 的 `<synthetic>` assistant)会把 API/传输故障的
|
|
6499
|
+
// 错误原文当成正文送进来。这段字是诊断回显,不是模型正文——出身由 runner 标注,
|
|
6500
|
+
// 不靠字符串或计数猜。不进入正文通道(lastReplyText / hasReceivedText / 投影),
|
|
6501
|
+
// 只单独留存,供终态错误分支取用。
|
|
6502
|
+
if (event.synthetic) {
|
|
6503
|
+
syntheticDiagnosticText = event.text;
|
|
6238
6504
|
}
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6505
|
+
else {
|
|
6506
|
+
// 网关偶尔会把上下文超限错误伪装成普通 text event。不要向事件总线或
|
|
6507
|
+
// 渲染器投影错误行;lastReplyText 仍保留原文本,以便流结束后触发恢复。
|
|
6508
|
+
const visibleText = stripContextLimitErrorLines(event.text);
|
|
6509
|
+
if (visibleText) {
|
|
6510
|
+
hasReceivedText = true;
|
|
6511
|
+
if (visibleText.trim())
|
|
6512
|
+
madeProgress = true;
|
|
6513
|
+
}
|
|
6514
|
+
lastReplyText += event.text;
|
|
6515
|
+
if (visibleText) {
|
|
6516
|
+
this.eventBus.publish({ type: 'message:text', sessionId: session.id, text: visibleText, isFinal: false });
|
|
6517
|
+
}
|
|
6518
|
+
if (visibleText && !shouldSuppress()) {
|
|
6519
|
+
await renderer.addText(visibleText, event.outputTokens, event.turn, event.assistantUuid, event.phase);
|
|
6520
|
+
hasProjectedCurrentReplyText = true;
|
|
6521
|
+
}
|
|
6242
6522
|
}
|
|
6243
6523
|
}
|
|
6244
6524
|
// compact 完成
|
|
@@ -6330,6 +6610,7 @@ export class ResponseEngine {
|
|
|
6330
6610
|
state: modeHooks.state,
|
|
6331
6611
|
sessionState: modeHooks.sessionState,
|
|
6332
6612
|
turn: turnLease,
|
|
6613
|
+
modelTurn: typeof event.turn === 'number' ? event.turn : undefined,
|
|
6333
6614
|
toolName: event.name,
|
|
6334
6615
|
toolInput: event.input || {},
|
|
6335
6616
|
injectToModel: (text) => { agent.injectUserMessage?.(session.id, text); },
|
|
@@ -6344,7 +6625,7 @@ export class ResponseEngine {
|
|
|
6344
6625
|
});
|
|
6345
6626
|
},
|
|
6346
6627
|
getQueueLength: () => this.messageQueue?.getQueueLength(session.id) ?? 0,
|
|
6347
|
-
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, proactiveState?.chatType === 'group' ? 'group' : 'private', proactiveSelfAid || session.selfAID),
|
|
6628
|
+
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, proactiveState?.chatType === 'group' ? 'group' : 'private', proactiveSelfAid || session.selfAID) || isThreadRoutedSendForSelf(toolName, toolInput, proactiveSelfAid || session.selfAID),
|
|
6348
6629
|
logger,
|
|
6349
6630
|
});
|
|
6350
6631
|
}
|
|
@@ -6464,7 +6745,7 @@ export class ResponseEngine {
|
|
|
6464
6745
|
});
|
|
6465
6746
|
},
|
|
6466
6747
|
getQueueLength: () => this.messageQueue?.getQueueLength(session.id) ?? 0,
|
|
6467
|
-
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, proactiveState?.chatType === 'group' ? 'group' : 'private', proactiveSelfAid || session.selfAID),
|
|
6748
|
+
isSendCommand: (toolName, toolInput) => isExactEvolcoreSendCommandForSession(toolName, toolInput, session.channelId, proactiveState?.chatType === 'group' ? 'group' : 'private', proactiveSelfAid || session.selfAID) || isThreadRoutedSendForSelf(toolName, toolInput, proactiveSelfAid || session.selfAID),
|
|
6468
6749
|
logger,
|
|
6469
6750
|
});
|
|
6470
6751
|
}
|
|
@@ -6502,6 +6783,7 @@ export class ResponseEngine {
|
|
|
6502
6783
|
errors: [event.error],
|
|
6503
6784
|
lastReplyText,
|
|
6504
6785
|
fullText: '',
|
|
6786
|
+
syntheticDiagnosticText,
|
|
6505
6787
|
};
|
|
6506
6788
|
}
|
|
6507
6789
|
// 完成事件
|
|
@@ -6516,7 +6798,7 @@ export class ResponseEngine {
|
|
|
6516
6798
|
logger.info(`[ResponseEngine] Auto-filled session name: ${event.sessionTitle}`);
|
|
6517
6799
|
}
|
|
6518
6800
|
// 记录完成状态 + 最后一轮回复文本(后续 complete 覆盖前序)
|
|
6519
|
-
completeResult = { isError: !!event.isError, subtype: event.subtype, errors: event.errors, terminalReason: event.terminalReason, lastReplyText, fullText: event.result || '', hasReceivedText, numTurns: event.numTurns, ttftMs: event.ttftMs, model: event.model, tokenUsage: event.tokenUsage, contextUsage: event.contextUsage, lastModelCall: event.lastModelCall, modelCalls: event.modelCalls };
|
|
6801
|
+
completeResult = { isError: !!event.isError, subtype: event.subtype, errors: event.errors, terminalReason: event.terminalReason, lastReplyText, fullText: event.result || '', hasReceivedText, numTurns: event.numTurns, ttftMs: event.ttftMs, model: event.model, tokenUsage: event.tokenUsage, contextUsage: event.contextUsage, lastModelCall: event.lastModelCall, modelCalls: event.modelCalls, syntheticDiagnosticText };
|
|
6520
6802
|
if (!event.isError)
|
|
6521
6803
|
madeProgress = true;
|
|
6522
6804
|
// thought jsonl 写入已下沉到 aun.ts:sendThought 成功后,
|
|
@@ -6567,7 +6849,7 @@ export class ResponseEngine {
|
|
|
6567
6849
|
logger.info(`[ResponseEngine] Auto-filled session name: ${event.sessionTitle}`);
|
|
6568
6850
|
}
|
|
6569
6851
|
// 记录完成状态
|
|
6570
|
-
completeResult = { isError: !!event.isError, subtype: event.subtype, errors: event.errors, terminalReason: event.terminalReason, lastReplyText, fullText: event.result || '', hasReceivedText, numTurns: event.numTurns, ttftMs: event.ttftMs, model: event.model, tokenUsage: event.tokenUsage, contextUsage: event.contextUsage, lastModelCall: event.lastModelCall };
|
|
6852
|
+
completeResult = { isError: !!event.isError, subtype: event.subtype, errors: event.errors, terminalReason: event.terminalReason, lastReplyText, fullText: event.result || '', hasReceivedText, syntheticDiagnosticText, numTurns: event.numTurns, ttftMs: event.ttftMs, model: event.model, tokenUsage: event.tokenUsage, contextUsage: event.contextUsage, lastModelCall: event.lastModelCall };
|
|
6571
6853
|
if (event.subtype === 'success')
|
|
6572
6854
|
madeProgress = true;
|
|
6573
6855
|
}
|