@taskless-app/shared 0.1.30 → 0.1.32

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.
@@ -4,19 +4,29 @@ import { z } from "zod";
4
4
  * знает, была ли подписка и шли ли PCM-кадры; здесь мы её читаем.
5
5
  *
6
6
  * `not_subscribed` — публиковал аудио, а рекордера на дорожке не было ни разу;
7
+ * `track_unseen` — дорожка БЫЛА, а агент её не увидел (v6, TAS-536). Доказано
8
+ * одним из двух: комната называла человека активным
9
+ * говорящим (это считает SFU по звуку публикующей стороны и
10
+ * рассылает всем — мимо наших подписок), либо у него висели
11
+ * неподписанные публикации. Дейли 18.08: Дмитрий Гребенев
12
+ * отвечал 102 секунды, его слышали все, Сергей поблагодарил
13
+ * по имени — а в отчёте он «аудио не публиковал»;
7
14
  * `no_track` — аудио не публиковал ВОВСЕ, но просидел в комнате дольше
8
15
  * порога сверки присутствия (пять минут). Про этих людей до
9
16
  * TAS-511 отчёт говорил «слушатель»: Дима Гальянов, 39 минут
10
- * в звонке, ноль реплик — и ни строки о нём в логе агента;
17
+ * в звонке, ноль реплик — и ни строки о нём в логе агента.
18
+ * С v6 это ровно «микрофон не включали»: признаков дорожки
19
+ * нет ни одного, и раньше сюда же уезжала потеря;
11
20
  * `no_audio` — подписка была, но ни одного кадра не доехало;
12
21
  * `suppressed` — сегменты распознавались, но их выбрасывал анти-дубль
13
22
  * «участник вышел» (собственно баг PRJ3-101).
14
23
  *
15
24
  * Молчавших и слушателей здесь НЕТ: их ноль объяснён, и сигналом он не
16
- * является — иначе предупреждение висело бы на каждом созвоне. `no_track`
17
- * ровно граница между этими двумя случаями, и проводит её время в комнате.
25
+ * является — иначе предупреждение висело бы на каждом созвоне. Границу между
26
+ * «слушал» и «не слышали» проводит время в комнате, а границу между «не
27
+ * включал микрофон» и «не увидели дорожку» — доказательство, а не время.
18
28
  */
19
- export declare const TranscriptCoverageGapReason: z.ZodEnum<["not_subscribed", "no_track", "no_audio", "suppressed"]>;
29
+ export declare const TranscriptCoverageGapReason: z.ZodEnum<["not_subscribed", "track_unseen", "no_track", "no_audio", "suppressed"]>;
20
30
  export type TranscriptCoverageGapReason = z.infer<typeof TranscriptCoverageGapReason>;
