import { describe, expect, it, vi } from "vitest"; import { buildAssistantLivingChatAttemptRuntimeInput } from "../src/services/assistantLivingChatAttemptInputBuilder"; function buildInput(overrides: Record = {}) { return { sessionId: "asst-1", userMessage: "че там", sessionItems: [], modeDecision: { mode: "chat", reason: "living_chat_signal_detected" }, sessionScope: { knownOrganizations: ["Org A"], selectedOrganization: "Org A", activeOrganization: "Org A" }, addressRuntimeMeta: { source: "address_runtime" }, toNonEmptyString: (value: unknown) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null, mergeKnownOrganizations: (values: string[]) => values, hasAssistantDataScopeMetaQuestionSignal: () => false, shouldHandleAsAssistantCapabilityMetaQuery: () => false, hasDestructiveDataActionSignal: () => false, hasDangerOrCoercionSignal: () => false, hasOperationalAdminActionRequestSignal: () => false, hasOrganizationFactLookupSignal: () => false, hasOrganizationFactFollowupSignal: () => false, hasLivingChatSignal: () => false, shouldEmitOrganizationSelectionReply: () => false, hasAssistantCapabilityQuestionSignal: () => false, resolveOrganizationSelectionFromMessage: () => null, resolveDataScopeProbe: () => null, applyScriptGuard: (chatText: string) => chatText, applyGroundingGuard: (guardInput: Record) => guardInput, buildAssistantSafetyRefusalReply: () => "safety", buildAssistantDataScopeContractReply: () => "scope", buildAssistantProactiveOrganizationOfferReply: () => "offer", buildAssistantOrganizationFactBoundaryReply: () => "boundary", buildAssistantDataScopeSelectionReply: () => "selection", buildAssistantOperationalBoundaryReply: () => "operational", buildAssistantCapabilityContractReply: () => "capability", appendItem: () => {}, getSession: () => null, persistSession: () => {}, cloneConversation: (items: unknown[]) => items, logEvent: () => {}, messageIdFactory: vi.fn(() => "msg-abc123"), nowIso: () => "2026-04-11T00:00:00.000Z", payload: { llmProvider: "openai" }, chatClient: {}, loadAssistantCanonExcerpt: () => "", sanitizeOutgoingAssistantText: (value: unknown, fallback = "") => { const text = typeof value === "string" ? value.trim() : ""; return text || fallback; }, defaultModel: "gpt-5", defaultBaseUrl: "http://localhost", defaultApiKey: "key", ...overrides } as any; } describe("assistant living chat attempt input builder", () => { it("builds living-chat runtime input with derived trace id and session scope", () => { const runtimeInput = buildAssistantLivingChatAttemptRuntimeInput(buildInput()); expect(runtimeInput.sessionId).toBe("asst-1"); expect(runtimeInput.userMessage).toBe("че там"); expect(runtimeInput.traceIdFactory()).toBe("chat-abc123"); expect(runtimeInput.sessionScope).toEqual({ knownOrganizations: ["Org A"], selectedOrganization: "Org A", activeOrganization: "Org A" }); expect(runtimeInput.modeDecision).toEqual({ mode: "chat", reason: "living_chat_signal_detected" }); }); it("normalizes absent address runtime meta to null and preserves optional api key", () => { const runtimeInput = buildAssistantLivingChatAttemptRuntimeInput( buildInput({ addressRuntimeMeta: undefined, defaultApiKey: undefined }) ); expect(runtimeInput.addressRuntimeMeta).toBeNull(); expect(runtimeInput.defaultApiKey).toBeUndefined(); expect(runtimeInput.defaultBaseUrl).toBe("http://localhost"); expect(runtimeInput.defaultModel).toBe("gpt-5"); }); });