import test from "node:test";
import assert from "node:assert/strict";
import { EventEmitter } from "node:events";
import { access, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
  attachEventStream,
  attachSessionListEventStream,
  createAttachment,
  createSession,
  deleteAttachment,
  deleteQueuedMessage,
  deleteThreadLocalData,
  getAttachment,
  getArtifact,
  getSession,
  initializeSessions,
  listApprovals,
  listCapabilities,
  listSessions,
  openThread,
  prepareSessionsForApplicationDataReset,
  publicSession,
  runSessionMaintenance,
  sendMessage,
  stopSession,
  steerQueuedMessage,
  syncExternalThread,
  updateThreadSessionTitle,
  resolveApproval
} from "./sessions.js";
import { INTERNAL_GENERATED_IMAGE_ITEM } from "./codex-runtime.js";
import { createSessionStore } from "./session-store.js";
import { createActionReceiptStore } from "./action-receipt-store.js";

const PNG_IMAGE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00]);
const JPEG_IMAGE = Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00]);
const GIF_IMAGE = Buffer.from("GIF89a", "ascii");
const WEBP_IMAGE = Buffer.from("RIFF\x04\x00\x00\x00WEBP", "binary");

async function waitFor(predicate, timeoutMs = 1000) {
  const deadline = Date.now() + timeoutMs;
  while (!predicate()) {
    if (Date.now() >= deadline) throw new Error("condition_not_met");
    await new Promise((resolve) => setTimeout(resolve, 5));
  }
}

function createTestActionReceiptStore() {
  const messages = new Map();
  const keyFor = (sessionId, clientMessageId) => `${sessionId}\u0000${clientMessageId}`;
  return {
    async initialize() {},
    async findMessage({ sessionId, clientMessageId, payloadHash }) {
      const receipt = messages.get(keyFor(sessionId, clientMessageId));
      if (!receipt) return null;
      if (receipt.payloadHash !== payloadHash) {
        const error = new Error("idempotency_conflict");
        error.status = 409;
        throw error;
      }
      return receipt;
    },
    async reserveMessage(input) {
      const key = keyFor(input.sessionId, input.clientMessageId);
      const existing = messages.get(key);
      if (existing) return { created: false, receipt: existing };
      const receipt = { ...input };
      messages.set(key, receipt);
      return { created: true, receipt };
    },
    async getMessage({ sessionId, clientMessageId }) {
      return messages.get(keyFor(sessionId, clientMessageId)) ?? null;
    },
    async updateMessage({ sessionId, clientMessageId, state }) {
      const receipt = messages.get(keyFor(sessionId, clientMessageId));
      if (receipt) receipt.state = state;
      return receipt;
    }
  };
}

test("global session stream publishes safe snapshots and status changes without attaching chat viewers", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => ({ threadId: "thread-1", async sendMessage() {}, stop() {} })
  });
  const writes = [];
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = (value) => writes.push(value);

  attachSessionListEventStream(response);
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "Start" });

  assert.equal(session.clients.size, 0);
  assert.equal(session.conversationTitle, "Start");
  assert.match(writes.join(""), /event: sessions\.snapshot/);
  assert.match(writes.join(""), /event: session\.updated/);
  assert.match(writes.join(""), /"status":"running"/);
  assert.match(writes.join(""), /"conversationTitle":"Start"/);
  response.emit("close");
});

test("first Codex assistant output starts title generation before the response completes", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  let runtimeEmit;
  const titleRequests = [];
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    startThreadWatcher: async () => ({ stop() {} }),
    generateConversationTitle: async (request) => {
      titleRequests.push(request);
      return "로그인 오류 수정";
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "로그인 오류를 고쳐줘" });
  assert.equal(titleRequests.length, 0);
  runtimeEmit("session.output", {
    threadId: "thread-1",
    turnId: "turn-1",
    itemId: "assistant-1",
    text: "로그인 오류를 수정했습니다.",
    kind: "assistant_delta"
  });

  await waitFor(() => session.conversationTitle === "로그인 오류 수정");
  assert.equal(titleRequests.length, 1);
  assert.equal(titleRequests[0].accountId, "account-a");
  assert.equal(titleRequests[0].threadId, "thread-1");
  assert.match(titleRequests[0].conversation, /User: 로그인 오류를 고쳐줘/);
  assert.match(titleRequests[0].conversation, /Assistant: 로그인 오류를 수정했습니다\./);

  runtimeEmit("turn.completed", { threadId: "thread-1", turnId: "turn-1", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));
  assert.equal(titleRequests.length, 1);
});

test("session rejects runtime events from another thread", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  let runtimeEmit;
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    startThreadWatcher: async () => ({ stop() {} })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "Keep working" });
  const eventCount = session.events.length;

  runtimeEmit("session.output", {
    threadId: "thread-2",
    turnId: "turn-2",
    itemId: "assistant-2",
    text: "Wrong conversation",
    kind: "assistant_delta"
  });
  runtimeEmit("turn.completed", {
    threadId: "thread-2",
    turnId: "turn-2",
    status: "completed"
  });

  assert.equal(session.events.length, eventCount);
  assert.equal(session.status, "running");

  runtimeEmit("session.output", {
    threadId: "thread-1",
    turnId: "turn-1",
    itemId: "assistant-1",
    text: "Right conversation",
    kind: "assistant_delta"
  });
  runtimeEmit("turn.completed", {
    threadId: "thread-1",
    turnId: "turn-1",
    status: "completed"
  });

  assert.equal(session.events.findLast((event) => event.event === "session.output").data.text, "Right conversation");
  assert.equal(session.status, "completed");
});

test("manual rename wins when it happens while a generated title is pending", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  let runtimeEmit;
  let finishTitle;
  let titleStarted = false;
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    startThreadWatcher: async () => ({ stop() {} }),
    generateConversationTitle: async () => {
      titleStarted = true;
      return new Promise((resolve) => { finishTitle = resolve; });
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "첫 요청" });
  runtimeEmit("session.output", {
    threadId: "thread-1",
    turnId: "turn-1",
    itemId: "assistant-1",
    text: "첫 답변",
    kind: "assistant_delta"
  });
  runtimeEmit("turn.completed", { threadId: "thread-1", turnId: "turn-1", status: "completed" });
  await waitFor(() => titleStarted);

  await updateThreadSessionTitle("account-a", "thread-1", "사용자 지정 제목");
  finishTitle("자동 생성 제목");
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(session.conversationTitle, "사용자 지정 제목");
});

test("title generation failure keeps the first prompt fallback and is not retried", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  let runtimeEmit;
  let attempts = 0;
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    startThreadWatcher: async () => ({ stop() {} }),
    generateConversationTitle: async () => {
      attempts += 1;
      throw new Error("title_generation_failed");
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "첫 요청" });
  runtimeEmit("session.output", {
    threadId: "thread-1",
    turnId: "turn-1",
    itemId: "assistant-1",
    text: "첫 답변",
    kind: "assistant_delta"
  });
  runtimeEmit("turn.completed", { threadId: "thread-1", turnId: "turn-1", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));

  runtimeEmit("turn.completed", { threadId: "thread-1", turnId: "turn-2", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(session.conversationTitle, "첫 요청");
  assert.equal(attempts, 1);
});

test("external thread title updates the session even when history is unchanged", () => {
  const session = {
    id: "title-session",
    accountId: "desktop",
    codexThreadId: "thread-1",
    conversationTitle: "Initial request",
    status: "completed",
    events: [],
    externalSignature: JSON.stringify([]),
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };

  assert.equal(syncExternalThread(session, {
    id: "thread-1",
    name: "Generated title",
    status: { type: "idle" },
    updatedAt: 200,
    turns: []
  }), true);
  assert.equal(session.conversationTitle, "Generated title");
});

test("manual thread rename updates linked session snapshots immediately", async () => {
  const config = {
    dataDir: "unused",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: []
  };
  await initializeSessions(config, { load: async () => [{
    id: "linked-session",
    accountId: "account-a",
    codexThreadId: "thread-1",
    threadRefs: [{ accountId: "account-a", threadId: "thread-1" }],
    conversationTitle: "Old title",
    status: "completed",
    createdAt: "2026-07-17T00:00:00.000Z",
    updatedAt: "2026-07-17T00:00:00.000Z",
    events: []
  }], save: async () => {} });
  const writes = [];
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = (value) => writes.push(value);
  attachEventStream(getSession("linked-session"), response);

  await updateThreadSessionTitle("account-a", "thread-1", "Renamed title");

  assert.equal(getSession("linked-session").conversationTitle, "Renamed title");
  assert.match(writes.join(""), /event: session\.snapshot/);
  assert.match(writes.join(""), /"conversationTitle":"Renamed title"/);
  response.emit("close");
});

test("restored sessions recover a missing title from the first visible user message", async () => {
  const config = {
    dataDir: "unused",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: []
  };
  await initializeSessions(config, { load: async () => [{
    id: "untitled-session",
    accountId: "account-a",
    codexThreadId: "thread-1",
    status: "running",
    createdAt: "2026-07-17T00:00:00.000Z",
    updatedAt: "2026-07-17T00:00:01.000Z",
    events: [{
      id: "first-message",
      event: "message.accepted",
      data: { text: "Recovered chat title" },
      createdAt: "2026-07-17T00:00:01.000Z"
    }]
  }], save: async () => {} });

  assert.equal(getSession("untitled-session").conversationTitle, "Recovered chat title");
  assert.equal(getSession("untitled-session").status, "interrupted");
});

test("external thread snapshot replaces events only when history changes", () => {
  const session = {
    id: "external-session",
    accountId: "desktop",
    codexThreadId: "thread-1",
    status: "idle",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };
  const thread = {
    id: "thread-1",
    status: { type: "notLoaded" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "inProgress",
      startedAt: 100,
      items: [{ id: "u1", type: "userMessage", content: [{ type: "text", text: "Live" }] }]
    }]
  };

  assert.equal(syncExternalThread(session, thread), true);
  assert.equal(syncExternalThread(session, thread), false);
  assert.deepEqual(session.events.map((event) => event.event), ["message.accepted", "turn.started"]);
  assert.equal(session.status, "running");
});

test("external thread snapshots preserve uploaded attachment metadata for chat re-entry", () => {
  const attachment = { id: "att-1", kind: "image", name: "photo.jpg", mimeType: "image/jpeg" };
  const session = {
    id: "attachment-session",
    accountId: "desktop",
    codexThreadId: "thread-1",
    status: "completed",
    events: [{
      id: "accepted-live",
      event: "message.accepted",
      data: { sessionId: "attachment-session", text: "Look", attachmentCount: 1, attachments: [attachment] }
    }],
    attachments: [{ ...attachment, path: "C:\\private\\att-1.jpg" }],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };

  const changed = syncExternalThread(session, {
    id: "thread-1",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "completed",
      startedAt: 100,
      completedAt: 200,
      items: [{
        id: "user-1",
        type: "userMessage",
        content: [{ type: "text", text: "Look" }, { type: "localImage", path: "C:\\private\\att-1.jpg" }]
      }]
    }]
  });

  assert.equal(changed, true);
  const accepted = session.events.find((event) => event.event === "message.accepted");
  assert.equal(accepted.data.sessionId, "attachment-session");
  assert.deepEqual(accepted.data.attachments, [attachment]);
  assert.equal(JSON.stringify(accepted).includes("C:\\private"), false);
});

test("external thread sync does not erase completed live command activity while history is stale", () => {
  const commandEvent = {
    id: "live-command",
    event: "activity.completed",
    data: { turnId: "turn-1", itemId: "command-1", kind: "commandExecution", summary: "powershell", status: "completed" }
  };
  const session = {
    id: "completed-session",
    accountId: "desktop",
    codexThreadId: "thread-1",
    status: "completed",
    events: [commandEvent, { id: "done", event: "turn.completed", data: { turnId: "turn-1", status: "completed" } }],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };
  const staleThread = {
    id: "thread-1",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "completed",
      startedAt: 100,
      completedAt: 200,
      items: [{ id: "u1", type: "userMessage", content: [{ type: "text", text: "Run it" }] }]
    }]
  };

  assert.equal(syncExternalThread(session, staleThread), false);
  assert.equal(session.events.includes(commandEvent), true);

  const completeThread = {
    ...staleThread,
    turns: [{ ...staleThread.turns[0], items: [...staleThread.turns[0].items, {
      id: "command-1", type: "commandExecution", command: "powershell", status: "completed"
    }] }]
  };
  assert.equal(syncExternalThread(session, completeThread), true);
  assert.equal(session.events.some((event) => event.data?.itemId === "command-1"), true);
});

test("external thread sync preserves durable plan and terminal errors missing from history", () => {
  const session = {
    id: "durable-session",
    accountId: "desktop",
    codexThreadId: "thread-1",
    status: "failed",
    events: [
      { id: "plan-live", event: "plan.updated", data: { turnId: "turn-1", plan: [{ step: "Inspect", status: "completed" }] } },
      { id: "error-live", event: "error", data: { turnId: "turn-1", message: "usage_limit_reached" } },
      { id: "done-live", event: "turn.completed", data: { turnId: "turn-1", status: "failed", error: "usage_limit_reached" } }
    ],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };

  assert.equal(syncExternalThread(session, {
    id: "thread-1",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "completed",
      items: [{ id: "user-1", type: "userMessage", content: [{ type: "text", text: "Continue" }] }]
    }]
  }), true);

  assert.equal(session.events.some((event) => event.event === "plan.updated"), true);
  assert.equal(session.events.some((event) => event.event === "error" && event.data.message === "usage_limit_reached"), true);
  assert.equal(session.events.find((event) => event.event === "turn.completed").data.status, "failed");
});

test("rejects creating a session with a history-only account", async () => {
  let runtimeStarted = false;
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "history", codexHome: "HISTORY_HOME", historyOnly: true }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => {
      runtimeStarted = true;
      throw new Error("must not start runtime");
    }
  });

  await assert.rejects(
    createSession(config, { accountId: "history", workspaceId: "workspace-a" }),
    (error) => error.message === "account_not_available_for_execution" && error.status === 403
  );
  assert.equal(runtimeStarted, false);
});

test("locked accounts reject session creation and capabilities before app-server access", async () => {
  let runtimeStarts = 0;
  let capabilityRequests = 0;
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-b", codexHome: "B_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }],
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    getAccountExecutionAccess: () => lockedEntitlementAccess(),
    startRuntime: async () => { runtimeStarts += 1; },
  });

  await assert.rejects(
    createSession(config, { accountId: "account-b", workspaceId: "workspace-a" }),
    proRequired
  );
  await assert.rejects(
    listCapabilities(config, "account-b", "workspace-a", async () => { capabilityRequests += 1; }),
    proRequired
  );
  assert.equal(runtimeStarts, 0);
  assert.equal(capabilityRequests, 0);
});

test("a downgraded account keeps readable history but cannot send or steer", async () => {
  let locked = false;
  let runtimeStarts = 0;
  let steerCalls = 0;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-b", codexHome: "B_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }],
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    getAccountExecutionAccess: () => locked ? lockedEntitlementAccess() : { allowed: true, entitlementLocked: false },
    startRuntime: async () => { runtimeStarts += 1; },
  });
  const created = await createSession(config, { accountId: "account-b", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  locked = true;

  const opened = await openThread(config, "account-b", "thread-readable", {}, async () => ({
    thread: { id: "thread-readable", status: { type: "idle" }, turns: [] },
  }));
  assert.equal(opened.session.codexThreadId, "thread-readable");
  await assert.rejects(sendMessage(session, { text: "Blocked" }), proRequired);

  session.status = "running";
  session.queuedMessages = [{ id: "queued-locked", text: "Blocked steer", attachments: [], skills: [] }];
  session.runtime = { async steerMessage() { steerCalls += 1; } };
  await assert.rejects(steerQueuedMessage(session, "queued-locked"), proRequired);
  assert.equal(runtimeStarts, 0);
  assert.equal(steerCalls, 0);
  assert.equal(session.accountId, "account-b");
});

test("automatic queue dispatch keeps locked work queued and does not start runtime", async () => {
  const sent = [];
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-b", codexHome: "B_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }],
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    getAccountExecutionAccess: () => lockedEntitlementAccess(),
    startRuntime: async () => ({
      threadId: "thread-b",
      async sendMessage(payload) { sent.push(payload.text); },
      stop() {},
    }),
  });
  const session = {
    id: "locked-queue-session",
    accountId: "account-b",
    workspaceId: "workspace-a",
    codexThreadId: "thread-b",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-1", text: "Still queued", options: {}, attachments: [], skills: [], createdAt: new Date().toISOString() }],
    runtime: null,
  };
  const completedThread = {
    id: "thread-b",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "completed",
      startedAt: 100,
      completedAt: 200,
      items: [{ id: "u1", type: "userMessage", content: [{ type: "text", text: "Active" }] }],
    }],
  };

  assert.equal(syncExternalThread(session, completedThread), true);
  await waitFor(() => !session.queueAdvancing);
  assert.deepEqual(sent, []);
  assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-1"]);
  assert.equal(session.events.some((event) => event.event === "error" && event.data.message === "pro_required"), true);
});

test("cross-account handoff rejects a locked execution account before runtime start", async () => {
  let runtimeStarts = 0;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" },
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }],
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    getAccountExecutionAccess: (accountId) => accountId === "account-b"
      ? lockedEntitlementAccess()
      : { allowed: true, entitlementLocked: false },
    startRuntime: async () => { runtimeStarts += 1; },
  });
  const session = {
    id: "locked-handoff-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-a",
    status: "completed",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    queuedMessages: [],
    runtime: null,
  };

  await assert.rejects(
    sendMessage(session, { text: "Continue on B", executionAccountId: "account-b" }),
    proRequired
  );
  assert.equal(runtimeStarts, 0);
  assert.equal(session.accountId, "account-a");
  assert.equal(session.codexThreadId, "thread-a");
});

