























@@ -3,6 +3,7 @@ import os from "node:os";
33import path from "node:path";
44import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
55import type { AcpInitializeSessionInput } from "../acp/control-plane/manager.types.js";
6+import type { SessionEntry } from "../config/sessions/types.js";
67import type { OpenClawConfig } from "../config/types.openclaw.js";
78import {
89__testing as sessionBindingServiceTesting,
@@ -726,6 +727,93 @@ describe("spawnAcpDirect", () => {
726727expect(transcriptCalls[1]?.threadId).toBe("child-thread");
727728});
728729730+it("allows ACP resume IDs recorded for the requester session", async () => {
731+const resumeSessionId = "codex-inner-resume";
732+hoisted.loadSessionStoreMock.mockReturnValue({
733+"agent:codex:acp:owned": {
734+sessionId: "sess-owned",
735+updatedAt: Date.now(),
736+spawnedBy: "agent:main:main",
737+acp: {
738+backend: "acpx",
739+agent: "codex",
740+runtimeSessionName: "codex",
741+identity: {
742+state: "resolved",
743+source: "ensure",
744+agentSessionId: resumeSessionId,
745+acpxSessionId: "acpx-owned",
746+lastUpdatedAt: Date.now(),
747+},
748+mode: "oneshot",
749+state: "idle",
750+lastActivityAt: Date.now(),
751+},
752+} satisfies SessionEntry,
753+});
754+755+const result = await spawnAcpDirect(
756+{
757+task: "Resume owned ACP session",
758+agentId: "codex",
759+ resumeSessionId,
760+},
761+{
762+agentSessionKey: "agent:main:main",
763+},
764+);
765+766+expectAcceptedSpawn(result);
767+expect(hoisted.initializeSessionMock).toHaveBeenCalledWith(
768+expect.objectContaining({
769+ resumeSessionId,
770+}),
771+);
772+});
773+774+it("rejects ACP resume IDs not recorded for the requester session", async () => {
775+hoisted.loadSessionStoreMock.mockReturnValue({
776+"agent:codex:acp:other": {
777+sessionId: "sess-other",
778+updatedAt: Date.now(),
779+spawnedBy: "agent:other:main",
780+acp: {
781+backend: "acpx",
782+agent: "codex",
783+runtimeSessionName: "codex",
784+identity: {
785+state: "resolved",
786+source: "ensure",
787+agentSessionId: "codex-inner-other",
788+acpxSessionId: "acpx-other",
789+lastUpdatedAt: Date.now(),
790+},
791+mode: "oneshot",
792+state: "idle",
793+lastActivityAt: Date.now(),
794+},
795+} satisfies SessionEntry,
796+});
797+798+const result = await spawnAcpDirect(
799+{
800+task: "Resume other ACP session",
801+agentId: "codex",
802+resumeSessionId: "codex-inner-other",
803+},
804+{
805+agentSessionKey: "agent:main:main",
806+},
807+);
808+809+expect(result).toMatchObject({
810+status: "forbidden",
811+errorCode: "resume_forbidden",
812+});
813+expect(hoisted.initializeSessionMock).not.toHaveBeenCalled();
814+expect(hoisted.callGatewayMock).not.toHaveBeenCalled();
815+});
816+729817it("passes model and thinking overrides into ACP session initialization", async () => {
730818const result = await spawnAcpDirect(
731819{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。