Ctrl K
ComunidadGuías…

test/home-chat-sessions.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
var test = require("node:test");
var assert = require("node:assert/strict");
var attachHomeChat = require("../lib/server-home-chat").attachHomeChat;

function settle() {
  return new Promise(function (resolve) { setImmediate(resolve); });
}

function fixture(options) {
  var opts = options || {};
  var mate = { id: "mate-a", name: "A", vendor: "claude", model: "sonnet" };
  if (opts.clay) mate.builtinKey = "clay";
  var sessions = new Map([
    [1, { localId: 1, cliSessionId: "session-old", ownerId: "u1", title: "Older", lastActivity: 10, vendor: "claude", model: "sonnet", history: [] }],
    [2, { localId: 2, cliSessionId: "session-new", ownerId: "u1", title: "Newer", lastActivity: 30, vendor: "codex", model: "gpt-5.6", history: [{ type: "user_message", text: "Hello" }] }],
    [3, { localId: 3, cliSessionId: "session-other", ownerId: "u2", title: "Other user's chat", lastActivity: 40, history: [] }],
    [4, { localId: 4, cliSessionId: "session-hidden", ownerId: "u1", title: "Hidden", lastActivity: 50, hidden: true, history: [] }],
    [5, { localId: 5, cliSessionId: "session-single", title: "Single-user chat", lastActivity: 20, history: [] }],
  ]);
  var subscribed = null;
  var subscription = null;
  var subscriptions = {};
  var recorded = [];
  var manager = {
    sessions: sessions,
    subscribeSession: function (id, callback) {
      subscribed = id;
      subscription = callback;
      subscriptions[id] = callback;
      return function () { if (subscriptions[id] === callback) delete subscriptions[id]; };
    },
    saveSessionFile: function () {},
    createSession: function (createOptions) {
      if (!opts.allowCreate) throw new Error("unexpected session creation");
      var session = { localId: 6, ownerId: createOptions.ownerId, vendor: createOptions.vendor, model: createOptions.model, effort: createOptions.effort || null, history: [], isProcessing: false };
      sessions.set(6, session);
      return session;
    },
    sendAndRecord: function (session, event) {
      recorded.push(event);
      session.history.push(event);
      if (subscribed === session.localId && subscription) subscription(event);
    },
  };
  var project = {
    getSessionManager: function () { return manager; },
    getVendorModelCatalog: function () { return Promise.resolve({ status: "ready", models: ["sonnet"], defaultModel: "sonnet", error: "" }); },
    getMemoryState: function () { return { entries: [], summary: "" }; },
    listKnowledgeFiles: function () { return []; },
    forEachClient: function () {},
    handleHomePermissionResponse: function (ws, message, session) {
      if (typeof opts.onPermissionResponse === "function") return opts.onPermissionResponse(ws, message, session, manager);
      return false;
    },
    stopHomeSession: function (session) {
      if (typeof opts.onStop === "function") return opts.onStop(session);
      return false;
    },
    sdk: Object.prototype.hasOwnProperty.call(opts, "sdk") ? opts.sdk : null,
  };
  var handler = attachHomeChat({
    users: { isMultiUser: function () { return opts.singleUser !== true; } },
    mates: {
      buildMateCtx: function () { return {}; },
      getAllMates: function () { return [mate]; },
      getMate: function () { return mate; },
      updateMate: function (ctx, mateId, patch) { mate = Object.assign({}, mate, patch); return mate; },
      getMateDir: function () { return "/tmp/mate-a"; },
    },
    projects: new Map([["mate-mate-a", project]]),
    addProject: function () {},
    resolveDefaultAi: opts.resolveDefaultAi,
  });
  var messages = [];
  var ws = {
    readyState: 1,
    _clayUser: opts.unauthenticated ? null : { id: "u1" },
    send: function (value) { messages.push(JSON.parse(value)); },
  };
  return {
    handler: handler,
    ws: ws,
    messages: messages,
    getSubscribed: function () { return subscribed; },
    emit: function (event) { if (subscription) subscription(event); },
    emitFor: function (sessionId, event) { if (subscriptions[sessionId]) subscriptions[sessionId](event); },
    emitRecorded: function (event) {
      var session = sessions.get(subscribed);
      if (event.type === "session_id") session.cliSessionId = event.cliSessionId;
      session.history.push(event);
      if (subscription) subscription(event);
    },
    addSession: function (session) { sessions.set(session.localId, session); },
    getSession: function (id) { return sessions.get(id); },
    getRecorded: function () { return recorded; },
    record: function (sessionId, event) { manager.sendAndRecord(sessions.get(sessionId), event); },
  };
}