function lockedEntitlementAccess() {
  return {
    allowed: false,
    entitlementLocked: true,
    status: 402,
    code: "pro_required",
    message: "Pro is required to use this account.",
  };
}

function proRequired(error) {
  return error?.status === 402 && error?.code === "pro_required";
}

test("creates a chat without a project workspace", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });

  const created = await createSession(config, { accountId: "account-a" });

  assert.equal(created.accountId, "account-a");
  assert.equal(created.workspaceId, undefined);
  assert.equal(created.projectLabel, undefined);
  assert.equal(created.projectless, true);
});

test("creates a workspace chat with the current folder label instead of stale config metadata", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Current Project", label: "Old project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });

  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });

  assert.equal(created.projectLabel, "Current Project");
});

test("starts a projectless chat without a workspace path", async () => {
  let runtimeInput;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: []
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async (input) => {
      runtimeInput = input;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    }
  });
  const created = await createSession(config, { accountId: "account-a" });

  await sendMessage(getSession(created.id), { text: "Hello" });

  assert.equal(runtimeInput.cwd, undefined);
});

test("coalesces persistence while a session save is already running", async () => {
  let runtimeEmit;
  let saveCount = 0;
  let blockSaves = false;
  let releaseBlockedSave;
  const store = {
    load: async () => [],
    save: async () => {
      saveCount += 1;
      if (blockSaves && !releaseBlockedSave) {
        await new Promise((resolve) => { releaseBlockedSave = resolve; });
      }
    }
  };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  await sendMessage(getSession(created.id), { text: "Start" });
  await new Promise((resolve) => setImmediate(resolve));

  const baseline = saveCount;
  blockSaves = true;
  runtimeEmit("reasoning.delta", { turnId: "turn-1", text: "1" });
  await waitFor(() => Boolean(releaseBlockedSave));
  for (let index = 2; index <= 10; index += 1) {
    runtimeEmit("reasoning.delta", { turnId: "turn-1", text: String(index) });
  }
  releaseBlockedSave();
  await waitFor(() => saveCount >= baseline + 2);
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(saveCount - baseline, 2);
});

test("coalesces burst assistant deltas in memory and defers their persistence", async () => {
  let runtimeEmit;
  let saveCount = 0;
  const store = {
    load: async () => [],
    save: async () => { saveCount += 1; }
  };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    startThreadWatcher: async () => ({ stop() {} })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "Start" });
  await new Promise((resolve) => setImmediate(resolve));
  const baseline = saveCount;
  const writes = [];
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = (chunk) => { writes.push(chunk); };
  attachEventStream(session, response);

  for (let index = 0; index < 20; index += 1) {
    runtimeEmit("session.output", {
      turnId: "turn-1",
      itemId: "answer-1",
      kind: "assistant_delta",
      text: String(index % 10)
    });
  }
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(saveCount, baseline);
  const outputEvents = session.events.filter((event) =>
    event.event === "session.output" && event.data?.kind === "assistant_delta"
  );
  assert.equal(outputEvents.length, 1);
  assert.equal(outputEvents[0].data.text, "01234567890123456789");
  assert.equal((writes.join("").match(/event: session\.output/g) ?? []).length, 0);
  await waitFor(() => (writes.join("").match(/event: session\.output/g) ?? []).length === 1);
  assert.match(writes.join(""), /01234567890123456789/);
  await waitFor(() => saveCount === baseline + 1);

  runtimeEmit("reasoning.delta", { turnId: "turn-1", text: "think " });
  runtimeEmit("reasoning.delta", { turnId: "turn-1", text: "more" });
  await waitFor(() => (writes.join("").match(/event: reasoning\.delta/g) ?? []).length === 1);
  assert.match(writes.join(""), /think more/);

  const savedAfterDebounce = saveCount;
  runtimeEmit("activity.updated", { turnId: "turn-1", itemId: "command-1", text: "left " });
  runtimeEmit("activity.updated", { turnId: "turn-1", itemId: "command-1", text: "right" });
  runtimeEmit("session.output", {
    turnId: "turn-1",
    itemId: "answer-1",
    kind: "assistant_delta",
    text: "done"
  });
  runtimeEmit("turn.completed", { turnId: "turn-1", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));
  await new Promise((resolve) => setImmediate(resolve));
  const streamed = writes.join("");
  assert.equal((streamed.match(/event: session\.output/g) ?? []).length, 2);
  assert.equal((streamed.match(/event: activity\.updated/g) ?? []).length, 1);
  assert.match(streamed, /left right/);
  assert.ok(streamed.indexOf("event: activity.updated") < streamed.lastIndexOf("event: session.output"));
  assert.ok(streamed.lastIndexOf("event: session.output") < streamed.indexOf("event: turn.completed"));
  assert.ok(saveCount > savedAfterDebounce);
  const savedAfterCompletion = saveCount;
  await new Promise((resolve) => setTimeout(resolve, 300));
  assert.equal(saveCount, savedAfterCompletion);
  response.emit("close");
});

test("resolves a durable save after its own coalesced batch while later events keep streaming", async () => {
  let runtimeEmit;
  let blockSaves = false;
  const blockedSaves = [];
  const store = {
    load: async () => [],
    save: async () => {
      if (blockSaves) await new Promise((resolve) => blockedSaves.push(resolve));
    }
  };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    }
  });
  const active = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  await sendMessage(getSession(active.id), { text: "Start" });
  await new Promise((resolve) => setImmediate(resolve));

  blockSaves = true;
  runtimeEmit("reasoning.delta", { turnId: "turn-1", text: "before" });
  await waitFor(() => blockedSaves.length === 1);
  let created = false;
  const createPromise = createSession(config, { accountId: "account-a", workspaceId: "workspace-a" })
    .then(() => { created = true; });
  blockedSaves.shift()();
  await waitFor(() => blockedSaves.length === 1);
  runtimeEmit("reasoning.delta", { turnId: "turn-1", text: "after" });
  blockedSaves.shift()();
  await waitFor(() => blockedSaves.length === 1);
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(created, true);
  blockedSaves.shift()();
  await createPromise;
});

test("persists a newly created session once", async () => {
  let saveCount = 0;
  const store = {
    load: async () => [],
    save: async () => { saveCount += 1; }
  };
  const config = {
    dataDir: "unused",
    engine: "fake",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store);
  const baseline = saveCount;

  await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });

  assert.equal(saveCount - baseline, 1);
});

test("creates a Codex session without starting the runtime until the first message", async () => {
  let runtimeStarted = false;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => {
      runtimeStarted = true;
      throw new Error("runtime must remain lazy");
    }
  });

  const created = await createSession(config, {
    accountId: "account-a",
    workspaceId: "workspace-a"
  });

  assert.equal(runtimeStarted, false);
  assert.equal(created.status, "idle");
  assert.equal(created.codexThreadId, undefined);
});

test("starts multiple active sessions on the same execution account", async () => {
  const config = {
    dataDir: "unused", engine: "codex", codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => ({ threadId: "thread", async sendMessage() {}, stop() {} })
  });
  const first = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const second = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });

  await sendMessage(getSession(first.id), { text: "First", executionAccountId: "account-a" });

  await sendMessage(getSession(second.id), { text: "Second", executionAccountId: "account-a" });

  assert.equal(getSession(first.id).status, "running");
  assert.equal(getSession(second.id).status, "running");
});

test("rejects resuming a history-only session without starting runtime", async () => {
  let runtimeStarted = false;
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "history", codexHome: "HISTORY_HOME", historyOnly: true }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => {
      runtimeStarted = true;
      throw new Error("must not start runtime");
    }
  });
  const session = {
    id: "history-session",
    accountId: "history",
    workspaceId: "workspace-a",
    codexThreadId: "thread-history",
    status: "completed",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: null
  };

  await assert.rejects(
    sendMessage(session, { text: "Resume" }),
    (error) => error.message === "account_not_available_for_execution" && error.status === 403
  );
  assert.equal(runtimeStarted, false);
});

test("rejects resuming a session with a stale account before starting runtime", async () => {
  let runtimeStarted = false;
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => {
      runtimeStarted = true;
      throw new Error("must not start runtime");
    }
  });
  const session = {
    id: "stale-account-session",
    accountId: "removed-account",
    workspaceId: "workspace-a",
    codexThreadId: "thread-removed",
    status: "completed",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: null
  };

  await assert.rejects(
    sendMessage(session, { text: "Resume stale account" }),
    (error) => error.message === "invalid_session_context" && error.status === 400
  );
  assert.equal(runtimeStarted, false);
});

test("failed runtime resume restores the previous session status", async () => {
  const statusEvents = [];
  let nextEvent;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => { throw new Error("runtime_failed"); }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  Object.assign(session, {
    codexThreadId: "thread-a",
    status: "completed",
    events: [{ id: "old-user", event: "message.accepted", data: { text: "Old request" } }],
    clients: new Set([{
      write(chunk) {
        const text = String(chunk);
        if (text.startsWith("event: ")) {
          nextEvent = text.slice("event: ".length).trim();
          return;
        }
        if (nextEvent !== "session.status" || !text.startsWith("data: ")) return;
        statusEvents.push(JSON.parse(text.slice("data: ".length)));
        nextEvent = undefined;
      }
    }])
  });

  await assert.rejects(
    sendMessage(session, { text: "Resume", executionAccountId: "account-a" }),
    /runtime_failed/
  );

  assert.equal(session.status, "completed");
  assert.equal(session.codexThreadId, "thread-a");
  assert.equal(session.runtime, null);
  assert.equal(statusEvents.at(-1).status, "completed");
});

test("rejects queuing on a running session with a stale workspace", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  const session = {
    id: "stale-workspace-running-session",
    accountId: "account-a",
    workspaceId: "removed-workspace",
    codexThreadId: "thread-1",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: { async sendMessage() {} }
  };

  await assert.rejects(
    sendMessage(session, { text: "Queue on stale workspace" }),
    (error) => error.message === "invalid_session_context" && error.status === 400
  );
  assert.deepEqual(session.queuedMessages, []);
});

test("rejects queuing on a running session without an attached workspace", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  const session = {
    id: "unknown-workspace-running-session",
    accountId: "account-a",
    codexThreadId: "thread-unknown",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: { async sendMessage() {} }
  };

  await assert.rejects(
    sendMessage(session, { text: "Queue without workspace" }),
    (error) => error.message === "invalid_session_context" && error.status === 400
  );
  assert.deepEqual(session.queuedMessages, []);
});

test("rejects steering a queued message with a stale workspace", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  const steeringCalls = [];
  const session = {
    id: "stale-workspace-steer-session",
    accountId: "account-a",
    workspaceId: "removed-workspace",
    codexThreadId: "thread-1",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-1", text: "Do this now", attachments: [], skills: [], createdAt: new Date().toISOString() }],
    runtime: {
      async steerMessage(payload) { steeringCalls.push(payload); return "turn-1"; }
    }
  };

  await assert.rejects(
    steerQueuedMessage(session, "queued-1"),
    (error) => error.message === "invalid_session_context" && error.status === 400
  );
  assert.deepEqual(steeringCalls, []);
  assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-1"]);
});

test("allows opening history from a history-only account", async () => {
  const config = {
    dataDir: "unused",
    accounts: [{ id: "history", codexHome: "HISTORY_HOME", historyOnly: true }],
    workspaces: []
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  const reader = async () => ({
    thread: { id: "thread-history", status: { type: "idle" }, turns: [] }
  });

  const result = await openThread(config, "history", "thread-history", {}, reader);

  assert.equal(result.session.accountId, "history");
  assert.equal(result.session.codexThreadId, "thread-history");
});

test("advances queued messages when an externally running turn completes", async () => {
  const sent = [];
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => ({
      threadId: "thread-1",
      async sendMessage(payload) { sent.push(payload.text); },
      stop() {}
    })
  });
  const session = {
    id: "external-queue-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-1",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-1", text: "Next", options: {}, attachments: [], skills: [], createdAt: new Date().toISOString() }],
    runtime: null
  };
  const completedThread = {
    id: "thread-1",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "completed",
      startedAt: 100,
      completedAt: 200,
      items: [{ id: "u1", type: "userMessage", content: [{ type: "text", text: "Active" }] }]
    }]
  };

  assert.equal(syncExternalThread(session, completedThread), true);
  await new Promise((resolve) => setImmediate(resolve));
  await new Promise((resolve) => setImmediate(resolve));

  assert.deepEqual(sent, ["Next"]);
  assert.deepEqual(session.queuedMessages, []);
  assert.equal(session.status, "running");
});
test("completed runtime turns keep the app-server while viewed and release it on disconnect", async () => {
  let runtimeEmit;
  let runtimeStopped = false;
  let watcherStarted = false;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };

  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return {
        threadId: "thread-1",
        async sendMessage() {},
        stop() { runtimeStopped = true; }
      };
    },
    startThreadWatcher: async () => {
      watcherStarted = true;
      return { stop() {} };
    }
  });

  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  await sendMessage(getSession(id), { text: "Start the turn", executionAccountId: "account-a" });
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = () => {};

  attachEventStream(getSession(id), response);
  runtimeEmit("turn.completed", { turnId: "turn-1", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(runtimeStopped, false);
  assert.notEqual(getSession(id).runtime, null);
  assert.equal(watcherStarted, true);

  response.emit("close");

  assert.equal(runtimeStopped, true);
  assert.equal(getSession(id).runtime, null);
});

test("SSE snapshots sanitize restored public events before writing to clients", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "C:\\CodexHome\\account-a" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\SecretApp", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  const session = {
    id: "snapshot-secret-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "completed",
    events: [{
      id: "event-1",
      event: "error",
      data: { message: "Bearer snapshot-token at C:\\Project\\SecretApp\\auth.json" }
    }],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };
  const writes = [];
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = (chunk) => { writes.push(chunk); };

  attachEventStream(session, response);

  const output = writes.join("");
  assert.equal(output.includes("snapshot-token"), false);
  assert.equal(output.includes("C:\\\\Project\\\\SecretApp"), false);
  assert.match(output, /Bearer \[redacted\]/);
  assert.match(output, /\[private-path\]/);
  response.emit("close");
});

test("public queued messages redact secrets and hide skill paths", async () => {
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "C:\\CodexHome\\account-a" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\SecretApp", label: "Project" }]
  }, { load: async () => [], save: async () => {} });
  const session = {
    id: "queued-public-secret",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{
      id: "queued-1",
      text: "Open C:\\Project\\SecretApp\\auth.json with Bearer queued-token",
      attachments: [],
      skills: [{ name: "imagegen", path: "C:\\Skills\\imagegen" }],
      createdAt: "2026-06-27T00:00:30.000Z"
    }],
    runtime: {}
  };

  const projected = publicSession(session);
  const serialized = JSON.stringify(projected);

  assert.equal(serialized.includes("queued-token"), false);
  assert.equal(serialized.includes("C:\\\\Project\\\\SecretApp"), false);
  assert.equal(serialized.includes("auth.json"), false);
  assert.equal(serialized.includes("C:\\\\Skills"), false);
  assert.deepEqual(projected.queuedMessages[0].skills, [{ name: "imagegen" }]);
  assert.equal(projected.queuedMessages[0].text, "Open [private-path][redacted] with Bearer [redacted]");
});

test("stopping a pending external watcher does not fail a newly started turn", async () => {
  let runtimeEmit;
  let watcherEmit;
  let finishWatcherStart;
  const watcherStartGate = new Promise((resolve) => { finishWatcherStart = resolve; });
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    startThreadWatcher: async ({ onEvent }) => {
      watcherEmit = onEvent;
      await watcherStartGate;
      return { stop() { watcherEmit("process.exited", { code: null }); } };
    }
  });
  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const live = getSession(id);
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = () => {};

  await sendMessage(live, { text: "First turn" });
  attachEventStream(live, response);
  runtimeEmit("turn.completed", { turnId: "turn-1", status: "completed" });
  await sendMessage(live, { text: "Second turn" });
  finishWatcherStart();
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(live.status, "running");
  assert.equal(live.events.some((event) => event.event === "process.exited"), false);
  response.emit("close");
});

test("runtime file-change activity exposes safe paths and patch diffs", async () => {
  let runtimeEmit;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    }
  });
  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const live = getSession(id);

  await sendMessage(live, { text: "Patch" });
  runtimeEmit("activity.completed", {
    turnId: "turn-1",
    itemId: "files-1",
    kind: "fileChange",
    status: "completed",
    changes: [
      { path: "src\\app.js", diff: "+safe" },
      { path: "C:\\Private\\secret.txt", diff: "+secret" }
    ]
  });

  await waitFor(() => live.events.some((event) => event.data?.itemId === "files-1"));
  const activity = live.events.find((event) => event.data?.itemId === "files-1");
  assert.deepEqual(activity.data.paths, ["src/app.js"]);
  assert.deepEqual(activity.data.diffs, [{ path: "src/app.js", diff: "+safe" }]);
  assert.equal(JSON.stringify(activity).includes("Private"), false);
});

