Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 208 additions & 5 deletions apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {
type ProviderTextDeltaUpdate,
} from "./ProviderTextDeltaCoalescer.ts";
import * as NodeServices from "@effect/platform-node/NodeServices";
import * as HostProcess from "@t3tools/shared/HostProcess";
// @effect-diagnostics-next-line nodeBuiltinImport:off -- Directory junctions keep these fixtures available on Windows.
import * as NodeFSP from "node:fs/promises";
import {
CommandId,
CheckpointId,
Expand Down Expand Up @@ -915,6 +918,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
Effect.void,
onRequest: (method: string, params: unknown) => Effect.Effect<void> = () => Effect.void,
readChildMetadata?: Parameters<typeof withCodexReplayChildMetadata>[2],
runtimePolicy = CODEX_TEST_RUNTIME_POLICY,
) =>
Effect.gen(function* () {
const fileSystem = yield* FileSystem.FileSystem;
Expand Down Expand Up @@ -943,6 +947,13 @@ describe("CodexAdapterV2 post-settle continuation", () => {
(client) =>
({
...client,
raw: {
...client.raw,
request: (method, params) =>
onRequest(method, params).pipe(
Effect.andThen(client.raw.request(method, params)),
),
},
request: (method, params) =>
onRequest(method, params).pipe(
Effect.andThen(client.request(method, params)),
Expand Down Expand Up @@ -975,12 +986,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
threadId,
providerSessionId: ProviderSessionId.make(`provider-session-${transcript.scenario}`),
modelSelection: CODEX_TEST_MODEL_SELECTION,
runtimePolicy: CODEX_TEST_RUNTIME_POLICY,
runtimePolicy,
});
const providerThread = yield* runtime.ensureThread({
threadId,
modelSelection: CODEX_TEST_MODEL_SELECTION,
runtimePolicy: CODEX_TEST_RUNTIME_POLICY,
runtimePolicy,
});
const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
const firstTerminal = yield* Deferred.make<void>();
Expand Down Expand Up @@ -1146,6 +1157,192 @@ describe("CodexAdapterV2 post-settle continuation", () => {
),
);

it.effect.each(["darwin", "win32"] as const)(
"resolves the Codex workspace per platform (%s) without changing the registered path",
(platform) =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const root = yield* fs.makeTempDirectoryScoped({ prefix: "t3-codex-symlink-" });
const target = `${root}/physical`;
const alias = `${root}/alias`;
yield* fs.makeDirectory(target);
yield* Effect.tryPromise(() => NodeFSP.symlink(target, alias, "junction"));
const expectedCwd = platform === "win32" ? alias : yield* fs.realPath(target);
const policy = { ...CODEX_TEST_RUNTIME_POLICY, runtimeMode: "auto" as const, cwd: alias };
const requests: Array<unknown> = [];
const preamble = codexReplayPreamble({
nativeThreadId: "physical-workspace",
nativeTurnId: "physical-turn",
prompt: "Inspect the workspace.",
});
const entries: Array<CodexReplay.CodexAppServerReplayEntry> = [
...preamble.slice(0, 5),
{
type: "expect_outbound",
label: "resume alias",
frame: {
id: 3,
method: "thread/resume",
params: {
threadId: "physical-workspace",
excludeTurns: true,
config: CodexAdapterV2.CODEX_THREAD_CONFIG,
},
},
},
{
type: "emit_inbound",
label: "resume alias",
frame: {
id: 3,
result: { thread: { id: "physical-workspace", updatedAt: 1782622440 } },
},
},
{
type: "expect_outbound",
label: "turn/start",
frame: {
id: 4,
method: "turn/start",
params: {
threadId: "physical-workspace",
input: [{ type: "text", text: "Inspect the workspace." }],
cwd: "/workspace",
model: "gpt-5.4",
approvalPolicy: "on-request",
approvalsReviewer: "auto_review",
sandboxPolicy: { type: "workspaceWrite" },
summary: "detailed",
collaborationMode: { mode: "default", settings: { model: "gpt-5.4" } },
},
},
},
...preamble
.slice(6)
.map((entry) =>
entry.type !== "runtime_exit" && Predicate.hasProperty(entry.frame, "id")
? withReplayRequestId(entry, 4)
: entry,
),
];
const transcript = yield* decodeReplayTranscriptJson(
(yield* encodeReplayTranscriptJson(
makeCodexReplayTranscript({
scenario: `physical-workspace-${platform}`,
entries,
}),
)).replaceAll(
yield* encodeStringJson("/workspace"),
yield* encodeStringJson(expectedCwd),
),
);
const harness = yield* makeCodexReplayHarness(
transcript,
() => Effect.void,
(method, params) =>
Effect.sync(() => {
if (method === "thread/start" || method === "thread/resume") requests.push(params);
}),
undefined,
policy,
);
yield* harness.runtime.resumeThread({
providerThread: harness.providerThread,
runtimePolicy: policy,
modelSelection: CODEX_TEST_MODEL_SELECTION,
});
assert.lengthOf(requests, 2);
for (const request of requests) assert.propertyVal(request, "cwd", expectedCwd);
yield* harness.runtime.startTurn({
...makeCodexTestTurnInput({
threadId: harness.threadId,
providerThread: harness.providerThread,
now: yield* DateTime.now,
attemptId: RunAttemptId.make("physical-attempt"),
text: "Inspect the workspace.",
}),
runtimePolicy: policy,
});
assert.equal(policy.cwd, alias);
assert.equal(harness.runtime.providerSession.cwd, alias);
}).pipe(
Effect.scoped,
Effect.provideService(HostProcess.Platform, platform),
Effect.provide(
Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer),
),
),
);

it.effect("follows a workspace alias retargeted between Codex turns", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const root = yield* fs.makeTempDirectoryScoped({ prefix: "t3-codex-retarget-" });
const alias = `${root}/alias`;
const targets = [`${root}/first`, `${root}/second`];
for (const target of targets) yield* fs.makeDirectory(target);
yield* Effect.tryPromise(() => NodeFSP.symlink(targets[0]!, alias, "junction"));
const entries: Array<CodexReplay.CodexAppServerReplayEntry> = [];
for (const [index, target] of targets.entries()) {
const preamble = codexReplayPreamble({
nativeThreadId: "retargeted-workspace",
nativeTurnId: `retargeted-turn-${index}`,
prompt: `Inspect workspace ${index}.`,
});
const transcript = yield* decodeReplayTranscriptJson(
(yield* encodeReplayTranscriptJson(
makeCodexReplayTranscript({
scenario: "retargeted-workspace",
entries:
index === 0
? preamble
: preamble
.slice(5)
.map((entry) =>
entry.type !== "runtime_exit" && Predicate.hasProperty(entry.frame, "id")
? withReplayRequestId(entry, 4)
: entry,
),
}),
)).replaceAll(
yield* encodeStringJson("/workspace"),
yield* encodeStringJson(yield* fs.realPath(target)),
),
);
entries.push(...transcript.entries);
}
const policy = { ...CODEX_TEST_RUNTIME_POLICY, cwd: alias };
const harness = yield* makeCodexReplayHarness(
makeCodexReplayTranscript({ scenario: "retargeted-workspace", entries }),
undefined,
undefined,
undefined,
policy,
);
for (const [index, target] of targets.entries()) {
yield* fs.remove(alias);
yield* Effect.tryPromise(() => NodeFSP.symlink(target, alias, "junction"));
yield* harness.runtime.startTurn({
...makeCodexTestTurnInput({
threadId: harness.threadId,
providerThread: harness.providerThread,
now: yield* DateTime.now,
attemptId: RunAttemptId.make(`retargeted-attempt-${index}`),
text: `Inspect workspace ${index}.`,
}),
runtimePolicy: policy,
});
}
assert.equal(policy.cwd, alias);
}).pipe(
Effect.scoped,
Effect.provideService(HostProcess.Platform, "darwin"),
Effect.provide(
Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer),
),
),
);

