evolcore 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -1
- package/README.md +60 -9
- package/bin/install-codex-managed-hooks.mjs +61 -0
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +383 -111
- package/dist/agents/codex-app-server-client.js +10 -2
- package/dist/agents/codex-runner.js +405 -137
- package/dist/agents/ecagent-runner.js +174 -63
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +66 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +86 -9
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +26 -11
- package/dist/aun/outbox.js +295 -68
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +1004 -273
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +81 -121
- package/dist/cli/index.js +1 -0
- package/dist/cli/init.js +76 -24
- package/dist/cli/restart-monitor.js +3 -3
- package/dist/cli/task-context.js +46 -0
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +16 -5
- package/dist/config/config-manager.js +226 -24
- package/dist/config/config-operation-service.js +1 -2
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +29 -16
- package/dist/config/lifecycle.js +16 -5
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +161 -12
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +31 -1
- package/dist/core/auth/auth-gateway.js +33 -4
- package/dist/core/auth/authorization-audit.js +155 -4
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +2 -2
- package/dist/core/bootstrap-service.js +27 -38
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/channel-loader.js +0 -2
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +290 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +279 -80
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +350 -32
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +9 -4
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +10 -0
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +83 -84
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +124 -10
- package/dist/core/message/message-log.js +14 -7
- package/dist/core/message/message-queue.js +206 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +495 -72
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +465 -56
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +205 -13
- package/dist/core/permission/tool-policy.js +673 -62
- package/dist/core/relation/peer-identity.js +18 -0
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +299 -30
- package/dist/core/session/session-renew.js +19 -12
- package/dist/core/session/session-turn-coordinator.js +11 -4
- package/dist/eck/kit-renderer.js +18 -9
- package/dist/index.js +283 -65
- package/dist/ipc.js +374 -24
- package/dist/paths.js +64 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +56 -5
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +3 -2
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +21 -2
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/dist/utils/stats.js +52 -18
- package/dist/utils/welcome.js +2 -2
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/rules/01-overview.md +1 -1
- package/kits/rules/03-identity.md +1 -1
- package/kits/rules/05-venue.md +1 -1
- package/kits/schemas/_meta.json +10 -5
- package/kits/schemas/agent-config.schema.10.json +2 -1
- package/kits/schemas/agent-config.schema.11.json +421 -0
- package/kits/schemas/daemon.schema.5.json +135 -0
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +119 -0
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/package.json +7 -3
- package/skills/eclink/SKILL.md +2 -0
|
@@ -5,7 +5,7 @@ import { resolveAnthropicConfig } from './baseagent.js';
|
|
|
5
5
|
import { buildModelRequestHeaders } from './request-identity.js';
|
|
6
6
|
import { DEFAULT_PERMISSION_MODE } from '../types.js';
|
|
7
7
|
import { renderActionAsText } from '../core/interaction-router.js';
|
|
8
|
-
import { buildEnvelope, sendInteractionPayload } from '../core/message/message-utils.js';
|
|
8
|
+
import { buildEnvelope, isInteractionSendAccepted, sendInteractionPayload } from '../core/message/message-utils.js';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import os from 'os';
|
|
@@ -21,12 +21,13 @@ import { getPackageRoot, resolvePaths } from '../paths.js';
|
|
|
21
21
|
import { resolveEffective } from '../config/config-manager.js';
|
|
22
22
|
import { sanitizeSessionTitle } from '../core/session/session-title.js';
|
|
23
23
|
import { resolveClaudeCapabilityRunOptionsForProject } from '../core/capability/capability-manager.js';
|
|
24
|
-
import { normalizePermissionMode, resolvePhaseOneExecutionSandbox } from '../core/permission/mode.js';
|
|
25
|
-
import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClassReference, isHClassPath, isLClassPath, isSameOrDescendant, resolveProtectedCandidate, } from '../core/protected-paths.js';
|
|
26
|
-
import { buildHClassGuardCommand, createSandboxInitializationError, getActiveSandboxInitializationFailure, isSandboxInitializationFailure, recordSandboxInitializationFailure, SANDBOX_INITIALIZATION_COOLDOWN_MS, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, shouldFailIfClaudeSandboxUnavailable, shouldLogSandboxCircuitNotice, } from '../core/permission/sandbox-runtime.js';
|
|
24
|
+
import { normalizeExecutionPermissionMode, normalizePermissionMode, resolvePhaseOneExecutionSandbox } from '../core/permission/mode.js';
|
|
25
|
+
import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClassReference, isHClassPath, isLClassPath, isSameOrDescendant, resolveProtectedCandidate, } from '../core/permission/protected-paths.js';
|
|
26
|
+
import { buildHClassGuardCommand, collectSandboxGuardDiagnostics, createSandboxInitializationError, getActiveSandboxInitializationFailure, isSandboxInitializationFailure, recordSandboxInitializationFailure, SANDBOX_INITIALIZATION_COOLDOWN_MS, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, summarizeBubblewrapArgsForLog, summarizeSandboxGuardDiagnostics, shouldFailIfClaudeSandboxUnavailable, shouldLogSandboxCircuitNotice, } from '../core/permission/sandbox-runtime.js';
|
|
27
27
|
import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-policy.js';
|
|
28
28
|
import { auditToolInfrastructureFailure, auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
|
|
29
|
-
import { isManagedSessionRuntimeDir } from '../cli/task-context.js';
|
|
29
|
+
import { getManagedTaskTempDir, isManagedSessionRuntimeDir } from '../cli/task-context.js';
|
|
30
|
+
import { hasTrustedFullAccessContext } from './runner-types.js';
|
|
30
31
|
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel } from './runner-types.js';
|
|
31
32
|
export { hasCompact, hasModelSwitcher, hasPermissionController } from './runner-types.js';
|
|
32
33
|
// Built-in tools execute inside the Claude runtime and are covered by the
|
|
@@ -65,10 +66,18 @@ const CLAUDE_BUILTIN_TOOLS = new Set([
|
|
|
65
66
|
'Write',
|
|
66
67
|
]);
|
|
67
68
|
function spawnClaudeWithHClassGuard(options, root, onStderr) {
|
|
69
|
+
const preflight = collectSandboxGuardDiagnostics(root);
|
|
70
|
+
logger.info(`[ClaudeSandbox] H-class guard preflight ${JSON.stringify(summarizeSandboxGuardDiagnostics(preflight))}`);
|
|
68
71
|
const guarded = buildHClassGuardCommand(options.command, options.args, root);
|
|
69
72
|
if (!guarded) {
|
|
70
73
|
throw new Error('Claude owner bypass 缺少 EvolCore H 类路径隔离运行时,已拒绝启动');
|
|
71
74
|
}
|
|
75
|
+
const launch = summarizeBubblewrapArgsForLog(guarded.args);
|
|
76
|
+
const postBuild = collectSandboxGuardDiagnostics(root);
|
|
77
|
+
logger.info(`[ClaudeSandbox] H-class guard launch ${JSON.stringify({
|
|
78
|
+
...launch,
|
|
79
|
+
diagnostics: summarizeSandboxGuardDiagnostics(postBuild),
|
|
80
|
+
})}`);
|
|
72
81
|
const child = spawn(guarded.command, guarded.args, {
|
|
73
82
|
cwd: options.cwd,
|
|
74
83
|
env: options.env,
|
|
@@ -111,23 +120,6 @@ function stableClaudePermissionValue(value, depth = 0) {
|
|
|
111
120
|
.sort()
|
|
112
121
|
.map(key => [key, stableClaudePermissionValue(record[key], depth + 1)]));
|
|
113
122
|
}
|
|
114
|
-
function collectClaudePermissionStrings(value, output = []) {
|
|
115
|
-
if (typeof value === 'string') {
|
|
116
|
-
output.push(value);
|
|
117
|
-
return output;
|
|
118
|
-
}
|
|
119
|
-
if (Array.isArray(value)) {
|
|
120
|
-
for (const entry of value)
|
|
121
|
-
collectClaudePermissionStrings(entry, output);
|
|
122
|
-
return output;
|
|
123
|
-
}
|
|
124
|
-
if (!value || typeof value !== 'object')
|
|
125
|
-
return output;
|
|
126
|
-
for (const entry of Object.values(value)) {
|
|
127
|
-
collectClaudePermissionStrings(entry, output);
|
|
128
|
-
}
|
|
129
|
-
return output;
|
|
130
|
-
}
|
|
131
123
|
function inspectClaudePermissionExpansion(input, options, projectPath) {
|
|
132
124
|
const blockedPath = typeof options.blockedPath === 'string' && options.blockedPath.length > 0
|
|
133
125
|
? options.blockedPath
|
|
@@ -161,9 +153,6 @@ function inspectClaudePermissionExpansion(input, options, projectPath) {
|
|
|
161
153
|
break;
|
|
162
154
|
}
|
|
163
155
|
}
|
|
164
|
-
if (!protectedReason && collectClaudePermissionStrings(rawSuggestions).some(containsHClassReference)) {
|
|
165
|
-
protectedReason = '🔒 Claude SDK 权限规则涉及 EvolCore H 类受保护路径,已拒绝';
|
|
166
|
-
}
|
|
167
156
|
const hasDirectoryExpansion = rawSuggestions.some(entry => {
|
|
168
157
|
if (!entry || typeof entry !== 'object' || Array.isArray(entry))
|
|
169
158
|
return false;
|
|
@@ -600,7 +589,7 @@ class MessageStream {
|
|
|
600
589
|
}
|
|
601
590
|
export class AgentRunner {
|
|
602
591
|
name = 'claude';
|
|
603
|
-
capabilities = { clear: true, compact: true, fork: true, forkAtTurn: true, askUserQuestion: true, planApproval: true, fileRewind: 'checkpoint' };
|
|
592
|
+
capabilities = { fullaccess: true, clear: true, compact: true, fork: true, forkAtTurn: true, askUserQuestion: true, planApproval: true, fileRewind: 'checkpoint' };
|
|
604
593
|
apiKey;
|
|
605
594
|
model;
|
|
606
595
|
effort;
|
|
@@ -609,6 +598,8 @@ export class AgentRunner {
|
|
|
609
598
|
customHeaders;
|
|
610
599
|
customQueryParams;
|
|
611
600
|
config;
|
|
601
|
+
agentAid;
|
|
602
|
+
agentConfig;
|
|
612
603
|
activeSessions = new Map();
|
|
613
604
|
activeStreams = new Map();
|
|
614
605
|
activeMessageStreams = new Map();
|
|
@@ -636,6 +627,7 @@ export class AgentRunner {
|
|
|
636
627
|
reason: detail.message.slice(0, 512),
|
|
637
628
|
sessionId,
|
|
638
629
|
agentAid: context?.selfAid,
|
|
630
|
+
agentName: context?.agentName,
|
|
639
631
|
permissionMode,
|
|
640
632
|
role: context?.role,
|
|
641
633
|
callId: inputId,
|
|
@@ -654,6 +646,7 @@ export class AgentRunner {
|
|
|
654
646
|
policyCode: SANDBOX_INITIALIZATION_FAILED,
|
|
655
647
|
decisionSource: 'infrastructure',
|
|
656
648
|
agentAid: context?.selfAid,
|
|
649
|
+
agentName: context?.agentName,
|
|
657
650
|
permissionMode: permissionMode,
|
|
658
651
|
summary: detail.message.slice(0, 512),
|
|
659
652
|
effect: 'operation_skipped',
|
|
@@ -687,12 +680,17 @@ export class AgentRunner {
|
|
|
687
680
|
this.cleanupSandboxSettingsFile(sessionId, filePath);
|
|
688
681
|
}
|
|
689
682
|
}
|
|
690
|
-
constructor(apiKey, model, onSessionIdUpdate, baseUrl, config) {
|
|
683
|
+
constructor(apiKey, model, onSessionIdUpdate, baseUrl, config, runtime) {
|
|
684
|
+
// Compatibility for legacy direct callers that still pass the removed
|
|
685
|
+
// internal fields in a synthetic config. AgentLoader uses runtime binding.
|
|
686
|
+
const legacy = config?.agents?.claude;
|
|
691
687
|
this.apiKey = apiKey;
|
|
692
688
|
this.model = model || 'sonnet';
|
|
693
689
|
this.effort = undefined;
|
|
694
690
|
this.baseUrl = baseUrl;
|
|
695
691
|
this.config = config;
|
|
692
|
+
this.agentAid = runtime?.agentAid ?? legacy?.['evolcoreAgentAid'];
|
|
693
|
+
this.agentConfig = runtime?.agentConfig ?? legacy?.['evolcoreAgentConfig'];
|
|
696
694
|
this.onSessionIdUpdate = onSessionIdUpdate;
|
|
697
695
|
if (config) {
|
|
698
696
|
const anthropic = resolveAnthropicConfig(config);
|
|
@@ -700,7 +698,7 @@ export class AgentRunner {
|
|
|
700
698
|
this.customHeaders = buildModelRequestHeaders({
|
|
701
699
|
baseagent: 'claude',
|
|
702
700
|
baseUrl: anthropic.baseUrl,
|
|
703
|
-
agentAid:
|
|
701
|
+
agentAid: this.agentAid,
|
|
704
702
|
configuredHeaders: anthropic.headers,
|
|
705
703
|
});
|
|
706
704
|
this.customQueryParams = anthropic.queryParams;
|
|
@@ -744,11 +742,10 @@ export class AgentRunner {
|
|
|
744
742
|
return entry?.cache;
|
|
745
743
|
}
|
|
746
744
|
async resolveCapabilityRunOptions(projectPath) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
if (claudeConfig?.evolcoreAgentAid) {
|
|
745
|
+
let agentConfig = this.agentConfig;
|
|
746
|
+
if (this.agentAid) {
|
|
750
747
|
try {
|
|
751
|
-
agentConfig = resolveEffective({ self:
|
|
748
|
+
agentConfig = resolveEffective({ self: this.agentAid }, { cache: true, expand: true });
|
|
752
749
|
}
|
|
753
750
|
catch { }
|
|
754
751
|
}
|
|
@@ -826,14 +823,16 @@ export class AgentRunner {
|
|
|
826
823
|
this.permissionContexts.set(sessionId, context);
|
|
827
824
|
}
|
|
828
825
|
toSdkPermissionMode(mode) {
|
|
826
|
+
if (mode === 'fullaccess')
|
|
827
|
+
return 'bypassPermissions';
|
|
829
828
|
const normalized = normalizePermissionMode(mode ?? this.permissionMode);
|
|
830
829
|
// Public permission policy is enforced by PreToolUse. Keeping the SDK in
|
|
831
830
|
// default mode preserves canUseTool reachability for request mode.
|
|
832
831
|
return normalized.workflow === 'plan' ? 'plan' : 'default';
|
|
833
832
|
}
|
|
834
833
|
// ── Compactable 接口 ──
|
|
835
|
-
async compact(sessionId, agentSessionId, projectPath) {
|
|
836
|
-
return this.compactSession(sessionId, agentSessionId, projectPath);
|
|
834
|
+
async compact(sessionId, agentSessionId, projectPath, modelOverride) {
|
|
835
|
+
return this.compactSession(sessionId, agentSessionId, projectPath, modelOverride);
|
|
837
836
|
}
|
|
838
837
|
syncFromUserSettings() {
|
|
839
838
|
try {
|
|
@@ -972,7 +971,7 @@ export class AgentRunner {
|
|
|
972
971
|
: '回复 /ask 1,或 /ask <自定义内容>';
|
|
973
972
|
const fallbackText = `💬 ${q.header || q.question}\n${q.header ? q.question + '\n' : ''}${optionLines}\n\n${answerHint}`;
|
|
974
973
|
const result = await sendInteractionPayload(permCtx.adapter, envelope, interaction, fallbackText, permCtx.replyContext);
|
|
975
|
-
cardSent =
|
|
974
|
+
cardSent = isInteractionSendAccepted(result);
|
|
976
975
|
}
|
|
977
976
|
catch (err) {
|
|
978
977
|
logger.warn(`[AgentRunner] AskUserQuestion card send failed for q${i}:`, err);
|
|
@@ -1393,7 +1392,7 @@ export class AgentRunner {
|
|
|
1393
1392
|
});
|
|
1394
1393
|
const fallbackText = '📋 计划审批:AI 已完成规划,等待审批。\n回复 /ask 1 批准 / /ask 2 拒绝';
|
|
1395
1394
|
const result = await sendInteractionPayload(permCtx.adapter, envelope, interaction, fallbackText, permCtx.replyContext);
|
|
1396
|
-
cardSent =
|
|
1395
|
+
cardSent = isInteractionSendAccepted(result);
|
|
1397
1396
|
}
|
|
1398
1397
|
catch (err) {
|
|
1399
1398
|
logger.warn('[AgentRunner] ExitPlanMode card send failed:', err);
|
|
@@ -1441,7 +1440,7 @@ export class AgentRunner {
|
|
|
1441
1440
|
* SDK 原始事件 → 标准 AgentEvent 转换
|
|
1442
1441
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
1443
1442
|
*/
|
|
1444
|
-
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode) {
|
|
1443
|
+
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode, turn) {
|
|
1445
1444
|
let lastSessionId;
|
|
1446
1445
|
let hasTurnActivity = false;
|
|
1447
1446
|
let ignoredPreTurnResult = false;
|
|
@@ -1458,6 +1457,7 @@ export class AgentRunner {
|
|
|
1458
1457
|
let turnCount = 0;
|
|
1459
1458
|
const seenMessageIds = new Set();
|
|
1460
1459
|
let lastModelCall;
|
|
1460
|
+
let lastAssistantWasSynthetic = false;
|
|
1461
1461
|
let lastAssistantUuid;
|
|
1462
1462
|
// 流式收集各次大模型调用(fallback:SDK iterations 为空时使用)
|
|
1463
1463
|
const collectedCalls = [];
|
|
@@ -1466,8 +1466,15 @@ export class AgentRunner {
|
|
|
1466
1466
|
// 提取 session_id(任意 SDK 事件都可能携带)
|
|
1467
1467
|
if (event.session_id && event.session_id !== lastSessionId) {
|
|
1468
1468
|
lastSessionId = event.session_id;
|
|
1469
|
-
this.
|
|
1470
|
-
|
|
1469
|
+
if (await this.acceptDiscoveredSessionId(sessionId, event.session_id, turn)) {
|
|
1470
|
+
yield { type: 'session_id', sessionId: event.session_id };
|
|
1471
|
+
}
|
|
1472
|
+
else {
|
|
1473
|
+
logger.warn(`[AgentRunner] Rejected stale backend discovery: sessionId=${sessionId} backend=${event.session_id} turn=${turn?.turnId ?? 'legacy'}`);
|
|
1474
|
+
const error = new Error('backend discovery rejected by active turn lease');
|
|
1475
|
+
error.code = 'TOPIC_BACKEND_DISCOVERY_REJECTED';
|
|
1476
|
+
throw error;
|
|
1477
|
+
}
|
|
1471
1478
|
}
|
|
1472
1479
|
if (event.type === 'user' && event.uuid === inputId) {
|
|
1473
1480
|
hasTurnActivity = true;
|
|
@@ -1563,7 +1570,9 @@ export class AgentRunner {
|
|
|
1563
1570
|
const streamEvent = event.event;
|
|
1564
1571
|
if (streamEvent?.type === 'message_start' && streamEvent.message?.usage) {
|
|
1565
1572
|
hasTurnActivity = true;
|
|
1573
|
+
lastAssistantWasSynthetic = streamEvent.message.model === '<synthetic>';
|
|
1566
1574
|
lastModelCall = {
|
|
1575
|
+
messageId: streamEvent.message.id,
|
|
1567
1576
|
uuid: event.uuid,
|
|
1568
1577
|
model: streamEvent.message.model,
|
|
1569
1578
|
tokenUsage: streamEvent.message.usage,
|
|
@@ -1573,6 +1582,7 @@ export class AgentRunner {
|
|
|
1573
1582
|
call_index: collectedCalls.length,
|
|
1574
1583
|
model: streamEvent.message.model ?? callModel ?? this.model,
|
|
1575
1584
|
request_id: event.request_id,
|
|
1585
|
+
message_id: streamEvent.message.id,
|
|
1576
1586
|
tokenUsage: { ...streamEvent.message.usage },
|
|
1577
1587
|
});
|
|
1578
1588
|
}
|
|
@@ -1625,27 +1635,40 @@ export class AgentRunner {
|
|
|
1625
1635
|
yield { type: 'state_changed', state: event.state };
|
|
1626
1636
|
}
|
|
1627
1637
|
// assistant: 提取 tool_use 和文本(仅无 text_delta 时提取文本)
|
|
1628
|
-
if (event.type === 'assistant'
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
if (
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
+
if (event.type === 'assistant') {
|
|
1639
|
+
const isSyntheticAssistant = event.message?.model === '<synthetic>'
|
|
1640
|
+
|| (typeof event.error === 'string' && event.isApiErrorMessage === true);
|
|
1641
|
+
lastAssistantWasSynthetic = isSyntheticAssistant;
|
|
1642
|
+
if (event.message && (event.message.usage || isSyntheticAssistant)) {
|
|
1643
|
+
// A message id is stable across the partial stream and the final
|
|
1644
|
+
// assistant event. Never merge a synthetic/API-error assistant into
|
|
1645
|
+
// the preceding real call, even if an upstream adapter reuses an id
|
|
1646
|
+
// or omits the usage object on the error frame.
|
|
1647
|
+
const sameModelCall = !isSyntheticAssistant
|
|
1648
|
+
&& typeof event.message.id === 'string'
|
|
1649
|
+
&& event.message.id === lastModelCall?.messageId;
|
|
1638
1650
|
lastModelCall = {
|
|
1639
|
-
...lastModelCall,
|
|
1651
|
+
...(sameModelCall ? lastModelCall : undefined),
|
|
1640
1652
|
messageId: event.message.id,
|
|
1653
|
+
uuid: event.uuid,
|
|
1641
1654
|
requestId: event.request_id,
|
|
1642
1655
|
model: event.message.model,
|
|
1643
|
-
tokenUsage: {
|
|
1656
|
+
tokenUsage: sameModelCall ? {
|
|
1644
1657
|
...event.message.usage,
|
|
1645
1658
|
...(lastModelCall?.tokenUsage ?? {}),
|
|
1646
|
-
},
|
|
1659
|
+
} : { ...(event.message.usage ?? {}) },
|
|
1647
1660
|
};
|
|
1648
1661
|
}
|
|
1662
|
+
if (!event.message?.content)
|
|
1663
|
+
continue;
|
|
1664
|
+
hasTurnActivity = true;
|
|
1665
|
+
lastAssistantUuid = event.uuid ?? lastAssistantUuid;
|
|
1666
|
+
const msgId = event.message.id;
|
|
1667
|
+
if (!msgId || !seenMessageIds.has(msgId)) {
|
|
1668
|
+
if (msgId)
|
|
1669
|
+
seenMessageIds.add(msgId);
|
|
1670
|
+
turnCount++;
|
|
1671
|
+
}
|
|
1649
1672
|
// 统计本轮 base agent 全部输出字符数(text + tool_use input)
|
|
1650
1673
|
let turnOutputChars = 0;
|
|
1651
1674
|
for (const content of event.message.content) {
|
|
@@ -1720,6 +1743,18 @@ export class AgentRunner {
|
|
|
1720
1743
|
// 非 Claude(DeepSeek/OpenAI 兼容):cache_read 是服务端 KV cache 不占上下文窗口,
|
|
1721
1744
|
// input_tokens 本身就是完整的上下文输入量。
|
|
1722
1745
|
const u = event.usage;
|
|
1746
|
+
// Claude Code represents transport/API failures as a fresh
|
|
1747
|
+
// `<synthetic>` assistant message. Its usage describes the failed
|
|
1748
|
+
// terminal request (normally zero); event.usage remains the aggregate
|
|
1749
|
+
// of earlier successful calls in the query and must not be presented
|
|
1750
|
+
// as the failed request's context usage.
|
|
1751
|
+
const isSyntheticError = event.is_error === true
|
|
1752
|
+
&& (lastAssistantWasSynthetic || lastModelCall?.model === '<synthetic>');
|
|
1753
|
+
const syntheticUsage = lastModelCall?.tokenUsage ?? {
|
|
1754
|
+
input_tokens: 0,
|
|
1755
|
+
output_tokens: 0,
|
|
1756
|
+
};
|
|
1757
|
+
const terminalUsage = isSyntheticError ? syntheticUsage : u;
|
|
1723
1758
|
const effectiveModel = callModel ?? this.model;
|
|
1724
1759
|
const isClaudeModel = isClaudeContextUsageModel(effectiveModel);
|
|
1725
1760
|
const totalTokens = contextTokensForUsage(u, !!isClaudeModel);
|
|
@@ -1762,7 +1797,11 @@ export class AgentRunner {
|
|
|
1762
1797
|
// 组装 modelCalls:优先 SDK iterations,fallback 流式收集,兜底降级单行。
|
|
1763
1798
|
const callModel_ = callModel ?? this.model;
|
|
1764
1799
|
let modelCalls;
|
|
1765
|
-
const iterArr =
|
|
1800
|
+
const iterArr = !isSyntheticError
|
|
1801
|
+
&& Array.isArray(u?.iterations)
|
|
1802
|
+
&& u.iterations.length > 0
|
|
1803
|
+
? u.iterations
|
|
1804
|
+
: null;
|
|
1766
1805
|
if (iterArr) {
|
|
1767
1806
|
modelCalls = iterArr.map((it, i) => ({
|
|
1768
1807
|
call_index: i, model: callModel_, tokenUsage: it, contextUsage: contextUsageForCall(it),
|
|
@@ -1774,16 +1813,19 @@ export class AgentRunner {
|
|
|
1774
1813
|
contextUsage: contextUsageForCall(call.tokenUsage),
|
|
1775
1814
|
}));
|
|
1776
1815
|
}
|
|
1777
|
-
else if (u) {
|
|
1816
|
+
else if (u && !isSyntheticError) {
|
|
1778
1817
|
// 降级:无逐次数据,写一条累计行
|
|
1779
1818
|
modelCalls = [{ call_index: 0, model: callModel_, tokenUsage: u, contextUsage: contextUsageForCall(u), degraded: true }];
|
|
1780
1819
|
}
|
|
1781
1820
|
// `event.usage` is aggregate query usage when the SDK made multiple
|
|
1782
1821
|
// model calls. The top-level context field must describe a single
|
|
1783
|
-
// current request
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1822
|
+
// current request. For normal results aggregate usage remains available
|
|
1823
|
+
// in tokenUsage; synthetic errors use their own terminal usage instead.
|
|
1824
|
+
const contextUsage = isSyntheticError
|
|
1825
|
+
? lastModelCall?.contextUsage
|
|
1826
|
+
: lastModelCall?.contextUsage
|
|
1827
|
+
?? modelCalls?.at(-1)?.contextUsage
|
|
1828
|
+
?? aggregateContextUsage;
|
|
1787
1829
|
const completeEvent = {
|
|
1788
1830
|
type: 'complete',
|
|
1789
1831
|
result: cleanResult,
|
|
@@ -1796,7 +1838,7 @@ export class AgentRunner {
|
|
|
1796
1838
|
terminalReason: event.terminal_reason,
|
|
1797
1839
|
sessionTitle: event.session_title,
|
|
1798
1840
|
numTurns: event.num_turns,
|
|
1799
|
-
tokenUsage:
|
|
1841
|
+
tokenUsage: terminalUsage,
|
|
1800
1842
|
contextUsage,
|
|
1801
1843
|
lastModelCall,
|
|
1802
1844
|
modelCalls,
|
|
@@ -1871,6 +1913,7 @@ export class AgentRunner {
|
|
|
1871
1913
|
policyCode: 'claude_startup_arguments_too_long',
|
|
1872
1914
|
decisionSource: 'infrastructure',
|
|
1873
1915
|
agentAid: this.permissionContexts.get(sessionId)?.selfAid,
|
|
1916
|
+
agentName: this.permissionContexts.get(sessionId)?.agentName,
|
|
1874
1917
|
summary: detail.message.slice(0, 512),
|
|
1875
1918
|
effect: 'operation_skipped',
|
|
1876
1919
|
});
|
|
@@ -1904,6 +1947,9 @@ export class AgentRunner {
|
|
|
1904
1947
|
}
|
|
1905
1948
|
}
|
|
1906
1949
|
async runQuery(sessionId, prompt, projectPath, initialClaudeSessionId, images, systemPromptAppend, sessionManager, modelOverride, runtimeEnv) {
|
|
1950
|
+
// Validate the daemon-injected session namespace before the SDK inherits
|
|
1951
|
+
// the process environment for any temporary state.
|
|
1952
|
+
getManagedTaskTempDir(runtimeEnv);
|
|
1907
1953
|
// 记录当前 evolcore session ID,用于 Agent ctl 环境变量注入
|
|
1908
1954
|
// 同步用户级配置到内存
|
|
1909
1955
|
this.syncFromUserSettings();
|
|
@@ -1922,8 +1968,22 @@ export class AgentRunner {
|
|
|
1922
1968
|
}
|
|
1923
1969
|
ensureDir(projectPath);
|
|
1924
1970
|
ensureDir(path.join(projectPath, '.claude'));
|
|
1925
|
-
|
|
1926
|
-
|
|
1971
|
+
const topicBinding = modelOverride?.backend?.kind === 'topic';
|
|
1972
|
+
if (topicBinding && (!modelOverride?.turn || modelOverride.turn.sessionId !== sessionId || !this.onSessionIdUpdate)) {
|
|
1973
|
+
const error = new Error('topic backend run requires an active TurnLease and binding callback');
|
|
1974
|
+
error.code = 'TOPIC_TURN_LEASE_REQUIRED';
|
|
1975
|
+
throw error;
|
|
1976
|
+
}
|
|
1977
|
+
const topicAgentSessionId = topicBinding ? modelOverride?.backend?.agentSessionId ?? null : null;
|
|
1978
|
+
const cachedAgentSessionId = topicBinding
|
|
1979
|
+
? (topicAgentSessionId && this.activeSessions.get(sessionId) === topicAgentSessionId
|
|
1980
|
+
? topicAgentSessionId
|
|
1981
|
+
: undefined)
|
|
1982
|
+
: this.activeSessions.get(sessionId);
|
|
1983
|
+
let agentSessionId = initialClaudeSessionId || cachedAgentSessionId;
|
|
1984
|
+
if (topicBinding && topicAgentSessionId !== agentSessionId) {
|
|
1985
|
+
agentSessionId = topicAgentSessionId || undefined;
|
|
1986
|
+
}
|
|
1927
1987
|
// 验证会话文件是否存在且有效(仅在有 agentSessionId 时)
|
|
1928
1988
|
if (agentSessionId) {
|
|
1929
1989
|
const homeDir = os.homedir();
|
|
@@ -1954,11 +2014,18 @@ export class AgentRunner {
|
|
|
1954
2014
|
}
|
|
1955
2015
|
}
|
|
1956
2016
|
if (!isValid) {
|
|
2017
|
+
if (topicBinding) {
|
|
2018
|
+
const error = new Error(`bound topic backend is unavailable: ${agentSessionId}`);
|
|
2019
|
+
error.code = 'TOPIC_BACKEND_UNAVAILABLE';
|
|
2020
|
+
throw error;
|
|
2021
|
+
}
|
|
1957
2022
|
logger.warn(`[AgentRunner] Invalid session file, starting new session`);
|
|
1958
2023
|
agentSessionId = undefined;
|
|
1959
2024
|
this.activeSessions.delete(sessionId);
|
|
1960
2025
|
if (this.onSessionIdUpdate) {
|
|
1961
|
-
this.onSessionIdUpdate(sessionId, '')
|
|
2026
|
+
void Promise.resolve(this.onSessionIdUpdate(sessionId, '')).catch(error => {
|
|
2027
|
+
logger.debug(`[AgentRunner] ignored empty session binding callback failure: ${error instanceof Error ? error.message : String(error)}`);
|
|
2028
|
+
});
|
|
1962
2029
|
}
|
|
1963
2030
|
}
|
|
1964
2031
|
}
|
|
@@ -1973,9 +2040,11 @@ export class AgentRunner {
|
|
|
1973
2040
|
// 缺省回落 agent 级 this.permissionMode。作为 per-call 入参(hook/canUseTool 闭包捕获),
|
|
1974
2041
|
// 不写实例字段,多对端并发互不污染(与 model/effort 同构)。
|
|
1975
2042
|
const requestedPermissionMode = modelOverride?.permissionMode || this.permissionMode;
|
|
1976
|
-
const
|
|
1977
|
-
const callPermissionMode = normalizedPermission.mode;
|
|
2043
|
+
const callPermissionMode = normalizeExecutionPermissionMode(requestedPermissionMode);
|
|
1978
2044
|
const runPermissionContext = this.permissionContexts.get(sessionId);
|
|
2045
|
+
if (callPermissionMode === 'fullaccess' && !hasTrustedFullAccessContext(runPermissionContext)) {
|
|
2046
|
+
throw new Error('Claude fullaccess requires a trusted per-call execution authorization');
|
|
2047
|
+
}
|
|
1979
2048
|
const executionSandbox = resolvePhaseOneExecutionSandbox(callPermissionMode, runPermissionContext?.role);
|
|
1980
2049
|
const inputId = modelOverride?.turn?.inputId ?? crypto.randomUUID();
|
|
1981
2050
|
if (executionSandbox.state !== 'off') {
|
|
@@ -1990,6 +2059,7 @@ export class AgentRunner {
|
|
|
1990
2059
|
reason: detail.message.slice(0, 512),
|
|
1991
2060
|
sessionId,
|
|
1992
2061
|
agentAid: context?.selfAid,
|
|
2062
|
+
agentName: context?.agentName,
|
|
1993
2063
|
permissionMode: callPermissionMode,
|
|
1994
2064
|
role: context?.role,
|
|
1995
2065
|
callId: inputId,
|
|
@@ -2008,6 +2078,7 @@ export class AgentRunner {
|
|
|
2008
2078
|
policyCode: SANDBOX_INITIALIZATION_FAILED,
|
|
2009
2079
|
decisionSource: 'infrastructure',
|
|
2010
2080
|
agentAid: context?.selfAid,
|
|
2081
|
+
agentName: context?.agentName,
|
|
2011
2082
|
permissionMode: callPermissionMode,
|
|
2012
2083
|
summary: detail.message.slice(0, 512),
|
|
2013
2084
|
effect: 'operation_skipped',
|
|
@@ -2041,7 +2112,7 @@ export class AgentRunner {
|
|
|
2041
2112
|
...(updatedInput ? { updatedInput } : {}),
|
|
2042
2113
|
},
|
|
2043
2114
|
});
|
|
2044
|
-
const recordPreflightDenial = async (toolName, toolInput, preflight, requestId, policyCode) => {
|
|
2115
|
+
const recordPreflightDenial = async (toolName, toolInput, preflight, requestId, policyCode, matchedPath) => {
|
|
2045
2116
|
const code = policyCode ?? (preflight?.behavior === 'deny' ? preflight.policyCode : undefined);
|
|
2046
2117
|
if (!code)
|
|
2047
2118
|
return;
|
|
@@ -2054,6 +2125,7 @@ export class AgentRunner {
|
|
|
2054
2125
|
summary,
|
|
2055
2126
|
sessionId,
|
|
2056
2127
|
agentAid: ctx?.selfAid,
|
|
2128
|
+
agentName: ctx?.agentName,
|
|
2057
2129
|
permissionMode: callPermissionMode,
|
|
2058
2130
|
channel: ctx?.channel,
|
|
2059
2131
|
actorId: ctx?.userId,
|
|
@@ -2061,6 +2133,7 @@ export class AgentRunner {
|
|
|
2061
2133
|
selfAid: ctx?.selfAid,
|
|
2062
2134
|
requestId: typeof requestId === 'string' ? requestId : undefined,
|
|
2063
2135
|
taskId: ctx?.taskId,
|
|
2136
|
+
matchedPath: matchedPath ?? (preflight?.behavior === 'deny' ? preflight.matchedPath : undefined),
|
|
2064
2137
|
});
|
|
2065
2138
|
try {
|
|
2066
2139
|
await ctx?.recordExecutionAnomaly?.({
|
|
@@ -2073,6 +2146,7 @@ export class AgentRunner {
|
|
|
2073
2146
|
policyCode: code,
|
|
2074
2147
|
decisionSource: 'policy',
|
|
2075
2148
|
agentAid: ctx?.selfAid,
|
|
2149
|
+
agentName: ctx?.agentName,
|
|
2076
2150
|
permissionMode: callPermissionMode,
|
|
2077
2151
|
summary,
|
|
2078
2152
|
effect: 'operation_skipped',
|
|
@@ -2121,6 +2195,14 @@ export class AgentRunner {
|
|
|
2121
2195
|
delete toolInput.pages;
|
|
2122
2196
|
}
|
|
2123
2197
|
let updatedInput = toolInput === originalInput ? undefined : toolInput;
|
|
2198
|
+
if (callPermissionMode === 'fullaccess') {
|
|
2199
|
+
if (!hasTrustedFullAccessContext(this.permissionContexts.get(sessionId))) {
|
|
2200
|
+
return hookDecision('deny', 'fullaccess execution authorization is no longer active', updatedInput);
|
|
2201
|
+
}
|
|
2202
|
+
return hookDecision('allow', undefined, toolName === 'Bash'
|
|
2203
|
+
? { ...toolInput, dangerouslyDisableSandbox: true }
|
|
2204
|
+
: updatedInput);
|
|
2205
|
+
}
|
|
2124
2206
|
const preparedSafeOutputCommand = toolName === 'Bash' && typeof toolInput.command === 'string'
|
|
2125
2207
|
? toolInput.command
|
|
2126
2208
|
: undefined;
|
|
@@ -2142,6 +2224,7 @@ export class AgentRunner {
|
|
|
2142
2224
|
: undefined;
|
|
2143
2225
|
const preflight = evaluateToolPreflight(toolName, toolInput, {
|
|
2144
2226
|
sessionId,
|
|
2227
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2145
2228
|
selfAid: permCtx?.selfAid,
|
|
2146
2229
|
channel: permCtx?.channel,
|
|
2147
2230
|
userId: permCtx?.userId,
|
|
@@ -2178,6 +2261,7 @@ export class AgentRunner {
|
|
|
2178
2261
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
2179
2262
|
const readonlyContext = {
|
|
2180
2263
|
sessionId,
|
|
2264
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2181
2265
|
channel: permCtx?.channel,
|
|
2182
2266
|
peerId: permCtx?.userId,
|
|
2183
2267
|
role: permCtx?.role,
|
|
@@ -2186,7 +2270,7 @@ export class AgentRunner {
|
|
|
2186
2270
|
};
|
|
2187
2271
|
const roResult = checkReadonly(toolName, toolInput, projectPath, readonlyContext);
|
|
2188
2272
|
if (roResult.behavior === 'deny') {
|
|
2189
|
-
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, roResult.policyCode ?? 'readonly_mode');
|
|
2273
|
+
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, roResult.policyCode ?? 'readonly_mode', roResult.matchedPath);
|
|
2190
2274
|
return hookDecision('deny', roResult.message, updatedInput);
|
|
2191
2275
|
}
|
|
2192
2276
|
return hookDecision('allow', undefined, updatedInput);
|
|
@@ -2249,6 +2333,20 @@ export class AgentRunner {
|
|
|
2249
2333
|
if (toolName === 'ExitPlanMode') {
|
|
2250
2334
|
return await this.handleExitPlanMode(sessionId, input, options);
|
|
2251
2335
|
}
|
|
2336
|
+
if (callPermissionMode === 'fullaccess') {
|
|
2337
|
+
if (!hasTrustedFullAccessContext(this.permissionContexts.get(sessionId))) {
|
|
2338
|
+
return {
|
|
2339
|
+
behavior: 'deny',
|
|
2340
|
+
message: 'fullaccess execution authorization is no longer active',
|
|
2341
|
+
decisionClassification: 'user_reject',
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
return {
|
|
2345
|
+
behavior: 'allow',
|
|
2346
|
+
updatedInput: toolName === 'Bash' ? { ...input, dangerouslyDisableSandbox: true } : input,
|
|
2347
|
+
decisionClassification: 'user_permanent',
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2252
2350
|
if (toolName === 'Bash' && 'dangerouslyDisableSandbox' in input) {
|
|
2253
2351
|
input = { ...input };
|
|
2254
2352
|
delete input.dangerouslyDisableSandbox;
|
|
@@ -2266,6 +2364,7 @@ export class AgentRunner {
|
|
|
2266
2364
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
2267
2365
|
const preflight = evaluateToolPreflight(toolName, input, {
|
|
2268
2366
|
sessionId,
|
|
2367
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2269
2368
|
selfAid: permCtx?.selfAid,
|
|
2270
2369
|
channel: permCtx?.channel,
|
|
2271
2370
|
userId: permCtx?.userId,
|
|
@@ -2338,6 +2437,7 @@ export class AgentRunner {
|
|
|
2338
2437
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
2339
2438
|
const readonlyContext = {
|
|
2340
2439
|
sessionId,
|
|
2440
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2341
2441
|
channel: permCtx?.channel,
|
|
2342
2442
|
peerId: permCtx?.userId,
|
|
2343
2443
|
role: permCtx?.role,
|
|
@@ -2346,7 +2446,7 @@ export class AgentRunner {
|
|
|
2346
2446
|
};
|
|
2347
2447
|
const roResult = checkReadonly(toolName, input, projectPath, readonlyContext);
|
|
2348
2448
|
if (roResult.behavior === 'deny') {
|
|
2349
|
-
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, roResult.policyCode ?? 'readonly_mode');
|
|
2449
|
+
await recordPreflightDenial(toolName, input, undefined, options.toolUseID, roResult.policyCode ?? 'readonly_mode', roResult.matchedPath);
|
|
2350
2450
|
return { behavior: 'deny', message: roResult.message, decisionClassification: 'user_reject' };
|
|
2351
2451
|
}
|
|
2352
2452
|
return { behavior: 'allow', updatedInput: input, decisionClassification: 'user_permanent' };
|
|
@@ -2402,7 +2502,9 @@ export class AgentRunner {
|
|
|
2402
2502
|
};
|
|
2403
2503
|
};
|
|
2404
2504
|
const useSettingSources = this.config?.agents?.claude?.useSettingSources !== false;
|
|
2405
|
-
const settingSources =
|
|
2505
|
+
const settingSources = callPermissionMode === 'fullaccess'
|
|
2506
|
+
? []
|
|
2507
|
+
: useSettingSources ? ['project', 'user'] : [];
|
|
2406
2508
|
const enableSummaries = this.config?.agents?.claude?.agentProgressSummaries !== false;
|
|
2407
2509
|
const excludeDynamic = this.config?.agents?.claude?.excludeDynamicSections === true;
|
|
2408
2510
|
// 公共 options(新旧模式共用)
|
|
@@ -2425,7 +2527,9 @@ export class AgentRunner {
|
|
|
2425
2527
|
}
|
|
2426
2528
|
const sdkModel = resolveSdkModel(callModel, this.baseUrl);
|
|
2427
2529
|
const capabilityOptions = await this.resolveCapabilityRunOptions(projectPath);
|
|
2428
|
-
const managedSettings =
|
|
2530
|
+
const managedSettings = callPermissionMode === 'fullaccess'
|
|
2531
|
+
? undefined
|
|
2532
|
+
: buildClaudeManagedLockdownSettings(capabilityOptions);
|
|
2429
2533
|
const sandboxOptions = executionSandbox.state === 'off'
|
|
2430
2534
|
? { enabled: false }
|
|
2431
2535
|
: await (async () => {
|
|
@@ -2465,9 +2569,11 @@ export class AgentRunner {
|
|
|
2465
2569
|
};
|
|
2466
2570
|
})();
|
|
2467
2571
|
if (executionSandbox.state === 'off') {
|
|
2468
|
-
logger.info(
|
|
2469
|
-
? '[ClaudeSandbox]
|
|
2470
|
-
:
|
|
2572
|
+
logger.info(callPermissionMode === 'fullaccess'
|
|
2573
|
+
? '[ClaudeSandbox] disabled for trusted per-call fullaccess execution'
|
|
2574
|
+
: process.platform === 'linux'
|
|
2575
|
+
? '[ClaudeSandbox] task sandbox disabled for authenticated owner bypass; outer H-class guard remains active'
|
|
2576
|
+
: '[ClaudeSandbox] disabled for authenticated owner bypass task');
|
|
2471
2577
|
}
|
|
2472
2578
|
const handleClaudeStderr = (msg) => {
|
|
2473
2579
|
const trimmed = msg.trim();
|
|
@@ -2493,16 +2599,17 @@ export class AgentRunner {
|
|
|
2493
2599
|
model: sdkModel,
|
|
2494
2600
|
...capabilityOptions,
|
|
2495
2601
|
strictMcpConfig: false,
|
|
2496
|
-
managedSettings,
|
|
2602
|
+
...(managedSettings ? { managedSettings } : {}),
|
|
2497
2603
|
...(callEffort ? { effort: callEffort } : {}),
|
|
2498
2604
|
...(this.claudeExecutablePath ? { pathToClaudeCodeExecutable: this.claudeExecutablePath } : {}),
|
|
2499
|
-
...(executionSandbox.state === 'off' && process.platform === 'linux' ? {
|
|
2605
|
+
...(executionSandbox.state === 'off' && callPermissionMode !== 'fullaccess' && process.platform === 'linux' ? {
|
|
2500
2606
|
spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr),
|
|
2501
2607
|
} : {}),
|
|
2502
2608
|
autoCompactWindow: autoCompactWindowForModel(sdkModel),
|
|
2503
2609
|
advisorModel: 'haiku',
|
|
2504
2610
|
canUseTool: canUseToolCallback,
|
|
2505
2611
|
permissionMode: sdkPermissionMode,
|
|
2612
|
+
...(callPermissionMode === 'fullaccess' ? { allowDangerouslySkipPermissions: true } : {}),
|
|
2506
2613
|
sandbox: sandboxOptions,
|
|
2507
2614
|
persistSession: true,
|
|
2508
2615
|
includePartialMessages: true,
|
|
@@ -2624,9 +2731,14 @@ export class AgentRunner {
|
|
|
2624
2731
|
const currentSession = await sessionManager.getSessionById?.(sessionId);
|
|
2625
2732
|
if (currentSession?.metadata?.resumeAt) {
|
|
2626
2733
|
resumeAt = currentSession.metadata.resumeAt;
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2734
|
+
if (typeof sessionManager.patchSessionMetadata === 'function') {
|
|
2735
|
+
await sessionManager.patchSessionMetadata(sessionId, { resumeAt: undefined });
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
const newMeta = { ...currentSession.metadata };
|
|
2739
|
+
delete newMeta.resumeAt;
|
|
2740
|
+
await sessionManager.updateSession(sessionId, { metadata: newMeta });
|
|
2741
|
+
}
|
|
2630
2742
|
logger.info(`[AgentRunner] Consuming resumeAt: ${resumeAt}`);
|
|
2631
2743
|
}
|
|
2632
2744
|
}
|
|
@@ -2638,10 +2750,14 @@ export class AgentRunner {
|
|
|
2638
2750
|
const msgStream = new MessageStream();
|
|
2639
2751
|
if (images && images.length > 0) {
|
|
2640
2752
|
logger.info('[AgentRunner] Creating query with images:', images.length, 'first image size:', images[0]?.data?.length ?? 0);
|
|
2641
|
-
logger.debug('[AgentRunner] Skipping resume for image message to avoid history conflict');
|
|
2642
2753
|
msgStream.push(prompt, images, inputId);
|
|
2643
2754
|
try {
|
|
2644
|
-
|
|
2755
|
+
// Topic bindings are authoritative for every input shape. Starting
|
|
2756
|
+
// an unresumed Claude query for a bound topic would discover a second
|
|
2757
|
+
// backend and then fail activation with a binding conflict.
|
|
2758
|
+
sdkStream = topicBinding
|
|
2759
|
+
? createQuery(msgStream, agentSessionId, resumeAt)
|
|
2760
|
+
: createQuery(msgStream);
|
|
2645
2761
|
}
|
|
2646
2762
|
catch (error) {
|
|
2647
2763
|
this.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
@@ -2675,14 +2791,16 @@ export class AgentRunner {
|
|
|
2675
2791
|
}
|
|
2676
2792
|
this.activeMessageStreams.set(sessionId, msgStream);
|
|
2677
2793
|
this.activeQueries.set(sessionId, sdkStream);
|
|
2678
|
-
|
|
2794
|
+
let resolveDone;
|
|
2795
|
+
const donePromise = new Promise(resolve => { resolveDone = resolve; });
|
|
2796
|
+
this.streamDoneResolvers.set(sessionId, resolveDone);
|
|
2679
2797
|
this.streamDone.set(sessionId, donePromise);
|
|
2680
2798
|
// 保存 interrupt 能力(不写 activeStreams,由 registerStream 管理活跃状态)
|
|
2681
2799
|
if ('interrupt' in sdkStream && typeof sdkStream.interrupt === 'function') {
|
|
2682
2800
|
this.interruptFns.set(sessionId, () => sdkStream.interrupt());
|
|
2683
2801
|
}
|
|
2684
2802
|
// 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
|
|
2685
|
-
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode);
|
|
2803
|
+
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn);
|
|
2686
2804
|
const self = this;
|
|
2687
2805
|
return (async function* () {
|
|
2688
2806
|
try {
|
|
@@ -2690,10 +2808,15 @@ export class AgentRunner {
|
|
|
2690
2808
|
}
|
|
2691
2809
|
finally {
|
|
2692
2810
|
self.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2693
|
-
|
|
2694
|
-
self.streamDoneResolvers.
|
|
2695
|
-
|
|
2696
|
-
self.
|
|
2811
|
+
resolveDone();
|
|
2812
|
+
if (self.streamDoneResolvers.get(sessionId) === resolveDone)
|
|
2813
|
+
self.streamDoneResolvers.delete(sessionId);
|
|
2814
|
+
if (self.streamDone.get(sessionId) === donePromise)
|
|
2815
|
+
self.streamDone.delete(sessionId);
|
|
2816
|
+
if (self.activeQueries.get(sessionId) === sdkStream)
|
|
2817
|
+
self.activeQueries.delete(sessionId);
|
|
2818
|
+
if (self.activeMessageStreams.get(sessionId) === msgStream)
|
|
2819
|
+
self.activeMessageStreams.delete(sessionId);
|
|
2697
2820
|
}
|
|
2698
2821
|
})();
|
|
2699
2822
|
}
|
|
@@ -2761,19 +2884,64 @@ export class AgentRunner {
|
|
|
2761
2884
|
injectUserMessage(sessionId, text) {
|
|
2762
2885
|
this.activeMessageStreams.get(sessionId)?.push(text);
|
|
2763
2886
|
}
|
|
2887
|
+
async acceptDiscoveredSessionId(sessionId, agentSessionId, turn) {
|
|
2888
|
+
const result = this.onSessionIdUpdate
|
|
2889
|
+
? (turn === undefined
|
|
2890
|
+
? await this.onSessionIdUpdate(sessionId, agentSessionId)
|
|
2891
|
+
: await this.onSessionIdUpdate(sessionId, agentSessionId, { turn }))
|
|
2892
|
+
: 'legacy_updated';
|
|
2893
|
+
const accepted = result === undefined || result === 'activated' || result === 'already_active' || result === 'legacy_updated';
|
|
2894
|
+
if (accepted) {
|
|
2895
|
+
this.activeSessions.set(sessionId, agentSessionId);
|
|
2896
|
+
}
|
|
2897
|
+
return accepted;
|
|
2898
|
+
}
|
|
2764
2899
|
updateSessionId(sessionId, agentSessionId) {
|
|
2765
2900
|
logger.info(`[AgentRunner] updateSessionId called: sessionId=${sessionId}, agentSessionId=${agentSessionId}`);
|
|
2766
|
-
this.activeSessions.
|
|
2767
|
-
|
|
2768
|
-
this.
|
|
2901
|
+
const previousAgentSessionId = this.activeSessions.get(sessionId);
|
|
2902
|
+
const canApply = () => {
|
|
2903
|
+
const current = this.activeSessions.get(sessionId);
|
|
2904
|
+
return current === previousAgentSessionId || (previousAgentSessionId === undefined && current === undefined);
|
|
2905
|
+
};
|
|
2906
|
+
if (!this.onSessionIdUpdate) {
|
|
2907
|
+
if (!canApply())
|
|
2908
|
+
return;
|
|
2909
|
+
if (agentSessionId)
|
|
2910
|
+
this.activeSessions.set(sessionId, agentSessionId);
|
|
2911
|
+
else if (this.activeSessions.get(sessionId) === previousAgentSessionId)
|
|
2912
|
+
this.activeSessions.delete(sessionId);
|
|
2913
|
+
return;
|
|
2769
2914
|
}
|
|
2915
|
+
void Promise.resolve(this.onSessionIdUpdate(sessionId, agentSessionId)).then(result => {
|
|
2916
|
+
if (!canApply())
|
|
2917
|
+
return;
|
|
2918
|
+
if (result === undefined || result === 'activated' || result === 'already_active' || result === 'legacy_updated') {
|
|
2919
|
+
if (agentSessionId)
|
|
2920
|
+
this.activeSessions.set(sessionId, agentSessionId);
|
|
2921
|
+
else if (this.activeSessions.get(sessionId) === previousAgentSessionId)
|
|
2922
|
+
this.activeSessions.delete(sessionId);
|
|
2923
|
+
}
|
|
2924
|
+
else if (!agentSessionId) {
|
|
2925
|
+
if (this.activeSessions.get(sessionId) === previousAgentSessionId)
|
|
2926
|
+
this.activeSessions.delete(sessionId);
|
|
2927
|
+
}
|
|
2928
|
+
}).catch(error => {
|
|
2929
|
+
logger.warn(`[AgentRunner] session binding callback failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2930
|
+
if (!agentSessionId && canApply() && this.activeSessions.get(sessionId) === previousAgentSessionId) {
|
|
2931
|
+
this.activeSessions.delete(sessionId);
|
|
2932
|
+
}
|
|
2933
|
+
});
|
|
2770
2934
|
}
|
|
2771
|
-
runSessionCommand(prompt, agentSessionId, projectPath) {
|
|
2935
|
+
runSessionCommand(prompt, agentSessionId, projectPath, modelOverride) {
|
|
2936
|
+
const callModel = modelOverride?.model || this.model;
|
|
2937
|
+
const callEffort = (modelOverride?.effortMode === 'model_default'
|
|
2938
|
+
? undefined
|
|
2939
|
+
: modelOverride?.effort ?? this.effort);
|
|
2772
2940
|
return query({
|
|
2773
2941
|
prompt,
|
|
2774
2942
|
options: {
|
|
2775
2943
|
cwd: projectPath,
|
|
2776
|
-
model: resolveSdkModel(
|
|
2944
|
+
model: resolveSdkModel(callModel, this.baseUrl),
|
|
2777
2945
|
resume: agentSessionId,
|
|
2778
2946
|
maxTurns: 1,
|
|
2779
2947
|
tools: [],
|
|
@@ -2782,6 +2950,7 @@ export class AgentRunner {
|
|
|
2782
2950
|
strictMcpConfig: true,
|
|
2783
2951
|
settingSources: [],
|
|
2784
2952
|
managedSettings: buildClaudeManagedLockdownSettings(),
|
|
2953
|
+
...(callEffort ? { effort: callEffort } : {}),
|
|
2785
2954
|
permissionMode: this.toSdkPermissionMode(),
|
|
2786
2955
|
env: this.getAgentEnv()
|
|
2787
2956
|
}
|
|
@@ -2790,31 +2959,74 @@ export class AgentRunner {
|
|
|
2790
2959
|
/**
|
|
2791
2960
|
* 主动压缩会话上下文
|
|
2792
2961
|
*/
|
|
2793
|
-
async compactSession(sessionId, agentSessionId, projectPath) {
|
|
2962
|
+
async compactSession(sessionId, agentSessionId, projectPath, modelOverride) {
|
|
2963
|
+
const startedAt = Date.now();
|
|
2964
|
+
let eventCount = 0;
|
|
2965
|
+
let lastEvent;
|
|
2794
2966
|
try {
|
|
2795
|
-
logger.info(`[AgentRunner] Compacting session:
|
|
2796
|
-
const stream = this.runSessionCommand('/compact', agentSessionId, projectPath);
|
|
2967
|
+
logger.info(`[AgentRunner] Compacting session: session=${sessionId} agent=${agentSessionId} project=${projectPath}`);
|
|
2968
|
+
const stream = this.runSessionCommand('/compact', agentSessionId, projectPath, modelOverride);
|
|
2797
2969
|
this.activeStreams.set(sessionId, stream);
|
|
2798
2970
|
try {
|
|
2799
|
-
let
|
|
2971
|
+
let boundary;
|
|
2972
|
+
let compactStatus;
|
|
2800
2973
|
for await (const event of stream) {
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2974
|
+
eventCount += 1;
|
|
2975
|
+
lastEvent = { type: event?.type, subtype: event?.subtype };
|
|
2976
|
+
if (event.type === 'system' && event.subtype === 'status' && event.compact_result) {
|
|
2977
|
+
compactStatus = {
|
|
2978
|
+
result: event.compact_result,
|
|
2979
|
+
error: event.compact_error,
|
|
2980
|
+
};
|
|
2804
2981
|
}
|
|
2982
|
+
if (event.type === 'system' && event.subtype === 'compact_boundary')
|
|
2983
|
+
boundary = event;
|
|
2984
|
+
}
|
|
2985
|
+
const durationMs = Date.now() - startedAt;
|
|
2986
|
+
if (compactStatus?.result === 'failed') {
|
|
2987
|
+
const result = {
|
|
2988
|
+
ok: false,
|
|
2989
|
+
code: 'sdk_error',
|
|
2990
|
+
message: compactStatus.error || 'Claude SDK reported compact failure',
|
|
2991
|
+
durationMs,
|
|
2992
|
+
eventCount,
|
|
2993
|
+
lastEvent,
|
|
2994
|
+
};
|
|
2995
|
+
logger.warn(`[AgentRunner] Compact failed: session=${sessionId} agent=${agentSessionId} code=${result.code} events=${eventCount} durationMs=${durationMs} message=${result.message}`);
|
|
2996
|
+
return result;
|
|
2805
2997
|
}
|
|
2806
|
-
if (!
|
|
2807
|
-
|
|
2998
|
+
if (!boundary) {
|
|
2999
|
+
const result = {
|
|
3000
|
+
ok: false,
|
|
3001
|
+
code: 'missing_boundary',
|
|
3002
|
+
message: 'Compact stream ended without compact_boundary event',
|
|
3003
|
+
durationMs,
|
|
3004
|
+
eventCount,
|
|
3005
|
+
lastEvent,
|
|
3006
|
+
};
|
|
3007
|
+
logger.warn(`[AgentRunner] Compact failed: session=${sessionId} agent=${agentSessionId} code=${result.code} events=${eventCount} durationMs=${durationMs}`);
|
|
3008
|
+
return result;
|
|
2808
3009
|
}
|
|
2809
|
-
|
|
3010
|
+
const result = {
|
|
3011
|
+
ok: true,
|
|
3012
|
+
durationMs,
|
|
3013
|
+
eventCount,
|
|
3014
|
+
preTokens: boundary.compact_metadata?.pre_tokens,
|
|
3015
|
+
postTokens: boundary.compact_metadata?.post_tokens,
|
|
3016
|
+
};
|
|
3017
|
+
logger.info(`[AgentRunner] Compact completed: session=${sessionId} agent=${agentSessionId} events=${eventCount} durationMs=${durationMs} pre_tokens=${result.preTokens ?? 'unknown'} post_tokens=${result.postTokens ?? 'unknown'}`);
|
|
3018
|
+
return result;
|
|
2810
3019
|
}
|
|
2811
3020
|
finally {
|
|
2812
3021
|
this.activeStreams.delete(sessionId);
|
|
2813
3022
|
}
|
|
2814
3023
|
}
|
|
2815
3024
|
catch (error) {
|
|
2816
|
-
|
|
2817
|
-
|
|
3025
|
+
const durationMs = Date.now() - startedAt;
|
|
3026
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3027
|
+
const result = { ok: false, code: 'sdk_error', message, durationMs, eventCount, lastEvent };
|
|
3028
|
+
logger.error(`[AgentRunner] Compact failed: session=${sessionId} agent=${agentSessionId} project=${projectPath} code=${result.code} events=${eventCount} durationMs=${durationMs} error=${message}`);
|
|
3029
|
+
return result;
|
|
2818
3030
|
}
|
|
2819
3031
|
}
|
|
2820
3032
|
/**
|
|
@@ -2835,7 +3047,7 @@ export class AgentRunner {
|
|
|
2835
3047
|
}
|
|
2836
3048
|
if (cleared) {
|
|
2837
3049
|
this.activeSessions.delete(sessionId);
|
|
2838
|
-
this.onSessionIdUpdate?.(sessionId, '');
|
|
3050
|
+
await this.onSessionIdUpdate?.(sessionId, '');
|
|
2839
3051
|
}
|
|
2840
3052
|
else {
|
|
2841
3053
|
logger.warn('[AgentRunner] Clear stream ended without session reset signal');
|
|
@@ -2852,16 +3064,76 @@ export class AgentRunner {
|
|
|
2852
3064
|
}
|
|
2853
3065
|
}
|
|
2854
3066
|
async closeSession(sessionId) {
|
|
3067
|
+
const capturedAgentSessionId = this.activeSessions.get(sessionId);
|
|
3068
|
+
const capturedPermissionContext = this.permissionContexts.get(sessionId);
|
|
2855
3069
|
this.permissionContexts.get(sessionId)?.pauseController?.cancel();
|
|
2856
3070
|
const query = this.activeQueries.get(sessionId);
|
|
3071
|
+
const messageStream = this.activeMessageStreams.get(sessionId);
|
|
3072
|
+
const registeredStream = this.activeStreams.get(sessionId);
|
|
3073
|
+
const interrupt = this.interruptFns.get(sessionId);
|
|
3074
|
+
const done = this.streamDone.get(sessionId);
|
|
3075
|
+
const resolveDone = this.streamDoneResolvers.get(sessionId);
|
|
3076
|
+
let interruptError;
|
|
3077
|
+
if (interrupt) {
|
|
3078
|
+
try {
|
|
3079
|
+
await interrupt();
|
|
3080
|
+
}
|
|
3081
|
+
catch (error) {
|
|
3082
|
+
interruptError = error;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
messageStream?.end();
|
|
2857
3086
|
if (query && typeof query.close === 'function')
|
|
2858
|
-
query.close();
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
3087
|
+
await query.close();
|
|
3088
|
+
if (done && registeredStream) {
|
|
3089
|
+
let timer;
|
|
3090
|
+
try {
|
|
3091
|
+
await Promise.race([
|
|
3092
|
+
done,
|
|
3093
|
+
new Promise((_, reject) => {
|
|
3094
|
+
timer = setTimeout(() => reject(new Error(`Claude session did not become quiescent: ${sessionId}`)), 30_000);
|
|
3095
|
+
timer.unref?.();
|
|
3096
|
+
}),
|
|
3097
|
+
]);
|
|
3098
|
+
}
|
|
3099
|
+
finally {
|
|
3100
|
+
if (timer)
|
|
3101
|
+
clearTimeout(timer);
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
else if (done) {
|
|
3105
|
+
// runQuery resolved but ResponseEngine never registered/consumed the
|
|
3106
|
+
// wrapper. No consumer can reach its finally block, so close the exact
|
|
3107
|
+
// captured lifecycle explicitly.
|
|
3108
|
+
resolveDone?.();
|
|
3109
|
+
}
|
|
3110
|
+
const lifecycleReplaced = (query && this.activeQueries.get(sessionId) !== undefined && this.activeQueries.get(sessionId) !== query)
|
|
3111
|
+
|| (messageStream && this.activeMessageStreams.get(sessionId) !== undefined && this.activeMessageStreams.get(sessionId) !== messageStream)
|
|
3112
|
+
|| (registeredStream && this.activeStreams.get(sessionId) !== undefined && this.activeStreams.get(sessionId) !== registeredStream);
|
|
3113
|
+
if (lifecycleReplaced) {
|
|
3114
|
+
const error = new Error(`Claude session lifecycle changed during close: ${sessionId}`);
|
|
3115
|
+
error.code = 'BACKEND_RUNTIME_REPLACED';
|
|
3116
|
+
throw error;
|
|
3117
|
+
}
|
|
3118
|
+
if (this.activeQueries.get(sessionId) === query)
|
|
3119
|
+
this.activeQueries.delete(sessionId);
|
|
3120
|
+
if (this.activeMessageStreams.get(sessionId) === messageStream)
|
|
3121
|
+
this.activeMessageStreams.delete(sessionId);
|
|
3122
|
+
if (this.activeStreams.get(sessionId) === registeredStream)
|
|
3123
|
+
this.activeStreams.delete(sessionId);
|
|
3124
|
+
if (this.interruptFns.get(sessionId) === interrupt)
|
|
3125
|
+
this.interruptFns.delete(sessionId);
|
|
3126
|
+
if (this.streamDone.get(sessionId) === done)
|
|
3127
|
+
this.streamDone.delete(sessionId);
|
|
3128
|
+
if (this.streamDoneResolvers.get(sessionId) === resolveDone)
|
|
3129
|
+
this.streamDoneResolvers.delete(sessionId);
|
|
3130
|
+
if (this.activeSessions.get(sessionId) === capturedAgentSessionId)
|
|
3131
|
+
this.activeSessions.delete(sessionId);
|
|
3132
|
+
if (this.permissionContexts.get(sessionId) === capturedPermissionContext)
|
|
3133
|
+
this.permissionContexts.delete(sessionId);
|
|
2864
3134
|
this.cleanupSandboxSettingsFiles(sessionId);
|
|
3135
|
+
if (interruptError)
|
|
3136
|
+
throw interruptError;
|
|
2865
3137
|
}
|
|
2866
3138
|
async dispose() {
|
|
2867
3139
|
const interrupts = [...this.interruptFns.values()].map(async (interrupt) => {
|
|
@@ -2977,9 +3249,9 @@ export class ClaudeAgentPlugin {
|
|
|
2977
3249
|
const syntheticConfig = { agents: { claude: override } };
|
|
2978
3250
|
const anthropic = resolveAnthropicConfig(syntheticConfig, override);
|
|
2979
3251
|
const merged = {
|
|
2980
|
-
agents: { claude: { ...(override || {})
|
|
3252
|
+
agents: { claude: { ...(override || {}) } },
|
|
2981
3253
|
};
|
|
2982
|
-
const agentRunner = new AgentRunner(anthropic.apiKey, anthropic.model, callbacks.onSessionIdUpdate, anthropic.baseUrl, merged);
|
|
3254
|
+
const agentRunner = new AgentRunner(anthropic.apiKey, anthropic.model, callbacks.onSessionIdUpdate, anthropic.baseUrl, merged, { agentAid: agent.aid, agentConfig: agent.config });
|
|
2983
3255
|
if (anthropic.effort) {
|
|
2984
3256
|
agentRunner.setEffort(anthropic.effort);
|
|
2985
3257
|
}
|