



























@@ -50,13 +50,18 @@ type OpenClawAcpxRuntimeOptions = AcpRuntimeOptions & {
5050openclawWrapperRoot?: string;
5151openclawGatewayInstanceId?: string;
5252openclawProcessLeaseStore?: AcpxProcessLeaseStore;
53+openclawToolsMcpBridgeEnabled?: boolean;
5354};
5455type AcpxRuntimeTestOptions = Record<string, unknown> & {
5556openclawProcessCleanup?: AcpxProcessCleanupDeps;
5657};
5758type OpenClawRuntimeTurnInput = Parameters<NonNullable<AcpRuntime["startTurn"]>>[0];
5859type OpenClawRuntimeEnsureInput = Parameters<AcpRuntime["ensureSession"]>[0];
5960type AcpxDelegateEnsureInput = Parameters<BaseAcpxRuntime["ensureSession"]>[0];
61+type AcpxMcpServer = NonNullable<AcpRuntimeOptions["mcpServers"]>[number];
62+63+const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
64+const OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV = "OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY";
60656166type ResetAwareSessionStore = AcpSessionStore & {
6267markFresh: (sessionKey: string) => void;
@@ -682,6 +687,33 @@ function shouldUseDistinctBridgeDelegate(options: AcpRuntimeOptions): boolean {
682687return Array.isArray(mcpServers) && mcpServers.length > 0;
683688}
684689690+function withOpenClawToolsMcpSessionEnv(params: {
691+enabled: boolean | undefined;
692+mcpServers: AcpRuntimeOptions["mcpServers"];
693+sessionKey: string;
694+}): AcpRuntimeOptions["mcpServers"] {
695+const sessionKey = params.sessionKey.trim();
696+if (!params.enabled || !sessionKey || !params.mcpServers?.length) {
697+return params.mcpServers;
698+}
699+let changed = false;
700+const nextServers = params.mcpServers.map((server): AcpxMcpServer => {
701+if (server.name !== ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME || !("command" in server)) {
702+return server;
703+}
704+changed = true;
705+const env = [
706+ ...server.env.filter((entry) => entry.name !== OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV),
707+{
708+name: OPENCLAW_TOOLS_MCP_AGENT_SESSION_KEY_ENV,
709+value: sessionKey,
710+},
711+];
712+return { ...server, env };
713+});
714+return changed ? nextServers : params.mcpServers;
715+}
716+685717/** OpenClaw-managed ACP runtime implementation backed by the upstream acpx runtime. */
686718export class AcpxRuntime implements AcpRuntime {
687719private readonly sessionStore: ResetAwareSessionStore;
@@ -693,6 +725,10 @@ export class AcpxRuntime implements AcpRuntime {
693725private readonly delegate: BaseAcpxRuntime;
694726private readonly bridgeSafeDelegate: BaseAcpxRuntime;
695727private readonly probeDelegate: BaseAcpxRuntime;
728+private readonly delegateOptions: AcpRuntimeOptions;
729+private readonly delegateTestOptions: BaseAcpxRuntimeTestOptions;
730+private readonly openclawToolsMcpBridgeEnabled: boolean;
731+private readonly openclawToolsSessionDelegates = new Map<string, BaseAcpxRuntime>();
696732private readonly processCleanupDeps: AcpxProcessCleanupDeps | undefined;
697733private readonly wrapperRoot: string | undefined;
698734private readonly gatewayInstanceId: string | undefined;
@@ -706,6 +742,7 @@ export class AcpxRuntime implements AcpRuntime {
706742this.wrapperRoot = options.openclawWrapperRoot;
707743this.gatewayInstanceId = options.openclawGatewayInstanceId;
708744this.processLeaseStore = options.openclawProcessLeaseStore;
745+this.openclawToolsMcpBridgeEnabled = options.openclawToolsMcpBridgeEnabled === true;
709746this.cwd = options.cwd;
710747this.sessionStore = createResetAwareSessionStore(options.sessionStore, {
711748gatewayInstanceId: this.gatewayInstanceId,
@@ -723,20 +760,21 @@ export class AcpxRuntime implements AcpRuntime {
723760sessionStore: this.sessionStore,
724761agentRegistry: this.scopedAgentRegistry,
725762};
726-this.delegate = new BaseAcpxRuntime(
727-sharedOptions,
728-delegateTestOptions as BaseAcpxRuntimeTestOptions,
729-);
763+this.delegateOptions = sharedOptions;
764+this.delegateTestOptions = delegateTestOptions as BaseAcpxRuntimeTestOptions;
765+this.delegate = new BaseAcpxRuntime(sharedOptions, this.delegateTestOptions);
730766this.bridgeSafeDelegate = shouldUseDistinctBridgeDelegate(options)
731767 ? new BaseAcpxRuntime(
732768{
733769 ...sharedOptions,
734770mcpServers: [],
735771},
736-delegateTestOptions as BaseAcpxRuntimeTestOptions,
772+this.delegateTestOptions,
737773)
738774 : this.delegate;
739-this.probeDelegate = this.resolveDelegateForAgent(resolveProbeAgentName(options));
775+this.probeDelegate = this.openclawToolsMcpBridgeEnabled
776+ ? this.bridgeSafeDelegate
777+ : this.resolveDelegateForAgent(resolveProbeAgentName(options));
740778}
741779742780private resolveDelegateForAgent(agentName: string | undefined): BaseAcpxRuntime {
@@ -751,6 +789,57 @@ export class AcpxRuntime implements AcpRuntime {
751789return shouldUseBridgeSafeDelegateForCommand(command) ? this.bridgeSafeDelegate : this.delegate;
752790}
753791792+private resolveDelegateForSession(params: {
793+command: string | undefined;
794+sessionKey: string;
795+}): BaseAcpxRuntime {
796+if (shouldUseBridgeSafeDelegateForCommand(params.command)) {
797+return this.bridgeSafeDelegate;
798+}
799+return this.resolveOpenClawToolsDelegateForSession(params.sessionKey);
800+}
801+802+private resolveOpenClawToolsDelegateForSession(sessionKey: string): BaseAcpxRuntime {
803+if (!this.openclawToolsMcpBridgeEnabled) {
804+return this.delegate;
805+}
806+const normalizedSessionKey = sessionKey.trim();
807+if (!normalizedSessionKey) {
808+return this.delegate;
809+}
810+const cached = this.openclawToolsSessionDelegates.get(normalizedSessionKey);
811+if (cached) {
812+return cached;
813+}
814+// Upstream acpx captures mcpServers at runtime construction. The managed
815+// OpenClaw tools bridge needs per-session identity, so cache one delegate
816+// per session with the scoped MCP env already embedded.
817+const delegate = new BaseAcpxRuntime(
818+{
819+ ...this.delegateOptions,
820+mcpServers: withOpenClawToolsMcpSessionEnv({
821+enabled: this.openclawToolsMcpBridgeEnabled,
822+mcpServers: this.delegateOptions.mcpServers,
823+sessionKey: normalizedSessionKey,
824+}),
825+},
826+this.delegateTestOptions,
827+);
828+this.openclawToolsSessionDelegates.set(normalizedSessionKey, delegate);
829+return delegate;
830+}
831+832+private releaseOpenClawToolsDelegateForSession(sessionKey: string): void {
833+if (!this.openclawToolsMcpBridgeEnabled) {
834+return;
835+}
836+const normalizedSessionKey = sessionKey.trim();
837+if (!normalizedSessionKey) {
838+return;
839+}
840+this.openclawToolsSessionDelegates.delete(normalizedSessionKey);
841+}
842+754843private async resolveDelegateForHandle(handle: AcpRuntimeHandle): Promise<BaseAcpxRuntime> {
755844const record = await this.sessionStore.load(handle.acpxRecordId ?? handle.sessionKey);
756845return this.resolveDelegateForLoadedRecord(handle, record);
@@ -762,9 +851,17 @@ export class AcpxRuntime implements AcpRuntime {
762851): BaseAcpxRuntime {
763852const recordCommand = readAgentCommandFromRecord(record);
764853if (recordCommand) {
765-return this.resolveDelegateForCommand(recordCommand);
854+return this.resolveDelegateForSession({
855+command: recordCommand,
856+sessionKey: handle.sessionKey,
857+});
766858}
767-return this.resolveDelegateForAgent(readAgentFromHandle(handle));
859+const agentName = readAgentFromHandle(handle);
860+const command = resolveAgentCommandForName({
861+ agentName,
862+agentRegistry: this.agentRegistry,
863+});
864+return this.resolveDelegateForSession({ command, sessionKey: handle.sessionKey });
768865}
769866770867private async resolveCommandForHandle(handle: AcpRuntimeHandle): Promise<string | undefined> {
@@ -980,7 +1077,7 @@ export class AcpxRuntime implements AcpRuntime {
9801077agentName: input.agent,
9811078agentRegistry: this.agentRegistry,
9821079});
983-const delegate = this.resolveDelegateForCommand(command);
1080+const delegate = this.resolveDelegateForSession({ command, sessionKey: input.sessionKey });
9841081const claudeModelOverride = isClaudeAcpCommand(command)
9851082 ? normalizeClaudeAcpModelOverride(input.model)
9861083 : undefined;
@@ -1264,6 +1361,9 @@ export class AcpxRuntime implements AcpRuntime {
12641361}
1265136212661363async prepareFreshSession(input: { sessionKey: string }): Promise<void> {
1364+// Fresh reset has no ACP handle to close the delegate's upstream client.
1365+// Keep the scoped delegate reachable so the next ensure can replace it;
1366+// close() owns cache release when the session lifecycle ends.
12671367this.sessionStore.markFresh(input.sessionKey);
12681368}
12691369@@ -1272,8 +1372,9 @@ export class AcpxRuntime implements AcpRuntime {
12721372input.handle.acpxRecordId ?? input.handle.sessionKey,
12731373);
12741374let closeSucceeded;
1375+const delegate = this.resolveDelegateForLoadedRecord(input.handle, record);
12751376try {
1276-await this.resolveDelegateForLoadedRecord(input.handle, record).close({
1377+await delegate.close({
12771378handle: input.handle,
12781379reason: input.reason,
12791380discardPersistentState: input.discardPersistentState,
@@ -1282,6 +1383,9 @@ export class AcpxRuntime implements AcpRuntime {
12821383} finally {
12831384await this.cleanupProcessTreeForRecord(input.handle, record);
12841385}
1386+if (closeSucceeded) {
1387+this.releaseOpenClawToolsDelegateForSession(input.handle.sessionKey);
1388+}
12851389if (closeSucceeded && input.discardPersistentState) {
12861390this.sessionStore.markFresh(input.handle.sessionKey);
12871391}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。