summaryrefslogtreecommitdiffhomepage
path: root/src/features/chat/ports.ts
blob: ffe2c946aa96ebd296d04fbfba6808d9f79b4474 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import type {
	ChatQueueMessage,
	ChatSendMessage,
	ConversationHistoryResponse,
	ConversationMetricsResponse,
} from "@dispatch/transport-contract";

/**
 * Injected transport port — sends chat messages to the server. Accepts both
 * `chat.send` (start a turn) and `chat.queue` (enqueue a steering message;
 * auto-starts a turn if idle).
 */
export interface ChatTransport {
	send(msg: ChatSendMessage | ChatQueueMessage): void;
}

/**
 * Optional windowing for a history fetch ([email protected], CR-5).
 * Both must be POSITIVE integers when present (the server 400s otherwise).
 */
export interface HistoryWindow {
	/** Return only the NEWEST `limit` chunks of the selection (still ascending). */
	readonly limit?: number;
	/** Exclusive upper bound: only chunks with `seq < beforeSeq` (backfill paging). */
	readonly beforeSeq?: number;
}

/**
 * Injected history-sync port — fetches incremental history from the server
 * (`GET /conversations/:id?sinceSeq=&beforeSeq=&limit=`). NOTE the contract
 * caveat: on a windowed/backfill read the response's `latestSeq` describes the
 * returned window, not the conversation's high-water mark — never regress a
 * tail cursor from it (the FE's cursor comes from the cache's max seq, which
 * satisfies this naturally).
 */
export type HistorySync = (
	conversationId: string,
	sinceSeq: number,
	window?: HistoryWindow,
) => Promise<ConversationHistoryResponse>;

/** Injected metrics-sync port — fetches persisted per-turn metrics from the server. */
export type MetricsSync = (conversationId: string) => Promise<ConversationMetricsResponse>;