951 lines · 28.0 KB · TypeScript
RawDownload
import * as NodeAssert from "node:assert/strict"; import { it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import { describe } from "vite-plus/test";import { DEFAULT_MODEL, ThreadId } from "@t3tools/contracts";import * as CodexErrors from "effect-codex-app-server/errors";import * as CodexRpc from "effect-codex-app-server/rpc";import * as EffectCodexSchema from "effect-codex-app-server/schema"; import { buildCodexDeveloperInstructions } from "../CodexDeveloperInstructions.ts";import { codexSessionAppServerArgs } from "./codexLaunchArgs.ts";import {  buildTurnStartParams,  describeMcpElicitation,  hasConfiguredMcpServer,  isRecoverableThreadResumeError,  makeMemoryConsolidationNotificationFilter,  openCodexThread,  toMcpElicitationResponse,} from "./CodexSessionRuntime.ts";const isCodexAppServerRequestError = Schema.is(CodexErrors.CodexAppServerRequestError); describe("CodexSessionRuntimeIdentifierGenerationError", () => {  it("retains identifier purpose and the random source failure", () => {    const cause = new Error("random source unavailable");    const error = new CodexErrors.CodexAppServerIdentifierGenerationError({      purpose: "provider-event",      cause,    });     NodeAssert.equal(error.purpose, "provider-event");    NodeAssert.strictEqual(error.cause, cause);    NodeAssert.equal(      error.message,      "Failed to generate Codex App Server identifier for provider-event.",    );  });}); function makeThreadOpenResponse(  threadId: string,): CodexRpc.ClientRequestResponsesByMethod["thread/start"] {  return {    cwd: "/tmp/project",    model: "gpt-5.3-codex",    modelProvider: "openai",    approvalPolicy: "never",    approvalsReviewer: "user",    sandbox: { type: "danger-full-access" },    thread: {      id: threadId,      createdAt: "2026-04-18T00:00:00.000Z",      source: { session: "cli" },      turns: [],      status: {        state: "idle",        activeFlags: [],      },    },  } as unknown as CodexRpc.ClientRequestResponsesByMethod["thread/start"];} describe("buildTurnStartParams", () => {  it("keeps invalid turn values only in the schema cause", () => {    const secret = "codex-turn-input-secret-sentinel";    const error = Effect.runSync(      buildTurnStartParams({        threadId: "provider-thread-1",        runtimeMode: "full-access",        attachments: [          {            type: "image",            url: { secret } as unknown as string,          },        ],      }).pipe(Effect.flip),    );    const { cause, ...directDiagnostics } = error;     NodeAssert.equal(error.operation, "decode-request-payload");    NodeAssert.equal(error.method, "turn/start");    NodeAssert.ok((error.issueCount ?? 0) > 0);    NodeAssert.ok(error.issueKinds?.includes("Pointer"));    NodeAssert.ok((error.maximumPathDepth ?? 0) > 0);    NodeAssert.ok(Schema.isSchemaError(cause));    NodeAssert.doesNotMatch(error.message, new RegExp(secret));    NodeAssert.doesNotMatch(JSON.stringify(directDiagnostics), new RegExp(secret));  });   it("includes plan collaboration mode when requested", () => {    const params = Effect.runSync(      buildTurnStartParams({        threadId: "provider-thread-1",        runtimeMode: "full-access",        prompt: "Make a plan",        model: "gpt-5.3-codex",        effort: "medium",        interactionMode: "plan",      }),    );     NodeAssert.deepStrictEqual(params, {      threadId: "provider-thread-1",      approvalPolicy: "never",      approvalsReviewer: "user",      sandboxPolicy: {        type: "dangerFullAccess",      },      input: [        {          type: "text",          text: "Make a plan",        },      ],      model: "gpt-5.3-codex",      effort: "medium",      collaborationMode: {        mode: "plan",        settings: {          model: "gpt-5.3-codex",          reasoning_effort: "medium",          developer_instructions: buildCodexDeveloperInstructions("plan", {            model: "gpt-5.3-codex",            reasoningEffort: "medium",          }),        },      },    });  });   it("includes default collaboration mode and image attachments", () => {    const params = Effect.runSync(      buildTurnStartParams({        threadId: "provider-thread-1",        runtimeMode: "auto-accept-edits",        prompt: "Implement it",        model: "gpt-5.3-codex",        interactionMode: "default",        attachments: [          {            type: "image",            url: "data:image/png;base64,abc",          },        ],      }),    );     NodeAssert.deepStrictEqual(params, {      threadId: "provider-thread-1",      approvalPolicy: "on-request",      approvalsReviewer: "user",      sandboxPolicy: {        type: "workspaceWrite",      },      input: [        {          type: "text",          text: "Implement it",        },        {          type: "image",          url: "data:image/png;base64,abc",        },      ],      model: "gpt-5.3-codex",      collaborationMode: {        mode: "default",        settings: {          model: "gpt-5.3-codex",          reasoning_effort: "medium",          developer_instructions: buildCodexDeveloperInstructions("default", {            model: "gpt-5.3-codex",            reasoningEffort: "medium",          }),        },      },    });  });   it("reports the same fallback model and effort in settings and instructions", () => {    const params = Effect.runSync(      buildTurnStartParams({        threadId: "provider-thread-1",        runtimeMode: "full-access",        prompt: "Go",        interactionMode: "default",      }),    );     const settings = params.collaborationMode?.settings;    NodeAssert.equal(settings?.model, DEFAULT_MODEL);    NodeAssert.equal(settings?.reasoning_effort, "medium");    NodeAssert.ok(settings?.developer_instructions?.includes(`as ${DEFAULT_MODEL} with medium`));  });   it.effect("routes approvals to the auto reviewer in auto mode", () =>    Effect.gen(function* () {      const params = yield* buildTurnStartParams({        threadId: "provider-thread-1",        runtimeMode: "auto",        prompt: "Ship it",      });       NodeAssert.deepStrictEqual(params, {        threadId: "provider-thread-1",        approvalPolicy: "on-request",        approvalsReviewer: "auto_review",        sandboxPolicy: {          type: "workspaceWrite",        },        input: [          {            type: "text",            text: "Ship it",          },        ],      });    }),  );   it("omits collaboration mode when interaction mode is absent", () => {    const params = Effect.runSync(      buildTurnStartParams({        threadId: "provider-thread-1",        runtimeMode: "approval-required",        prompt: "Review",      }),    );     NodeAssert.deepStrictEqual(params, {      threadId: "provider-thread-1",      approvalPolicy: "untrusted",      approvalsReviewer: "user",      sandboxPolicy: {        type: "readOnly",      },      input: [        {          type: "text",          text: "Review",        },      ],    });  });}); describe("Codex MCP elicitation approvals", () => {  const request = {    mode: "form",    message: "Allow ChatGPT to use Safari?",    serverName: "computer-use",    threadId: "provider-thread-1",    turnId: "turn-1",    _meta: {      app_name: "Safari",      persist: ["session", "always"],    },    requestedSchema: {      type: "object",      properties: {        approval: {          type: "string",          oneOf: [            { const: "once", title: "Allow once" },            { const: "session", title: "Allow for this session" },            { const: "always", title: "Always allow Safari" },          ],        },      },      required: ["approval"],    },  } satisfies EffectCodexSchema.McpServerElicitationRequestParams;   it("preserves the app name and advertised persistence choices", () => {    NodeAssert.deepStrictEqual(describeMcpElicitation(request), {      appName: "Safari",      options: [        { decision: "cancel", label: "Cancel" },        { decision: "decline", label: "Decline" },        { decision: "acceptForSession", label: "Allow for this session" },        { decision: "acceptAlways", label: "Always allow Safari" },        { decision: "accept", label: "Approve" },      ],    });  });   it("extracts the app name from a Computer Use request without metadata", () => {    const { _meta, ...requestWithoutMetadata } = request;     NodeAssert.equal(describeMcpElicitation(requestWithoutMetadata).appName, "Safari");  });   it("returns the accepted form option to Codex", () => {    NodeAssert.deepStrictEqual(toMcpElicitationResponse(request, "accept"), {      action: "accept",      content: { approval: "once" },    });  });   it("returns session-scoped approval in the MCP response", () => {    NodeAssert.deepStrictEqual(toMcpElicitationResponse(request, "acceptForSession"), {      action: "accept",      _meta: { persist: "session" },      content: { approval: "session" },    });  });   it("returns persistent approval in the MCP response", () => {    NodeAssert.deepStrictEqual(toMcpElicitationResponse(request, "acceptAlways"), {      action: "accept",      _meta: { persist: "always" },      content: { approval: "always" },    });  });   it("returns rejection without form content", () => {    NodeAssert.deepStrictEqual(toMcpElicitationResponse(request, "decline"), {      action: "decline",    });  });   it("returns cancellation without form content", () => {    NodeAssert.deepStrictEqual(toMcpElicitationResponse(request, "cancel"), {      action: "cancel",    });  });   it("supports boolean permanent-approval fields", () => {    const booleanRequest = {      ...request,      _meta: { app_name: "Safari" },      requestedSchema: {        type: "object",        properties: {          always: { type: "boolean", title: "Always allow Safari" },        },      },    } satisfies EffectCodexSchema.McpServerElicitationRequestParams;     NodeAssert.ok(      describeMcpElicitation(booleanRequest).options.some(        (option) => option.decision === "acceptAlways",      ),    );    NodeAssert.deepStrictEqual(toMcpElicitationResponse(booleanRequest, "acceptAlways"), {      action: "accept",      _meta: { persist: "always" },      content: { always: true },    });  });   it("preserves valid nullable MCP form fields and persistence choices", () => {    const nullableRequest = {      ...request,      _meta: {        app_name: null,        appName: "Safari",        connector_name: null,        persist: null,        target: null,        tool_params: null,      },      requestedSchema: {        type: "object",        properties: {          approval: {            type: "string",            title: null,            description: null,            default: null,            enum: ["once", "always"],            enumNames: null,          },        },        required: ["approval"],      },    } satisfies EffectCodexSchema.McpServerElicitationRequestParams;     NodeAssert.equal(describeMcpElicitation(nullableRequest).appName, "Safari");    NodeAssert.ok(      describeMcpElicitation(nullableRequest).options.some(        (option) => option.decision === "acceptAlways",      ),    );    NodeAssert.deepStrictEqual(toMcpElicitationResponse(nullableRequest, "acceptAlways"), {      action: "accept",      _meta: { persist: "always" },      content: { approval: "always" },    });  });   it("declines required form fields that an approval prompt cannot collect", () => {    const inputRequest = {      ...request,      requestedSchema: {        type: "object",        properties: {          email: { type: "string", format: "email" },        },        required: ["email"],      },    } satisfies EffectCodexSchema.McpServerElicitationRequestParams;     NodeAssert.deepStrictEqual(toMcpElicitationResponse(inputRequest, "accept"), {      action: "decline",    });  });   it("does not approve URL elicitations without opening their requested URL", () => {    const urlRequest = {      mode: "url",      message: "Finish signing in to continue.",      serverName: "computer-use",      threadId: "provider-thread-1",      turnId: "turn-1",      elicitationId: "sign-in-1",      url: "https://example.com/authorize",    } satisfies EffectCodexSchema.McpServerElicitationRequestParams;     NodeAssert.deepStrictEqual(toMcpElicitationResponse(urlRequest, "accept"), {      action: "decline",    });  });   it("omits persistence choices that cannot satisfy required form fields", () => {    const onceOnlyRequest = {      ...request,      _meta: { app_name: "Safari", persist: ["session", "always"] },      requestedSchema: {        type: "object",        properties: {          approval: {            type: "string",            enum: ["once"],          },        },        required: ["approval"],      },    } satisfies EffectCodexSchema.McpServerElicitationRequestParams;     NodeAssert.deepStrictEqual(describeMcpElicitation(onceOnlyRequest).options, [      { decision: "cancel", label: "Cancel" },      { decision: "decline", label: "Decline" },      { decision: "accept", label: "Approve" },    ]);  });}); describe("buildCodexDeveloperInstructions", () => {  it("appends runtime info after the mode instructions", () => {    const instructions = buildCodexDeveloperInstructions("default", {      model: "gpt-5.3-codex",      reasoningEffort: "high",    });     NodeAssert.match(instructions, /^<collaboration_mode># Collaboration Mode: Default/);    NodeAssert.match(instructions, /T3 Code/);    NodeAssert.match(instructions, /Codex harness/);    NodeAssert.match(instructions, /as gpt-5\.3-codex with high reasoning effort/);  });   it("describes Markdown media support in the runtime context in both modes", () => {    for (const mode of ["default", "plan"] as const) {      const instructions = buildCodexDeveloperInstructions(mode, {        model: "gpt-5.3-codex",        reasoningEffort: "high",      });      NodeAssert.match(        instructions,        /<runtime_info>.*embed images and videos.*Markdown.*<\/runtime_info>/,      );    }  });   it("includes runtime info alongside plan mode instructions", () => {    const instructions = buildCodexDeveloperInstructions("plan", {      model: "gpt-5.3-codex",      reasoningEffort: "medium",    });     NodeAssert.match(instructions, /^<collaboration_mode># Plan Mode/);    NodeAssert.match(instructions, /as gpt-5\.3-codex with medium reasoning effort/);  });   it("varies with the model and effort of each turn", () => {    const first = buildCodexDeveloperInstructions("default", {      model: "gpt-5.3-codex",      reasoningEffort: "medium",    });    const second = buildCodexDeveloperInstructions("default", {      model: "gpt-5.4",      reasoningEffort: "high",    });     NodeAssert.notEqual(first, second);  });   it("flattens multiline metadata into single-line runtime info", () => {

Lines 1–500 of 951.