test("fresh built-in Clay Home and search sessions use the shared Default AI runtime", async function () {
  function resolveDefaultAi() { return Promise.resolve({ ready: true, vendor: "codex", model: "gpt-6-astra", effort: "high" }); }
  var home = fixture({ clay: true, allowCreate: true, resolveDefaultAi: resolveDefaultAi });
  home.handler.handleMessage(home.ws, { type: "home_mate_new_session", mateId: "mate-a", requestId: "fresh-home" });
  await settle();
  assert.deepEqual({ vendor: home.getSession(6).vendor, model: home.getSession(6).model, effort: home.getSession(6).effort }, { vendor: "codex", model: "gpt-6-astra", effort: "high" });

  var search = fixture({ clay: true, allowCreate: true, resolveDefaultAi: resolveDefaultAi, sdk: { startQuery: function () {} } });
  search.handler.handleMessage(search.ws, { type: "home_clay_ask", requestId: "fresh-search", text: "Find the roadmap" });
  await settle();
  assert.deepEqual({ vendor: search.getSession(6).vendor, model: search.getSession(6).model, effort: search.getSession(6).effort }, { vendor: "codex", model: "gpt-6-astra", effort: "high" });
});

test("Mate conversation list includes only the requesting user's visible sessions", function () {
  var f = fixture();
  assert.strictEqual(f.handler.handleMessage(f.ws, { type: "home_mate_sessions_list", mateId: "mate-a" }), true);
  assert.deepStrictEqual(f.messages[0], {
    type: "home_mate_sessions_state",
    mateId: "mate-a",
    sessions: [
      { id: "session-new", cliSessionId: "session-new", localId: 2, title: "Newer", vendor: "codex", model: "gpt-5.6", createdAt: 0, lastActivity: 30, isProcessing: false, sessionOriginId: null, sessionRole: "driver", parentSessionId: null, parentAvailable: false, workerGeneration: null },
      { id: "session-old", cliSessionId: "session-old", localId: 1, title: "Older", vendor: "claude", model: "sonnet", createdAt: 0, lastActivity: 10, isProcessing: false, sessionOriginId: null, sessionRole: "driver", parentSessionId: null, parentAvailable: false, workerGeneration: null },
    ],
  });
});

test("Mate conversation details expose local-only identity without weakening ownership filtering", function () {
  var f = fixture();
  f.addSession({ localId: 6, cliSessionId: null, ownerId: "u1", title: "Local\ndraft\u0000", createdAt: 35, lastActivity: 60, vendor: "claude", model: "haiku", isProcessing: true, history: [] });
  f.handler.handleMessage(f.ws, { type: "home_mate_sessions_list", mateId: "mate-a" });
  var sessions = f.messages[0].sessions;
  assert.deepStrictEqual(sessions[0], {
    id: "local:6",
    cliSessionId: null,
    localId: 6,
    title: "Local draft",
    vendor: "claude",
    model: "haiku",
    createdAt: 35,
    lastActivity: 60,
    isProcessing: true,
    sessionOriginId: null, sessionRole: "driver",
    parentSessionId: null,
    parentAvailable: false,
    workerGeneration: null,
  });
  assert.strictEqual(sessions.some(function (session) { return session.title === "Other user's chat" || session.title === "Hidden"; }), false);
});

