@wirecat/cli-core 0.551.0 → 0.553.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 +83 -0
- package/README.md +1 -1
- package/dist/background/schedules.d.ts +12 -1
- package/dist/background/schedules.js +65 -24
- package/dist/background/units.d.ts +16 -9
- package/dist/background/units.js +26 -11
- package/dist/cli/compact.d.ts +4 -0
- package/dist/cli/compact.js +65 -7
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/meeting-preparation-answer.d.ts +3 -0
- package/dist/cli/meeting-preparation-projects.d.ts +3 -0
- package/dist/cli/meeting-preparation-read.d.ts +3 -0
- package/dist/cli/messenger/context.d.ts +3 -0
- package/dist/cli/messenger/projects-command.js +38 -11
- package/dist/cli/messenger/search-answers.js +8 -1
- package/dist/cli/messenger/serve-catch-up.d.ts +43 -0
- package/dist/cli/messenger/serve-catch-up.js +90 -0
- package/dist/cli/messenger/serve-command.js +54 -1
- package/dist/cli/messenger/serve-relay.d.ts +1 -0
- package/dist/cli/messenger/server-command.d.ts +4 -3
- package/dist/cli/messenger/server-command.js +30 -12
- package/dist/cli/program.js +9 -4
- package/dist/cli/project-evidence.d.ts +33 -4
- package/dist/cli/project-evidence.js +38 -4
- package/dist/cli/project-status-rows.js +11 -1
- package/dist/cli/project-status.d.ts +24 -0
- package/dist/cli/project-status.js +52 -9
- package/dist/cli/schedule/schedule-command.js +5 -1
- package/dist/entities/hit.d.ts +5 -0
- package/dist/entities/hit.js +5 -0
- package/dist/entities/index.d.ts +27 -0
- package/dist/entities/listener.d.ts +6 -0
- package/dist/entities/listener.js +1 -0
- package/dist/entities/meeting-preparation.d.ts +3 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/mcp/meeting-preparation.d.ts +3 -0
- package/dist/meetings/answers.d.ts +5 -0
- package/dist/meetings/answers.js +8 -0
- package/dist/meetings/cli/index.js +14 -0
- package/dist/meetings/index.d.ts +2 -1
- package/dist/meetings/index.js +1 -0
- package/dist/meetings/ingest.js +2 -1
- package/dist/meetings/language.d.ts +5 -0
- package/dist/meetings/language.js +57 -0
- package/dist/meetings/mcp/search.d.ts +3 -0
- package/dist/meetings/project-evidence-answer.d.ts +3 -0
- package/dist/meetings/result-items.d.ts +9 -0
- package/dist/meetings/result-items.js +7 -0
- package/dist/meetings/store.d.ts +22 -0
- package/dist/meetings/testing/memory.js +3 -0
- package/dist/meetings/transcription.d.ts +2 -0
- package/dist/meetings/transcription.js +5 -1
- package/dist/search/lucene/types.d.ts +7 -0
- package/dist/search/lucene/types.js +11 -1
- package/dist/services/index.d.ts +4 -3
- package/dist/services/index.js +4 -3
- package/dist/services/link-scorers/index.d.ts +1 -1
- package/dist/services/link-scorers/index.js +1 -1
- package/dist/services/link-scorers/scan.d.ts +0 -1
- package/dist/services/link-scorers/scan.js +1 -1
- package/dist/services/messages-archive-search.js +3 -2
- package/dist/services/messages-combined.js +2 -2
- package/dist/services/messages-discovery.js +2 -2
- package/dist/services/notes-search.js +4 -5
- package/dist/services/project-purposes.d.ts +28 -0
- package/dist/services/project-purposes.js +41 -0
- package/dist/services/project-status/index.d.ts +22 -8
- package/dist/services/project-status/index.js +17 -9
- package/dist/services/search-all-meetings.d.ts +3 -0
- package/dist/services/search-all-meetings.js +6 -1
- package/dist/services/stored-project-gathering.d.ts +79 -4
- package/dist/services/stored-project-gathering.js +171 -22
- package/dist/speech/local.d.ts +2 -0
- package/dist/speech/local.js +8 -1
- package/dist/speech/models.d.ts +2 -0
- package/dist/speech/models.js +2 -0
- package/dist/store/driver.d.ts +11 -0
- package/dist/store/driver.js +13 -0
- package/dist/store/drivers/node-sqlite.d.ts +1 -1
- package/dist/store/drivers/node-sqlite.js +3 -0
- package/dist/store/messages.d.ts +2 -1
- package/dist/store/messages.js +2 -2
- package/dist/store/sqlite/lucene.d.ts +1 -1
- package/dist/store/sqlite/lucene.js +33 -7
- package/dist/store/sqlite/meeting-hit-search.js +10 -3
- package/dist/store/sqlite/meeting-languages.d.ts +4 -0
- package/dist/store/sqlite/meeting-languages.js +36 -0
- package/dist/store/sqlite/meetings.d.ts +2 -1
- package/dist/store/sqlite/meetings.js +9 -3
- package/dist/store/sqlite/note-search.js +2 -2
- package/dist/store/sqlite/reads.d.ts +1 -1
- package/dist/store/sqlite/reads.js +2 -2
- package/package.json +1 -1
- package/parity.json +10 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { MAX_NEXT_STEPS } from "@wirecat/cli-base/standards";
|
|
2
3
|
import * as v from "valibot";
|
|
3
4
|
import { dateWindow, momentOf } from "../services/moment.js";
|
|
4
5
|
import { coverageGaps, heldPhrase } from "./messenger/search-answers.js";
|
|
6
|
+
import { heldInWindow } from "./project-status.js";
|
|
5
7
|
/** Days back (`90d`) or a start date (`2026-09-01`); either way the run freezes its end, so a resume keeps it. */
|
|
6
8
|
export const projectWindowOf = (value) => {
|
|
7
9
|
const days = /^([1-9]\d{0,2})d$/.exec(value)?.[1];
|
|
@@ -33,10 +35,24 @@ const coverageRows = ({ linkedChats }) => linkedChats.flatMap((one) => one.compl
|
|
|
33
35
|
* The zone a typed start date was read in, and the window the run used, in UTC. `complete` is "the answer is whole":
|
|
34
36
|
* reads finished and every linked chat held in full; the answer's own `complete` stays "reads finished".
|
|
35
37
|
*/
|
|
36
|
-
export const projectAnswerParts = (sinceTime, found) => {
|
|
38
|
+
export const projectAnswerParts = (sinceTime, found, hints) => {
|
|
37
39
|
const { window } = found;
|
|
38
40
|
const { timezone } = dateWindow({ sinceTime }).meta;
|
|
39
|
-
const
|
|
41
|
+
const gaps = coverageGaps(coverageRows(found), 0, new Date(window.after).toISOString());
|
|
42
|
+
const next = [...gaps.next];
|
|
43
|
+
const missing = [
|
|
44
|
+
...gaps.missing,
|
|
45
|
+
...found.leftOut.map((one) => {
|
|
46
|
+
const read = hints?.read?.(one, day(window.after)) ?? null;
|
|
47
|
+
return { field: "linked_chats", why: leftOutLine(one, read), ...(read ? { step: "messages.list" } : {}) };
|
|
48
|
+
}),
|
|
49
|
+
];
|
|
50
|
+
if (found.purposeHint) {
|
|
51
|
+
const why = purposeHintLine(found.purposeHint);
|
|
52
|
+
missing.push({ field: "purpose", why });
|
|
53
|
+
if (next.length < MAX_NEXT_STEPS)
|
|
54
|
+
next.push({ kind: "user_action", step: "relationships.purpose", why, safe_to_auto_execute: false });
|
|
55
|
+
}
|
|
40
56
|
return {
|
|
41
57
|
...(next.length ? { next } : {}),
|
|
42
58
|
...(missing.length ? { missing } : {}),
|
|
@@ -51,19 +67,27 @@ export const projectAnswerParts = (sinceTime, found) => {
|
|
|
51
67
|
* The gathering with each linked chat's `syncCommand`: the line that syncs a chat held in part back to the window
|
|
52
68
|
* start, or null when it needs none or no command here can. JSON readers get what the text says per chat.
|
|
53
69
|
*/
|
|
54
|
-
export const withSyncCommands = (found, sync) => ({
|
|
70
|
+
export const withSyncCommands = (found, sync, read) => ({
|
|
55
71
|
...found,
|
|
56
72
|
linkedChats: found.linkedChats.map((one) => ({
|
|
57
73
|
...one,
|
|
58
74
|
syncCommand: one.completeness === null || one.completeness.state === "complete" ? null : sync(one, day(found.window.after)),
|
|
59
75
|
})),
|
|
76
|
+
leftOut: found.leftOut.map((one) => ({ ...one, readCommand: read?.(one, day(found.window.after)) ?? null })),
|
|
60
77
|
});
|
|
78
|
+
/** One line for a chat left out by its purpose: what it holds in the window and how to read it. */
|
|
79
|
+
const leftOutLine = (one, read) => `${one.title ? `«${one.title}» (${one.ref})` : one.ref}, purpose ${one.purpose}` +
|
|
80
|
+
`${one.proposed ? `, proposed ${one.proposed}` : ""}, not read: ${heldInWindow(one)}` +
|
|
81
|
+
(read ? `; read it: ${read}` : `; add --purpose ${one.purpose} to read it here`);
|
|
82
|
+
const purposeHintLine = (hint) => `${hint.chat} gave ${hint.items} of ${hint.of} items by membership alone; if it is not a work chat, ask the owner ` +
|
|
83
|
+
`to set its purpose: memo relationships update ${hint.relationship} --purpose <purpose>`;
|
|
61
84
|
const LABELS = {
|
|
62
85
|
"person-only": " [person only]",
|
|
63
86
|
sender: " [sent by contact]",
|
|
64
87
|
"linked-chat": " [linked chat]",
|
|
65
88
|
};
|
|
66
89
|
const day = (at) => new Date(at).toISOString().slice(0, 10);
|
|
90
|
+
const PROPOSED = " [proposed, not confirmed]";
|
|
67
91
|
const FETCH_STATES = {
|
|
68
92
|
complete: "synced",
|
|
69
93
|
partial: "synced in part",
|
|
@@ -84,7 +108,7 @@ export const projectEvidenceText = (found, hints) => {
|
|
|
84
108
|
`${day(found.window.after)} to ${day(found.window.before)}, ${found.scope.sources.length} sources`,
|
|
85
109
|
`as of: ${new Date(found.window.asOf).toISOString()} (exclusive end; reuse --before-time to repeat this window)`,
|
|
86
110
|
`searched: ${[...lanes.values()].join("; ")}`,
|
|
87
|
-
...found.items.map((item) => `${day(item.at)} ${item.ref}${LABELS[item.context] ?? ""}\n ${item.text}` +
|
|
111
|
+
...found.items.map((item) => `${day(item.at)} ${item.ref}${LABELS[item.context] ?? ""}${item.proposed ? PROPOSED : ""}\n ${item.text}` +
|
|
88
112
|
(item.around ?? [])
|
|
89
113
|
.map((one) => `\n ${one.found ? "(hit) " : ""}${one.sender ? `${one.sender}: ` : ""}${one.text}`)
|
|
90
114
|
.join("")),
|
|
@@ -138,6 +162,16 @@ export const projectEvidenceText = (found, hints) => {
|
|
|
138
162
|
lines.push(`${one.title ?? one.ref}: ${held}${one.state === "pending" ? ", not read this run" : ""}` +
|
|
139
163
|
(sync ? ` — sync it: ${sync}` : ""));
|
|
140
164
|
}
|
|
165
|
+
const proposed = [
|
|
166
|
+
...found.linkedChats.flatMap((one) => (one.proposed ? [`${one.title ?? one.ref} (${one.proposed})`] : [])),
|
|
167
|
+
...found.members.flatMap((one) => (one.proposed ? [`${one.person} (${one.proposed})`] : [])),
|
|
168
|
+
];
|
|
169
|
+
if (proposed.length)
|
|
170
|
+
lines.push(`read as proposed, not confirmed: ${proposed.join(", ")}`);
|
|
171
|
+
for (const one of found.leftOut)
|
|
172
|
+
lines.push(`left out: ${leftOutLine(one, hints.read?.(one, day(found.window.after)) ?? null)}`);
|
|
173
|
+
if (found.purposeHint)
|
|
174
|
+
lines.push(purposeHintLine(found.purposeHint));
|
|
141
175
|
const capped = found.linkedChats.filter((one) => one.capped);
|
|
142
176
|
if (capped.length)
|
|
143
177
|
lines.push(`linked chats with more to read, left for --cursor so one chat does not fill the run: ${capped
|
|
@@ -97,6 +97,16 @@ export const STATUS_ROWS = {
|
|
|
97
97
|
],
|
|
98
98
|
},
|
|
99
99
|
"status.signal": { columns: [{ field: "id" }, { field: "version" }] },
|
|
100
|
-
"status.out-of-scope": {
|
|
100
|
+
"status.out-of-scope": {
|
|
101
|
+
columns: [
|
|
102
|
+
{ field: "chat" },
|
|
103
|
+
{ field: "reason" },
|
|
104
|
+
{ field: "purpose" },
|
|
105
|
+
{ field: "messages", as: "count", tag: "messages" },
|
|
106
|
+
{ field: "first_at", as: "day" },
|
|
107
|
+
{ field: "last_at", as: "day" },
|
|
108
|
+
{ field: "read_command" },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
101
111
|
"status.failed": { columns: [{ field: "signal" }], text: ["why"] },
|
|
102
112
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { NextStep } from "@wirecat/cli-base/standards";
|
|
2
2
|
import * as v from "valibot";
|
|
3
|
+
import type { Id } from "../domain/models.js";
|
|
3
4
|
import type { ProjectStatus, StatusChatSummary } from "../services/project-status/index.js";
|
|
5
|
+
import type { AccountKey } from "../store/store.js";
|
|
4
6
|
import type { RowShape } from "./compact.js";
|
|
5
7
|
export interface ProjectStatusHints {
|
|
6
8
|
/** The step that syncs a chat held in part back to `since` (`YYYY-MM-DD`); null when no command here can. */
|
|
@@ -9,6 +11,12 @@ export interface ProjectStatusHints {
|
|
|
9
11
|
read: (chat: StatusChatSummary, since: string) => NextStep | null;
|
|
10
12
|
/** The command line a step runs, for the text form. */
|
|
11
13
|
line: (step: NextStep) => string;
|
|
14
|
+
/** The step that reads a chat left out by its purpose from `since`; null when no command here can. */
|
|
15
|
+
readChat?: (chat: {
|
|
16
|
+
ref: string;
|
|
17
|
+
account: AccountKey;
|
|
18
|
+
chatId: Id;
|
|
19
|
+
}, since: string) => NextStep | null;
|
|
12
20
|
}
|
|
13
21
|
/** The `project_status` answer drawn by `--compact`: each signal's list with that signal's row. */
|
|
14
22
|
export declare const projectStatusRow: (signals: readonly {
|
|
@@ -44,10 +52,15 @@ export declare const projectStatusSchema: v.LooseObjectSchema<{
|
|
|
44
52
|
readonly attention: v.NumberSchema<undefined>;
|
|
45
53
|
readonly why: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
46
54
|
}, undefined>, undefined>;
|
|
55
|
+
readonly purposes: v.UnionSchema<[v.LiteralSchema<"all", undefined>, v.ArraySchema<v.StringSchema<undefined>, undefined>], undefined>;
|
|
47
56
|
readonly out_of_scope: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
48
57
|
readonly chat: v.StringSchema<undefined>;
|
|
49
58
|
readonly reason: v.StringSchema<undefined>;
|
|
50
59
|
readonly purpose: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
60
|
+
readonly messages: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
61
|
+
readonly first_at: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
62
|
+
readonly last_at: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
63
|
+
readonly read_command: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
51
64
|
}, undefined>, undefined>, undefined>;
|
|
52
65
|
readonly failed: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
53
66
|
readonly signal: v.StringSchema<undefined>;
|
|
@@ -55,6 +68,12 @@ export declare const projectStatusSchema: v.LooseObjectSchema<{
|
|
|
55
68
|
}, undefined>, undefined>, undefined>;
|
|
56
69
|
}, undefined>;
|
|
57
70
|
export type ProjectStatusItem = v.InferOutput<typeof projectStatusSchema>;
|
|
71
|
+
/** `12 messages 2026-10-03…2026-11-01`, or that the window holds none. */
|
|
72
|
+
export declare const heldInWindow: (one: {
|
|
73
|
+
messages: number;
|
|
74
|
+
firstAt: string | null;
|
|
75
|
+
lastAt: string | null;
|
|
76
|
+
}) => string;
|
|
58
77
|
/** The answer of `projects status`: the item, two steps at most, what is missing, and the window. */
|
|
59
78
|
export declare const projectStatusAnswer: (found: ProjectStatus, hints: ProjectStatusHints) => {
|
|
60
79
|
item: {
|
|
@@ -86,10 +105,15 @@ export declare const projectStatusAnswer: (found: ProjectStatus, hints: ProjectS
|
|
|
86
105
|
attention: number;
|
|
87
106
|
why: string | null;
|
|
88
107
|
}[];
|
|
108
|
+
purposes: "all" | string[];
|
|
89
109
|
out_of_scope?: {
|
|
90
110
|
chat: string;
|
|
91
111
|
reason: string;
|
|
92
112
|
purpose?: string | undefined;
|
|
113
|
+
messages?: number | undefined;
|
|
114
|
+
first_at?: string | null | undefined;
|
|
115
|
+
last_at?: string | null | undefined;
|
|
116
|
+
read_command?: string | null | undefined;
|
|
93
117
|
}[] | undefined;
|
|
94
118
|
failed?: {
|
|
95
119
|
signal: string;
|
|
@@ -49,7 +49,16 @@ export const projectStatusSchema = v.looseObject({
|
|
|
49
49
|
attention: v.number(),
|
|
50
50
|
why: v.nullable(v.string()),
|
|
51
51
|
})),
|
|
52
|
-
|
|
52
|
+
purposes: v.union([v.literal("all"), v.array(v.string())]),
|
|
53
|
+
out_of_scope: v.optional(v.array(v.strictObject({
|
|
54
|
+
chat: v.string(),
|
|
55
|
+
reason: v.string(),
|
|
56
|
+
purpose: v.optional(v.string()),
|
|
57
|
+
messages: v.optional(v.number()),
|
|
58
|
+
first_at: v.optional(v.nullable(v.string())),
|
|
59
|
+
last_at: v.optional(v.nullable(v.string())),
|
|
60
|
+
read_command: v.optional(v.nullable(v.string())),
|
|
61
|
+
}))),
|
|
53
62
|
failed: v.optional(v.array(v.strictObject({ signal: v.string(), why: v.string() }))),
|
|
54
63
|
...Object.fromEntries(Object.keys(STATUS_SIGNAL_ROWS).map((name) => [listKey(name.slice(7)), v.optional(v.array(statusItem))])),
|
|
55
64
|
});
|
|
@@ -78,9 +87,21 @@ const nextOf = (found, hints) => {
|
|
|
78
87
|
const reads = top.flatMap(({ chat, why }) => partial(found, chat) ? [] : because(hints.read(chat, readFrom(found, chat.ref)), why));
|
|
79
88
|
return [...syncs, ...reads].slice(0, 2);
|
|
80
89
|
};
|
|
81
|
-
const
|
|
90
|
+
const leftOutOf = (found) => found.outOfScope.filter((one) => one.reason === "purpose");
|
|
91
|
+
const readLine = (found, one, hints) => {
|
|
92
|
+
const step = hints.readChat?.({ ref: one.chat, account: one.account, chatId: one.chatId }, day(found.window.since));
|
|
93
|
+
return step ? hints.line(step) : null;
|
|
94
|
+
};
|
|
95
|
+
/** `12 messages 2026-10-03…2026-11-01`, or that the window holds none. */
|
|
96
|
+
export const heldInWindow = (one) => one.messages && one.firstAt && one.lastAt
|
|
97
|
+
? `${one.messages} message${one.messages === 1 ? "" : "s"} ${day(one.firstAt)}…${day(one.lastAt)}`
|
|
98
|
+
: "no messages in the window";
|
|
99
|
+
const leftOutWhy = (one, read) => `${one.chat}, purpose ${one.purpose}, not read: ${heldInWindow(one)}` +
|
|
100
|
+
(read ? `; read it: ${read}` : "; add its purpose to --purpose to read it");
|
|
101
|
+
const missingOf = (found, hints) => {
|
|
82
102
|
const held = found.chats.filter((chat) => partial(found, chat));
|
|
83
103
|
const [first] = held;
|
|
104
|
+
const others = found.outOfScope.filter((one) => one.reason !== "purpose");
|
|
84
105
|
return [
|
|
85
106
|
...(first
|
|
86
107
|
? [
|
|
@@ -92,16 +113,20 @@ const missingOf = (found) => {
|
|
|
92
113
|
},
|
|
93
114
|
]
|
|
94
115
|
: []),
|
|
95
|
-
...(
|
|
116
|
+
...(others.length
|
|
96
117
|
? [
|
|
97
118
|
{
|
|
98
119
|
field: "chats",
|
|
99
|
-
why: `${
|
|
100
|
-
...new Set(
|
|
120
|
+
why: `${others.length} linked chats were not read here (${[
|
|
121
|
+
...new Set(others.map((one) => one.reason)),
|
|
101
122
|
].join(", ")}); memo projects status reads every account`,
|
|
102
123
|
},
|
|
103
124
|
]
|
|
104
125
|
: []),
|
|
126
|
+
...leftOutOf(found).map((one) => {
|
|
127
|
+
const read = readLine(found, one, hints);
|
|
128
|
+
return { field: "chats", why: leftOutWhy(one, read), ...(read ? { step: "messages.list" } : {}) };
|
|
129
|
+
}),
|
|
105
130
|
...found.failed.map((one) => ({ field: `signal:${one.signal}`, why: one.why })),
|
|
106
131
|
];
|
|
107
132
|
};
|
|
@@ -145,11 +170,26 @@ export const projectStatusAnswer = (found, hints) => {
|
|
|
145
170
|
attention: weights.get(chat.ref)?.weight ?? 0,
|
|
146
171
|
why: weights.get(chat.ref)?.why ?? null,
|
|
147
172
|
})),
|
|
148
|
-
|
|
173
|
+
purposes: found.purposes,
|
|
174
|
+
...(found.outOfScope.length
|
|
175
|
+
? {
|
|
176
|
+
outOfScope: found.outOfScope.map((one) => one.reason === "purpose"
|
|
177
|
+
? {
|
|
178
|
+
chat: one.chat,
|
|
179
|
+
reason: one.reason,
|
|
180
|
+
purpose: one.purpose,
|
|
181
|
+
messages: one.messages,
|
|
182
|
+
firstAt: one.firstAt,
|
|
183
|
+
lastAt: one.lastAt,
|
|
184
|
+
readCommand: readLine(found, one, hints),
|
|
185
|
+
}
|
|
186
|
+
: one),
|
|
187
|
+
}
|
|
188
|
+
: {}),
|
|
149
189
|
...(found.failed.length ? { failed: found.failed } : {}),
|
|
150
190
|
...lists,
|
|
151
191
|
});
|
|
152
|
-
const missing = missingOf(found);
|
|
192
|
+
const missing = missingOf(found, hints);
|
|
153
193
|
const complete = found.failed.length === 0 && !found.chats.some((chat) => partial(found, chat));
|
|
154
194
|
return {
|
|
155
195
|
item,
|
|
@@ -235,8 +275,11 @@ export const projectStatusText = (found, hints) => {
|
|
|
235
275
|
const cut = one.data.cut;
|
|
236
276
|
lines.push(` ${cut ? `${data.child} not read (${cut})` : `${data.key ?? data.child} · ${data.name} · ${data.chats} chats`}`);
|
|
237
277
|
}
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
const others = found.outOfScope.filter((one) => one.reason !== "purpose");
|
|
279
|
+
if (others.length)
|
|
280
|
+
lines.push(`Not read here: ${others.map((one) => `${one.chat} (${one.reason})`).join(", ")}`);
|
|
281
|
+
for (const one of leftOutOf(found))
|
|
282
|
+
lines.push(`Left out: ${leftOutWhy(one, readLine(found, one, hints))}`);
|
|
240
283
|
for (const one of found.failed)
|
|
241
284
|
lines.push(`Signal ${one.signal} failed: ${one.why}`);
|
|
242
285
|
if (next.length)
|
|
@@ -268,7 +268,11 @@ export const scheduleCommandFor = ({ app, context: contextOf }) => {
|
|
|
268
268
|
.action(async function (file) {
|
|
269
269
|
// A timer starts it with no profile named; the schedule's own environment carries the profile.
|
|
270
270
|
const { context, system, timer } = prepare(this, { needsProfile: false });
|
|
271
|
-
const last = await untilChildEnds(this, (signal) => runSchedule(app, system, context.env, file, {
|
|
271
|
+
const last = await untilChildEnds(this, (signal) => runSchedule(app, system, context.env, file, {
|
|
272
|
+
timer: timer(),
|
|
273
|
+
untilUnlocked: true,
|
|
274
|
+
...(signal ? { signal } : {}),
|
|
275
|
+
}));
|
|
272
276
|
if (last.exitCode !== 0)
|
|
273
277
|
setExitCode(this, 1);
|
|
274
278
|
});
|
package/dist/entities/hit.d.ts
CHANGED
|
@@ -24,6 +24,11 @@ export declare const hit: v.StrictObjectSchema<{
|
|
|
24
24
|
readonly scope: v.OptionalSchema<v.PicklistSchema<["transcript", "chat", "summary"], undefined>, undefined>;
|
|
25
25
|
/** A meeting transcript hit's cue, which `meetings passage` opens with the lines around it. */
|
|
26
26
|
readonly cue_ref: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
|
|
27
|
+
/** A meeting transcript hit's transcript language (`ru`, `en`…), `null` when unknown. */
|
|
28
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
29
|
+
/** A meeting transcript hit's line, from 1, of `lines_total` lines in its transcript. */
|
|
30
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
31
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
27
32
|
/** A meeting passage's stored line, so two passages of one meeting stay apart; `search meetings` gives the same. */
|
|
28
33
|
readonly id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
29
34
|
/** A message's files; absent when it has none. */
|
package/dist/entities/hit.js
CHANGED
|
@@ -26,6 +26,11 @@ export const hit = v.strictObject({
|
|
|
26
26
|
scope: v.optional(v.picklist(["transcript", "chat", "summary"])),
|
|
27
27
|
/** A meeting transcript hit's cue, which `meetings passage` opens with the lines around it. */
|
|
28
28
|
cue_ref: v.optional(reference),
|
|
29
|
+
/** A meeting transcript hit's transcript language (`ru`, `en`…), `null` when unknown. */
|
|
30
|
+
language: v.optional(v.nullable(text)),
|
|
31
|
+
/** A meeting transcript hit's line, from 1, of `lines_total` lines in its transcript. */
|
|
32
|
+
line: v.optional(v.pipe(count, v.minValue(1))),
|
|
33
|
+
lines_total: v.optional(count),
|
|
29
34
|
/** A meeting passage's stored line, so two passages of one meeting stay apart; `search meetings` gives the same. */
|
|
30
35
|
id: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
31
36
|
/** A message's files; absent when it has none. */
|
package/dist/entities/index.d.ts
CHANGED
|
@@ -214,6 +214,15 @@ export declare const ENTITIES: {
|
|
|
214
214
|
readonly next_transcript_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
215
215
|
}, undefined>;
|
|
216
216
|
}, undefined>], undefined>;
|
|
217
|
+
readonly meeting_repair: v.StrictObjectSchema<{
|
|
218
|
+
readonly account_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
219
|
+
readonly applied: v.BooleanSchema<undefined>;
|
|
220
|
+
readonly transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
221
|
+
readonly languages: v.ArraySchema<v.StrictObjectSchema<{
|
|
222
|
+
readonly language: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
223
|
+
readonly transcripts: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
224
|
+
}, undefined>, undefined>;
|
|
225
|
+
}, undefined>;
|
|
217
226
|
readonly sync_progress: v.StrictObjectSchema<{
|
|
218
227
|
readonly job: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
219
228
|
readonly profile: v.StringSchema<undefined>;
|
|
@@ -4575,6 +4584,9 @@ export declare const ENTITIES: {
|
|
|
4575
4584
|
readonly offset_ms: v.OptionalSchema<v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>, undefined>;
|
|
4576
4585
|
readonly scope: v.OptionalSchema<v.PicklistSchema<["transcript", "chat", "summary"], undefined>, undefined>;
|
|
4577
4586
|
readonly cue_ref: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
|
|
4587
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
4588
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
4589
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
4578
4590
|
readonly id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
4579
4591
|
readonly attachments: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
4580
4592
|
readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -5625,6 +5637,9 @@ export declare const ENTITIES: {
|
|
|
5625
5637
|
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
5626
5638
|
readonly excerpt: v.StringSchema<undefined>;
|
|
5627
5639
|
readonly offset_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
5640
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
5641
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
5642
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
5628
5643
|
readonly match: v.OptionalSchema<v.PicklistSchema<["words", "corrected"], undefined>, undefined>;
|
|
5629
5644
|
readonly corrections: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
5630
5645
|
readonly from: v.StringSchema<undefined>;
|
|
@@ -5790,6 +5805,9 @@ export declare const ENTITIES: {
|
|
|
5790
5805
|
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
5791
5806
|
readonly excerpt: v.StringSchema<undefined>;
|
|
5792
5807
|
readonly offset_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
5808
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
5809
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
5810
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
5793
5811
|
readonly match: v.OptionalSchema<v.PicklistSchema<["words", "corrected"], undefined>, undefined>;
|
|
5794
5812
|
readonly corrections: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
5795
5813
|
readonly from: v.StringSchema<undefined>;
|
|
@@ -6161,6 +6179,9 @@ export declare const ENTITIES: {
|
|
|
6161
6179
|
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
6162
6180
|
readonly excerpt: v.StringSchema<undefined>;
|
|
6163
6181
|
readonly offset_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
6182
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
6183
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
6184
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
6164
6185
|
readonly match: v.OptionalSchema<v.PicklistSchema<["words", "corrected"], undefined>, undefined>;
|
|
6165
6186
|
readonly corrections: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
6166
6187
|
readonly from: v.StringSchema<undefined>;
|
|
@@ -8812,6 +8833,12 @@ export declare const ENTITIES: {
|
|
|
8812
8833
|
readonly stems: v.OptionalSchema<v.StrictObjectSchema<{
|
|
8813
8834
|
readonly stemmed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
8814
8835
|
}, undefined>, undefined>;
|
|
8836
|
+
readonly caught_up: v.OptionalSchema<v.StrictObjectSchema<{
|
|
8837
|
+
readonly chats: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
8838
|
+
readonly fetched: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
8839
|
+
readonly failed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
8840
|
+
readonly stopped: v.BooleanSchema<undefined>;
|
|
8841
|
+
}, undefined>, undefined>;
|
|
8815
8842
|
readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
|
|
8816
8843
|
readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
8817
8844
|
readonly closed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -2266,6 +2266,12 @@ export declare const serveSummary: v.StrictObjectSchema<{
|
|
|
2266
2266
|
readonly stems: v.OptionalSchema<v.StrictObjectSchema<{
|
|
2267
2267
|
readonly stemmed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
2268
2268
|
}, undefined>, undefined>;
|
|
2269
|
+
readonly caught_up: v.OptionalSchema<v.StrictObjectSchema<{
|
|
2270
|
+
readonly chats: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
2271
|
+
readonly fetched: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
2272
|
+
readonly failed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
2273
|
+
readonly stopped: v.BooleanSchema<undefined>;
|
|
2274
|
+
}, undefined>, undefined>;
|
|
2269
2275
|
readonly tasks: v.OptionalSchema<v.StrictObjectSchema<{
|
|
2270
2276
|
readonly added: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
2271
2277
|
readonly closed: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -42,5 +42,6 @@ export const serveSummary = v.strictObject({
|
|
|
42
42
|
replies: v.optional(v.strictObject({ sent: totals, skipped: totals, tasks: v.optional(totals) })),
|
|
43
43
|
members: v.optional(v.strictObject({ fetched: count, failed: count })),
|
|
44
44
|
stems: v.optional(v.strictObject({ stemmed: count })),
|
|
45
|
+
caught_up: v.optional(v.strictObject({ chats: count, fetched: count, failed: count, stopped: v.boolean() })),
|
|
45
46
|
tasks: v.optional(v.strictObject({ added: count, closed: count, review: count })),
|
|
46
47
|
});
|
|
@@ -309,6 +309,9 @@ export declare const meetingPreparation: v.StrictObjectSchema<{
|
|
|
309
309
|
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
310
310
|
readonly excerpt: v.StringSchema<undefined>;
|
|
311
311
|
readonly offset_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
312
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
313
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
314
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
312
315
|
readonly match: v.OptionalSchema<v.PicklistSchema<["words", "corrected"], undefined>, undefined>;
|
|
313
316
|
readonly corrections: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
314
317
|
readonly from: v.StringSchema<undefined>;
|
package/dist/errors/reasons.js
CHANGED
|
@@ -33,6 +33,7 @@ const SIGN_IN = [
|
|
|
33
33
|
["sign-in.client-file-invalid", "configuration_error", "the file is not a Desktop app OAuth client JSON"],
|
|
34
34
|
];
|
|
35
35
|
const OTHERS = [
|
|
36
|
+
["query.time-limit", "timeout", "the search took longer than its time limit; the query itself is valid"],
|
|
36
37
|
["model.offline", "permission_error", "downloading a model needs the network; --offline was requested"],
|
|
37
38
|
[
|
|
38
39
|
"contract.unacknowledged-override",
|
|
@@ -376,6 +376,9 @@ export declare const callMeetingPreparationTool: (read: MeetingPreparationReader
|
|
|
376
376
|
id: string;
|
|
377
377
|
excerpt: string;
|
|
378
378
|
offset_ms: number | null;
|
|
379
|
+
language?: string | null | undefined;
|
|
380
|
+
line?: number | undefined;
|
|
381
|
+
lines_total?: number | undefined;
|
|
379
382
|
match?: "corrected" | "words" | undefined;
|
|
380
383
|
corrections?: {
|
|
381
384
|
from: string;
|
|
@@ -15,6 +15,11 @@ export declare const meetingHit: v.StrictObjectSchema<{
|
|
|
15
15
|
readonly excerpt: v.StringSchema<undefined>;
|
|
16
16
|
/** How far into the meeting the passage is. */
|
|
17
17
|
readonly offset_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
18
|
+
/** A transcript passage's transcript language (`ru`, `en`…), `null` when unknown. */
|
|
19
|
+
readonly language: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
20
|
+
/** A transcript passage's line, from 1, of `lines_total`. */
|
|
21
|
+
readonly line: v.OptionalSchema<v.SchemaWithPipe<readonly [v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, v.MinValueAction<number, 1, undefined>]>, undefined>;
|
|
22
|
+
readonly lines_total: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
18
23
|
/** `corrected`: a query word matched only a garbled form of itself, listed in `corrections`. */
|
|
19
24
|
readonly match: v.OptionalSchema<v.PicklistSchema<["words", "corrected"], undefined>, undefined>;
|
|
20
25
|
readonly corrections: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
package/dist/meetings/answers.js
CHANGED
|
@@ -16,6 +16,11 @@ export const meetingHit = v.strictObject({
|
|
|
16
16
|
excerpt: text,
|
|
17
17
|
/** How far into the meeting the passage is. */
|
|
18
18
|
offset_ms: v.nullable(count),
|
|
19
|
+
/** A transcript passage's transcript language (`ru`, `en`…), `null` when unknown. */
|
|
20
|
+
language: v.optional(v.nullable(text)),
|
|
21
|
+
/** A transcript passage's line, from 1, of `lines_total`. */
|
|
22
|
+
line: v.optional(v.pipe(count, v.minValue(1))),
|
|
23
|
+
lines_total: v.optional(count),
|
|
19
24
|
/** `corrected`: a query word matched only a garbled form of itself, listed in `corrections`. */
|
|
20
25
|
match: v.optional(v.picklist(["words", "corrected"])),
|
|
21
26
|
corrections: v.optional(v.array(v.strictObject({ from: text, to: v.array(text) }))),
|
|
@@ -190,6 +195,9 @@ export const meetingHitItem = (hit) => ({
|
|
|
190
195
|
id: String(hit.id),
|
|
191
196
|
excerpt: hit.text,
|
|
192
197
|
offset_ms: hit.startMs,
|
|
198
|
+
...(hit.language === undefined ? {} : { language: hit.language }),
|
|
199
|
+
...(hit.line === undefined ? {} : { line: hit.line }),
|
|
200
|
+
...(hit.linesTotal === undefined ? {} : { lines_total: hit.linesTotal }),
|
|
193
201
|
untrusted: hit.source === undefined ? ["excerpt"] : ["excerpt", "source.title"],
|
|
194
202
|
...(hit.match === undefined ? {} : { match: hit.match }),
|
|
195
203
|
...(hit.corrections === undefined ? {} : { corrections: hit.corrections }),
|
|
@@ -146,6 +146,20 @@ const addMeetingCommandsDefinition = (program, deps) => {
|
|
|
146
146
|
throw new MeetingError("validation_error", "file account differs from selected account");
|
|
147
147
|
print(await importFiles(files, deps.store, deps.now?.()), command);
|
|
148
148
|
});
|
|
149
|
+
output(annotate(group.command("repair"), {
|
|
150
|
+
effects: LOCAL_WRITE,
|
|
151
|
+
output: { entity: "meeting_repair" },
|
|
152
|
+
}).description("preview filling in what older builds left out of stored transcripts (their language); --yes stores it"))
|
|
153
|
+
.option("--yes", "store the detected languages; otherwise only preview")
|
|
154
|
+
.action(async (opts, command) => {
|
|
155
|
+
if (!deps.store.fillTranscriptLanguages)
|
|
156
|
+
throw new MeetingError("configuration_error", "this store cannot fill transcript languages");
|
|
157
|
+
print(await deps.store.fillTranscriptLanguages({
|
|
158
|
+
accountId: deps.accountId,
|
|
159
|
+
apply: opts.yes === true,
|
|
160
|
+
now: deps.now?.() ?? Date.now(),
|
|
161
|
+
}), command);
|
|
162
|
+
});
|
|
149
163
|
const eventList = queryWordsAfterOptions(annotate(program.command("events").description("the owner's stored events"), {
|
|
150
164
|
effects: { remote: "none", local: "none" },
|
|
151
165
|
}).command("list"))
|
package/dist/meetings/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { liftMeetingFields, MEETING_HIT_QUERY, meetingWordsQuery, parseMeetingQu
|
|
|
14
14
|
export { type ImportFile, importFiles } from "./import.js";
|
|
15
15
|
export { MEETING_ISSUES, MEETING_QUERY, type MeetingIssue, type MeetingItem, type MeetingOwner, meeting, meetingIssuesOf, meetingItem, } from "./item.js";
|
|
16
16
|
export { joinKeyOf, joinUrlWithoutQuery } from "./join-key.js";
|
|
17
|
+
export { transcriptLanguage } from "./language.js";
|
|
17
18
|
export { CHAT_MESSAGES_READ, type MeetingChatLine, type MeetingChatLinesInput, type MeetingChatLinesStore, type MeetingLink, } from "./links.js";
|
|
18
19
|
export { createMeetingContext, type MeetingContextAction, type MeetingContextChange, type MeetingContextOperation, type MeetingContextPorts, type MeetingContextReceipt, validateMeetingContextChange, } from "./meeting-context.js";
|
|
19
20
|
export { createMeetingManagement, dispatchMeetingMutation, type ManagedMeeting, type ManagedMeetingChange, type MeetingManagement, type MeetingManagementPorts, type MeetingMutationOptions, type MeetingMutationProposal, type MeetingMutationReceipt, type MeetingOperationOptions, type MeetingProviderContext, validateMeetingChange, } from "./meeting-management.js";
|
|
@@ -51,7 +52,7 @@ export type { MeetingSeriesCandidate, MeetingSeriesInventoryPage, MeetingSeriesI
|
|
|
51
52
|
export { validateMeetingSeriesInventoryCursor } from "./series-inventory-validation.js";
|
|
52
53
|
export type { MeetingSource, SourceParticipant } from "./source.js";
|
|
53
54
|
export { aggregateMeetingStats, type MeetingSpeakerStats, type MeetingStats, type MeetingStatsInput, type MeetingStatsRow, type MeetingStatsSelection, type MeetingStatsStore, type MeetingWeekStats, } from "./stats.js";
|
|
54
|
-
export type { ChatInput, ContractCase, EventCandidateFilter, IdentityInput, MeetingDetails, MeetingFilter, MeetingHitSource, MeetingInput, MeetingSave, MeetingStore, NewRecord, ParticipantInput, SearchHit, TranscriptInput, TranscriptRowInput, } from "./store.js";
|
|
55
|
+
export type { ChatInput, ContractCase, EventCandidateFilter, IdentityInput, MeetingDetails, MeetingFilter, MeetingHitSource, MeetingInput, MeetingSave, MeetingStore, NewRecord, ParticipantInput, SearchHit, TranscriptInput, TranscriptLanguageFill, TranscriptRowInput, } from "./store.js";
|
|
55
56
|
export type { MeetingTranscriptReceiptStore, MeetingTranscriptStore, TranscriptAppend, TranscriptAppendReceipt, UnlinkedTranscriptInput, } from "./transcript-store.js";
|
|
56
57
|
export { parseTranscriptText, TranscriptTextError, type TranscriptTextHeader } from "./transcript-text.js";
|
|
57
58
|
export { type MeetingMediaDescriptor, type MeetingRecognitionSegment, type MeetingTranscriptionContext, type MeetingTranscriptionOptions, type MeetingTranscriptionReceipt, type MeetingTranscriptionSource, transcribeMeeting, } from "./transcription.js";
|
package/dist/meetings/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { liftMeetingFields, MEETING_HIT_QUERY, meetingWordsQuery, parseMeetingQu
|
|
|
11
11
|
export { importFiles } from "./import.js";
|
|
12
12
|
export { MEETING_ISSUES, MEETING_QUERY, meeting, meetingIssuesOf, meetingItem, } from "./item.js";
|
|
13
13
|
export { joinKeyOf, joinUrlWithoutQuery } from "./join-key.js";
|
|
14
|
+
export { transcriptLanguage } from "./language.js";
|
|
14
15
|
export { CHAT_MESSAGES_READ, } from "./links.js";
|
|
15
16
|
export { createMeetingContext, validateMeetingContextChange, } from "./meeting-context.js";
|
|
16
17
|
export { createMeetingManagement, dispatchMeetingMutation, validateMeetingChange, } from "./meeting-management.js";
|
package/dist/meetings/ingest.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { transcriptLanguage } from "./language.js";
|
|
1
2
|
export const contentHash = async (text) => {
|
|
2
3
|
const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
3
4
|
return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
@@ -11,7 +12,7 @@ export const speakerPosition = (name, participants) => {
|
|
|
11
12
|
export const transcriptInput = async (lines, participants, source, hashText = JSON.stringify(lines), format = null) => ({
|
|
12
13
|
source,
|
|
13
14
|
format,
|
|
14
|
-
language:
|
|
15
|
+
language: transcriptLanguage(lines.map((line) => line.text)),
|
|
15
16
|
contentHash: await contentHash(hashText),
|
|
16
17
|
externalCreatedAt: null,
|
|
17
18
|
metadata: null,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The language most of a transcript is in, from its letters: Cyrillic is Russian (Ukrainian when its own letters
|
|
3
|
+
* show), Latin is decided by frequent words. `null` when the text is too short, mixed, or in neither script.
|
|
4
|
+
*/
|
|
5
|
+
export declare const transcriptLanguage: (texts: Iterable<string>) => string | null;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Words frequent in one Latin-script language and rare in the others here. Letters alone cannot tell Spanish from
|
|
3
|
+
* English, and calling a Spanish meeting English is worse than calling it nothing.
|
|
4
|
+
*/
|
|
5
|
+
const LATIN_WORDS = {
|
|
6
|
+
en: ["the", "and", "is", "that", "you", "this", "what", "have", "with", "are"],
|
|
7
|
+
es: ["el", "los", "las", "por", "pero", "para", "está", "muy", "también", "hay"],
|
|
8
|
+
de: ["der", "und", "ist", "nicht", "das", "ich", "wir", "mit", "auch", "ein"],
|
|
9
|
+
fr: ["le", "les", "est", "une", "pas", "je", "vous", "nous", "avec", "mais"],
|
|
10
|
+
it: ["gli", "della", "sono", "perché", "anche", "questo", "molto", "che", "di", "è"],
|
|
11
|
+
pt: ["não", "você", "isso", "muito", "então", "com", "uma", "também", "mas", "eu"],
|
|
12
|
+
};
|
|
13
|
+
const LATIN_LANGUAGE = new Map(Object.entries(LATIN_WORDS).flatMap(([language, words]) => words.map((word) => [word, language])));
|
|
14
|
+
/** Letters Ukrainian writes and Russian does not. */
|
|
15
|
+
const UKRAINIAN = /[іїєґ]/giu;
|
|
16
|
+
const CYRILLIC = /\p{Script=Cyrillic}/gu;
|
|
17
|
+
const LATIN = /\p{Script=Latin}/gu;
|
|
18
|
+
const MIN_LETTERS = 20;
|
|
19
|
+
/** A meeting in one language keeps a few lines of another; a share below this is a mixed meeting. */
|
|
20
|
+
const DOMINANT = 0.75;
|
|
21
|
+
const countOf = (text, pattern) => text.match(pattern)?.length ?? 0;
|
|
22
|
+
const latinLanguage = (texts) => {
|
|
23
|
+
const votes = new Map();
|
|
24
|
+
for (const text of texts)
|
|
25
|
+
for (const word of text.toLowerCase().split(/[^\p{L}]+/u)) {
|
|
26
|
+
const language = LATIN_LANGUAGE.get(word);
|
|
27
|
+
if (language)
|
|
28
|
+
votes.set(language, (votes.get(language) ?? 0) + 1);
|
|
29
|
+
}
|
|
30
|
+
const [first, second] = [...votes].sort((a, b) => b[1] - a[1]);
|
|
31
|
+
if (!first || first[1] < 3 || (second && first[1] < 2 * second[1]))
|
|
32
|
+
return null;
|
|
33
|
+
return first[0];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The language most of a transcript is in, from its letters: Cyrillic is Russian (Ukrainian when its own letters
|
|
37
|
+
* show), Latin is decided by frequent words. `null` when the text is too short, mixed, or in neither script.
|
|
38
|
+
*/
|
|
39
|
+
export const transcriptLanguage = (texts) => {
|
|
40
|
+
const all = [...texts];
|
|
41
|
+
let cyrillic = 0;
|
|
42
|
+
let latin = 0;
|
|
43
|
+
let ukrainian = 0;
|
|
44
|
+
for (const text of all) {
|
|
45
|
+
cyrillic += countOf(text, CYRILLIC);
|
|
46
|
+
latin += countOf(text, LATIN);
|
|
47
|
+
ukrainian += countOf(text, UKRAINIAN);
|
|
48
|
+
}
|
|
49
|
+
const letters = cyrillic + latin;
|
|
50
|
+
if (letters < MIN_LETTERS)
|
|
51
|
+
return null;
|
|
52
|
+
if (cyrillic / letters >= DOMINANT)
|
|
53
|
+
return ukrainian / cyrillic >= 0.01 ? "uk" : "ru";
|
|
54
|
+
if (latin / letters >= DOMINANT)
|
|
55
|
+
return latinLanguage(all);
|
|
56
|
+
return null;
|
|
57
|
+
};
|