21
31
  /**
22
32
  * Отчёт агента за одну сессию.
@@ -33,7 +43,17 @@ export declare const TranscriptCoverage: z.ZodObject<{
33
43
  * комнате дольше пяти минут без единой дорожки, теперь gap с причиной
34
44
  * `no_track`, а не слушатель; v4 (TAS-509) — появился `audioStall`;
35
45
  * v5 (TAS-524) — появились `window` и `jobId`: отчёт наконец говорит, за
36
- * какой отрезок он отвечает.
46
+ * какой отрезок он отвечает; v6 (TAS-536) — `no_track` разошёлся надвое:
47
+ * «микрофон не включали» остался `no_track`, а доказанная потеря стала
48
+ * `track_unseen`; появились `presenceChecks`, `heardWithoutTrack` и
49
+ * `evidence` у gap'ов.
50
+ *
51
+ * ВЫКАТКА v6: агент едет образом, api — своим релизом, и порядок не
52
+ * гарантирован. Новый агент шлёт причину `track_unseen`, которой у старого
53
+ * api нет в enum, — такой отчёт не разберётся целиком и станет `null`, то
54
+ * есть «данных нет». Это безопасная сторона (звук держим, ничего не удаляем),
55
+ * но api с этой схемой должен уехать НЕ ПОЗЖЕ образа агента. Ровно те же
56
+ * грабли были у `no_track` в TAS-511, см. api calls/audioReconcile.test.ts.
37
57
  *
38
58
  * ПРИ ЧТЕНИИ поля разбираются по одному, отсутствующие дают значение по
39
59
  * умолчанию — агент и api выкатываются раздельно, и отчёт старого образа не
@@ -103,20 +123,31 @@ export declare const TranscriptCoverage: z.ZodObject<{
103
123
  identity: string;
104
124
  name?: string | undefined;
105
125
  }>, "many">>;
106
- /** Те, у кого речь могла потеряться. Список урезан, счётчик — нет. */
126
+ /**
127
+ * Те, у кого речь могла потеряться. Список урезан, счётчик — нет.
128
+ *
129
+ * `evidence` (v6, TAS-536) — чем причина доказана, словами: «комната слышала
130
+ * его как говорящего (4)», «проверок присутствия: 20, публикаций не видели ни
131
+ * одной». Разбирать такой случай будут по одному отчёту, без доступа к логам
132
+ * того контейнера, а `track_unseen` без основания — это утверждение, которому
133
+ * нечем верить. Пусто у отчётов старее v6 и там, где причина однозначна.
134
+ */
107
135
  gaps: z.ZodDefault<z.ZodArray<z.ZodObject<{
108
136
  identity: z.ZodString;
109
137
  name: z.ZodDefault<z.ZodString>;
110
138
  } & {
111
- reason: z.ZodEnum<["not_subscribed", "no_track", "no_audio", "suppressed"]>;
139
+ reason: z.ZodEnum<["not_subscribed", "track_unseen", "no_track", "no_audio", "suppressed"]>;
140
+ evidence: z.ZodDefault<z.ZodString>;
112
141
  }, "strip", z.ZodTypeAny, {
113
142
  identity: string;
114
143
  name: string;
115
- reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed";
144
+ reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed" | "track_unseen";
145
+ evidence: string;
116
146
  }, {
117
147
  identity: string;
118
148
  name?: string | undefined;
119
- reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed";
149
+ reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed" | "track_unseen";
150
+ evidence?: string | undefined;
120
151
  }>, "many">>;
121
152
  gapCount: z.ZodDefault<z.ZodNumber>;
122
153
  /**
@@ -150,6 +181,30 @@ export declare const TranscriptCoverage: z.ZodObject<{
150
181
  * дожимать было нечего — публикации от него нет вовсе.
151
182
  */
152
183
  presenceProbes: z.ZodDefault<z.ZodNumber>;