test("Home lists repair legacy global-search boilerplate titles", function () {
  var f = fixture();
  f.addSession({
    localId: 6,
    cliSessionId: "legacy-search",
    ownerId: "u1",
    title: "This conversation began in Clay Studio's global search. Resp",
    titleAutoGenerated: true,
    lastActivity: 60,
    vendor: "codex",
    model: "gpt-5.6-sol",
    history: [{ type: "user_message", text: "Find our accessibility review" }],
  });
  f.handler.handleMessage(f.ws, { type: "home_mate_sessions_list", mateId: "mate-a" });
  assert.equal(f.messages[0].sessions[0].title, "Find our accessibility review");
  assert.equal(f.getSession(6).titleAutoGenerated, false);
});

test("Mate settings memory and knowledge responses preserve request correlation", function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_memory_list", mateId: "mate-a", requestId: "memory-a" });
  f.handler.handleMessage(f.ws, { type: "home_mate_knowledge_list", mateId: "mate-a", requestId: "knowledge-a" });
  assert.deepStrictEqual(f.messages.map(function (message) { return [message.type, message.mateId, message.requestId]; }), [
    ["home_mate_memory_state", "mate-a", "memory-a"],
    ["home_mate_knowledge_state", "mate-a", "knowledge-a"],
  ]);
});

test("Explicit Mate conversation open restores the exact requested session", async function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-old" });
  await settle();
  assert.strictEqual(f.getSubscribed(), 1);
  assert.strictEqual(f.messages[0].type, "home_mate_history");
  assert.strictEqual(f.messages[0].sessionId, "session-old");
  assert.strictEqual(f.messages[0].requestId, "open-old");
  assert.strictEqual(f.messages[0].vendor, "claude");
  assert.strictEqual(f.messages[0].model, "sonnet");
});

test("global Ask Clay keeps its stream subscription when Home opens another conversation", async function () {
  var starts = [];
  var f = fixture({
    clay: true,
    allowCreate: true,
    sdk: { startQuery: function (session, text) { starts.push({ session: session, text: text }); } },
  });
  f.handler.handleMessage(f.ws, { type: "home_clay_ask", requestId: "search-request", text: "Find fruit" });
  await settle();
  assert.equal(starts.length, 1);
  assert.equal(f.ws._searchClayTap.sessionId, 6);
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "home-request" });
  await settle();
  assert.equal(f.ws._homeChatTap.sessionId, 1);
  assert.equal(f.ws._searchClayTap.sessionId, 6);
  f.messages.length = 0;
  f.getSession(6).responsePreview = "The fruit conversation is here.";
  f.getSession(6).history.push({ type: "delta", text: "The fruit conversation is here." });
  f.emitFor(6, { type: "done" });
  var reply = f.messages.find(function (message) { return message.type === "home_mate_done"; });
  assert.equal(reply.requestId, "search-request");
  assert.equal(reply.text, "The fruit conversation is here.");
  f.getSession(6).isProcessing = false;
  f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "local:6", requestId: "search-request", text: "Which one?" });
  await settle();
  assert.equal(starts.length, 2);
  assert.equal(starts[1].session.localId, 6);
  assert.equal(f.getSession(1).history.length, 0);
  assert.equal(f.getSession(6).history[f.getSession(6).history.length - 1].text, "Which one?");
});

