evolcore 0.0.20 → 0.0.22
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 +65 -0
- package/README.md +58 -9
- package/bin/codex-managed-hook.mjs +3 -0
- package/bin/install-codex-managed-hooks.mjs +3 -1
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +393 -108
- package/dist/agents/codex-app-server-client.js +41 -7
- package/dist/agents/codex-runner.js +1292 -220
- package/dist/agents/ecagent-runner.js +171 -61
- 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 +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +189 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +618 -123
- 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 +98 -123
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +50 -0
- package/dist/cli/trigger-command.js +14 -5
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +19 -5
- package/dist/config/config-manager.js +167 -22
- package/dist/config/contact-book-store.js +25 -3
- 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 +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- 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 +43 -1
- package/dist/core/auth/auth-gateway.js +41 -4
- package/dist/core/auth/authorization-audit.js +216 -8
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-messages.js +8 -0
- package/dist/core/bootstrap-service.js +99 -27
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +311 -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 +288 -80
- package/dist/core/command/menu-protocol.js +1 -1
- 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 +377 -36
- package/dist/core/data-migration.js +11 -1
- package/dist/core/event-catalog.js +37 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +33 -3
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +90 -87
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +184 -12
- package/dist/core/message/message-log.js +47 -7
- package/dist/core/message/message-queue.js +227 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +658 -109
- 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 +627 -69
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +27 -14
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-error-code.js +12 -0
- package/dist/core/permission/tool-policy.js +618 -23
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +329 -30
- package/dist/core/session/session-renew.js +37 -13
- package/dist/core/session/session-turn-coordinator.js +16 -5
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +316 -50
- package/dist/ipc.js +459 -29
- package/dist/paths.js +82 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/response-system/engines/v1/proactive-flow.js +7 -2
- package/dist/stats/price-resolver.js +4 -0
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +70 -7
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +13 -3
- package/dist/trigger/scheduler.js +20 -3
- 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 +30 -6
- 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/tool-summary.js +59 -0
- package/dist/utils/windows-shell-trust.js +201 -0
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +3 -3
- package/kits/docs/evolcore/agent-create.md +146 -0
- package/kits/docs/evolcore/agent.md +6 -0
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/group-collaboration.md +251 -0
- package/kits/docs/evolcore/group-rules.md +1 -19
- package/kits/docs/evolcore/group.md +3 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/docs/evolcore/trigger.md +6 -3
- package/kits/docs/prompt-loading-architecture.md +6 -0
- package/kits/eck_message_manifest.json +6 -6
- package/kits/schemas/_meta.json +7 -4
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/agent-config.schema.12.json +427 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- 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/message-fragments/item.md +1 -1
- 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/kits/templates/system-fragments/bootstrap.md +2 -1
- package/kits/templates/system-fragments/commands.md +2 -2
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -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;
|
|
@@ -721,6 +719,20 @@ export class AgentRunner {
|
|
|
721
719
|
...(evolcoreSessionId ? { EVOLCORE_SESSION_ID: evolcoreSessionId } : {}),
|
|
722
720
|
...(runtimeEnv ?? {}),
|
|
723
721
|
}, { lockExecutableResolution: permissionMode === 'readonly' });
|
|
722
|
+
const trustedRootFullAccess = permissionMode === 'fullaccess'
|
|
723
|
+
&& typeof process.getuid === 'function'
|
|
724
|
+
&& process.getuid() === 0
|
|
725
|
+
&& typeof evolcoreSessionId === 'string'
|
|
726
|
+
&& hasTrustedFullAccessContext(this.permissionContexts.get(evolcoreSessionId));
|
|
727
|
+
// Claude Code refuses --dangerously-skip-permissions as root unless its
|
|
728
|
+
// caller asserts that the execution is intentionally sandbox-managed.
|
|
729
|
+
// EvolCore fullaccess is an explicit daemon-owner-authorized host run, so
|
|
730
|
+
// scope this assertion to that single trusted call. Never inherit a
|
|
731
|
+
// process-wide IS_SANDBOX into ordinary or untrusted Claude sessions.
|
|
732
|
+
if (trustedRootFullAccess)
|
|
733
|
+
env.IS_SANDBOX = '1';
|
|
734
|
+
else
|
|
735
|
+
delete env.IS_SANDBOX;
|
|
724
736
|
const bubblewrapPath = resolveBubblewrapPath();
|
|
725
737
|
return bubblewrapPath
|
|
726
738
|
? prependExecutableDirectory(env, bubblewrapPath)
|
|
@@ -744,11 +756,10 @@ export class AgentRunner {
|
|
|
744
756
|
return entry?.cache;
|
|
745
757
|
}
|
|
746
758
|
async resolveCapabilityRunOptions(projectPath) {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
if (claudeConfig?.evolcoreAgentAid) {
|
|
759
|
+
let agentConfig = this.agentConfig;
|
|
760
|
+
if (this.agentAid) {
|
|
750
761
|
try {
|
|
751
|
-
agentConfig = resolveEffective({ self:
|
|
762
|
+
agentConfig = resolveEffective({ self: this.agentAid }, { cache: true, expand: true });
|
|
752
763
|
}
|
|
753
764
|
catch { }
|
|
754
765
|
}
|
|
@@ -826,14 +837,16 @@ export class AgentRunner {
|
|
|
826
837
|
this.permissionContexts.set(sessionId, context);
|
|
827
838
|
}
|
|
828
839
|
toSdkPermissionMode(mode) {
|
|
840
|
+
if (mode === 'fullaccess')
|
|
841
|
+
return 'bypassPermissions';
|
|
829
842
|
const normalized = normalizePermissionMode(mode ?? this.permissionMode);
|
|
830
843
|
// Public permission policy is enforced by PreToolUse. Keeping the SDK in
|
|
831
844
|
// default mode preserves canUseTool reachability for request mode.
|
|
832
845
|
return normalized.workflow === 'plan' ? 'plan' : 'default';
|
|
833
846
|
}
|
|
834
847
|
// ── Compactable 接口 ──
|
|
835
|
-
async compact(sessionId, agentSessionId, projectPath) {
|
|
836
|
-
return this.compactSession(sessionId, agentSessionId, projectPath);
|
|
848
|
+
async compact(sessionId, agentSessionId, projectPath, modelOverride) {
|
|
849
|
+
return this.compactSession(sessionId, agentSessionId, projectPath, modelOverride);
|
|
837
850
|
}
|
|
838
851
|
syncFromUserSettings() {
|
|
839
852
|
try {
|
|
@@ -972,7 +985,7 @@ export class AgentRunner {
|
|
|
972
985
|
: '回复 /ask 1,或 /ask <自定义内容>';
|
|
973
986
|
const fallbackText = `💬 ${q.header || q.question}\n${q.header ? q.question + '\n' : ''}${optionLines}\n\n${answerHint}`;
|
|
974
987
|
const result = await sendInteractionPayload(permCtx.adapter, envelope, interaction, fallbackText, permCtx.replyContext);
|
|
975
|
-
cardSent =
|
|
988
|
+
cardSent = isInteractionSendAccepted(result);
|
|
976
989
|
}
|
|
977
990
|
catch (err) {
|
|
978
991
|
logger.warn(`[AgentRunner] AskUserQuestion card send failed for q${i}:`, err);
|
|
@@ -1393,7 +1406,7 @@ export class AgentRunner {
|
|
|
1393
1406
|
});
|
|
1394
1407
|
const fallbackText = '📋 计划审批:AI 已完成规划,等待审批。\n回复 /ask 1 批准 / /ask 2 拒绝';
|
|
1395
1408
|
const result = await sendInteractionPayload(permCtx.adapter, envelope, interaction, fallbackText, permCtx.replyContext);
|
|
1396
|
-
cardSent =
|
|
1409
|
+
cardSent = isInteractionSendAccepted(result);
|
|
1397
1410
|
}
|
|
1398
1411
|
catch (err) {
|
|
1399
1412
|
logger.warn('[AgentRunner] ExitPlanMode card send failed:', err);
|
|
@@ -1441,7 +1454,7 @@ export class AgentRunner {
|
|
|
1441
1454
|
* SDK 原始事件 → 标准 AgentEvent 转换
|
|
1442
1455
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
1443
1456
|
*/
|
|
1444
|
-
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode) {
|
|
1457
|
+
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile, permissionMode, turn) {
|
|
1445
1458
|
let lastSessionId;
|
|
1446
1459
|
let hasTurnActivity = false;
|
|
1447
1460
|
let ignoredPreTurnResult = false;
|
|
@@ -1458,6 +1471,7 @@ export class AgentRunner {
|
|
|
1458
1471
|
let turnCount = 0;
|
|
1459
1472
|
const seenMessageIds = new Set();
|
|
1460
1473
|
let lastModelCall;
|
|
1474
|
+
let lastAssistantWasSynthetic = false;
|
|
1461
1475
|
let lastAssistantUuid;
|
|
1462
1476
|
// 流式收集各次大模型调用(fallback:SDK iterations 为空时使用)
|
|
1463
1477
|
const collectedCalls = [];
|
|
@@ -1466,8 +1480,15 @@ export class AgentRunner {
|
|
|
1466
1480
|
// 提取 session_id(任意 SDK 事件都可能携带)
|
|
1467
1481
|
if (event.session_id && event.session_id !== lastSessionId) {
|
|
1468
1482
|
lastSessionId = event.session_id;
|
|
1469
|
-
this.
|
|
1470
|
-
|
|
1483
|
+
if (await this.acceptDiscoveredSessionId(sessionId, event.session_id, turn)) {
|
|
1484
|
+
yield { type: 'session_id', sessionId: event.session_id };
|
|
1485
|
+
}
|
|
1486
|
+
else {
|
|
1487
|
+
logger.warn(`[AgentRunner] Rejected stale backend discovery: sessionId=${sessionId} backend=${event.session_id} turn=${turn?.turnId ?? 'legacy'}`);
|
|
1488
|
+
const error = new Error('backend discovery rejected by active turn lease');
|
|
1489
|
+
error.code = 'TOPIC_BACKEND_DISCOVERY_REJECTED';
|
|
1490
|
+
throw error;
|
|
1491
|
+
}
|
|
1471
1492
|
}
|
|
1472
1493
|
if (event.type === 'user' && event.uuid === inputId) {
|
|
1473
1494
|
hasTurnActivity = true;
|
|
@@ -1563,7 +1584,9 @@ export class AgentRunner {
|
|
|
1563
1584
|
const streamEvent = event.event;
|
|
1564
1585
|
if (streamEvent?.type === 'message_start' && streamEvent.message?.usage) {
|
|
1565
1586
|
hasTurnActivity = true;
|
|
1587
|
+
lastAssistantWasSynthetic = streamEvent.message.model === '<synthetic>';
|
|
1566
1588
|
lastModelCall = {
|
|
1589
|
+
messageId: streamEvent.message.id,
|
|
1567
1590
|
uuid: event.uuid,
|
|
1568
1591
|
model: streamEvent.message.model,
|
|
1569
1592
|
tokenUsage: streamEvent.message.usage,
|
|
@@ -1573,6 +1596,7 @@ export class AgentRunner {
|
|
|
1573
1596
|
call_index: collectedCalls.length,
|
|
1574
1597
|
model: streamEvent.message.model ?? callModel ?? this.model,
|
|
1575
1598
|
request_id: event.request_id,
|
|
1599
|
+
message_id: streamEvent.message.id,
|
|
1576
1600
|
tokenUsage: { ...streamEvent.message.usage },
|
|
1577
1601
|
});
|
|
1578
1602
|
}
|
|
@@ -1625,27 +1649,40 @@ export class AgentRunner {
|
|
|
1625
1649
|
yield { type: 'state_changed', state: event.state };
|
|
1626
1650
|
}
|
|
1627
1651
|
// assistant: 提取 tool_use 和文本(仅无 text_delta 时提取文本)
|
|
1628
|
-
if (event.type === 'assistant'
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
if (
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1652
|
+
if (event.type === 'assistant') {
|
|
1653
|
+
const isSyntheticAssistant = event.message?.model === '<synthetic>'
|
|
1654
|
+
|| (typeof event.error === 'string' && event.isApiErrorMessage === true);
|
|
1655
|
+
lastAssistantWasSynthetic = isSyntheticAssistant;
|
|
1656
|
+
if (event.message && (event.message.usage || isSyntheticAssistant)) {
|
|
1657
|
+
// A message id is stable across the partial stream and the final
|
|
1658
|
+
// assistant event. Never merge a synthetic/API-error assistant into
|
|
1659
|
+
// the preceding real call, even if an upstream adapter reuses an id
|
|
1660
|
+
// or omits the usage object on the error frame.
|
|
1661
|
+
const sameModelCall = !isSyntheticAssistant
|
|
1662
|
+
&& typeof event.message.id === 'string'
|
|
1663
|
+
&& event.message.id === lastModelCall?.messageId;
|
|
1638
1664
|
lastModelCall = {
|
|
1639
|
-
...lastModelCall,
|
|
1665
|
+
...(sameModelCall ? lastModelCall : undefined),
|
|
1640
1666
|
messageId: event.message.id,
|
|
1667
|
+
uuid: event.uuid,
|
|
1641
1668
|
requestId: event.request_id,
|
|
1642
1669
|
model: event.message.model,
|
|
1643
|
-
tokenUsage: {
|
|
1670
|
+
tokenUsage: sameModelCall ? {
|
|
1644
1671
|
...event.message.usage,
|
|
1645
1672
|
...(lastModelCall?.tokenUsage ?? {}),
|
|
1646
|
-
},
|
|
1673
|
+
} : { ...(event.message.usage ?? {}) },
|
|
1647
1674
|
};
|
|
1648
1675
|
}
|
|
1676
|
+
if (!event.message?.content)
|
|
1677
|
+
continue;
|
|
1678
|
+
hasTurnActivity = true;
|
|
1679
|
+
lastAssistantUuid = event.uuid ?? lastAssistantUuid;
|
|
1680
|
+
const msgId = event.message.id;
|
|
1681
|
+
if (!msgId || !seenMessageIds.has(msgId)) {
|
|
1682
|
+
if (msgId)
|
|
1683
|
+
seenMessageIds.add(msgId);
|
|
1684
|
+
turnCount++;
|
|
1685
|
+
}
|
|
1649
1686
|
// 统计本轮 base agent 全部输出字符数(text + tool_use input)
|
|
1650
1687
|
let turnOutputChars = 0;
|
|
1651
1688
|
for (const content of event.message.content) {
|
|
@@ -1720,6 +1757,18 @@ export class AgentRunner {
|
|
|
1720
1757
|
// 非 Claude(DeepSeek/OpenAI 兼容):cache_read 是服务端 KV cache 不占上下文窗口,
|
|
1721
1758
|
// input_tokens 本身就是完整的上下文输入量。
|
|
1722
1759
|
const u = event.usage;
|
|
1760
|
+
// Claude Code represents transport/API failures as a fresh
|
|
1761
|
+
// `<synthetic>` assistant message. Its usage describes the failed
|
|
1762
|
+
// terminal request (normally zero); event.usage remains the aggregate
|
|
1763
|
+
// of earlier successful calls in the query and must not be presented
|
|
1764
|
+
// as the failed request's context usage.
|
|
1765
|
+
const isSyntheticError = event.is_error === true
|
|
1766
|
+
&& (lastAssistantWasSynthetic || lastModelCall?.model === '<synthetic>');
|
|
1767
|
+
const syntheticUsage = lastModelCall?.tokenUsage ?? {
|
|
1768
|
+
input_tokens: 0,
|
|
1769
|
+
output_tokens: 0,
|
|
1770
|
+
};
|
|
1771
|
+
const terminalUsage = isSyntheticError ? syntheticUsage : u;
|
|
1723
1772
|
const effectiveModel = callModel ?? this.model;
|
|
1724
1773
|
const isClaudeModel = isClaudeContextUsageModel(effectiveModel);
|
|
1725
1774
|
const totalTokens = contextTokensForUsage(u, !!isClaudeModel);
|
|
@@ -1762,7 +1811,11 @@ export class AgentRunner {
|
|
|
1762
1811
|
// 组装 modelCalls:优先 SDK iterations,fallback 流式收集,兜底降级单行。
|
|
1763
1812
|
const callModel_ = callModel ?? this.model;
|
|
1764
1813
|
let modelCalls;
|
|
1765
|
-
const iterArr =
|
|
1814
|
+
const iterArr = !isSyntheticError
|
|
1815
|
+
&& Array.isArray(u?.iterations)
|
|
1816
|
+
&& u.iterations.length > 0
|
|
1817
|
+
? u.iterations
|
|
1818
|
+
: null;
|
|
1766
1819
|
if (iterArr) {
|
|
1767
1820
|
modelCalls = iterArr.map((it, i) => ({
|
|
1768
1821
|
call_index: i, model: callModel_, tokenUsage: it, contextUsage: contextUsageForCall(it),
|
|
@@ -1774,16 +1827,19 @@ export class AgentRunner {
|
|
|
1774
1827
|
contextUsage: contextUsageForCall(call.tokenUsage),
|
|
1775
1828
|
}));
|
|
1776
1829
|
}
|
|
1777
|
-
else if (u) {
|
|
1830
|
+
else if (u && !isSyntheticError) {
|
|
1778
1831
|
// 降级:无逐次数据,写一条累计行
|
|
1779
1832
|
modelCalls = [{ call_index: 0, model: callModel_, tokenUsage: u, contextUsage: contextUsageForCall(u), degraded: true }];
|
|
1780
1833
|
}
|
|
1781
1834
|
// `event.usage` is aggregate query usage when the SDK made multiple
|
|
1782
1835
|
// model calls. The top-level context field must describe a single
|
|
1783
|
-
// current request
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1836
|
+
// current request. For normal results aggregate usage remains available
|
|
1837
|
+
// in tokenUsage; synthetic errors use their own terminal usage instead.
|
|
1838
|
+
const contextUsage = isSyntheticError
|
|
1839
|
+
? lastModelCall?.contextUsage
|
|
1840
|
+
: lastModelCall?.contextUsage
|
|
1841
|
+
?? modelCalls?.at(-1)?.contextUsage
|
|
1842
|
+
?? aggregateContextUsage;
|
|
1787
1843
|
const completeEvent = {
|
|
1788
1844
|
type: 'complete',
|
|
1789
1845
|
result: cleanResult,
|
|
@@ -1796,7 +1852,7 @@ export class AgentRunner {
|
|
|
1796
1852
|
terminalReason: event.terminal_reason,
|
|
1797
1853
|
sessionTitle: event.session_title,
|
|
1798
1854
|
numTurns: event.num_turns,
|
|
1799
|
-
tokenUsage:
|
|
1855
|
+
tokenUsage: terminalUsage,
|
|
1800
1856
|
contextUsage,
|
|
1801
1857
|
lastModelCall,
|
|
1802
1858
|
modelCalls,
|
|
@@ -1871,6 +1927,7 @@ export class AgentRunner {
|
|
|
1871
1927
|
policyCode: 'claude_startup_arguments_too_long',
|
|
1872
1928
|
decisionSource: 'infrastructure',
|
|
1873
1929
|
agentAid: this.permissionContexts.get(sessionId)?.selfAid,
|
|
1930
|
+
agentName: this.permissionContexts.get(sessionId)?.agentName,
|
|
1874
1931
|
summary: detail.message.slice(0, 512),
|
|
1875
1932
|
effect: 'operation_skipped',
|
|
1876
1933
|
});
|
|
@@ -1904,6 +1961,9 @@ export class AgentRunner {
|
|
|
1904
1961
|
}
|
|
1905
1962
|
}
|
|
1906
1963
|
async runQuery(sessionId, prompt, projectPath, initialClaudeSessionId, images, systemPromptAppend, sessionManager, modelOverride, runtimeEnv) {
|
|
1964
|
+
// Validate the daemon-injected session namespace before the SDK inherits
|
|
1965
|
+
// the process environment for any temporary state.
|
|
1966
|
+
getManagedTaskTempDir(runtimeEnv);
|
|
1907
1967
|
// 记录当前 evolcore session ID,用于 Agent ctl 环境变量注入
|
|
1908
1968
|
// 同步用户级配置到内存
|
|
1909
1969
|
this.syncFromUserSettings();
|
|
@@ -1922,8 +1982,22 @@ export class AgentRunner {
|
|
|
1922
1982
|
}
|
|
1923
1983
|
ensureDir(projectPath);
|
|
1924
1984
|
ensureDir(path.join(projectPath, '.claude'));
|
|
1925
|
-
|
|
1926
|
-
|
|
1985
|
+
const topicBinding = modelOverride?.backend?.kind === 'topic';
|
|
1986
|
+
if (topicBinding && (!modelOverride?.turn || modelOverride.turn.sessionId !== sessionId || !this.onSessionIdUpdate)) {
|
|
1987
|
+
const error = new Error('topic backend run requires an active TurnLease and binding callback');
|
|
1988
|
+
error.code = 'TOPIC_TURN_LEASE_REQUIRED';
|
|
1989
|
+
throw error;
|
|
1990
|
+
}
|
|
1991
|
+
const topicAgentSessionId = topicBinding ? modelOverride?.backend?.agentSessionId ?? null : null;
|
|
1992
|
+
const cachedAgentSessionId = topicBinding
|
|
1993
|
+
? (topicAgentSessionId && this.activeSessions.get(sessionId) === topicAgentSessionId
|
|
1994
|
+
? topicAgentSessionId
|
|
1995
|
+
: undefined)
|
|
1996
|
+
: this.activeSessions.get(sessionId);
|
|
1997
|
+
let agentSessionId = initialClaudeSessionId || cachedAgentSessionId;
|
|
1998
|
+
if (topicBinding && topicAgentSessionId !== agentSessionId) {
|
|
1999
|
+
agentSessionId = topicAgentSessionId || undefined;
|
|
2000
|
+
}
|
|
1927
2001
|
// 验证会话文件是否存在且有效(仅在有 agentSessionId 时)
|
|
1928
2002
|
if (agentSessionId) {
|
|
1929
2003
|
const homeDir = os.homedir();
|
|
@@ -1954,11 +2028,18 @@ export class AgentRunner {
|
|
|
1954
2028
|
}
|
|
1955
2029
|
}
|
|
1956
2030
|
if (!isValid) {
|
|
2031
|
+
if (topicBinding) {
|
|
2032
|
+
const error = new Error(`bound topic backend is unavailable: ${agentSessionId}`);
|
|
2033
|
+
error.code = 'TOPIC_BACKEND_UNAVAILABLE';
|
|
2034
|
+
throw error;
|
|
2035
|
+
}
|
|
1957
2036
|
logger.warn(`[AgentRunner] Invalid session file, starting new session`);
|
|
1958
2037
|
agentSessionId = undefined;
|
|
1959
2038
|
this.activeSessions.delete(sessionId);
|
|
1960
2039
|
if (this.onSessionIdUpdate) {
|
|
1961
|
-
this.onSessionIdUpdate(sessionId, '')
|
|
2040
|
+
void Promise.resolve(this.onSessionIdUpdate(sessionId, '')).catch(error => {
|
|
2041
|
+
logger.debug(`[AgentRunner] ignored empty session binding callback failure: ${error instanceof Error ? error.message : String(error)}`);
|
|
2042
|
+
});
|
|
1962
2043
|
}
|
|
1963
2044
|
}
|
|
1964
2045
|
}
|
|
@@ -1973,9 +2054,11 @@ export class AgentRunner {
|
|
|
1973
2054
|
// 缺省回落 agent 级 this.permissionMode。作为 per-call 入参(hook/canUseTool 闭包捕获),
|
|
1974
2055
|
// 不写实例字段,多对端并发互不污染(与 model/effort 同构)。
|
|
1975
2056
|
const requestedPermissionMode = modelOverride?.permissionMode || this.permissionMode;
|
|
1976
|
-
const
|
|
1977
|
-
const callPermissionMode = normalizedPermission.mode;
|
|
2057
|
+
const callPermissionMode = normalizeExecutionPermissionMode(requestedPermissionMode);
|
|
1978
2058
|
const runPermissionContext = this.permissionContexts.get(sessionId);
|
|
2059
|
+
if (callPermissionMode === 'fullaccess' && !hasTrustedFullAccessContext(runPermissionContext)) {
|
|
2060
|
+
throw new Error('Claude fullaccess requires a trusted per-call execution authorization');
|
|
2061
|
+
}
|
|
1979
2062
|
const executionSandbox = resolvePhaseOneExecutionSandbox(callPermissionMode, runPermissionContext?.role);
|
|
1980
2063
|
const inputId = modelOverride?.turn?.inputId ?? crypto.randomUUID();
|
|
1981
2064
|
if (executionSandbox.state !== 'off') {
|
|
@@ -1990,6 +2073,7 @@ export class AgentRunner {
|
|
|
1990
2073
|
reason: detail.message.slice(0, 512),
|
|
1991
2074
|
sessionId,
|
|
1992
2075
|
agentAid: context?.selfAid,
|
|
2076
|
+
agentName: context?.agentName,
|
|
1993
2077
|
permissionMode: callPermissionMode,
|
|
1994
2078
|
role: context?.role,
|
|
1995
2079
|
callId: inputId,
|
|
@@ -2008,6 +2092,7 @@ export class AgentRunner {
|
|
|
2008
2092
|
policyCode: SANDBOX_INITIALIZATION_FAILED,
|
|
2009
2093
|
decisionSource: 'infrastructure',
|
|
2010
2094
|
agentAid: context?.selfAid,
|
|
2095
|
+
agentName: context?.agentName,
|
|
2011
2096
|
permissionMode: callPermissionMode,
|
|
2012
2097
|
summary: detail.message.slice(0, 512),
|
|
2013
2098
|
effect: 'operation_skipped',
|
|
@@ -2054,6 +2139,7 @@ export class AgentRunner {
|
|
|
2054
2139
|
summary,
|
|
2055
2140
|
sessionId,
|
|
2056
2141
|
agentAid: ctx?.selfAid,
|
|
2142
|
+
agentName: ctx?.agentName,
|
|
2057
2143
|
permissionMode: callPermissionMode,
|
|
2058
2144
|
channel: ctx?.channel,
|
|
2059
2145
|
actorId: ctx?.userId,
|
|
@@ -2074,6 +2160,7 @@ export class AgentRunner {
|
|
|
2074
2160
|
policyCode: code,
|
|
2075
2161
|
decisionSource: 'policy',
|
|
2076
2162
|
agentAid: ctx?.selfAid,
|
|
2163
|
+
agentName: ctx?.agentName,
|
|
2077
2164
|
permissionMode: callPermissionMode,
|
|
2078
2165
|
summary,
|
|
2079
2166
|
effect: 'operation_skipped',
|
|
@@ -2122,6 +2209,14 @@ export class AgentRunner {
|
|
|
2122
2209
|
delete toolInput.pages;
|
|
2123
2210
|
}
|
|
2124
2211
|
let updatedInput = toolInput === originalInput ? undefined : toolInput;
|
|
2212
|
+
if (callPermissionMode === 'fullaccess') {
|
|
2213
|
+
if (!hasTrustedFullAccessContext(this.permissionContexts.get(sessionId))) {
|
|
2214
|
+
return hookDecision('deny', 'fullaccess execution authorization is no longer active', updatedInput);
|
|
2215
|
+
}
|
|
2216
|
+
return hookDecision('allow', undefined, toolName === 'Bash'
|
|
2217
|
+
? { ...toolInput, dangerouslyDisableSandbox: true }
|
|
2218
|
+
: updatedInput);
|
|
2219
|
+
}
|
|
2125
2220
|
const preparedSafeOutputCommand = toolName === 'Bash' && typeof toolInput.command === 'string'
|
|
2126
2221
|
? toolInput.command
|
|
2127
2222
|
: undefined;
|
|
@@ -2143,6 +2238,7 @@ export class AgentRunner {
|
|
|
2143
2238
|
: undefined;
|
|
2144
2239
|
const preflight = evaluateToolPreflight(toolName, toolInput, {
|
|
2145
2240
|
sessionId,
|
|
2241
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2146
2242
|
selfAid: permCtx?.selfAid,
|
|
2147
2243
|
channel: permCtx?.channel,
|
|
2148
2244
|
userId: permCtx?.userId,
|
|
@@ -2179,6 +2275,7 @@ export class AgentRunner {
|
|
|
2179
2275
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
2180
2276
|
const readonlyContext = {
|
|
2181
2277
|
sessionId,
|
|
2278
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2182
2279
|
channel: permCtx?.channel,
|
|
2183
2280
|
peerId: permCtx?.userId,
|
|
2184
2281
|
role: permCtx?.role,
|
|
@@ -2250,6 +2347,20 @@ export class AgentRunner {
|
|
|
2250
2347
|
if (toolName === 'ExitPlanMode') {
|
|
2251
2348
|
return await this.handleExitPlanMode(sessionId, input, options);
|
|
2252
2349
|
}
|
|
2350
|
+
if (callPermissionMode === 'fullaccess') {
|
|
2351
|
+
if (!hasTrustedFullAccessContext(this.permissionContexts.get(sessionId))) {
|
|
2352
|
+
return {
|
|
2353
|
+
behavior: 'deny',
|
|
2354
|
+
message: 'fullaccess execution authorization is no longer active',
|
|
2355
|
+
decisionClassification: 'user_reject',
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
return {
|
|
2359
|
+
behavior: 'allow',
|
|
2360
|
+
updatedInput: toolName === 'Bash' ? { ...input, dangerouslyDisableSandbox: true } : input,
|
|
2361
|
+
decisionClassification: 'user_permanent',
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2253
2364
|
if (toolName === 'Bash' && 'dangerouslyDisableSandbox' in input) {
|
|
2254
2365
|
input = { ...input };
|
|
2255
2366
|
delete input.dangerouslyDisableSandbox;
|
|
@@ -2267,6 +2378,7 @@ export class AgentRunner {
|
|
|
2267
2378
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
2268
2379
|
const preflight = evaluateToolPreflight(toolName, input, {
|
|
2269
2380
|
sessionId,
|
|
2381
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2270
2382
|
selfAid: permCtx?.selfAid,
|
|
2271
2383
|
channel: permCtx?.channel,
|
|
2272
2384
|
userId: permCtx?.userId,
|
|
@@ -2339,6 +2451,7 @@ export class AgentRunner {
|
|
|
2339
2451
|
const permCtx = this.permissionContexts.get(sessionId);
|
|
2340
2452
|
const readonlyContext = {
|
|
2341
2453
|
sessionId,
|
|
2454
|
+
managedTempDir: permCtx?.managedTempDir,
|
|
2342
2455
|
channel: permCtx?.channel,
|
|
2343
2456
|
peerId: permCtx?.userId,
|
|
2344
2457
|
role: permCtx?.role,
|
|
@@ -2403,7 +2516,9 @@ export class AgentRunner {
|
|
|
2403
2516
|
};
|
|
2404
2517
|
};
|
|
2405
2518
|
const useSettingSources = this.config?.agents?.claude?.useSettingSources !== false;
|
|
2406
|
-
const settingSources =
|
|
2519
|
+
const settingSources = callPermissionMode === 'fullaccess'
|
|
2520
|
+
? []
|
|
2521
|
+
: useSettingSources ? ['project', 'user'] : [];
|
|
2407
2522
|
const enableSummaries = this.config?.agents?.claude?.agentProgressSummaries !== false;
|
|
2408
2523
|
const excludeDynamic = this.config?.agents?.claude?.excludeDynamicSections === true;
|
|
2409
2524
|
// 公共 options(新旧模式共用)
|
|
@@ -2426,7 +2541,9 @@ export class AgentRunner {
|
|
|
2426
2541
|
}
|
|
2427
2542
|
const sdkModel = resolveSdkModel(callModel, this.baseUrl);
|
|
2428
2543
|
const capabilityOptions = await this.resolveCapabilityRunOptions(projectPath);
|
|
2429
|
-
const managedSettings =
|
|
2544
|
+
const managedSettings = callPermissionMode === 'fullaccess'
|
|
2545
|
+
? undefined
|
|
2546
|
+
: buildClaudeManagedLockdownSettings(capabilityOptions);
|
|
2430
2547
|
const sandboxOptions = executionSandbox.state === 'off'
|
|
2431
2548
|
? { enabled: false }
|
|
2432
2549
|
: await (async () => {
|
|
@@ -2466,9 +2583,11 @@ export class AgentRunner {
|
|
|
2466
2583
|
};
|
|
2467
2584
|
})();
|
|
2468
2585
|
if (executionSandbox.state === 'off') {
|
|
2469
|
-
logger.info(
|
|
2470
|
-
? '[ClaudeSandbox]
|
|
2471
|
-
:
|
|
2586
|
+
logger.info(callPermissionMode === 'fullaccess'
|
|
2587
|
+
? '[ClaudeSandbox] disabled for trusted per-call fullaccess execution'
|
|
2588
|
+
: process.platform === 'linux'
|
|
2589
|
+
? '[ClaudeSandbox] task sandbox disabled for authenticated owner bypass; outer H-class guard remains active'
|
|
2590
|
+
: '[ClaudeSandbox] disabled for authenticated owner bypass task');
|
|
2472
2591
|
}
|
|
2473
2592
|
const handleClaudeStderr = (msg) => {
|
|
2474
2593
|
const trimmed = msg.trim();
|
|
@@ -2494,16 +2613,17 @@ export class AgentRunner {
|
|
|
2494
2613
|
model: sdkModel,
|
|
2495
2614
|
...capabilityOptions,
|
|
2496
2615
|
strictMcpConfig: false,
|
|
2497
|
-
managedSettings,
|
|
2616
|
+
...(managedSettings ? { managedSettings } : {}),
|
|
2498
2617
|
...(callEffort ? { effort: callEffort } : {}),
|
|
2499
2618
|
...(this.claudeExecutablePath ? { pathToClaudeCodeExecutable: this.claudeExecutablePath } : {}),
|
|
2500
|
-
...(executionSandbox.state === 'off' && process.platform === 'linux' ? {
|
|
2619
|
+
...(executionSandbox.state === 'off' && callPermissionMode !== 'fullaccess' && process.platform === 'linux' ? {
|
|
2501
2620
|
spawnClaudeCodeProcess: (options) => spawnClaudeWithHClassGuard(options, resolvePaths().root, handleClaudeStderr),
|
|
2502
2621
|
} : {}),
|
|
2503
2622
|
autoCompactWindow: autoCompactWindowForModel(sdkModel),
|
|
2504
2623
|
advisorModel: 'haiku',
|
|
2505
2624
|
canUseTool: canUseToolCallback,
|
|
2506
2625
|
permissionMode: sdkPermissionMode,
|
|
2626
|
+
...(callPermissionMode === 'fullaccess' ? { allowDangerouslySkipPermissions: true } : {}),
|
|
2507
2627
|
sandbox: sandboxOptions,
|
|
2508
2628
|
persistSession: true,
|
|
2509
2629
|
includePartialMessages: true,
|
|
@@ -2625,9 +2745,14 @@ export class AgentRunner {
|
|
|
2625
2745
|
const currentSession = await sessionManager.getSessionById?.(sessionId);
|
|
2626
2746
|
if (currentSession?.metadata?.resumeAt) {
|
|
2627
2747
|
resumeAt = currentSession.metadata.resumeAt;
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2748
|
+
if (typeof sessionManager.patchSessionMetadata === 'function') {
|
|
2749
|
+
await sessionManager.patchSessionMetadata(sessionId, { resumeAt: undefined });
|
|
2750
|
+
}
|
|
2751
|
+
else {
|
|
2752
|
+
const newMeta = { ...currentSession.metadata };
|
|
2753
|
+
delete newMeta.resumeAt;
|
|
2754
|
+
await sessionManager.updateSession(sessionId, { metadata: newMeta });
|
|
2755
|
+
}
|
|
2631
2756
|
logger.info(`[AgentRunner] Consuming resumeAt: ${resumeAt}`);
|
|
2632
2757
|
}
|
|
2633
2758
|
}
|
|
@@ -2639,10 +2764,14 @@ export class AgentRunner {
|
|
|
2639
2764
|
const msgStream = new MessageStream();
|
|
2640
2765
|
if (images && images.length > 0) {
|
|
2641
2766
|
logger.info('[AgentRunner] Creating query with images:', images.length, 'first image size:', images[0]?.data?.length ?? 0);
|
|
2642
|
-
logger.debug('[AgentRunner] Skipping resume for image message to avoid history conflict');
|
|
2643
2767
|
msgStream.push(prompt, images, inputId);
|
|
2644
2768
|
try {
|
|
2645
|
-
|
|
2769
|
+
// Topic bindings are authoritative for every input shape. Starting
|
|
2770
|
+
// an unresumed Claude query for a bound topic would discover a second
|
|
2771
|
+
// backend and then fail activation with a binding conflict.
|
|
2772
|
+
sdkStream = topicBinding
|
|
2773
|
+
? createQuery(msgStream, agentSessionId, resumeAt)
|
|
2774
|
+
: createQuery(msgStream);
|
|
2646
2775
|
}
|
|
2647
2776
|
catch (error) {
|
|
2648
2777
|
this.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
@@ -2676,14 +2805,16 @@ export class AgentRunner {
|
|
|
2676
2805
|
}
|
|
2677
2806
|
this.activeMessageStreams.set(sessionId, msgStream);
|
|
2678
2807
|
this.activeQueries.set(sessionId, sdkStream);
|
|
2679
|
-
|
|
2808
|
+
let resolveDone;
|
|
2809
|
+
const donePromise = new Promise(resolve => { resolveDone = resolve; });
|
|
2810
|
+
this.streamDoneResolvers.set(sessionId, resolveDone);
|
|
2680
2811
|
this.streamDone.set(sessionId, donePromise);
|
|
2681
2812
|
// 保存 interrupt 能力(不写 activeStreams,由 registerStream 管理活跃状态)
|
|
2682
2813
|
if ('interrupt' in sdkStream && typeof sdkStream.interrupt === 'function') {
|
|
2683
2814
|
this.interruptFns.set(sessionId, () => sdkStream.interrupt());
|
|
2684
2815
|
}
|
|
2685
2816
|
// 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
|
|
2686
|
-
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode);
|
|
2817
|
+
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile, callPermissionMode, modelOverride?.turn);
|
|
2687
2818
|
const self = this;
|
|
2688
2819
|
return (async function* () {
|
|
2689
2820
|
try {
|
|
@@ -2691,10 +2822,15 @@ export class AgentRunner {
|
|
|
2691
2822
|
}
|
|
2692
2823
|
finally {
|
|
2693
2824
|
self.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2694
|
-
|
|
2695
|
-
self.streamDoneResolvers.
|
|
2696
|
-
|
|
2697
|
-
self.
|
|
2825
|
+
resolveDone();
|
|
2826
|
+
if (self.streamDoneResolvers.get(sessionId) === resolveDone)
|
|
2827
|
+
self.streamDoneResolvers.delete(sessionId);
|
|
2828
|
+
if (self.streamDone.get(sessionId) === donePromise)
|
|
2829
|
+
self.streamDone.delete(sessionId);
|
|
2830
|
+
if (self.activeQueries.get(sessionId) === sdkStream)
|
|
2831
|
+
self.activeQueries.delete(sessionId);
|
|
2832
|
+
if (self.activeMessageStreams.get(sessionId) === msgStream)
|
|
2833
|
+
self.activeMessageStreams.delete(sessionId);
|
|
2698
2834
|
}
|
|
2699
2835
|
})();
|
|
2700
2836
|
}
|
|
@@ -2762,19 +2898,64 @@ export class AgentRunner {
|
|
|
2762
2898
|
injectUserMessage(sessionId, text) {
|
|
2763
2899
|
this.activeMessageStreams.get(sessionId)?.push(text);
|
|
2764
2900
|
}
|
|
2901
|
+
async acceptDiscoveredSessionId(sessionId, agentSessionId, turn) {
|
|
2902
|
+
const result = this.onSessionIdUpdate
|
|
2903
|
+
? (turn === undefined
|
|
2904
|
+
? await this.onSessionIdUpdate(sessionId, agentSessionId)
|
|
2905
|
+
: await this.onSessionIdUpdate(sessionId, agentSessionId, { turn }))
|
|
2906
|
+
: 'legacy_updated';
|
|
2907
|
+
const accepted = result === undefined || result === 'activated' || result === 'already_active' || result === 'legacy_updated';
|
|
2908
|
+
if (accepted) {
|
|
2909
|
+
this.activeSessions.set(sessionId, agentSessionId);
|
|
2910
|
+
}
|
|
2911
|
+
return accepted;
|
|
2912
|
+
}
|
|
2765
2913
|
updateSessionId(sessionId, agentSessionId) {
|
|
2766
2914
|
logger.info(`[AgentRunner] updateSessionId called: sessionId=${sessionId}, agentSessionId=${agentSessionId}`);
|
|
2767
|
-
this.activeSessions.
|
|
2768
|
-
|
|
2769
|
-
this.
|
|
2915
|
+
const previousAgentSessionId = this.activeSessions.get(sessionId);
|
|
2916
|
+
const canApply = () => {
|
|
2917
|
+
const current = this.activeSessions.get(sessionId);
|
|
2918
|
+
return current === previousAgentSessionId || (previousAgentSessionId === undefined && current === undefined);
|
|
2919
|
+
};
|
|
2920
|
+
if (!this.onSessionIdUpdate) {
|
|
2921
|
+
if (!canApply())
|
|
2922
|
+
return;
|
|
2923
|
+
if (agentSessionId)
|
|
2924
|
+
this.activeSessions.set(sessionId, agentSessionId);
|
|
2925
|
+
else if (this.activeSessions.get(sessionId) === previousAgentSessionId)
|
|
2926
|
+
this.activeSessions.delete(sessionId);
|
|
2927
|
+
return;
|
|
2770
2928
|
}
|
|
2929
|
+
void Promise.resolve(this.onSessionIdUpdate(sessionId, agentSessionId)).then(result => {
|
|
2930
|
+
if (!canApply())
|
|
2931
|
+
return;
|
|
2932
|
+
if (result === undefined || result === 'activated' || result === 'already_active' || result === 'legacy_updated') {
|
|
2933
|
+
if (agentSessionId)
|
|
2934
|
+
this.activeSessions.set(sessionId, agentSessionId);
|
|
2935
|
+
else if (this.activeSessions.get(sessionId) === previousAgentSessionId)
|
|
2936
|
+
this.activeSessions.delete(sessionId);
|
|
2937
|
+
}
|
|
2938
|
+
else if (!agentSessionId) {
|
|
2939
|
+
if (this.activeSessions.get(sessionId) === previousAgentSessionId)
|
|
2940
|
+
this.activeSessions.delete(sessionId);
|
|
2941
|
+
}
|
|
2942
|
+
}).catch(error => {
|
|
2943
|
+
logger.warn(`[AgentRunner] session binding callback failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2944
|
+
if (!agentSessionId && canApply() && this.activeSessions.get(sessionId) === previousAgentSessionId) {
|
|
2945
|
+
this.activeSessions.delete(sessionId);
|
|
2946
|
+
}
|
|
2947
|
+
});
|
|
2771
2948
|
}
|
|
2772
|
-
runSessionCommand(prompt, agentSessionId, projectPath) {
|
|
2949
|
+
runSessionCommand(prompt, agentSessionId, projectPath, modelOverride) {
|
|
2950
|
+
const callModel = modelOverride?.model || this.model;
|
|
2951
|
+
const callEffort = (modelOverride?.effortMode === 'model_default'
|
|
2952
|
+
? undefined
|
|
2953
|
+
: modelOverride?.effort ?? this.effort);
|
|
2773
2954
|
return query({
|
|
2774
2955
|
prompt,
|
|
2775
2956
|
options: {
|
|
2776
2957
|
cwd: projectPath,
|
|
2777
|
-
model: resolveSdkModel(
|
|
2958
|
+
model: resolveSdkModel(callModel, this.baseUrl),
|
|
2778
2959
|
resume: agentSessionId,
|
|
2779
2960
|
maxTurns: 1,
|
|
2780
2961
|
tools: [],
|
|
@@ -2783,6 +2964,7 @@ export class AgentRunner {
|
|
|
2783
2964
|
strictMcpConfig: true,
|
|
2784
2965
|
settingSources: [],
|
|
2785
2966
|
managedSettings: buildClaudeManagedLockdownSettings(),
|
|
2967
|
+
...(callEffort ? { effort: callEffort } : {}),
|
|
2786
2968
|
permissionMode: this.toSdkPermissionMode(),
|
|
2787
2969
|
env: this.getAgentEnv()
|
|
2788
2970
|
}
|
|
@@ -2791,31 +2973,74 @@ export class AgentRunner {
|
|
|
2791
2973
|
/**
|
|
2792
2974
|
* 主动压缩会话上下文
|
|
2793
2975
|
*/
|
|
2794
|
-
async compactSession(sessionId, agentSessionId, projectPath) {
|
|
2976
|
+
async compactSession(sessionId, agentSessionId, projectPath, modelOverride) {
|
|
2977
|
+
const startedAt = Date.now();
|
|
2978
|
+
let eventCount = 0;
|
|
2979
|
+
let lastEvent;
|
|
2795
2980
|
try {
|
|
2796
|
-
logger.info(`[AgentRunner] Compacting session:
|
|
2797
|
-
const stream = this.runSessionCommand('/compact', agentSessionId, projectPath);
|
|
2981
|
+
logger.info(`[AgentRunner] Compacting session: session=${sessionId} agent=${agentSessionId} project=${projectPath}`);
|
|
2982
|
+
const stream = this.runSessionCommand('/compact', agentSessionId, projectPath, modelOverride);
|
|
2798
2983
|
this.activeStreams.set(sessionId, stream);
|
|
2799
2984
|
try {
|
|
2800
|
-
let
|
|
2985
|
+
let boundary;
|
|
2986
|
+
let compactStatus;
|
|
2801
2987
|
for await (const event of stream) {
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2988
|
+
eventCount += 1;
|
|
2989
|
+
lastEvent = { type: event?.type, subtype: event?.subtype };
|
|
2990
|
+
if (event.type === 'system' && event.subtype === 'status' && event.compact_result) {
|
|
2991
|
+
compactStatus = {
|
|
2992
|
+
result: event.compact_result,
|
|
2993
|
+
error: event.compact_error,
|
|
2994
|
+
};
|
|
2805
2995
|
}
|
|
2996
|
+
if (event.type === 'system' && event.subtype === 'compact_boundary')
|
|
2997
|
+
boundary = event;
|
|
2998
|
+
}
|
|
2999
|
+
const durationMs = Date.now() - startedAt;
|
|
3000
|
+
if (compactStatus?.result === 'failed') {
|
|
3001
|
+
const result = {
|
|
3002
|
+
ok: false,
|
|
3003
|
+
code: 'sdk_error',
|
|
3004
|
+
message: compactStatus.error || 'Claude SDK reported compact failure',
|
|
3005
|
+
durationMs,
|
|
3006
|
+
eventCount,
|
|
3007
|
+
lastEvent,
|
|
3008
|
+
};
|
|
3009
|
+
logger.warn(`[AgentRunner] Compact failed: session=${sessionId} agent=${agentSessionId} code=${result.code} events=${eventCount} durationMs=${durationMs} message=${result.message}`);
|
|
3010
|
+
return result;
|
|
2806
3011
|
}
|
|
2807
|
-
if (!
|
|
2808
|
-
|
|
3012
|
+
if (!boundary) {
|
|
3013
|
+
const result = {
|
|
3014
|
+
ok: false,
|
|
3015
|
+
code: 'missing_boundary',
|
|
3016
|
+
message: 'Compact stream ended without compact_boundary event',
|
|
3017
|
+
durationMs,
|
|
3018
|
+
eventCount,
|
|
3019
|
+
lastEvent,
|
|
3020
|
+
};
|
|
3021
|
+
logger.warn(`[AgentRunner] Compact failed: session=${sessionId} agent=${agentSessionId} code=${result.code} events=${eventCount} durationMs=${durationMs}`);
|
|
3022
|
+
return result;
|
|
2809
3023
|
}
|
|
2810
|
-
|
|
3024
|
+
const result = {
|
|
3025
|
+
ok: true,
|
|
3026
|
+
durationMs,
|
|
3027
|
+
eventCount,
|
|
3028
|
+
preTokens: boundary.compact_metadata?.pre_tokens,
|
|
3029
|
+
postTokens: boundary.compact_metadata?.post_tokens,
|
|
3030
|
+
};
|
|
3031
|
+
logger.info(`[AgentRunner] Compact completed: session=${sessionId} agent=${agentSessionId} events=${eventCount} durationMs=${durationMs} pre_tokens=${result.preTokens ?? 'unknown'} post_tokens=${result.postTokens ?? 'unknown'}`);
|
|
3032
|
+
return result;
|
|
2811
3033
|
}
|
|
2812
3034
|
finally {
|
|
2813
3035
|
this.activeStreams.delete(sessionId);
|
|
2814
3036
|
}
|
|
2815
3037
|
}
|
|
2816
3038
|
catch (error) {
|
|
2817
|
-
|
|
2818
|
-
|
|
3039
|
+
const durationMs = Date.now() - startedAt;
|
|
3040
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3041
|
+
const result = { ok: false, code: 'sdk_error', message, durationMs, eventCount, lastEvent };
|
|
3042
|
+
logger.error(`[AgentRunner] Compact failed: session=${sessionId} agent=${agentSessionId} project=${projectPath} code=${result.code} events=${eventCount} durationMs=${durationMs} error=${message}`);
|
|
3043
|
+
return result;
|
|
2819
3044
|
}
|
|
2820
3045
|
}
|
|
2821
3046
|
/**
|
|
@@ -2836,7 +3061,7 @@ export class AgentRunner {
|
|
|
2836
3061
|
}
|
|
2837
3062
|
if (cleared) {
|
|
2838
3063
|
this.activeSessions.delete(sessionId);
|
|
2839
|
-
this.onSessionIdUpdate?.(sessionId, '');
|
|
3064
|
+
await this.onSessionIdUpdate?.(sessionId, '');
|
|
2840
3065
|
}
|
|
2841
3066
|
else {
|
|
2842
3067
|
logger.warn('[AgentRunner] Clear stream ended without session reset signal');
|
|
@@ -2853,16 +3078,76 @@ export class AgentRunner {
|
|
|
2853
3078
|
}
|
|
2854
3079
|
}
|
|
2855
3080
|
async closeSession(sessionId) {
|
|
3081
|
+
const capturedAgentSessionId = this.activeSessions.get(sessionId);
|
|
3082
|
+
const capturedPermissionContext = this.permissionContexts.get(sessionId);
|
|
2856
3083
|
this.permissionContexts.get(sessionId)?.pauseController?.cancel();
|
|
2857
3084
|
const query = this.activeQueries.get(sessionId);
|
|
3085
|
+
const messageStream = this.activeMessageStreams.get(sessionId);
|
|
3086
|
+
const registeredStream = this.activeStreams.get(sessionId);
|
|
3087
|
+
const interrupt = this.interruptFns.get(sessionId);
|
|
3088
|
+
const done = this.streamDone.get(sessionId);
|
|
3089
|
+
const resolveDone = this.streamDoneResolvers.get(sessionId);
|
|
3090
|
+
let interruptError;
|
|
3091
|
+
if (interrupt) {
|
|
3092
|
+
try {
|
|
3093
|
+
await interrupt();
|
|
3094
|
+
}
|
|
3095
|
+
catch (error) {
|
|
3096
|
+
interruptError = error;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
messageStream?.end();
|
|
2858
3100
|
if (query && typeof query.close === 'function')
|
|
2859
|
-
query.close();
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
3101
|
+
await query.close();
|
|
3102
|
+
if (done && registeredStream) {
|
|
3103
|
+
let timer;
|
|
3104
|
+
try {
|
|
3105
|
+
await Promise.race([
|
|
3106
|
+
done,
|
|
3107
|
+
new Promise((_, reject) => {
|
|
3108
|
+
timer = setTimeout(() => reject(new Error(`Claude session did not become quiescent: ${sessionId}`)), 30_000);
|
|
3109
|
+
timer.unref?.();
|
|
3110
|
+
}),
|
|
3111
|
+
]);
|
|
3112
|
+
}
|
|
3113
|
+
finally {
|
|
3114
|
+
if (timer)
|
|
3115
|
+
clearTimeout(timer);
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
else if (done) {
|
|
3119
|
+
// runQuery resolved but ResponseEngine never registered/consumed the
|
|
3120
|
+
// wrapper. No consumer can reach its finally block, so close the exact
|
|
3121
|
+
// captured lifecycle explicitly.
|
|
3122
|
+
resolveDone?.();
|
|
3123
|
+
}
|
|
3124
|
+
const lifecycleReplaced = (query && this.activeQueries.get(sessionId) !== undefined && this.activeQueries.get(sessionId) !== query)
|
|
3125
|
+
|| (messageStream && this.activeMessageStreams.get(sessionId) !== undefined && this.activeMessageStreams.get(sessionId) !== messageStream)
|
|
3126
|
+
|| (registeredStream && this.activeStreams.get(sessionId) !== undefined && this.activeStreams.get(sessionId) !== registeredStream);
|
|
3127
|
+
if (lifecycleReplaced) {
|
|
3128
|
+
const error = new Error(`Claude session lifecycle changed during close: ${sessionId}`);
|
|
3129
|
+
error.code = 'BACKEND_RUNTIME_REPLACED';
|
|
3130
|
+
throw error;
|
|
3131
|
+
}
|
|
3132
|
+
if (this.activeQueries.get(sessionId) === query)
|
|
3133
|
+
this.activeQueries.delete(sessionId);
|
|
3134
|
+
if (this.activeMessageStreams.get(sessionId) === messageStream)
|
|
3135
|
+
this.activeMessageStreams.delete(sessionId);
|
|
3136
|
+
if (this.activeStreams.get(sessionId) === registeredStream)
|
|
3137
|
+
this.activeStreams.delete(sessionId);
|
|
3138
|
+
if (this.interruptFns.get(sessionId) === interrupt)
|
|
3139
|
+
this.interruptFns.delete(sessionId);
|
|
3140
|
+
if (this.streamDone.get(sessionId) === done)
|
|
3141
|
+
this.streamDone.delete(sessionId);
|
|
3142
|
+
if (this.streamDoneResolvers.get(sessionId) === resolveDone)
|
|
3143
|
+
this.streamDoneResolvers.delete(sessionId);
|
|
3144
|
+
if (this.activeSessions.get(sessionId) === capturedAgentSessionId)
|
|
3145
|
+
this.activeSessions.delete(sessionId);
|
|
3146
|
+
if (this.permissionContexts.get(sessionId) === capturedPermissionContext)
|
|
3147
|
+
this.permissionContexts.delete(sessionId);
|
|
2865
3148
|
this.cleanupSandboxSettingsFiles(sessionId);
|
|
3149
|
+
if (interruptError)
|
|
3150
|
+
throw interruptError;
|
|
2866
3151
|
}
|
|
2867
3152
|
async dispose() {
|
|
2868
3153
|
const interrupts = [...this.interruptFns.values()].map(async (interrupt) => {
|
|
@@ -2978,9 +3263,9 @@ export class ClaudeAgentPlugin {
|
|
|
2978
3263
|
const syntheticConfig = { agents: { claude: override } };
|
|
2979
3264
|
const anthropic = resolveAnthropicConfig(syntheticConfig, override);
|
|
2980
3265
|
const merged = {
|
|
2981
|
-
agents: { claude: { ...(override || {})
|
|
3266
|
+
agents: { claude: { ...(override || {}) } },
|
|
2982
3267
|
};
|
|
2983
|
-
const agentRunner = new AgentRunner(anthropic.apiKey, anthropic.model, callbacks.onSessionIdUpdate, anthropic.baseUrl, merged);
|
|
3268
|
+
const agentRunner = new AgentRunner(anthropic.apiKey, anthropic.model, callbacks.onSessionIdUpdate, anthropic.baseUrl, merged, { agentAid: agent.aid, agentConfig: agent.config });
|
|
2984
3269
|
if (anthropic.effort) {
|
|
2985
3270
|
agentRunner.setEffort(anthropic.effort);
|
|
2986
3271
|
}
|