@wirecat/cli-core 0.563.0 → 0.565.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.
Files changed (143) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/dist/calendar/busy.d.ts +10 -10
  3. package/dist/calendar/busy.js +9 -9
  4. package/dist/calendar/cli/answers.d.ts +7 -8
  5. package/dist/calendar/cli/answers.js +4 -5
  6. package/dist/calendar/cli/bodies.d.ts +33 -0
  7. package/dist/calendar/cli/bodies.js +52 -0
  8. package/dist/calendar/cli/files.d.ts +8 -0
  9. package/dist/calendar/cli/files.js +41 -0
  10. package/dist/calendar/cli/index.d.ts +58 -51
  11. package/dist/calendar/cli/index.js +311 -284
  12. package/dist/calendar/cli/remind.d.ts +2 -11
  13. package/dist/calendar/cli/remind.js +81 -81
  14. package/dist/calendar/cli/search-all.d.ts +3 -8
  15. package/dist/calendar/cli/search-all.js +47 -42
  16. package/dist/calendar/mcp/index.d.ts +1 -1
  17. package/dist/calendar/prepare.d.ts +1 -1
  18. package/dist/calendar/testing/answers.d.ts +27 -5
  19. package/dist/calendar/testing/answers.js +88 -17
  20. package/dist/calendar/testing/writer.d.ts +1 -0
  21. package/dist/calendar/testing/writer.js +6 -0
  22. package/dist/calendar/writer.d.ts +6 -1
  23. package/dist/calendar/writes.d.ts +3 -1
  24. package/dist/calendar/writes.js +42 -20
  25. package/dist/cli/config-command.js +1 -1
  26. package/dist/cli/config-issues.d.ts +3 -1
  27. package/dist/cli/config-issues.js +2 -2
  28. package/dist/cli/contract-evidence.d.ts +1 -1
  29. package/dist/cli/early-refusal.d.ts +2 -1
  30. package/dist/cli/early-refusal.js +4 -2
  31. package/dist/cli/entry.d.ts +6 -2
  32. package/dist/cli/entry.js +4 -1
  33. package/dist/cli/envelope.d.ts +2 -2
  34. package/dist/cli/index.d.ts +1 -0
  35. package/dist/cli/index.js +1 -0
  36. package/dist/cli/messenger/archive-commands.js +17 -0
  37. package/dist/cli/messenger/catch-up-answers.d.ts +5 -0
  38. package/dist/cli/messenger/catch-up-answers.js +3 -0
  39. package/dist/cli/messenger/search-answers.d.ts +1 -0
  40. package/dist/cli/messenger/search-answers.js +1 -1
  41. package/dist/cli/messenger/stats-answers.d.ts +3 -3
  42. package/dist/cli/messenger/stats-answers.js +21 -9
  43. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  44. package/dist/cli/messenger/tasks-command.js +35 -14
  45. package/dist/cli/permission-keys.d.ts +3 -1
  46. package/dist/cli/permission-keys.js +2 -1
  47. package/dist/cli/preview.d.ts +12 -0
  48. package/dist/cli/preview.js +8 -1
  49. package/dist/cli/settings.d.ts +2 -0
  50. package/dist/cli/settings.js +8 -1
  51. package/dist/conversations/mail-text.d.ts +6 -0
  52. package/dist/conversations/mail-text.js +73 -0
  53. package/dist/domain/calendar.d.ts +2 -0
  54. package/dist/domain/calendar.js +6 -0
  55. package/dist/entities/chat-stats.d.ts +16 -2
  56. package/dist/entities/chat-stats.js +18 -3
  57. package/dist/entities/index.d.ts +50 -802
  58. package/dist/entities/index.js +23 -16
  59. package/dist/entities/mail-counts.d.ts +15 -0
  60. package/dist/entities/mail-counts.js +16 -0
  61. package/dist/entities/meeting.d.ts +1 -1
  62. package/dist/entities/meeting.js +1 -1
  63. package/dist/entities/store-records.d.ts +8 -0
  64. package/dist/entities/store-records.js +8 -0
  65. package/dist/errors/reasons.js +19 -1
  66. package/dist/mail/cli/index.d.ts +5 -0
  67. package/dist/mail/cli/index.js +27 -3
  68. package/dist/mail/cli/review.js +11 -4
  69. package/dist/mail/index.d.ts +2 -0
  70. package/dist/mail/index.js +2 -0
  71. package/dist/mail/stats.d.ts +50 -0
  72. package/dist/mail/stats.js +60 -0
  73. package/dist/mail/whole.d.ts +4 -4
  74. package/dist/mail/whole.js +4 -3
  75. package/dist/mcp/meeting-preparation.d.ts +1 -1
  76. package/dist/mcp/meeting-project-candidates.d.ts +1 -1
  77. package/dist/mcp/meeting-series-inventory.d.ts +1 -1
  78. package/dist/meetings/index.d.ts +2 -2
  79. package/dist/meetings/index.js +2 -2
  80. package/dist/meetings/mcp/index.d.ts +15 -15
  81. package/dist/meetings/mcp/passage.d.ts +1 -1
  82. package/dist/meetings/mcp/project-evidence.d.ts +1 -1
  83. package/dist/meetings/mcp/search.d.ts +1 -1
  84. package/dist/meetings/read-answers.d.ts +22 -1
  85. package/dist/meetings/read-answers.js +38 -0
  86. package/dist/meetings/stats.d.ts +44 -0
  87. package/dist/meetings/stats.js +77 -7
  88. package/dist/search/lucene/registry.js +2 -1
  89. package/dist/search/question-plan.d.ts +16 -0
  90. package/dist/search/question-plan.js +34 -0
  91. package/dist/services/events-search.d.ts +5 -1
  92. package/dist/services/events-search.js +10 -4
  93. package/dist/services/index.d.ts +1 -1
  94. package/dist/services/index.js +1 -1
  95. package/dist/services/meeting-stats.d.ts +12 -1
  96. package/dist/services/meeting-stats.js +12 -0
  97. package/dist/services/messages-discovery.d.ts +1 -5
  98. package/dist/services/messages-discovery.js +5 -18
  99. package/dist/services/messages-meaning.js +25 -3
  100. package/dist/services/messages.js +4 -3
  101. package/dist/services/metadata.d.ts +1 -1
  102. package/dist/services/notes-search.d.ts +5 -1
  103. package/dist/services/notes-search.js +16 -5
  104. package/dist/services/proposals/ids.d.ts +2 -2
  105. package/dist/services/proposals/ids.js +5 -3
  106. package/dist/services/proposals/kinds/findings.d.ts +1 -1
  107. package/dist/services/proposals/kinds/findings.js +1 -1
  108. package/dist/services/proposals/kinds/meeting-findings.d.ts +7 -0
  109. package/dist/services/proposals/kinds/meeting-findings.js +73 -0
  110. package/dist/services/proposals/kinds/task-close.d.ts +7 -0
  111. package/dist/services/proposals/kinds/task-close.js +76 -0
  112. package/dist/services/proposals/registry.js +4 -0
  113. package/dist/services/search-all.d.ts +5 -0
  114. package/dist/services/search-all.js +47 -14
  115. package/dist/services/task-stats.d.ts +41 -0
  116. package/dist/services/task-stats.js +126 -0
  117. package/dist/services/tasks.d.ts +17 -4
  118. package/dist/services/tasks.js +31 -14
  119. package/dist/store/chats.d.ts +3 -1
  120. package/dist/store/chats.js +6 -0
  121. package/dist/store/decisions.d.ts +4 -2
  122. package/dist/store/decisions.js +5 -3
  123. package/dist/store/exports.d.ts +1 -1
  124. package/dist/store/exports.js +1 -1
  125. package/dist/store/index.d.ts +2 -1
  126. package/dist/store/index.js +1 -1
  127. package/dist/store/sqlite/completeness.d.ts +17 -0
  128. package/dist/store/sqlite/completeness.js +49 -13
  129. package/dist/store/sqlite/lucene-fields.js +2 -0
  130. package/dist/store/sqlite/lucene-groups.d.ts +15 -0
  131. package/dist/store/sqlite/lucene-groups.js +21 -0
  132. package/dist/store/sqlite/lucene.d.ts +2 -9
  133. package/dist/store/sqlite/lucene.js +1 -12
  134. package/dist/store/sqlite/meeting-stats.d.ts +2 -0
  135. package/dist/store/sqlite/meeting-stats.js +27 -1
  136. package/dist/store/sqlite/retention.js +2 -7
  137. package/dist/store/sqlite/task-board.js +2 -0
  138. package/dist/store/sqlite/task-stats.d.ts +32 -0
  139. package/dist/store/sqlite/task-stats.js +45 -0
  140. package/dist/store/sqlite/tasks.d.ts +2 -1
  141. package/dist/store/sqlite/tasks.js +2 -0
  142. package/package.json +3 -3
  143. 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;