184
+ /**
185
+ * А сколько раз сверка присутствия вообще СМОТРЕЛА на людей без дорожки
186
+ * (v6, TAS-536).
187
+ *
188
+ * Ровно то, чего не хватало при разборе дейли 18.08: в отчёте стояло
189
+ * `presenceProbes: 0` при восьми gap'ах `no_track`, и это прочли как «сверка
190
+ * не отработала ни разу». Отработала: флаг `no_track` ставит ТОЛЬКО она, то
191
+ * есть проходов было минимум восемь — просто дожимать было нечего, публикаций
192
+ * у этих людей агент не видел ни одной («дожали публикаций: 0» в логе).
193
+ *
194
+ * Ноль ЗДЕСЬ при непустых gap'ах — вот это уже поломка самой сверки, и она
195
+ * наконец отличима от «сверка работала, а дожимать нечего».
196
+ */
197
+ presenceChecks: z.ZodDefault<z.ZodNumber>;
198
+ /**
199
+ * Скольких комната слышала как говорящих, а аудио-дорожки от них агент не
200
+ * видел (v6, TAS-536).
201
+ *
202
+ * Самое дорогое число в отчёте: больше нуля значит, что человек говорил, его
203
+ * слышали остальные, и мимо расшифровки прошла живая речь — не тишина, не
204
+ * режим слушателя. До v6 такой человек попадал в `no_track` вперемешку с
205
+ * теми, кто просто не включал микрофон, и потеря была невидима.
206
+ */
207
+ heardWithoutTrack: z.ZodDefault<z.ZodNumber>;
153
208
  /**
154
209
  * Чем кончился выход джоба. `drain: "cancelled" | "abandoned"` означает, что
155
210
  * хвост очереди распознавания не доехал до базы, — снаружи это выглядит как
@@ -269,7 +324,8 @@ export declare const TranscriptCoverage: z.ZodObject<{
269
324
  gaps: {
270
325
  identity: string;
271
326
  name: string;
272
- reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed";
327
+ reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed" | "track_unseen";
328
+ evidence: string;
273
329
  }[];
274
330
  gapCount: number;
275
331
  repairedSubscriptions: number;
@@ -281,6 +337,8 @@ export declare const TranscriptCoverage: z.ZodObject<{
281
337
  stuckCount: number;
282
338
  suppressedSegments: number;
283
339
  presenceProbes: number;
340
+ presenceChecks: number;
341
+ heardWithoutTrack: number;
284
342
  shutdown: {
285
343
  drain: "abandoned" | "cancelled" | "drained";
286
344
  unfinishedRecorders: number;
@@ -323,7 +381,8 @@ export declare const TranscriptCoverage: z.ZodObject<{
323
381
  gaps?: {
324
382
  identity: string;
325
383
  name?: string | undefined;
326
- reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed";
384
+ reason: "no_audio" | "no_track" | "not_subscribed" | "suppressed" | "track_unseen";
385
+ evidence?: string | undefined;
327
386
  }[] | undefined;
328
387
  gapCount?: number | undefined;
329
388
  repairedSubscriptions?: number | undefined;
@@ -335,6 +394,8 @@ export declare const TranscriptCoverage: z.ZodObject<{
335
394
  stuckCount?: number | undefined;
336
395
  suppressedSegments?: number | undefined;
337
396
  presenceProbes?: number | undefined;
397
+ presenceChecks?: number | undefined;
398
+ heardWithoutTrack?: number | undefined;
338
399
  shutdown?: {
339
400
  drain?: "abandoned" | "cancelled" | "drained" | undefined;
340
401
  unfinishedRecorders?: number | undefined;
@@ -388,9 +449,91 @@ export declare function hasTranscriptCoverageGap(coverage: TranscriptCoverage |
388
449
  * увидеть, не разбирая базу руками.
389
450
  */
390
451
  export declare function transcriptCoverageSummary(coverage: TranscriptCoverage): string;
452
+ /**
453
+ * Те же цифры без головы фразы — «0 дорожек из 1 участника».
454
+ *
455
+ * Отдельно от `transcriptCoverageSummary`, потому что с PRJ3-109 эти цифры
456
+ * иногда приходится вставлять в другую фразу: когда отчёт покрывает не весь
457
+ * звонок, «Распознавали 0» в начале строки читается как приговор всей встрече.
458
+ */
459
+ export declare function transcriptCoverageCounts(coverage: TranscriptCoverage): string;
391
460
  /** «1 дорожку», «2 дорожки», «5 дорожек» — винительный, как в «распознавали N». */
392
461
  export declare function pluralTracks(n: number): string;
393
462
  /** «из 1 участника», «из 13 участников» — родительный, как в «из N». */
394
463
  export declare function pluralPeople(n: number): string;
