import { describe, expect, it } from "vite-plus/test"; import * as Schema from "effect/Schema"; import { extractXAiAskUserQuestions, makeXAiAskUserQuestionCancelledResponse, makeXAiAskUserQuestionResponse, XAiAskUserQuestionRequest, } from "./XAiAcpExtension.ts"; const decodeXAiAskUserQuestionRequest = Schema.decodeUnknownSync(XAiAskUserQuestionRequest); describe("XAiAcpExtension", () => { it("extracts questions from the real xAI ask_user_question payload shape", () => { const questions = extractXAiAskUserQuestions({ sessionId: "session-1", toolCallId: "tool-call-1", mode: "default", questions: [ { id: "scope", question: "Which scope should Grok use?", options: [ { label: "Workspace", description: "Use the current workspace" }, { label: "Session", description: "Only use this session" }, ], }, ], }); expect(questions).toEqual([ { id: "scope", header: "Question", question: "Which scope should Grok use?", multiSelect: false, options: [ { label: "Workspace", description: "Use the current workspace" }, { label: "Session", description: "Only use this session" }, ], }, ]); }); it("extracts questions from wrapped _x.ai extension payloads", () => { const payload = { method: "_x.ai/ask_user_question", params: { sessionId: "session-1", toolCallId: "tool-call-1", mode: "plan", questions: [ { question: "Which changes should be included?", multiSelect: true, options: [{ label: "Tests" }, { label: "Docs" }], }, ], }, }; const decoded = decodeXAiAskUserQuestionRequest(payload); const questions = extractXAiAskUserQuestions(decoded); expect(questions).toEqual([ { id: "Which changes should be included?", header: "Question", question: "Which changes should be included?", multiSelect: true, options: [ { label: "Tests", description: "Tests" }, { label: "Docs", description: "Docs" }, ], }, ]); }); it("treats nullable multiSelect from Grok as single-select", () => { const questions = extractXAiAskUserQuestions({ sessionId: "session-1", toolCallId: "tool-call-1", mode: "default", questions: [ { question: "Which label should Grok use?", multiSelect: null, options: [ { label: "Alpha", description: "Use the Alpha label" }, { label: "Beta", description: "Use the Beta label" }, { label: "Other", description: "Use the Other label" }, ], }, ], }); expect(questions).toEqual([ { id: "Which label should Grok use?", header: "Question", question: "Which label should Grok use?", multiSelect: false, options: [ { label: "Alpha", description: "Use the Alpha label" }, { label: "Beta", description: "Use the Beta label" }, { label: "Other", description: "Use the Other label" }, ], }, ]); }); it("maps UI question ids back to xAI question text in accepted responses", () => { const response = makeXAiAskUserQuestionResponse( { sessionId: "session-1", toolCallId: "tool-call-1", mode: "default", questions: [ { id: "scope", question: "Which scope should Grok use?", options: [ { label: "workspace", description: "Use the current workspace" }, { label: "session", description: "Only use this session" }, ], }, ], }, { scope: "workspace" }, ); expect(response).toEqual({ outcome: "accepted", answers: { "Which scope should Grok use?": ["workspace"], }, }); }); it("orders accepted answers by the original xAI question order", () => { const response = makeXAiAskUserQuestionResponse( { sessionId: "session-1", toolCallId: "tool-call-1", mode: "default", questions: [ { id: "first", question: "First question?", options: [{ label: "A", description: "A" }], }, { id: "second", question: "Second question?", options: [{ label: "B", description: "B" }], }, ], }, { second: "B", first: "A", }, ); expect(Object.keys(response.answers)).toEqual(["First question?", "Second question?"]); expect(response).toMatchObject({ outcome: "accepted", answers: { "First question?": ["A"], "Second question?": ["B"], }, }); }); it("encodes typed custom answers as xAI Other annotations", () => { const response = makeXAiAskUserQuestionResponse( { method: "x.ai/ask_user_question", params: { sessionId: "session-1", toolCallId: "tool-call-1", mode: "default", questions: [ { question: "Which ice cream flavor?", options: [ { label: "vanilla", description: "Vanilla flavor" }, { label: "chocolate", description: "Chocolate flavor" }, ], }, ], }, }, { "Which ice cream flavor?": "pistachio" }, ); expect(response).toEqual({ outcome: "accepted", answers: { "Which ice cream flavor?": ["Other"], }, annotations: { "Which ice cream flavor?": { notes: "pistachio", }, }, }); }); it("encodes interrupted dialogs as xAI cancelled responses", () => { expect(makeXAiAskUserQuestionCancelledResponse()).toEqual({ outcome: "cancelled", }); }); it("does not echo preview annotations for multi-select answers", () => { const response = makeXAiAskUserQuestionResponse( { sessionId: "session-1", toolCallId: "tool-call-1", mode: "default", questions: [ { question: "Which files should Grok touch?", multiSelect: true, options: [ { label: "Tests", description: "Update tests", preview: "test preview", }, { label: "Docs", description: "Update docs", preview: "docs preview", }, ], }, ], }, { "Which files should Grok touch?": ["Tests", "Docs"] }, ); expect(response).toEqual({ outcome: "accepted", answers: { "Which files should Grok touch?": ["Tests", "Docs"], }, }); }); });