test("global Ask Clay restores and resolves only the exact session-bound pending permission", async function () {
  var resolved = [];
  var f = fixture({
    clay: true,
    allowCreate: true,
    sdk: { startQuery: function () {} },
    onPermissionResponse: function (ws, message, session, manager) {
      var pending = session.pendingPermissions[message.requestId];
      delete session.pendingPermissions[message.requestId];
      pending.resolve(message.decision);
      manager.sendAndRecord(session, { type: "permission_resolved", requestId: message.requestId, decision: message.decision });
      return true;
    },
  });
  f.handler.handleMessage(f.ws, { type: "home_clay_ask", requestId: "search-control", text: "Change a file" });
  await settle();
  var session = f.getSession(6);
  session.pendingPermissions = {
    "permission-1": { toolName: "Edit", toolInput: { file_path: "/repo/a.js" }, decisionReason: "Update the file", resolve: function (decision) { resolved.push(decision); } },
  };
  f.messages.length = 0;
  f.handler.handleMessage(f.ws, { type: "home_clay_ask", requestId: "search-control", text: "Change a file" });
  await settle();
  var history = f.messages.find(function (message) { return message.type === "home_mate_history"; });
  assert.deepEqual(history.pendingPermissions, [{ permissionRequestId: "permission-1", toolName: "Edit", toolInput: { file_path: "/repo/a.js" }, decisionReason: "Update the file" }]);

  f.handler.handleMessage(f.ws, { type: "home_mate_permission_response", mateId: "mate-a", sessionId: "local:6", requestId: "wrong-search", permissionRequestId: "permission-1", decision: "allow" });
  assert.deepEqual(resolved, []);
  f.handler.handleMessage(f.ws, { type: "home_mate_permission_response", mateId: "mate-a", sessionId: "local:6", requestId: "search-control", permissionRequestId: "permission-1", decision: "allow" });
  assert.deepEqual(resolved, ["allow"]);
  assert.equal(session.pendingPermissions["permission-1"], undefined);
  assert.equal(f.messages.some(function (message) { return message.type === "home_mate_permission_resolved" && message.permissionRequestId === "permission-1"; }), true);
});

test("global Ask Clay Stop targets its exact session and rejects stale session controls", async function () {
  var stopped = [];
  var f = fixture({ clay: true, allowCreate: true, sdk: { startQuery: function () {} }, onStop: function (session) { stopped.push(session.localId); return true; } });
  f.handler.handleMessage(f.ws, { type: "home_clay_ask", requestId: "search-stop", text: "Keep working" });
  await settle();
  f.handler.handleMessage(f.ws, { type: "home_mate_stop", mateId: "mate-a", sessionId: "session-old", requestId: "search-stop" });
  assert.deepEqual(stopped, []);
  f.handler.handleMessage(f.ws, { type: "home_mate_stop", mateId: "mate-a", sessionId: "local:6", requestId: "search-stop" });
  assert.deepEqual(stopped, [6]);
  assert.equal(f.messages.some(function (message) { return message.type === "home_mate_stopping" && message.sessionId === "local:6"; }), true);
});

test("exact Home sessions retain distinct committed model metadata", async function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-a" });
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-new", requestId: "open-b" });
  await settle();
  var histories = f.messages.filter(function (message) { return message.type === "home_mate_history"; });
  assert.deepStrictEqual(histories.map(function (message) {
    return [message.sessionId, message.vendor, message.model, message.requestId];
  }), [
    ["session-old", "claude", "sonnet", "open-a"],
    ["session-new", "codex", "gpt-5.6", "open-b"],
  ]);
});

test("Home live session events refresh committed model metadata", async function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-new", requestId: "open-live" });
  await settle();
  f.messages.length = 0;
  f.emit({ type: "delta", text: "Hi" });
  f.emit({ type: "done" });
  assert.deepStrictEqual(f.messages.slice(0, 2).map(function (message) {
    return [message.type, message.sessionId, message.vendor, message.model, message.requestId];
  }), [
    ["home_mate_delta", "session-new", "codex", "gpt-5.6", "open-live"],
    ["home_mate_done", "session-new", "codex", "gpt-5.6", "open-live"],
  ]);
});