464
+ /** «1 человек», «2 человека», «13 человек» — именительный, как в «говорят N». */
465
+ export declare function pluralPersons(n: number): string;
466
+ /**
467
+ * Насколько окно отчёта может не дотягивать до границ звонка.
468
+ *
469
+ * Тот же допуск и по той же причине, что у сверки звука в api
470
+ * (`calls/audioReconcile.ts`): джоб диспатчится и финализируется не в ту же
471
+ * секунду, в которую началась и кончилась сессия, и держать из-за этих секунд
472
+ * пометку «отчёт не про весь звонок» значит показывать её всегда. Минута —
473
+ * заведомо больше этой разницы и заведомо меньше перезапуска джоба, ради
474
+ * которого пометка и заводится.
475
+ */
476
+ export declare const COVERAGE_WINDOW_SLACK_MS = 60000;
477
+ /** Кто и сколько наговорил по базе (`CallTranscript` этой сессии) — ФАКТ, с
478
+ * которым сверяется отчёт агента. */
479
+ export interface TranscriptSpeakerFact {
480
+ /** userId говорящего; null у гостей — у них `speakerId` в базе пуст. */
481
+ id: string | null;
482
+ name: string;
483
+ lines: number;
484
+ }
485
+ export interface TranscriptCoverageContext {
486
+ /**
487
+ * Границы ЗВОНКА (сессии), с которыми сверяется окно отчёта. `to: null` —
488
+ * сессия ещё идёт.
489
+ */
490
+ callSpan?: {
491
+ from: string | Date;
492
+ to?: string | Date | null;
493
+ } | null;
494
+ /** Факт из базы: кто попал в расшифровку. `null`/`undefined` — факта нет,
495
+ * и тогда отчёт остаётся единственным источником. */
496
+ speakers?: TranscriptSpeakerFact[] | null;
497
+ }
498
+ /**
499
+ * Что на самом деле известно про покрытие — семантика без копирайта.
500
+ *
501
+ * PRJ3-109. Отчёт агента описывает ОКНО ДЖОБА, а страница читала его как итог
502
+ * по звонку: на часовом созвоне с 800 репликами и тринадцатью говорящими висело
503
+ * «Распознавали 0 дорожек из 1 участника» — отчёт хвостового джоба, который
504
+ * успел увидеть одного человека. Здесь мы отвечаем на три вопроса, а словами их
505
+ * называет интерфейс (`TranscriptCoverageNotice`):
506
+ *
507
+ * 1. покрывает ли отчёт весь звонок (`coversCall`);
508
+ * 2. не разошёлся ли он с базой (`underReported`) — в сессии остаётся ПОСЛЕДНИЙ
509
+ * отчёт, а не сумма, поэтому вклад умерших джобов из цифр исчезает;
510
+ * 3. какие дыры фактом уже сняты (`resolvedGapCount`) — человек, у которого в
511
+ * расшифровке 238 реплик, потерянным не является, чей бы отчёт его так ни
512
+ * называл.
513
+ */
514
+ export interface TranscriptCoverageReading {
515
+ /** Покрывает ли отчёт звонок целиком. `null` — сверить не с чем: окна нет
516
+ * (агент старее v5) либо границы звонка не переданы. */
517
+ coversCall: boolean | null;
518
+ /** Отрезок, за который отчёт отвечает. */
519
+ window: {
520
+ from: Date;
521
+ to: Date;
522
+ } | null;
523
+ /** Сколько человек реально попало в расшифровку. `null` — факта нет. */
524
+ transcriptSpeakers: number | null;
525
+ /** Отчёт насчитал меньше дорожек, чем в базе говоривших: цифры описывают не
526
+ * весь звонок. Это расхождение ОТЧЁТА С ДАННЫМИ, а не потеря текста. */
527
+ underReported: boolean;
528
+ /** Дыры, которые фактом не сняты, — в том же порядке, что в отчёте. */
529
+ gaps: TranscriptCoverage["gaps"];
530
+ /** Счётчик дыр за вычетом снятых (у отчёта он бывает больше списка). */
531
+ gapCount: number;
532
+ /** Сколько дыр сняла база: эти люди в расшифровке говорят. */
533
+ resolvedGapCount: number;
534
+ /** Есть ли о чём предупреждать после сверки с фактом. */
535
+ hasGap: boolean;
536
+ }
537
+ export declare function transcriptCoverageReading(coverage: TranscriptCoverage, ctx?: TranscriptCoverageContext): TranscriptCoverageReading;
395
538
  /** Человеческая причина провала — одними словами в UI и в логах. */
396
539
  export declare const TRANSCRIPT_COVERAGE_GAP_LABEL: Record<TranscriptCoverageGapReason, string>;
@@ -19,20 +19,31 @@ import { z } from "zod";
19
19
  * знает, была ли подписка и шли ли PCM-кадры; здесь мы её читаем.
20
20
  *
21
21
  * `not_subscribed` — публиковал аудио, а рекордера на дорожке не было ни разу;