it.effect("adds the session MCP server without replacing launch-argument servers", () =>
Effect.gen(function* () {
const scenario = "launch-mcp-servers";
Expand Down Expand Up @@ -2552,7 +2749,9 @@ describe("CodexAdapterV2 post-settle continuation", () => {

if (outcome === "recover") {
const fs = yield* FileSystem.FileSystem;
const cwd = yield* fs.makeTempDirectoryScoped({ prefix: "t3-capacity-retry-" });
const cwd = yield* fs.realPath(
yield* fs.makeTempDirectoryScoped({ prefix: "t3-capacity-retry-" }),
);
const localTranscript = yield* decodeReplayTranscriptJson(
(yield* encodeReplayTranscriptJson(
makeCodexReplayTranscript({ scenario, entries }),
Expand Down Expand Up @@ -3476,7 +3675,9 @@ describe("CodexAdapterV2 post-settle continuation", () => {
return Effect.scoped(
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const cwd = yield* fs.makeTempDirectoryScoped({ prefix: "t3-bg-stop-workspace-" });
const cwd = yield* fs.realPath(
yield* fs.makeTempDirectoryScoped({ prefix: "t3-bg-stop-workspace-" }),
);
const localTranscript = yield* decodeReplayTranscriptJson(
(yield* encodeReplayTranscriptJson(transcript)).replaceAll(
yield* encodeStringJson("/workspace"),
Expand Down Expand Up @@ -3603,7 +3804,9 @@ describe("CodexAdapterV2 post-settle continuation", () => {
Effect.scoped(
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const cwd = yield* fs.makeTempDirectoryScoped({ prefix: "t3-bg-stale-workspace-" });
const cwd = yield* fs.realPath(
yield* fs.makeTempDirectoryScoped({ prefix: "t3-bg-stale-workspace-" }),
);
const staleTranscript = makeCodexReplayTranscript({
scenario: "codex-bg-stop-untracked",
entries: [
Expand Down
36 changes: 28 additions & 8 deletions apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,18 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions;
const continuationRequests = adapterOptions.continuationRequests;

const resolveCodexRuntimePolicy = Effect.fn("CodexAdapterV2.resolveCodexRuntimePolicy")(
function* (policy: ProviderAdapter.ProviderAdapterV2RuntimePolicy) {
const cwd = policy.cwd;
// Windows realpath can rewrite mapped drives to network paths; preserve that platform's cwd.
if (cwd === null || (yield* HostProcess.Platform) === "win32") return policy;
return {
...policy,
cwd: yield* fileSystem.realPath(cwd).pipe(Effect.orElseSucceed(() => cwd)),
};
},
);

return ProviderAdapter.ProviderAdapterV2.of({
instanceId: adapterOptions.instanceId,
driver: CODEX_PROVIDER,
Expand Down Expand Up @@ -6490,7 +6502,7 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
const turnStartParams = yield* buildCodexTurnStartParams({
nativeThreadId: threadId,
codexInput,
runtimePolicy: turnInput.runtimePolicy,
runtimePolicy: yield* resolveCodexRuntimePolicy(turnInput.runtimePolicy),
modelSelection: turnInput.modelSelection,
hasT3Mcp: mcpSession !== undefined,
browserToolsAvailable: mcpSession?.browserToolsAvailable ?? true,
Expand Down Expand Up @@ -6774,16 +6786,17 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
}
return false;
}),
ensureThread: (threadInput) =>
ensureInitialized.pipe(
ensureThread: Effect.fn("CodexAdapterV2.ensureThread")(function* (threadInput) {
const runtimePolicy = yield* resolveCodexRuntimePolicy(threadInput.runtimePolicy);
return yield* ensureInitialized.pipe(
Effect.andThen(mcpSessions.read(threadInput.threadId)),
Effect.flatMap((mcpSession) =>
client.request(
"thread/start",
codexThreadRuntimeParams({
mcpSession,
modelSelection: threadInput.modelSelection,
runtimePolicy: threadInput.runtimePolicy,
runtimePolicy,
}),
),
),
Expand All @@ -6805,7 +6818,8 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
cause: normalizeCodexCause(cause),
}),
),
),
);
}),
resumeThread: Effect.fn("CodexAdapterV2.resumeThread")(
function* (threadInput) {
const nativeThreadId = yield* getNativeThreadId(threadInput.providerThread);
Expand All @@ -6822,7 +6836,9 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
: { modelSelection: threadInput.modelSelection }),
...(threadInput.runtimePolicy === undefined
? {}
: { runtimePolicy: threadInput.runtimePolicy }),
: {
runtimePolicy: yield* resolveCodexRuntimePolicy(threadInput.runtimePolicy),
}),
}),
});
const response = yield* ensureInitialized.pipe(
Expand Down Expand Up @@ -7602,7 +7618,7 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
...codexThreadRuntimeParams({
mcpSession: yield* readMcpSession(threadInput.providerThread.appThreadId),
modelSelection: input.modelSelection,
runtimePolicy: input.runtimePolicy,
runtimePolicy: yield* resolveCodexRuntimePolicy(input.runtimePolicy),
}),
});
}
Expand Down Expand Up @@ -7658,7 +7674,11 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* (
: { modelSelection: threadInput.modelSelection }),
...(threadInput.runtimePolicy === undefined
? {}
: { runtimePolicy: threadInput.runtimePolicy }),
: {
runtimePolicy: yield* resolveCodexRuntimePolicy(
threadInput.runtimePolicy,
),
}),
}),
}),
),
Expand Down
Loading