evolcore 0.0.33 → 0.0.35-test.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +38 -4
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +93 -14
- package/dist/agents/codex/diagnostics.js +79 -22
- package/dist/agents/codex-runner.js +245 -47
- package/dist/agents/ecagent-runner.js +90 -27
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/agentmd.js +13 -2
- package/dist/aun/aid/client.js +1 -1
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/msg/managed-operation.js +16 -0
- package/dist/aun/msg/p2p.js +18 -2
- package/dist/channels/aun.js +123 -7
- package/dist/channels/daemon.js +6 -3
- package/dist/cli/aun-commands.js +39 -14
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +130 -23
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/getmeta-command.js +211 -0
- package/dist/cli/index.js +26 -6
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/memory-command.js +200 -156
- package/dist/cli/thread-command.js +17 -7
- 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/resolved-config-op.js +25 -8
- 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 +22 -3
- package/dist/core/auth/operation-catalog.js +16 -0
- package/dist/core/channel-runtime.js +1 -1
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +145 -5
- 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 +24 -7
- package/dist/core/command/menu-protocol.js +1 -0
- package/dist/core/command/slash-handler.js +2 -0
- package/dist/core/command/topic-slash.js +4 -7
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/event-catalog.js +14 -0
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/ipc-registration.js +47 -2
- package/dist/core/memory/batch-meta.js +133 -0
- package/dist/core/memory/consolidation-context.js +132 -28
- package/dist/core/memory/consolidation-ops-records.js +14 -16
- package/dist/core/memory/consolidation-trigger.js +15 -7
- package/dist/core/memory/consolidation.js +146 -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 +50 -19
- package/dist/core/message/aun-ipc-sender.js +19 -1
- package/dist/core/message/im-renderer.js +20 -4
- package/dist/core/message/message-bridge.js +5 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +17 -0
- package/dist/core/message/response-engine.js +598 -50
- package/dist/core/permission/ec-command-parser.js +169 -44
- 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 +60 -62
- package/dist/core/relation/peer-identity.js +87 -12
- 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 +131 -6
- package/dist/core/session/session-title.js +26 -1
- 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 +289 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +6 -1
- package/dist/core/thread/ipc-operation.js +33 -16
- package/dist/core/thread/thread-operation.js +62 -24
- package/dist/core/thread/thread-service.js +258 -51
- 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 +17 -5
- 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 +9 -10
- package/kits/docs/evolcore/fs.md +2 -2
- package/kits/docs/evolcore/group-collaboration.md +3 -3
- package/kits/docs/evolcore/group.md +14 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -16
- 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/stats.md +1 -1
- package/kits/docs/evolcore/thread-chain.md +30 -14
- package/kits/docs/prompt-loading-architecture.md +0 -1
- package/kits/eck_manifest.auxiliary.json +9 -0
- package/kits/eck_manifest.json +20 -20
- package/kits/eck_manifest.memory.json +20 -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 +13 -16
- 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 +8 -5
- package/kits/skills/ec-skill/scripts/cli.mjs +1 -1
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/thread-ops/SKILL.md +18 -18
- 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 +12 -12
- 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 +42 -7
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/system-fragments/bash-description.md +9 -0
- 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,8 +933,18 @@ 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
|
|
558
|
-
|
|
936
|
+
const raw = getErrorRawText(error);
|
|
937
|
+
const record = error && typeof error === 'object' ? error : undefined;
|
|
938
|
+
const retryCount = typeof record?.codexRetryCount === 'number' && Number.isSafeInteger(record.codexRetryCount)
|
|
939
|
+
? record.codexRetryCount : undefined;
|
|
940
|
+
const statusCode = raw?.match(/\b([45]\d{2})\b/)?.[1];
|
|
941
|
+
if (retryCount !== undefined && statusCode) {
|
|
942
|
+
return `⚠️ Codex 已重连 ${retryCount} 次模型服务后仍失败(${statusCode})。本次执行已停止。`;
|
|
943
|
+
}
|
|
944
|
+
if (retryCount !== undefined) {
|
|
945
|
+
return `⚠️ Codex 已重连 ${retryCount} 次模型服务后仍失败。本次执行已停止。`;
|
|
946
|
+
}
|
|
947
|
+
return '⚠️ Codex 已接收本次消息,但模型服务请求失败。本次执行已停止。';
|
|
559
948
|
}
|
|
560
949
|
function isNonErrorInterruptReason(reason) {
|
|
561
950
|
return reason === 'new_message'
|
|
@@ -567,7 +956,7 @@ function isExpectedTerminalInterrupt(reason) {
|
|
|
567
956
|
return isAbortTerminalReason(reason);
|
|
568
957
|
}
|
|
569
958
|
function formatRetryableErrorFinalMessage(error, retries) {
|
|
570
|
-
const reason =
|
|
959
|
+
const reason = getErrorRawText(error) || 'API 暂时不可用';
|
|
571
960
|
return `❌ API 暂时不可用,已自动重试 ${retries} 次仍失败,任务已停止。\n原因:${reason}`;
|
|
572
961
|
}
|
|
573
962
|
/**
|
|
@@ -678,6 +1067,20 @@ function getStreamErrorText(result) {
|
|
|
678
1067
|
}
|
|
679
1068
|
return parts.join('\n');
|
|
680
1069
|
}
|
|
1070
|
+
function getStreamRawErrorText(result, fallback) {
|
|
1071
|
+
const fullText = result.fullText.trim();
|
|
1072
|
+
const fullTextLooksLikeDiagnostic = fullText.length > 0 && (isRetryableError(new Error(fullText))
|
|
1073
|
+
|| isContextTooLongText(fullText));
|
|
1074
|
+
const fallbackLooksLikeDiagnostic = typeof fallback === 'string' && fallback.trim().length > 0 && (isRetryableError(new Error(fallback))
|
|
1075
|
+
|| isContextTooLongText(fallback));
|
|
1076
|
+
const candidates = [
|
|
1077
|
+
result.syntheticDiagnosticText,
|
|
1078
|
+
result.errors?.join('\n'),
|
|
1079
|
+
fullTextLooksLikeDiagnostic ? fullText : undefined,
|
|
1080
|
+
fallbackLooksLikeDiagnostic ? fallback : undefined,
|
|
1081
|
+
];
|
|
1082
|
+
return candidates.find((part) => typeof part === 'string' && part.trim().length > 0)?.trim();
|
|
1083
|
+
}
|
|
681
1084
|
function getStreamErrorMessage(result, includeEmoji = false) {
|
|
682
1085
|
const raw = getStreamErrorText(result);
|
|
683
1086
|
return getErrorMessage(new Error(raw), result.terminalReason, includeEmoji);
|
|
@@ -993,7 +1396,7 @@ export class ResponseEngine {
|
|
|
993
1396
|
// revocation for legacy events without a taskId.
|
|
994
1397
|
const interruptedTaskId = eventTaskId;
|
|
995
1398
|
if (interruptedTaskId && activeTask?.taskId === interruptedTaskId) {
|
|
996
|
-
this.agentDelegationRegistry?.revokeTask(event.sessionId, interruptedTaskId);
|
|
1399
|
+
this.agentDelegationRegistry?.revokeTask(event.sessionId, interruptedTaskId, 'interrupted');
|
|
997
1400
|
}
|
|
998
1401
|
}
|
|
999
1402
|
});
|
|
@@ -1337,7 +1740,8 @@ export class ResponseEngine {
|
|
|
1337
1740
|
|| this.isPauseRequested(sessionId) || this.retryDelayWaiters.has(sessionId))
|
|
1338
1741
|
return { state: 'live', ...identity };
|
|
1339
1742
|
const runner = this.getAgent(session.metadata?.channelKey || session.channel, session.baseagent, selfAid);
|
|
1340
|
-
|
|
1743
|
+
const owned = runner.hasActiveOperation?.(sessionId, active.taskId) ?? runner.hasActiveStream(sessionId);
|
|
1744
|
+
return { state: owned ? 'live' : 'missing', ...identity,
|
|
1341
1745
|
hasTerminalOwner: this.activeTimeoutHandlers.has(sessionId) };
|
|
1342
1746
|
}
|
|
1343
1747
|
// Never adopt or interrupt a newer task to clean up an older attempt.
|
|
@@ -1380,6 +1784,14 @@ export class ResponseEngine {
|
|
|
1380
1784
|
const interruptedTaskId = interruptedTurn?.active?.taskId;
|
|
1381
1785
|
this.interruptedSessions.set(sessionId, effectiveReason);
|
|
1382
1786
|
this.messageQueue?.markActiveTerminal?.(sessionId, effectiveReason);
|
|
1787
|
+
const channelKey = session.metadata?.channelKey || session.channel;
|
|
1788
|
+
const agent = this.getAgent(channelKey, session.baseagent, session.selfAID);
|
|
1789
|
+
const cancelledQueuedTurn = interruptedTurn?.active
|
|
1790
|
+
? agent.cancelQueuedTurn?.(sessionId, {
|
|
1791
|
+
taskId: interruptedTurn.active.taskId,
|
|
1792
|
+
generation: interruptedTurn.active.generation,
|
|
1793
|
+
}) ?? false
|
|
1794
|
+
: false;
|
|
1383
1795
|
// 终态必须先于渠道发送和 runner I/O 落盘。否则任一外部调用卡住时,
|
|
1384
1796
|
// shutdown 超时退出会再次留下 started/interrupting 的悬空任务。
|
|
1385
1797
|
const runtimeContext = this.activeTaskRuntimeContexts.get(sessionId);
|
|
@@ -1406,12 +1818,10 @@ export class ResponseEngine {
|
|
|
1406
1818
|
this.activeTaskRuntimeContexts.delete(sessionId);
|
|
1407
1819
|
this.activeRunnerModels.delete(sessionId);
|
|
1408
1820
|
}
|
|
1409
|
-
this.agentDelegationRegistry?.revokeTask(sessionId, taskId);
|
|
1821
|
+
this.agentDelegationRegistry?.revokeTask(sessionId, taskId, 'interrupted');
|
|
1410
1822
|
}
|
|
1411
1823
|
this.closeActiveRunnerTasks(sessionId, effectiveReason, taskSpan?.causation ?? runtimeContext?.causation);
|
|
1412
1824
|
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
1825
|
const terminalStatus = (() => {
|
|
1416
1826
|
// TimeoutController owns timeout delivery (including user-facing timeout
|
|
1417
1827
|
// details) and already guarantees a single terminal status. Sending it
|
|
@@ -1442,7 +1852,9 @@ export class ResponseEngine {
|
|
|
1442
1852
|
logger.debug(`[ResponseEngine] interrupt terminal status failure: session=${sessionId} task=${interruptedTaskId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1443
1853
|
});
|
|
1444
1854
|
})();
|
|
1445
|
-
const runnerInterrupt =
|
|
1855
|
+
const runnerInterrupt = (cancelledQueuedTurn
|
|
1856
|
+
? Promise.resolve()
|
|
1857
|
+
: agent.interrupt(sessionId)).catch(error => {
|
|
1446
1858
|
logger.debug(`[ResponseEngine] interrupt barrier runner failure: session=${sessionId} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1447
1859
|
if (strict)
|
|
1448
1860
|
throw error;
|
|
@@ -1759,9 +2171,15 @@ export class ResponseEngine {
|
|
|
1759
2171
|
if (this.turnCoordinator.isCurrent(turnLease))
|
|
1760
2172
|
return false;
|
|
1761
2173
|
logger.info(`[ResponseEngine] Stale turn detected after agent.runQuery start; cancelling unconsumed stream: session=${sessionId} task=${taskId}`);
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
2174
|
+
const cancelledQueuedTurn = agent.cancelQueuedTurn?.(sessionId, {
|
|
2175
|
+
taskId: turnLease.taskId,
|
|
2176
|
+
generation: turnLease.generation,
|
|
2177
|
+
}) ?? false;
|
|
2178
|
+
if (!cancelledQueuedTurn) {
|
|
2179
|
+
await agent.interrupt(sessionId).catch(interruptError => {
|
|
2180
|
+
logger.warn(`[ResponseEngine] Failed to cancel stale runner start: session=${sessionId} task=${taskId} error=${interruptError instanceof Error ? interruptError.message : String(interruptError)}`);
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
1765
2183
|
agent.cleanupStream(streamKey);
|
|
1766
2184
|
return true;
|
|
1767
2185
|
}
|
|
@@ -3099,7 +3517,7 @@ export class ResponseEngine {
|
|
|
3099
3517
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
3100
3518
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
3101
3519
|
}
|
|
3102
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
3520
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'interrupted');
|
|
3103
3521
|
const daemonTrigger = this.isTrustedDaemonTrigger(message);
|
|
3104
3522
|
if (!daemonTrigger) {
|
|
3105
3523
|
await adapter.send(envelope, statusPayload).catch(() => { });
|
|
@@ -3107,7 +3525,11 @@ export class ResponseEngine {
|
|
|
3107
3525
|
await adapter.send(envelope, {
|
|
3108
3526
|
kind: 'result.error',
|
|
3109
3527
|
text: userMessage,
|
|
3110
|
-
|
|
3528
|
+
...(getErrorRawText(timeoutError) ? { raw: getErrorRawText(timeoutError) } : {}),
|
|
3529
|
+
subtype: normalizeCoreErrorSubtype(timeoutError, undefined, isTotalExecutionTimeout ? 'timeout' : 'timeout'),
|
|
3530
|
+
reason: isExecutionFailure(timeoutError)
|
|
3531
|
+
? normalizeCoreErrorReason(timeoutError.reason)
|
|
3532
|
+
: 'timeout',
|
|
3111
3533
|
}).catch(() => { });
|
|
3112
3534
|
if (daemonTrigger) {
|
|
3113
3535
|
await adapter.send(envelope, statusPayload).catch(() => { });
|
|
@@ -3575,11 +3997,53 @@ export class ResponseEngine {
|
|
|
3575
3997
|
? resolvedMode.mode.configureRun(modeProcessCtx)
|
|
3576
3998
|
: undefined;
|
|
3577
3999
|
const modePolicyHook = runModeConfig?.policyHook;
|
|
3578
|
-
//
|
|
3579
|
-
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3582
|
-
|
|
4000
|
+
// 系统会话:路径 / 工具由 system-session 模块判定,与角色体系无关。
|
|
4001
|
+
// 有标记就只走模块,不挂模式策略、不算授权表、不并 runtime grant。
|
|
4002
|
+
const sessionMetadata = session.metadata;
|
|
4003
|
+
const declaredSessionType = session.sessionType ?? (typeof sessionMetadata?.sessionType === 'string' ? sessionMetadata.sessionType : undefined);
|
|
4004
|
+
const systemSessionMarker = readSystemSessionMarker(sessionMetadata);
|
|
4005
|
+
if (isSystemSessionType(declaredSessionType) && !systemSessionMarker) {
|
|
4006
|
+
throw new Error(`系统会话 ${declaredSessionType} 缺少有效 systemSession 标记`);
|
|
4007
|
+
}
|
|
4008
|
+
if (systemSessionMarker && declaredSessionType && declaredSessionType !== systemSessionMarker.type) {
|
|
4009
|
+
throw new Error(`系统会话标记类型 ${systemSessionMarker.type} 与 sessionType ${declaredSessionType} 不一致`);
|
|
4010
|
+
}
|
|
4011
|
+
const systemSessionModule = systemSessionMarker
|
|
4012
|
+
? getSystemSessionModule(systemSessionMarker.type)
|
|
4013
|
+
: undefined;
|
|
4014
|
+
const systemSessionContext = systemSessionModule && systemSessionMarker
|
|
4015
|
+
? buildSystemSessionContext({
|
|
4016
|
+
marker: systemSessionMarker,
|
|
4017
|
+
sessionId: session.id,
|
|
4018
|
+
selfAid: selfAid || session.selfAID || '',
|
|
4019
|
+
projectPath: absoluteProjectPath,
|
|
4020
|
+
managedTempDir: sessionRuntimeDir,
|
|
4021
|
+
metadata: sessionMetadata,
|
|
4022
|
+
})
|
|
4023
|
+
: undefined;
|
|
4024
|
+
const systemSessionCheck = systemSessionModule && systemSessionContext
|
|
4025
|
+
? (toolName, toolInput) => systemSessionModule.checkTool(toolName, toolInput, systemSessionContext)
|
|
4026
|
+
: undefined;
|
|
4027
|
+
// 沙箱声明与 runner 有关,但当前所有 runner 共用同一个结论;仍按 runner 问一次,
|
|
4028
|
+
// 以便将来某个 runner 需要不同取舍时只改模块。
|
|
4029
|
+
const systemSessionSandbox = systemSessionModule && systemSessionContext
|
|
4030
|
+
? systemSessionModule.runnerLaunch(normalizeBaseagent(agent.name).canonical, systemSessionContext).sandbox
|
|
4031
|
+
: undefined;
|
|
4032
|
+
const pureSessionPolicyHook = systemSessionMarker ? undefined : modePolicyHook;
|
|
4033
|
+
// 路径授权(仅用户会话):身份就是角色名;运行时授予(ec ctl path grant)按 sessionId 再并一层。
|
|
4034
|
+
const pathGrantIdentity = systemSessionMarker ? undefined : (peerRole || undefined);
|
|
4035
|
+
const grantedReadRoots = (() => {
|
|
4036
|
+
if (!pathGrantIdentity)
|
|
4037
|
+
return [];
|
|
4038
|
+
const table = mergeRuntimeGrantsForSession(BUILTIN_SESSION_PATH_GRANTS, session.id, pathGrantIdentity);
|
|
4039
|
+
return resolveGrantedReadRoots(table, {
|
|
4040
|
+
selfAid,
|
|
4041
|
+
peerKey: authPeerKey,
|
|
4042
|
+
projectPath: absoluteProjectPath,
|
|
4043
|
+
identity: pathGrantIdentity,
|
|
4044
|
+
permissionMode: effectivePermissionMode,
|
|
4045
|
+
});
|
|
4046
|
+
})();
|
|
3583
4047
|
// 设置权限审批的交互上下文(支持交互卡片)
|
|
3584
4048
|
const permissionContext = {
|
|
3585
4049
|
sendPrompt: permissionPromptForOrigin,
|
|
@@ -3593,10 +4057,12 @@ export class ResponseEngine {
|
|
|
3593
4057
|
agentName: agentNameForStats,
|
|
3594
4058
|
taskId,
|
|
3595
4059
|
chatmode: isProactive ? 'proactive' : 'interactive',
|
|
3596
|
-
|
|
4060
|
+
// 系统会话没有角色
|
|
4061
|
+
role: systemSessionMarker ? undefined : peerRole,
|
|
3597
4062
|
chatType: authChatType,
|
|
3598
4063
|
selfAid,
|
|
3599
4064
|
managedTempDir: sessionRuntimeDir,
|
|
4065
|
+
grantedReadRoots,
|
|
3600
4066
|
allowReadonlySourceDiagnostics: effectiveAgentConfig?.readonlySourceDiagnostics === true,
|
|
3601
4067
|
peerKey: authPeerKey,
|
|
3602
4068
|
causation: taskCausation,
|
|
@@ -3606,6 +4072,13 @@ export class ResponseEngine {
|
|
|
3606
4072
|
recordExecutionAnomaly,
|
|
3607
4073
|
pauseController: taskPauseController,
|
|
3608
4074
|
preToolUsePolicyHook: pureSessionPolicyHook,
|
|
4075
|
+
systemSessionCheck,
|
|
4076
|
+
...(systemSessionModule && systemSessionContext
|
|
4077
|
+
? {
|
|
4078
|
+
systemSessionGrants: systemSessionModule.grants(systemSessionContext),
|
|
4079
|
+
...(systemSessionSandbox ? { systemSessionSandbox } : {}),
|
|
4080
|
+
}
|
|
4081
|
+
: {}),
|
|
3609
4082
|
armApprovedDelegationCommand: this.agentDelegationRegistry
|
|
3610
4083
|
? (sessionToken, commandHash) => this.agentDelegationRegistry.armApprovedCommand({
|
|
3611
4084
|
sessionToken,
|
|
@@ -3614,6 +4087,11 @@ export class ResponseEngine {
|
|
|
3614
4087
|
commandHash,
|
|
3615
4088
|
})
|
|
3616
4089
|
: undefined,
|
|
4090
|
+
bindSessionCredential: this.agentDelegationRegistry
|
|
4091
|
+
? (sessionToken, threadId) => this.agentDelegationRegistry.registerSessionCredential({
|
|
4092
|
+
sessionToken, threadId, sessionId: session.id, taskId,
|
|
4093
|
+
})
|
|
4094
|
+
: undefined,
|
|
3617
4095
|
turn: {
|
|
3618
4096
|
sessionId: turnLease.sessionId,
|
|
3619
4097
|
taskId,
|
|
@@ -3797,7 +4275,15 @@ export class ResponseEngine {
|
|
|
3797
4275
|
const triggerPermissionModeOverride = message.triggerMeta?.permissionModeOverride;
|
|
3798
4276
|
const commandFullAccess = message.executionPermissionOverride;
|
|
3799
4277
|
const triggerFullAccess = triggerPermissionModeOverride === 'fullaccess';
|
|
3800
|
-
if (
|
|
4278
|
+
if (systemSessionMarker) {
|
|
4279
|
+
// 系统会话不走角色 / trigger override / fullaccess:runner 模式由模块固定
|
|
4280
|
+
if (commandFullAccess || triggerFullAccess) {
|
|
4281
|
+
throw new Error('系统会话不支持 fullaccess');
|
|
4282
|
+
}
|
|
4283
|
+
effectivePermissionMode = systemSessionModule
|
|
4284
|
+
.runnerLaunch(normalizedBaseagent.canonical, systemSessionContext).runnerMode;
|
|
4285
|
+
}
|
|
4286
|
+
else if (commandFullAccess || triggerFullAccess) {
|
|
3801
4287
|
if (commandFullAccess) {
|
|
3802
4288
|
if (commandFullAccess.permissionMode !== 'fullaccess'
|
|
3803
4289
|
|| commandFullAccess.processRole !== 'fullaccess-run'
|
|
@@ -4011,10 +4497,13 @@ export class ResponseEngine {
|
|
|
4011
4497
|
bootstrapAgentMdPath: agentMdPath(selfAid),
|
|
4012
4498
|
};
|
|
4013
4499
|
}
|
|
4500
|
+
// persona.md 是唯一的人格/交接文件(personal.md 已废弃并入)。
|
|
4501
|
+
// 该白名单让 owner 会话可直接用文件工具分段更新 `## Bootstrap 交接`,
|
|
4502
|
+
// 避免把长 Markdown 交接摘要塞进 `ec agent persona --text` 的 shell 转义。
|
|
4014
4503
|
if (selfAid && peerRole === 'owner' && effectivePermissionMode !== 'readonly') {
|
|
4015
4504
|
modelOverride = {
|
|
4016
4505
|
...(modelOverride || {}),
|
|
4017
|
-
personalMdPath: path.join(resolveRoot(), 'agents', selfAid, 'personal', '
|
|
4506
|
+
personalMdPath: path.join(resolveRoot(), 'agents', selfAid, 'personal', 'persona.md'),
|
|
4018
4507
|
};
|
|
4019
4508
|
}
|
|
4020
4509
|
if (normalizedBaseagent.canonical === 'claude') {
|
|
@@ -4181,7 +4670,8 @@ export class ResponseEngine {
|
|
|
4181
4670
|
peerId: peerIdRaw || undefined,
|
|
4182
4671
|
peerKey,
|
|
4183
4672
|
peerName: peerName || undefined,
|
|
4184
|
-
peerRole
|
|
4673
|
+
// 系统会话与角色体系无关:不给 peerRole,消息头就不渲染 `· role:` 段
|
|
4674
|
+
peerRole: systemSessionMarker ? undefined : peerRole,
|
|
4185
4675
|
// Display missing identity explicitly without changing the mode resolver's legacy fallback.
|
|
4186
4676
|
peerType: peerType || 'unknown',
|
|
4187
4677
|
sameDevice: message.sameDevice ?? false,
|
|
@@ -4258,9 +4748,12 @@ export class ResponseEngine {
|
|
|
4258
4748
|
// Bootstrap always uses the lifecycle-gated main manifest. A custom
|
|
4259
4749
|
// session manifest (for example the auxiliary manifest with `always`
|
|
4260
4750
|
// sections) must not reintroduce normal rules or command context.
|
|
4751
|
+
// 系统会话:manifest 由模块按 type/subtype 决定,不读 config
|
|
4261
4752
|
const manifestFile = isBootstrapping
|
|
4262
4753
|
? DEFAULT_MANIFEST_FILE
|
|
4263
|
-
:
|
|
4754
|
+
: systemSessionMarker
|
|
4755
|
+
? getSystemSessionModule(systemSessionMarker.type).manifest(systemSessionMarker.subtype)
|
|
4756
|
+
: resolveSessionManifest(sessionType, sessionManifests);
|
|
4264
4757
|
const kitContext = renderKitSections(kitCtx, manifestFile);
|
|
4265
4758
|
effectiveSystemPrompt = [options?.systemPromptAppend, kitContext].filter(Boolean).join('\n') || undefined;
|
|
4266
4759
|
// ── Stats: context_breakdown 旁路采集(各段估算 token 数,字符数/4 近似) ──
|
|
@@ -4312,7 +4805,7 @@ export class ResponseEngine {
|
|
|
4312
4805
|
} : {}),
|
|
4313
4806
|
peerId: message.peerId, peerName: peerName || undefined,
|
|
4314
4807
|
peerType: message.peerType,
|
|
4315
|
-
peerRole: message.batchRole || session.identity?.role || 'none',
|
|
4808
|
+
peerRole: systemSessionMarker ? undefined : (message.batchRole || session.identity?.role || 'none'),
|
|
4316
4809
|
sameDevice: message.sameDevice, sameNetwork: message.sameNetwork, sameEgressIp: message.sameEgressIp,
|
|
4317
4810
|
encrypted: message.encrypted,
|
|
4318
4811
|
content: message.content, timestamp: message.timestamp,
|
|
@@ -4495,7 +4988,8 @@ export class ResponseEngine {
|
|
|
4495
4988
|
peerKey,
|
|
4496
4989
|
peerName: peerName || undefined,
|
|
4497
4990
|
peerType: message.peerType || session.metadata?.peerType || undefined,
|
|
4498
|
-
|
|
4991
|
+
// 系统会话没有角色
|
|
4992
|
+
...(systemSessionMarker ? {} : { peerRole }),
|
|
4499
4993
|
// 会话原型与整理模式:ipc 侧靠 sessionType 拦非整理会话的写命令,
|
|
4500
4994
|
// 靠 memoryMode 校验 --scope 是否与本模式匹配。
|
|
4501
4995
|
sessionType: renderedSessionType,
|
|
@@ -4532,7 +5026,8 @@ export class ResponseEngine {
|
|
|
4532
5026
|
chatType: configChatType,
|
|
4533
5027
|
selfAid,
|
|
4534
5028
|
peerKey,
|
|
4535
|
-
|
|
5029
|
+
// 系统会话与角色体系无关:带标记、不签角色
|
|
5030
|
+
...(systemSessionMarker ? { systemSession: systemSessionMarker } : { issuedRole: peerRole }),
|
|
4536
5031
|
executionSource: fullAccessAuthorization?.source ?? (message.source === 'trigger' ? 'trigger' : 'user'),
|
|
4537
5032
|
permissionMode: effectivePermissionMode,
|
|
4538
5033
|
approvalInteractionPolicy,
|
|
@@ -4798,7 +5293,7 @@ export class ResponseEngine {
|
|
|
4798
5293
|
const candidateHint = fallbackCandidates.length === 0
|
|
4799
5294
|
? '未配置可用备用模型'
|
|
4800
5295
|
: '备用模型均不可用';
|
|
4801
|
-
throw markModelFallbackExhausted(new Error(`model_not_found: ${primaryModel ?? 'runner_default'}`), `当前模型 ${primaryModel ?? '默认模型'} 不可用,${candidateHint}。请稍后重试;管理员可在 agents/defaults.json 的 baseagents.${normalizedBaseagent.canonical}.fallbackModels
|
|
5296
|
+
throw markModelFallbackExhausted(new Error(`model_not_found: ${primaryModel ?? 'runner_default'}`), `当前模型 ${primaryModel ?? '默认模型'} 不可用,${candidateHint}。请稍后重试;管理员可在 agents/defaults.json 的 baseagents.${normalizedBaseagent.canonical}.fallbackModels 配置同类备用模型。`, retryError);
|
|
4802
5297
|
}
|
|
4803
5298
|
if (retryAttemptTimedOut || isRetryableError(retryError)) {
|
|
4804
5299
|
await recordRetryHealthError(retryError);
|
|
@@ -4926,7 +5421,7 @@ export class ResponseEngine {
|
|
|
4926
5421
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
4927
5422
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4928
5423
|
}
|
|
4929
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5424
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'replaced');
|
|
4930
5425
|
logger.info(`[ResponseEngine] Stale turn stopped before publish: session=${session.id} task=${taskId}`);
|
|
4931
5426
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation, {
|
|
4932
5427
|
expected: turnLease?.generation,
|
|
@@ -4942,7 +5437,7 @@ export class ResponseEngine {
|
|
|
4942
5437
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
4943
5438
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
4944
5439
|
}
|
|
4945
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5440
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'replaced');
|
|
4946
5441
|
logger.info(`[ResponseEngine] Stale turn stopped before protocol rejection: session=${session.id} task=${taskId}`);
|
|
4947
5442
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_generation', taskCausation, {
|
|
4948
5443
|
expected: turnLease?.generation,
|
|
@@ -5096,6 +5591,7 @@ export class ResponseEngine {
|
|
|
5096
5591
|
let sentErrorAsSystemError = false;
|
|
5097
5592
|
const finalReplyText = streamResult.lastReplyText || streamResult.fullText;
|
|
5098
5593
|
const visibleFinalReplyText = stripContextLimitErrorLines(finalReplyText);
|
|
5594
|
+
const streamRawErrorText = getStreamRawErrorText(streamResult, visibleFinalReplyText || undefined);
|
|
5099
5595
|
const isTriggerNoop = message.source === 'trigger'
|
|
5100
5596
|
&& typeof message.triggerMeta?.noopSentinel === 'string'
|
|
5101
5597
|
&& message.triggerMeta.noopSentinel.length > 0
|
|
@@ -5107,7 +5603,7 @@ export class ResponseEngine {
|
|
|
5107
5603
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5108
5604
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5109
5605
|
}
|
|
5110
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5606
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, 'replaced');
|
|
5111
5607
|
logger.info(`[ResponseEngine] Stale turn stopped before final flush: session=${session.id} task=${taskId}`);
|
|
5112
5608
|
await this.publishTriggerExecutionInterrupted(message, adapter, envelope, 'stale_turn', taskCausation);
|
|
5113
5609
|
return;
|
|
@@ -5128,10 +5624,14 @@ export class ResponseEngine {
|
|
|
5128
5624
|
if (streamResult.isError) {
|
|
5129
5625
|
// 用原始诊断文本(含错误码)判断可重试性,不用已过 stripContextLimitErrorLines 的展示文本
|
|
5130
5626
|
const recoverable = isRetryableError(new Error(getStreamErrorText(streamResult)));
|
|
5627
|
+
const subtype = normalizeCoreErrorSubtype(new Error(streamRawErrorText || getStreamErrorText(streamResult)), streamResult.subtype, streamResult.terminalReason);
|
|
5628
|
+
const reason = normalizeCoreErrorReason(streamResult.terminalReason);
|
|
5131
5629
|
await adapter.send(envelope, {
|
|
5132
5630
|
kind: 'system.error',
|
|
5133
5631
|
text: errorText,
|
|
5134
|
-
|
|
5632
|
+
...(streamRawErrorText ? { raw: streamRawErrorText } : {}),
|
|
5633
|
+
subtype,
|
|
5634
|
+
...(reason ? { reason } : {}),
|
|
5135
5635
|
recoverable,
|
|
5136
5636
|
}).catch(() => { });
|
|
5137
5637
|
sentErrorAsSystemError = true;
|
|
@@ -5148,7 +5648,7 @@ export class ResponseEngine {
|
|
|
5148
5648
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5149
5649
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5150
5650
|
}
|
|
5151
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
5651
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, streamResult.isError ? 'failed' : 'completed');
|
|
5152
5652
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared task=${taskId}`);
|
|
5153
5653
|
import('../../utils/msg-diag.js').then(m => m.diagLog('task_done', { aid: session.selfAID ?? '', msgId: message.messageId ?? '', taskId, text: message.content ?? '' })).catch(() => { });
|
|
5154
5654
|
// 降级模型回复末尾追加标记(代码层硬注入,不依赖模型输出)
|
|
@@ -5217,6 +5717,10 @@ export class ResponseEngine {
|
|
|
5217
5717
|
: getStreamErrorMessage(streamResult, false);
|
|
5218
5718
|
const rawSubtype = streamResult.subtype || 'agent_error';
|
|
5219
5719
|
const errorType = prefixErrorType(ERROR_PREFIX.AGENT, rawSubtype);
|
|
5720
|
+
const coreSubtype = normalizeCoreErrorSubtype(new Error(streamRawErrorText || errorSummary), rawSubtype, streamResult.terminalReason);
|
|
5721
|
+
const coreReason = permissionTerminalFailure
|
|
5722
|
+
? normalizeCoreErrorReason(permissionTerminalFailure.reason)
|
|
5723
|
+
: normalizeCoreErrorReason(streamResult.terminalReason);
|
|
5220
5724
|
// 用户主动打断(新消息//stop/撤回)会让 SDK 流在工具调用中途被掐断,
|
|
5221
5725
|
// 末尾 result message 形状异常并被标记为 error(含 SDK 内部 ede_diagnostic 串)。
|
|
5222
5726
|
// 这不是真正的失败,不应把诊断串暴露给用户,也不计入错误统计。
|
|
@@ -5241,12 +5745,24 @@ export class ResponseEngine {
|
|
|
5241
5745
|
if (this.isTrustedDaemonTrigger(message)) {
|
|
5242
5746
|
await adapter.send(envelope, { kind: 'status.error', metadata: terminalMetadata }).catch(() => { });
|
|
5243
5747
|
if (!sentErrorAsSystemError) {
|
|
5244
|
-
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(() => { });
|
|
5245
5755
|
}
|
|
5246
5756
|
}
|
|
5247
5757
|
else {
|
|
5248
5758
|
if (!sentErrorAsSystemError) {
|
|
5249
|
-
await adapter.send(envelope, {
|
|
5759
|
+
await adapter.send(envelope, {
|
|
5760
|
+
kind: 'result.error',
|
|
5761
|
+
text: userErrorSummary,
|
|
5762
|
+
...(streamRawErrorText ? { raw: streamRawErrorText } : {}),
|
|
5763
|
+
subtype: coreSubtype,
|
|
5764
|
+
...(coreReason ? { reason: coreReason } : {}),
|
|
5765
|
+
}).catch(() => { });
|
|
5250
5766
|
}
|
|
5251
5767
|
await adapter.send(envelope, { kind: 'status.error', metadata: terminalMetadata }).catch(() => { });
|
|
5252
5768
|
}
|
|
@@ -5592,7 +6108,7 @@ export class ResponseEngine {
|
|
|
5592
6108
|
this.activeTaskRuntimeContexts.delete(session.id);
|
|
5593
6109
|
this.activeRunnerModels.delete(session.id); // F1修复
|
|
5594
6110
|
}
|
|
5595
|
-
this.agentDelegationRegistry?.revokeTask(session.id, taskId);
|
|
6111
|
+
this.agentDelegationRegistry?.revokeTask(session.id, taskId, this.interruptedSessions.has(session.id) ? 'interrupted' : 'failed');
|
|
5596
6112
|
logger.info(`[ResponseEngine] session ${session.id} processing cleared (on error) task=${taskId}`);
|
|
5597
6113
|
}
|
|
5598
6114
|
catch { }
|
|
@@ -5739,10 +6255,29 @@ export class ResponseEngine {
|
|
|
5739
6255
|
sessionId: session.id,
|
|
5740
6256
|
metadata: withTaskMetadata(sendOpts?.metadata),
|
|
5741
6257
|
};
|
|
6258
|
+
const errorReason = isExecutionFailure(error)
|
|
6259
|
+
? normalizeCoreErrorReason(error.reason)
|
|
6260
|
+
: retryExhaustedCount
|
|
6261
|
+
? 'retry_exhausted'
|
|
6262
|
+
: retryInputAlreadySubmitted
|
|
6263
|
+
? 'input_already_submitted'
|
|
6264
|
+
: modelFallbackExhaustedMessage
|
|
6265
|
+
? 'fallback_exhausted'
|
|
6266
|
+
: isTotalExecutionTimeout || isTimeout
|
|
6267
|
+
? 'timeout'
|
|
6268
|
+
: undefined;
|
|
6269
|
+
const errorSubtype = error instanceof OrphanedTargetSessionError
|
|
6270
|
+
? ErrorType.SESSION_UNAVAILABLE
|
|
6271
|
+
: modelFallbackExhaustedMessage
|
|
6272
|
+
? ErrorType.MODEL_UNAVAILABLE
|
|
6273
|
+
: normalizeCoreErrorSubtype(error, undefined, isTotalExecutionTimeout || isTimeout ? 'timeout' : undefined);
|
|
6274
|
+
const rawErrorText = getErrorRawText(error);
|
|
5742
6275
|
const errorPayload = {
|
|
5743
6276
|
kind: 'result.error',
|
|
5744
6277
|
text: userMessage,
|
|
5745
|
-
|
|
6278
|
+
subtype: errorSubtype,
|
|
6279
|
+
...(rawErrorText ? { raw: rawErrorText } : {}),
|
|
6280
|
+
...(errorReason ? { reason: errorReason } : {}),
|
|
5746
6281
|
};
|
|
5747
6282
|
if (!isUserInterrupt && daemonTrigger && procStatus === 'error') {
|
|
5748
6283
|
// DaemonChannel settles a trigger on the first terminal payload. Put
|
|
@@ -6178,10 +6713,11 @@ export class ResponseEngine {
|
|
|
6178
6713
|
if (event.type === 'complete') {
|
|
6179
6714
|
event = normalizeCompleteAgentEvent(event);
|
|
6180
6715
|
}
|
|
6181
|
-
if (event.type === 'tool_result' && event
|
|
6716
|
+
if (event.type === 'tool_result' && isToolResultFailure(event)) {
|
|
6182
6717
|
const classification = normalizeToolErrorClassification(event);
|
|
6183
6718
|
event = {
|
|
6184
6719
|
...event,
|
|
6720
|
+
isError: true,
|
|
6185
6721
|
errorCode: classification.errorCode,
|
|
6186
6722
|
errorDetailCode: classification.errorDetailCode,
|
|
6187
6723
|
classificationMissing: classification.classificationMissing || event.classificationMissing,
|
|
@@ -6327,7 +6863,20 @@ export class ResponseEngine {
|
|
|
6327
6863
|
logger.info(`[ResponseEngine] Event: type=${event.type}${eventDetail}`);
|
|
6328
6864
|
}
|
|
6329
6865
|
// IMRenderer 旁路:proactive 模式逐事件投影为 thought(fire-and-forget)
|
|
6330
|
-
|
|
6866
|
+
let diagnosticSuffix = '';
|
|
6867
|
+
let projectedEvent = event;
|
|
6868
|
+
if (event.type === 'tool_result' && isToolResultFailure(event)) {
|
|
6869
|
+
const byCallId = event.callId ? toolInputByCallId.get(event.callId) : undefined;
|
|
6870
|
+
const byCorrelationId = event.correlationId
|
|
6871
|
+
? toolInputByCorrelationId.get(event.correlationId)
|
|
6872
|
+
: undefined;
|
|
6873
|
+
const original = event.callId && event.correlationId
|
|
6874
|
+
? byCallId === byCorrelationId ? byCallId : undefined
|
|
6875
|
+
: byCallId ?? byCorrelationId;
|
|
6876
|
+
diagnosticSuffix = formatToolInputDiagnostics(diagnoseToolInputAfterFailure(original, event));
|
|
6877
|
+
projectedEvent = { ...event, error: getToolResultErrorText(event) + diagnosticSuffix };
|
|
6878
|
+
}
|
|
6879
|
+
renderer.emit(projectedEvent);
|
|
6331
6880
|
if (event.type === 'text_start') {
|
|
6332
6881
|
await renderer.sendTextStart(event.contentIndex, event.turn);
|
|
6333
6882
|
continue;
|
|
@@ -6751,15 +7300,14 @@ export class ResponseEngine {
|
|
|
6751
7300
|
}
|
|
6752
7301
|
// 从 tool_use 阶段缓存的描述中回溯
|
|
6753
7302
|
const cachedDesc = event.callId ? toolDescByCallId.get(event.callId) : undefined;
|
|
6754
|
-
|
|
7303
|
+
const toolResultFailed = isToolResultFailure(event);
|
|
7304
|
+
if (toolResultFailed && !shouldSuppress()) {
|
|
6755
7305
|
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);
|
|
7306
|
+
const errorMsg = getToolResultErrorText(event) + diagnosticSuffix;
|
|
7307
|
+
renderer.addToolResult(event.name || '\u5de5\u5177', false, event.result, errorMsg, event.callId, event.durationMs, cachedDesc, event, event.assistantUuid);
|
|
6760
7308
|
}
|
|
6761
|
-
else if (!
|
|
6762
|
-
renderer.addToolResult(event.name || '\u5de5\u5177', true, event.result, undefined, event.callId, event.durationMs, cachedDesc,
|
|
7309
|
+
else if (!toolResultFailed && !shouldSuppress()) {
|
|
7310
|
+
renderer.addToolResult(event.name || '\u5de5\u5177', true, event.result, undefined, event.callId, event.durationMs, cachedDesc, event, event.assistantUuid);
|
|
6763
7311
|
}
|
|
6764
7312
|
}
|
|
6765
7313
|
// 非终态运行时告警走 status notify,不进入 activity/thought 历史。
|