22
+ * `track_unseen` — дорожка БЫЛА, а агент её не увидел (v6, TAS-536). Доказано
23
+ * одним из двух: комната называла человека активным
24
+ * говорящим (это считает SFU по звуку публикующей стороны и
25
+ * рассылает всем — мимо наших подписок), либо у него висели
26
+ * неподписанные публикации. Дейли 18.08: Дмитрий Гребенев
27
+ * отвечал 102 секунды, его слышали все, Сергей поблагодарил
28
+ * по имени — а в отчёте он «аудио не публиковал»;
22
29
  * `no_track` — аудио не публиковал ВОВСЕ, но просидел в комнате дольше
23
30
  * порога сверки присутствия (пять минут). Про этих людей до
24
31
  * TAS-511 отчёт говорил «слушатель»: Дима Гальянов, 39 минут
25
- * в звонке, ноль реплик — и ни строки о нём в логе агента;
32
+ * в звонке, ноль реплик — и ни строки о нём в логе агента.
33
+ * С v6 это ровно «микрофон не включали»: признаков дорожки
34
+ * нет ни одного, и раньше сюда же уезжала потеря;
26
35
  * `no_audio` — подписка была, но ни одного кадра не доехало;
27
36
  * `suppressed` — сегменты распознавались, но их выбрасывал анти-дубль
28
37
  * «участник вышел» (собственно баг PRJ3-101).
29
38
  *
30
39
  * Молчавших и слушателей здесь НЕТ: их ноль объяснён, и сигналом он не
31
- * является — иначе предупреждение висело бы на каждом созвоне. `no_track`
32
- * ровно граница между этими двумя случаями, и проводит её время в комнате.
40
+ * является — иначе предупреждение висело бы на каждом созвоне. Границу между
41
+ * «слушал» и «не слышали» проводит время в комнате, а границу между «не
42
+ * включал микрофон» и «не увидели дорожку» — доказательство, а не время.
33
43
  */
