chattercatcher 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,808 @@
1
+ import { z } from 'zod';
2
+ import Database from 'better-sqlite3';
3
+ import * as lark from '@larksuiteoapi/node-sdk';
4
+ import { FastifyInstance } from 'fastify';
5
+
6
+ declare const appConfigSchema: z.ZodObject<{
7
+ feishu: z.ZodObject<{
8
+ domain: z.ZodDefault<z.ZodEnum<{
9
+ feishu: "feishu";
10
+ lark: "lark";
11
+ }>>;
12
+ appId: z.ZodDefault<z.ZodString>;
13
+ groupPolicy: z.ZodDefault<z.ZodEnum<{
14
+ open: "open";
15
+ allowlist: "allowlist";
16
+ disabled: "disabled";
17
+ }>>;
18
+ requireMention: z.ZodDefault<z.ZodBoolean>;
19
+ }, z.core.$strip>;
20
+ llm: z.ZodObject<{
21
+ baseUrl: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>>;
22
+ model: z.ZodDefault<z.ZodString>;
23
+ }, z.core.$strip>;
24
+ embedding: z.ZodObject<{
25
+ baseUrl: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>>;
26
+ model: z.ZodDefault<z.ZodString>;
27
+ dimension: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
28
+ }, z.core.$strip>;
29
+ storage: z.ZodObject<{
30
+ dataDir: z.ZodDefault<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ web: z.ZodObject<{
33
+ host: z.ZodDefault<z.ZodString>;
34
+ port: z.ZodDefault<z.ZodNumber>;
35
+ }, z.core.$strip>;
36
+ schedules: z.ZodObject<{
37
+ indexing: z.ZodDefault<z.ZodString>;
38
+ }, z.core.$strip>;
39
+ }, z.core.$strip>;
40
+ declare const appSecretsSchema: z.ZodObject<{
41
+ feishu: z.ZodObject<{
42
+ appSecret: z.ZodDefault<z.ZodString>;
43
+ }, z.core.$strip>;
44
+ llm: z.ZodObject<{
45
+ apiKey: z.ZodDefault<z.ZodString>;
46
+ }, z.core.$strip>;
47
+ embedding: z.ZodObject<{
48
+ apiKey: z.ZodDefault<z.ZodString>;
49
+ }, z.core.$strip>;
50
+ }, z.core.$strip>;
51
+ type AppConfig = z.infer<typeof appConfigSchema>;
52
+ type AppSecrets = z.infer<typeof appSecretsSchema>;
53
+ declare function createDefaultConfig(): AppConfig;
54
+ declare function createDefaultSecrets(): AppSecrets;
55
+
56
+ declare function loadConfig(): Promise<AppConfig>;
57
+ declare function saveConfig(config: AppConfig): Promise<void>;
58
+ declare function loadSecrets(): Promise<AppSecrets>;
59
+ declare function saveSecrets(secrets: AppSecrets): Promise<void>;
60
+ declare function ensureConfigFiles(): Promise<{
61
+ config: AppConfig;
62
+ secrets: AppSecrets;
63
+ }>;
64
+ declare function resetConfigFiles(): Promise<void>;
65
+ declare function maskSecret(value: string): string;
66
+
67
+ declare function applySecretInput(currentValue: string, nextValue: string | undefined): string;
68
+ declare function resolveEmbeddingApiKey(input: {
69
+ currentEmbeddingKey: string;
70
+ nextEmbeddingKey?: string;
71
+ llmApiKey: string;
72
+ }): string;
73
+
74
+ type SqliteDatabase = Database.Database;
75
+ declare function getDatabasePath(config: AppConfig): string;
76
+ declare function openDatabase(config: AppConfig): SqliteDatabase;
77
+ declare function migrateDatabase(database: SqliteDatabase): void;
78
+
79
+ type DeleteTargetType = "message" | "chat" | "file";
80
+ interface DeleteLocalDataResult {
81
+ targetType: DeleteTargetType;
82
+ targetId: string;
83
+ deletedMessages: number;
84
+ deletedChunks: number;
85
+ deletedFileJobs: number;
86
+ deletedChats: number;
87
+ deletedStoredFiles: string[];
88
+ skippedStoredFiles: string[];
89
+ }
90
+ declare function deleteLocalData(input: {
91
+ config: AppConfig;
92
+ database: SqliteDatabase;
93
+ targetType: DeleteTargetType;
94
+ targetId: string;
95
+ }): Promise<DeleteLocalDataResult>;
96
+
97
+ type DoctorStatus = "pass" | "warn" | "fail";
98
+ interface DoctorCheck {
99
+ name: string;
100
+ status: DoctorStatus;
101
+ message: string;
102
+ }
103
+ interface DoctorOptions {
104
+ online?: boolean;
105
+ }
106
+ declare function runDoctor(config: AppConfig, secrets: AppSecrets, options?: DoctorOptions): Promise<DoctorCheck[]>;
107
+ declare function formatDoctorChecks(checks: DoctorCheck[]): string;
108
+
109
+ interface DataExportResult {
110
+ outputPath: string;
111
+ chats: number;
112
+ messages: number;
113
+ chunks: number;
114
+ fileJobs: number;
115
+ }
116
+ declare function exportLocalData(input: {
117
+ config: AppConfig;
118
+ database: SqliteDatabase;
119
+ outputPath?: string;
120
+ exportedAt?: string;
121
+ }): Promise<DataExportResult>;
122
+
123
+ interface DataRestoreResult {
124
+ inputPath: string;
125
+ mode: "merge" | "replace";
126
+ chats: number;
127
+ messages: number;
128
+ chunks: number;
129
+ fileJobs: number;
130
+ }
131
+ declare function restoreLocalData(input: {
132
+ database: SqliteDatabase;
133
+ inputPath: string;
134
+ replace?: boolean;
135
+ }): Promise<DataRestoreResult>;
136
+
137
+ interface ChatRecord {
138
+ id: string;
139
+ platform: string;
140
+ platformChatId: string;
141
+ name: string;
142
+ createdAt: string;
143
+ updatedAt: string;
144
+ }
145
+ interface MessageRecord {
146
+ id: string;
147
+ platform: string;
148
+ platformMessageId: string;
149
+ chatId: string;
150
+ senderId: string;
151
+ senderName: string;
152
+ messageType: string;
153
+ text: string;
154
+ rawPayloadJson: string;
155
+ sentAt: string;
156
+ receivedAt: string;
157
+ createdAt: string;
158
+ }
159
+ interface FileRecord {
160
+ messageId: string;
161
+ fileName: string;
162
+ sourcePath?: string;
163
+ storedPath?: string;
164
+ bytes?: number;
165
+ characters: number;
166
+ parser?: string;
167
+ parserWarnings?: string[];
168
+ importedAt: string;
169
+ }
170
+ interface IngestMessageInput {
171
+ platform: string;
172
+ platformChatId: string;
173
+ chatName: string;
174
+ platformMessageId: string;
175
+ senderId: string;
176
+ senderName: string;
177
+ messageType: string;
178
+ text: string;
179
+ rawPayload?: unknown;
180
+ sentAt: string;
181
+ }
182
+ interface MessageSearchResult {
183
+ chunkId: string;
184
+ messageId: string;
185
+ platform: string;
186
+ text: string;
187
+ score: number;
188
+ messageType: string;
189
+ chatName: string;
190
+ senderName: string;
191
+ sentAt: string;
192
+ }
193
+
194
+ type JsonObject = Record<string, unknown>;
195
+ interface FeishuAttachmentMetadata {
196
+ platform: "feishu";
197
+ kind: "file" | "image" | "audio" | "media";
198
+ fileKey: string;
199
+ fileName?: string;
200
+ mimeType?: string;
201
+ size?: number;
202
+ }
203
+ interface FeishuReceiveMessageEvent {
204
+ event?: {
205
+ sender?: {
206
+ sender_id?: {
207
+ open_id?: string;
208
+ user_id?: string;
209
+ union_id?: string;
210
+ };
211
+ };
212
+ message?: {
213
+ message_id?: string;
214
+ chat_id?: string;
215
+ chat_type?: string;
216
+ create_time?: string;
217
+ message_type?: string;
218
+ content?: string;
219
+ mentions?: Array<{
220
+ name?: string;
221
+ key?: string;
222
+ id?: {
223
+ open_id?: string;
224
+ user_id?: string;
225
+ union_id?: string;
226
+ };
227
+ }>;
228
+ };
229
+ };
230
+ }
231
+ declare function extractFeishuAttachment(messageType: string, content: JsonObject): FeishuAttachmentMetadata | undefined;
232
+ declare function normalizeFeishuReceiveMessageEvent(payload: FeishuReceiveMessageEvent): IngestMessageInput | null;
233
+
234
+ interface DownloadResponseLike {
235
+ writeFile(filePath: string): Promise<unknown>;
236
+ }
237
+ interface FeishuResourceClientLike {
238
+ im: {
239
+ v1?: {
240
+ messageResource?: {
241
+ get(payload: {
242
+ params: {
243
+ type: string;
244
+ };
245
+ path: {
246
+ message_id: string;
247
+ file_key: string;
248
+ };
249
+ }): Promise<DownloadResponseLike>;
250
+ };
251
+ };
252
+ messageResource?: {
253
+ get(payload: {
254
+ params: {
255
+ type: string;
256
+ };
257
+ path: {
258
+ message_id: string;
259
+ file_key: string;
260
+ };
261
+ }): Promise<DownloadResponseLike>;
262
+ };
263
+ };
264
+ }
265
+ interface FeishuDownloadResourceInput {
266
+ messageId: string;
267
+ attachment: FeishuAttachmentMetadata;
268
+ }
269
+ interface FeishuDownloadedResource {
270
+ messageId: string;
271
+ fileKey: string;
272
+ fileName: string;
273
+ resourceType: string;
274
+ storedPath: string;
275
+ }
276
+ declare class FeishuResourceDownloader {
277
+ private readonly client;
278
+ private readonly dataDir;
279
+ constructor(client: FeishuResourceClientLike, dataDir: string);
280
+ static fromConfig(config: AppConfig, secrets: AppSecrets): FeishuResourceDownloader;
281
+ download(input: FeishuDownloadResourceInput): Promise<FeishuDownloadedResource>;
282
+ }
283
+
284
+ declare class MessageRepository {
285
+ private readonly database;
286
+ constructor(database: SqliteDatabase);
287
+ ingest(input: IngestMessageInput): string;
288
+ listRecentMessages(limit?: number): MessageSearchResult[];
289
+ listAllMessageChunks(limit?: number): MessageSearchResult[];
290
+ listMessageChunksByMessageIds(messageIds: string[], limit?: number): MessageSearchResult[];
291
+ searchMessages(query: string, limit?: number, options?: {
292
+ excludeMessageIds?: string[];
293
+ }): MessageSearchResult[];
294
+ getChatCount(): number;
295
+ getMessageCount(): number;
296
+ hasPlatformMessage(platform: string, platformMessageId: string): boolean;
297
+ listChats(): ChatRecord[];
298
+ listFiles(limit?: number): FileRecord[];
299
+ }
300
+
301
+ type FileJobStatus = "processing" | "indexed" | "failed";
302
+ interface FileJobRecord {
303
+ id: string;
304
+ sourcePath: string;
305
+ storedPath?: string;
306
+ fileName: string;
307
+ status: FileJobStatus;
308
+ parser?: string;
309
+ messageId?: string;
310
+ bytes?: number;
311
+ characters?: number;
312
+ warnings: string[];
313
+ error?: string;
314
+ createdAt: string;
315
+ updatedAt: string;
316
+ }
317
+ declare class FileJobRepository {
318
+ private readonly database;
319
+ constructor(database: SqliteDatabase);
320
+ start(input: {
321
+ sourcePath: string;
322
+ fileName?: string;
323
+ }): string;
324
+ complete(input: {
325
+ id: string;
326
+ storedPath: string;
327
+ parser: string;
328
+ messageId: string;
329
+ bytes: number;
330
+ characters: number;
331
+ warnings: string[];
332
+ }): void;
333
+ fail(input: {
334
+ id: string;
335
+ error: string;
336
+ }): void;
337
+ get(id: string): FileJobRecord | null;
338
+ list(limit?: number, options?: {
339
+ status?: FileJobStatus;
340
+ }): FileJobRecord[];
341
+ private listByWhere;
342
+ }
343
+
344
+ interface IngestLocalFileResult {
345
+ messageId: string;
346
+ sourcePath: string;
347
+ storedPath: string;
348
+ fileName: string;
349
+ bytes: number;
350
+ characters: number;
351
+ parser: string;
352
+ warnings: string[];
353
+ jobId?: string;
354
+ }
355
+ declare function isSupportedTextFile(filePath: string): boolean;
356
+ declare function ingestLocalFile(input: {
357
+ config: AppConfig;
358
+ messages: MessageRepository;
359
+ filePath: string;
360
+ jobs?: FileJobRepository;
361
+ }): Promise<IngestLocalFileResult>;
362
+
363
+ interface GatewayIngestResult {
364
+ accepted: boolean;
365
+ messageId?: string;
366
+ message?: IngestMessageInput;
367
+ duplicate?: boolean;
368
+ reason?: string;
369
+ }
370
+ interface GatewayAttachmentIngestResult {
371
+ downloaded?: FeishuDownloadedResource;
372
+ indexedMessageId?: string;
373
+ vectorIndexed?: {
374
+ chunks: number;
375
+ vectors: number;
376
+ };
377
+ skippedReason?: string;
378
+ }
379
+ interface GatewayIngestAndDownloadResult extends GatewayIngestResult {
380
+ attachment?: GatewayAttachmentIngestResult;
381
+ }
382
+ declare class GatewayIngestor {
383
+ private readonly messages;
384
+ private readonly jobs;
385
+ constructor(database: SqliteDatabase);
386
+ ingestFeishuEvent(payload: FeishuReceiveMessageEvent): GatewayIngestResult;
387
+ ingestFeishuEventAndDownloadAttachments(input: {
388
+ payload: FeishuReceiveMessageEvent;
389
+ downloader: FeishuResourceDownloader;
390
+ config: Parameters<typeof ingestLocalFile>[0]["config"];
391
+ vectorIndexMessage?: (messageId: string) => Promise<{
392
+ chunks: number;
393
+ vectors: number;
394
+ }>;
395
+ }): Promise<GatewayIngestAndDownloadResult>;
396
+ }
397
+
398
+ type SourceType = "message" | "file" | "image" | "audio" | "link" | "feishu_doc";
399
+ interface EvidenceSource {
400
+ type: SourceType;
401
+ label: string;
402
+ timestamp?: string;
403
+ sender?: string;
404
+ location?: string;
405
+ }
406
+ interface EvidenceBlock {
407
+ id: string;
408
+ text: string;
409
+ score: number;
410
+ source: EvidenceSource;
411
+ }
412
+ interface Citation {
413
+ marker: string;
414
+ evidenceId: string;
415
+ source: EvidenceSource;
416
+ text: string;
417
+ }
418
+ interface GroundedAnswer {
419
+ answer: string;
420
+ citations: Citation[];
421
+ }
422
+ interface ChatMessage {
423
+ role: "system" | "user" | "assistant";
424
+ content: string;
425
+ }
426
+ interface ChatModel {
427
+ complete(messages: ChatMessage[]): Promise<string>;
428
+ }
429
+
430
+ interface MessageSender {
431
+ sendTextToChat(chatId: string, text: string): Promise<void>;
432
+ replyTextToMessage?(messageId: string, text: string): Promise<void>;
433
+ addReactionToMessage?(messageId: string, emojiType: string): Promise<void>;
434
+ }
435
+ interface FeishuClientLike {
436
+ im: {
437
+ v1?: {
438
+ message: {
439
+ create(payload: {
440
+ data: {
441
+ receive_id: string;
442
+ msg_type: string;
443
+ content: string;
444
+ };
445
+ params: {
446
+ receive_id_type: "chat_id";
447
+ };
448
+ }): Promise<unknown>;
449
+ reply?: (payload: {
450
+ path: {
451
+ message_id: string;
452
+ };
453
+ data: {
454
+ msg_type: string;
455
+ content: string;
456
+ };
457
+ }) => Promise<unknown>;
458
+ };
459
+ messageReaction?: {
460
+ create(payload: {
461
+ path: {
462
+ message_id: string;
463
+ };
464
+ data: {
465
+ reaction_type: {
466
+ emoji_type: string;
467
+ };
468
+ };
469
+ }): Promise<unknown>;
470
+ };
471
+ };
472
+ message?: {
473
+ create(payload: {
474
+ data: {
475
+ receive_id: string;
476
+ msg_type: string;
477
+ content: string;
478
+ };
479
+ params: {
480
+ receive_id_type: "chat_id";
481
+ };
482
+ }): Promise<unknown>;
483
+ reply?: (payload: {
484
+ path: {
485
+ message_id: string;
486
+ };
487
+ data: {
488
+ msg_type: string;
489
+ content: string;
490
+ };
491
+ }) => Promise<unknown>;
492
+ };
493
+ };
494
+ }
495
+ declare function mapDomain(domain: AppConfig["feishu"]["domain"]): lark.Domain;
496
+ declare class FeishuMessageSender implements MessageSender {
497
+ private readonly client;
498
+ constructor(client: FeishuClientLike);
499
+ static fromConfig(config: AppConfig, secrets: AppSecrets): FeishuMessageSender;
500
+ sendTextToChat(chatId: string, text: string): Promise<void>;
501
+ replyTextToMessage(messageId: string, text: string): Promise<void>;
502
+ addReactionToMessage(messageId: string, emojiType: string): Promise<void>;
503
+ }
504
+
505
+ interface FeishuQuestionHandlerOptions {
506
+ config: AppConfig;
507
+ secrets: AppSecrets;
508
+ database: SqliteDatabase;
509
+ model: ChatModel;
510
+ sender: MessageSender;
511
+ thinkingEmojiType?: string;
512
+ }
513
+ interface FeishuQuestionDecision {
514
+ shouldAnswer: boolean;
515
+ question?: string;
516
+ chatId?: string;
517
+ reason?: string;
518
+ }
519
+ declare function isFeishuMessageAddressedToBot(payload: FeishuReceiveMessageEvent): boolean;
520
+ declare function getFeishuQuestionDecision(payload: FeishuReceiveMessageEvent, config: AppConfig): FeishuQuestionDecision;
521
+ declare class FeishuQuestionHandler {
522
+ private readonly options;
523
+ constructor(options: FeishuQuestionHandlerOptions);
524
+ private sendResponse;
525
+ private acknowledgeQuestion;
526
+ handle(payload: FeishuReceiveMessageEvent, options?: {
527
+ excludeMessageIds?: string[];
528
+ }): Promise<FeishuQuestionDecision>;
529
+ }
530
+
531
+ interface FeishuGatewayRuntime {
532
+ start(): Promise<void>;
533
+ stop(): void;
534
+ }
535
+ interface WsClientLike {
536
+ start(params: {
537
+ eventDispatcher: lark.EventDispatcher;
538
+ }): Promise<void>;
539
+ close(params?: {
540
+ force?: boolean;
541
+ }): void;
542
+ }
543
+ interface FeishuGatewayOptions {
544
+ config: AppConfig;
545
+ secrets: AppSecrets;
546
+ ingestor: GatewayIngestor;
547
+ questionHandler?: FeishuQuestionHandler;
548
+ resourceDownloader?: FeishuResourceDownloader;
549
+ attachmentVectorIndexer?: (messageId: string) => Promise<{
550
+ chunks: number;
551
+ vectors: number;
552
+ }>;
553
+ wsClientFactory?: (params: {
554
+ appId: string;
555
+ appSecret: string;
556
+ domain: lark.Domain;
557
+ onReady: () => void;
558
+ onError: (error: Error) => void;
559
+ onReconnecting: () => void;
560
+ onReconnected: () => void;
561
+ }) => WsClientLike;
562
+ }
563
+ declare function createFeishuEventDispatcher(options: {
564
+ config: AppConfig;
565
+ ingestor: GatewayIngestor;
566
+ questionHandler?: FeishuQuestionHandler;
567
+ resourceDownloader?: FeishuResourceDownloader;
568
+ attachmentVectorIndexer?: (messageId: string) => Promise<{
569
+ chunks: number;
570
+ vectors: number;
571
+ }>;
572
+ }): lark.EventDispatcher;
573
+ declare function createFeishuGateway(options: FeishuGatewayOptions): FeishuGatewayRuntime;
574
+
575
+ interface ParsedFile {
576
+ text: string;
577
+ parser: "text" | "docx" | "pdf";
578
+ warnings: string[];
579
+ }
580
+ declare function isSupportedParseFile(filePath: string): boolean;
581
+ declare function describeSupportedParseTypes(): string;
582
+ declare function parseFileToText(filePath: string): Promise<ParsedFile>;
583
+
584
+ interface GatewayPidRecord {
585
+ pid: number;
586
+ startedAt: string;
587
+ command: string;
588
+ }
589
+ interface GatewayRuntimeState {
590
+ pidFile: string;
591
+ record: GatewayPidRecord | null;
592
+ running: boolean;
593
+ stale: boolean;
594
+ }
595
+ interface StopGatewayResult {
596
+ stopped: boolean;
597
+ message: string;
598
+ }
599
+ declare function getGatewayPidPath(): string;
600
+ declare function isProcessRunning(pid: number): boolean;
601
+ declare function readGatewayPidRecord(pidFile?: string): GatewayPidRecord | null;
602
+ declare function writeGatewayPidRecord(pidFile?: string, record?: GatewayPidRecord): void;
603
+ declare function removeGatewayPidRecord(pidFile?: string): void;
604
+ declare function getGatewayRuntimeState(pidFile?: string): GatewayRuntimeState;
605
+ declare function stopGatewayProcess(pidFile?: string): StopGatewayResult;
606
+
607
+ interface EmbeddingModel {
608
+ embed(text: string): Promise<number[]>;
609
+ embedBatch(texts: string[]): Promise<number[][]>;
610
+ }
611
+ declare function cosineSimilarity(left: number[], right: number[]): number;
612
+
613
+ interface OpenAICompatibleChatOptions {
614
+ baseUrl: string;
615
+ apiKey: string;
616
+ model: string;
617
+ temperature?: number;
618
+ }
619
+ declare class OpenAICompatibleChatModel implements ChatModel {
620
+ private readonly options;
621
+ constructor(options: OpenAICompatibleChatOptions);
622
+ complete(messages: ChatMessage[]): Promise<string>;
623
+ }
624
+ interface OpenAICompatibleEmbeddingOptions {
625
+ baseUrl: string;
626
+ apiKey: string;
627
+ model: string;
628
+ }
629
+ declare class OpenAICompatibleEmbeddingModel implements EmbeddingModel {
630
+ private readonly options;
631
+ constructor(options: OpenAICompatibleEmbeddingOptions);
632
+ embed(text: string): Promise<number[]>;
633
+ embedBatch(texts: string[]): Promise<number[][]>;
634
+ }
635
+ declare function createChatModel(config: AppConfig, secrets: AppSecrets): OpenAICompatibleChatModel;
636
+ declare function createEmbeddingModel(config: AppConfig, secrets: AppSecrets): OpenAICompatibleEmbeddingModel;
637
+
638
+ interface LogFileInfo {
639
+ name: string;
640
+ path: string;
641
+ updatedAt: Date;
642
+ bytes: number;
643
+ }
644
+ interface LogTailResult {
645
+ file: LogFileInfo;
646
+ content: string;
647
+ }
648
+ declare function getLogsDirectory(): string;
649
+ declare function resolveLogPath(fileName: string, logsDir?: string): string;
650
+ declare function normalizeLineCount(value: number | string | undefined, fallback?: number): number;
651
+ declare function listLogFiles(logsDir?: string): Promise<LogFileInfo[]>;
652
+ declare function readLogTail(input: {
653
+ filePath: string;
654
+ lines?: number;
655
+ }): Promise<LogTailResult>;
656
+ declare function readLatestLogTail(input?: {
657
+ fileName?: string;
658
+ lines?: number;
659
+ logsDir?: string;
660
+ }): Promise<LogTailResult | null>;
661
+ declare function followLogFile(input: {
662
+ filePath: string;
663
+ onChunk: (chunk: string) => void;
664
+ onError?: (error: Error) => void;
665
+ }): Promise<() => void>;
666
+
667
+ interface TextChunk {
668
+ index: number;
669
+ text: string;
670
+ }
671
+ declare function chunkText(text: string, maxChars?: number, overlapChars?: number): TextChunk[];
672
+
673
+ interface BuildEvidencePromptOptions {
674
+ maxEvidenceBlocks?: number;
675
+ maxCharsPerBlock?: number;
676
+ }
677
+ interface EvidencePrompt {
678
+ messages: ChatMessage[];
679
+ citations: Citation[];
680
+ }
681
+ declare function rankEvidenceForPrompt(evidence: EvidenceBlock[]): EvidenceBlock[];
682
+ declare function buildEvidencePrompt(question: string, evidence: EvidenceBlock[], options?: BuildEvidencePromptOptions): EvidencePrompt;
683
+ declare function generateGroundedAnswer(input: {
684
+ question: string;
685
+ evidence: EvidenceBlock[];
686
+ model: ChatModel;
687
+ }): Promise<GroundedAnswer>;
688
+
689
+ declare function formatCitation(citation: Citation, options?: {
690
+ maxTextLength?: number;
691
+ }): string;
692
+ declare function formatCitations(citations: Citation[], options?: {
693
+ maxTextLength?: number;
694
+ }): string;
695
+
696
+ interface Retriever {
697
+ retrieve(question: string): Promise<EvidenceBlock[]>;
698
+ }
699
+
700
+ interface HybridRetrieverOptions {
701
+ limit?: number;
702
+ }
703
+ declare class HybridRetriever implements Retriever {
704
+ private readonly retrievers;
705
+ private readonly options;
706
+ constructor(retrievers: Retriever[], options?: HybridRetrieverOptions);
707
+ retrieve(question: string): Promise<EvidenceBlock[]>;
708
+ }
709
+
710
+ interface VectorRecord {
711
+ id: string;
712
+ vector: number[];
713
+ evidence: EvidenceBlock;
714
+ }
715
+ interface VectorSearchResult extends EvidenceBlock {
716
+ vectorScore: number;
717
+ }
718
+ interface VectorStore {
719
+ upsert(records: VectorRecord[]): Promise<void>;
720
+ search(vector: number[], limit: number): Promise<VectorSearchResult[]>;
721
+ }
722
+ declare class MemoryVectorStore implements VectorStore {
723
+ private readonly records;
724
+ upsert(records: VectorRecord[]): Promise<void>;
725
+ search(vector: number[], limit: number): Promise<VectorSearchResult[]>;
726
+ }
727
+
728
+ interface VectorIndexStats {
729
+ chunks: number;
730
+ vectors: number;
731
+ }
732
+ declare function indexMessageChunks(input: {
733
+ messages: MessageRepository;
734
+ embedding: EmbeddingModel;
735
+ store: VectorStore;
736
+ limit?: number;
737
+ messageIds?: string[];
738
+ }): Promise<VectorIndexStats>;
739
+
740
+ declare function getLanceDbPath(config: AppConfig): string;
741
+ declare class LanceDbVectorStore implements VectorStore {
742
+ private readonly connection;
743
+ private readonly tableName;
744
+ private constructor();
745
+ static connect(uri: string, tableName?: string): Promise<LanceDbVectorStore>;
746
+ static connectFromConfig(config: AppConfig, tableName?: string): Promise<LanceDbVectorStore>;
747
+ close(): void;
748
+ upsert(records: VectorRecord[]): Promise<void>;
749
+ search(vector: number[], limit: number): Promise<VectorSearchResult[]>;
750
+ count(): Promise<number>;
751
+ private ensureTable;
752
+ private openTableIfExists;
753
+ }
754
+
755
+ declare function hasEmbeddingConfig(config: AppConfig, secrets: AppSecrets): boolean;
756
+ declare function createHybridRetriever(input: {
757
+ config: AppConfig;
758
+ secrets: AppSecrets;
759
+ messages: MessageRepository;
760
+ excludeMessageIds?: string[];
761
+ }): Promise<{
762
+ retriever: Retriever;
763
+ close: () => void;
764
+ }>;
765
+
766
+ interface ManualMessageIndexResult {
767
+ status: "completed" | "skipped";
768
+ reason?: string;
769
+ chunks: number;
770
+ vectors: number;
771
+ startedAt: string;
772
+ finishedAt: string;
773
+ }
774
+ declare function processMessagesNow(input: {
775
+ config: AppConfig;
776
+ secrets: AppSecrets;
777
+ database: SqliteDatabase;
778
+ limit?: number;
779
+ }): Promise<ManualMessageIndexResult>;
780
+
781
+ declare class MessageFtsRetriever implements Retriever {
782
+ private readonly messages;
783
+ private readonly options;
784
+ constructor(messages: MessageRepository, options?: {
785
+ excludeMessageIds?: string[];
786
+ });
787
+ retrieve(question: string): Promise<EvidenceBlock[]>;
788
+ }
789
+
790
+ interface AskWithRagInput {
791
+ question: string;
792
+ retriever: Retriever;
793
+ model: ChatModel;
794
+ }
795
+ declare function askWithRag(input: AskWithRagInput): Promise<GroundedAnswer>;
796
+
797
+ declare class VectorRetriever implements Retriever {
798
+ private readonly embedding;
799
+ private readonly store;
800
+ private readonly limit;
801
+ constructor(embedding: EmbeddingModel, store: VectorStore, limit?: number);
802
+ retrieve(question: string): Promise<EvidenceBlock[]>;
803
+ }
804
+
805
+ declare function createWebApp(config: AppConfig): FastifyInstance;
806
+ declare function startWebServer(config: AppConfig): Promise<void>;
807
+
808
+ export { type AppConfig, type AppSecrets, type AskWithRagInput, type BuildEvidencePromptOptions, type ChatMessage, type ChatModel, type ChatRecord, type Citation, type DataExportResult, type DataRestoreResult, type DeleteLocalDataResult, type DeleteTargetType, type DoctorCheck, type DoctorOptions, type DoctorStatus, type EmbeddingModel, type EvidenceBlock, type EvidencePrompt, type EvidenceSource, type FeishuAttachmentMetadata, type FeishuDownloadResourceInput, type FeishuDownloadedResource, type FeishuGatewayOptions, type FeishuGatewayRuntime, FeishuMessageSender, type FeishuQuestionDecision, FeishuQuestionHandler, type FeishuQuestionHandlerOptions, type FeishuReceiveMessageEvent, FeishuResourceDownloader, type FileJobRecord, FileJobRepository, type FileJobStatus, type FileRecord, type GatewayAttachmentIngestResult, type GatewayIngestAndDownloadResult, type GatewayIngestResult, GatewayIngestor, type GatewayPidRecord, type GatewayRuntimeState, type GroundedAnswer, HybridRetriever, type HybridRetrieverOptions, type IngestLocalFileResult, type IngestMessageInput, LanceDbVectorStore, type LogFileInfo, type LogTailResult, type ManualMessageIndexResult, MemoryVectorStore, MessageFtsRetriever, type MessageRecord, MessageRepository, type MessageSearchResult, type MessageSender, OpenAICompatibleChatModel, type OpenAICompatibleChatOptions, OpenAICompatibleEmbeddingModel, type OpenAICompatibleEmbeddingOptions, type ParsedFile, type SourceType, type SqliteDatabase, type StopGatewayResult, type TextChunk, type VectorIndexStats, type VectorRecord, VectorRetriever, type VectorSearchResult, type VectorStore, appConfigSchema, appSecretsSchema, applySecretInput, askWithRag, buildEvidencePrompt, chunkText, cosineSimilarity, createChatModel, createDefaultConfig, createDefaultSecrets, createEmbeddingModel, createFeishuEventDispatcher, createFeishuGateway, createHybridRetriever, createWebApp, deleteLocalData, describeSupportedParseTypes, ensureConfigFiles, exportLocalData, extractFeishuAttachment, followLogFile, formatCitation, formatCitations, formatDoctorChecks, generateGroundedAnswer, getDatabasePath, getFeishuQuestionDecision, getGatewayPidPath, getGatewayRuntimeState, getLanceDbPath, getLogsDirectory, hasEmbeddingConfig, indexMessageChunks, ingestLocalFile, isFeishuMessageAddressedToBot, isProcessRunning, isSupportedParseFile, isSupportedTextFile, listLogFiles, loadConfig, loadSecrets, mapDomain, maskSecret, migrateDatabase, normalizeFeishuReceiveMessageEvent, normalizeLineCount, openDatabase, parseFileToText, processMessagesNow, rankEvidenceForPrompt, readGatewayPidRecord, readLatestLogTail, readLogTail, removeGatewayPidRecord, resetConfigFiles, resolveEmbeddingApiKey, resolveLogPath, restoreLocalData, runDoctor, saveConfig, saveSecrets, startWebServer, stopGatewayProcess, writeGatewayPidRecord };