test("fresh Home taps promote session identity before streaming notification-visible output", async function () {
  var f = fixture();
  f.addSession({ localId: 6, cliSessionId: null, ownerId: "u1", title: "Fresh", lastActivity: 60, vendor: "claude", model: "sonnet", history: [] });
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "local:6", requestId: "open-fresh" });
  await settle();
  assert.equal(f.messages[0].sessionId, "local:6");
  f.messages.length = 0;
  f.getSession(6).responsePreview = "The visible assistant reply";
  f.emitRecorded({ type: "session_id", cliSessionId: "durable-fresh" });
  f.emitRecorded({ type: "delta", text: "The visible assistant reply" });
  f.emitRecorded({ type: "result" });
  var stream = f.messages.filter(function (message) {
    return message.type === "home_mate_session_identity" || message.type === "home_mate_delta" || message.type === "home_mate_done";
  });
  assert.deepEqual(stream.map(function (message) {
    return [message.type, message.previousSessionId || null, message.sessionId, message.requestId, message.text || null];
  }), [
    ["home_mate_session_identity", "local:6", "durable-fresh", "open-fresh", null],
    ["home_mate_delta", null, "durable-fresh", "open-fresh", "The visible assistant reply"],
    ["home_mate_done", null, "durable-fresh", "open-fresh", "The visible assistant reply"],
  ]);
  assert.equal(f.getSession(6).responsePreview, "The visible assistant reply");
  assert.equal(f.ws._homeChatTap.sessionReference, "durable-fresh");
  var refreshed = f.messages.find(function (message) { return message.type === "home_mate_sessions_state"; });
  assert.equal(refreshed.sessions[0].id, "durable-fresh");
  assert.equal(refreshed.sessions[0].cliSessionId, "durable-fresh");
  assert.equal(refreshed.sessions[0].localId, 6);
});

test("Home final events recover canonical history text when a live delta was missed", async function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-final-only" });
  await settle();
  f.messages.length = 0;
  f.getSession(1).history.push({ type: "user_message", text: "Question" });
  f.getSession(1).history.push({ type: "delta", text: "Recovered from canonical history" });
  f.emitRecorded({ type: "done" });
  assert.equal(f.messages[0].type, "home_mate_done");
  assert.equal(f.messages[0].text, "Recovered from canonical history");
  assert.equal(f.messages[0].requestId, "open-final-only");
});

test("Explicit Mate conversation open rejects another user's session", function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-other", requestId: "open-denied" });
  assert.strictEqual(f.getSubscribed(), null);
  assert.strictEqual(f.messages[0].type, "home_mate_error");
  assert.strictEqual(f.messages[0].code, "session_not_found");
  assert.strictEqual(f.messages[0].sessionId, "session-other");
  assert.strictEqual(f.messages[0].requestId, "open-denied");
});

test("Home send failures inherit the active session correlation", async function () {
  var f = fixture();
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-send" });
  await settle();
  f.messages.length = 0;
  f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", text: "Hello" });
  await settle();
  assert.strictEqual(f.messages[0].type, "home_mate_error");
  assert.strictEqual(f.messages[0].sessionId, "session-old");
  assert.strictEqual(f.messages[0].requestId, "open-send");
  assert.match(f.messages[0].text, /SDK bridge unavailable/);
});

test("Home send records one canonical user turn and restores user plus assistant history", async function () {
  var startCalls = 0;
  var f = fixture({ sdk: {
    startQuery: function () { startCalls++; },
    pushMessage: function () { throw new Error("unexpected push"); },
  } });
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-record" });
  await settle();
  f.messages.length = 0;
  f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "session-old", requestId: "open-record", text: "Remember my question" });
  await settle();
  assert.equal(startCalls, 1);
  assert.deepEqual(f.getRecorded(), [{ type: "user_message", text: "Remember my question" }]);
  assert.equal(f.messages.some(function (message) { return message.type === "home_mate_user_message" || message.type === "user_message"; }), false);
  f.record(1, { type: "delta", text: "Here is the answer" });
  f.record(1, { type: "result" });
  f.handler.handleMessage(f.ws, { type: "home_mate_close" });
  f.messages.length = 0;
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "reopen-record" });
  await settle();
  assert.deepEqual(f.messages[0].messages, [
    { role: "user", text: "Remember my question" },
    { role: "assistant", text: "Here is the answer" },
  ]);
});