34
44
  export const TranscriptCoverageGapReason = z.enum([
35
45
  "not_subscribed",
46
+ "track_unseen",
36
47
  "no_track",
37
48
  "no_audio",
38
49
  "suppressed",
@@ -56,7 +67,17 @@ export const TranscriptCoverage = z.object({
56
67
  * комнате дольше пяти минут без единой дорожки, теперь gap с причиной
57
68
  * `no_track`, а не слушатель; v4 (TAS-509) — появился `audioStall`;
58
69
  * v5 (TAS-524) — появились `window` и `jobId`: отчёт наконец говорит, за
59
- * какой отрезок он отвечает.
70
+ * какой отрезок он отвечает; v6 (TAS-536) — `no_track` разошёлся надвое:
71
+ * «микрофон не включали» остался `no_track`, а доказанная потеря стала
72
+ * `track_unseen`; появились `presenceChecks`, `heardWithoutTrack` и
73
+ * `evidence` у gap'ов.
74
+ *
75
+ * ВЫКАТКА v6: агент едет образом, api — своим релизом, и порядок не
76
+ * гарантирован. Новый агент шлёт причину `track_unseen`, которой у старого
77
+ * api нет в enum, — такой отчёт не разберётся целиком и станет `null`, то
78
+ * есть «данных нет». Это безопасная сторона (звук держим, ничего не удаляем),
79
+ * но api с этой схемой должен уехать НЕ ПОЗЖЕ образа агента. Ровно те же
80
+ * грабли были у `no_track` в TAS-511, см. api calls/audioReconcile.test.ts.
60
81
  *
61
82
  * ПРИ ЧТЕНИИ поля разбираются по одному, отсутствующие дают значение по
62
83
  * умолчанию — агент и api выкатываются раздельно, и отчёт старого образа не
@@ -105,8 +126,21 @@ export const TranscriptCoverage = z.object({
105
126
  silent: z.array(Person).default([]),
106
127
  /** Аудио не публиковал вовсе — слушатель, ноль реплик ожидаем. */
107
128
  listeners: z.array(Person).default([]),
108
- /** Те, у кого речь могла потеряться. Список урезан, счётчик — нет. */
109
- gaps: z.array(Person.extend({ reason: TranscriptCoverageGapReason })).default([]),
129
+ /**
130
+ * Те, у кого речь могла потеряться. Список урезан, счётчик — нет.
131
+ *
132
+ * `evidence` (v6, TAS-536) — чем причина доказана, словами: «комната слышала
133
+ * его как говорящего (4)», «проверок присутствия: 20, публикаций не видели ни
134
+ * одной». Разбирать такой случай будут по одному отчёту, без доступа к логам
135
+ * того контейнера, а `track_unseen` без основания — это утверждение, которому
136
+ * нечем верить. Пусто у отчётов старее v6 и там, где причина однозначна.
137
+ */
138
+ gaps: z
139
+ .array(Person.extend({
140
+ reason: TranscriptCoverageGapReason,
141
+ evidence: z.string().default(""),
142
+ }))
143
+ .default([]),
110
144
  gapCount: z.number().int().min(0).default(0),
111
145
  /**
112
146
  * Сколько дорожек реконсиляция починила ПО-НАСТОЯЩЕМУ: дожала подписку, и
@@ -130,6 +164,30 @@ export const TranscriptCoverage = z.object({
130
164
  * дожимать было нечего — публикации от него нет вовсе.
131
165
  */
132
166
  presenceProbes: z.number().int().min(0).default(0),
167
+ /**
168
+ * А сколько раз сверка присутствия вообще СМОТРЕЛА на людей без дорожки
169
+ * (v6, TAS-536).
170
+ *
171
+ * Ровно то, чего не хватало при разборе дейли 18.08: в отчёте стояло
172
+ * `presenceProbes: 0` при восьми gap'ах `no_track`, и это прочли как «сверка
173
+ * не отработала ни разу». Отработала: флаг `no_track` ставит ТОЛЬКО она, то
174
+ * есть проходов было минимум восемь — просто дожимать было нечего, публикаций
175
+ * у этих людей агент не видел ни одной («дожали публикаций: 0» в логе).
176
+ *
177
+ * Ноль ЗДЕСЬ при непустых gap'ах — вот это уже поломка самой сверки, и она
178
+ * наконец отличима от «сверка работала, а дожимать нечего».
179
+ */
180
+ presenceChecks: z.number().int().min(0).default(0),
181
+ /**
182
+ * Скольких комната слышала как говорящих, а аудио-дорожки от них агент не
183
+ * видел (v6, TAS-536).
184
+ *
185
+ * Самое дорогое число в отчёте: больше нуля значит, что человек говорил, его
186
+ * слышали остальные, и мимо расшифровки прошла живая речь — не тишина, не
187
+ * режим слушателя. До v6 такой человек попадал в `no_track` вперемешку с
188
+ * теми, кто просто не включал микрофон, и потеря была невидима.
189
+ */
190
+ heardWithoutTrack: z.number().int().min(0).default(0),
133
191
  /**
134
192
  * Чем кончился выход джоба. `drain: "cancelled" | "abandoned"` означает, что
135
193
  * хвост очереди распознавания не доехал до базы, — снаружи это выглядит как
@@ -218,7 +276,17 @@ export function hasTranscriptCoverageGap(coverage) {
218
276
  * увидеть, не разбирая базу руками.
219
277
  */
220
278
  export function transcriptCoverageSummary(coverage) {
221
- return `Распознавали ${coverage.recognized} ${pluralTracks(coverage.recognized)} из ${coverage.participants} ${pluralPeople(coverage.participants)}`;
279
+ return `Распознавали ${transcriptCoverageCounts(coverage)}`;
280
+ }
281
+ /**
282
+ * Те же цифры без головы фразы — «0 дорожек из 1 участника».
283
+ *
284
+ * Отдельно от `transcriptCoverageSummary`, потому что с PRJ3-109 эти цифры
285
+ * иногда приходится вставлять в другую фразу: когда отчёт покрывает не весь
286
+ * звонок, «Распознавали 0» в начале строки читается как приговор всей встрече.
287
+ */
288
+ export function transcriptCoverageCounts(coverage) {
289
+ return `${coverage.recognized} ${pluralTracks(coverage.recognized)} из ${coverage.participants} ${pluralPeople(coverage.participants)}`;
222
290
  }
223
291
  function plural(n, one, few, many) {
224
292
  const mod100 = n % 100;
@@ -239,9 +307,78 @@ export function pluralTracks(n) {
239
307
  export function pluralPeople(n) {
240
308
  return plural(n, "участника", "участников", "участников");
241
309
  }
310
+ /** «1 человек», «2 человека», «13 человек» — именительный, как в «говорят N». */
311
+ export function pluralPersons(n) {
312
+ return plural(n, "человек", "человека", "человек");
313
+ }
314
+ // ─── PRJ3-109: отчёт за окно джоба ≠ итог по звонку ──────────────────────
315
+ /**
316
+ * Насколько окно отчёта может не дотягивать до границ звонка.
317
+ *
318
+ * Тот же допуск и по той же причине, что у сверки звука в api
319
+ * (`calls/audioReconcile.ts`): джоб диспатчится и финализируется не в ту же
320
+ * секунду, в которую началась и кончилась сессия, и держать из-за этих секунд
321
+ * пометку «отчёт не про весь звонок» значит показывать её всегда. Минута —
322
+ * заведомо больше этой разницы и заведомо меньше перезапуска джоба, ради
323
+ * которого пометка и заводится.
324
+ */
325
+ export const COVERAGE_WINDOW_SLACK_MS = 60_000;
326
+ export function transcriptCoverageReading(coverage, ctx = {}) {
327
+ const window = transcriptCoverageWindow(coverage);
328
+ const speakers = ctx.speakers ?? null;
329
+ const spoke = speakers ? speakers.filter((s) => s.lines > 0) : null;
330
+ // Сняты те дыры, у кого в базе есть реплики. Сверяем по userId: identity
331
+ // участника-мембера в LiveKit — это и есть его userId, а `speakerId` в
332
+ // `CallTranscript` — он же. Гость (identity `guest_*`, speakerId пуст) так не
333
+ // сверяется — по имени сверять нельзя, тёзки и переименования дают ложное
334
+ // «всё хорошо», а цена ошибки здесь — спрятанная потеря речи.
335
+ const spokeIds = new Set((spoke ?? []).map((s) => s.id).filter((id) => !!id));
336
+ const gaps = spokeIds.size > 0
337
+ ? coverage.gaps.filter((g) => !spokeIds.has(g.identity))
338
+ : coverage.gaps;
339
+ const resolvedGapCount = coverage.gaps.length - gaps.length;
340
+ const gapCount = Math.max(0, coverage.gapCount - resolvedGapCount);
341
+ return {
342
+ coversCall: windowCoversCall(window, ctx.callSpan ?? null),
343
+ window,
344
+ transcriptSpeakers: spoke ? spoke.length : null,
345
+ underReported: !!spoke && spoke.length > coverage.recognized,
346
+ gaps,
347
+ gapCount,
348
+ resolvedGapCount,
349
+ hasGap: gapCount > 0,
350
+ };
351
+ }
352
+ /**
353
+ * Покрывает ли окно отчёта весь звонок.
354
+ *
355
+ * У живой сессии (`to` не задан) сверяем ТОЛЬКО начало: конец отчёта у идущего
356
+ * звонка всегда в прошлом — джоб пишет отчёт на переоткрытии сессии, — и
357
+ * сверка по концу означала бы вечное «отчёт не про весь звонок» на каждом
358
+ * здоровом созвоне. А вот отчёт, начавшийся заметно позже начала звонка, — это
359
+ * ровно тот перезапуск джоба, ради которого всё написано.
360
+ */
361
+ function windowCoversCall(window, span) {
362
+ if (!window || !span)
363
+ return null;
364
+ const from = new Date(span.from);
365
+ if (Number.isNaN(from.getTime()))
366
+ return null;
367
+ if (window.from.getTime() - COVERAGE_WINDOW_SLACK_MS > from.getTime())
368
+ return false;
369
+ if (span.to === null || span.to === undefined)
370
+ return true;
371
+ const to = new Date(span.to);
372
+ if (Number.isNaN(to.getTime()))
373
+ return null;
374
+ return window.to.getTime() + COVERAGE_WINDOW_SLACK_MS >= to.getTime();
375
+ }
242
376
  /** Человеческая причина провала — одними словами в UI и в логах. */
243
377
  export const TRANSCRIPT_COVERAGE_GAP_LABEL = {
244
378
  not_subscribed: "дорожку не слушали",
379
+ // TAS-536: два соседних ярлыка — это и есть развод причин. Первый читается
380
+ // как потеря («его слышали, а мы нет»), второй — как объяснимый ноль.
381
+ track_unseen: "его слышали в звонке, а дорожку мы не увидели",
245
382
  no_track: "дорожки от него не было",
246
383
  no_audio: "звук не доехал",
247
384
  suppressed: "речь распозналась, но не записалась",
@@ -1 +1 @@
1
- {"version":3,"file":"transcriptCoverage.js","sourceRoot":"","sources":["../src/transcriptCoverage.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,EAAE;AACF,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,4EAA4E;AAC5E,oDAAoD;AACpD,EAAE;AACF,gEAAgE;AAChE,+EAA+E;AAC/E,2EAA2E;AAC3E,0EAA0E;AAC1E,EAAE;AACF,4EAA4E;AAC5E,iEAAiE;AAEjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,IAAI,CAAC;IAChD,gBAAgB;IAChB,UAAU;IACV,UAAU;IACV,YAAY;CACb,CAAC,CAAC;AAGH,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC7B,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC;;;;;;;;;;;;;;;;;OAiBG;IACH,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9B;;;;;;;;;;;;;OAaG;IACH,MAAM,EAAE,CAAC;SACN,MAAM,CAAC;QACN,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;KACpB,CAAC;SACD,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;IAChB;;+EAE2E;IAC3E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACzC,0EAA0E;IAC1E,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,wEAAwE;IACxE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,gEAAgE;IAChE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,mEAAmE;IACnE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,wEAAwE;IACxE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACnC,kEAAkE;IAClE,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACtC,sEAAsE;IACtE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACjF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C;;;;OAIG;IACH,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACzD;;;OAGG;IACH,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,iFAAiF;IACjF,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC/C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,wEAAwE;IACxE,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD;;;;OAIG;IACH,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD;;;;OAIG;IACH,QAAQ,EAAE,CAAC;SACR,MAAM,CAAC;QACN,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QACvE,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACvD;;;;;;WAMG;QACH,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1C,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KAC9E,CAAC;SACD,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;IAChB;;;;;;OAMG;IACH,UAAU,EAAE,CAAC;SACV,MAAM,CAAC;QACN,0EAA0E;QAC1E,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACzC,4CAA4C;QAC5C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1C,+CAA+C;QAC/C,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/E,6DAA6D;QAC7D,iBAAiB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;KAC9C,CAAC;SACD,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;CACjB,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAY;IAClD,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAmC;IAEnC,MAAM,CAAC,GAAG,QAAQ,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5E,IAAI,EAAE,GAAG,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAmC;IAC1E,OAAO,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAA4B;IACpE,OAAO,gBAAgB,QAAQ,CAAC,UAAU,IAAI,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,YAAY,IAAI,YAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;AACvJ,CAAC;AAED,SAAS,MAAM,CAAC,CAAS,EAAE,GAAW,EAAE,GAAW,EAAE,IAAY;IAC/D,MAAM,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC;IACvB,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9C,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC;IACrB,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5B,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IACzC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;AAC5D,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,6BAA6B,GAAgD;IACxF,cAAc,EAAE,oBAAoB;IACpC,QAAQ,EAAE,yBAAyB;IACnC,QAAQ,EAAE,gBAAgB;IAC1B,UAAU,EAAE,qCAAqC;CAClD,CAAC"}
1
+ {"version":3,"file":"transcriptCoverage.js","sourceRoot":"","sources":["../src/transcriptCoverage.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskless-app/shared",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "description": "Shared types, Zod schemas and design tokens for the Taskless API contract — consumed by @taskless/api, @taskless/web and the Taskless mobile app.",
6
6
  "license": "MIT",