@@ -57,7 +57,7 @@ export declare const callMeetingPassageTool: (read: MeetingPassageReader, input:
57
57
  }[];
58
58
  structuredContent: {
59
59
  error: {
60
- code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
60
+ code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "conflict" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
61
61
  reason: string;
62
62
  message: string;
63
63
  param?: string | undefined;
@@ -51,7 +51,7 @@ export declare const callMeetingProjectEvidenceTool: (read: MeetingProjectDiscov
51
51
  }[];
52
52
  structuredContent: {
53
53
  error: {
54
- code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
54
+ code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "conflict" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
55
55
  reason: string;
56
56
  message: string;
57
57
  param?: string | undefined;
@@ -94,7 +94,7 @@ export declare const callMeetingSearchTool: (search: MeetingHitSearch, input: un
94
94
  }[];
95
95
  structuredContent: {
96
96
  error: {
97
- code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
97
+ code: "authentication_error" | "cancelled" | "configuration_error" | "confirmation_required" | "conflict" | "internal_error" | "invalid_response" | "network_error" | "not_found" | "outcome_unknown" | "permission_error" | "provider_error" | "provider_unavailable" | "rate_limited" | "timeout" | "validation_error";
98
98
  reason: string;
99
99
  message: string;
100
100
  param?: string | 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({
@@ -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
@@ -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 = new Intl.DateTimeFormat("en-US", {
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 { root, stemmer } = await storeQuery(store, query, {
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 }) => foundEvent(hit, query, 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,
@@ -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";
@@ -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";