@wirecat/cli-core 0.500.0 → 0.501.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.
- package/CHANGELOG.md +28 -0
- package/dist/calendar/mcp/index.d.ts +12 -12
- package/dist/cli/envelope.d.ts +11 -1
- package/dist/cli/envelope.js +15 -3
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/meeting-preparation-answer.d.ts +8 -0
- package/dist/cli/meeting-preparation-projects.d.ts +8 -0
- package/dist/cli/meeting-preparation-read.d.ts +8 -0
- package/dist/cli/meeting-series-inventory-read.d.ts +8 -0
- package/dist/cli/messenger/messages-search-command.js +4 -1
- package/dist/cli/messenger/search-answers.d.ts +3 -0
- package/dist/cli/messenger/search-answers.js +6 -0
- package/dist/cli/messenger/search-command.js +4 -1
- package/dist/entities/index.d.ts +24 -0
- package/dist/entities/meeting-preparation.d.ts +8 -0
- package/dist/entities/meeting-series-inventory.d.ts +8 -0
- package/dist/mcp/meeting-preparation.d.ts +8 -0
- package/dist/mcp/meeting-series-inventory.d.ts +8 -0
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/meetings/project-evidence-answer.d.ts +8 -0
- package/dist/search/total.d.ts +13 -0
- package/dist/search/total.js +14 -0
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +2 -1
- package/dist/services/messages-archive-search.js +13 -3
- package/dist/services/messages-discovery.d.ts +6 -0
- package/dist/services/messages-discovery.js +14 -11
- package/dist/services/messages-search.d.ts +5 -0
- package/dist/services/messages-search.js +22 -0
- package/dist/services/messages.d.ts +3 -0
- package/dist/services/notes-search.d.ts +14 -2
- package/dist/services/notes-search.js +101 -19
- package/dist/store/queries.d.ts +2 -0
- package/dist/store/queries.js +1 -0
- package/dist/store/sqlite/lucene.d.ts +5 -0
- package/dist/store/sqlite/lucene.js +17 -0
- package/dist/store/sqlite/note-search.d.ts +18 -0
- package/dist/store/sqlite/note-search.js +40 -4
- package/package.json +3 -3
|
@@ -38,6 +38,7 @@ export declare const meetingTools: ({
|
|
|
38
38
|
max_read_bytes?: undefined;
|
|
39
39
|
cursor?: undefined;
|
|
40
40
|
} | {
|
|
41
|
+
limit?: undefined;
|
|
41
42
|
event_id?: undefined;
|
|
42
43
|
series_id?: undefined;
|
|
43
44
|
page?: undefined;
|
|
@@ -53,11 +54,11 @@ export declare const meetingTools: ({
|
|
|
53
54
|
format?: undefined;
|
|
54
55
|
transcript_id?: undefined;
|
|
55
56
|
query?: undefined;
|
|
56
|
-
limit?: undefined;
|
|
57
57
|
scan_limit?: undefined;
|
|
58
58
|
max_read_bytes?: undefined;
|
|
59
59
|
cursor?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
limit?: undefined;
|
|
61
62
|
event_id?: undefined;
|
|
62
63
|
series_id?: undefined;
|
|
63
64
|
page?: undefined;
|
|
@@ -75,11 +76,11 @@ export declare const meetingTools: ({
|
|
|
75
76
|
format?: undefined;
|
|
76
77
|
transcript_id?: undefined;
|
|
77
78
|
query?: undefined;
|
|
78
|
-
limit?: undefined;
|
|
79
79
|
scan_limit?: undefined;
|
|
80
80
|
max_read_bytes?: undefined;
|
|
81
81
|
cursor?: undefined;
|
|
82
82
|
} | {
|
|
83
|
+
limit?: undefined;
|
|
83
84
|
event_id?: undefined;
|
|
84
85
|
series_id?: undefined;
|
|
85
86
|
page?: undefined;
|
|
@@ -101,7 +102,6 @@ export declare const meetingTools: ({
|
|
|
101
102
|
minimum: number;
|
|
102
103
|
};
|
|
103
104
|
query?: undefined;
|
|
104
|
-
limit?: undefined;
|
|
105
105
|
scan_limit?: undefined;
|
|
106
106
|
max_read_bytes?: undefined;
|
|
107
107
|
cursor?: undefined;
|
|
@@ -188,6 +188,7 @@ export declare const meetingTools: ({
|
|
|
188
188
|
max_read_bytes?: undefined;
|
|
189
189
|
cursor?: undefined;
|
|
190
190
|
} | {
|
|
191
|
+
limit?: undefined;
|
|
191
192
|
event_id?: undefined;
|
|
192
193
|
series_id?: undefined;
|
|
193
194
|
page?: undefined;
|
|
@@ -203,11 +204,11 @@ export declare const meetingTools: ({
|
|
|
203
204
|
format?: undefined;
|
|
204
205
|
transcript_id?: undefined;
|
|
205
206
|
query?: undefined;
|
|
206
|
-
limit?: undefined;
|
|
207
207
|
scan_limit?: undefined;
|
|
208
208
|
max_read_bytes?: undefined;
|
|
209
209
|
cursor?: undefined;
|
|
210
210
|
} | {
|
|
211
|
+
limit?: undefined;
|
|
211
212
|
event_id?: undefined;
|
|
212
213
|
series_id?: undefined;
|
|
213
214
|
page?: undefined;
|
|
@@ -225,11 +226,11 @@ export declare const meetingTools: ({
|
|
|
225
226
|
format?: undefined;
|
|
226
227
|
transcript_id?: undefined;
|
|
227
228
|
query?: undefined;
|
|
228
|
-
limit?: undefined;
|
|
229
229
|
scan_limit?: undefined;
|
|
230
230
|
max_read_bytes?: undefined;
|
|
231
231
|
cursor?: undefined;
|
|
232
232
|
} | {
|
|
233
|
+
limit?: undefined;
|
|
233
234
|
event_id?: undefined;
|
|
234
235
|
series_id?: undefined;
|
|
235
236
|
page?: undefined;
|
|
@@ -251,7 +252,6 @@ export declare const meetingTools: ({
|
|
|
251
252
|
minimum: number;
|
|
252
253
|
};
|
|
253
254
|
query?: undefined;
|
|
254
|
-
limit?: undefined;
|
|
255
255
|
scan_limit?: undefined;
|
|
256
256
|
max_read_bytes?: undefined;
|
|
257
257
|
cursor?: undefined;
|
|
@@ -338,6 +338,7 @@ export declare const meetingTools: ({
|
|
|
338
338
|
max_read_bytes?: undefined;
|
|
339
339
|
cursor?: undefined;
|
|
340
340
|
} | {
|
|
341
|
+
limit?: undefined;
|
|
341
342
|
event_id?: undefined;
|
|
342
343
|
series_id?: undefined;
|
|
343
344
|
page?: undefined;
|
|
@@ -353,11 +354,11 @@ export declare const meetingTools: ({
|
|
|
353
354
|
format?: undefined;
|
|
354
355
|
transcript_id?: undefined;
|
|
355
356
|
query?: undefined;
|
|
356
|
-
limit?: undefined;
|
|
357
357
|
scan_limit?: undefined;
|
|
358
358
|
max_read_bytes?: undefined;
|
|
359
359
|
cursor?: undefined;
|
|
360
360
|
} | {
|
|
361
|
+
limit?: undefined;
|
|
361
362
|
event_id?: undefined;
|
|
362
363
|
series_id?: undefined;
|
|
363
364
|
page?: undefined;
|
|
@@ -375,11 +376,11 @@ export declare const meetingTools: ({
|
|
|
375
376
|
format?: undefined;
|
|
376
377
|
transcript_id?: undefined;
|
|
377
378
|
query?: undefined;
|
|
378
|
-
limit?: undefined;
|
|
379
379
|
scan_limit?: undefined;
|
|
380
380
|
max_read_bytes?: undefined;
|
|
381
381
|
cursor?: undefined;
|
|
382
382
|
} | {
|
|
383
|
+
limit?: undefined;
|
|
383
384
|
event_id?: undefined;
|
|
384
385
|
series_id?: undefined;
|
|
385
386
|
page?: undefined;
|
|
@@ -401,7 +402,6 @@ export declare const meetingTools: ({
|
|
|
401
402
|
minimum: number;
|
|
402
403
|
};
|
|
403
404
|
query?: undefined;
|
|
404
|
-
limit?: undefined;
|
|
405
405
|
scan_limit?: undefined;
|
|
406
406
|
max_read_bytes?: undefined;
|
|
407
407
|
cursor?: undefined;
|
|
@@ -488,6 +488,7 @@ export declare const meetingTools: ({
|
|
|
488
488
|
max_read_bytes?: undefined;
|
|
489
489
|
cursor?: undefined;
|
|
490
490
|
} | {
|
|
491
|
+
limit?: undefined;
|
|
491
492
|
event_id?: undefined;
|
|
492
493
|
series_id?: undefined;
|
|
493
494
|
page?: undefined;
|
|
@@ -503,11 +504,11 @@ export declare const meetingTools: ({
|
|
|
503
504
|
format?: undefined;
|
|
504
505
|
transcript_id?: undefined;
|
|
505
506
|
query?: undefined;
|
|
506
|
-
limit?: undefined;
|
|
507
507
|
scan_limit?: undefined;
|
|
508
508
|
max_read_bytes?: undefined;
|
|
509
509
|
cursor?: undefined;
|
|
510
510
|
} | {
|
|
511
|
+
limit?: undefined;
|
|
511
512
|
event_id?: undefined;
|
|
512
513
|
series_id?: undefined;
|
|
513
514
|
page?: undefined;
|
|
@@ -525,11 +526,11 @@ export declare const meetingTools: ({
|
|
|
525
526
|
format?: undefined;
|
|
526
527
|
transcript_id?: undefined;
|
|
527
528
|
query?: undefined;
|
|
528
|
-
limit?: undefined;
|
|
529
529
|
scan_limit?: undefined;
|
|
530
530
|
max_read_bytes?: undefined;
|
|
531
531
|
cursor?: undefined;
|
|
532
532
|
} | {
|
|
533
|
+
limit?: undefined;
|
|
533
534
|
event_id?: undefined;
|
|
534
535
|
series_id?: undefined;
|
|
535
536
|
page?: undefined;
|
|
@@ -551,7 +552,6 @@ export declare const meetingTools: ({
|
|
|
551
552
|
minimum: number;
|
|
552
553
|
};
|
|
553
554
|
query?: undefined;
|
|
554
|
-
limit?: undefined;
|
|
555
555
|
scan_limit?: undefined;
|
|
556
556
|
max_read_bytes?: undefined;
|
|
557
557
|
cursor?: undefined;
|
|
@@ -638,6 +638,7 @@ export declare const meetingTools: ({
|
|
|
638
638
|
max_read_bytes?: undefined;
|
|
639
639
|
cursor?: undefined;
|
|
640
640
|
} | {
|
|
641
|
+
limit?: undefined;
|
|
641
642
|
event_id?: undefined;
|
|
642
643
|
series_id?: undefined;
|
|
643
644
|
page?: undefined;
|
|
@@ -653,11 +654,11 @@ export declare const meetingTools: ({
|
|
|
653
654
|
format?: undefined;
|
|
654
655
|
transcript_id?: undefined;
|
|
655
656
|
query?: undefined;
|
|
656
|
-
limit?: undefined;
|
|
657
657
|
scan_limit?: undefined;
|
|
658
658
|
max_read_bytes?: undefined;
|
|
659
659
|
cursor?: undefined;
|
|
660
660
|
} | {
|
|
661
|
+
limit?: undefined;
|
|
661
662
|
event_id?: undefined;
|
|
662
663
|
series_id?: undefined;
|
|
663
664
|
page?: undefined;
|
|
@@ -675,11 +676,11 @@ export declare const meetingTools: ({
|
|
|
675
676
|
format?: undefined;
|
|
676
677
|
transcript_id?: undefined;
|
|
677
678
|
query?: undefined;
|
|
678
|
-
limit?: undefined;
|
|
679
679
|
scan_limit?: undefined;
|
|
680
680
|
max_read_bytes?: undefined;
|
|
681
681
|
cursor?: undefined;
|
|
682
682
|
} | {
|
|
683
|
+
limit?: undefined;
|
|
683
684
|
event_id?: undefined;
|
|
684
685
|
series_id?: undefined;
|
|
685
686
|
page?: undefined;
|
|
@@ -701,7 +702,6 @@ export declare const meetingTools: ({
|
|
|
701
702
|
minimum: number;
|
|
702
703
|
};
|
|
703
704
|
query?: undefined;
|
|
704
|
-
limit?: undefined;
|
|
705
705
|
scan_limit?: undefined;
|
|
706
706
|
max_read_bytes?: undefined;
|
|
707
707
|
cursor?: undefined;
|
|
@@ -60,11 +60,19 @@ export declare const meetingProjectEvidence: v.StrictObjectSchema<{
|
|
|
60
60
|
readonly limit: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>;
|
|
61
61
|
readonly has_more: v.BooleanSchema<undefined>;
|
|
62
62
|
readonly next_page: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 2, undefined>]>, undefined>;
|
|
63
|
+
readonly total: v.OptionalSchema<v.StrictObjectSchema<{
|
|
64
|
+
readonly value: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
65
|
+
readonly relation: v.PicklistSchema<["eq", "gte"], undefined>;
|
|
66
|
+
}, undefined>, undefined>;
|
|
63
67
|
}, undefined>, v.StrictObjectSchema<{
|
|
64
68
|
readonly mode: v.LiteralSchema<"cursor", undefined>;
|
|
65
69
|
readonly limit: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>]>;
|
|
66
70
|
readonly has_more: v.BooleanSchema<undefined>;
|
|
67
71
|
readonly next_cursor: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "must not be empty">]>, undefined>;
|
|
72
|
+
readonly total: v.OptionalSchema<v.StrictObjectSchema<{
|
|
73
|
+
readonly value: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
74
|
+
readonly relation: v.PicklistSchema<["eq", "gte"], undefined>;
|
|
75
|
+
}, undefined>, undefined>;
|
|
68
76
|
}, undefined>], undefined>;
|
|
69
77
|
readonly coverage: v.StrictObjectSchema<{
|
|
70
78
|
readonly archive_completeness: v.LiteralSchema<"unknown", undefined>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** How many records match a search, every page together, shaped as Elasticsearch's `hits.total`. */
|
|
2
|
+
export interface SearchTotal {
|
|
3
|
+
value: number;
|
|
4
|
+
/** `eq`: the exact count. `gte`: at least `value`; counting stopped at the cap or could not be done cheaply. */
|
|
5
|
+
relation: "eq" | "gte";
|
|
6
|
+
}
|
|
7
|
+
/** Matches counted at most, as Elasticsearch's default `track_total_hits`; past it the total is `gte`. */
|
|
8
|
+
export declare const TOTAL_CAP = 10000;
|
|
9
|
+
/**
|
|
10
|
+
* A total from a count capped at `TOTAL_CAP` (`undefined` when it could not be counted), never below what the pages
|
|
11
|
+
* already showed: `shown` hits, and one more when `hasMore`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const totalOf: (count: number | undefined, shown: number, hasMore: boolean, exact?: boolean) => SearchTotal;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Matches counted at most, as Elasticsearch's default `track_total_hits`; past it the total is `gte`. */
|
|
2
|
+
export const TOTAL_CAP = 10_000;
|
|
3
|
+
/**
|
|
4
|
+
* A total from a count capped at `TOTAL_CAP` (`undefined` when it could not be counted), never below what the pages
|
|
5
|
+
* already showed: `shown` hits, and one more when `hasMore`.
|
|
6
|
+
*/
|
|
7
|
+
export const totalOf = (count, shown, hasMore, exact = true) => {
|
|
8
|
+
const least = shown + (hasMore ? 1 : 0);
|
|
9
|
+
if (count === undefined || count < least)
|
|
10
|
+
return { value: least, relation: "gte" };
|
|
11
|
+
if (count > TOTAL_CAP)
|
|
12
|
+
return { value: TOTAL_CAP, relation: "gte" };
|
|
13
|
+
return { value: count, relation: exact ? "eq" : "gte" };
|
|
14
|
+
};
|
package/dist/services/index.d.ts
CHANGED
|
@@ -142,6 +142,7 @@ export declare const servicesFor: (deps: ServiceDeps) => Services;
|
|
|
142
142
|
export { entriesOfList, type ListContext, type ListedEntry } from "../search/account-list.js";
|
|
143
143
|
export { FIELD_VERSION, QUERY_FIELDS, QUERY_OPERATORS, validateAst } from "../search/lucene/registry.js";
|
|
144
144
|
export { QUERY_LIMITS, QUERY_VERSION } from "../search/lucene/types.js";
|
|
145
|
+
export { type SearchTotal, TOTAL_CAP } from "../search/total.js";
|
|
145
146
|
export { TASK_KINDS, TASK_ORIGINS, type TaskKind, type TaskOrigin } from "../tasks/index.js";
|
|
146
147
|
export type { AdminFound, AdminQuery, AdminSelection, AdminStatisticsService } from "./admin-statistics.js";
|
|
147
148
|
export { adminStatisticsService } from "./admin-statistics.js";
|
|
@@ -170,7 +171,7 @@ export type { MeaningLane, MeaningSkipped } from "./messages-discovery.js";
|
|
|
170
171
|
export { meaningLane } from "./messages-meaning.js";
|
|
171
172
|
export type { QueryMetadata, SearchCoverage } from "./messages-search.js";
|
|
172
173
|
export { metadataService } from "./metadata.js";
|
|
173
|
-
export { embedEvents, embedMail, embedNotes, type FoundNote, type LinkedRecord, type NotesEmbedded, type NotesEmbedOptions, type NotesFound, type NotesFoundOf, type NotesQuery, type NotesSearchRequest, nearestNotes, searchNotes, searchNotesOf, searchNotesQuery, storeQuery, } from "./notes-search.js";
|
|
174
|
+
export { embedEvents, embedMail, embedNotes, type FoundNote, type LinkedRecord, type NotesEmbedded, type NotesEmbedOptions, type NotesFound, type NotesFoundOf, type NotesQuery, type NotesSearchRequest, nearestNotes, RANK_WITHIN, searchNotes, searchNotesOf, searchNotesQuery, storeQuery, } from "./notes-search.js";
|
|
174
175
|
export { type PackageUpgradeOutcome, type PackageUpgradePorts, type PackageUpgradeResult, type ServerRestarts, upgradePackage, } from "./package-upgrade.js";
|
|
175
176
|
export { type PersonMeetingContext, type PersonMeetingContextItem, type PersonMeetingContextOptions, type PersonMeetingReadStore, personMeetingContext, } from "./person-meeting-context.js";
|
|
176
177
|
export { privatePeopleService } from "./private-people.js";
|
package/dist/services/index.js
CHANGED
|
@@ -107,6 +107,7 @@ export const servicesFor = (deps) => {
|
|
|
107
107
|
export { entriesOfList } from "../search/account-list.js";
|
|
108
108
|
export { FIELD_VERSION, QUERY_FIELDS, QUERY_OPERATORS, validateAst } from "../search/lucene/registry.js";
|
|
109
109
|
export { QUERY_LIMITS, QUERY_VERSION } from "../search/lucene/types.js";
|
|
110
|
+
export { TOTAL_CAP } from "../search/total.js";
|
|
110
111
|
export { TASK_KINDS, TASK_ORIGINS } from "../tasks/index.js";
|
|
111
112
|
export { adminStatisticsService } from "./admin-statistics.js";
|
|
112
113
|
export { GAP_BOUNDS, gapsService } from "./archive-gaps.js";
|
|
@@ -128,7 +129,7 @@ export { proposeMeetingTask } from "./meeting-task-proposal.js";
|
|
|
128
129
|
export { createTaskFromMeeting, readMeetingTask, } from "./meeting-tasks.js";
|
|
129
130
|
export { meaningLane } from "./messages-meaning.js";
|
|
130
131
|
export { metadataService } from "./metadata.js";
|
|
131
|
-
export { embedEvents, embedMail, embedNotes, nearestNotes, searchNotes, searchNotesOf, searchNotesQuery, storeQuery, } from "./notes-search.js";
|
|
132
|
+
export { embedEvents, embedMail, embedNotes, nearestNotes, RANK_WITHIN, searchNotes, searchNotesOf, searchNotesQuery, storeQuery, } from "./notes-search.js";
|
|
132
133
|
export { upgradePackage, } from "./package-upgrade.js";
|
|
133
134
|
export { personMeetingContext, } from "./person-meeting-context.js";
|
|
134
135
|
export { privatePeopleService } from "./private-people.js";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { CliError } from "@wirecat/cli-base";
|
|
1
|
+
import { CliError, isCliError } from "@wirecat/cli-base";
|
|
2
2
|
import { parseLocator } from "../domain/locator.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { TOTAL_CAP, totalOf } from "../search/total.js";
|
|
4
|
+
import { discoveryRequest, searchDiscovery } from "./messages-discovery.js";
|
|
5
|
+
import { countLucene, searchLucene, statsLucene } from "./messages-search.js";
|
|
5
6
|
import { readThreadContext, threadBounds } from "./thread-context.js";
|
|
6
7
|
/** How long a search may spend building the word index, then the stems, first (phase 2 plan S3: about 200 ms). */
|
|
7
8
|
export const SEARCH_FILL_MS = 200;
|
|
@@ -48,8 +49,17 @@ export const searchStore = async (store, account, request, messenger = {}, meani
|
|
|
48
49
|
if (!Number.isInteger(offset) || offset < 0 || offset > MAX_SEARCH_OFFSET)
|
|
49
50
|
throw new CliError("validation_error", `offset takes a whole number from 0 to ${MAX_SEARCH_OFFSET}`);
|
|
50
51
|
const found = await searchPage(store, account, { ...request, limit: request.limit + offset }, messenger, meaning, rerank);
|
|
52
|
+
const discovery = found.query?.discovery;
|
|
53
|
+
const total = discovery
|
|
54
|
+
? totalOf(discovery.candidates, found.items.length, found.hasMore, !discovery.truncated)
|
|
55
|
+
: totalOf(await countLucene(store, account, { ...(request.discover ? discoveryRequest(request) : request), limit: request.limit + offset }, messenger, TOTAL_CAP).catch((error) => {
|
|
56
|
+
if (isCliError(error) && error.details.reason === "query.too-large")
|
|
57
|
+
return undefined;
|
|
58
|
+
throw error;
|
|
59
|
+
}), found.items.length, found.hasMore);
|
|
51
60
|
return {
|
|
52
61
|
...found,
|
|
62
|
+
total,
|
|
53
63
|
items: found.items.slice(offset),
|
|
54
64
|
...(found.hasMore && offset + request.limit <= MAX_SEARCH_OFFSET ? { nextOffset: offset + request.limit } : {}),
|
|
55
65
|
};
|
|
@@ -55,6 +55,12 @@ export type MeaningLane = (question: string, accounts: AccountKey[], embedders?:
|
|
|
55
55
|
} | {
|
|
56
56
|
skipped: MeaningSkipped;
|
|
57
57
|
}>;
|
|
58
|
+
/**
|
|
59
|
+
* The query discovery searches. A question's own "?" would parse as a one-character wildcard and send it to strict
|
|
60
|
+
* search (BUG-395); the colon of a time or ratio ("22:00", "1:1") would parse as a field and fail the whole search,
|
|
61
|
+
* and escaped it is syntax again, so it splits the numbers (BUG-450). A quoted timestamp keeps its colons (BUG-458).
|
|
62
|
+
*/
|
|
63
|
+
export declare const discoveryRequest: (asked: SearchQuery) => SearchQuery;
|
|
58
64
|
export declare const searchDiscovery: (store: MessageStore, account: AccountKey, asked: SearchQuery, messenger?: Partial<Pick<MessengerTraits, "savedChatId" | "app" | "history" | "provider">>, meaning?: MeaningLane, rerank?: RerankLane) => Promise<SearchFound>;
|
|
59
65
|
/** The nearest messages the question's filters allow, as hits, nearest first. */
|
|
60
66
|
export declare const nearEligible: (store: MessageStore, account: AccountKey, request: SearchQuery, messenger: Partial<Pick<MessengerTraits, "savedChatId" | "app" | "history" | "provider">>, ranked: MeaningRanked, scope: string) => Promise<(FoundMessage & {
|
|
@@ -28,18 +28,21 @@ const FOLLOW_UPS_PER_HIT = 2;
|
|
|
28
28
|
const RARE_AT = 3;
|
|
29
29
|
/** Later messages read per hit before the search's filters; the latest that pass are kept. */
|
|
30
30
|
const FOLLOW_UP_CANDIDATES = 20;
|
|
31
|
+
/**
|
|
32
|
+
* The query discovery searches. A question's own "?" would parse as a one-character wildcard and send it to strict
|
|
33
|
+
* search (BUG-395); the colon of a time or ratio ("22:00", "1:1") would parse as a field and fail the whole search,
|
|
34
|
+
* and escaped it is syntax again, so it splits the numbers (BUG-450). A quoted timestamp keeps its colons (BUG-458).
|
|
35
|
+
*/
|
|
36
|
+
export const discoveryRequest = (asked) => asked.ast !== undefined || asked.exact || !asked.text
|
|
37
|
+
? asked
|
|
38
|
+
: {
|
|
39
|
+
...asked,
|
|
40
|
+
text: asked.text
|
|
41
|
+
.replace(/(?<=\p{L})\?(?=\s|$)/gu, "")
|
|
42
|
+
.replace(/"(?:[^"\\]|\\.)*"|(?<=\d):(?=\d)/gu, (found) => (found === ":" ? " " : found)),
|
|
43
|
+
};
|
|
31
44
|
export const searchDiscovery = async (store, account, asked, messenger = {}, meaning, rerank) => {
|
|
32
|
-
|
|
33
|
-
// colon of a time or ratio ("22:00", "1:1") would parse as a field and fail the whole search, and escaped it is
|
|
34
|
-
// syntax again, so it splits the numbers (BUG-450). A quoted timestamp keeps its colons (BUG-458).
|
|
35
|
-
const request = asked.ast !== undefined || asked.exact || !asked.text
|
|
36
|
-
? asked
|
|
37
|
-
: {
|
|
38
|
-
...asked,
|
|
39
|
-
text: asked.text
|
|
40
|
-
.replace(/(?<=\p{L})\?(?=\s|$)/gu, "")
|
|
41
|
-
.replace(/"(?:[^"\\]|\\.)*"|(?<=\d):(?=\d)/gu, (found) => (found === ":" ? " " : found)),
|
|
42
|
-
};
|
|
45
|
+
const request = discoveryRequest(asked);
|
|
43
46
|
const original = await prepareLucene(store, account, request, messenger);
|
|
44
47
|
const text = request.text ?? "";
|
|
45
48
|
const plan = planQuestion(text, request.ast !== undefined || !!request.exact || !!request.newest);
|
|
@@ -118,6 +118,11 @@ export declare const coverageOf: (store: MessageStore, { scopeAccounts, selected
|
|
|
118
118
|
coverage: SearchCoverage;
|
|
119
119
|
}>;
|
|
120
120
|
export declare const searchLucene: (store: MessageStore, account: AccountKey, request: SearchQuery, messenger?: Partial<Pick<MessengerTraits, "savedChatId" | "app" | "history" | "provider">>) => Promise<SearchFound>;
|
|
121
|
+
/**
|
|
122
|
+
* How many messages the query matches, mail included, each counted no further than `cap + 1`; `undefined` when only
|
|
123
|
+
* the search itself could tell.
|
|
124
|
+
*/
|
|
125
|
+
export declare const countLucene: (store: MessageStore, account: AccountKey, request: SearchQuery, messenger: Partial<Pick<MessengerTraits, "savedChatId" | "app">>, cap: number) => Promise<number | undefined>;
|
|
121
126
|
export type StatsGrouping = "chat" | "sender" | "day" | "hour";
|
|
122
127
|
export interface StatsRow {
|
|
123
128
|
key: string;
|
|
@@ -366,6 +366,28 @@ export const searchLucene = async (store, account, request, messenger = {}) => {
|
|
|
366
366
|
: {}),
|
|
367
367
|
};
|
|
368
368
|
};
|
|
369
|
+
/**
|
|
370
|
+
* How many messages the query matches, mail included, each counted no further than `cap + 1`; `undefined` when only
|
|
371
|
+
* the search itself could tell.
|
|
372
|
+
*/
|
|
373
|
+
export const countLucene = async (store, account, request, messenger, cap) => {
|
|
374
|
+
const prepared = await prepareLucene(store, account, request, messenger);
|
|
375
|
+
if (!prepared.scopeAccounts.length)
|
|
376
|
+
return 0;
|
|
377
|
+
const count = (execution) => store.queries.countMatches(execution, cap);
|
|
378
|
+
const mailAccounts = prepared.scopeAccounts.filter(({ provider }) => provider === MAIL);
|
|
379
|
+
const messages = await count(prepared.execution);
|
|
380
|
+
let mail = 0;
|
|
381
|
+
if (mailAccounts.length)
|
|
382
|
+
try {
|
|
383
|
+
mail = await count({ ...prepared.execution, accounts: mailAccounts, corpus: "mail" });
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
if (!(request.kind !== "mail" && error instanceof CliError && error.code === "validation_error"))
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
return messages === undefined || mail === undefined ? undefined : messages + mail;
|
|
390
|
+
};
|
|
369
391
|
/** The garbled spellings voice-note hits were found through, one entry per query word. */
|
|
370
392
|
const correctionsOf = (items) => {
|
|
371
393
|
const found = new Map();
|
|
@@ -3,6 +3,7 @@ import { type MessageLink } from "../domain/message-link.js";
|
|
|
3
3
|
import type { Deletion, Discussion, Id, Message, Page, Provider, WindowedMessage } from "../domain/models.js";
|
|
4
4
|
import { type After, type Download, type Sent } from "../ports/messenger.js";
|
|
5
5
|
import type { SkippedSource } from "../search/account-list.js";
|
|
6
|
+
import type { SearchTotal } from "../search/total.js";
|
|
6
7
|
import { type Operated } from "../sends/guarded.js";
|
|
7
8
|
import type { PermissionKey } from "../sends/permissions.js";
|
|
8
9
|
import type { AccountKey, ChatCompleteness, MatchPlace, StoredHit } from "../store/store.js";
|
|
@@ -104,6 +105,8 @@ export type FoundMessage = StoredHit & {
|
|
|
104
105
|
export interface SearchFound extends Page<FoundMessage> {
|
|
105
106
|
/** Where the next page starts, as `offset`; absent on the last page. */
|
|
106
107
|
nextOffset?: number;
|
|
108
|
+
/** How many messages match, every page together, as Elasticsearch's `hits.total`; set by the archive search. */
|
|
109
|
+
total?: SearchTotal;
|
|
107
110
|
refreshed?: SearchRefreshed;
|
|
108
111
|
server?: ServerSearched;
|
|
109
112
|
query?: QueryMetadata;
|
|
@@ -6,6 +6,7 @@ import type { AppIdentity } from "../ports/app.js";
|
|
|
6
6
|
import { type SkippedSource } from "../search/account-list.js";
|
|
7
7
|
import { type ResolvedNode } from "../search/lucene/resolved.js";
|
|
8
8
|
import { type Stemmer } from "../search/stem.js";
|
|
9
|
+
import { type SearchTotal } from "../search/total.js";
|
|
9
10
|
import type { Link, NearestNote, Note, NoteHit } from "../store/index.js";
|
|
10
11
|
import type { AccountKey, MessageStore } from "../store/store.js";
|
|
11
12
|
export interface NotesSearchRequest {
|
|
@@ -71,7 +72,8 @@ export declare const embedMail: (store: MessageStore, options?: NotesEmbedOption
|
|
|
71
72
|
command?: string;
|
|
72
73
|
}) => Promise<NotesEmbedded>;
|
|
73
74
|
/** Notes nearest in meaning to the query, best first; only notes already embedded can be found. */
|
|
74
|
-
export declare const nearestNotes: (store: MessageStore, query: string,
|
|
75
|
+
export declare const nearestNotes: (store: MessageStore, query: string, options: NearestOptions) => Promise<NearestNote[]>;
|
|
76
|
+
type NearestOptions = Omit<NotesEmbedOptions, "maxChunks"> & {
|
|
75
77
|
limit: number;
|
|
76
78
|
folderIds?: string[];
|
|
77
79
|
withInternal?: boolean;
|
|
@@ -79,7 +81,7 @@ export declare const nearestNotes: (store: MessageStore, query: string, { model:
|
|
|
79
81
|
command?: string;
|
|
80
82
|
/** The search's own, when another part of it loads the same model. */
|
|
81
83
|
embedders?: Pick<WarmEmbedders, "get">;
|
|
82
|
-
}
|
|
84
|
+
};
|
|
83
85
|
export interface FoundNote {
|
|
84
86
|
kind?: "document" | "note";
|
|
85
87
|
provider?: string;
|
|
@@ -120,6 +122,8 @@ export interface NotesFound {
|
|
|
120
122
|
/** Why meaning was not searched, when it was not. */
|
|
121
123
|
meaning_skipped?: string;
|
|
122
124
|
hits: FoundNote[];
|
|
125
|
+
/** How many notes match the query, every page together, as Elasticsearch's `hits.total`. */
|
|
126
|
+
total?: SearchTotal;
|
|
123
127
|
has_more: boolean;
|
|
124
128
|
next_offset?: number;
|
|
125
129
|
/** What the notes found link to, most linked first. */
|
|
@@ -173,6 +177,13 @@ export declare const noteSourceOfQuery: (query: string) => {
|
|
|
173
177
|
export declare const fieldsOfQuery: (root: QueryNode) => QueryNode | undefined;
|
|
174
178
|
/** The words and phrases the query asks for, for the meaning half to embed; none it excludes, no field. */
|
|
175
179
|
export declare const meaningWords: (root: QueryNode) => string;
|
|
180
|
+
/**
|
|
181
|
+
* The most notes a filter may hold for meaning to rank all of them, each by its best chunk. Above it, meaning ranks
|
|
182
|
+
* the nearest notes overall and keeps those inside the filter, so a narrow filter over a large store loses some. On
|
|
183
|
+
* the owner's store (1,454 documents, 23,308 chunks) scoring all of them took ~0.5 s against ~0.17 s for the
|
|
184
|
+
* nearest 50 overall, and the cost grows with chunks, so 2,000 notes keeps it under a second at that density.
|
|
185
|
+
*/
|
|
186
|
+
export declare const RANK_WITHIN = 2000;
|
|
176
187
|
/**
|
|
177
188
|
* `search notes`: words and stems, and meaning when the local model is there, merged by reciprocal rank;
|
|
178
189
|
* each hit says which half found it, and the answer lists what the notes found link to. Every field of the query and
|
|
@@ -199,3 +210,4 @@ export declare const searchNotesOf: (store: MessageStore, account: AccountKey, t
|
|
|
199
210
|
accounts?: string;
|
|
200
211
|
app?: AppIdentity;
|
|
201
212
|
}) => Promise<NotesFoundOf>;
|
|
213
|
+
export {};
|