test("runtime generated images become durable opaque attachments before public emission", async () => {
  const root = await mkdtemp(join(tmpdir(), "code-remote-manager-runtime-image-"));
  const sourcePath = join(root, "outside-workspace.png");
  await writeFile(sourcePath, Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
  let runtimeEmit;
  const config = {
    dataDir: join(root, "data"),
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: join(root, "workspace"), label: "Project" }]
  };

  try {
    await initializeSessions(config, { load: async () => [], save: async () => {} }, {
      startRuntime: async ({ emit }) => {
        runtimeEmit = emit;
        return { threadId: "thread-image", async sendMessage() {}, stop() {} };
      }
    });
    const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    const live = getSession(id);
    await sendMessage(live, { text: "Generate" });
    const data = { turnId: "turn-image", itemId: "image-1", kind: "imageGeneration", status: "completed" };
    Object.defineProperty(data, INTERNAL_GENERATED_IMAGE_ITEM, {
      value: { id: "image-1", type: "imageGeneration", savedPath: sourcePath }
    });

    runtimeEmit("activity.completed", data);

    const activity = live.events.find((event) => event.data?.itemId === "image-1");
    assert.equal(activity.data.images.length, 1);
    assert.match(activity.data.images[0].url, new RegExp(`^/api/sessions/${id}/attachments/generated-`));
    assert.equal(JSON.stringify(activity).includes(sourcePath), false);
    assert.deepEqual((await getAttachment(live, activity.data.images[0].id)).data,
      Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
  } finally {
    await rm(root, { recursive: true, force: true });
  }
});

test("live assistant artifact directives are projected from cumulative snapshots across split deltas", async () => {
  const root = await mkdtemp(join(tmpdir(), "code-remote-manager-live-artifact-"));
  const workspacePath = join(root, "workspace");
  let runtimeEmit;
  const config = {
    dataDir: join(root, "data"),
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: workspacePath, label: "Project" }]
  };

  try {
    await mkdir(workspacePath, { recursive: true });
    await writeFile(join(workspacePath, "preview.html"), "<h1>Preview</h1>");
    await writeFile(join(workspacePath, "README.md"), "# Read me");
    await initializeSessions(config, { load: async () => [], save: async () => {} }, {
      startRuntime: async ({ emit }) => {
        runtimeEmit = emit;
        return { threadId: "thread-artifact", async sendMessage() {}, stop() {} };
      }
    });
    const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    const live = getSession(id);
    await sendMessage(live, { text: "Preview" });
    runtimeEmit("activity.started", { turnId: "turn-1", itemId: "answer-1", kind: "agentMessage", phase: "final_answer" });
    runtimeEmit("session.output", { turnId: "turn-1", itemId: "answer-1", kind: "assistant_delta", text: "::codex-inline-vis{file=\"pre" });
    runtimeEmit("session.output", { turnId: "turn-1", itemId: "answer-1", kind: "assistant_delta", text: "view.html\"}" });

    const snapshots = live.events.filter((event) => event.data?.kind === "assistant_snapshot");
    assert.equal(snapshots.length, 1);
    assert.equal(live.events.some((event) => event.data?.kind === "assistant_delta" && event.data.text.includes("::codex-inline-vis")), true);
    assert.match(snapshots[0].data.text, /::codex-inline-artifact\{id="artifact-[a-f0-9]{24}" name="preview\.html" url="\/api\/sessions\/.+\/artifacts\/artifact-[a-f0-9]{24}\/preview\.html"\}/);
    assert.equal(snapshots[0].data.text.includes(workspacePath), false);
    assert.equal(live.attachments.some((item) => item.kind === "artifact" && item.name === "preview.html"), true);
    runtimeEmit("session.output", { turnId: "turn-1", itemId: "answer-1", kind: "assistant_delta", text: " then " });
    runtimeEmit("session.output", { turnId: "turn-1", itemId: "answer-1", kind: "assistant_delta", text: "[Read](README.md)" });
    const finalAssistantEvents = live.events.filter((event) => event.event === "session.output" && event.data?.itemId === "answer-1");
    assert.equal(finalAssistantEvents.length, 1);
    assert.equal(finalAssistantEvents[0].data.kind, "assistant_snapshot");
    assert.match(finalAssistantEvents[0].data.text, /inline-artifact[\s\S]*\[Read\]\(\/api\/sessions\//);
  } finally {
    await rm(root, { recursive: true, force: true });
  }
});

test("live command output is bounded before SSE persistence", async () => {
  let runtimeEmit;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-output", async sendMessage() {}, stop() {} };
    }
  });
  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const live = getSession(id);
  await sendMessage(live, { text: "Run" });
  live.events.findLast = () => { throw new Error("activity updates must not scan the event list"); };

  runtimeEmit("activity.updated", { turnId: "turn-output", itemId: "command-1", text: "a".repeat(20 * 1024) });
  runtimeEmit("activity.updated", { turnId: "turn-output", itemId: "command-1", text: "b".repeat(20 * 1024) });
  runtimeEmit("activity.updated", { turnId: "turn-output", itemId: "command-1", text: "must-not-pass" });

  const text = live.events.filter((event) => event.event === "activity.updated").map((event) => event.data.text).join("");
  assert.equal(live.events.filter((event) => event.event === "activity.updated").length, 1);
  assert.equal(text.includes("must-not-pass"), false);
  assert.match(text, /\[output truncated\]$/);
  assert.ok(Buffer.byteLength(text, "utf8") <= 32 * 1024 + 32);
});

test("runtime file-change activity snapshots workspace diffs when app-server sends only paths", async () => {
  let runtimeEmit;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    },
    readWorkspaceDiff: async (cwd, path) => ({ path, diff: `diff --git a/${path} b/${path}\n+snapshot from ${cwd}` })
  });
  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const live = getSession(id);

  await sendMessage(live, { text: "Patch" });
  runtimeEmit("activity.completed", {
    turnId: "turn-1",
    itemId: "files-1",
    kind: "fileChange",
    status: "completed",
    changes: [{ path: "src\\app.js" }]
  });

  await waitFor(() => live.events.some((event) => event.data?.itemId === "files-1"));
  const activity = live.events.find((event) => event.data?.itemId === "files-1");
  assert.deepEqual(activity.data.paths, ["src/app.js"]);
  assert.deepEqual(activity.data.diffs, [{ path: "src/app.js", diff: "diff --git a/src/app.js b/src/app.js\n+snapshot from [private-path]" }]);
});

test("completed runtime turns release the app-server when viewer disconnected during work", async () => {
  let runtimeEmit;
  let runtimeStopped = false;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() { runtimeStopped = true; } };
    }
  });

  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  await sendMessage(getSession(id), { text: "Start", executionAccountId: "account-a" });
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = () => {};
  attachEventStream(getSession(id), response);
  response.emit("close");

  runtimeEmit("turn.completed", { turnId: "turn-1", status: "completed" });

  assert.equal(runtimeStopped, true);
  assert.equal(getSession(id).runtime, null);
});

test("runtime process exit fails an active session and releases the app-server without restart", async () => {
  let runtimeEmit;
  let runtimeStops = 0;
  let runtimeStarts = 0;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeStarts += 1;
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() { runtimeStops += 1; } };
    }
  });

  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(id);
  await sendMessage(session, { text: "Start" });

  runtimeEmit("process.exited", { code: 1 });

  assert.equal(session.status, "failed");
  assert.equal(session.runtime, null);
  assert.equal(runtimeStarts, 1);
  assert.equal(runtimeStops, 1);
  assert.equal(session.events.at(-2).event, "process.exited");
  assert.equal(session.events.at(-1).event, "session.status");
});

test("runtime errors fail an active session, release runtime, and stay sanitized", async () => {
  let runtimeEmit;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "C:\\Users\\operator\\.codex-main" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Codex-remote", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    }
  });

  const { id } = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(id);
  await sendMessage(session, { text: "Start" });

  runtimeEmit("error", { message: "Bearer secret-token at C:\\Project\\Codex-remote\\auth.json" });

  const serialized = JSON.stringify(session.events);
  assert.equal(session.status, "failed");
  assert.equal(session.runtime, null);
  assert.equal(serialized.includes("secret-token"), false);
  assert.equal(serialized.includes("C:\\\\Project\\\\Codex-remote"), false);
  assert.equal(serialized.includes("auth.json"), false);
  assert.match(serialized, /\[redacted\]|\[private-path\]/);
});

test("completed runtime turns reuse the live app-server before advancing queued work", async () => {
  let runtimeEmit;
  let runtimeStops = 0;
  const sent = [];
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };

  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push(payload);
          return "turn-" + sent.length;
        },
        stop() { runtimeStops += 1; }
      };
    }
  });

  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "First", executionAccountId: "account-a", options: { permissionProfile: ":workspace" } });
  await sendMessage(session, { text: "Next", executionAccountId: "account-a", options: { permissionProfile: ":danger-full-access" } });

  runtimeEmit("turn.completed", { turnId: "turn-1", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));
  await new Promise((resolve) => setImmediate(resolve));

  assert.deepEqual(sent.map(({ text, options }) => ({ text, options })), [
    { text: "First", options: { permissionProfile: ":workspace", planMode: false } },
    { text: "Next", options: { permissionProfile: ":danger-full-access", planMode: false } }
  ]);
  assert.equal(runtimeStops, 0);
  assert.equal(session.runtime?.threadId, "thread-1");
  assert.deepEqual(session.queuedMessages, []);
  assert.equal(session.status, "running");
});

test("queued work hands off to the execution account selected when queued", async () => {
  const runtimeCalls = [];
  const runtimeEmits = new Map();
  const runtimeCounts = new Map();
  const sent = [];
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ accountId, codexHome, threadId, emit }) => {
      const runtimeNumber = (runtimeCounts.get(accountId) ?? 0) + 1;
      runtimeCounts.set(accountId, runtimeNumber);
      const runtimeThreadId = `thread-${accountId}-${runtimeNumber}`;
      runtimeCalls.push({ accountId, codexHome, threadId, runtimeThreadId });
      runtimeEmits.set(accountId, emit);
      return {
        threadId: runtimeThreadId,
        async sendMessage(payload) {
          sent.push({ accountId, text: payload.text });
          return `turn-${sent.length}`;
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Active on A", executionAccountId: "account-a" });
  await sendMessage(session, { text: "Continue on B", executionAccountId: "account-b" });
  await sendMessage(session, { text: "Later on A", executionAccountId: "account-a" });

  assert.equal(session.queuedMessages[0].executionAccountId, "account-b");
  assert.equal(session.queuedMessages[1].executionAccountId, "account-a");
  runtimeEmits.get("account-a")("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => sent.length === 2);

  assert.deepEqual(runtimeCalls, [
    { accountId: "account-a", codexHome: "A_HOME", threadId: undefined, runtimeThreadId: "thread-account-a-1" },
    { accountId: "account-b", codexHome: "B_HOME", threadId: undefined, runtimeThreadId: "thread-account-b-1" }
  ]);
  assert.equal(sent[0].accountId, "account-a");
  assert.equal(sent[1].accountId, "account-b");
  assert.match(sent[1].text, /Current user request:\nContinue on B/);
  assert.equal(sent[1].text.match(/Continue on B/g)?.length, 1);
  assert.equal(sent[1].text.includes("Later on A"), false);
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Later on A"]);

  runtimeEmits.get("account-b")("turn.completed", { turnId: "turn-2", status: "completed" });
  await waitFor(() => sent.length === 3);

  assert.deepEqual(runtimeCalls, [
    { accountId: "account-a", codexHome: "A_HOME", threadId: undefined, runtimeThreadId: "thread-account-a-1" },
    { accountId: "account-b", codexHome: "B_HOME", threadId: undefined, runtimeThreadId: "thread-account-b-1" },
    { accountId: "account-a", codexHome: "A_HOME", threadId: undefined, runtimeThreadId: "thread-account-a-2" }
  ]);
  assert.equal(sent[2].accountId, "account-a");
  assert.match(sent[2].text, /Current user request:\nLater on A/);
  assert.equal(sent[2].text.match(/Continue on B/g)?.length, 1);
  assert.equal(session.accountId, "account-a");
  assert.deepEqual(session.threadRefs, [
    { accountId: "account-a", threadId: "thread-account-a-1" },
    { accountId: "account-b", threadId: "thread-account-b-1" },
    { accountId: "account-a", threadId: "thread-account-a-2" }
  ]);
  assert.deepEqual(session.queuedMessages, []);
});

test("sanitizes restored events before sending SSE snapshots", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "C:\\CodexHome\\account-a" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\SecretApp" }]
  };
  await initializeSessions(config, {
    load: async () => [{
      id: "restored-session",
      accountId: "account-a",
      workspaceId: "workspace-a",
      status: "completed",
      createdAt: "2026-06-27T00:00:00.000Z",
      updatedAt: new Date().toISOString(),
      events: [{
        id: "event-1",
        event: "error",
        data: { message: "Failed at C:\\Project\\SecretApp with Bearer abc.def and sk-secret" }
      }],
      approvals: []
    }],
    save: async () => {}
  });
  const writes = [];
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = (chunk) => { writes.push(chunk); };

  attachEventStream(getSession("restored-session"), response);

  const payload = writes.join("");
  assert.equal(payload.includes("C:\\\\Project\\\\SecretApp"), false);
  assert.equal(payload.includes("abc.def"), false);
  assert.equal(payload.includes("sk-secret"), false);
  assert.match(payload, /\[private-path\]/);
  assert.match(payload, /\[redacted\]/);
});

test("sending with a different execution account starts a handoff thread", async () => {
  const runtimeCalls = [];
  const sentPayloads = [];
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME", historyOnly: true },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ codexHome, threadId }) => {
      runtimeCalls.push({ codexHome, threadId });
      return {
        threadId: "thread-b",
        async sendMessage(payload) { sentPayloads.push(payload); },
        stop() {}
      };
    }
  });
  const session = {
    id: "handoff-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-a",
    status: "completed",
    events: [
      { id: "old-user", event: "message.accepted", data: { text: "Old request" }, createdAt: "2026-06-29T00:00:00.000Z" },
      { id: "old-answer-partial", event: "session.output", data: { itemId: "answer-1", kind: "assistant_snapshot", text: "Old" }, createdAt: "2026-06-29T00:00:01.000Z" },
      { id: "old-answer", event: "session.output", data: { itemId: "answer-1", kind: "assistant_snapshot", text: "Old answer" }, createdAt: "2026-06-29T00:00:02.000Z" }
    ],
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };

  await sendMessage(session, { text: "Continue on B", executionAccountId: "account-b" });

  assert.deepEqual(runtimeCalls, [{ codexHome: "B_HOME", threadId: undefined }]);
  assert.equal(session.accountId, "account-b");
  assert.equal(session.codexThreadId, "thread-b");
  assert.deepEqual(session.threadRefs, [
    { accountId: "account-a", threadId: "thread-a" },
    { accountId: "account-b", threadId: "thread-b" }
  ]);
  assert.equal(session.conversationTitle, "Old request");
  assert.equal(session.handoffBaseEvents.length, 3);
  assert.match(sentPayloads[0].text, /Old request/);
  assert.match(sentPayloads[0].text, /Old answer/);
  assert.equal(sentPayloads[0].text.match(/Assistant:/g)?.length, 1);
  assert.match(sentPayloads[0].text, /Current user request:\nContinue on B/);
});

test("failed account handoff preserves the original session owner and thread", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async () => { throw new Error("runtime_failed"); }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  const existingRuntime = { threadId: "thread-a", async sendMessage() {}, stop() {} };
  Object.assign(session, {
    codexThreadId: "thread-a",
    threadRefs: [{ accountId: "account-a", threadId: "thread-a" }],
    status: "completed",
    events: [{ id: "old-user", event: "message.accepted", data: { text: "Old request" } }],
    runtime: existingRuntime
  });

  await assert.rejects(
    sendMessage(session, { text: "Continue on B", executionAccountId: "account-b" }),
    /runtime_failed/
  );

  assert.equal(session.accountId, "account-a");
  assert.equal(session.codexThreadId, "thread-a");
  assert.deepEqual(session.threadRefs, [{ accountId: "account-a", threadId: "thread-a" }]);
  assert.equal(session.status, "completed");
  assert.equal(session.runtime, existingRuntime);
});

test("handoff sync keeps prior events and hides the injected handoff prompt", () => {
  const handoffPrompt = "Continue this work from prior context.\n\nCurrent user request:\nContinue on B";
  const session = {
    id: "handoff-sync",
    accountId: "account-b",
    codexThreadId: "thread-b",
    status: "completed",
    events: [],
    handoffBaseEvents: [
      { id: "old-user", event: "message.accepted", data: { text: "Old request" }, createdAt: "2026-06-29T00:00:00.000Z" }
    ],
    handoffPromptText: handoffPrompt,
    handoffUserText: "Continue on B",
    clients: new Set(),
    approvals: new Map(),
    files: [],
    runtime: null
  };

  const changed = syncExternalThread(session, {
    id: "thread-b",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-b",
      status: "completed",
      startedAt: 100,
      completedAt: 101,
      items: [
        { id: "user-b", type: "userMessage", content: [{ type: "text", text: handoffPrompt }] },
        { id: "answer-b", type: "agentMessage", phase: "final_answer", text: "B answer" }
      ]
    }]
  });

  assert.equal(changed, true);
  assert.deepEqual(
    session.events.filter((event) => event.event === "message.accepted").map((event) => event.data.text),
    ["Old request", "Continue on B"]
  );
  assert.equal(JSON.stringify(session.events).includes("prior context"), false);
});

test("stops an active runtime turn and marks the session interrupted", async () => {
  let runtimeInterrupted = false;
  let runtimeStopped = false;
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);
  const session = {
    id: "stop-session",
    status: "running",
    events: [{ id: "started", event: "turn.started", data: { turnId: "turn-1" }, createdAt: new Date().toISOString() }],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-1", text: "Later", attachments: [], skills: [], createdAt: new Date().toISOString() }],
    runtime: {
      async interrupt() { runtimeInterrupted = true; },
      stop() { runtimeStopped = true; }
    }
  };

  await stopSession(session);

  assert.equal(runtimeInterrupted, true);
  assert.equal(runtimeStopped, true);
  assert.equal(session.runtime, null);
  assert.equal(session.status, "interrupted");
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Later"]);
  const completed = session.events.findLast((event) => event.event === "turn.completed");
  assert.equal(completed.data.status, "interrupted");
});

