@wirecat/cli-core 0.563.0 → 0.564.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 +77 -0
- package/dist/calendar/mcp/index.d.ts +12 -12
- package/dist/cli/messenger/archive-commands.js +17 -0
- package/dist/cli/messenger/catch-up-answers.d.ts +5 -0
- package/dist/cli/messenger/catch-up-answers.js +3 -0
- package/dist/cli/messenger/search-answers.d.ts +1 -0
- package/dist/cli/messenger/search-answers.js +1 -1
- package/dist/cli/messenger/stats-answers.d.ts +3 -3
- package/dist/cli/messenger/stats-answers.js +21 -9
- package/dist/cli/messenger/tasks-command.js +35 -14
- package/dist/conversations/mail-text.d.ts +6 -0
- package/dist/conversations/mail-text.js +73 -0
- package/dist/domain/calendar.d.ts +2 -0
- package/dist/domain/calendar.js +6 -0
- package/dist/entities/chat-stats.d.ts +16 -2
- package/dist/entities/chat-stats.js +18 -3
- package/dist/entities/index.d.ts +50 -802
- package/dist/entities/index.js +23 -16
- package/dist/entities/mail-counts.d.ts +15 -0
- package/dist/entities/mail-counts.js +16 -0
- package/dist/entities/meeting.d.ts +1 -1
- package/dist/entities/meeting.js +1 -1
- package/dist/entities/store-records.d.ts +8 -0
- package/dist/entities/store-records.js +8 -0
- package/dist/errors/reasons.js +5 -0
- package/dist/mail/cli/index.d.ts +5 -0
- package/dist/mail/cli/index.js +27 -3
- package/dist/mail/cli/review.js +11 -4
- package/dist/mail/index.d.ts +2 -0
- package/dist/mail/index.js +2 -0
- package/dist/mail/stats.d.ts +50 -0
- package/dist/mail/stats.js +60 -0
- package/dist/mail/whole.d.ts +4 -4
- package/dist/mail/whole.js +4 -3
- package/dist/meetings/index.d.ts +2 -2
- package/dist/meetings/index.js +2 -2
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/meetings/read-answers.d.ts +22 -1
- package/dist/meetings/read-answers.js +38 -0
- package/dist/meetings/stats.d.ts +44 -0
- package/dist/meetings/stats.js +77 -7
- package/dist/search/lucene/registry.js +2 -1
- package/dist/search/question-plan.d.ts +16 -0
- package/dist/search/question-plan.js +34 -0
- package/dist/services/events-search.d.ts +5 -1
- package/dist/services/events-search.js +10 -4
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/meeting-stats.d.ts +12 -1
- package/dist/services/meeting-stats.js +12 -0
- package/dist/services/messages-discovery.d.ts +1 -5
- package/dist/services/messages-discovery.js +5 -18
- package/dist/services/messages-meaning.js +25 -3
- package/dist/services/messages.js +4 -3
- package/dist/services/notes-search.d.ts +5 -1
- package/dist/services/notes-search.js +16 -5
- package/dist/services/proposals/ids.d.ts +2 -2
- package/dist/services/proposals/ids.js +5 -3
- package/dist/services/proposals/kinds/findings.d.ts +1 -1
- package/dist/services/proposals/kinds/findings.js +1 -1
- package/dist/services/proposals/kinds/meeting-findings.d.ts +7 -0
- package/dist/services/proposals/kinds/meeting-findings.js +73 -0
- package/dist/services/proposals/kinds/task-close.d.ts +7 -0
- package/dist/services/proposals/kinds/task-close.js +76 -0
- package/dist/services/proposals/registry.js +4 -0
- package/dist/services/search-all.d.ts +5 -0
- package/dist/services/search-all.js +47 -14
- package/dist/services/task-stats.d.ts +41 -0
- package/dist/services/task-stats.js +126 -0
- package/dist/services/tasks.d.ts +17 -4
- package/dist/services/tasks.js +31 -14
- package/dist/store/chats.d.ts +3 -1
- package/dist/store/chats.js +6 -0
- package/dist/store/decisions.d.ts +4 -2
- package/dist/store/decisions.js +5 -3
- package/dist/store/exports.d.ts +1 -1
- package/dist/store/exports.js +1 -1
- package/dist/store/index.d.ts +2 -1
- package/dist/store/index.js +1 -1
- package/dist/store/sqlite/completeness.d.ts +17 -0
- package/dist/store/sqlite/completeness.js +49 -13
- package/dist/store/sqlite/lucene-fields.js +2 -0
- package/dist/store/sqlite/lucene-groups.d.ts +15 -0
- package/dist/store/sqlite/lucene-groups.js +21 -0
- package/dist/store/sqlite/lucene.d.ts +2 -9
- package/dist/store/sqlite/lucene.js +1 -12
- package/dist/store/sqlite/meeting-stats.d.ts +2 -0
- package/dist/store/sqlite/meeting-stats.js +27 -1
- package/dist/store/sqlite/retention.js +2 -7
- package/dist/store/sqlite/task-board.js +2 -0
- package/dist/store/sqlite/task-stats.d.ts +32 -0
- package/dist/store/sqlite/task-stats.js +45 -0
- package/dist/store/sqlite/tasks.d.ts +2 -1
- package/dist/store/sqlite/tasks.js +2 -0
- package/package.json +1 -1
- package/parity.json +13 -1
|
@@ -38,7 +38,6 @@ export declare const meetingTools: ({
|
|
|
38
38
|
max_read_bytes?: undefined;
|
|
39
39
|
cursor?: undefined;
|
|
40
40
|
} | {
|
|
41
|
-
limit?: undefined;
|
|
42
41
|
event_id?: undefined;
|
|
43
42
|
series_id?: undefined;
|
|
44
43
|
page?: undefined;
|
|
@@ -54,11 +53,11 @@ export declare const meetingTools: ({
|
|
|
54
53
|
format?: undefined;
|
|
55
54
|
transcript_id?: undefined;
|
|
56
55
|
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;
|
|
62
61
|
event_id?: undefined;
|
|
63
62
|
series_id?: undefined;
|
|
64
63
|
page?: undefined;
|
|
@@ -76,11 +75,11 @@ export declare const meetingTools: ({
|
|
|
76
75
|
format?: undefined;
|
|
77
76
|
transcript_id?: undefined;
|
|
78
77
|
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;
|
|
84
83
|
event_id?: undefined;
|
|
85
84
|
series_id?: undefined;
|
|
86
85
|
page?: undefined;
|
|
@@ -102,6 +101,7 @@ export declare const meetingTools: ({
|
|
|
102
101
|
minimum: number;
|
|
103
102
|
};
|
|
104
103
|
query?: undefined;
|
|
104
|
+
limit?: undefined;
|
|
105
105
|
scan_limit?: undefined;
|
|
106
106
|
max_read_bytes?: undefined;
|
|
107
107
|
cursor?: undefined;
|
|
@@ -188,7 +188,6 @@ export declare const meetingTools: ({
|
|
|
188
188
|
max_read_bytes?: undefined;
|
|
189
189
|
cursor?: undefined;
|
|
190
190
|
} | {
|
|
191
|
-
limit?: undefined;
|
|
192
191
|
event_id?: undefined;
|
|
193
192
|
series_id?: undefined;
|
|
194
193
|
page?: undefined;
|
|
@@ -204,11 +203,11 @@ export declare const meetingTools: ({
|
|
|
204
203
|
format?: undefined;
|
|
205
204
|
transcript_id?: undefined;
|
|
206
205
|
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;
|
|
212
211
|
event_id?: undefined;
|
|
213
212
|
series_id?: undefined;
|
|
214
213
|
page?: undefined;
|
|
@@ -226,11 +225,11 @@ export declare const meetingTools: ({
|
|
|
226
225
|
format?: undefined;
|
|
227
226
|
transcript_id?: undefined;
|
|
228
227
|
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;
|
|
234
233
|
event_id?: undefined;
|
|
235
234
|
series_id?: undefined;
|
|
236
235
|
page?: undefined;
|
|
@@ -252,6 +251,7 @@ export declare const meetingTools: ({
|
|
|
252
251
|
minimum: number;
|
|
253
252
|
};
|
|
254
253
|
query?: undefined;
|
|
254
|
+
limit?: undefined;
|
|
255
255
|
scan_limit?: undefined;
|
|
256
256
|
max_read_bytes?: undefined;
|
|
257
257
|
cursor?: undefined;
|
|
@@ -338,7 +338,6 @@ export declare const meetingTools: ({
|
|
|
338
338
|
max_read_bytes?: undefined;
|
|
339
339
|
cursor?: undefined;
|
|
340
340
|
} | {
|
|
341
|
-
limit?: undefined;
|
|
342
341
|
event_id?: undefined;
|
|
343
342
|
series_id?: undefined;
|
|
344
343
|
page?: undefined;
|
|
@@ -354,11 +353,11 @@ export declare const meetingTools: ({
|
|
|
354
353
|
format?: undefined;
|
|
355
354
|
transcript_id?: undefined;
|
|
356
355
|
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;
|
|
362
361
|
event_id?: undefined;
|
|
363
362
|
series_id?: undefined;
|
|
364
363
|
page?: undefined;
|
|
@@ -376,11 +375,11 @@ export declare const meetingTools: ({
|
|
|
376
375
|
format?: undefined;
|
|
377
376
|
transcript_id?: undefined;
|
|
378
377
|
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;
|
|
384
383
|
event_id?: undefined;
|
|
385
384
|
series_id?: undefined;
|
|
386
385
|
page?: undefined;
|
|
@@ -402,6 +401,7 @@ export declare const meetingTools: ({
|
|
|
402
401
|
minimum: number;
|
|
403
402
|
};
|
|
404
403
|
query?: undefined;
|
|
404
|
+
limit?: undefined;
|
|
405
405
|
scan_limit?: undefined;
|
|
406
406
|
max_read_bytes?: undefined;
|
|
407
407
|
cursor?: undefined;
|
|
@@ -488,7 +488,6 @@ export declare const meetingTools: ({
|
|
|
488
488
|
max_read_bytes?: undefined;
|
|
489
489
|
cursor?: undefined;
|
|
490
490
|
} | {
|
|
491
|
-
limit?: undefined;
|
|
492
491
|
event_id?: undefined;
|
|
493
492
|
series_id?: undefined;
|
|
494
493
|
page?: undefined;
|
|
@@ -504,11 +503,11 @@ export declare const meetingTools: ({
|
|
|
504
503
|
format?: undefined;
|
|
505
504
|
transcript_id?: undefined;
|
|
506
505
|
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;
|
|
512
511
|
event_id?: undefined;
|
|
513
512
|
series_id?: undefined;
|
|
514
513
|
page?: undefined;
|
|
@@ -526,11 +525,11 @@ export declare const meetingTools: ({
|
|
|
526
525
|
format?: undefined;
|
|
527
526
|
transcript_id?: undefined;
|
|
528
527
|
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;
|
|
534
533
|
event_id?: undefined;
|
|
535
534
|
series_id?: undefined;
|
|
536
535
|
page?: undefined;
|
|
@@ -552,6 +551,7 @@ export declare const meetingTools: ({
|
|
|
552
551
|
minimum: number;
|
|
553
552
|
};
|
|
554
553
|
query?: undefined;
|
|
554
|
+
limit?: undefined;
|
|
555
555
|
scan_limit?: undefined;
|
|
556
556
|
max_read_bytes?: undefined;
|
|
557
557
|
cursor?: undefined;
|
|
@@ -638,7 +638,6 @@ export declare const meetingTools: ({
|
|
|
638
638
|
max_read_bytes?: undefined;
|
|
639
639
|
cursor?: undefined;
|
|
640
640
|
} | {
|
|
641
|
-
limit?: undefined;
|
|
642
641
|
event_id?: undefined;
|
|
643
642
|
series_id?: undefined;
|
|
644
643
|
page?: undefined;
|
|
@@ -654,11 +653,11 @@ export declare const meetingTools: ({
|
|
|
654
653
|
format?: undefined;
|
|
655
654
|
transcript_id?: undefined;
|
|
656
655
|
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;
|
|
662
661
|
event_id?: undefined;
|
|
663
662
|
series_id?: undefined;
|
|
664
663
|
page?: undefined;
|
|
@@ -676,11 +675,11 @@ export declare const meetingTools: ({
|
|
|
676
675
|
format?: undefined;
|
|
677
676
|
transcript_id?: undefined;
|
|
678
677
|
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;
|
|
684
683
|
event_id?: undefined;
|
|
685
684
|
series_id?: undefined;
|
|
686
685
|
page?: undefined;
|
|
@@ -702,6 +701,7 @@ export declare const meetingTools: ({
|
|
|
702
701
|
minimum: number;
|
|
703
702
|
};
|
|
704
703
|
query?: undefined;
|
|
704
|
+
limit?: undefined;
|
|
705
705
|
scan_limit?: undefined;
|
|
706
706
|
max_read_bytes?: undefined;
|
|
707
707
|
cursor?: undefined;
|
|
@@ -2,7 +2,7 @@ import * as v from "valibot";
|
|
|
2
2
|
import type { MeetingLink } from "./links.js";
|
|
3
3
|
import type { Event } from "./model.js";
|
|
4
4
|
import type { MeetingReviewItem } from "./review.js";
|
|
5
|
-
import type { MeetingSpeakerStats, MeetingStats } from "./stats.js";
|
|
5
|
+
import type { MeetingCounts, MeetingSpeakerStats, MeetingStats } from "./stats.js";
|
|
6
6
|
/** One meeting in `review`: what the store holds about it, never transcript text. */
|
|
7
7
|
export declare const meetingReview: v.StrictObjectSchema<{
|
|
8
8
|
readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
|
|
@@ -91,6 +91,27 @@ export declare const meetingStats: v.StrictObjectSchema<{
|
|
|
91
91
|
}, undefined>, undefined>;
|
|
92
92
|
}, undefined>;
|
|
93
93
|
export declare const meetingStatsItem: (found: MeetingStats, top?: number) => v.InferOutput<typeof meetingStats>;
|
|
94
|
+
/**
|
|
95
|
+
* `stats meetings history`: meetings, minutes and meetings with a transcript, in total and per day, week (its Monday)
|
|
96
|
+
* or series. `has_more` says `--limit` left rows out.
|
|
97
|
+
*/
|
|
98
|
+
export declare const meetingCounts: v.StrictObjectSchema<{
|
|
99
|
+
readonly by: v.PicklistSchema<["day", "week", "series"], undefined>;
|
|
100
|
+
readonly meetings: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
101
|
+
readonly minutes: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
102
|
+
readonly mean_minutes: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
103
|
+
readonly transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
104
|
+
readonly rows: v.ArraySchema<v.StrictObjectSchema<{
|
|
105
|
+
readonly key: v.StringSchema<undefined>;
|
|
106
|
+
readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
107
|
+
readonly meetings: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
108
|
+
readonly minutes: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
109
|
+
readonly mean_minutes: v.NullableSchema<v.NumberSchema<undefined>, undefined>;
|
|
110
|
+
readonly transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
111
|
+
}, undefined>, undefined>;
|
|
112
|
+
readonly has_more: v.BooleanSchema<undefined>;
|
|
113
|
+
}, undefined>;
|
|
114
|
+
export declare const meetingCountsItem: (found: MeetingCounts) => v.InferOutput<typeof meetingCounts>;
|
|
94
115
|
/** `meetings people`: one page of participant observations whose name or email matches. */
|
|
95
116
|
export declare const meetingParticipants: v.StrictObjectSchema<{
|
|
96
117
|
readonly schema_version: v.LiteralSchema<1, undefined>;
|
|
@@ -113,6 +113,44 @@ export const meetingStatsItem = (found, top = 10) => ({
|
|
|
113
113
|
weeks: found.weeks.map((week) => ({ week_start: week.weekStart, meetings: week.meetings, talk_ms: week.talkMs })),
|
|
114
114
|
top_speakers: found.speakers.slice(0, top).map(meetingSpeakerItem),
|
|
115
115
|
});
|
|
116
|
+
const meetingCountsRow = v.strictObject({
|
|
117
|
+
key: text,
|
|
118
|
+
name: v.nullable(text),
|
|
119
|
+
meetings: count,
|
|
120
|
+
minutes: count,
|
|
121
|
+
mean_minutes: v.nullable(v.number()),
|
|
122
|
+
transcripts: count,
|
|
123
|
+
});
|
|
124
|
+
/**
|
|
125
|
+
* `stats meetings history`: meetings, minutes and meetings with a transcript, in total and per day, week (its Monday)
|
|
126
|
+
* or series. `has_more` says `--limit` left rows out.
|
|
127
|
+
*/
|
|
128
|
+
export const meetingCounts = v.strictObject({
|
|
129
|
+
by: v.picklist(["day", "week", "series"]),
|
|
130
|
+
meetings: count,
|
|
131
|
+
minutes: count,
|
|
132
|
+
mean_minutes: v.nullable(v.number()),
|
|
133
|
+
transcripts: count,
|
|
134
|
+
rows: v.array(meetingCountsRow),
|
|
135
|
+
has_more: v.boolean(),
|
|
136
|
+
});
|
|
137
|
+
const countsRowItem = (row) => ({
|
|
138
|
+
key: row.key,
|
|
139
|
+
name: row.name,
|
|
140
|
+
meetings: row.meetings,
|
|
141
|
+
minutes: row.minutes,
|
|
142
|
+
mean_minutes: row.meanMinutes,
|
|
143
|
+
transcripts: row.transcripts,
|
|
144
|
+
});
|
|
145
|
+
export const meetingCountsItem = (found) => ({
|
|
146
|
+
by: found.by,
|
|
147
|
+
meetings: found.meetings,
|
|
148
|
+
minutes: found.minutes,
|
|
149
|
+
mean_minutes: found.meanMinutes,
|
|
150
|
+
transcripts: found.transcripts,
|
|
151
|
+
rows: found.rows.map(countsRowItem),
|
|
152
|
+
has_more: found.hasMore,
|
|
153
|
+
});
|
|
116
154
|
const truncated = (...why) => v.nullable(v.picklist(why));
|
|
117
155
|
/** `meetings people`: one page of participant observations whose name or email matches. */
|
|
118
156
|
export const meetingParticipants = v.strictObject({
|
package/dist/meetings/stats.d.ts
CHANGED
|
@@ -48,9 +48,53 @@ export interface MeetingStats {
|
|
|
48
48
|
speakers: MeetingSpeakerStats[];
|
|
49
49
|
weeks: MeetingWeekStats[];
|
|
50
50
|
}
|
|
51
|
+
export type MeetingCountsGrouping = "day" | "week" | "series";
|
|
52
|
+
export declare const MEETING_COUNTS_GROUPINGS: readonly MeetingCountsGrouping[];
|
|
53
|
+
export interface MeetingCountsInput extends MeetingStatsSelection {
|
|
54
|
+
by: MeetingCountsGrouping;
|
|
55
|
+
/** The zone days and weeks are read in; UTC when not given. */
|
|
56
|
+
timeZone?: string;
|
|
57
|
+
/** Most rows: the newest days or weeks, or the busiest series (default 100). */
|
|
58
|
+
limit?: number;
|
|
59
|
+
}
|
|
60
|
+
/** One selected meeting as the counts need it; no title text but the series' or the meeting's own name. */
|
|
61
|
+
export interface MeetingCountsMeeting {
|
|
62
|
+
startedAt: number | null;
|
|
63
|
+
/** `duration_ms`, else `ended_at - started_at`; `null` for a meeting with no end. */
|
|
64
|
+
durationMs: number | null;
|
|
65
|
+
transcribed: boolean;
|
|
66
|
+
seriesKey: string | null;
|
|
67
|
+
seriesTitle: string | null;
|
|
68
|
+
}
|
|
69
|
+
export interface MeetingCountsRow {
|
|
70
|
+
/** A day or the Monday of a week (`YYYY-MM-DD` in the zone), or `series:<external id>`, or `NO_SERIES`. */
|
|
71
|
+
key: string;
|
|
72
|
+
/** The series title under `series`; `null` otherwise. */
|
|
73
|
+
name: string | null;
|
|
74
|
+
meetings: number;
|
|
75
|
+
minutes: number;
|
|
76
|
+
/** Over the meetings with a duration; `null` when none has one. */
|
|
77
|
+
meanMinutes: number | null;
|
|
78
|
+
transcripts: number;
|
|
79
|
+
}
|
|
80
|
+
export interface MeetingCounts extends Omit<MeetingCountsRow, "key" | "name"> {
|
|
81
|
+
by: MeetingCountsGrouping;
|
|
82
|
+
timeZone: string;
|
|
83
|
+
rows: MeetingCountsRow[];
|
|
84
|
+
hasMore: boolean;
|
|
85
|
+
}
|
|
51
86
|
export interface MeetingStatsStore {
|
|
52
87
|
meetingStats(input: MeetingStatsInput): Promise<MeetingStats>;
|
|
88
|
+
meetingCounts(input: MeetingCountsInput): Promise<MeetingCounts>;
|
|
53
89
|
}
|
|
90
|
+
/** The row of meetings outside any series, under `--by series`; a constant for the same reason as `UNKNOWN_SPEAKER`. */
|
|
91
|
+
export declare const NO_SERIES = "no-series";
|
|
92
|
+
/**
|
|
93
|
+
* Meetings, minutes and meetings with a transcript, in total and per day, week or series. A meeting with no end
|
|
94
|
+
* counts in `meetings` but not in `minutes` or the mean; one with no start counts in the totals and in its series,
|
|
95
|
+
* never in a day or week. Days and weeks with no meeting have no row.
|
|
96
|
+
*/
|
|
97
|
+
export declare const aggregateMeetingCounts: (meetings: readonly MeetingCountsMeeting[], by: MeetingCountsGrouping, timeZone?: string, limit?: number) => MeetingCounts;
|
|
54
98
|
/**
|
|
55
99
|
* Talk time, lines and words per speaker, and meetings and talk time per week. A speaker is a linked person when the
|
|
56
100
|
* line's participant has one, else the participant's identity, else the name the transcript gives; lines with none
|
package/dist/meetings/stats.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
export const MEETING_COUNTS_GROUPINGS = ["day", "week", "series"];
|
|
2
3
|
const zoneOf = (timeZone) => {
|
|
3
4
|
const zone = timeZone ?? "UTC";
|
|
4
5
|
try {
|
|
@@ -15,6 +16,81 @@ const weekStart = (at, format) => {
|
|
|
15
16
|
const day = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day));
|
|
16
17
|
return new Date(day - DAYS.indexOf(parts.weekday ?? "Mon") * 86_400_000).toISOString().slice(0, 10);
|
|
17
18
|
};
|
|
19
|
+
const dayKey = (at, format) => {
|
|
20
|
+
const parts = Object.fromEntries(format.formatToParts(new Date(at)).map((part) => [part.type, part.value]));
|
|
21
|
+
return `${parts.year}-${parts.month}-${parts.day}`;
|
|
22
|
+
};
|
|
23
|
+
const zoneFormat = (zone) => new Intl.DateTimeFormat("en-US", {
|
|
24
|
+
timeZone: zone,
|
|
25
|
+
year: "numeric",
|
|
26
|
+
month: "2-digit",
|
|
27
|
+
day: "2-digit",
|
|
28
|
+
weekday: "short",
|
|
29
|
+
});
|
|
30
|
+
/** The row of meetings outside any series, under `--by series`; a constant for the same reason as `UNKNOWN_SPEAKER`. */
|
|
31
|
+
export const NO_SERIES = "no-series";
|
|
32
|
+
const MOST_COUNT_ROWS = 100;
|
|
33
|
+
const minutesOf = (ms) => Math.round(ms / 60_000);
|
|
34
|
+
const meanOf = (ms, timed) => (timed === 0 ? null : Math.round(ms / timed / 6_000) / 10);
|
|
35
|
+
/**
|
|
36
|
+
* Meetings, minutes and meetings with a transcript, in total and per day, week or series. A meeting with no end
|
|
37
|
+
* counts in `meetings` but not in `minutes` or the mean; one with no start counts in the totals and in its series,
|
|
38
|
+
* never in a day or week. Days and weeks with no meeting have no row.
|
|
39
|
+
*/
|
|
40
|
+
export const aggregateMeetingCounts = (meetings, by, timeZone, limit = MOST_COUNT_ROWS) => {
|
|
41
|
+
if (!MEETING_COUNTS_GROUPINGS.includes(by))
|
|
42
|
+
throw new CliError("validation_error", `--by takes ${MEETING_COUNTS_GROUPINGS.join(", ")}`);
|
|
43
|
+
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
44
|
+
throw new CliError("validation_error", "--limit is a positive number");
|
|
45
|
+
const zone = zoneOf(timeZone);
|
|
46
|
+
const format = zoneFormat(zone);
|
|
47
|
+
const sum = () => ({ meetings: 0, ms: 0, timed: 0, transcripts: 0 });
|
|
48
|
+
const add = (into, meeting) => {
|
|
49
|
+
into.meetings += 1;
|
|
50
|
+
if (meeting.durationMs !== null) {
|
|
51
|
+
into.ms += Math.max(0, meeting.durationMs);
|
|
52
|
+
into.timed += 1;
|
|
53
|
+
}
|
|
54
|
+
if (meeting.transcribed)
|
|
55
|
+
into.transcripts += 1;
|
|
56
|
+
};
|
|
57
|
+
const total = { ...sum(), name: null };
|
|
58
|
+
const groups = new Map();
|
|
59
|
+
for (const meeting of meetings) {
|
|
60
|
+
add(total, meeting);
|
|
61
|
+
const key = by === "series"
|
|
62
|
+
? meeting.seriesKey === null
|
|
63
|
+
? NO_SERIES
|
|
64
|
+
: `series:${meeting.seriesKey}`
|
|
65
|
+
: meeting.startedAt === null
|
|
66
|
+
? null
|
|
67
|
+
: by === "day"
|
|
68
|
+
? dayKey(meeting.startedAt, format)
|
|
69
|
+
: weekStart(meeting.startedAt, format);
|
|
70
|
+
if (key === null)
|
|
71
|
+
continue;
|
|
72
|
+
const group = groups.get(key) ?? { ...sum(), name: by === "series" ? meeting.seriesTitle : null };
|
|
73
|
+
add(group, meeting);
|
|
74
|
+
groups.set(key, group);
|
|
75
|
+
}
|
|
76
|
+
const ordered = [...groups.entries()].sort(([a, one], [b, other]) => by === "series" ? other.ms - one.ms || other.meetings - one.meetings || a.localeCompare(b) : b.localeCompare(a));
|
|
77
|
+
const kept = ordered.slice(0, limit);
|
|
78
|
+
if (by !== "series")
|
|
79
|
+
kept.reverse();
|
|
80
|
+
const counted = ({ meetings: count, ms, timed, transcripts }) => ({
|
|
81
|
+
meetings: count,
|
|
82
|
+
minutes: minutesOf(ms),
|
|
83
|
+
meanMinutes: meanOf(ms, timed),
|
|
84
|
+
transcripts,
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
by,
|
|
88
|
+
timeZone: zone,
|
|
89
|
+
...counted(total),
|
|
90
|
+
rows: kept.map(([key, group]) => ({ key, name: group.name, ...counted(group) })),
|
|
91
|
+
hasMore: ordered.length > limit,
|
|
92
|
+
};
|
|
93
|
+
};
|
|
18
94
|
/** Lines with no speaker at all; a constant, since a `key: "…"` literal reads as a permission key to the key test. */
|
|
19
95
|
const UNKNOWN_SPEAKER = "unknown";
|
|
20
96
|
const speakerOf = (row) => row.personId !== null
|
|
@@ -31,13 +107,7 @@ const speakerOf = (row) => row.personId !== null
|
|
|
31
107
|
*/
|
|
32
108
|
export const aggregateMeetingStats = (rows, timeZone) => {
|
|
33
109
|
const zone = zoneOf(timeZone);
|
|
34
|
-
const format =
|
|
35
|
-
timeZone: zone,
|
|
36
|
-
year: "numeric",
|
|
37
|
-
month: "2-digit",
|
|
38
|
-
day: "2-digit",
|
|
39
|
-
weekday: "short",
|
|
40
|
-
});
|
|
110
|
+
const format = zoneFormat(zone);
|
|
41
111
|
const speakers = new Map();
|
|
42
112
|
const weeks = new Map();
|
|
43
113
|
const meetings = new Set();
|
|
@@ -192,7 +192,7 @@ export const QUERY_FIELDS = [
|
|
|
192
192
|
{
|
|
193
193
|
name: "has",
|
|
194
194
|
example: "has:file",
|
|
195
|
-
index: "attachments / messages_fts links",
|
|
195
|
+
index: "attachments / messages_fts links / message_transcripts",
|
|
196
196
|
aliases: [],
|
|
197
197
|
type: "enum",
|
|
198
198
|
operators: terms,
|
|
@@ -212,6 +212,7 @@ export const QUERY_FIELDS = [
|
|
|
212
212
|
"contact",
|
|
213
213
|
"location",
|
|
214
214
|
"poll",
|
|
215
|
+
"transcript",
|
|
215
216
|
],
|
|
216
217
|
},
|
|
217
218
|
{
|
|
@@ -14,3 +14,19 @@ export declare function planQuestion(text: string, strict?: boolean): {
|
|
|
14
14
|
removed: string[];
|
|
15
15
|
aliases: string[];
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Text as a question is searched. Its own "?" would parse as a one-character wildcard and send it to strict search
|
|
19
|
+
* (BUG-395); the colon of a time or ratio ("22:00", "1:1") would parse as a field and fail the whole search, and
|
|
20
|
+
* escaped it is syntax again, so it splits the numbers (BUG-450). A quoted timestamp keeps its colons (BUG-458).
|
|
21
|
+
*/
|
|
22
|
+
export declare const questionText: (text: string) => string;
|
|
23
|
+
/** The words a question's partial lookup takes: its content words and the words its aliases stand for. */
|
|
24
|
+
export declare const lookupTerms: (plan: ReturnType<typeof planQuestion>) => string[];
|
|
25
|
+
/** Any of the words, and every field the question names. */
|
|
26
|
+
export declare const anyOfTerms: (terms: string[], scope: string) => string;
|
|
27
|
+
/**
|
|
28
|
+
* The partial lookup `search messages --discover` runs, for a store that has no discovery of its own: a question's
|
|
29
|
+
* content words joined by OR, its fields kept. `undefined` when the text is not a question in natural language, so
|
|
30
|
+
* words and query syntax are searched as typed.
|
|
31
|
+
*/
|
|
32
|
+
export declare const questionLookup: (text: string) => string | undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
1
2
|
import { implicitTextTerms, parseLucene, walkQuery } from "@wirecat/cli-base/query";
|
|
2
3
|
import { normalize } from "../store/normalize.js";
|
|
3
4
|
import { MESSAGE_PARSING } from "./lucene/types.js";
|
|
@@ -129,3 +130,36 @@ export function planQuestion(text, strict = false) {
|
|
|
129
130
|
const queries = [...new Set([render(terms), render(alias)])].slice(0, 2);
|
|
130
131
|
return { changed: true, natural: true, queries, scope, terms, rankingTerms: original, removed, aliases: changes };
|
|
131
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Text as a question is searched. Its own "?" would parse as a one-character wildcard and send it to strict search
|
|
135
|
+
* (BUG-395); the colon of a time or ratio ("22:00", "1:1") would parse as a field and fail the whole search, and
|
|
136
|
+
* escaped it is syntax again, so it splits the numbers (BUG-450). A quoted timestamp keeps its colons (BUG-458).
|
|
137
|
+
*/
|
|
138
|
+
export const questionText = (text) => text
|
|
139
|
+
.replace(/(?<=\p{L})\?(?=\s|$)/gu, "")
|
|
140
|
+
.replace(/"(?:[^"\\]|\\.)*"|(?<=\d):(?=\d)/gu, (found) => (found === ":" ? " " : found));
|
|
141
|
+
/** The words a question's partial lookup takes: its content words and the words its aliases stand for. */
|
|
142
|
+
export const lookupTerms = (plan) => [
|
|
143
|
+
...new Set(plan.terms.concat(plan.aliases.flatMap((alias) => words(alias.split(" -> ")[1] ?? "")))),
|
|
144
|
+
];
|
|
145
|
+
/** Any of the words, and every field the question names. */
|
|
146
|
+
export const anyOfTerms = (terms, scope) => `(${terms.join(" OR ")})${scope ? ` ${scope}` : ""}`;
|
|
147
|
+
/**
|
|
148
|
+
* The partial lookup `search messages --discover` runs, for a store that has no discovery of its own: a question's
|
|
149
|
+
* content words joined by OR, its fields kept. `undefined` when the text is not a question in natural language, so
|
|
150
|
+
* words and query syntax are searched as typed.
|
|
151
|
+
*/
|
|
152
|
+
export const questionLookup = (text) => {
|
|
153
|
+
let plan;
|
|
154
|
+
try {
|
|
155
|
+
plan = planQuestion(questionText(text));
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
// A query the messages language refuses is not a plain question; the store's own parser answers it.
|
|
159
|
+
if (error instanceof CliError)
|
|
160
|
+
return undefined;
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
const terms = plan.changed ? lookupTerms(plan) : [];
|
|
164
|
+
return terms.length ? anyOfTerms(terms, plan.scope) : undefined;
|
|
165
|
+
};
|
|
@@ -8,6 +8,8 @@ export interface EventsQuery {
|
|
|
8
8
|
accounts?: readonly AccountKey[];
|
|
9
9
|
/** Every word as written: no stems. */
|
|
10
10
|
exact?: boolean;
|
|
11
|
+
/** A question typed as a sentence finds what holds some of its words, as `search messages --discover` does. */
|
|
12
|
+
discover?: boolean;
|
|
11
13
|
/** Latest start first instead of best first. */
|
|
12
14
|
newest?: boolean;
|
|
13
15
|
/** For `date:`, which reads an event's start — the zone a bare day is read in. */
|
|
@@ -43,6 +45,8 @@ export interface FoundEvent {
|
|
|
43
45
|
/** When the store's copy last changed. */
|
|
44
46
|
updated_at: string;
|
|
45
47
|
found_by: ("words" | "meaning")[];
|
|
48
|
+
/** The cosine of its series' nearest piece, when meaning found it: comparable with other records under the same model. */
|
|
49
|
+
meaning_score?: number;
|
|
46
50
|
}
|
|
47
51
|
export interface EventsFound {
|
|
48
52
|
query: string;
|
|
@@ -64,4 +68,4 @@ export declare const foundEvent: (hit: Omit<EventHit, "relevance">, query: strin
|
|
|
64
68
|
* by reciprocal rank: title, cleaned description, place and attendees' names. The occurrences of one series are one
|
|
65
69
|
* hit. Working location, focus time, out of office, birthdays and busy-only slots are never found.
|
|
66
70
|
*/
|
|
67
|
-
export declare const searchEvents: (store: MessageStore, query: string, { limit, offset, accounts, exact, newest, timezone, signal, candidates, env, command, embedders, }?: EventsQuery) => Promise<EventsFound>;
|
|
71
|
+
export declare const searchEvents: (store: MessageStore, query: string, { limit, offset, accounts, exact, discover, newest, timezone, signal, candidates, env, command, embedders, }?: EventsQuery) => Promise<EventsFound>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
2
|
import { kindIn } from "../domain/providers.js";
|
|
3
3
|
import { meaningFloor } from "../embeddings/models.js";
|
|
4
|
+
import { questionLookup, questionText } from "../search/question-plan.js";
|
|
4
5
|
import { excerpt } from "./excerpt.js";
|
|
5
6
|
import { localModel, RRF_K, storeQuery, withLocal } from "./notes-search.js";
|
|
6
7
|
import { plainQueryWords } from "./notes-title-tier.js";
|
|
@@ -51,17 +52,18 @@ const keyOf = ({ event }) => event.eventSeriesId === null ? `e${event.id}` : `s$
|
|
|
51
52
|
* by reciprocal rank: title, cleaned description, place and attendees' names. The occurrences of one series are one
|
|
52
53
|
* hit. Working location, focus time, out of office, birthdays and busy-only slots are never found.
|
|
53
54
|
*/
|
|
54
|
-
export const searchEvents = async (store, query, { limit = 20, offset = 0, accounts, exact, newest, timezone, signal, candidates, env, command, embedders, } = {}) => {
|
|
55
|
+
export const searchEvents = async (store, query, { limit = 20, offset = 0, accounts, exact, discover, newest, timezone, signal, candidates, env, command, embedders, } = {}) => {
|
|
55
56
|
if (!query.trim())
|
|
56
57
|
throw new CliError("validation_error", "say what to find in the events");
|
|
57
|
-
const
|
|
58
|
+
const asked = discover && !exact && !newest ? questionLookup(query) : undefined;
|
|
59
|
+
const { root, stemmer } = await storeQuery(store, asked ?? query, {
|
|
58
60
|
...(exact ? { exact: true } : {}),
|
|
59
61
|
...(timezone === undefined ? {} : { timezone }),
|
|
60
62
|
});
|
|
61
63
|
const accountIds = accounts === undefined ? undefined : await accountIdsOf(store, accounts);
|
|
62
64
|
if (accountIds?.length === 0)
|
|
63
65
|
return { query, by: "words", hits: [], has_more: false };
|
|
64
|
-
const words = plainQueryWords(query);
|
|
66
|
+
const words = plainQueryWords(asked === undefined ? query : questionText(query));
|
|
65
67
|
const window = offset + limit;
|
|
66
68
|
let meaningSkipped;
|
|
67
69
|
let meaning;
|
|
@@ -118,12 +120,16 @@ export const searchEvents = async (store, query, { limit = 20, offset = 0, accou
|
|
|
118
120
|
});
|
|
119
121
|
add(found.items, "words");
|
|
120
122
|
add(meaning, "meaning");
|
|
123
|
+
const cosines = new Map(meaning.map((hit) => [keyOf(hit), hit.score]));
|
|
121
124
|
const ordered = [...ranked.values()].sort((a, b) => b.score - a.score);
|
|
122
125
|
const hasMore = ordered.length > window || found.hasMore;
|
|
123
126
|
return {
|
|
124
127
|
query,
|
|
125
128
|
by,
|
|
126
|
-
hits: ordered.slice(offset, window).map(({ hit, by: halves }) =>
|
|
129
|
+
hits: ordered.slice(offset, window).map(({ hit, by: halves }) => {
|
|
130
|
+
const cosine = cosines.get(keyOf(hit));
|
|
131
|
+
return { ...foundEvent(hit, query, halves), ...(cosine === undefined ? {} : { meaning_score: cosine }) };
|
|
132
|
+
}),
|
|
127
133
|
has_more: hasMore,
|
|
128
134
|
...(hasMore && window <= MAX_EVENTS_OFFSET ? { next_offset: window } : {}),
|
|
129
135
|
...truncated,
|
package/dist/services/index.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export { createMeetingProjectCandidateReader } from "./meeting-project-candidate
|
|
|
87
87
|
export { readMeetingProjectDiscovery } from "./meeting-project-discovery.js";
|
|
88
88
|
export { type MeetingReviewRequest, readMeetingReview } from "./meeting-review.js";
|
|
89
89
|
export { type MeetingSearchesService, type MeetingSearchParams, meetingSearchesService, type SavedMeetingSearch, } from "./meeting-searches.js";
|
|
90
|
-
export { type MeetingStatsRequest, readMeetingStats } from "./meeting-stats.js";
|
|
90
|
+
export { type MeetingCountsRequest, type MeetingStatsRequest, readMeetingCounts, readMeetingStats, } from "./meeting-stats.js";
|
|
91
91
|
export { chatIdIn, storedChatId } from "./message-resolution.js";
|
|
92
92
|
export type { AroundWindow, FoundMessage, ListWindow, MessagesService, MessageTarget, Pinned, Reacted, SearchFound, SearchQuery, SendRequest, Starred, } from "./messages.js";
|
|
93
93
|
export { DELETE_AT_ONCE, messagesService } from "./messages.js";
|
package/dist/services/index.js
CHANGED
|
@@ -65,7 +65,7 @@ export { createMeetingProjectCandidateReader } from "./meeting-project-candidate
|
|
|
65
65
|
export { readMeetingProjectDiscovery } from "./meeting-project-discovery.js";
|
|
66
66
|
export { readMeetingReview } from "./meeting-review.js";
|
|
67
67
|
export { meetingSearchesService, } from "./meeting-searches.js";
|
|
68
|
-
export { readMeetingStats } from "./meeting-stats.js";
|
|
68
|
+
export { readMeetingCounts, readMeetingStats, } from "./meeting-stats.js";
|
|
69
69
|
export { chatIdIn, storedChatId } from "./message-resolution.js";
|
|
70
70
|
export { DELETE_AT_ONCE, messagesService } from "./messages.js";
|
|
71
71
|
export { searchStore } from "./messages-archive-search.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MeetingHitSearchRequest, MeetingStats } from "../meetings/index.js";
|
|
1
|
+
import type { MeetingCounts, MeetingCountsGrouping, MeetingHitSearchRequest, MeetingStats } from "../meetings/index.js";
|
|
2
2
|
import type { MessageStore } from "../store/store.js";
|
|
3
3
|
import { resolveMeetingSelection } from "./meeting-hit-search.js";
|
|
4
4
|
export type MeetingStatsRequest = Omit<MeetingHitSearchRequest, "limit" | "after" | "maxReadBytes"> & {
|
|
@@ -11,3 +11,14 @@ export declare const readMeetingStats: (store: Pick<MessageStore, "meetings"> &
|
|
|
11
11
|
}, request: MeetingStatsRequest) => Promise<MeetingStats & {
|
|
12
12
|
selection: Awaited<ReturnType<typeof resolveMeetingSelection>>["selection"];
|
|
13
13
|
}>;
|
|
14
|
+
export type MeetingCountsRequest = Omit<MeetingHitSearchRequest, "limit" | "after" | "maxReadBytes"> & {
|
|
15
|
+
by: MeetingCountsGrouping;
|
|
16
|
+
timeZone?: string;
|
|
17
|
+
limit?: number;
|
|
18
|
+
};
|
|
19
|
+
/** Meetings, minutes and transcripts per day, week or series over the meetings search would read. */
|
|
20
|
+
export declare const readMeetingCounts: (store: Pick<MessageStore, "meetings"> & {
|
|
21
|
+
knowledge?: MessageStore["knowledge"];
|
|
22
|
+
}, request: MeetingCountsRequest) => Promise<MeetingCounts & {
|
|
23
|
+
selection: Awaited<ReturnType<typeof resolveMeetingSelection>>["selection"];
|
|
24
|
+
}>;
|
|
@@ -10,3 +10,15 @@ export const readMeetingStats = async (store, request) => {
|
|
|
10
10
|
});
|
|
11
11
|
return { ...stats, selection };
|
|
12
12
|
};
|
|
13
|
+
/** Meetings, minutes and transcripts per day, week or series over the meetings search would read. */
|
|
14
|
+
export const readMeetingCounts = async (store, request) => {
|
|
15
|
+
const { by, timeZone, limit, ...selecting } = request;
|
|
16
|
+
const { input, selection } = await resolveMeetingSelection(store, selecting);
|
|
17
|
+
const counts = await store.meetings.meetingCounts({
|
|
18
|
+
...input,
|
|
19
|
+
by,
|
|
20
|
+
...(timeZone === undefined ? {} : { timeZone }),
|
|
21
|
+
...(limit === undefined ? {} : { limit }),
|
|
22
|
+
});
|
|
23
|
+
return { ...counts, selection };
|
|
24
|
+
};
|