mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-16 13:40:45 +00:00
Isolate Codex app-server state per agent (#74556)
* fix(codex): isolate app-server home per agent * fix(codex): isolate native Codex assets per agent * fix(channels): mark inbound system events untrusted * fix(doctor): warn on personal Codex agent skills * test(doctor): cover personal Codex agent skills warning * fix(codex): forward auth profiles to harness runs * fix(codex): preserve auto auth for harness runs * fix(codex): auto-select harness auth profiles * test(codex): type harness auth mock * feat(codex): select migrated skills * fix(codex): satisfy migration selection lint * docs: add codex isolation changelog
This commit is contained in:
@@ -764,6 +764,73 @@ describe("embedded attempt harness pinning", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("auto-forwards OpenAI Codex auth profiles to configured Codex harness runs", async () => {
|
||||
const sessionEntry: SessionEntry = {
|
||||
sessionId: "codex-auth-session",
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
await fs.writeFile(
|
||||
path.join(tmpDir, "auth-profiles.json"),
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
profiles: {
|
||||
"openai-codex:work": {
|
||||
type: "oauth",
|
||||
provider: "openai-codex",
|
||||
access: "access-token",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() + 60_000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
runEmbeddedPiAgentMock.mockResolvedValueOnce({
|
||||
meta: { durationMs: 1 },
|
||||
} satisfies EmbeddedPiRunResult);
|
||||
|
||||
await runAgentAttempt({
|
||||
providerOverride: "openai",
|
||||
originalProvider: "openai",
|
||||
modelOverride: "gpt-5.4",
|
||||
cfg: {
|
||||
agents: {
|
||||
defaults: {
|
||||
agentRuntime: { id: "codex", fallback: "none" },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
sessionEntry,
|
||||
sessionId: sessionEntry.sessionId,
|
||||
sessionKey: "agent:main:main",
|
||||
sessionAgentId: "main",
|
||||
sessionFile: path.join(tmpDir, "session.jsonl"),
|
||||
workspaceDir: tmpDir,
|
||||
body: "continue",
|
||||
isFallbackRetry: false,
|
||||
resolvedThinkLevel: "medium",
|
||||
timeoutMs: 1_000,
|
||||
runId: "run-codex-auto-auth-profile",
|
||||
opts: { senderIsOwner: false } as Parameters<typeof runAgentAttempt>[0]["opts"],
|
||||
runContext: {} as Parameters<typeof runAgentAttempt>[0]["runContext"],
|
||||
spawnedBy: undefined,
|
||||
messageChannel: undefined,
|
||||
skillsSnapshot: undefined,
|
||||
resolvedVerboseLevel: undefined,
|
||||
agentDir: tmpDir,
|
||||
onAgentEvent: vi.fn(),
|
||||
authProfileProvider: "openai",
|
||||
sessionHasHistory: true,
|
||||
});
|
||||
|
||||
expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
agentHarnessId: "codex",
|
||||
authProfileId: "openai-codex:work",
|
||||
authProfileIdSource: "auto",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("pins a fresh unpinned session to the default PI harness", async () => {
|
||||
const sessionEntry: SessionEntry = {
|
||||
sessionId: "fresh-session",
|
||||
|
||||
@@ -11,6 +11,8 @@ import { annotateInterSessionPromptText } from "../../sessions/input-provenance.
|
||||
import { emitSessionTranscriptUpdate } from "../../sessions/transcript-events.js";
|
||||
import { sanitizeForLog } from "../../terminal/ansi.js";
|
||||
import { resolveMessageChannel } from "../../utils/message-channel.js";
|
||||
import { resolveAuthProfileOrder } from "../auth-profiles/order.js";
|
||||
import { ensureAuthProfileStore } from "../auth-profiles/store.js";
|
||||
import { resolveBootstrapWarningSignaturesSeen } from "../bootstrap-budget.js";
|
||||
import { runCliAgent } from "../cli-runner.js";
|
||||
import { getCliSessionBinding, setCliSessionBinding } from "../cli-session.js";
|
||||
@@ -85,6 +87,82 @@ type PersistTextTurnTranscriptParams = {
|
||||
};
|
||||
};
|
||||
|
||||
type HarnessAuthProfileSelection = {
|
||||
authProfileId?: string;
|
||||
authProfileIdSource?: "auto" | "user";
|
||||
authProfileProvider: string;
|
||||
};
|
||||
|
||||
function resolveProfileProviderFromStore(params: {
|
||||
agentDir: string;
|
||||
profileId: string | undefined;
|
||||
}): string | undefined {
|
||||
const profileId = params.profileId?.trim();
|
||||
if (!profileId) {
|
||||
return undefined;
|
||||
}
|
||||
return ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
}).profiles[profileId]?.provider;
|
||||
}
|
||||
|
||||
function resolveHarnessAuthProfileSelection(params: {
|
||||
config: OpenClawConfig;
|
||||
agentDir: string;
|
||||
workspaceDir: string;
|
||||
provider: string;
|
||||
authProfileProvider: string;
|
||||
sessionAuthProfileId?: string;
|
||||
sessionAuthProfileSource?: "auto" | "user";
|
||||
harnessId?: string;
|
||||
harnessRuntime?: string;
|
||||
allowHarnessAuthProfileForwarding: boolean;
|
||||
}): HarnessAuthProfileSelection {
|
||||
const sessionAuthProfileId = params.sessionAuthProfileId?.trim();
|
||||
if (sessionAuthProfileId) {
|
||||
return {
|
||||
authProfileId: sessionAuthProfileId,
|
||||
authProfileIdSource: params.sessionAuthProfileSource,
|
||||
authProfileProvider:
|
||||
resolveProfileProviderFromStore({
|
||||
agentDir: params.agentDir,
|
||||
profileId: sessionAuthProfileId,
|
||||
}) ?? params.authProfileProvider,
|
||||
};
|
||||
}
|
||||
|
||||
const runtimeAuthPlan = buildAgentRuntimeAuthPlan({
|
||||
provider: params.provider,
|
||||
authProfileProvider: params.authProfileProvider,
|
||||
config: params.config,
|
||||
workspaceDir: params.workspaceDir,
|
||||
harnessId: params.harnessId,
|
||||
harnessRuntime: params.harnessRuntime,
|
||||
allowHarnessAuthProfileForwarding: params.allowHarnessAuthProfileForwarding,
|
||||
});
|
||||
const harnessAuthProvider = runtimeAuthPlan.harnessAuthProvider;
|
||||
if (!harnessAuthProvider) {
|
||||
return { authProfileProvider: params.authProfileProvider };
|
||||
}
|
||||
|
||||
const store = ensureAuthProfileStore(params.agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
});
|
||||
const authProfileId = resolveAuthProfileOrder({
|
||||
cfg: params.config,
|
||||
store,
|
||||
provider: harnessAuthProvider,
|
||||
})[0];
|
||||
|
||||
return authProfileId
|
||||
? {
|
||||
authProfileId,
|
||||
authProfileIdSource: "auto",
|
||||
authProfileProvider: harnessAuthProvider,
|
||||
}
|
||||
: { authProfileProvider: params.authProfileProvider };
|
||||
}
|
||||
|
||||
function resolveTranscriptUsage(usage: PersistTextTurnTranscriptParams["assistant"]["usage"]) {
|
||||
if (!usage) {
|
||||
return ACP_TRANSCRIPT_USAGE;
|
||||
@@ -320,10 +398,22 @@ export function runAgentAttempt(params: {
|
||||
agentId: params.sessionAgentId,
|
||||
sessionKey: params.sessionKey ?? params.sessionId,
|
||||
});
|
||||
const runtimeAuthPlan = buildAgentRuntimeAuthPlan({
|
||||
const harnessAuthSelection = resolveHarnessAuthProfileSelection({
|
||||
config: params.cfg,
|
||||
agentDir: params.agentDir,
|
||||
workspaceDir: params.workspaceDir,
|
||||
provider: params.providerOverride,
|
||||
authProfileProvider: params.authProfileProvider,
|
||||
sessionAuthProfileId: params.sessionEntry?.authProfileOverride,
|
||||
sessionAuthProfileSource: params.sessionEntry?.authProfileOverrideSource,
|
||||
harnessId: sessionPinnedAgentHarnessId,
|
||||
harnessRuntime: agentHarnessPolicy.runtime,
|
||||
allowHarnessAuthProfileForwarding: !isCliProvider(cliExecutionProvider, params.cfg),
|
||||
});
|
||||
const runtimeAuthPlan = buildAgentRuntimeAuthPlan({
|
||||
provider: params.providerOverride,
|
||||
authProfileProvider: harnessAuthSelection.authProfileProvider,
|
||||
sessionAuthProfileId: harnessAuthSelection.authProfileId,
|
||||
config: params.cfg,
|
||||
workspaceDir: params.workspaceDir,
|
||||
harnessId: sessionPinnedAgentHarnessId,
|
||||
@@ -484,7 +574,7 @@ export function runAgentAttempt(params: {
|
||||
provider: params.providerOverride,
|
||||
model: params.modelOverride,
|
||||
authProfileId,
|
||||
authProfileIdSource: authProfileId ? params.sessionEntry?.authProfileOverrideSource : undefined,
|
||||
authProfileIdSource: authProfileId ? harnessAuthSelection.authProfileIdSource : undefined,
|
||||
thinkLevel: params.resolvedThinkLevel,
|
||||
verboseLevel: params.resolvedVerboseLevel,
|
||||
timeoutMs: params.timeoutMs,
|
||||
|
||||
Reference in New Issue
Block a user