mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
Asking the Coach about a document opened a separate, half-finished chat panel that never appeared in Coach history. The bespoke drawer is gone; a document conversation now opens the real Coach view, appears in the Coach list with a document badge and a "Chatting about: <title>" note, and is read back through the same history rail as any other thread. The prompt-injection fence is unchanged and stays server-side. A document turn is still sent through the hardened fenced endpoint (no tools, no health snapshot, grounded in the document text only); the coach tool route continues to fetch with documentId null, so a document-scoped conversation can never be continued through the tool loop even from a hostile client. Only the surface moved into the Coach; the boundary did not. The relaxed conversation-list and detail filters drop only the documentId predicate — userId scoping is unchanged, so a document thread is still visible only to its owner. CoachConversationDTO gains documentId + documentTitle (server-derived, title/filename only); no schema change (documentId exists since 0230). OpenAPI regenerated.
76 lines
3.5 KiB
TypeScript
76 lines
3.5 KiB
TypeScript
/**
|
|
* Query keys — the Dokumente vault: the filtered timeline list (keyset-
|
|
* paginated), a single document's detail, and the usage/limits read the
|
|
* upload path pre-flights against. Part of the centralized factory;
|
|
* aggregated in `./index.ts`. Any document write (upload / edit / link /
|
|
* delete / restore / bulk) invalidates the `["documents"]` prefix so the
|
|
* timeline, an open detail sheet, and the quota bar repaint in lockstep.
|
|
*/
|
|
import type { InboundDocumentKindValue } from "@/lib/validations/inbound-documents";
|
|
|
|
/**
|
|
* The parameter set that distinguishes one vault timeline view from another —
|
|
* mirrors the URL facets (`?q&kind&episode&year`) one-to-one so a deep link
|
|
* and an in-page filter change resolve to the same cache bucket. `undefined`
|
|
* fields are dropped by TanStack's stable hash, so an all-defaults view and
|
|
* an explicit `{ q: undefined, … }` share a key. Sort is pinned server-side
|
|
* (filing date, newest first) and is deliberately NOT part of the key.
|
|
*/
|
|
export interface DocumentVaultFilters {
|
|
q?: string;
|
|
/** Type facet — OR inside the facet, sorted before keying by the caller. */
|
|
kinds?: InboundDocumentKindValue[];
|
|
/** Only documents linked to this illness/condition episode. */
|
|
episodeId?: string;
|
|
/** Only documents whose filing date falls in this calendar year. */
|
|
year?: number;
|
|
}
|
|
|
|
export const documentKeys = {
|
|
documents: () => ["documents"] as const,
|
|
inboundDocuments: () => ["documents", "inbound"] as const,
|
|
/**
|
|
* The parameterised vault list (keyset-paginated via `useInfiniteQuery`).
|
|
* The literal `"list"` segment keeps it distinct from a single-document key
|
|
* (`inboundDocument(id)`); cuid ids never collide with it. Stays under the
|
|
* `["documents", "inbound"]` prefix so prefix invalidation still reaches it.
|
|
*/
|
|
inboundDocumentList: (filters: DocumentVaultFilters) =>
|
|
["documents", "inbound", "list", filters] as const,
|
|
inboundDocument: (id: string) => ["documents", "inbound", id] as const,
|
|
/**
|
|
* The illness-episode detail's compact document preview (single page,
|
|
* small limit). Deliberately NOT `inboundDocumentList({ episodeId })` —
|
|
* that key belongs to the vault's `useInfiniteQuery` and a plain query
|
|
* under the same key would poison the cache with a different shape.
|
|
*/
|
|
inboundDocumentEpisodePreview: (episodeId: string) =>
|
|
["documents", "inbound", "episode-preview", episodeId] as const,
|
|
/**
|
|
* The link-existing-documents picker (searchable single page). Own key
|
|
* for the same shape-collision reason as the episode preview.
|
|
*/
|
|
inboundDocumentPicker: (q: string) =>
|
|
["documents", "inbound", "picker", q] as const,
|
|
/**
|
|
* Storage usage + effective limits (`GET /api/documents/inbound/usage`).
|
|
* Invalidated through the `["documents"]` prefix after every upload /
|
|
* delete so the quota bar tracks reality.
|
|
*/
|
|
inboundDocumentUsage: () => ["documents", "inbound", "usage"] as const,
|
|
/**
|
|
* Document-scoped AI capability probe
|
|
* (`GET /api/documents/inbound/capability`). Distinct from the labs
|
|
* `ocrCapability` key because it resolves over the DOCUMENT provider order
|
|
* (local-first) and carries the per-egress class the vault notice reads.
|
|
*/
|
|
inboundDocumentAiCapability: () =>
|
|
["documents", "inbound", "ai-capability"] as const,
|
|
/**
|
|
* The per-user "read documents automatically with AI" opt-in
|
|
* (`GET/PATCH /api/auth/me/documents-auto-ai-read`). Drives the AI-settings
|
|
* toggle; a flip invalidates the document AI capability probe.
|
|
*/
|
|
documentsAutoAiRead: () => ["documents", "auto-ai-read"] as const,
|
|
};
|