evOo-local/packages/adapters/src/ai-consent.test.ts
Claude 533f82d856
feat(media): disclose media servers in AI data sharing
Media tools and card actions send prompts, images, video and audio to the
deployment owner's ComfyUI and transcription servers. Like the embeddings
server behind built-in memory, those self-hosted endpoints are recipients in
the AI data-sharing disclosure: they appear with the model uses ("ComfyUI
(<origin>)", "Transcription (<origin>)", keyed by the full endpoint so a new
server asks again), and starting a card job asks for model consent on clients
that run the foreground check.

Also adds unit tests for the media settings store: URL normalization and
refusals, encryption, the key-free view, one decryption per saved version, key
retention per server and removing empty settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:51:13 +00:00

65 lines
2.5 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { aiRecipient } from "./ai-consent.js";
afterEach(() => vi.unstubAllGlobals());
describe("mobile AI recipient disclosure", () => {
it("identifies both gateways without querying or changing their routing", () => {
const fetch = vi.fn();
vi.stubGlobal("fetch", fetch);
for (const provider of ["openrouter", "vercel-ai-gateway"]) {
const recipient = aiRecipient({ provider, use: "model", modelId: "any/model" });
expect(recipient?.detail).toContain("forwards requests to model providers");
expect(recipient?.privacyUrl).toBeTruthy();
expect(recipient).not.toHaveProperty("payloadFields");
}
expect(fetch).not.toHaveBeenCalled();
});
it("supports custom endpoints without exposing secrets or inventing a provider policy", () => {
const recipient = aiRecipient({
provider: "openai-compatible",
use: "model",
baseUrl: "https://user:password@example.com/private?token=secret",
})!;
expect(recipient.name).toContain("https://example.com");
expect(JSON.stringify(recipient)).not.toMatch(/password|private|token|secret/);
expect(recipient.privacyUrl).toBeUndefined();
expect(
aiRecipient({
provider: "openai-compatible",
use: "model",
baseUrl: "https://example.com/other",
})?.key,
).not.toBe(recipient.key);
});
it("names the embeddings server that built-in memory sends text to", () => {
const recipient = aiRecipient({
provider: "builtin",
use: "memory",
baseUrl: "http://192.168.1.20:8080/v1",
})!;
expect(recipient.name).toBe("Embeddings (http://192.168.1.20:8080)");
expect(recipient.privacyUrl).toBeUndefined();
});
it("names the media servers that media jobs send files to", () => {
expect(
aiRecipient({ provider: "comfyui", use: "model", baseUrl: "http://127.0.0.1:8188" })?.name,
).toBe("ComfyUI (http://127.0.0.1:8188)");
expect(
aiRecipient({
provider: "media-transcription",
use: "model",
baseUrl: "http://127.0.0.1:8080/v1",
})?.name,
).toBe("Transcription (http://127.0.0.1:8080)");
});
it("supports local models without a hosted dependency", () => {
const fetch = vi.fn();
vi.stubGlobal("fetch", fetch);
expect(
aiRecipient({ provider: "local", use: "model", baseUrl: "http://127.0.0.1:11434/v1" })?.name,
).toContain("http://127.0.0.1:11434");
expect(fetch).not.toHaveBeenCalled();
});
});