test("stops a pending turn start without restoring a false running state", async () => {
  let resolveTurnStart;
  let startCanceled = false;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => ({
      threadId: "thread-1",
      async sendMessage() {
        await new Promise((resolve) => { resolveTurnStart = resolve; });
        if (startCanceled) throw new Error("turn_start_canceled");
        return "turn-1";
      },
      cancelPendingStart() {
        startCanceled = true;
        return true;
      },
      async interrupt() {
        throw new Error("active_turn_unavailable");
      },
      stop() {}
    })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  const sending = sendMessage(session, { text: "Start", executionAccountId: "account-a" });
  await waitFor(() => typeof resolveTurnStart === "function");

  assert.equal(session.status, "starting");
  const stopping = stopSession(session);
  assert.equal(startCanceled, true);
  resolveTurnStart();
  await stopping;
  await sending;
  assert.equal(session.status, "interrupted");
  assert.equal(session.events.some((event) => event.event === "error"), false);
});

test("stops runtime initialization and discards a late runtime handle", async () => {
  let resolveRuntime;
  let runtimeStopped = false;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const receipts = createTestActionReceiptStore();
  await initializeSessions(config, store, {
    actionReceiptStore: receipts,
    startRuntime: async () => new Promise((resolve) => { resolveRuntime = resolve; })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  const clientMessageId = "00000000-0000-4000-8000-000000000101";
  const sending = sendMessage(session, { text: "Start", executionAccountId: "account-a", clientMessageId });
  await waitFor(() => typeof resolveRuntime === "function");

  assert.equal(session.status, "starting");
  await stopSession(session);
  assert.equal(session.status, "interrupted");

  resolveRuntime({
    threadId: "thread-late",
    async sendMessage() { throw new Error("late_runtime_must_not_start_turn"); },
    stop() { runtimeStopped = true; }
  });
  await sending;
  assert.equal(runtimeStopped, true);
  assert.equal(session.runtime, null);
  assert.equal(session.status, "interrupted");
  assert.equal(session.events.filter((event) => event.event === "message.accepted" && event.data.clientMessageId === clientMessageId).length, 1);
  assert.deepEqual(
    await sendMessage(session, { text: "Start", executionAccountId: "account-a", clientMessageId }),
    { disposition: "started" }
  );
});

test("stops account handoff initialization without adopting its late runtime", async () => {
  let resolveRuntime;
  let runtimeStopped = false;
  let runtimeSendCount = 0;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const receipts = createTestActionReceiptStore();
  await initializeSessions(config, store, {
    actionReceiptStore: receipts,
    startRuntime: async () => new Promise((resolve) => { resolveRuntime = resolve; })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  const clientMessageId = "00000000-0000-4000-8000-000000000102";
  const sending = sendMessage(session, { text: "Switch", executionAccountId: "account-b", clientMessageId });
  await waitFor(() => typeof resolveRuntime === "function");
  await stopSession(session);

  resolveRuntime({
    threadId: "thread-b-late",
    async sendMessage() { runtimeSendCount += 1; return "turn-b-late"; },
    stop() { runtimeStopped = true; }
  });
  await sending;

  assert.equal(runtimeStopped, true);
  assert.equal(runtimeSendCount, 0);
  assert.equal(session.runtime, null);
  assert.equal(session.accountId, "account-a");
  assert.equal(session.status, "interrupted");
  assert.equal(session.events.filter((event) => event.event === "message.accepted" && event.data.clientMessageId === clientMessageId).length, 1);
  assert.deepEqual(
    await sendMessage(session, { text: "Switch", executionAccountId: "account-b", clientMessageId }),
    { disposition: "started" }
  );
});

test("projects a safe timeout code when turn start never succeeds", async () => {
  let runtimeStopped = false;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => ({
      threadId: "thread-1",
      async sendMessage() { throw new Error("turn/start timeout with private details"); },
      stop() { runtimeStopped = true; }
    })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Start", executionAccountId: "account-a" });

  assert.equal(session.status, "failed");
  assert.equal(runtimeStopped, true);
  assert.equal(session.runtime, null);
  assert.equal(session.events.findLast((event) => event.event === "error").data.message, "turn_start_timeout");
  assert.equal(JSON.stringify(session.events).includes("private details"), false);
});

test("keeps timeout feedback when runtime cleanup reports interruption first", async () => {
  let emitRuntime;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage() {
          emitRuntime("turn.completed", { turnId: "turn-1", status: "interrupted" });
          throw new Error("turn/start timeout with private details");
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Start", executionAccountId: "account-a" });

  assert.equal(session.status, "failed");
  assert.equal(session.events.findLast((event) => event.event === "error")?.data.message, "turn_start_timeout");
});

test("does not start a turn when stop wins during skill resolution", async () => {
  let resolveSkills;
  let runtimeSendCount = 0;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => ({
      threadId: "thread-1",
      async sendMessage() { runtimeSendCount += 1; return "turn-late"; },
      cancelPendingStart() { return false; },
      stop() {}
    }),
    requestCodexAppServer: async () => new Promise((resolve) => { resolveSkills = resolve; })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  const sending = sendMessage(session, {
    text: "Start",
    executionAccountId: "account-a",
    skills: [{ name: "review" }]
  });
  await waitFor(() => typeof resolveSkills === "function");

  await stopSession(session);
  resolveSkills({ data: [{ skills: [{ name: "review", path: "C:\\Private\\SKILL.md", enabled: true }] }] });
  await sending;

  assert.equal(runtimeSendCount, 0);
  assert.equal(session.status, "interrupted");
});

test("stop keeps the session running until runtime interrupt settles", async () => {
  let resolveInterrupt;
  const interruptStarted = new Promise((resolve) => {
    resolveInterrupt = resolve;
  });
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);
  const session = {
    id: "pending-stop-session",
    status: "running",
    events: [{ id: "started", event: "turn.started", data: { turnId: "turn-1" }, createdAt: new Date().toISOString() }],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: {
      async interrupt() { await interruptStarted; },
      stop() {}
    }
  };

  const stopping = stopSession(session);
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(session.status, "running");
  assert.equal(session.stopping, true);
  resolveInterrupt();
  await stopping;
});

test("does not queue new messages while stop is still interrupting runtime", async () => {
  let resolveInterrupt;
  const interruptStarted = new Promise((resolve) => {
    resolveInterrupt = resolve;
  });
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);
  const session = {
    id: "stopping-send-session",
    status: "running",
    workspaceId: "workspace-a",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: {
      async interrupt() { await interruptStarted; },
      stop() {},
      async sendMessage() { throw new Error("must_not_send_while_stopping"); }
    }
  };

  const stopping = stopSession(session);
  await new Promise((resolve) => setImmediate(resolve));

  await assert.rejects(sendMessage(session, { text: "Next" }), /session_busy/);
  assert.deepEqual(session.queuedMessages, []);
  resolveInterrupt();
  await stopping;
});

test("stop is idempotent after a turn has already reached a terminal status", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);
  const session = {
    id: "already-failed-session",
    status: "failed",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: null
  };

  await stopSession(session);

  assert.equal(session.status, "failed");
});

test("repeated account handoffs preserve every visible turn and thread reference", async () => {
  let nextThreadId = "thread-b";
  const sentPayloads = [];
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => ({
      threadId: nextThreadId,
      async sendMessage(payload) { sentPayloads.push(payload); },
      stop() {}
    })
  });
  const session = {
    id: "multi-handoff",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-a",
    status: "completed",
    events: [
      { id: "user-a", event: "message.accepted", data: { text: "First request" } },
      { id: "answer-a", event: "session.output", data: { kind: "assistant_delta", text: "First answer" } }
    ],
    clients: new Set(), approvals: new Map(), files: [], runtime: null
  };

  await sendMessage(session, { text: "Continue on B", executionAccountId: "account-b" });
  session.status = "completed";
  session.runtime = null;
  session.events = [
    ...session.handoffBaseEvents,
    { id: "user-b", event: "message.accepted", data: { text: "Continue on B" } },
    { id: "answer-b", event: "session.output", data: { kind: "assistant_delta", text: "Second answer" } }
  ];
  nextThreadId = "thread-c";

  await sendMessage(session, { text: "Back on A", executionAccountId: "account-a" });

  assert.match(sentPayloads[1].text, /Second answer/);
  assert.deepEqual(session.threadRefs, [
    { accountId: "account-a", threadId: "thread-a" },
    { accountId: "account-b", threadId: "thread-b" },
    { accountId: "account-a", threadId: "thread-c" }
  ]);
});

test("account handoff limits prior visible conversation to recent context", async () => {
  const sentPayloads = [];
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    handoffContextMaxChars: 90,
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => ({
      threadId: "thread-b",
      async sendMessage(payload) { sentPayloads.push(payload); },
      stop() {}
    })
  });
  const session = {
    id: "handoff-limit",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-a",
    status: "completed",
    events: [
      { id: "old-user", event: "message.accepted", data: { text: "Old request should be trimmed away" } },
      { id: "old-answer", event: "session.output", data: { kind: "assistant_delta", text: "Old answer should be trimmed away" } },
      { id: "new-user", event: "message.accepted", data: { text: "Recent request stays" } },
      { id: "new-answer", event: "session.output", data: { kind: "assistant_delta", text: "Recent answer stays" } }
    ],
    clients: new Set(), approvals: new Map(), files: [], runtime: null
  };

  await sendMessage(session, { text: "Continue on B", executionAccountId: "account-b" });

  assert.equal(sentPayloads.length, 1);
  assert.equal(sentPayloads[0].text.includes("Old request should be trimmed away"), false);
  assert.equal(sentPayloads[0].text.includes("Old answer should be trimmed away"), false);
  assert.match(sentPayloads[0].text, /Recent request stays/);
  assert.match(sentPayloads[0].text, /Recent answer stays/);
  assert.match(sentPayloads[0].text, /Current user request:\nContinue on B/);
});

test("reopening a handoff thread keeps the injected context prompt hidden", async () => {
  const handoffPrompt = "Continue this work from prior context.\n\nCurrent user request:\nContinue on B";
  const stored = [{
    id: "handoff-reopen",
    accountId: "account-b",
    workspaceId: "workspace-a",
    codexThreadId: "thread-b",
    status: "completed",
    events: [],
    handoffBaseEvents: [
      { id: "old-user", event: "message.accepted", data: { text: "Old request" }, createdAt: "2026-06-29T00:00:00.000Z" }
    ],
    handoffPromptText: handoffPrompt,
    handoffUserText: "Continue on B",
    approvals: [],
    files: []
  }];
  const store = { load: async () => stored, save: async () => {} };
  const config = {
    dataDir: "unused",
    accounts: [{ id: "account-b", codexHome: "B_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const reader = async () => ({
    thread: {
      id: "thread-b",
      cwd: "C:\\Project",
      status: { type: "idle" },
      turns: [{
        id: "turn-b",
        status: "completed",
        items: [
          { id: "user-b", type: "userMessage", content: [{ type: "text", text: handoffPrompt }] },
          { id: "answer-b", type: "agentMessage", phase: "final_answer", text: "B answer" }
        ]
      }]
    }
  });
  await initializeSessions(config, store);

  const result = await openThread(config, "account-b", "thread-b", {}, reader);

  assert.deepEqual(
    result.events.filter((event) => event.event === "message.accepted").map((event) => event.data.text),
    ["Old request", "Continue on B"]
  );
  assert.equal(JSON.stringify(result.events).includes("prior context"), false);
});

test("restores sessions and keeps queued work interrupted", async () => {
  const stored = [{
    id: "restored-1",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-1",
    status: "running",
    createdAt: "2026-06-27T00:00:00.000Z",
    updatedAt: "2026-06-27T00:01:00.000Z",
    events: [],
    approvals: [],
    files: [],
    queuedMessages: [{ id: "queued-restored", text: "Do not auto-run", options: {}, attachments: [], skills: [], createdAt: "2026-06-27T00:00:30.000Z" }]
  }];
  const store = { load: async () => stored, save: async () => {} };

  await initializeSessions({ dataDir: "unused" }, store, {
    startRuntime: async () => { throw new Error("restored queue must not auto-run"); }
  });

  const [session] = listSessions();
  assert.equal(session.id, "restored-1");
  assert.equal(session.status, "interrupted");
  assert.equal(session.codexThreadId, "thread-1");
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Do not auto-run"]);
  assert.equal("codexHome" in session, false);
});

test("restores a linked session with the current workspace label", async () => {
  const stored = [{
    id: "restored-label",
    accountId: "account-a",
    workspaceId: "workspace-a",
    projectLabel: "Old project",
    status: "completed",
    createdAt: "2026-06-27T00:00:00.000Z",
    updatedAt: new Date().toISOString(),
    events: []
  }];
  const config = {
    dataDir: "unused",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Current Project", label: "Old project" }]
  };

  await initializeSessions(config, { load: async () => stored, save: async () => {} });

  assert.equal(getSession("restored-label").projectLabel, "Current Project");
});

test("restored queues wait for an explicit action after external completion", async () => {
  let watcherThread;
  let runtimeEmit;
  let runtimeStarts = 0;
  const sent = [];
  const stored = [{
    id: "restored-external-queue",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-1",
    status: "running",
    createdAt: "2026-06-27T00:00:00.000Z",
    updatedAt: "2026-06-27T00:01:00.000Z",
    events: [],
    approvals: [],
    files: [],
    queuedMessages: [{
      id: "queued-restored",
      text: "Do not auto-run",
      options: {},
      attachments: [],
      skills: [],
      createdAt: "2026-06-27T00:00:30.000Z"
    }]
  }];
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => stored, save: async () => {} }, {
    startThreadWatcher: async ({ onThread }) => {
      watcherThread = onThread;
      return { stop() {} };
    },
    startRuntime: async ({ emit }) => {
      runtimeStarts += 1;
      runtimeEmit = emit;
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push(payload.text);
          return `turn-${sent.length}`;
        },
        stop() {}
      };
    }
  });
  const session = getSession("restored-external-queue");
  const response = new EventEmitter();
  response.writeHead = () => {};
  response.write = () => {};
  attachEventStream(session, response);
  await waitFor(() => typeof watcherThread === "function");

  watcherThread({
    id: "thread-1",
    status: { type: "active" },
    updatedAt: 100,
    turns: [{
      id: "turn-1",
      status: "inProgress",
      startedAt: 100,
      items: [{ id: "user-1", type: "userMessage", content: [{ type: "text", text: "Active" }] }]
    }]
  });
  watcherThread({
    id: "thread-1",
    status: { type: "idle" },
    updatedAt: 200,
    turns: [{
      id: "turn-1",
      status: "completed",
      startedAt: 100,
      completedAt: 200,
      items: [{ id: "user-1", type: "userMessage", content: [{ type: "text", text: "Active" }] }]
    }]
  });
  await new Promise((resolve) => setImmediate(resolve));
  await new Promise((resolve) => setImmediate(resolve));

  assert.equal(runtimeStarts, 0);
  assert.deepEqual(sent, []);
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Do not auto-run"]);
  assert.equal(session.status, "completed");

  await sendMessage(session, { text: "Resume explicitly", executionAccountId: "account-a" });
  runtimeEmit("turn.completed", { turnId: "turn-2", status: "completed" });
  await waitFor(() => sent.length === 2);

  assert.equal(runtimeStarts, 1);
  assert.deepEqual(sent, ["Resume explicitly", "Do not auto-run"]);
  assert.deepEqual(session.queuedMessages, []);
  response.emit("close");
});