test("Capsule creation starts one exact Mate conversation without mutating a Capsule", async function () {
  var starts = [];
  var f = fixture({ allowCreate: true, sdk: {
    startQuery: function (session, text) { starts.push({ session: session, text: text }); },
    pushMessage: function () { throw new Error("unexpected push"); },
  } });
  f.handler.handleMessage(f.ws, {
    type: "home_mate_new_session",
    mateId: "mate-a",
    requestId: "capsule-create-1",
    intent: { type: "capsule_creation", description: "  A weekly review board  " },
  });
  await settle();
  assert.equal(f.getSubscribed(), 6);
  assert.deepEqual(f.getRecorded(), [{ type: "user_message", text: "A weekly review board" }]);
  assert.equal(starts.length, 1);
  assert.equal(starts[0].session.localId, 6);
  assert.match(starts[0].text, /durable Capsule interface/);
  assert.match(starts[0].text, /A weekly review board/);
  assert.match(starts[0].text, /explicitly approved/);
  var history = f.messages.find(function (message) { return message.type === "home_mate_history"; });
  assert.equal(history.mateId, "mate-a");
  assert.equal(history.sessionId, "local:6");
  assert.equal(history.requestId, "capsule-create-1");
  assert.deepEqual(history.messages, [{ role: "user", text: "A weekly review board" }]);
  assert.equal(history.isProcessing, true);
});

test("invalid Capsule creation intent is correlated and creates no conversation", async function () {
  var f = fixture({ allowCreate: true, sdk: { startQuery: function () { throw new Error("unexpected start"); } } });
  f.handler.handleMessage(f.ws, {
    type: "home_mate_new_session",
    mateId: "mate-a",
    requestId: "capsule-create-empty",
    intent: { type: "capsule_creation", description: "   " },
  });
  await settle();
  assert.equal(f.getSession(6), undefined);
  assert.equal(f.messages[0].type, "home_mate_error");
  assert.equal(f.messages[0].requestId, "capsule-create-empty");
  assert.equal(f.messages[0].code, "capsule_description_required");
});

test("Home push-to-existing-query records one canonical user turn", async function () {
  var pushCalls = 0;
  var f = fixture({ sdk: {
    startQuery: function () { throw new Error("unexpected start"); },
    pushMessage: function () { pushCalls++; },
  } });
  f.getSession(1).isProcessing = true;
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-push" });
  await settle();
  f.messages.length = 0;
  f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "session-old", requestId: "open-push", text: "Follow up" });
  await settle();
  assert.equal(pushCalls, 1);
  assert.deepEqual(f.getRecorded(), [{ type: "user_message", text: "Follow up" }]);
  assert.equal(f.messages.length, 0);
});

test("a synchronous Home dispatch failure preserves one submitted user turn", async function () {
  var f = fixture({ sdk: {
    startQuery: function () { throw new Error("dispatch failed"); },
    pushMessage: function () { throw new Error("unexpected push"); },
  } });
  f.handler.handleMessage(f.ws, { type: "home_mate_session_open", mateId: "mate-a", sessionId: "session-old", requestId: "open-fail" });
  await settle();
  f.messages.length = 0;
  f.handler.handleMessage(f.ws, { type: "home_mate_send", mateId: "mate-a", sessionId: "session-old", requestId: "open-fail", text: "Keep this submission" });
  await settle();
  assert.deepEqual(f.getRecorded(), [{ type: "user_message", text: "Keep this submission" }]);
  assert.equal(f.messages[0].type, "home_mate_error");
  assert.match(f.messages[0].text, /dispatch failed/);
});

test("single-user Mate conversation lists include only ownerless sessions", function () {
  var f = fixture({ singleUser: true });
  f.handler.handleMessage(f.ws, { type: "home_mate_sessions_list", mateId: "mate-a" });
  assert.deepStrictEqual(f.messages[0].sessions, [
    { id: "session-single", cliSessionId: "session-single", localId: 5, title: "Single-user chat", vendor: null, model: null, createdAt: 0, lastActivity: 20, isProcessing: false, sessionOriginId: null, sessionRole: "driver", parentSessionId: null, parentAvailable: false, workerGeneration: null },
  ]);
});

test("unauthenticated multi-user Mate conversation requests reveal no sessions", function () {
  var f = fixture({ unauthenticated: true });
  f.handler.handleMessage(f.ws, { type: "home_mate_sessions_list", mateId: "mate-a", requestId: "list-auth" });
  assert.strictEqual(f.messages[0].type, "home_mate_error");
  assert.strictEqual(f.messages[0].text, "Not authenticated.");
  assert.strictEqual(f.messages[0].requestId, "list-auth");
});