evolcore 0.0.33 → 0.0.34
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 +37 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +12 -1
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +82 -13
- package/dist/agents/codex/diagnostics.js +37 -11
- package/dist/agents/codex-runner.js +218 -44
- package/dist/agents/ecagent-runner.js +73 -20
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/identity.js +1 -1
- package/dist/channels/aun.js +16 -6
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +3 -2
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +120 -19
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/index.js +20 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/memory-command.js +158 -145
- package/dist/cli/thread-command.js +5 -3
- package/dist/cli/trigger-command.js +6 -1
- package/dist/config/config-field-policy.js +4 -0
- package/dist/config/config-manager.js +19 -0
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +30 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +108 -35
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +1 -1
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +138 -4
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/menu-catalog.js +3 -0
- package/dist/core/command/menu-handler.js +22 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +36 -1
- package/dist/core/memory/batch-meta.js +132 -0
- package/dist/core/memory/consolidation-context.js +35 -14
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation.js +91 -26
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +30 -15
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +2 -1
- package/dist/core/message/response-engine.js +584 -47
- package/dist/core/permission/ec-command-parser.js +93 -4
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +30 -62
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-manager.js +75 -5
- package/dist/core/skill/index-generator.js +17 -3
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +222 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/thread-service.js +13 -20
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/index.js +11 -2
- package/dist/ipc.js +51 -40
- package/dist/paths.js +70 -0
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/feedback.js +6 -1
- package/dist/trigger/parser.js +8 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/utils/cross-platform.js +11 -2
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +121 -0
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +0 -1
- package/kits/docs/evolcore/agent-create.md +5 -4
- package/kits/docs/evolcore/agent.md +2 -2
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +1 -1
- package/kits/docs/evolcore/fs-architecture.md +4 -5
- package/kits/docs/evolcore/fs.md +1 -1
- package/kits/docs/evolcore/group-collaboration.md +2 -2
- package/kits/docs/evolcore/group.md +10 -10
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +18 -14
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +7 -7
- package/kits/docs/evolcore/thread-chain.md +5 -5
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.json +0 -20
- package/kits/eck_manifest.memory.json +0 -12
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +12 -15
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/skills/ec-group/SKILL.md +4 -4
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/templates/memory-consolidation/forget.md +2 -2
- package/kits/templates/memory-consolidation/generalize.md +4 -4
- package/kits/templates/memory-consolidation/notes.md +11 -11
- package/kits/templates/memory-consolidation/profile.md +3 -3
- package/kits/templates/memory-consolidation/promote.md +3 -3
- package/kits/templates/memory-consolidation/workflow.md +6 -6
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bootstrap.md +4 -3
- package/kits/templates/system-fragments/channel.md +2 -2
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +6 -6
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +4 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
|
@@ -3,13 +3,15 @@ import path from 'path';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
|
+
import { MAIN_CLI_BIN } from '../../product.js';
|
|
6
7
|
import { BaseagentRunnerUnavailableError, hasCompact, autoCompactWindowForModel, isClaudeContextUsageModel, numericToken, formatAsyncQuestionAnswer, parseAsyncUserQuestions, } from '../../agents/runner-types.js';
|
|
7
8
|
import { loadedBaseagentIds } from '../../agents/baseagent-availability.js';
|
|
8
9
|
import { isDiagnosticSessionActive } from '../command/diag-service.js';
|
|
9
10
|
import { evaluateInboundAdmissionPreflight, evaluateOrdinaryInboundAdmission } from './message-admission.js';
|
|
10
11
|
import { authorizeAccess, authorizeOperation, buildAuthSubject } from '../auth/auth-gateway.js';
|
|
11
12
|
import { buildTaskRuntimeEnv, ensureRuntimeLockDir, ensureSessionRuntimeDir } from '../../cli/task-context.js';
|
|
12
|
-
import {
|
|
13
|
+
import { BUILTIN_SESSION_PATH_GRANTS, mergeRuntimeGrantsForSession, resolveGrantedReadRoots, } from '../permission/session-path-grants.js';
|
|
14
|
+
import { buildSystemSessionContext, getSystemSessionModule, isSystemSessionType, readSystemSessionMarker } from '../system-session/registry.js';
|
|
13
15
|
import { IMRenderer } from './im-renderer.js';
|
|
14
16
|
import { projectSubtaskStarted, projectSubtaskTerminal } from './subtask-tool-projection.js';
|
|
15
17
|
import { TaskActivityStore } from '../task/task-activity-store.js';
|
|
@@ -18,9 +20,9 @@ import { createDecisionInferenceProvider, createTextInferenceProvider } from '..
|
|
|
18
20
|
import { StreamIdleMonitor } from './stream-idle-monitor.js';
|
|
19
21
|
import { logger } from '../../utils/logger.js';
|
|
20
22
|
import { isHostChinese } from '../../utils/locale.js';
|
|
21
|
-
import { ExecutionTimeoutError, OrphanedTargetSessionError, isExecutionFailure, getErrorMessage, classifyError, ErrorType, ERROR_PREFIX, isInfraError, isAbortTerminalReason, prefixErrorType, isRetryableError, isContextTooLongText } from '../../utils/error-utils.js';
|
|
23
|
+
import { ExecutionTimeoutError, OrphanedTargetSessionError, isExecutionFailure, getErrorMessage, getErrorRawText, getToolResultErrorText, classifyError, normalizeCoreErrorReason, normalizeCoreErrorSubtype, ErrorType, ERROR_PREFIX, isInfraError, isAbortTerminalReason, prefixErrorType, isRetryableError, isContextTooLongText } from '../../utils/error-utils.js';
|
|
22
24
|
import { readEvolcoreRuntimeFlavor, tagErrorTextWithEvolcoreVersion } from '../../utils/evolcore-version.js';
|
|
23
|
-
import { describeProactiveSendCommandFailure, isExactEvolcoreSendCommandForSession, isThreadRoutedEvolcoreSendCommand } from '../permission/ec-command-parser.js';
|
|
25
|
+
import { describeProactiveSendCommandFailure, isExactEvolcoreSendCommandForSession, isThreadRoutedEvolcoreSendCommand, parseCodexToolCommand, resolveCodexShellCarrierArgv, resolveCodexShellCarrierString, } from '../permission/ec-command-parser.js';
|
|
24
26
|
import { resolveThreadRouteTarget } from '../thread/thread-route.js';
|
|
25
27
|
import { summarizeToolInputForDisplay } from '../../utils/tool-summary.js';
|
|
26
28
|
import { agentDataDir, agentDir as resolveAgentDir, agentMdPath, getPackageRoot, resolveRoot, resolvePaths, debugSessionsDir } from '../../paths.js';
|
|
@@ -414,6 +416,381 @@ export class PauseController {
|
|
|
414
416
|
function isShowActivitiesMode(value) {
|
|
415
417
|
return value === 'all' || value === 'text' || value === 'none';
|
|
416
418
|
}
|
|
419
|
+
const TOOL_INPUT_DIAGNOSTIC_MAX_LENGTH = 96_000;
|
|
420
|
+
const TOOL_INPUT_DIAGNOSTIC_MAX_FINDINGS = 3;
|
|
421
|
+
const TOOL_INPUT_DIAGNOSTIC_TIMEOUT_MS = 1_000;
|
|
422
|
+
function isToolResultFailure(event) {
|
|
423
|
+
return event.isError === true
|
|
424
|
+
|| event.executionState === 'failed'
|
|
425
|
+
|| event.executionState === 'blocked'
|
|
426
|
+
|| event.decision === 'deny'
|
|
427
|
+
|| event.decision === 'error'
|
|
428
|
+
|| (typeof event.exitCode === 'number' && Number.isFinite(event.exitCode) && event.exitCode !== 0);
|
|
429
|
+
}
|
|
430
|
+
function isStringArgv(value) {
|
|
431
|
+
return Array.isArray(value) && value.every(item => typeof item === 'string');
|
|
432
|
+
}
|
|
433
|
+
function boundedDiagnosticInput(value) {
|
|
434
|
+
return value.length <= TOOL_INPUT_DIAGNOSTIC_MAX_LENGTH;
|
|
435
|
+
}
|
|
436
|
+
function findApplyPatchArgumentsOffset(value) {
|
|
437
|
+
const match = value.trimStart().match(/^(?:"([^"\r\n]+)"|'([^'\r\n]+)'|([^\s;&|<>"']+))(?=\s|[;&|<>]|$)/u);
|
|
438
|
+
const executable = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
439
|
+
if (!executable)
|
|
440
|
+
return -1;
|
|
441
|
+
const basename = executable.replaceAll('\\', '/').split('/').at(-1)?.toLowerCase();
|
|
442
|
+
if (basename !== 'apply_patch' && basename !== 'apply_patch.exe')
|
|
443
|
+
return -1;
|
|
444
|
+
return value.length - value.trimStart().length + match[0].length;
|
|
445
|
+
}
|
|
446
|
+
function findUnquotedDiagnosticText(value, needle, startOffset) {
|
|
447
|
+
let quote = null;
|
|
448
|
+
let escaped = false;
|
|
449
|
+
for (let index = startOffset; index < value.length; index++) {
|
|
450
|
+
const char = value[index];
|
|
451
|
+
if (escaped) {
|
|
452
|
+
escaped = false;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (quote === 'single') {
|
|
456
|
+
if (char === "'")
|
|
457
|
+
quote = null;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (quote === 'double') {
|
|
461
|
+
if (char === '\\')
|
|
462
|
+
escaped = true;
|
|
463
|
+
else if (char === '"')
|
|
464
|
+
quote = null;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (char === '\\') {
|
|
468
|
+
escaped = true;
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
if (char === "'") {
|
|
472
|
+
quote = 'single';
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (char === '"') {
|
|
476
|
+
quote = 'double';
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (value.startsWith(needle, index))
|
|
480
|
+
return index;
|
|
481
|
+
}
|
|
482
|
+
return -1;
|
|
483
|
+
}
|
|
484
|
+
function diagnosticLineEnd(value, startOffset) {
|
|
485
|
+
const newline = value.indexOf('\n', startOffset);
|
|
486
|
+
return newline < 0 ? value.length : newline;
|
|
487
|
+
}
|
|
488
|
+
function parseDiagnosticHeredoc(value, argumentsOffset) {
|
|
489
|
+
const openOffset = findUnquotedDiagnosticText(value, '<<', argumentsOffset);
|
|
490
|
+
if (openOffset < 0 || openOffset >= diagnosticLineEnd(value, argumentsOffset)
|
|
491
|
+
|| value[openOffset + 2] === '<')
|
|
492
|
+
return undefined;
|
|
493
|
+
let cursor = openOffset + 2;
|
|
494
|
+
const stripTabs = value[cursor] === '-';
|
|
495
|
+
if (stripTabs)
|
|
496
|
+
cursor++;
|
|
497
|
+
while (value[cursor] === ' ' || value[cursor] === '\t')
|
|
498
|
+
cursor++;
|
|
499
|
+
const quote = value[cursor] === "'" || value[cursor] === '"' ? value[cursor] : undefined;
|
|
500
|
+
if (quote)
|
|
501
|
+
cursor++;
|
|
502
|
+
const delimiterStart = cursor;
|
|
503
|
+
while (cursor < value.length) {
|
|
504
|
+
const char = value[cursor];
|
|
505
|
+
if (quote ? char === quote : char === ' ' || char === '\t' || char === '\r' || char === '\n')
|
|
506
|
+
break;
|
|
507
|
+
cursor++;
|
|
508
|
+
}
|
|
509
|
+
const delimiter = value.slice(delimiterStart, cursor);
|
|
510
|
+
if (!delimiter)
|
|
511
|
+
return undefined;
|
|
512
|
+
const firstNewline = value.indexOf('\n', cursor);
|
|
513
|
+
if (firstNewline < 0)
|
|
514
|
+
return {};
|
|
515
|
+
let lineStart = firstNewline + 1;
|
|
516
|
+
let trailing;
|
|
517
|
+
while (lineStart <= value.length) {
|
|
518
|
+
const end = diagnosticLineEnd(value, lineStart);
|
|
519
|
+
const rawLine = value.slice(lineStart, end).replace(/\r$/u, '');
|
|
520
|
+
const line = stripTabs ? rawLine.replace(/^\t+/u, '') : rawLine;
|
|
521
|
+
if (line === delimiter) {
|
|
522
|
+
let extraOffset;
|
|
523
|
+
let next = end < value.length ? end + 1 : value.length;
|
|
524
|
+
while (next < value.length) {
|
|
525
|
+
const nextEnd = diagnosticLineEnd(value, next);
|
|
526
|
+
const nextLine = value.slice(next, nextEnd).trim();
|
|
527
|
+
if (nextLine !== '' && !nextLine.startsWith('#')) {
|
|
528
|
+
extraOffset = next;
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
next = nextEnd < value.length ? nextEnd + 1 : value.length;
|
|
532
|
+
}
|
|
533
|
+
return { delimiterOffset: lineStart + rawLine.length - line.length, extraOffset };
|
|
534
|
+
}
|
|
535
|
+
if (line.startsWith(delimiter) && line.length > delimiter.length) {
|
|
536
|
+
trailing ??= { trailingOffset: lineStart + rawLine.length - line.length + delimiter.length, trailingEndOffset: end };
|
|
537
|
+
}
|
|
538
|
+
if (end >= value.length)
|
|
539
|
+
break;
|
|
540
|
+
lineStart = end + 1;
|
|
541
|
+
}
|
|
542
|
+
return trailing ?? {};
|
|
543
|
+
}
|
|
544
|
+
function findDiagnosticShellSeparator(value, startOffset, endOffset) {
|
|
545
|
+
let quote = null;
|
|
546
|
+
let escaped = false;
|
|
547
|
+
for (let index = startOffset; index < endOffset; index++) {
|
|
548
|
+
const char = value[index];
|
|
549
|
+
if (escaped) {
|
|
550
|
+
escaped = false;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (quote === 'single') {
|
|
554
|
+
if (char === "'")
|
|
555
|
+
quote = null;
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
if (quote === 'double') {
|
|
559
|
+
if (char === '\\')
|
|
560
|
+
escaped = true;
|
|
561
|
+
else if (char === '"')
|
|
562
|
+
quote = null;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (char === '\\') {
|
|
566
|
+
escaped = true;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (char === "'") {
|
|
570
|
+
quote = 'single';
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
if (char === '"') {
|
|
574
|
+
quote = 'double';
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (char === ';' || char === '|' || char === '&' || char === '\n' || char === '\r')
|
|
578
|
+
return index;
|
|
579
|
+
}
|
|
580
|
+
return -1;
|
|
581
|
+
}
|
|
582
|
+
function diagnosticLocation(source, startOffset, endOffset = startOffset) {
|
|
583
|
+
const start = Math.max(0, Math.min(startOffset, source.value.length));
|
|
584
|
+
const end = Math.max(start, Math.min(endOffset, source.value.length));
|
|
585
|
+
const lineStart = Math.max(source.value.lastIndexOf('\n', Math.max(0, start - 1)) + 1, 0);
|
|
586
|
+
return {
|
|
587
|
+
field: source.field,
|
|
588
|
+
...(source.argumentIndex !== undefined ? { argumentIndex: source.argumentIndex } : {}),
|
|
589
|
+
line: source.value.slice(0, start).split('\n').length,
|
|
590
|
+
column: [...source.value.slice(lineStart, start)].length + 1,
|
|
591
|
+
startOffset: start,
|
|
592
|
+
endOffset: end,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
function diagnosticFinding(ruleId, title, advice, confidence, source, startOffset, endOffset) {
|
|
596
|
+
return { ruleId, title, advice, confidence, location: diagnosticLocation(source, startOffset, endOffset) };
|
|
597
|
+
}
|
|
598
|
+
function diagnosticCandidates(input) {
|
|
599
|
+
const candidates = [];
|
|
600
|
+
if (typeof input.command === 'string' && boundedDiagnosticInput(input.command)) {
|
|
601
|
+
const source = { field: 'command', value: input.command };
|
|
602
|
+
const carrier = resolveCodexShellCarrierString(input.command);
|
|
603
|
+
if (carrier) {
|
|
604
|
+
const commandOffset = input.command.indexOf(carrier.command);
|
|
605
|
+
candidates.push({
|
|
606
|
+
source,
|
|
607
|
+
command: carrier.command,
|
|
608
|
+
commandOffset: commandOffset >= 0 ? commandOffset : 0,
|
|
609
|
+
carrier: true,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
candidates.push({ source, command: input.command, commandOffset: 0, carrier: false });
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (isStringArgv(input.commandArgv) && input.commandArgv.every(boundedDiagnosticInput)) {
|
|
617
|
+
const carrier = resolveCodexShellCarrierArgv(input.commandArgv);
|
|
618
|
+
if (carrier) {
|
|
619
|
+
const argumentIndex = input.commandArgv.length - 1;
|
|
620
|
+
candidates.push({
|
|
621
|
+
source: { field: 'commandArgv', value: input.commandArgv[argumentIndex], argumentIndex },
|
|
622
|
+
command: carrier.command,
|
|
623
|
+
commandOffset: 0,
|
|
624
|
+
carrier: true,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
const argumentIndex = 0;
|
|
629
|
+
if (findApplyPatchArgumentsOffset(input.commandArgv[argumentIndex] ?? '') >= 0) {
|
|
630
|
+
candidates.push({
|
|
631
|
+
source: { field: 'commandArgv', value: input.commandArgv[argumentIndex], argumentIndex },
|
|
632
|
+
command: input.commandArgv[argumentIndex],
|
|
633
|
+
commandOffset: 0,
|
|
634
|
+
carrier: false,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return candidates;
|
|
640
|
+
}
|
|
641
|
+
function addHeredocDiagnosticFindings(findings, candidate) {
|
|
642
|
+
const argumentsOffset = findApplyPatchArgumentsOffset(candidate.command);
|
|
643
|
+
if (argumentsOffset < 0)
|
|
644
|
+
return;
|
|
645
|
+
const heredoc = parseDiagnosticHeredoc(candidate.command, argumentsOffset);
|
|
646
|
+
if (!heredoc)
|
|
647
|
+
return;
|
|
648
|
+
const map = (offset) => candidate.commandOffset + offset;
|
|
649
|
+
if (heredoc.trailingOffset !== undefined) {
|
|
650
|
+
findings.push(diagnosticFinding('heredoc-terminator-trailing-characters', 'heredoc 结束标记行包含尾随字符', '让结束标记单独占一行,不要在标记后追加引号、命令或其它字符。', '高', candidate.source, map(heredoc.trailingOffset), map(heredoc.trailingEndOffset ?? heredoc.trailingOffset)));
|
|
651
|
+
}
|
|
652
|
+
else if (heredoc.delimiterOffset === undefined) {
|
|
653
|
+
findings.push(diagnosticFinding('heredoc-terminator-missing', '未找到匹配的 heredoc 结束标记', '补上与起始标记完全一致、且单独占一行的结束标记。', '高', candidate.source, map(candidate.command.length), map(candidate.command.length)));
|
|
654
|
+
}
|
|
655
|
+
else if (heredoc.extraOffset !== undefined) {
|
|
656
|
+
findings.push(diagnosticFinding('heredoc-extra-command', 'heredoc 结束标记后仍有额外命令或文本', '将 apply_patch 与测试或其它命令拆成两次工具调用。', '高', candidate.source, map(heredoc.extraOffset), map(heredoc.extraOffset)));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function addApplyPatchCompositionFinding(findings, candidate) {
|
|
660
|
+
const argumentsOffset = findApplyPatchArgumentsOffset(candidate.command);
|
|
661
|
+
if (argumentsOffset < 0 || candidate.carrier)
|
|
662
|
+
return;
|
|
663
|
+
const executableMatch = candidate.command.trimStart().match(/^(?:"([^"]+)"|'([^']+)'|([^\s]+))/u);
|
|
664
|
+
const executable = executableMatch?.[1] ?? executableMatch?.[2] ?? executableMatch?.[3] ?? '';
|
|
665
|
+
const basename = executable.replaceAll('\\', '/').split('/').at(-1)?.toLowerCase();
|
|
666
|
+
if (basename !== 'apply_patch' && basename !== 'apply_patch.exe')
|
|
667
|
+
return;
|
|
668
|
+
const heredocOffset = findUnquotedDiagnosticText(candidate.command, '<<', argumentsOffset);
|
|
669
|
+
const endOffset = heredocOffset >= 0 ? heredocOffset : candidate.command.length;
|
|
670
|
+
const separator = findDiagnosticShellSeparator(candidate.command, argumentsOffset, endOffset);
|
|
671
|
+
if (separator < 0)
|
|
672
|
+
return;
|
|
673
|
+
findings.push(diagnosticFinding('apply-patch-composed-command', 'apply_patch 与其它 Shell 命令组合提交', '一次工具调用只提交完整的 apply_patch;测试、检查或其它命令另行调用。', '高', candidate.source, candidate.commandOffset + separator, candidate.commandOffset + separator + 1));
|
|
674
|
+
}
|
|
675
|
+
function addNestedShellDiagnosticFinding(findings, candidate) {
|
|
676
|
+
if (!candidate.carrier || findApplyPatchArgumentsOffset(candidate.command) < 0)
|
|
677
|
+
return;
|
|
678
|
+
findings.push(diagnosticFinding('nested-shell-escaping-risk', '命令包含嵌套 Shell 包装层', '尽量直接提交单层 Shell 命令;需要 heredoc 或 JSON 时,减少外层转义和包装。', '中', candidate.source, candidate.commandOffset, candidate.commandOffset + candidate.command.length));
|
|
679
|
+
}
|
|
680
|
+
function addJsonArrayDiagnosticFinding(findings, input) {
|
|
681
|
+
const rawArgv = isStringArgv(input.commandArgv) ? input.commandArgv : undefined;
|
|
682
|
+
const parsedArgv = rawArgv ?? (typeof input.command === 'string'
|
|
683
|
+
? (() => {
|
|
684
|
+
const parsed = parseCodexToolCommand({ command: input.command });
|
|
685
|
+
return parsed.ok ? parsed.argv : undefined;
|
|
686
|
+
})()
|
|
687
|
+
: undefined);
|
|
688
|
+
if (!parsedArgv || parsedArgv.length < 6)
|
|
689
|
+
return;
|
|
690
|
+
if (parsedArgv[0].toLowerCase() !== MAIN_CLI_BIN || parsedArgv[1].toLowerCase() !== 'aid'
|
|
691
|
+
|| parsedArgv[2].toLowerCase() !== 'agentmd' || parsedArgv[3].toLowerCase() !== 'set'
|
|
692
|
+
|| parsedArgv[5].toLowerCase() !== 'tags')
|
|
693
|
+
return;
|
|
694
|
+
const value = parsedArgv[6];
|
|
695
|
+
const source = rawArgv
|
|
696
|
+
? { field: 'commandArgv', value: rawArgv[6] ?? '', argumentIndex: 6 }
|
|
697
|
+
: { field: 'command', value: typeof input.command === 'string' ? input.command : '' };
|
|
698
|
+
if (value === undefined) {
|
|
699
|
+
findings.push(diagnosticFinding('json-array-argument-missing', 'tags 参数缺少完整 JSON 数组值', '将 tags 作为一个完整参数传入,例如 PowerShell 使用单引号包住 ["DSH","AUN"]。', '高', source, source.value.length));
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
let valid = false;
|
|
703
|
+
try {
|
|
704
|
+
const decoded = JSON.parse(value);
|
|
705
|
+
valid = Array.isArray(decoded) && decoded.every(item => typeof item === 'string');
|
|
706
|
+
}
|
|
707
|
+
catch {
|
|
708
|
+
valid = false;
|
|
709
|
+
}
|
|
710
|
+
if (valid)
|
|
711
|
+
return;
|
|
712
|
+
const split = rawArgv !== undefined && rawArgv.length > 7 && !rawArgv[7].startsWith('--')
|
|
713
|
+
&& (value.startsWith('[') || value.endsWith(','));
|
|
714
|
+
findings.push(diagnosticFinding(split ? 'json-array-argument-splitting' : 'json-array-argument-invalid', split ? 'tags 的 JSON 数组疑似被拆成多个参数' : 'tags 参数不是可解析的 JSON 字符串数组', '保证整个 JSON 数组位于同一个参数中;PowerShell 普通单引号字符串内不要再添加反斜杠转义。', split ? '中' : '高', source, split ? source.value.length : 0, source.value.length));
|
|
715
|
+
}
|
|
716
|
+
function addCommandArgvMismatchFinding(findings, input) {
|
|
717
|
+
if (typeof input.command !== 'string' || !isStringArgv(input.commandArgv))
|
|
718
|
+
return;
|
|
719
|
+
const commandArgv = input.commandArgv;
|
|
720
|
+
if (!boundedDiagnosticInput(input.command) || commandArgv.some(value => !boundedDiagnosticInput(value)))
|
|
721
|
+
return;
|
|
722
|
+
if (resolveCodexShellCarrierArgv(commandArgv) || resolveCodexShellCarrierString(input.command))
|
|
723
|
+
return;
|
|
724
|
+
const parsed = parseCodexToolCommand({ command: input.command });
|
|
725
|
+
if (!parsed.ok || parsed.argv.length === commandArgv.length
|
|
726
|
+
&& parsed.argv.every((value, index) => value === commandArgv[index]))
|
|
727
|
+
return;
|
|
728
|
+
findings.push(diagnosticFinding('command-argv-mismatch', 'command 与 commandArgv 的解析结果不一致', '以执行器提供的结构化 commandArgv 为准,避免在外层重新拼接或改写命令文本。', '中', { field: 'command', value: input.command }, 0, input.command.length));
|
|
729
|
+
}
|
|
730
|
+
function deduplicateDiagnosticFindings(findings) {
|
|
731
|
+
const seen = new Set();
|
|
732
|
+
return findings.filter(item => {
|
|
733
|
+
const key = `${item.ruleId}:${item.location.field}:${item.location.argumentIndex ?? ''}:${item.location.startOffset}`;
|
|
734
|
+
if (seen.has(key))
|
|
735
|
+
return false;
|
|
736
|
+
seen.add(key);
|
|
737
|
+
return true;
|
|
738
|
+
}).slice(0, TOOL_INPUT_DIAGNOSTIC_MAX_FINDINGS);
|
|
739
|
+
}
|
|
740
|
+
function diagnoseToolInput(original, event, deadline) {
|
|
741
|
+
if (!isToolResultFailure(event) || !original.input || typeof original.input !== 'object')
|
|
742
|
+
return undefined;
|
|
743
|
+
const input = original.input;
|
|
744
|
+
if (typeof input.command === 'string' && !boundedDiagnosticInput(input.command))
|
|
745
|
+
return undefined;
|
|
746
|
+
if (Array.isArray(input.commandArgv)) {
|
|
747
|
+
if (input.commandArgv.length > 1_024 || !isStringArgv(input.commandArgv)
|
|
748
|
+
|| input.commandArgv.reduce((length, value) => length + value.length, 0) > TOOL_INPUT_DIAGNOSTIC_MAX_LENGTH) {
|
|
749
|
+
return undefined;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
const candidates = diagnosticCandidates(input);
|
|
753
|
+
if (Date.now() > deadline)
|
|
754
|
+
return undefined;
|
|
755
|
+
const findings = [];
|
|
756
|
+
for (const candidate of candidates) {
|
|
757
|
+
if (Date.now() > deadline)
|
|
758
|
+
return undefined;
|
|
759
|
+
addHeredocDiagnosticFindings(findings, candidate);
|
|
760
|
+
addApplyPatchCompositionFinding(findings, candidate);
|
|
761
|
+
addNestedShellDiagnosticFinding(findings, candidate);
|
|
762
|
+
}
|
|
763
|
+
if (Date.now() <= deadline)
|
|
764
|
+
addJsonArrayDiagnosticFinding(findings, input);
|
|
765
|
+
if (Date.now() <= deadline)
|
|
766
|
+
addCommandArgvMismatchFinding(findings, input);
|
|
767
|
+
if (Date.now() > deadline)
|
|
768
|
+
return undefined;
|
|
769
|
+
const deduplicated = deduplicateDiagnosticFindings(findings);
|
|
770
|
+
return deduplicated.length > 0 ? { findings: deduplicated } : undefined;
|
|
771
|
+
}
|
|
772
|
+
export function diagnoseToolInputAfterFailure(original, event) {
|
|
773
|
+
if (!original)
|
|
774
|
+
return undefined;
|
|
775
|
+
try {
|
|
776
|
+
return diagnoseToolInput(original, event, Date.now() + TOOL_INPUT_DIAGNOSTIC_TIMEOUT_MS);
|
|
777
|
+
}
|
|
778
|
+
catch {
|
|
779
|
+
return undefined;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function formatToolInputDiagnosticLocation(location) {
|
|
783
|
+
const field = location.argumentIndex === undefined
|
|
784
|
+
? location.field
|
|
785
|
+
: `${location.field}[${location.argumentIndex}]`;
|
|
786
|
+
return `${field},第 ${location.line} 行第 ${location.column} 列,UTF-16 偏移 ${location.startOffset}-${location.endOffset}`;
|
|
787
|
+
}
|
|
788
|
+
export function formatToolInputDiagnostics(report) {
|
|
789
|
+
if (!report || report.findings.length === 0)
|
|
790
|
+
return '';
|
|
791
|
+
const lines = report.findings.map(item => `- [${item.confidence}] ${item.title}(${formatToolInputDiagnosticLocation(item.location)})\n 建议:${item.advice}`);
|
|
792
|
+
return `\n\n输入核验(辅助):\n${lines.join('\n')}`;
|
|
793
|
+
}
|
|
417
794
|
function approvalActorRevision(actor, owners = [], admins = [], selfAid) {
|
|
418
795
|
if (!actor)
|
|
419
796
|
return undefined;
|
|
@@ -532,8 +909,10 @@ function markRetryHealthRecorded(error) {
|
|
|
532
909
|
function wasRetryHealthRecorded(error) {
|
|
533
910
|
return !!(error && typeof error === 'object' && error[RETRY_HEALTH_RECORDED_MARK] === true);
|
|
534
911
|
}
|
|
535
|
-
function markModelFallbackExhausted(error, message) {
|
|
912
|
+
function markModelFallbackExhausted(error, message, cause) {
|
|
536
913
|
error[MODEL_FALLBACK_EXHAUSTED_MARK] = message;
|
|
914
|
+
if (cause !== undefined)
|
|
915
|
+
error.cause = cause;
|
|
537
916
|
return error;
|
|
538
917
|
}
|
|
539
918
|
function getModelFallbackExhaustedMessage(error) {
|
|
@@ -554,7 +933,7 @@ function wasRetryInputAlreadySubmitted(error) {
|
|
|
554
933
|
return !!(error && typeof error === 'object' && error[RETRY_INPUT_ALREADY_SUBMITTED_MARK] === true);
|
|
555
934
|
}
|
|
556
935
|
function formatRetryInputAlreadySubmittedMessage(error) {
|
|
557
|
-
const reason =
|
|
936
|
+
const reason = getErrorRawText(error) || 'Codex 暂时不可用';
|
|
558
937
|
return `⚠️ Codex 已接收本次消息,但本轮未能确认完成。为避免在会话中重复写入同一消息,系统未自动重试。请检查会话状态后重新发送。\n原因:${reason}`;
|
|
559
938
|
}
|
|
560
939
|
function isNonErrorInterruptReason(reason) {
|
|
@@ -567,7 +946,7 @@ function isExpectedTerminalInterrupt(reason) {
|
|
|
567
946
|
return isAbortTerminalReason(reason);
|
|
568
947
|
}
|
|
569
948
|
function formatRetryableErrorFinalMessage(error, retries) {
|
|
570
|
-
const reason =
|
|
949
|
+
const reason = getErrorRawText(error) || 'API 暂时不可用';
|
|
571
950
|
return `❌ API 暂时不可用,已自动重试 ${retries} 次仍失败,任务已停止。\n原因:${reason}`;
|
|
572
951
|
}
|
|
573
952
|
/**
|
|
@@ -678,6 +1057,20 @@ function getStreamErrorText(result) {
|
|
|
678
1057
|
}
|
|
679
1058
|
return parts.join('\n');
|
|
680
1059
|
}
|
|
1060
|
+
function getStreamRawErrorText(result, fallback) {
|
|
1061
|
+
const fullText = result.fullText.trim();
|
|
1062
|
+
const fullTextLooksLikeDiagnostic = fullText.length > 0 && (isRetryableError(new Error(fullText))
|
|
1063
|
+
|| isContextTooLongText(fullText));
|
|
1064
|
+
const fallbackLooksLikeDiagnostic = typeof fallback === 'string' && fallback.trim().length > 0 && (isRetryableError(new Error(fallback))
|
|
1065
|
+
|| isContextTooLongText(fallback));
|
|
1066
|
+
const candidates = [
|
|
1067
|
+
result.syntheticDiagnosticText,
|
|
1068
|
+
result.errors?.join('\n'),
|
|
1069
|
+
fullTextLooksLikeDiagnostic ? fullText : undefined,
|
|
1070
|
+
fallbackLooksLikeDiagnostic ? fallback : undefined,
|
|
1071
|
+
];
|
|
1072
|
+
return candidates.find((part) => typeof part === 'string' && part.trim().length > 0)?.trim();
|
|
1073
|
+
}
|
|
681
1074
|
function getStreamErrorMessage(result, includeEmoji = false) {
|
|
682
1075
|
const raw = getStreamErrorText(result);
|
|
683
1076
|
return getErrorMessage(new Error(raw), result.terminalReason, includeEmoji);
|
|
@@ -993,7 +1386,7 @@ export class ResponseEngine {
|
|
|
993
1386
|
// revocation for legacy events without a taskId.
|
|
994
1387
|
const interruptedTaskId = eventTaskId;
|
|
995
1388
|
if (interruptedTaskId && activeTask?.taskId === interruptedTaskId) {
|
|
996
|
-
this.agentDelegationRegistry?.revokeTask(event.sessionId, interruptedTaskId);
|
|
1389
|
+
this.agentDelegationRegistry?.revokeTask(event.sessionId, interruptedTaskId, 'interrupted');
|
|
997
1390
|
}
|
|
998
1391
|
}
|
|
999
1392
|
});
|
|
@@ -1337,7 +1730,8 @@ export class ResponseEngine {
|
|
|
1337
1730
|
|| this.isPauseRequested(sessionId) || this.retryDelayWaiters.has(sessionId))
|
|
1338
1731
|
return { state: 'live', ...identity };
|
|
1339
1732
|
const runner = this.getAgent(session.metadata?.channelKey || session.channel, session.baseagent, selfAid);
|
|
1340
|
-
|
|
1733
|
+
const owned = runner.hasActiveOperation?.(sessionId, active.taskId) ?? runner.hasActiveStream(sessionId);
|
|
1734
|
+
return { state: owned ? 'live' : 'missing', ...identity,
|
|
1341
1735
|
hasTerminalOwner: this.activeTimeoutHandlers.has(sessionId) };
|
|
1342
1736
|
}
|
|
1343
1737
|
// Never adopt or interrupt a newer task to clean up an older attempt.
|
|
@@ -1380,6 +1774,14 @@ export class ResponseEngine {
|
|
|
1380
1774
|
const interruptedTaskId = interruptedTurn?.active?.taskId;
|
|
1381
1775
|
this.interruptedSessions.set(sessionId, effectiveReason);
|
|
1382
1776
|
this.messageQueue?.markActiveTerminal?.(sessionId, effectiveReason);
|
|
1777
|
+
const channelKey = session.metadata?.channelKey || session.channel;
|
|
1778
|
+
const agent = this.getAgent(channelKey, session.baseagent, session.selfAID);
|
|
1779
|
+
const cancelledQueuedTurn = interruptedTurn?.active
|
|
1780
|
+
? agent.cancelQueuedTurn?.(sessionId, {
|
|
1781
|
+
taskId: interruptedTurn.active.taskId,
|
|
1782
|
+
generation: interruptedTurn.active.generation,
|
|
1783
|
+
}) ?? false
|
|
1784
|
+
: false;
|
|
1383
1785
|
// 终态必须先于渠道发送和 runner I/O 落盘。否则任一外部调用卡住时,
|
|
1384
1786
|
// shutdown 超时退出会再次留下 started/interrupting 的悬空任务。
|
|
1385
1787
|
const runtimeContext = this.activeTaskRuntimeContexts.get(sessionId);
|
|
@@ -1406,12 +1808,10 @@ export class ResponseEngine {
|
|
|
1406
1808
|
this.activeTaskRuntimeContexts.delete(sessionId);
|
|
1407
1809
|
this.activeRunnerModels.delete(sessionId);
|
|
1408
1810
|
}
|
|
1409
|
-
this.agentDelegationRegistry?.revokeTask(sessionId, taskId);
|
|
1811
|
+
this.agentDelegationRegistry?.revokeTask(sessionId, taskId, 'interrupted');
|
|
1410
1812
|
}
|
|
1411
1813
|
this.closeActiveRunnerTasks(sessionId, effectiveReason, taskSpan?.causation ?? runtimeContext?.causation);
|
|
1412
1814
|
await this.interactionRouter?.cancelAll(sessionId, effectiveReason);
|
|
1413
|
-
const channelKey = session.metadata?.channelKey || session.channel;
|
|
1414
|
-
const agent = this.getAgent(channelKey, session.baseagent, session.selfAID);
|
|
1415
1815
|
const terminalStatus = (() => {
|
|
1416
1816
|
// TimeoutController owns timeout delivery (including user-facing timeout
|
|
1417
1817
|
// details) and already guarantees a single terminal status. Sending it
|
|
@@ -1442,7 +1842,9 @@ export class ResponseEngine {
|
|
|
1442
1842
|
logger.debug(`[ResponseEngine] interrupt terminal status failure: session=${sessionId} task=${interruptedTaskId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1443
1843
|
});
|
|
1444
1844
|
})();
|
|
1445
|
-
const runnerInterrupt =
|
|
1845
|
+
const runnerInterrupt = (cancelledQueuedTurn
|
|
1846
|
+
? Promise.resolve()
|
|
1847
|
+
: agent.interrupt(sessionId)).catch(error => {
|
|
1446
1848
|
logger.debug(`[ResponseEngine] interrupt barrier runner failure: session=${sessionId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1447
1849
|
if (strict)
|
|
1448
1850
|
throw error;
|
|
@@ -1759,9 +2161,15 @@ export class ResponseEngine {
|
|
|
1759
2161
|
if (this.turnCoordinator.isCurrent(turnLease))
|
|
1760
2162
|
return false;
|
|
1761
2163
|
logger.info(`[ResponseEngine] Stale turn detected after agent.runQuery start; cancelling unconsumed stream: session=${sessionId} task=${taskId}`);
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
2164
|
+
const cancelledQueuedTurn = agent.cancelQueuedTurn?.(sessionId, {
|
|
2165
|
+
taskId: turnLease.taskId,
|
|
2166
|
+
generation: turnLease.generation,
|
|
2167
|
+
}) ?? false;
|
|
2168
|
+
if (!cancelledQueuedTurn) {
|
|
2169
|
+
await agent.interrupt(sessionId).catch(interruptError => {
|
|
2170
|
+
logger.warn(`[ResponseEngine] Failed to cancel stale runner start: session=${sessionId} task=${taskId} error=${interruptError instanceof Error ? interruptError.message : String(interruptError)}`);
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
1765
2173
|
agent.cleanupStream(streamKey);
|
|
1766
2174
|
return true;
|
|
1767
2175
|
}
|
|
@@ -3099,7 +3507,7 @@ export class ResponseEngine {
|
|
|
3099
3507
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
3100
3508
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
3101
3509
|
}
|
|
3102
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
3510
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'interrupted');
|
|
3103
3511
|
const daemonTrigger = this.isTrustedDaemonTrigger(message);
|
|
3104
3512
|
if (!daemonTrigger) {
|
|
3105
3513
|
await adapter.send(envelope, statusPayload).catch(() => { });
|
|
@@ -3107,7 +3515,11 @@ export class ResponseEngine {
|
|
|
3107
3515
|
await adapter.send(envelope, {
|
|
3108
3516
|
kind: 'result.error',
|
|
3109
3517
|
text: userMessage,
|
|
3110
|
-
|
|
3518
|
+
...(getErrorRawText(timeoutError) ? { raw: getErrorRawText(timeoutError) } : {}),
|
|
3519
|
+
subtype: normalizeCoreErrorSubtype(timeoutError, undefined, isTotalExecutionTimeout ? 'timeout' : 'timeout'),
|
|
3520
|
+
reason: isExecutionFailure(timeoutError)
|
|
3521
|
+
? normalizeCoreErrorReason(timeoutError.reason)
|
|
3522
|
+
: 'timeout',
|
|
3111
3523
|
}).catch(() => { });
|
|
3112
3524
|
if (daemonTrigger) {
|
|
3113
3525
|
await adapter.send(envelope, statusPayload).catch(() => { });
|
|
@@ -3575,11 +3987,53 @@ export class ResponseEngine {
|
|
|
3575
3987
|
? resolvedMode.mode.configureRun(modeProcessCtx)
|
|
3576
3988
|
: undefined;
|
|
3577
3989
|
const modePolicyHook = runModeConfig?.policyHook;
|
|
3578
|
-
//
|
|
3579
|
-
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3582
|
-
|
|
3990
|
+
// 系统会话:路径 / 工具由 system-session 模块判定,与角色体系无关。
|
|
3991
|
+
// 有标记就只走模块,不挂模式策略、不算授权表、不并 runtime grant。
|
|
3992
|
+
const sessionMetadata = session.metadata;
|
|
3993
|
+
const declaredSessionType = session.sessionType ?? (typeof sessionMetadata?.sessionType === 'string' ? sessionMetadata.sessionType : undefined);
|
|
3994
|
+
const systemSessionMarker = readSystemSessionMarker(sessionMetadata);
|
|
3995
|
+
if (isSystemSessionType(declaredSessionType) && !systemSessionMarker) {
|
|
3996
|
+
throw new Error(`系统会话 ${declaredSessionType} 缺少有效 systemSession 标记`);
|
|
3997
|
+
}
|
|
3998
|
+
if (systemSessionMarker && declaredSessionType && declaredSessionType !== systemSessionMarker.type) {
|
|
3999
|
+
throw new Error(`系统会话标记类型 ${systemSessionMarker.type} 与 sessionType ${declaredSessionType} 不一致`);
|
|
4000
|
+
}
|
|
4001
|
+
const systemSessionModule = systemSessionMarker
|
|
4002
|
+
? getSystemSessionModule(systemSessionMarker.type)
|
|
4003
|
+
: undefined;
|
|
4004
|
+
const systemSessionContext = systemSessionModule && systemSessionMarker
|
|
4005
|
+
? buildSystemSessionContext({
|
|
4006
|
+
marker: systemSessionMarker,
|
|
4007
|
+
sessionId: session.id,
|
|
4008
|
+
selfAid: selfAid || session.selfAID || '',
|
|
4009
|
+
projectPath: absoluteProjectPath,
|
|
4010
|
+
managedTempDir: sessionRuntimeDir,
|
|
4011
|
+
metadata: sessionMetadata,
|
|
4012
|
+
})
|
|
4013
|
+
: undefined;
|
|
4014
|
+
const systemSessionCheck = systemSessionModule && systemSessionContext
|
|
4015
|
+
? (toolName, toolInput) => systemSessionModule.checkTool(toolName, toolInput, systemSessionContext)
|
|
4016
|
+
: undefined;
|
|
4017
|
+
// 沙箱声明与 runner 有关,但当前所有 runner 共用同一个结论;仍按 runner 问一次,
|
|
4018
|
+
// 以便将来某个 runner 需要不同取舍时只改模块。
|
|
4019
|
+
const systemSessionSandbox = systemSessionModule && systemSessionContext
|
|
4020
|
+
? systemSessionModule.runnerLaunch(normalizeBaseagent(agent.name).canonical, systemSessionContext).sandbox
|
|
4021
|
+
: undefined;
|
|
4022
|
+
const pureSessionPolicyHook = systemSessionMarker ? undefined : modePolicyHook;
|
|
4023
|
+
// 路径授权(仅用户会话):身份就是角色名;运行时授予(ec ctl path grant)按 sessionId 再并一层。
|
|
4024
|
+
const pathGrantIdentity = systemSessionMarker ? undefined : (peerRole || undefined);
|
|
4025
|
+
const grantedReadRoots = (() => {
|
|
4026
|
+
if (!pathGrantIdentity)
|
|
4027
|
+
return [];
|
|
4028
|
+
const table = mergeRuntimeGrantsForSession(BUILTIN_SESSION_PATH_GRANTS, session.id, pathGrantIdentity);
|
|
4029
|
+
return resolveGrantedReadRoots(table, {
|
|
4030
|
+
selfAid,
|
|
4031
|
+
peerKey: authPeerKey,
|
|
4032
|
+
projectPath: absoluteProjectPath,
|
|
4033
|
+
identity: pathGrantIdentity,
|
|
4034
|
+
permissionMode: effectivePermissionMode,
|
|
4035
|
+
});
|
|
4036
|
+
})();
|
|
3583
4037
|
// 设置权限审批的交互上下文(支持交互卡片)
|
|
3584
4038
|
const permissionContext = {
|
|
3585
4039
|
sendPrompt: permissionPromptForOrigin,
|
|
@@ -3593,10 +4047,12 @@ export class ResponseEngine {
|
|
|
3593
4047
|
agentName: agentNameForStats,
|
|
3594
4048
|
taskId,
|
|
3595
4049
|
chatmode: isProactive ? 'proactive' : 'interactive',
|
|
3596
|
-
|
|
4050
|
+
// 系统会话没有角色
|
|
4051
|
+
role: systemSessionMarker ? undefined : peerRole,
|
|
3597
4052
|
chatType: authChatType,
|
|
3598
4053
|
selfAid,
|
|
3599
4054
|
managedTempDir: sessionRuntimeDir,
|
|
4055
|
+
grantedReadRoots,
|
|
3600
4056
|
allowReadonlySourceDiagnostics: effectiveAgentConfig?.readonlySourceDiagnostics === true,
|
|
3601
4057
|
peerKey: authPeerKey,
|
|
3602
4058
|
causation: taskCausation,
|
|
@@ -3606,6 +4062,13 @@ export class ResponseEngine {
|
|
|
3606
4062
|
recordExecutionAnomaly,
|
|
3607
4063
|
pauseController: taskPauseController,
|
|
3608
4064
|
preToolUsePolicyHook: pureSessionPolicyHook,
|
|
4065
|
+
systemSessionCheck,
|
|
4066
|
+
...(systemSessionModule && systemSessionContext
|
|
4067
|
+
? {
|
|
4068
|
+
systemSessionGrants: systemSessionModule.grants(systemSessionContext),
|
|
4069
|
+
...(systemSessionSandbox ? { systemSessionSandbox } : {}),
|
|
4070
|
+
}
|
|
4071
|
+
: {}),
|
|
3609
4072
|
armApprovedDelegationCommand: this.agentDelegationRegistry
|
|
3610
4073
|
? (sessionToken, commandHash) => this.agentDelegationRegistry.armApprovedCommand({
|
|
3611
4074
|
sessionToken,
|
|
@@ -3614,6 +4077,11 @@ export class ResponseEngine {
|
|
|
3614
4077
|
commandHash,
|
|
3615
4078
|
})
|
|
3616
4079
|
: undefined,
|
|
4080
|
+
bindSessionCredential: this.agentDelegationRegistry
|
|
4081
|
+
? (sessionToken, threadId) => this.agentDelegationRegistry.registerSessionCredential({
|
|
4082
|
+
sessionToken, threadId, sessionId: session.id, taskId,
|
|
4083
|
+
})
|
|
4084
|
+
: undefined,
|
|
3617
4085
|
turn: {
|
|
3618
4086
|
sessionId: turnLease.sessionId,
|
|
3619
4087
|
taskId,
|
|
@@ -3797,7 +4265,15 @@ export class ResponseEngine {
|
|
|
3797
4265
|
const triggerPermissionModeOverride = message.triggerMeta?.permissionModeOverride;
|
|
3798
4266
|
const commandFullAccess = message.executionPermissionOverride;
|
|
3799
4267
|
const triggerFullAccess = triggerPermissionModeOverride === 'fullaccess';
|
|
3800
|
-
if (
|
|
4268
|
+
if (systemSessionMarker) {
|
|
4269
|
+
// 系统会话不走角色 / trigger override / fullaccess:runner 模式由模块固定
|
|
4270
|
+
if (commandFullAccess || triggerFullAccess) {
|
|
4271
|
+
throw new Error('系统会话不支持 fullaccess');
|
|
4272
|
+
}
|
|
4273
|
+
effectivePermissionMode = systemSessionModule
|
|
4274
|
+
.runnerLaunch(normalizedBaseagent.canonical, systemSessionContext).runnerMode;
|
|
4275
|
+
}
|
|
4276
|
+
else if (commandFullAccess || triggerFullAccess) {
|
|
3801
4277
|
if (commandFullAccess) {
|
|
3802
4278
|
if (commandFullAccess.permissionMode !== 'fullaccess'
|
|
3803
4279
|
|| commandFullAccess.processRole !== 'fullaccess-run'
|
|
@@ -4011,10 +4487,13 @@ export class ResponseEngine {
|
|
|
4011
4487
|
bootstrapAgentMdPath: agentMdPath(selfAid),
|
|
4012
4488
|
};
|
|
4013
4489
|
}
|
|
4490
|
+
// persona.md 是唯一的人格/交接文件(personal.md 已废弃并入)。
|
|
4491
|
+
// 该白名单让 owner 会话可直接用文件工具分段更新 `## Bootstrap 交接`,
|
|
4492
|
+
// 避免把长 Markdown 交接摘要塞进 `ec agent persona --text` 的 shell 转义。
|
|
4014
4493
|
if (selfAid && peerRole === 'owner' && effectivePermissionMode !== 'readonly') {
|
|
4015
4494
|
modelOverride = {
|
|
4016
4495
|
...(modelOverride || {}),
|
|
4017
|
-
personalMdPath: path.join(resolveRoot(), 'agents', selfAid, 'personal', '
|
|
4496
|
+
personalMdPath: path.join(resolveRoot(), 'agents', selfAid, 'personal', 'persona.md'),
|
|
4018
4497
|
};
|
|
4019
4498
|
}
|
|
4020
4499
|
if (normalizedBaseagent.canonical === 'claude') {
|
|
@@ -4258,9 +4737,12 @@ export class ResponseEngine {
|
|
|
4258
4737
|
// Bootstrap always uses the lifecycle-gated main manifest. A custom
|
|
4259
4738
|
// session manifest (for example the auxiliary manifest with `always`
|
|
4260
4739
|
// sections) must not reintroduce normal rules or command context.
|
|
4740
|
+
// 系统会话:manifest 由模块按 type/subtype 决定,不读 config
|
|
4261
4741
|
const manifestFile = isBootstrapping
|
|
4262
4742
|
? DEFAULT_MANIFEST_FILE
|
|
4263
|
-
:
|
|
4743
|
+
: systemSessionMarker
|
|
4744
|
+
? getSystemSessionModule(systemSessionMarker.type).manifest(systemSessionMarker.subtype)
|
|
4745
|
+
: resolveSessionManifest(sessionType, sessionManifests);
|
|
4264
4746
|
const kitContext = renderKitSections(kitCtx, manifestFile);
|
|
4265
4747
|
effectiveSystemPrompt = [options?.systemPromptAppend, kitContext].filter(Boolean).join('\n') || undefined;
|
|
4266
4748
|
// ── Stats: context_breakdown 旁路采集(各段估算 token 数,字符数/4 近似) ──
|
|
@@ -4495,7 +4977,8 @@ export class ResponseEngine {
|
|
|
4495
4977
|
peerKey,
|
|
4496
4978
|
peerName: peerName || undefined,
|
|
4497
4979
|
peerType: message.peerType || session.metadata?.peerType || undefined,
|
|
4498
|
-
|
|
4980
|
+
// 系统会话没有角色
|
|
4981
|
+
...(systemSessionMarker ? {} : { peerRole }),
|
|
4499
4982
|
// 会话原型与整理模式:ipc 侧靠 sessionType 拦非整理会话的写命令,
|
|
4500
4983
|
// 靠 memoryMode 校验 --scope 是否与本模式匹配。
|
|
4501
4984
|
sessionType: renderedSessionType,
|
|
@@ -4532,7 +5015,8 @@ export class ResponseEngine {
|
|
|
4532
5015
|
chatType: configChatType,
|
|
4533
5016
|
selfAid,
|
|
4534
5017
|
peerKey,
|
|
4535
|
-
|
|
5018
|
+
// 系统会话与角色体系无关:带标记、不签角色
|
|
5019
|
+
...(systemSessionMarker ? { systemSession: systemSessionMarker } : { issuedRole: peerRole }),
|
|
4536
5020
|
executionSource: fullAccessAuthorization?.source ?? (message.source === 'trigger' ? 'trigger' : 'user'),
|
|
4537
5021
|
permissionMode: effectivePermissionMode,
|
|
4538
5022
|
approvalInteractionPolicy,
|
|
@@ -4798,7 +5282,7 @@ export class ResponseEngine {
|
|
|
4798
5282
|
const candidateHint = fallbackCandidates.length === 0
|
|
4799
5283
|
? '未配置可用备用模型'
|
|
4800
5284
|
: '备用模型均不可用';
|
|
4801
|
-
throw markModelFallbackExhausted(new Error(`model_not_found: ${primaryModel ?? 'runner_default'}`), `当前模型 ${primaryModel ?? '默认模型'} 不可用,${candidateHint}。请稍后重试;管理员可在 agents/defaults.json 的 baseagents.${normalizedBaseagent.canonical}.fallbackModels
|
|
5285
|
+
throw markModelFallbackExhausted(new Error(`model_not_found: ${primaryModel ?? 'runner_default'}`), `当前模型 ${primaryModel ?? '默认模型'} 不可用,${candidateHint}。请稍后重试;管理员可在 agents/defaults.json 的 baseagents.${normalizedBaseagent.canonical}.fallbackModels 配置同类备用模型。`, retryError);
|
|
4802
5286
|
}
|
|
4803
5287
|
if (retryAttemptTimedOut || isRetryableError(retryError)) {
|
|
4804
5288
|
await recordRetryHealthError(retryError);
|
|
@@ -4926,7 +5410,7 @@ export class ResponseEngine {
|
|
|
4926
5410
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
4927
5411
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4928
5412
|
}
|
|
4929
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5413
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'replaced');
|
|
4930
5414
|
logger.info(`[ResponseEngine] Stale turn stopped before publish: session=${session.id} task=${taskId}`);
|
|
4931
5415
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation, {
|
|
4932
5416
|
expected: turnLease?.generation,
|
|
@@ -4942,7 +5426,7 @@ export class ResponseEngine {
|
|
|
4942
5426
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
4943
5427
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4944
5428
|
}
|
|
4945
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5429
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'replaced');
|
|
4946
5430
|
logger.info(`[ResponseEngine] Stale turn stopped before protocol rejection: session=${session.id} task=${taskId}`);
|
|
4947
5431
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation, {
|
|
4948
5432
|
expected: turnLease?.generation,
|
|
@@ -5096,6 +5580,7 @@ export class ResponseEngine {
|
|
|
5096
5580
|
let sentErrorAsSystemError = false;
|
|
5097
5581
|
const finalReplyText = streamResult.lastReplyText || streamResult.fullText;
|
|
5098
5582
|
const visibleFinalReplyText = stripContextLimitErrorLines(finalReplyText);
|
|
5583
|
+
const streamRawErrorText = getStreamRawErrorText(streamResult, visibleFinalReplyText || undefined);
|
|
5099
5584
|
const isTriggerNoop = message.source === 'trigger'
|
|
5100
5585
|
&& typeof message.triggerMeta?.noopSentinel === 'string'
|
|
5101
5586
|
&& message.triggerMeta.noopSentinel.length > 0
|
|
@@ -5107,7 +5592,7 @@ export class ResponseEngine {
|
|
|
5107
5592
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5108
5593
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5109
5594
|
}
|
|
5110
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5595
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'replaced');
|
|
5111
5596
|
logger.info(`[ResponseEngine] Stale turn stopped before final flush: session=${session.id} task=${taskId}`);
|
|
5112
5597
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_turn', taskCausation);
|
|
5113
5598
|
return;
|
|
@@ -5128,10 +5613,14 @@ export class ResponseEngine {
|
|
|
5128
5613
|
if (streamResult.isError) {
|
|
5129
5614
|
// 用原始诊断文本(含错误码)判断可重试性,不用已过 stripContextLimitErrorLines 的展示文本
|
|
5130
5615
|
const recoverable = isRetryableError(new Error(getStreamErrorText(streamResult)));
|
|
5616
|
+
const subtype = normalizeCoreErrorSubtype(new Error(streamRawErrorText || getStreamErrorText(streamResult)), streamResult.subtype, streamResult.terminalReason);
|
|
5617
|
+
const reason = normalizeCoreErrorReason(streamResult.terminalReason);
|
|
5131
5618
|
await adapter.send(envelope, {
|
|
5132
5619
|
kind: 'system.error',
|
|
5133
5620
|
text: errorText,
|
|
5134
|
-
|
|
5621
|
+
...(streamRawErrorText ? { raw: streamRawErrorText } : {}),
|
|
5622
|
+
subtype,
|
|
5623
|
+
...(reason ? { reason } : {}),
|
|
5135
5624
|
recoverable,
|
|
5136
5625
|
}).catch(() => { });
|
|
5137
5626
|
sentErrorAsSystemError = true;
|
|
@@ -5148,7 +5637,7 @@ export class ResponseEngine {
|
|
|
5148
5637
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5149
5638
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5150
5639
|
}
|
|
5151
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5640
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, streamResult.isError ? 'failed' : 'completed');
|
|
5152
5641
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared task=${taskId}`);
|
|
5153
5642
|
import('../../utils/msg-diag.js').then(m => m.diagLog('task_done', { aid: session.selfAID ?? '', msgId: message.messageId ?? '', taskId, text: message.content ?? '' })).catch(() => { });
|
|
5154
5643
|
// 降级模型回复末尾追加标记(代码层硬注入,不依赖模型输出)
|
|
@@ -5217,6 +5706,10 @@ export class ResponseEngine {
|
|
|
5217
5706
|
: getStreamErrorMessage(streamResult, false);
|
|
5218
5707
|
const rawSubtype = streamResult.subtype || 'agent_error';
|
|
5219
5708
|
const errorType = prefixErrorType(ERROR_PREFIX.AGENT, rawSubtype);
|
|
5709
|
+
const coreSubtype = normalizeCoreErrorSubtype(new Error(streamRawErrorText || errorSummary), rawSubtype, streamResult.terminalReason);
|
|
5710
|
+
const coreReason = permissionTerminalFailure
|
|
5711
|
+
? normalizeCoreErrorReason(permissionTerminalFailure.reason)
|
|
5712
|
+
: normalizeCoreErrorReason(streamResult.terminalReason);
|
|
5220
5713
|
// 用户主动打断(新消息//stop/撤回)会让 SDK 流在工具调用中途被掐断,
|
|
5221
5714
|
// 末尾 result message 形状异常并被标记为 error(含 SDK 内部 ede_diagnostic 串)。
|
|
5222
5715
|
// 这不是真正的失败,不应把诊断串暴露给用户,也不计入错误统计。
|
|
@@ -5241,12 +5734,24 @@ export class ResponseEngine {
|
|
|
5241
5734
|
if (this.isTrustedDaemonTrigger(message)) {
|
|
5242
5735
|
await adapter.send(envelope, { kind: 'status.error', metadata: terminalMetadata }).catch(() => { });
|
|
5243
5736
|
if (!sentErrorAsSystemError) {
|
|
5244
|
-
await adapter.send(envelope, {
|
|
5737
|
+
await adapter.send(envelope, {
|
|
5738
|
+
kind: 'result.error',
|
|
5739
|
+
text: userErrorSummary,
|
|
5740
|
+
...(streamRawErrorText ? { raw: streamRawErrorText } : {}),
|
|
5741
|
+
subtype: coreSubtype,
|
|
5742
|
+
...(coreReason ? { reason: coreReason } : {}),
|
|
5743
|
+
}).catch(() => { });
|
|
5245
5744
|
}
|
|
5246
5745
|
}
|
|
5247
5746
|
else {
|
|
5248
5747
|
if (!sentErrorAsSystemError) {
|
|
5249
|
-
await adapter.send(envelope, {
|
|
5748
|
+
await adapter.send(envelope, {
|
|
5749
|
+
kind: 'result.error',
|
|
5750
|
+
text: userErrorSummary,
|
|
5751
|
+
...(streamRawErrorText ? { raw: streamRawErrorText } : {}),
|
|
5752
|
+
subtype: coreSubtype,
|
|
5753
|
+
...(coreReason ? { reason: coreReason } : {}),
|
|
5754
|
+
}).catch(() => { });
|
|
5250
5755
|
}
|
|
5251
5756
|
await adapter.send(envelope, { kind: 'status.error', metadata: terminalMetadata }).catch(() => { });
|
|
5252
5757
|
}
|
|
@@ -5592,7 +6097,7 @@ export class ResponseEngine {
|
|
|
5592
6097
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5593
6098
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5594
6099
|
}
|
|
5595
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
6100
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, this.interruptedSessions.has(session.id) ? 'interrupted' : 'failed');
|
|
5596
6101
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared (on error) task=${taskId}`);
|
|
5597
6102
|
}
|
|
5598
6103
|
catch { }
|
|
@@ -5739,10 +6244,29 @@ export class ResponseEngine {
|
|
|
5739
6244
|
sessionId: session.id,
|
|
5740
6245
|
metadata: withTaskMetadata(sendOpts?.metadata),
|
|
5741
6246
|
};
|
|
6247
|
+
const errorReason = isExecutionFailure(error)
|
|
6248
|
+
? normalizeCoreErrorReason(error.reason)
|
|
6249
|
+
: retryExhaustedCount
|
|
6250
|
+
? 'retry_exhausted'
|
|
6251
|
+
: retryInputAlreadySubmitted
|
|
6252
|
+
? 'input_already_submitted'
|
|
6253
|
+
: modelFallbackExhaustedMessage
|
|
6254
|
+
? 'fallback_exhausted'
|
|
6255
|
+
: isTotalExecutionTimeout || isTimeout
|
|
6256
|
+
? 'timeout'
|
|
6257
|
+
: undefined;
|
|
6258
|
+
const errorSubtype = error instanceof OrphanedTargetSessionError
|
|
6259
|
+
? ErrorType.SESSION_UNAVAILABLE
|
|
6260
|
+
: modelFallbackExhaustedMessage
|
|
6261
|
+
? ErrorType.MODEL_UNAVAILABLE
|
|
6262
|
+
: normalizeCoreErrorSubtype(error, undefined, isTotalExecutionTimeout || isTimeout ? 'timeout' : undefined);
|
|
6263
|
+
const rawErrorText = getErrorRawText(error);
|
|
5742
6264
|
const errorPayload = {
|
|
5743
6265
|
kind: 'result.error',
|
|
5744
6266
|
text: userMessage,
|
|
5745
|
-
|
|
6267
|
+
subtype: errorSubtype,
|
|
6268
|
+
...(rawErrorText ? { raw: rawErrorText } : {}),
|
|
6269
|
+
...(errorReason ? { reason: errorReason } : {}),
|
|
5746
6270
|
};
|
|
5747
6271
|
if (!isUserInterrupt && daemonTrigger && procStatus === 'error') {
|
|
5748
6272
|
// DaemonChannel settles a trigger on the first terminal payload. Put
|
|
@@ -6178,10 +6702,11 @@ export class ResponseEngine {
|
|
|
6178
6702
|
if (event.type === 'complete') {
|
|
6179
6703
|
event = normalizeCompleteAgentEvent(event);
|
|
6180
6704
|
}
|
|
6181
|
-
if (event.type === 'tool_result' && event
|
|
6705
|
+
if (event.type === 'tool_result' && isToolResultFailure(event)) {
|
|
6182
6706
|
const classification = normalizeToolErrorClassification(event);
|
|
6183
6707
|
event = {
|
|
6184
6708
|
...event,
|
|
6709
|
+
isError: true,
|
|
6185
6710
|
errorCode: classification.errorCode,
|
|
6186
6711
|
errorDetailCode: classification.errorDetailCode,
|
|
6187
6712
|
classificationMissing: classification.classificationMissing || event.classificationMissing,
|
|
@@ -6327,7 +6852,20 @@ export class ResponseEngine {
|
|
|
6327
6852
|
logger.info(`[ResponseEngine] Event: type=${event.type}${eventDetail}`);
|
|
6328
6853
|
}
|
|
6329
6854
|
// IMRenderer 旁路:proactive 模式逐事件投影为 thought(fire-and-forget)
|
|
6330
|
-
|
|
6855
|
+
let diagnosticSuffix = '';
|
|
6856
|
+
let projectedEvent = event;
|
|
6857
|
+
if (event.type === 'tool_result' && isToolResultFailure(event)) {
|
|
6858
|
+
const byCallId = event.callId ? toolInputByCallId.get(event.callId) : undefined;
|
|
6859
|
+
const byCorrelationId = event.correlationId
|
|
6860
|
+
? toolInputByCorrelationId.get(event.correlationId)
|
|
6861
|
+
: undefined;
|
|
6862
|
+
const original = event.callId && event.correlationId
|
|
6863
|
+
? byCallId === byCorrelationId ? byCallId : undefined
|
|
6864
|
+
: byCallId ?? byCorrelationId;
|
|
6865
|
+
diagnosticSuffix = formatToolInputDiagnostics(diagnoseToolInputAfterFailure(original, event));
|
|
6866
|
+
projectedEvent = { ...event, error: getToolResultErrorText(event) + diagnosticSuffix };
|
|
6867
|
+
}
|
|
6868
|
+
renderer.emit(projectedEvent);
|
|
6331
6869
|
if (event.type === 'text_start') {
|
|
6332
6870
|
await renderer.sendTextStart(event.contentIndex, event.turn);
|
|
6333
6871
|
continue;
|
|
@@ -6751,15 +7289,14 @@ export class ResponseEngine {
|
|
|
6751
7289
|
}
|
|
6752
7290
|
// 从 tool_use 阶段缓存的描述中回溯
|
|
6753
7291
|
const cachedDesc = event.callId ? toolDescByCallId.get(event.callId) : undefined;
|
|
6754
|
-
|
|
7292
|
+
const toolResultFailed = isToolResultFailure(event);
|
|
7293
|
+
if (toolResultFailed && !shouldSuppress()) {
|
|
6755
7294
|
hasErrorResult = true;
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
errorMsg = errorMsg.replace(/<tool_use_error>(.*?)<\/tool_use_error>/gs, '$1');
|
|
6759
|
-
renderer.addToolResult(event.name || '\u5de5\u5177', false, undefined, errorMsg, event.callId, event.durationMs, cachedDesc, event, event.assistantUuid);
|
|
7295
|
+
const errorMsg = getToolResultErrorText(event) + diagnosticSuffix;
|
|
7296
|
+
renderer.addToolResult(event.name || '\u5de5\u5177', false, event.result, errorMsg, event.callId, event.durationMs, cachedDesc, event, event.assistantUuid);
|
|
6760
7297
|
}
|
|
6761
|
-
else if (!
|
|
6762
|
-
renderer.addToolResult(event.name || '\u5de5\u5177', true, event.result, undefined, event.callId, event.durationMs, cachedDesc,
|
|
7298
|
+
else if (!toolResultFailed && !shouldSuppress()) {
|
|
7299
|
+
renderer.addToolResult(event.name || '\u5de5\u5177', true, event.result, undefined, event.callId, event.durationMs, cachedDesc, event, event.assistantUuid);
|
|
6763
7300
|
}
|
|
6764
7301
|
}
|
|
6765
7302
|
// 非终态运行时告警走 status notify,不进入 activity/thought 历史。
|