test("restores queued attachment paths from safe persisted metadata", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-restore-"));
  const attachmentPath = join(dataDir, "attachments", "restored-attachment-session", "attachment-1.jpg");
  await mkdir(join(dataDir, "attachments", "restored-attachment-session"), { recursive: true });
  await writeFile(attachmentPath, "jpeg");
  const stored = [{
    id: "restored-attachment-session",
    status: "interrupted",
    events: [],
    approvals: [],
    attachments: [{ id: "attachment-1", kind: "image", name: "photo.jpg", mimeType: "image/jpeg" }],
    queuedMessages: [{ id: "queued-1", text: "Use image", attachments: [{ id: "attachment-1" }], skills: [] }]
  }];

  try {
    await initializeSessions({ dataDir, engine: "codex" }, { load: async () => stored, save: async () => {} });

    const session = getSession("restored-attachment-session");
    assert.equal(session.attachments[0].path, attachmentPath);
    assert.equal(session.queuedMessages[0].attachments[0].path, attachmentPath);
    assert.equal(JSON.stringify(listSessions()).includes(attachmentPath), false);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("restores artifact paths from safe persisted metadata", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-artifact-restore-"));
  const stored = [{
    id: "restored-artifact-session",
    status: "completed",
    events: [],
    approvals: [],
    attachments: [{ id: "artifact-abc123", kind: "artifact", name: "preview.html", resourcePath: "pages/preview.html", mimeType: "text/html", size: 12 }],
    queuedMessages: []
  }];

  try {
    await initializeSessions({ dataDir, engine: "codex" }, { load: async () => stored, save: async () => {} });
    const session = getSession("restored-artifact-session");
    assert.equal(session.attachments[0].path, join(dataDir, "artifacts", session.id, "artifact-abc123", "pages", "preview.html"));
    assert.equal(JSON.stringify(listSessions()).includes(dataDir), false);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("does not start queued work when a restored attachment file is missing", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-missing-"));
  let runtimeStarted = false;
  const stored = [{
    id: "missing-attachment-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    codexThreadId: "thread-1",
    status: "interrupted",
    events: [],
    approvals: [],
    attachments: [{ id: "attachment-1", kind: "image", name: "photo.jpg", mimeType: "image/jpeg" }],
    queuedMessages: [
      { id: "queued-1", text: "Missing image", attachments: [{ id: "attachment-1" }], skills: [] },
      { id: "queued-2", text: "Keep me", attachments: [], skills: [] }
    ]
  }];
  const config = {
    dataDir,
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };

  try {
    await initializeSessions(config, { load: async () => stored, save: async () => {} }, {
      startRuntime: async () => {
        runtimeStarted = true;
        throw new Error("must not start runtime");
      }
    });
    const session = getSession("missing-attachment-session");
    session.status = "running";
    const delivery = await sendMessage(session, {
      text: "Explicit queue action after restart",
      executionAccountId: "account-a"
    });

    syncExternalThread(session, { id: "thread-1", status: { type: "idle" }, turns: [] });
    await waitFor(() => session.status === "failed");

    assert.equal(runtimeStarted, false);
    assert.equal(session.status, "failed");
    assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-1", "queued-2", delivery.queuedMessageId]);
    assert.equal(session.events.at(-2).event, "error");
    assert.equal(session.events.at(-2).data.message, "queued_attachment_unavailable");
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("expires persisted Codex approvals after backend restart", async () => {
  const stored = [{
    id: "expired-approval-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "waiting_approval",
    createdAt: "2026-06-27T00:00:00.000Z",
    updatedAt: "2026-06-27T00:01:00.000Z",
    events: [],
    approvals: [{
      id: "approval-expired",
      runtimeRequestId: 76,
      status: "pending",
      title: "Command approval required",
      createdAt: "2026-06-27T00:00:30.000Z"
    }],
    files: []
  }];
  let saved;
  const store = {
    load: async () => stored,
    save: async (sessions) => { saved = sessions; }
  };

  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);

  const session = getSession("expired-approval-session");
  const approval = session.approvals.get("approval-expired");
  assert.equal(session.status, "interrupted");
  assert.equal(approval.status, "expired");
  assert.equal(approval.title, "Approval expired after backend restart");
  assert.match(approval.resolvedAt, /^2026-|^202[7-9]-|^20[3-9][0-9]-/);
  assert.equal(saved[0].approvals.get("approval-expired").status, "expired");

  await assert.rejects(
    resolveApproval(session, "approval-expired", { decision: "approved" }),
    /approval_not_found/
  );
});

test("keeps persisted fake approvals pending after backend restart", async () => {
  const stored = [{
    id: "fake-approval-session",
    status: "waiting_approval",
    events: [],
    approvals: [{ id: "approval-fake", status: "pending" }],
    files: []
  }];
  const store = { load: async () => stored, save: async () => {} };

  await initializeSessions({ dataDir: "unused", engine: "fake" }, store);

  const session = getSession("fake-approval-session");
  assert.equal(session.status, "interrupted");
  assert.equal(session.approvals.get("approval-fake").status, "pending");
});

test("reuses a client-created session identity after response loss", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  const input = {
    clientSessionId: "12121212-1212-4121-8121-121212121212",
    accountId: "account-a",
    workspaceId: "workspace-a"
  };

  const first = await createSession(config, input);
  const replay = await createSession(config, input);

  assert.equal(first.id, input.clientSessionId);
  assert.deepEqual(replay, first);
  assert.equal(listSessions().filter((item) => item.id === input.clientSessionId).length, 1);
  await assert.rejects(
    createSession(config, { ...input, workspaceId: "different-workspace" }),
    (error) => ["idempotency_conflict", "invalid_session_context"].includes(error.message)
  );
});

test("forwards a real approval decision to the app-server runtime", async () => {
  const stored = [{
    id: "approval-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "idle",
    createdAt: "2026-06-27T00:00:00.000Z",
    updatedAt: "2026-06-27T00:01:00.000Z",
    events: [],
    approvals: [],
    files: []
  }];
  const store = { load: async () => stored, save: async () => {} };
  await initializeSessions({ dataDir: "unused" }, store);
  const session = getSession("approval-session");
  session.status = "waiting_approval";
  session.approvals.set("approval-1", {
    id: "approval-1",
    runtimeRequestId: 77,
    status: "pending"
  });
  const calls = [];
  session.runtime = {
    resolveApproval(requestId, decision) {
      calls.push({ requestId, decision });
    }
  };

  await resolveApproval(session, "approval-1", { decision: "approved" });

  assert.deepEqual(calls, [{ requestId: 77, decision: "approved" }]);
  assert.equal(session.status, "running");
});

test("replays the same approval decision without contacting runtime twice", async () => {
  const receipts = createActionReceiptStore();
  await initializeSessions({ dataDir: "unused" }, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts
  });
  const calls = [];
  const session = {
    id: "approval-replay-session",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map([["approval-replay", {
      id: "approval-replay",
      sessionId: "approval-replay-session",
      runtimeRequestId: 177,
      kind: "command",
      status: "pending"
    }]]),
    runtime: {
      resolveApproval(requestId, decision) {
        calls.push({ requestId, decision });
      }
    }
  };

  const first = await resolveApproval(session, "approval-replay", { decision: "approved" });
  const replay = await resolveApproval(session, "approval-replay", { decision: "approved" });

  assert.deepEqual(calls, [{ requestId: 177, decision: "approved" }]);
  assert.deepEqual(replay, first);
  assert.equal(replay.status, "approved");
});

test("rejects a conflicting retransmission of an approval decision", async () => {
  const receipts = createActionReceiptStore();
  await initializeSessions({ dataDir: "unused" }, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts
  });
  const session = {
    id: "approval-conflict-session",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map([["approval-conflict", {
      id: "approval-conflict",
      sessionId: "approval-conflict-session",
      runtimeRequestId: 178,
      kind: "command",
      status: "pending"
    }]]),
    runtime: { resolveApproval() {} }
  };
  await resolveApproval(session, "approval-conflict", { decision: "approved" });

  await assert.rejects(
    resolveApproval(session, "approval-conflict", { decision: "rejected" }),
    (error) => error.message === "idempotency_conflict" && error.status === 409
  );
});

test("deduplicates repeated runtime approval notifications", async () => {
  let runtimeEmit;
  const config = {
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: createActionReceiptStore(),
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return { threadId: "thread-approval", async sendMessage() {}, resolveApproval() {}, stop() {} };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "Start approval test" });
  const notification = {
    runtimeRequestId: 179,
    threadId: "thread-approval",
    turnId: "turn-approval",
    itemId: "item-approval",
    kind: "command",
    title: "Approval"
  };

  runtimeEmit("approval.requested", notification);
  runtimeEmit("approval.requested", notification);
  await waitFor(() => session.approvals.size > 0);
  await new Promise((resolve) => setTimeout(resolve, 20));

  assert.equal(session.approvals.size, 1);
  assert.equal(session.events.filter((event) => event.event === "approval.requested").length, 1);
});

test("does not resurrect a duplicate runtime approval notification after backend restart", async () => {
  const directory = await mkdtemp(join(tmpdir(), "crm-approval-restart-"));
  let firstEmit;
  let restartedEmit;
  const config = {
    dataDir: directory,
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const notification = {
    runtimeRequestId: 180,
    threadId: "thread-restart-approval",
    turnId: "turn-restart-approval",
    itemId: "item-restart-approval",
    kind: "command",
    title: "Approval"
  };
  try {
    await initializeSessions(config, { load: async () => [], save: async () => {} }, {
      actionReceiptStore: createActionReceiptStore(directory),
      startRuntime: async ({ emit }) => {
        firstEmit = emit;
        return { threadId: notification.threadId, async sendMessage() {}, resolveApproval() {}, stop() {} };
      }
    });
    const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    const firstSession = getSession(created.id);
    await sendMessage(firstSession, {
      text: "First explicit action",
      clientMessageId: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
    });
    firstEmit("approval.requested", notification);
    await waitFor(() => firstSession.approvals.size === 1);
    const storedApproval = { ...firstSession.approvals.values().next().value };
    const stored = [{
      id: firstSession.id,
      accountId: "account-a",
      workspaceId: "workspace-a",
      codexThreadId: notification.threadId,
      status: "waiting_approval",
      createdAt: firstSession.createdAt,
      updatedAt: firstSession.updatedAt,
      events: [],
      approvals: [storedApproval],
      attachments: [],
      queuedMessages: [],
      files: []
    }];

    await initializeSessions(config, { load: async () => stored, save: async () => {} }, {
      actionReceiptStore: createActionReceiptStore(directory),
      startRuntime: async ({ emit }) => {
        restartedEmit = emit;
        return { threadId: notification.threadId, async sendMessage() {}, resolveApproval() {}, stop() {} };
      }
    });
    const restartedSession = getSession(firstSession.id);
    assert.equal(restartedSession.approvals.get(storedApproval.id).status, "expired");
    await sendMessage(restartedSession, {
      text: "Second explicit action",
      clientMessageId: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
    });
    restartedEmit("approval.requested", notification);
    await new Promise((resolve) => setTimeout(resolve, 30));

    assert.equal(restartedSession.approvals.size, 1);
    assert.equal([...restartedSession.approvals.values()].filter((item) => item.status === "pending").length, 0);
    assert.equal(restartedSession.events.filter((event) => event.event === "approval.requested").length, 0);
  } finally {
    await rm(directory, { recursive: true, force: true });
  }
});

test("fails closed before contacting runtime when approval decision storage fails", async () => {
  const receipts = createActionReceiptStore();
  receipts.reserveApprovalDecision = async () => {
    const error = new Error("idempotency_store_unavailable");
    error.status = 503;
    error.safeLogCode = "idempotency_store_unavailable";
    throw error;
  };
  await initializeSessions({ dataDir: "unused" }, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts
  });
  let runtimeCalls = 0;
  const session = {
    id: "approval-store-failure-session",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map([["approval-store-failure", {
      id: "approval-store-failure",
      sessionId: "approval-store-failure-session",
      runtimeRequestId: 181,
      kind: "command",
      status: "pending"
    }]]),
    runtime: { resolveApproval() { runtimeCalls += 1; } }
  };

  await assert.rejects(
    resolveApproval(session, "approval-store-failure", { decision: "approved" }),
    /idempotency_store_unavailable/
  );
  assert.equal(runtimeCalls, 0);
  assert.equal(session.approvals.get("approval-store-failure").status, "pending");
});

test("replays a completed approval after backend restart without contacting runtime", async () => {
  const directory = await mkdtemp(join(tmpdir(), "crm-approval-decision-restart-"));
  const storedApproval = {
    id: "approval-decision-restart",
    sessionId: "approval-decision-restart-session",
    runtimeRequestId: 182,
    kind: "command",
    status: "pending",
    createdAt: "2026-07-17T00:00:00.000Z"
  };
  try {
    const firstReceipts = createActionReceiptStore(directory);
    await initializeSessions({ dataDir: directory }, { load: async () => [], save: async () => {} }, {
      actionReceiptStore: firstReceipts
    });
    let runtimeCalls = 0;
    const firstSession = {
      id: "approval-decision-restart-session",
      status: "waiting_approval",
      events: [],
      clients: new Set(),
      approvals: new Map([[storedApproval.id, { ...storedApproval }]]),
      runtime: { resolveApproval() { runtimeCalls += 1; } }
    };
    await resolveApproval(firstSession, storedApproval.id, { decision: "approved" });

    const stored = [{
      id: firstSession.id,
      accountId: "account-a",
      status: "waiting_approval",
      createdAt: "2026-07-17T00:00:00.000Z",
      updatedAt: "2026-07-17T00:00:01.000Z",
      events: [],
      approvals: [{ ...storedApproval }],
      attachments: [],
      queuedMessages: [],
      files: []
    }];
    await initializeSessions({ dataDir: directory, engine: "codex" }, {
      load: async () => stored,
      save: async () => {}
    }, {
      actionReceiptStore: createActionReceiptStore(directory)
    });
    const restartedSession = getSession(firstSession.id);
    assert.equal(restartedSession.approvals.get(storedApproval.id).status, "expired");

    const replay = await resolveApproval(restartedSession, storedApproval.id, { decision: "approved" });

    assert.equal(runtimeCalls, 1);
    assert.deepEqual(replay, { approvalId: storedApproval.id, status: "approved" });
    assert.equal(restartedSession.approvals.get(storedApproval.id).status, "approved");
  } finally {
    await rm(directory, { recursive: true, force: true });
  }
});

test("deduplicates the same fake-engine approval decision", async () => {
  await initializeSessions({ dataDir: "unused", engine: "fake" }, {
    load: async () => [],
    save: async () => {}
  }, {
    actionReceiptStore: createActionReceiptStore()
  });
  const approval = {
    id: "fake-approval-replay",
    sessionId: "fake-approval-replay-session",
    kind: "generic",
    status: "pending"
  };
  const session = {
    id: "fake-approval-replay-session",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map([[approval.id, approval]]),
    runtime: null
  };

  await resolveApproval(session, approval.id, { decision: "approved" });
  await resolveApproval(session, approval.id, { decision: "approved" });

  assert.equal(session.status, "completed");
  assert.equal(session.events.filter((event) =>
    event.event === "session.output" && event.data.text === "Fake Codex turn completed.").length, 1);
});

test("keeps proposed permanent approval rules private while forwarding the selected scope", async () => {
  let runtimeEmit;
  const calls = [];
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      runtimeEmit = emit;
      return {
        threadId: "thread-1",
        async sendMessage() {},
        resolveApproval(requestId, decision, approval) { calls.push({ requestId, decision, approval }); },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "Run tests" });
  await runtimeEmit("approval.requested", {
    runtimeRequestId: 81,
    kind: "command",
    title: "Approval",
    commandPreview: "npm test",
    proposedExecpolicyAmendment: ["npm", "test", "C:\\Private"]
  });

  const projected = listApprovals(session).approvals[0];
  assert.equal(projected.canApproveForSession, true);
  assert.equal(projected.canApproveAlways, true);
  assert.equal(JSON.stringify(projected).includes("Private"), false);

  await resolveApproval(session, projected.id, { decision: "approved_always" });
  assert.equal(calls[0].requestId, 81);
  assert.equal(calls[0].decision, "approved_always");
  assert.deepEqual(calls[0].approval.proposedExecpolicyAmendment, ["npm", "test", "C:\\Private"]);
});

test("rejects an unavailable permanent approval scope before contacting runtime", async () => {
  const session = {
    approvals: new Map([["approval-no-rule", {
      id: "approval-no-rule",
      runtimeRequestId: 82,
      kind: "command",
      status: "pending"
    }]]),
    runtime: { resolveApproval() { throw new Error("runtime_should_not_be_called"); } }
  };

  await assert.rejects(
    resolveApproval(session, "approval-no-rule", { decision: "approved_always" }),
    /approval_scope_unavailable/
  );
});

test("serializes concurrent real approval decisions before they reach runtime", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused" }, store);
  const releaseRuntime = [];
  const calls = [];
  const session = {
    id: "concurrent-approval-session",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map([["approval-concurrent", {
      id: "approval-concurrent",
      runtimeRequestId: 79,
      status: "pending"
    }]]),
    runtime: {
      resolveApproval(requestId, decision) {
        calls.push({ requestId, decision });
        return new Promise((resolve) => { releaseRuntime.push(resolve); });
      }
    }
  };

  const first = resolveApproval(session, "approval-concurrent", { decision: "approved" });
  await waitFor(() => calls.length === 1);
  const second = resolveApproval(session, "approval-concurrent", { decision: "rejected" });
  try {
    const secondResult = await Promise.race([
      second.then(() => "resolved", (error) => error.message),
      new Promise((resolve) => setTimeout(() => resolve("pending"), 10))
    ]);
    assert.equal(secondResult, "idempotency_conflict");
  } finally {
    for (const release of releaseRuntime) release();
    await Promise.allSettled([first, second]);
  }

  assert.deepEqual(calls, [{ requestId: 79, decision: "approved" }]);
  assert.equal(session.approvals.get("approval-concurrent").status, "approved");
});

test("keeps approval pending when app-server response fails", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused" }, store);
  const session = {
    id: "failed-approval-session",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map([["approval-2", {
      id: "approval-2",
      runtimeRequestId: 78,
      status: "pending"
    }]]),
    runtime: {
      resolveApproval() {
        throw new Error("runtime_closed");
      }
    }
  };

  await assert.rejects(
    resolveApproval(session, "approval-2", { decision: "approved" }),
    /runtime_closed/
  );

  assert.equal(session.approvals.get("approval-2").status, "pending");
  assert.equal(session.status, "waiting_approval");
});

test("queues multiple messages during a running Codex turn", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, store);
  const calls = [];
  const session = {
    id: "running-session",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    runtime: {
      async steerMessage(payload) { calls.push(payload); return "turn-1"; }
    }
  };

  const firstDelivery = await sendMessage(session, { text: "First later" });
  const secondDelivery = await sendMessage(session, { text: "Second later" });

  assert.deepEqual(calls, []);
  assert.equal(session.status, "running");
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["First later", "Second later"]);
  assert.deepEqual(publicSession(session).queuedMessages.map((item) => item.text), ["First later", "Second later"]);
  assert.deepEqual(firstDelivery, { disposition: "queued", queuedMessageId: session.queuedMessages[0].id });
  assert.deepEqual(secondDelivery, { disposition: "queued", queuedMessageId: session.queuedMessages[1].id });
});

test("replays an accepted message without starting a second runtime turn after response loss", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  let sends = 0;
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: createTestActionReceiptStore(),
    startRuntime: async () => ({
      threadId: "thread-1",
      async sendMessage() { sends += 1; return "turn-1"; },
      stop() {}
    })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  const input = {
    clientMessageId: "66666666-6666-4666-8666-666666666666",
    text: "Run once",
    executionAccountId: "account-a"
  };

  await sendMessage(session, input); // The HTTP response is intentionally treated as lost.
  const replay = await sendMessage(session, input);

  assert.equal(sends, 1);
  assert.deepEqual(replay, { disposition: "started" });
  assert.equal(session.events.filter((event) => event.event === "message.accepted").length, 1);
});

test("rejects a reused client message id with a different message payload", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: createTestActionReceiptStore(),
    startRuntime: async () => ({ threadId: "thread-1", async sendMessage() {}, stop() {} })
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  const clientMessageId = "77777777-7777-4777-8777-777777777777";
  await sendMessage(session, { clientMessageId, text: "First", executionAccountId: "account-a" });

  await assert.rejects(
    sendMessage(session, { clientMessageId, text: "Different", executionAccountId: "account-a" }),
    (error) => error.message === "idempotency_conflict" && error.status === 409
  );
});

test("does not insert the same client message into a running queue twice", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: createTestActionReceiptStore()
  });
  const session = {
    id: "queue-idempotency-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: { async sendMessage() {} }
  };
  const input = {
    clientMessageId: "88888888-8888-4888-8888-888888888888",
    text: "Queue once",
    executionAccountId: "account-a"
  };

  const first = await sendMessage(session, input);
  const replay = await sendMessage(session, input);

  assert.deepEqual(replay, first);
  assert.equal(session.queuedMessages.length, 1);
  assert.equal(session.queuedMessages[0].clientMessageId, input.clientMessageId);
});

test("restores a message receipt after backend restart and does not execute it again", async () => {
  const directory = await mkdtemp(join(tmpdir(), "crm-message-restart-"));
  const config = {
    dataDir: directory,
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: directory, label: "Project" }]
  };
  const clientMessageId = "99999999-9999-4999-8999-999999999999";
  let sends = 0;
  try {
    const actionReceiptStore = createTestActionReceiptStore();
    await initializeSessions(config, createSessionStore(directory), {
      actionReceiptStore,
      startRuntime: async () => ({
        threadId: "thread-1",
        async sendMessage() { sends += 1; return "turn-1"; },
        stop() {}
      })
    });
    const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    await sendMessage(getSession(created.id), { clientMessageId, text: "Run once", executionAccountId: "account-a" });

    await initializeSessions(config, createSessionStore(directory), {
      actionReceiptStore,
      startRuntime: async () => { throw new Error("duplicate_runtime_start"); }
    });
    const replay = await sendMessage(getSession(created.id), {
      clientMessageId,
      text: "Run once",
      executionAccountId: "account-a"
    });

    assert.equal(sends, 1);
    assert.deepEqual(replay, { disposition: "started" });
  } finally {
    await rm(directory, { recursive: true, force: true });
  }
});

test("replays an account handoff message without creating a second account-owned thread", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [
      { id: "account-a", codexHome: "A_HOME" },
      { id: "account-b", codexHome: "B_HOME" }
    ],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const starts = [];
  let sends = 0;
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: createTestActionReceiptStore(),
    startRuntime: async ({ accountId }) => {
      starts.push(accountId);
      return {
        threadId: `thread-${accountId}`,
        async sendMessage() { sends += 1; return "turn-1"; },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  session.codexThreadId = "thread-account-a";
  const input = {
    clientMessageId: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
    text: "Continue on B",
    executionAccountId: "account-b"
  };

  await sendMessage(session, input);
  const replay = await sendMessage(session, input);

  assert.deepEqual(replay, { disposition: "started" });
  assert.deepEqual(starts, ["account-b"]);
  assert.equal(sends, 1);
  assert.equal(session.threadRefs.filter((ref) => ref.accountId === "account-b").length, 1);
});

test("fails closed before runtime or queue mutation when the receipt store cannot reserve a message", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  let starts = 0;
  const failure = Object.assign(new Error("idempotency_store_unavailable"), {
    status: 503,
    safeLogCode: "idempotency_store_unavailable"
  });
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: {
      async initialize() {},
      async findMessage() { return null; },
      async reserveMessage() { throw failure; }
    },
    startRuntime: async () => { starts += 1; return { threadId: "thread-1", async sendMessage() {} }; }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await assert.rejects(
    sendMessage(session, {
      clientMessageId: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
      text: "Must not run",
      executionAccountId: "account-a"
    }),
    /idempotency_store_unavailable/
  );
  assert.equal(starts, 0);
  assert.deepEqual(session.queuedMessages, []);
  assert.equal(session.events.some((event) => event.event === "message.accepted"), false);
});

test("serializes concurrent sends so only the first starts runtime work", async () => {
  let releaseRuntimeStart;
  const runtimeStarted = new Promise((resolve) => { releaseRuntimeStart = resolve; });
  const sent = [];
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async () => {
      await runtimeStarted;
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push(payload.text);
          return `turn-${sent.length}`;
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  const first = sendMessage(session, { text: "First", executionAccountId: "account-a" });
  const second = sendMessage(session, { text: "Second", executionAccountId: "account-a" });
  releaseRuntimeStart();
  const deliveries = await Promise.all([first, second]);

  assert.deepEqual(sent, ["First"]);
  assert.deepEqual(deliveries.map((item) => item.disposition), ["started", "queued"]);
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Second"]);
});

test("starts an attachment-only message accepted by the composer", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, store);
  const calls = [];
  const attachment = { id: "image-1", kind: "image", path: "C:\\Private\\image.png" };
  const session = {
    id: "attachment-only-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "idle",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    attachments: [attachment],
    queuedMessages: [],
    runtime: {
      async sendMessage(payload) { calls.push(payload); return "turn-1"; }
    }
  };

  const delivery = await sendMessage(session, {
    text: "",
    executionAccountId: "account-a",
    attachments: [{ id: "image-1" }]
  });

  assert.equal(delivery.disposition, "started");
  assert.equal(calls.length, 1);
  assert.equal(calls[0].text, "");
  assert.deepEqual(calls[0].attachments, [attachment]);
});

test("explicitly steers a selected queued message", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, store);
  const steeringCalls = [];
  const session = {
    id: "explicit-steer-session",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [
      { id: "queued-1", text: "Later", attachments: [], skills: [], createdAt: new Date().toISOString() },
      { id: "queued-2", text: "Do this now", attachments: [], skills: [], createdAt: new Date().toISOString() }
    ],
    runtime: {
      async steerMessage(payload) { steeringCalls.push(payload); return "turn-1"; }
    }
  };

  await steerQueuedMessage(session, "queued-2");

  assert.deepEqual(steeringCalls, [{ text: "Do this now" }]);
  assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-1"]);
  assert.equal(session.events.at(-1).data.steering, true);
  assert.equal(session.events.at(-1).data.queuedMessageId, "queued-2");
});

test("keeps queued rich input instead of silently dropping it during steering", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, store);
  const steeringCalls = [];
  const session = {
    id: "rich-steer-session",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{
      id: "queued-1",
      text: "Use this image",
      options: {},
      attachments: [{ id: "image-1", kind: "image", path: "C:\\Private\\image.png" }],
      skills: [],
      createdAt: new Date().toISOString()
    }],
    runtime: { async steerMessage(payload) { steeringCalls.push(payload); } }
  };

  await assert.rejects(steerQueuedMessage(session, "queued-1"), /steering_rich_input_unsupported/);
  assert.deepEqual(steeringCalls, []);
  assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-1"]);
});

test("rejects messages while an approval is pending", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);
  const session = {
    id: "approval-message-session",
    workspaceId: "workspace-a",
    status: "waiting_approval",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    runtime: { async steerMessage() {} }
  };

  await assert.rejects(sendMessage(session, { text: "Continue" }), /session_busy/);
});

test("keeps a queued message when explicit steering fails", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, store);
  const session = {
    id: "failed-steering-session",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-1", text: "Redirect", attachments: [], skills: [], createdAt: new Date().toISOString() }],
    runtime: {
      async steerMessage() { throw new Error("private app-server details"); }
    }
  };

  await assert.rejects(steerQueuedMessage(session, "queued-1"), /steering_failed/);
  assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-1"]);
  assert.equal(session.events.length, 0);
});

test("deletes only the selected queued message", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir: "unused", engine: "codex" }, store);
  const session = {
    id: "delete-queued-session",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [
      { id: "queued-1", text: "First", attachments: [], skills: [], createdAt: new Date().toISOString() },
      { id: "queued-2", text: "Second", attachments: [], skills: [], createdAt: new Date().toISOString() }
    ]
  };

  await deleteQueuedMessage(session, "queued-1");
  assert.deepEqual(session.queuedMessages.map((item) => item.id), ["queued-2"]);
  await assert.rejects(deleteQueuedMessage(session, "missing"), /queued_message_not_found/);
});

test("replays explicit queue steering without contacting runtime twice", async () => {
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: createActionReceiptStore()
  });
  let steeringCalls = 0;
  const session = {
    id: "queue-steer-replay-session",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-replay", text: "Run once", attachments: [], skills: [] }],
    runtime: {
      async steerMessage() { steeringCalls += 1; return "turn-replay"; }
    }
  };

  const first = await steerQueuedMessage(session, "queued-replay");
  const replay = await steerQueuedMessage(session, "queued-replay");

  assert.equal(steeringCalls, 1);
  assert.deepEqual(replay, first);
  assert.deepEqual(replay, { queuedMessageId: "queued-replay", action: "steer" });
});

test("replays queue deletion and rejects a conflicting later steer", async () => {
  await initializeSessions({ dataDir: "unused", engine: "codex" }, {
    load: async () => [],
    save: async () => {}
  }, {
    actionReceiptStore: createActionReceiptStore()
  });
  const session = {
    id: "queue-delete-replay-session",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-delete-replay", text: "Delete once", attachments: [], skills: [] }]
  };

  const first = await deleteQueuedMessage(session, "queued-delete-replay");
  const replay = await deleteQueuedMessage(session, "queued-delete-replay");

  assert.deepEqual(replay, first);
  assert.deepEqual(replay, { queuedMessageId: "queued-delete-replay", action: "delete" });
  await assert.rejects(
    steerQueuedMessage(session, "queued-delete-replay"),
    (error) => error.message === "idempotency_conflict" && error.status === 409
  );
});

test("fails closed before queue steering when its receipt cannot be stored", async () => {
  const receipts = createActionReceiptStore();
  receipts.reserveQueuedAction = async () => {
    const error = new Error("idempotency_store_unavailable");
    error.status = 503;
    throw error;
  };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, { load: async () => [], save: async () => {} }, { actionReceiptStore: receipts });
  let steeringCalls = 0;
  const session = {
    id: "queue-store-failure-session",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [{ id: "queued-store-failure", text: "Do not run", attachments: [], skills: [] }],
    runtime: { async steerMessage() { steeringCalls += 1; } }
  };

  await assert.rejects(
    steerQueuedMessage(session, "queued-store-failure"),
    /idempotency_store_unavailable/
  );
  assert.equal(steeringCalls, 0);
  assert.equal(session.queuedMessages.length, 1);
});

test("advances queued messages one at a time after turn completion", async () => {
  const store = { load: async () => [], save: async () => {} };
  const sent = [];
  const emitters = [];
  let runtimeNumber = 0;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeNumber += 1;
      emitters.push(emit);
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push({ runtimeNumber, text: payload.text });
          return "turn-" + sent.length;
        },
        stop() {}
      };
    }
  });
  const session = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const live = getSession(session.id);

  await sendMessage(live, { text: "Active" });
  await sendMessage(live, { text: "First queued" });
  await sendMessage(live, { text: "Second queued" });
  emitters[0]("turn.completed", { turnId: "turn-1", status: "completed" });
  await new Promise((resolve) => setImmediate(resolve));
  await new Promise((resolve) => setImmediate(resolve));

  assert.deepEqual(sent, [
    { runtimeNumber: 1, text: "Active" },
    { runtimeNumber: 1, text: "First queued" }
  ]);
  assert.deepEqual(live.queuedMessages.map((item) => item.text), ["Second queued"]);
  assert.equal(live.status, "running");
  assert.equal(
    live.events.find((event) => event.event === "message.accepted" && event.data?.queued && event.data?.turnId)?.data.turnId,
    "turn-2"
  );
});

test("a successful queued deletion prevents an automatic dispatch still preparing", async () => {
  const store = { load: async () => [], save: async () => {} };
  const receipts = createActionReceiptStore();
  const getMessage = receipts.getMessage;
  let queuedReceiptRead = false;
  let releaseQueuedReceiptRead;
  let queuedClientMessageId;
  receipts.getMessage = async (input) => {
    if (input.clientMessageId === queuedClientMessageId) {
      queuedReceiptRead = true;
      await new Promise((resolve) => { releaseQueuedReceiptRead = resolve; });
    }
    return getMessage(input);
  };
  const sent = [];
  let emitRuntime;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    actionReceiptStore: receipts,
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push(payload.text);
          return `turn-${sent.length}`;
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Active", clientMessageId: "00000000-0000-4000-8000-000000000201" });
  queuedClientMessageId = "00000000-0000-4000-8000-000000000202";
  const queued = await sendMessage(session, { text: "Delete me", clientMessageId: queuedClientMessageId });
  await sendMessage(session, {
    text: "Keep me",
    clientMessageId: "00000000-0000-4000-8000-000000000205"
  });
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => queuedReceiptRead);

  await deleteQueuedMessage(session, queued.queuedMessageId);
  releaseQueuedReceiptRead();
  await waitFor(() => sent.length === 2);

  assert.deepEqual(sent, ["Active", "Keep me"]);
  assert.deepEqual(session.queuedMessages, []);
});

test("queued deletion reports a conflict after automatic dispatch owns the message", async () => {
  const store = { load: async () => [], save: async () => {} };
  const sent = [];
  let emitRuntime;
  let queuedDispatchStarted = false;
  let releaseQueuedDispatch;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    actionReceiptStore: createActionReceiptStore(),
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push(payload.text);
          if (sent.length === 2) {
            queuedDispatchStarted = true;
            await new Promise((resolve) => { releaseQueuedDispatch = resolve; });
          }
          return `turn-${sent.length}`;
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Active", clientMessageId: "00000000-0000-4000-8000-000000000203" });
  const queued = await sendMessage(session, {
    text: "Already dispatching",
    clientMessageId: "00000000-0000-4000-8000-000000000204"
  });
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => queuedDispatchStarted);

  await assert.rejects(
    deleteQueuedMessage(session, queued.queuedMessageId),
    (error) => error.message === "queued_message_dispatching" && error.status === 409
  );
  releaseQueuedDispatch();
  await waitFor(() => session.queueAdvancing === false);

  assert.deepEqual(sent, ["Active", "Already dispatching"]);
});

test("stopping a queued turn start keeps that message waiting and interrupted", async () => {
  let emitRuntime;
  let resolveQueuedStart;
  let sendCount = 0;
  let startCanceled = false;
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage() {
          sendCount += 1;
          if (sendCount === 1) return "turn-1";
          await new Promise((resolve) => { resolveQueuedStart = resolve; });
          if (startCanceled) throw new Error("turn_start_canceled");
          return "turn-2";
        },
        cancelPendingStart() { startCanceled = true; return true; },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Active", executionAccountId: "account-a" });
  await sendMessage(session, { text: "Wait", executionAccountId: "account-a" });
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => typeof resolveQueuedStart === "function");

  assert.equal(session.status, "starting");
  const stopping = stopSession(session);
  resolveQueuedStart();
  await stopping;
  await waitFor(() => session.queueAdvancing === false);

  assert.equal(session.status, "interrupted");
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Wait"]);
  assert.equal(session.events.some((event) => event.event === "error"), false);
});

test("restores a queued receipt when its turn start fails", async () => {
  let emitRuntime;
  let sendCount = 0;
  const receipts = createTestActionReceiptStore();
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts,
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage() {
          sendCount += 1;
          if (sendCount === 1) return "turn-1";
          throw new Error("turn/start timeout");
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  const queuedClientMessageId = "00000000-0000-4000-8000-000000000103";

  await sendMessage(session, { text: "Active", executionAccountId: "account-a" });
  const queued = await sendMessage(session, { text: "Wait", executionAccountId: "account-a", clientMessageId: queuedClientMessageId });
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => session.queueAdvancing === false && session.status === "failed");

  assert.equal((await receipts.getMessage({ sessionId: session.id, clientMessageId: queuedClientMessageId })).state, "accepted");
  assert.deepEqual(
    await sendMessage(session, { text: "Wait", executionAccountId: "account-a", clientMessageId: queuedClientMessageId }),
    queued
  );
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Wait"]);
});

test("blocks queue advancement and reports a safe error when receipt restoration fails", async () => {
  let emitRuntime;
  let sendCount = 0;
  let failRestore = false;
  const baseReceipts = createTestActionReceiptStore();
  const receipts = {
    ...baseReceipts,
    async updateMessage(input) {
      if (failRestore && input.state === "accepted") throw new Error("private receipt storage failure");
      return baseReceipts.updateMessage(input);
    }
  };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts,
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage() {
          sendCount += 1;
          if (sendCount === 1) return "turn-1";
          throw new Error("skill resolution failed");
        },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);

  await sendMessage(session, { text: "Active", executionAccountId: "account-a" });
  await sendMessage(session, {
    text: "Wait",
    executionAccountId: "account-a",
    clientMessageId: "00000000-0000-4000-8000-000000000104"
  });
  failRestore = true;
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => session.queueAdvancing === false);

  assert.equal(session.queueAdvanceBlocked, true);
  assert.equal(session.events.findLast((event) => event.event === "error")?.data.message, "idempotency_store_unavailable");
});

test("marks a queued delivery indeterminate and interrupts its created turn when dispatch persistence fails", async () => {
  let emitRuntime;
  let sendCount = 0;
  let interrupted = false;
  const baseReceipts = createTestActionReceiptStore();
  const receipts = {
    ...baseReceipts,
    async updateMessage(input) {
      if (input.state === "dispatched") throw new Error("private dispatched receipt failure");
      return baseReceipts.updateMessage(input);
    }
  };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts,
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage() { sendCount += 1; return `turn-${sendCount}`; },
        async interrupt() { interrupted = true; },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  const clientMessageId = "00000000-0000-4000-8000-000000000105";

  await sendMessage(session, { text: "Active", executionAccountId: "account-a" });
  await sendMessage(session, { text: "Wait", executionAccountId: "account-a", clientMessageId });
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => session.queueAdvancing === false && session.status === "failed");

  assert.equal(interrupted, true);
  assert.equal(session.queueAdvanceBlocked, true);
  assert.equal((await receipts.getMessage({ sessionId: session.id, clientMessageId })).state, "indeterminate");
  assert.deepEqual(session.queuedMessages.map((item) => item.text), ["Wait"]);
  await assert.rejects(
    sendMessage(session, { text: "Wait", executionAccountId: "account-a", clientMessageId }),
    /message_delivery_indeterminate/
  );
});

test("preserves a queued turn terminal state when dispatch persistence fails after completion", async () => {
  let emitRuntime;
  let sendCount = 0;
  const baseReceipts = createTestActionReceiptStore();
  const receipts = {
    ...baseReceipts,
    async updateMessage(input) {
      if (input.state === "dispatched") {
        emitRuntime("turn.completed", { turnId: "turn-2", status: "completed" });
        throw new Error("private dispatched receipt failure");
      }
      return baseReceipts.updateMessage(input);
    }
  };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    actionReceiptStore: receipts,
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return {
        threadId: "thread-1",
        async sendMessage() { sendCount += 1; return `turn-${sendCount}`; },
        async interrupt() { throw new Error("active_turn_unavailable"); },
        stop() {}
      };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  const clientMessageId = "00000000-0000-4000-8000-000000000106";

  await sendMessage(session, { text: "Active", executionAccountId: "account-a" });
  await sendMessage(session, { text: "Wait", executionAccountId: "account-a", clientMessageId });
  emitRuntime("turn.completed", { turnId: "turn-1", status: "completed" });
  await waitFor(() => session.queueAdvancing === false);

  assert.equal(session.status, "completed");
  assert.equal(session.queueAdvanceBlocked, true);
  assert.equal((await receipts.getMessage({ sessionId: session.id, clientMessageId })).state, "indeterminate");
});

test("does not advance queued messages after an interrupted runtime turn", async () => {
  const store = { load: async () => [], save: async () => {} };
  const sent = [];
  const emitters = [];
  let runtimeNumber = 0;
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, store, {
    startRuntime: async ({ emit }) => {
      runtimeNumber += 1;
      emitters.push(emit);
      return {
        threadId: "thread-1",
        async sendMessage(payload) {
          sent.push({ runtimeNumber, text: payload.text });
          return `turn-${runtimeNumber}`;
        },
        stop() {}
      };
    }
  });
  const session = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const live = getSession(session.id);

  await sendMessage(live, { text: "Active" });
  await sendMessage(live, { text: "Queued after stop" });
  emitters[0]("turn.completed", { turnId: "turn-1", status: "interrupted" });
  await new Promise((resolve) => setImmediate(resolve));
  await new Promise((resolve) => setImmediate(resolve));

  assert.deepEqual(sent, [{ runtimeNumber: 1, text: "Active" }]);
  assert.deepEqual(live.queuedMessages.map((item) => item.text), ["Queued after stop"]);
  assert.equal(live.status, "interrupted");
});

test("queued messages are persisted as visible conversation events", async () => {
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({
    dataDir: "unused",
    engine: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  }, store);
  const session = {
    id: "queued-visible-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "running",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: {}
  };

  const clientMessageId = "cccccccc-cccc-4ccc-8ccc-cccccccccccc";
  const delivery = await sendMessage(session, { clientMessageId, text: "Keep my bubble" });

  assert.equal(delivery.disposition, "queued");
  assert.equal(session.queuedMessages.length, 1);
  assert.deepEqual(
    session.events.filter((event) => event.event === "message.accepted").map((event) => event.data),
    [{
      sessionId: "queued-visible-session",
      clientMessageId,
      queuedMessageId: session.queuedMessages[0].id,
      text: "Keep my bubble",
      queued: true
    }]
  );
});

test("stopped sessions ignore external active snapshots after runtime release", () => {
  const session = {
    id: "stopped-external-session",
    accountId: "account-a",
    codexThreadId: "thread-1",
    status: "interrupted",
    events: [
      { id: "accepted", event: "message.accepted", data: { text: "Stop this" }, createdAt: "2026-06-30T00:00:00.000Z" },
      { id: "local-stop", event: "turn.completed", data: { turnId: "turn-1", status: "interrupted" }, createdAt: "2026-06-30T00:00:01.000Z" }
    ],
    clients: new Set(),
    approvals: new Map(),
    queuedMessages: [],
    runtime: null
  };
  const changed = syncExternalThread(session, {
    id: "thread-1",
    status: { type: "active" },
    updatedAt: 2000,
    turns: [
      {
        id: "turn-1",
        status: "active",
        startedAt: 1000,
        prompt: [{ type: "text", text: "Stop this" }]
      }
    ]
  });

  assert.equal(changed, false);
  assert.equal(session.status, "interrupted");
  assert.equal(session.events.at(-1).data.status, "interrupted");
});
test("sends structured composer payload to runtime", async () => {
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const calls = [];
  const capabilityRequests = [];
  await initializeSessions(config, store, {
    requestCodexAppServer: async ({ method, params }) => {
      capabilityRequests.push({ method, params });
      return {
        data: [{
          skills: [
            { name: "imagegen", path: "C:\\Skills\\imagegen", enabled: true },
            { name: "review", path: "C:\\Skills\\review", enabled: false }
          ]
        }]
      };
    }
  });
  const session = {
    id: "composer-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "idle",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    attachments: [{ id: "att-1", kind: "image", name: "photo.jpg", mimeType: "image/jpeg", path: "C:\\Data\\img.png" }],
    runtime: {
      async sendMessage(payload) {
        calls.push(payload);
      }
    }
  };

  await sendMessage(session, {
    text: "Look",
    options: { model: "gpt-5-codex", permissionProfile: ":danger-full-access", planMode: true },
    attachments: [{ id: "att-1" }],
    skills: [
      { name: "imagegen", path: "C:\\Browser\\must-not-be-trusted" },
      { name: "review" },
      { name: "missing", path: "C:\\Browser\\missing" }
    ]
  });

  assert.deepEqual(calls, [{
    text: "Look",
    options: { model: "gpt-5-codex", permissionProfile: ":danger-full-access", planMode: true },
    attachments: [{ id: "att-1", kind: "image", name: "photo.jpg", mimeType: "image/jpeg", path: "C:\\Data\\img.png" }],
    skills: [{ name: "imagegen", path: "C:\\Skills\\imagegen" }]
  }]);
  assert.deepEqual(capabilityRequests, [{
    method: "skills/list",
    params: { cwds: ["C:\\Project"] }
  }]);
  assert.equal(session.events.find((event) => event.event === "message.accepted").data.attachmentCount, 1);
});

test("rejects browser permissions above the PC ceiling before starting a turn", async () => {
  const config = {
    dataDir: "unused",
    engine: "codex",
    codexCliPath: "codex",
    permissionCeiling: { permissionProfile: ":workspace", approvalPolicy: "on-request" },
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} });
  let sends = 0;
  const session = {
    id: "ceiling-session",
    accountId: "account-a",
    workspaceId: "workspace-a",
    status: "idle",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    runtime: { async sendMessage() { sends += 1; } }
  };

  await assert.rejects(
    sendMessage(session, {
      text: "Run without limits",
      options: { permissionProfile: ":danger-full-access", approvalPolicy: "never" }
    }),
    (error) => error.message === "permission_ceiling_exceeded" && error.status === 400
  );
  assert.equal(sends, 0);
  assert.equal(session.events.length, 0);
});

test("detects attachment type from magic bytes instead of browser metadata", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  const store = { load: async () => [], save: async () => {} };
  await initializeSessions({ dataDir, engine: "fake" }, store);
  const session = {
    id: "attachment-session",
    workspaceId: "workspace-a",
    status: "idle",
    events: [],
    clients: new Set(),
    approvals: new Map()
  };

  try {
    const attachment = await createAttachment(session, {
      name: "photo\r\n\u202e.JPG",
      mimeType: "image/jpeg",
      dataBase64: PNG_IMAGE.toString("base64")
    });

    assert.equal(attachment.kind, "image");
    assert.equal(attachment.name, "photo_.png");
    assert.equal(attachment.mimeType, "image/png");
    assert.equal(attachment.size, PNG_IMAGE.length);
    assert.equal(attachment.path.startsWith(join(dataDir, "attachments", "attachment-session")), true);
    assert.equal(attachment.path.endsWith(".png"), true);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("accepts only PNG, JPEG, GIF, and WebP attachment magic bytes", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  await initializeSessions({ dataDir, engine: "fake" }, { load: async () => [], save: async () => {} });
  const session = { id: "attachment-types", status: "idle", events: [], clients: new Set(), approvals: new Map() };
  const cases = [
    [PNG_IMAGE, "image/png", ".png"],
    [JPEG_IMAGE, "image/jpeg", ".jpg"],
    [GIF_IMAGE, "image/gif", ".gif"],
    [WEBP_IMAGE, "image/webp", ".webp"]
  ];

  try {
    for (const [data, mimeType, extension] of cases) {
      const attachment = await createAttachment(session, {
        name: "untrusted.txt",
        mimeType: "text/plain",
        dataBase64: data.toString("base64")
      });
      assert.equal(attachment.mimeType, mimeType);
      assert.equal(attachment.path.endsWith(extension), true);
      assert.equal(attachment.name.endsWith(extension), true);
    }
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("rejects spoofed image metadata, malformed base64, and oversized images", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  await initializeSessions({ dataDir, engine: "fake" }, { load: async () => [], save: async () => {} });
  const session = { id: "attachment-rejections", status: "idle", events: [], clients: new Set(), approvals: new Map() };

  try {
    await assert.rejects(
      createAttachment(session, { name: "fake.png", mimeType: "image/png", dataBase64: Buffer.from("<html>").toString("base64") }),
      (error) => error.message === "unsupported_attachment_type" && error.status === 400
    );
    await assert.rejects(
      createAttachment(session, { name: "photo.png", mimeType: "image/png", dataBase64: "not base64!" }),
      (error) => error.message === "invalid_attachment_data" && error.status === 400
    );
    const oversized = Buffer.alloc((8 * 1024 * 1024) + 1);
    PNG_IMAGE.copy(oversized);
    await assert.rejects(
      createAttachment(session, { name: "large.png", mimeType: "image/png", dataBase64: oversized.toString("base64") }),
      (error) => error.message === "attachment_too_large" && error.status === 413
    );
    assert.deepEqual(session.attachments ?? [], []);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("rejects messages above the attachment count or total byte limits", async () => {
  await initializeSessions({ engine: "fake" }, { load: async () => [], save: async () => {} });
  const session = {
    id: "attachment-message-limits",
    status: "idle",
    events: [],
    clients: new Set(),
    approvals: new Map(),
    attachments: Array.from({ length: 9 }, (_, index) => ({
      id: `attachment-${index}`,
      kind: "image",
      name: `image-${index}.png`,
      mimeType: "image/png",
      path: `private-${index}.png`,
      size: 7 * 1024 * 1024,
      createdAt: new Date().toISOString()
    }))
  };

  await assert.rejects(
    sendMessage(session, { text: "Too many", attachments: session.attachments.map(({ id }) => ({ id })) }),
    (error) => error.message === "attachment_count_exceeded" && error.status === 413
  );
  await assert.rejects(
    sendMessage(session, { text: "Too large", attachments: session.attachments.slice(0, 5).map(({ id }) => ({ id })) }),
    (error) => error.message === "attachment_total_size_exceeded" && error.status === 413
  );
  assert.equal(session.events.length, 0);
});

test("deletes one terminal attachment and leaves a safe message placeholder", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  await initializeSessions({ dataDir, engine: "fake" }, { load: async () => [], save: async () => {} });
  const session = { id: "attachment-delete", status: "completed", events: [], clients: new Set(), approvals: new Map() };

  try {
    const first = await createAttachment(session, { name: "first.png", dataBase64: PNG_IMAGE.toString("base64") });
    const second = await createAttachment(session, { name: "second.jpg", dataBase64: JPEG_IMAGE.toString("base64") });
    session.events.push({
      id: "accepted",
      event: "message.accepted",
      data: { attachments: [{ id: first.id, name: first.name }, { id: second.id, name: second.name }] },
      createdAt: new Date().toISOString()
    });

    const result = await deleteAttachment(session, first.id);

    assert.deepEqual(result, { attachmentId: first.id, deleted: true });
    assert.deepEqual(session.attachments.map((item) => item.id), [second.id]);
    assert.deepEqual(session.events[0].data.attachments[0], { id: first.id, kind: "image", name: first.name, deleted: true });
    assert.equal(session.events[0].data.attachments[1].id, second.id);
    await assert.rejects(access(first.path));
    await access(second.path);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("blocks deletion while an attachment is queued, running, or awaiting approval", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  await initializeSessions({ dataDir, engine: "fake" }, { load: async () => [], save: async () => {} });
  const session = { id: "attachment-in-use", status: "running", events: [], clients: new Set(), approvals: new Map() };

  try {
    const queued = await createAttachment(session, { name: "queued.png", dataBase64: PNG_IMAGE.toString("base64") });
    const active = await createAttachment(session, { name: "active.jpg", dataBase64: JPEG_IMAGE.toString("base64") });
    session.queuedMessages = [{ id: "queued-1", attachments: [queued] }];
    session.activeAttachmentIds = new Set([active.id]);

    for (const attachment of [queued, active]) {
      await assert.rejects(
        deleteAttachment(session, attachment.id),
        (error) => error.message === "attachment_in_use" && error.status === 409
      );
      await access(attachment.path);
    }
    session.status = "waiting_approval";
    await assert.rejects(
      deleteAttachment(session, active.id),
      (error) => error.message === "attachment_in_use" && error.status === 409
    );
    assert.equal(session.attachments.length, 2);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("restores attachment metadata and file when deletion persistence fails", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  let failSave = false;
  let saveCalls = 0;
  const store = {
    load: async () => [],
    save: async () => {
      saveCalls += 1;
      if (failSave) throw new Error("disk_full");
    }
  };
  await initializeSessions({ dataDir, engine: "fake" }, store);
  const session = { id: "attachment-delete-rollback", status: "idle", events: [], clients: new Set(), approvals: new Map() };

  try {
    const attachment = await createAttachment(session, { name: "keep.png", dataBase64: PNG_IMAGE.toString("base64") });
    await waitFor(() => saveCalls >= 2);
    failSave = true;

    await assert.rejects(
      deleteAttachment(session, attachment.id),
      (error) => error.message === "attachment_delete_failed" && error.status === 503
    );
    assert.equal(session.attachments[0].id, attachment.id);
    await access(attachment.path);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("startup removes at most 128 expired abandoned uploads while preserving referenced and fresh images", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-cleanup-"));
  const sessionId = "cleanup-session";
  const attachmentDir = join(dataDir, "attachments", sessionId);
  const expired = Array.from({ length: 130 }, (_, index) => ({
    id: `expired-${String(index).padStart(3, "0")}`,
    kind: "image",
    name: `expired-${index}.jpg`,
    mimeType: "image/jpeg",
    size: JPEG_IMAGE.length,
    createdAt: "2000-01-01T00:00:00.000Z"
  }));
  const sent = { id: "sent-old", kind: "image", name: "sent.jpg", mimeType: "image/jpeg", size: JPEG_IMAGE.length, createdAt: "2000-01-01T00:00:00.000Z" };
  const queued = { id: "queued-old", kind: "image", name: "queued.jpg", mimeType: "image/jpeg", size: JPEG_IMAGE.length, createdAt: "2000-01-01T00:00:00.000Z" };
  const fresh = { id: "fresh", kind: "image", name: "fresh.jpg", mimeType: "image/jpeg", size: JPEG_IMAGE.length, createdAt: "2999-01-01T00:00:00.000Z" };
  const all = [...expired, sent, queued, fresh];
  const saved = [];
  await mkdir(attachmentDir, { recursive: true });
  await Promise.all(all.map((attachment) => writeFile(join(attachmentDir, `${attachment.id}.jpg`), JPEG_IMAGE)));

  try {
    await initializeSessions({ dataDir, engine: "fake" }, {
      load: async () => [{
        id: sessionId,
        accountId: "account-a",
        status: "interrupted",
        createdAt: "2000-01-01T00:00:00.000Z",
        updatedAt: "2000-01-01T00:00:00.000Z",
        attachments: all,
        events: [{ event: "message.accepted", data: { attachments: [{ id: sent.id }] } }],
        queuedMessages: [{ id: "queued-1", text: "Later", attachments: [{ id: queued.id }], skills: [] }]
      }],
      save: async (value) => saved.push(value)
    });

    const restored = getSession(sessionId);
    assert.deepEqual(restored.attachments.filter((item) => item.id.startsWith("expired-")).map((item) => item.id), ["expired-128", "expired-129"]);
    assert.equal(restored.attachments.some((item) => item.id === sent.id), true);
    assert.equal(restored.attachments.some((item) => item.id === queued.id), true);
    assert.equal(restored.attachments.some((item) => item.id === fresh.id), true);
    await assert.rejects(access(join(attachmentDir, "expired-000.jpg")));
    await access(join(attachmentDir, "expired-128.jpg"));
    await access(join(attachmentDir, `${sent.id}.jpg`));
    assert.equal(saved.at(-1)[0].attachments.length, 5);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("startup restores referenced attachment quarantine and removes an unowned delete leftover", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-quarantine-"));
  const sessionId = "quarantine-session";
  const attachmentDir = join(dataDir, "attachments", sessionId);
  const canonicalPath = join(attachmentDir, "recover.jpg");
  const recoverablePath = `${canonicalPath}.deleting-recoverable`;
  const orphanPath = join(attachmentDir, "orphan.jpg.deleting-unowned");
  await mkdir(attachmentDir, { recursive: true });
  await writeFile(recoverablePath, JPEG_IMAGE);
  await writeFile(orphanPath, JPEG_IMAGE);

  try {
    await initializeSessions({ dataDir, engine: "fake" }, {
      load: async () => [{
        id: sessionId,
        accountId: "account-a",
        status: "interrupted",
        createdAt: "2000-01-01T00:00:00.000Z",
        updatedAt: "2000-01-01T00:00:00.000Z",
        attachments: [{ id: "recover", kind: "image", name: "recover.jpg", mimeType: "image/jpeg", size: JPEG_IMAGE.length, createdAt: "2000-01-01T00:00:00.000Z" }],
        events: [{ event: "message.accepted", data: { attachments: [{ id: "recover" }] } }],
        queuedMessages: []
      }],
      save: async () => {}
    });

    await access(canonicalPath);
    await assert.rejects(access(recoverablePath));
    await assert.rejects(access(orphanPath));
    assert.deepEqual((await getAttachment(getSession(sessionId), "recover")).data, JPEG_IMAGE);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("startup cleanup rolls files and metadata back when its persistence fails", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-cleanup-failure-"));
  const sessionId = "cleanup-failure-session";
  const attachmentDir = join(dataDir, "attachments", sessionId);
  const attachmentPath = join(attachmentDir, "abandoned.jpg");
  let saveCalls = 0;
  await mkdir(attachmentDir, { recursive: true });
  await writeFile(attachmentPath, JPEG_IMAGE);

  try {
    await initializeSessions({ dataDir, engine: "fake" }, {
      load: async () => [{
        id: sessionId,
        accountId: "account-a",
        status: "interrupted",
        createdAt: "2000-01-01T00:00:00.000Z",
        updatedAt: "2000-01-01T00:00:00.000Z",
        attachments: [{ id: "abandoned", kind: "image", name: "abandoned.jpg", mimeType: "image/jpeg", size: JPEG_IMAGE.length, createdAt: "2000-01-01T00:00:00.000Z" }],
        events: [],
        queuedMessages: []
      }],
      save: async () => {
        saveCalls += 1;
        if (saveCalls === 1) throw new Error("storage unavailable");
      }
    });

    assert.equal(saveCalls, 2);
    assert.equal(getSession(sessionId).attachments.some((item) => item.id === "abandoned"), true);
    await access(attachmentPath);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("startup removes at most 64 completed session caches older than 30 days without touching recoverable work or receipts", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-session-retention-"));
  const expired = Array.from({ length: 66 }, (_, index) => ({
    id: `expired-session-${String(index).padStart(3, "0")}`,
    accountId: "account-a",
    status: index % 2 ? "failed" : "completed",
    createdAt: "2000-01-01T00:00:00.000Z",
    updatedAt: "2000-01-01T00:00:00.000Z",
    events: [],
    attachments: [],
    queuedMessages: []
  }));
  const interrupted = { ...expired[0], id: "interrupted-old", status: "running" };
  const queued = { ...expired[0], id: "queued-old", status: "completed", queuedMessages: [{ id: "queued-1", attachments: [], skills: [] }] };
  const fresh = { ...expired[0], id: "fresh-completed", updatedAt: "2999-01-01T00:00:00.000Z" };
  const stored = [...expired, interrupted, queued, fresh];
  const receiptSentinel = "receipt-ledger-must-remain-byte-identical";
  await writeFile(join(dataDir, "action-receipts.json"), receiptSentinel, "utf8");
  await Promise.all(stored.flatMap((session) => ["attachments", "artifacts"].map(async (kind) => {
    const directory = join(dataDir, kind, session.id);
    await mkdir(directory, { recursive: true });
    await writeFile(join(directory, "keep.bin"), "data");
  })));

  try {
    await initializeSessions({ dataDir, engine: "fake" }, {
      load: async () => stored,
      save: async () => {}
    });

    assert.deepEqual(listSessions().filter((session) => session.id.startsWith("expired-session-")).map((session) => session.id), ["expired-session-064", "expired-session-065"]);
    assert.equal(getSession("interrupted-old").status, "interrupted");
    assert.equal(getSession("queued-old").queuedMessages.length, 1);
    assert.equal(getSession("fresh-completed").id, "fresh-completed");
    await assert.rejects(access(join(dataDir, "attachments", "expired-session-000")));
    await access(join(dataDir, "attachments", "expired-session-064"));
    await access(join(dataDir, "attachments", "interrupted-old"));
    assert.equal(await readFile(join(dataDir, "action-receipts.json"), "utf8"), receiptSentinel);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("session retention restores cache and attachment directories when persistence fails", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-session-retention-failure-"));
  const sessionId = "expired-session-rollback";
  let saveCalls = 0;
  for (const kind of ["attachments", "artifacts"]) {
    await mkdir(join(dataDir, kind, sessionId), { recursive: true });
    await writeFile(join(dataDir, kind, sessionId, "keep.bin"), "data");
  }

  try {
    await initializeSessions({ dataDir, engine: "fake" }, {
      load: async () => [{
        id: sessionId,
        accountId: "account-a",
        status: "completed",
        createdAt: "2000-01-01T00:00:00.000Z",
        updatedAt: "2000-01-01T00:00:00.000Z",
        events: [],
        attachments: [],
        queuedMessages: []
      }],
      save: async () => {
        saveCalls += 1;
        if (saveCalls === 1) throw new Error("retention storage unavailable");
      }
    });

    assert.equal(saveCalls, 2);
    assert.equal(getSession(sessionId).id, sessionId);
    await access(join(dataDir, "attachments", sessionId, "keep.bin"));
    await access(join(dataDir, "artifacts", sessionId, "keep.bin"));
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("runtime maintenance removes only expired idle terminal session caches", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-runtime-retention-"));
  const freshTimestamp = "2999-01-01T00:00:00.000Z";
  const stored = [
    { id: "expired-idle", status: "completed" },
    { id: "expired-viewed", status: "completed" },
    { id: "expired-queued", status: "failed", queuedMessages: [{ id: "queued-1", attachments: [], skills: [] }] },
    { id: "expired-interrupted", status: "interrupted" }
  ].map((item) => ({
    accountId: "account-a",
    createdAt: freshTimestamp,
    updatedAt: freshTimestamp,
    events: [],
    attachments: [],
    queuedMessages: [],
    ...item
  }));

  try {
    await Promise.all(stored.flatMap((session) => ["attachments", "artifacts"].map(async (kind) => {
      const directory = join(dataDir, kind, session.id);
      await mkdir(directory, { recursive: true });
      await writeFile(join(directory, "keep.bin"), "data");
    })));
    await initializeSessions({ dataDir, engine: "fake" }, { load: async () => stored, save: async () => {} });
    const expiredAt = "2000-01-01T00:00:00.000Z";
    for (const session of stored) getSession(session.id).updatedAt = expiredAt;
    getSession("expired-viewed").clients.add({});

    const removed = await runSessionMaintenance({ now: Date.now(), budget: 8 });

    assert.equal(removed, 1);
    assert.throws(() => getSession("expired-idle"), /session_not_found/);
    assert.equal(getSession("expired-viewed").id, "expired-viewed");
    assert.equal(getSession("expired-queued").queuedMessages.length, 1);
    assert.equal(getSession("expired-interrupted").status, "interrupted");
    await assert.rejects(access(join(dataDir, "attachments", "expired-idle")));
    await access(join(dataDir, "attachments", "expired-viewed"));
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("sent attachment events expose safe metadata and can be read through the session endpoint", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir,
    engine: "fake",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  try {
    await initializeSessions(config, store);
    const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    const session = getSession(created.id);
    const attachment = await createAttachment(session, {
      name: "photo.jpg",
      mimeType: "image/jpeg",
      dataBase64: JPEG_IMAGE.toString("base64")
    });

    await sendMessage(session, { text: "Look", attachments: [{ id: attachment.id }] });
    const accepted = session.events.find((event) => event.event === "message.accepted");
    const payload = JSON.stringify(accepted.data);
    const fetched = await getAttachment(session, attachment.id);

    assert.deepEqual(accepted.data.attachments, [{
      id: attachment.id,
      kind: "image",
      name: "photo.jpg",
      mimeType: "image/jpeg",
      createdAt: attachment.createdAt
    }]);
    assert.equal(payload.includes(attachment.path), false);
    assert.deepEqual(fetched.data, JPEG_IMAGE);
    assert.equal(fetched.mimeType, "image/jpeg");
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("deleting local thread data removes linked sessions and only their attachment folders", async () => {
  const dataDir = await mkdtemp(join(tmpdir(), "code-remote-manager-test-"));
  const saved = [];
  const config = {
    dataDir,
    engine: "fake",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  try {
    await initializeSessions(config, { load: async () => [], save: async (sessions) => saved.push(sessions.map((item) => item.id)) });
    const removeMe = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    const keepMe = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
    getSession(removeMe.id).codexThreadId = "thread-remove";
    getSession(keepMe.id).codexThreadId = "thread-keep";
    await createAttachment(getSession(removeMe.id), { name: "a.jpg", mimeType: "image/jpeg", dataBase64: JPEG_IMAGE.toString("base64") });
    await createAttachment(getSession(keepMe.id), { name: "b.jpg", mimeType: "image/jpeg", dataBase64: JPEG_IMAGE.toString("base64") });
    await mkdir(join(dataDir, "artifacts", removeMe.id), { recursive: true });
    await writeFile(join(dataDir, "artifacts", removeMe.id, "preview.html"), "preview");

    const result = await deleteThreadLocalData(config, "account-a", "thread-remove");

    assert.deepEqual(result, { removedSessions: [removeMe.id] });
    assert.throws(() => getSession(removeMe.id), /session_not_found/);
    assert.equal(getSession(keepMe.id).id, keepMe.id);
    await assert.rejects(access(join(dataDir, "attachments", removeMe.id)));
    await assert.rejects(access(join(dataDir, "artifacts", removeMe.id)));
    await access(join(dataDir, "attachments", keepMe.id));
    assert.equal(saved.at(-1).includes(removeMe.id), false);
  } finally {
    await rm(dataDir, { recursive: true, force: true });
  }
});

test("drops internal process logs when restoring sessions", async () => {
  const store = {
    load: async () => [{
      id: "logs-session",
      accountId: "account-a",
      workspaceId: "workspace-a",
      status: "completed",
      createdAt: "2026-06-27T00:00:00.000Z",
      updatedAt: new Date().toISOString(),
      events: [
        { event: "process.stderr", data: { text: "private local path" } },
        { event: "codex.event", data: { method: "internal" } },
        { event: "session.output", data: { text: "visible" } }
      ],
      approvals: [],
      files: []
    }],
    save: async () => {}
  };

  await initializeSessions({ dataDir: "unused" }, store);

  assert.deepEqual(getSession("logs-session").events.map((item) => item.event), ["session.output"]);
});

test("opens existing thread once without starting runtime", async () => {
  const store = { load: async () => [], save: async () => {} };
  const config = {
    dataDir: "unused",
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project", label: "Project" }]
  };
  const reader = async () => ({
    thread: {
      id: "thread-existing",
      cwd: "C:\\Project\\apps\\backend",
      status: { type: "idle" },
      createdAt: 100,
      updatedAt: 110,
      turns: [{
        id: "turn-1",
        status: "completed",
        startedAt: 100,
        completedAt: 101,
        durationMs: 1000,
        items: [
          { id: "u1", type: "userMessage", content: [{ type: "text", text: "Old question" }] },
          { id: "a1", type: "agentMessage", phase: "final_answer", text: "Old answer" }
        ]
      }]
    }
  });
  await initializeSessions(config, store);

  const first = await openThread(config, "account-a", "thread-existing", { projectLabel: "Other project" }, reader);
  const second = await openThread(config, "account-a", "thread-existing", { projectLabel: "Other project" }, reader);

  assert.equal(first.session.id, second.session.id);
  assert.equal(first.session.codexThreadId, "thread-existing");
  assert.equal(first.session.workspaceId, "workspace-a");
  assert.equal(first.session.projectLabel, "Project");
  assert.equal(first.session.status, "completed");
  assert.equal(first.events.some((event) => event.data?.text === "Old answer"), true);
  assert.equal(getSession(first.session.id).runtime, null);
  assert.equal(listSessions().filter((session) => session.codexThreadId === "thread-existing").length, 1);
});
test("opening thread history projects the same workspace artifacts as live output", async () => {
  const root = await mkdtemp(join(tmpdir(), "code-remote-manager-history-artifact-"));
  const workspacePath = join(root, "workspace");
  const config = {
    dataDir: join(root, "data"),
    accounts: [{ id: "account-a", codexHome: "A_HOME" }],
    workspaces: [{ id: "workspace-a", path: workspacePath, label: "Project" }]
  };
  await mkdir(workspacePath, { recursive: true });
  await writeFile(join(workspacePath, "preview.html"), "<h1>History preview</h1>");
  const reader = async () => ({ thread: {
    id: "thread-history-artifact",
    cwd: workspacePath,
    status: { type: "idle" },
    turns: [{
      id: "turn-1", status: "completed", items: [
        { id: "u1", type: "userMessage", content: [{ type: "text", text: "Preview" }] },
        { id: "a1", type: "agentMessage", phase: "final_answer", text: '::codex-inline-vis{file="preview.html"}' }
      ]
    }]
  } });

  try {
    await initializeSessions(config, { load: async () => [], save: async () => {} });
    const opened = await openThread(config, "account-a", "thread-history-artifact", {}, reader);
    const output = opened.events.find((event) => event.event === "session.output");
    const artifactId = output.data.text.match(/id="(artifact-[a-f0-9]{24})"/)?.[1];
    assert.match(output.data.text, /::codex-inline-artifact/);
    assert.match((await getArtifact(getSession(opened.session.id), artifactId, "preview.html")).data.toString(), /<h1>History preview<\/h1>/);
  } finally {
    await rm(root, { recursive: true, force: true });
  }
});
test("capability failures expose a fixed safe message", async () => {
  const config = {
    engine: "codex", codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "C:\\Users\\operator\\.codex-main" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Codex-remote" }]
  };
  const requester = async () => { throw new Error("Bearer secret-token at C:\\Users\\operator\\.codex-main\\auth.json"); };

  const result = await listCapabilities(config, "account-a", "workspace-a", requester);

  assert.equal(result.errors.length, 4);
  assert.deepEqual(result.errors[0], { kind: "models", message: "capability_unavailable" });
  assert.equal(JSON.stringify(result).includes("secret-token"), false);
  assert.equal(JSON.stringify(result).includes("operator"), false);
});

test("rejects capability discovery for history-only accounts before contacting app-server", async () => {
  let requested = false;
  const config = {
    engine: "codex",
    codexCliPath: "codex",
    accounts: [{ id: "history", codexHome: "HISTORY_HOME", historyOnly: true }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Codex-remote" }]
  };

  await assert.rejects(
    listCapabilities(config, "history", "workspace-a", async () => {
      requested = true;
      return {};
    }),
    (error) => error.message === "account_not_available_for_execution" && error.status === 403
  );
  assert.equal(requested, false);
});

test("sanitizes runtime errors and approval previews before public events", async () => {
  let emitRuntime;
  const config = {
    dataDir: "unused", engine: "codex", codexCliPath: "codex",
    accounts: [{ id: "account-a", codexHome: "C:\\Users\\operator\\.codex-main" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Codex-remote" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => {} }, {
    startRuntime: async ({ emit }) => {
      emitRuntime = emit;
      return { threadId: "thread-1", async sendMessage() {}, stop() {} };
    }
  });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  await sendMessage(session, { text: "Start" });

  emitRuntime("error", { message: "Bearer secret-token at C:\\Project\\Codex-remote\\private.txt" });
  emitRuntime("approval.requested", {
    runtimeRequestId: 7, title: "Approval",
    reason: "Read C:\\Users\\operator\\.codex-main\\auth.json",
    commandPreview: "type C:\\Project\\Codex-remote\\secret.txt"
  });

  const serialized = JSON.stringify({ events: session.events, approvals: listApprovals(session) });
  assert.equal(serialized.includes("secret-token"), false);
  assert.equal(serialized.includes("C:\\\\Project\\\\Codex-remote"), false);
  assert.equal(serialized.includes("operator"), false);
});

test("PC data reset blocks queued work and closes idle session activity before deletion", async () => {
  let saves = 0;
  let clientEnded = false;
  const config = {
    dataDir: "unused", engine: "fake",
    accounts: [{ id: "account-a", codexHome: "unused" }],
    workspaces: [{ id: "workspace-a", path: "C:\\Project\\Workspace" }]
  };
  await initializeSessions(config, { load: async () => [], save: async () => { saves += 1; } });
  const created = await createSession(config, { accountId: "account-a", workspaceId: "workspace-a" });
  const session = getSession(created.id);
  session.queuedMessages = [{ id: "queued-1", text: "later", attachments: [], skills: [] }];

  await assert.rejects(prepareSessionsForApplicationDataReset(), (error) => error.message === "application_data_in_use" && error.status === 409);

  session.queuedMessages = [];
  session.clients.add({ end() { clientEnded = true; } });
  const beforeResetSaves = saves;
  await prepareSessionsForApplicationDataReset();

  assert.equal(clientEnded, true);
  assert.equal(session.clients.size, 0);
  assert.ok(saves > beforeResetSaves);
});
