@wirecat/cli-core 0.404.0 → 0.406.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 +45 -1
- package/dist/botcheck/reasons.d.ts +1 -1
- package/dist/cli/analysis-choice.js +1 -1
- package/dist/cli/answer-entity.d.ts +5 -0
- package/dist/cli/answer-entity.js +25 -0
- package/dist/cli/command-contract.js +2 -5
- package/dist/cli/commands-command.js +4 -1
- package/dist/cli/effects.js +1 -1
- package/dist/cli/embedding-choice.d.ts +1 -1
- package/dist/cli/embedding-choice.js +2 -2
- package/dist/cli/first-run.js +3 -3
- package/dist/cli/help/command-help.js +2 -2
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli/json-fields.js +2 -2
- package/dist/cli/meeting-project-candidates-command.js +0 -1
- package/dist/cli/meeting-series-inventory-command.js +0 -1
- package/dist/cli/messenger/admin-commands.js +14 -14
- package/dist/cli/messenger/archive-commands.js +2 -2
- package/dist/cli/messenger/attachments-command.js +7 -8
- package/dist/cli/messenger/backfill-command.js +1 -1
- package/dist/cli/messenger/chat-answers.js +3 -3
- package/dist/cli/messenger/chat-lists.js +3 -3
- package/dist/cli/messenger/chats-command.js +8 -8
- package/dist/cli/messenger/chats-members-command.js +1 -1
- package/dist/cli/messenger/contacts-command.js +10 -7
- package/dist/cli/messenger/context.d.ts +11 -4
- package/dist/cli/messenger/context.js +6 -0
- package/dist/cli/messenger/conversation-examples.js +7 -6
- package/dist/cli/messenger/conversations-command.d.ts +0 -4
- package/dist/cli/messenger/conversations-command.js +130 -135
- package/dist/cli/messenger/counters-command.js +4 -2
- package/dist/cli/messenger/doctor-command.js +8 -4
- package/dist/cli/messenger/gaps-command.js +2 -2
- package/dist/cli/messenger/hearing-command.d.ts +1 -1
- package/dist/cli/messenger/hearing-command.js +1 -1
- package/dist/cli/messenger/images-command.js +1 -1
- package/dist/cli/messenger/inbox.js +1 -3
- package/dist/cli/messenger/lacks.d.ts +1 -0
- package/dist/cli/messenger/lacks.js +1 -1
- package/dist/cli/messenger/mcp-command.js +4 -4
- package/dist/cli/messenger/messages-comments-command.js +10 -5
- package/dist/cli/messenger/messages-send-command.js +44 -3
- package/dist/cli/messenger/metadata-command.js +5 -5
- package/dist/cli/messenger/models-command.js +101 -90
- package/dist/cli/messenger/private-people-command.js +5 -6
- package/dist/cli/messenger/replies-edit-command.js +0 -1
- package/dist/cli/messenger/review.js +1 -3
- package/dist/cli/messenger/serve-relay.d.ts +2 -0
- package/dist/cli/messenger/stats-command.js +1 -1
- package/dist/cli/messenger/store-maintenance-command.d.ts +2 -2
- package/dist/cli/messenger/store-maintenance-command.js +4 -4
- package/dist/cli/messenger/stored.js +5 -0
- package/dist/cli/messenger/tags-command.js +4 -10
- package/dist/cli/messenger/tasks-command.js +3 -3
- package/dist/cli/messenger/transcribe-command.js +1 -1
- package/dist/cli/moved.d.ts +2 -0
- package/dist/cli/moved.js +39 -1
- package/dist/cli/permission-keys.js +2 -0
- package/dist/cli/settings.js +1 -1
- package/dist/cli/setup-answers.d.ts +2 -2
- package/dist/cli/setup-answers.js +2 -2
- package/dist/cli/steps.js +4 -4
- package/dist/cli/store-settings.js +1 -1
- package/dist/cli/upgrade-command.js +0 -1
- package/dist/domain/models.d.ts +3 -3
- package/dist/domain/task-categories.js +1 -1
- package/dist/embeddings/remote.js +1 -1
- package/dist/entities/index.d.ts +380 -0
- package/dist/entities/index.js +2 -1
- package/dist/entities/member.d.ts +2 -2
- package/dist/entities/member.js +2 -2
- package/dist/entities/message-write.d.ts +386 -0
- package/dist/entities/message-write.js +6 -0
- package/dist/mcp/agent.d.ts +1 -0
- package/dist/mcp/agent.js +1 -1
- package/dist/mcp/tools/attachments.js +1 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/ports/app.d.ts +1 -1
- package/dist/ports/messenger.d.ts +8 -1
- package/dist/ports/traits.d.ts +2 -0
- package/dist/search/task-similarity.js +1 -1
- package/dist/sends/guard.js +2 -2
- package/dist/sends/permission-aliases.d.ts +5 -0
- package/dist/sends/permission-aliases.js +49 -0
- package/dist/sends/permissions.d.ts +1 -1
- package/dist/sends/permissions.js +35 -25
- package/dist/services/admin.js +7 -7
- package/dist/services/chats.js +1 -1
- package/dist/services/conversations.d.ts +1 -1
- package/dist/services/conversations.js +1 -1
- package/dist/services/counters.js +1 -1
- package/dist/services/embeddings-maintenance.js +2 -2
- package/dist/services/embeddings-search.js +1 -1
- package/dist/services/embeddings.d.ts +4 -4
- package/dist/services/messages-search.d.ts +1 -1
- package/dist/services/messages-search.js +2 -2
- package/dist/services/messages.d.ts +5 -0
- package/dist/services/messages.js +23 -0
- package/dist/services/metadata.js +2 -2
- package/dist/services/notes-search.js +3 -3
- package/dist/services/search-catchup.js +1 -1
- package/dist/speech/install.d.ts +4 -2
- package/dist/speech/install.js +16 -4
- package/dist/speech/local.js +1 -1
- package/dist/speech/transcribe.js +1 -1
- package/dist/store/search-index.d.ts +1 -1
- package/dist/store/sqlite/attachment-texts.d.ts +1 -1
- package/dist/store/sqlite/attachment-texts.js +1 -1
- package/dist/store/sqlite/batches.js +1 -1
- package/dist/store/sqlite/involvements.d.ts +1 -1
- package/dist/store/sqlite/note-index.d.ts +2 -2
- package/dist/store/sqlite/note-index.js +2 -2
- package/dist/store/sqlite/search-index.d.ts +1 -1
- package/dist/store/sqlite/search-index.js +1 -1
- package/dist/store/sqlite/stems-fill.d.ts +3 -3
- package/dist/store/sqlite/stems-fill.js +3 -3
- package/dist/store/sqlite/stems.d.ts +1 -1
- package/dist/store/sqlite/task-pairs.d.ts +1 -1
- package/dist/store/sqlite/task-pairs.js +1 -1
- package/package.json +1 -1
- package/parity.json +186 -179
- package/skills/follow-ups/SKILL.md +5 -5
- package/skills/link-conversations/SKILL.md +8 -8
|
@@ -245,7 +245,7 @@ export const embeddingsSearch = (deps) => {
|
|
|
245
245
|
const { vectors } = await store.vectors.conversationVectors(account, source.id, target.key);
|
|
246
246
|
if (vectors.length === 0) {
|
|
247
247
|
const [own] = await readinessOf(deps, chatId, target.key);
|
|
248
|
-
const step = own && isStale(own) ? "
|
|
248
|
+
const step = own && isStale(own) ? "links rebuild" : "embed";
|
|
249
249
|
throw new CliError("not_found", `conversation ${source.id} has no vector of ${target.id} that matches its messages now — ` +
|
|
250
250
|
`\`${command} conversations ${step} --chat ${chatId}\``);
|
|
251
251
|
}
|
|
@@ -32,7 +32,7 @@ export interface Embedded {
|
|
|
32
32
|
chat: Id;
|
|
33
33
|
model: string;
|
|
34
34
|
embedded: number;
|
|
35
|
-
/** Chunks whose messages changed since the build: `conversations
|
|
35
|
+
/** Chunks whose messages changed since the build: `conversations links rebuild` cuts them again. */
|
|
36
36
|
skipped: number;
|
|
37
37
|
}
|
|
38
38
|
export interface EmbeddingsService {
|
|
@@ -77,7 +77,7 @@ export interface EmbeddingsService {
|
|
|
77
77
|
}): Promise<RelatedConversations>;
|
|
78
78
|
/**
|
|
79
79
|
* How fresh one chat's conversations and vectors are, or every built chat's — and then how many group chats
|
|
80
|
-
* were never built, which `conversations
|
|
80
|
+
* were never built, which `conversations links rebuild` would also do.
|
|
81
81
|
*/
|
|
82
82
|
readiness(options: {
|
|
83
83
|
chat?: string;
|
|
@@ -152,9 +152,9 @@ export interface SearchReadiness {
|
|
|
152
152
|
wordsOnly: Id[];
|
|
153
153
|
/** Searched by meaning, but some chunks have no vector yet: `conversations embed`. */
|
|
154
154
|
partial: Id[];
|
|
155
|
-
/** Messages or rules changed since the build: `conversations
|
|
155
|
+
/** Messages or rules changed since the build: `conversations links rebuild`. */
|
|
156
156
|
stale: Id[];
|
|
157
|
-
/** Never built, so their matches cannot be shown: `conversations
|
|
157
|
+
/** Never built, so their matches cannot be shown: `conversations links rebuild`. */
|
|
158
158
|
notBuilt: Id[];
|
|
159
159
|
}
|
|
160
160
|
export type ReadinessState = "ready" | "stale" | "partial" | "words-only" | "not-built";
|
|
@@ -93,7 +93,7 @@ export declare const prepareLucene: (store: MessageStore, account: AccountKey, r
|
|
|
93
93
|
/**
|
|
94
94
|
* The stemmer a search with stemmed words runs with. While the stems are built, or chosen by a newer
|
|
95
95
|
* tool, none: a half-built stem index would silently drop matches, while the word index answers each
|
|
96
|
-
* word's own form. Stems waiting for the owner's `store
|
|
96
|
+
* word's own form. Stems waiting for the owner's `store rebuild` still refuse.
|
|
97
97
|
*/
|
|
98
98
|
export declare const searchStemming: (store: MessageStore, command?: string) => Promise<{
|
|
99
99
|
stemmer?: Stemmer;
|
|
@@ -148,7 +148,7 @@ export const prepareLucene = async (store, account, request, messenger) => {
|
|
|
148
148
|
/**
|
|
149
149
|
* The stemmer a search with stemmed words runs with. While the stems are built, or chosen by a newer
|
|
150
150
|
* tool, none: a half-built stem index would silently drop matches, while the word index answers each
|
|
151
|
-
* word's own form. Stems waiting for the owner's `store
|
|
151
|
+
* word's own form. Stems waiting for the owner's `store rebuild` still refuse.
|
|
152
152
|
*/
|
|
153
153
|
export const searchStemming = async (store, command) => {
|
|
154
154
|
const stems = await store.searchIndex.stemsState();
|
|
@@ -203,7 +203,7 @@ const stemsNotReady = (state, command) => {
|
|
|
203
203
|
wanted: state.wanted,
|
|
204
204
|
};
|
|
205
205
|
if (state.cause === "stemmer_changed")
|
|
206
|
-
throw new CliError("validation_error", `the stems were built by ${state.built}, and the store now asks for ${state.wanted} — run \`${cli}store
|
|
206
|
+
throw new CliError("validation_error", `the stems were built by ${state.built}, and the store now asks for ${state.wanted} — run \`${cli}store rebuild\` (or \`${cli}store migrate\`), or ${exact}`, details);
|
|
207
207
|
if (state.cause === "stemmer_unknown")
|
|
208
208
|
throw new CliError("validation_error", `the store asks for stemmers this tool does not know — upgrade this tool, or ${exact}`, details);
|
|
209
209
|
const percent = Math.floor((Math.min(state.filledThrough, state.watermark) / Math.max(state.watermark, 1)) * 100);
|
|
@@ -159,6 +159,7 @@ export interface SendRequest {
|
|
|
159
159
|
/** A post of `chat`, a channel: the message goes to the post's discussion as a reply to it. */
|
|
160
160
|
commentTo?: Id;
|
|
161
161
|
}
|
|
162
|
+
export type DraftRequest = Pick<SendRequest, "chat" | "text" | "replyTo" | "threadId" | "markdown" | "html" | "noPreview">;
|
|
162
163
|
/** One message in a chat, as typed. */
|
|
163
164
|
export interface MessageTarget {
|
|
164
165
|
chat: string;
|
|
@@ -214,6 +215,10 @@ export interface MessagesService {
|
|
|
214
215
|
stats(query: SearchQuery & {
|
|
215
216
|
by: StatsGrouping;
|
|
216
217
|
}): Promise<MessageStats>;
|
|
218
|
+
saveDraft?(request: DraftRequest): Promise<Operated<{
|
|
219
|
+
chatId: Id;
|
|
220
|
+
saved: true;
|
|
221
|
+
}>>;
|
|
217
222
|
/** A reply is a send with `replyTo`. */
|
|
218
223
|
send(request: SendRequest): Promise<Operated<Sent>>;
|
|
219
224
|
/** With `markdown` or `html`, the marks are taken out as a send takes them. */
|
|
@@ -250,6 +250,29 @@ export const messagesService = (deps) => {
|
|
|
250
250
|
await remember(store, "stats", query);
|
|
251
251
|
return withRefresh(stats, refreshed);
|
|
252
252
|
}),
|
|
253
|
+
saveDraft: async ({ chat, text: typed, replyTo, threadId: typedThread, markdown, html, noPreview }) => {
|
|
254
|
+
if (typed.trim() === "")
|
|
255
|
+
throw new CliError("validation_error", "a draft needs nonempty text; clearing a draft is not supported");
|
|
256
|
+
const connection = await deps.connection();
|
|
257
|
+
const save = capability(connection, "saveDraft", "save a draft");
|
|
258
|
+
const prepared = await formatted(connection, typed, { markdown, html });
|
|
259
|
+
const { text, spans } = deps.agentText ? visibleFormattedText(prepared) : prepared;
|
|
260
|
+
if (text.trim() === "")
|
|
261
|
+
throw new CliError("validation_error", "a draft needs nonempty text after formatting");
|
|
262
|
+
const threadId = threadIdOf(typedThread);
|
|
263
|
+
const validate = threadId === undefined ? undefined : capability(connection, "validateThread", "save a draft in a forum topic");
|
|
264
|
+
const { id: chatId } = await connection.resolve(chat);
|
|
265
|
+
const operationId = newOperationId();
|
|
266
|
+
await guardedWrite(guard, { operationId, chatId, kind: "edit", key: "drafts", length: text.length }, () => save(chatId, text, {
|
|
267
|
+
...(replyTo === undefined ? {} : { replyTo }),
|
|
268
|
+
...(threadId === undefined ? {} : { threadId }),
|
|
269
|
+
...(spans.length === 0 ? {} : { formatting: spans }),
|
|
270
|
+
...(noPreview ? { noPreview } : {}),
|
|
271
|
+
}), undefined, validate === undefined || threadId === undefined
|
|
272
|
+
? undefined
|
|
273
|
+
: () => validate(chatId, threadId, { ...(replyTo === undefined ? {} : { replyTo }) }));
|
|
274
|
+
return { operationId, chatId, saved: true };
|
|
275
|
+
},
|
|
253
276
|
send: async ({ chat, text: typed, sendId, replyTo: typedReplyTo, threadId: typedThread, silent, noPreview, markdown, html, at, attachments = [], key, origin, spoiler, captionAbove, sendAs, commentTo, sticker, stickerFile, }) => {
|
|
254
277
|
const stickers = (sticker === undefined ? 0 : 1) + (stickerFile === undefined ? 0 : 1);
|
|
255
278
|
if (stickers > 0 && (stickers > 1 || typed.trim() !== "" || attachments.length > 0 || markdown || html))
|
|
@@ -11,11 +11,11 @@ export const metadataService = (deps) => {
|
|
|
11
11
|
};
|
|
12
12
|
const refresh = async (reference) => {
|
|
13
13
|
if (deps.offline)
|
|
14
|
-
throw new CliError("validation_error", "metadata
|
|
14
|
+
throw new CliError("validation_error", "metadata sync needs a connection; omit --offline");
|
|
15
15
|
const { store, account, chatId } = await held(reference);
|
|
16
16
|
const found = (await store.chats.chats(account, {})).items.find((chat) => chat.id === chatId);
|
|
17
17
|
if (!found || (found.kind !== "group" && found.kind !== "channel"))
|
|
18
|
-
throw new CliError("validation_error", "metadata
|
|
18
|
+
throw new CliError("validation_error", "metadata sync applies to groups and channels");
|
|
19
19
|
const read = async (adapter) => capability(adapter, "group", "read group or channel metadata")(chatId);
|
|
20
20
|
const card = deps.withConnection ? await deps.withConnection(read) : await read(await deps.connection());
|
|
21
21
|
return store.chats.saveChatMetadata(account, {
|
|
@@ -50,10 +50,10 @@ export const searchNotesQuery = async (store, request) => {
|
|
|
50
50
|
};
|
|
51
51
|
/** Chunks one batch embeds: one short transaction writes each, as for conversations. */
|
|
52
52
|
const NOTE_BATCH = 8;
|
|
53
|
-
/** `command` is given only by a tool that has `models
|
|
53
|
+
/** `command` is given only by a tool that has `models download`; any other gets the tools that do. */
|
|
54
54
|
const downloadHint = (model, command) => command === undefined
|
|
55
|
-
? `install it with a WireCat CLI that has \`models
|
|
56
|
-
: `\`${command} models
|
|
55
|
+
? `install it with a WireCat CLI that has \`models download\` (tg, max or wa); they share the model folder`
|
|
56
|
+
: `\`${command} models download ${model}\``;
|
|
57
57
|
const localModel = (choice, env, command) => {
|
|
58
58
|
const model = textModel(choice ?? DEFAULT_TEXT_MODEL);
|
|
59
59
|
const directory = textModelsDirectory(env);
|
|
@@ -12,7 +12,7 @@ export const validateCatchUpBounds = (options) => {
|
|
|
12
12
|
};
|
|
13
13
|
export const validateCatchUp = (deps, options) => {
|
|
14
14
|
validateCatchUpBounds(options);
|
|
15
|
-
for (const key of ["conversations.links", "conversations.
|
|
15
|
+
for (const key of ["conversations.links", "conversations.embeddings"])
|
|
16
16
|
deps.guard.check({ chatId: null, key }, { reserve: false });
|
|
17
17
|
};
|
|
18
18
|
export const catchUpSearch = async (deps, chat, options, signal, clock = Date.now) => {
|
package/dist/speech/install.d.ts
CHANGED
|
@@ -14,13 +14,15 @@ export declare const installedBytes: (model: SpeechModel) => number;
|
|
|
14
14
|
export type Fetch = (url: string, init?: {
|
|
15
15
|
signal?: AbortSignal;
|
|
16
16
|
}) => Promise<Response>;
|
|
17
|
-
export declare const install: (model: SpeechModel, directory: string, { fetch: get, progress }?: {
|
|
17
|
+
export declare const install: (model: SpeechModel, directory: string, { fetch: get, progress, verifyExisting, }?: {
|
|
18
18
|
fetch?: Fetch;
|
|
19
19
|
progress?: (line: string) => void;
|
|
20
|
+
verifyExisting?: boolean;
|
|
20
21
|
}) => Promise<void>;
|
|
21
22
|
/** Each file not already in place, downloaded and checked against its sha256 before it takes its name. */
|
|
22
|
-
export declare const installFiles: (files: [ModelFile, string][], { fetch: get, progress }?: {
|
|
23
|
+
export declare const installFiles: (files: [ModelFile, string][], { fetch: get, progress, verifyExisting, }?: {
|
|
23
24
|
fetch?: Fetch;
|
|
24
25
|
progress?: (line: string) => void;
|
|
26
|
+
verifyExisting?: boolean;
|
|
25
27
|
}) => Promise<void>;
|
|
26
28
|
export declare const megabytes: (bytes: number) => string;
|
package/dist/speech/install.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { createWriteStream, mkdirSync, statSync } from "node:fs";
|
|
2
|
+
import { createReadStream, createWriteStream, mkdirSync, statSync } from "node:fs";
|
|
3
3
|
import { rename, rm } from "node:fs/promises";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { Readable, Transform } from "node:stream";
|
|
@@ -45,12 +45,24 @@ const sizeOf = (path) => {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
export const installedBytes = (model) => model.files.reduce((sum, file) => sum + file.bytes, 0);
|
|
48
|
-
export const install = async (model, directory, { fetch: get = fetch, progress } = {}) => {
|
|
48
|
+
export const install = async (model, directory, { fetch: get = fetch, progress, verifyExisting, } = {}) => {
|
|
49
49
|
mkdirSync(join(directory, model.id), { recursive: true, mode: 0o700 });
|
|
50
|
-
await installFiles(placed(model, directory), { fetch: get,
|
|
50
|
+
await installFiles(placed(model, directory), { fetch: get, progress, verifyExisting });
|
|
51
51
|
};
|
|
52
52
|
/** Each file not already in place, downloaded and checked against its sha256 before it takes its name. */
|
|
53
|
-
export const installFiles = async (files, { fetch: get = fetch, progress } = {}) => {
|
|
53
|
+
export const installFiles = async (files, { fetch: get = fetch, progress, verifyExisting, } = {}) => {
|
|
54
|
+
if (verifyExisting)
|
|
55
|
+
for (const [file, path] of files) {
|
|
56
|
+
if (sizeOf(path) !== undefined) {
|
|
57
|
+
const hash = createHash("sha256");
|
|
58
|
+
for await (const chunk of createReadStream(path))
|
|
59
|
+
hash.update(chunk);
|
|
60
|
+
if (hash.digest("hex") !== file.sha256) {
|
|
61
|
+
progress?.(`${path}: not the file this version expects, downloading it again`);
|
|
62
|
+
await rm(path, { force: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
54
66
|
for (const [file, path] of files) {
|
|
55
67
|
if (sizeOf(path) === file.bytes)
|
|
56
68
|
continue;
|
package/dist/speech/local.js
CHANGED
|
@@ -41,7 +41,7 @@ export const openLocalSpeechRecognizer = async (id, input = {}) => {
|
|
|
41
41
|
throw new CliError("validation_error", "local speech requires an absolute model directory and bounded output bytes");
|
|
42
42
|
cancelled(options.signal);
|
|
43
43
|
if (!isInstalled(model, directory))
|
|
44
|
-
throw new CliError("not_found", `speech model ${description.model} is not installed; download it explicitly with a WireCat messenger's models
|
|
44
|
+
throw new CliError("not_found", `speech model ${description.model} is not installed; download it explicitly with a WireCat messenger's models download command`, { reason: "model.not-downloaded", model: description.model });
|
|
45
45
|
const { openRecognizer, toModelRate } = await import("./recognize.js");
|
|
46
46
|
cancelled(options.signal);
|
|
47
47
|
const engine = (options.open ?? openRecognizer)(model, modelPath(directory, model), vadPath(directory));
|
|
@@ -21,7 +21,7 @@ export const choose = (messenger, settings, flags, env = process.env) => {
|
|
|
21
21
|
};
|
|
22
22
|
/** Never a download: a model is hundreds of MB, and whether to fetch one is the owner's call. */
|
|
23
23
|
export const notDownloaded = (messenger, model) => new CliError("not_found", `the speech model ${model.id} is not on this machine — the owner downloads it once with ` +
|
|
24
|
-
`\`${typedCommand(messenger.app.command, `models
|
|
24
|
+
`\`${typedCommand(messenger.app.command, `models download ${model.id}`)}\` (${megabytes(installedBytes(model))})`, { reason: "model.not-downloaded", model: model.id });
|
|
25
25
|
/**
|
|
26
26
|
* Over an open connection: the messenger's own transcript, or the recording's bytes for a local model.
|
|
27
27
|
* The model runs after the connection closes — a minute of speech should not hold a socket open.
|
|
@@ -36,7 +36,7 @@ export interface SearchIndexStore {
|
|
|
36
36
|
* apply, `null` when a newer tool saved one this build does not know.
|
|
37
37
|
*/
|
|
38
38
|
stemmers(): Promise<Stemmers | null | undefined>;
|
|
39
|
-
/** Saves them; stems built by other choices wait for `store
|
|
39
|
+
/** Saves them; stems built by other choices wait for `store rebuild` or `store migrate`. */
|
|
40
40
|
saveStemmers(stemmers: Stemmers): Promise<void>;
|
|
41
41
|
}
|
|
42
42
|
export declare const searchIndexStoreOver: (context: StoreContext) => SearchIndexStore;
|
|
@@ -49,7 +49,7 @@ export declare const accountOf: ({ database }: StoreContext, attachmentPk: numbe
|
|
|
49
49
|
/** Failed extraction cannot erase good text; agent text wins over every automated write. */
|
|
50
50
|
export declare const keepText: ({ database, now }: StoreContext, attachmentPk: number, entry: AttachmentTextEntry) => boolean;
|
|
51
51
|
/**
|
|
52
|
-
* For `store
|
|
52
|
+
* For `store rebuild`: the normalized copies follow the current normalizer, then the word index is
|
|
53
53
|
* emptied and filled from them in one transaction — the table is small next to the messages.
|
|
54
54
|
*/
|
|
55
55
|
export declare const resetAttachmentWords: (database: CacheDatabase) => number;
|
|
@@ -64,7 +64,7 @@ export const keepText = ({ database, now }, attachmentPk, entry) => {
|
|
|
64
64
|
return Number(changes) > 0;
|
|
65
65
|
};
|
|
66
66
|
/**
|
|
67
|
-
* For `store
|
|
67
|
+
* For `store rebuild`: the normalized copies follow the current normalizer, then the word index is
|
|
68
68
|
* emptied and filled from them in one transaction — the table is small next to the messages.
|
|
69
69
|
*/
|
|
70
70
|
export const resetAttachmentWords = (database) => {
|
|
@@ -93,7 +93,7 @@ const windowOf = (context, id) => {
|
|
|
93
93
|
const { orm } = context;
|
|
94
94
|
const [, chat, first, last] = BATCH_ID.exec(id) ?? [];
|
|
95
95
|
if (!chat)
|
|
96
|
-
throw refused(`${id} is not a batch id from \`conversations batches
|
|
96
|
+
throw refused(`${id} is not a batch id from \`conversations batches show\``);
|
|
97
97
|
const chatKey = Number(chat);
|
|
98
98
|
const ends = orm.all(sql `SELECT id AS pk, sent_at FROM messages WHERE chat_id = ${chatKey} AND deleted_at IS NULL AND id IN (${Number(first)}, ${Number(last)})
|
|
99
99
|
ORDER BY sent_at, id`);
|
|
@@ -17,7 +17,7 @@ export interface Involvement {
|
|
|
17
17
|
message: string | null;
|
|
18
18
|
}
|
|
19
19
|
export interface InvolvementStore {
|
|
20
|
-
/** Every row from scratch, or one person's; `store
|
|
20
|
+
/** Every row from scratch, or one person's; `store rebuild` runs it, and it empties the queue. */
|
|
21
21
|
rebuild(personId?: number): number;
|
|
22
22
|
/** Recomputes what is still queued; every write already does this before it commits. */
|
|
23
23
|
drain(): {
|
|
@@ -84,7 +84,7 @@ export declare const corpusIndexState: (database: CacheDatabase, corpus: Corpus)
|
|
|
84
84
|
export declare const noteIndexState: (database: CacheDatabase) => NoteIndexState;
|
|
85
85
|
/**
|
|
86
86
|
* Indexes one corpus's rows written since the last drain, in short write transactions. These corpora are
|
|
87
|
-
* small, so a change of stemmer choices simply queues every row again instead of waiting for `store
|
|
87
|
+
* small, so a change of stemmer choices simply queues every row again instead of waiting for `store rebuild`.
|
|
88
88
|
*/
|
|
89
89
|
export declare const drainCorpus: (database: CacheDatabase, corpus: Corpus, stemmerFor: (stemmers: Stemmers) => Stemmer, { batch, until }?: {
|
|
90
90
|
batch?: number;
|
|
@@ -95,5 +95,5 @@ export declare const drainNoteIndex: (database: CacheDatabase, stemmerFor: (stem
|
|
|
95
95
|
batch?: number;
|
|
96
96
|
until?: () => boolean;
|
|
97
97
|
}) => number;
|
|
98
|
-
/** Queues every document, note and memory again — for `store
|
|
98
|
+
/** Queues every document, note and memory again — for `store rebuild`. */
|
|
99
99
|
export declare const resetNoteIndex: (database: CacheDatabase) => boolean;
|
|
@@ -143,7 +143,7 @@ const writer = (database, corpus, stemmer) => {
|
|
|
143
143
|
};
|
|
144
144
|
/**
|
|
145
145
|
* Indexes one corpus's rows written since the last drain, in short write transactions. These corpora are
|
|
146
|
-
* small, so a change of stemmer choices simply queues every row again instead of waiting for `store
|
|
146
|
+
* small, so a change of stemmer choices simply queues every row again instead of waiting for `store rebuild`.
|
|
147
147
|
*/
|
|
148
148
|
export const drainCorpus = (database, corpus, stemmerFor, { batch = 200, until = () => false } = {}) => {
|
|
149
149
|
// Pieces written before they carried their account get it, once: purge and vector search filter by it.
|
|
@@ -193,7 +193,7 @@ export const drainCorpus = (database, corpus, stemmerFor, { batch = 200, until =
|
|
|
193
193
|
/** Documents, then notes: what a notes search reads. */
|
|
194
194
|
export const drainNoteIndex = (database, stemmerFor, options = {}) => drainCorpus(database, CORPORA.document, stemmerFor, options) +
|
|
195
195
|
drainCorpus(database, CORPORA.note, stemmerFor, options);
|
|
196
|
-
/** Queues every document, note and memory again — for `store
|
|
196
|
+
/** Queues every document, note and memory again — for `store rebuild`. */
|
|
197
197
|
export const resetNoteIndex = (database) => {
|
|
198
198
|
write(database, () => {
|
|
199
199
|
for (const corpus of Object.values(CORPORA))
|
|
@@ -34,6 +34,6 @@ export declare const fillSearchIndex: (database: CacheDatabase, { batch, until,
|
|
|
34
34
|
}) => SearchIndexFill;
|
|
35
35
|
/**
|
|
36
36
|
* Empties the word index and its vocabulary and starts the fill again from the newest message — for
|
|
37
|
-
* `store
|
|
37
|
+
* `store rebuild`. Messages written meanwhile are indexed by the triggers, as after the migration.
|
|
38
38
|
*/
|
|
39
39
|
export declare const resetSearchIndex: (database: CacheDatabase) => void;
|
|
@@ -138,7 +138,7 @@ const buildTerms = (database, { batch, until, onBatch }) => {
|
|
|
138
138
|
};
|
|
139
139
|
/**
|
|
140
140
|
* Empties the word index and its vocabulary and starts the fill again from the newest message — for
|
|
141
|
-
* `store
|
|
141
|
+
* `store rebuild`. Messages written meanwhile are indexed by the triggers, as after the migration.
|
|
142
142
|
*/
|
|
143
143
|
export const resetSearchIndex = (database) => {
|
|
144
144
|
if (!searchIndexState(database))
|
|
@@ -3,7 +3,7 @@ import { type StemsFill } from "./stems.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Brings the stems towards "ready" in short write transactions: the messages up to the watermark, then
|
|
5
5
|
* the queue. One stemmer per run, so its cache serves every batch. Never rebuilds: a row built by other
|
|
6
|
-
* choices is left for `store migrate` or `store
|
|
6
|
+
* choices is left for `store migrate` or `store rebuild`.
|
|
7
7
|
*/
|
|
8
8
|
export declare const fillStems: (database: CacheDatabase, { batch, until, onBatch, now, }?: {
|
|
9
9
|
batch?: number;
|
|
@@ -13,7 +13,7 @@ export declare const fillStems: (database: CacheDatabase, { batch, until, onBatc
|
|
|
13
13
|
}) => StemsFill;
|
|
14
14
|
/**
|
|
15
15
|
* Empties the stems and starts them again from the newest message, claimed by the store's setting —
|
|
16
|
-
* for `store
|
|
16
|
+
* for `store rebuild`, and for `store migrate` when the stems were built by other choices. A row built
|
|
17
17
|
* by a newer Snowball than this binary has is refused: rebuilding it would downgrade every newer tool.
|
|
18
18
|
*/
|
|
19
19
|
export declare const resetStems: (database: CacheDatabase, { force, now }?: {
|
|
@@ -22,7 +22,7 @@ export declare const resetStems: (database: CacheDatabase, { force, now }?: {
|
|
|
22
22
|
}) => boolean;
|
|
23
23
|
/**
|
|
24
24
|
* Stems an older tool built with its own default, while nobody chose stemmers, start again with this
|
|
25
|
-
* build's default: nobody chose the old one, so it waits for no `store
|
|
25
|
+
* build's default: nobody chose the old one, so it waits for no `store rebuild`. Like a first fill
|
|
26
26
|
* from then on. A saved default is replaced only by newer defaults, so two versions never rebuild each
|
|
27
27
|
* other's. A row a newer Snowball built is left for an upgrade.
|
|
28
28
|
*/
|
|
@@ -5,7 +5,7 @@ import { writeBatch } from "./transaction.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Brings the stems towards "ready" in short write transactions: the messages up to the watermark, then
|
|
7
7
|
* the queue. One stemmer per run, so its cache serves every batch. Never rebuilds: a row built by other
|
|
8
|
-
* choices is left for `store migrate` or `store
|
|
8
|
+
* choices is left for `store migrate` or `store rebuild`.
|
|
9
9
|
*/
|
|
10
10
|
export const fillStems = (database, { batch = 5_000, until = () => false, onBatch, now = Date.now, } = {}) => {
|
|
11
11
|
const filled = { stemmed: 0, drained: 0 };
|
|
@@ -62,7 +62,7 @@ const newer = (a, b) => {
|
|
|
62
62
|
};
|
|
63
63
|
/**
|
|
64
64
|
* Empties the stems and starts them again from the newest message, claimed by the store's setting —
|
|
65
|
-
* for `store
|
|
65
|
+
* for `store rebuild`, and for `store migrate` when the stems were built by other choices. A row built
|
|
66
66
|
* by a newer Snowball than this binary has is refused: rebuilding it would downgrade every newer tool.
|
|
67
67
|
*/
|
|
68
68
|
export const resetStems = (database, { force = false, now = Date.now } = {}) => {
|
|
@@ -93,7 +93,7 @@ export const resetStems = (database, { force = false, now = Date.now } = {}) =>
|
|
|
93
93
|
};
|
|
94
94
|
/**
|
|
95
95
|
* Stems an older tool built with its own default, while nobody chose stemmers, start again with this
|
|
96
|
-
* build's default: nobody chose the old one, so it waits for no `store
|
|
96
|
+
* build's default: nobody chose the old one, so it waits for no `store rebuild`. Like a first fill
|
|
97
97
|
* from then on. A saved default is replaced only by newer defaults, so two versions never rebuild each
|
|
98
98
|
* other's. A row a newer Snowball built is left for an upgrade.
|
|
99
99
|
*/
|
|
@@ -15,7 +15,7 @@ export interface StemsState {
|
|
|
15
15
|
/** Every live message is stemmed by `wanted`: stemmed search may run. */
|
|
16
16
|
ready: boolean;
|
|
17
17
|
/**
|
|
18
|
-
* Why it is not ready: still filling, built by other choices and waiting for `store
|
|
18
|
+
* Why it is not ready: still filling, built by other choices and waiting for `store rebuild`, or the
|
|
19
19
|
* store asks for choices only a newer tool knows.
|
|
20
20
|
*/
|
|
21
21
|
cause?: "building" | "stemmer_changed" | "stemmer_unknown";
|
|
@@ -4,7 +4,7 @@ import type { StoreContext } from "./open.js";
|
|
|
4
4
|
import type { SimilarTask, TaskSimilarity } from "./task-candidates.js";
|
|
5
5
|
/**
|
|
6
6
|
* What became of a pair: `review` waits for the agent that reviews after a sync, `unsure` for the owner; the rest
|
|
7
|
-
* are labelled pairs (tasks
|
|
7
|
+
* are labelled pairs (tasks update, "Learn from answers") — `merged`, `attached` say one job, `rejected`, `unmerged`,
|
|
8
8
|
* `new` say two.
|
|
9
9
|
*/
|
|
10
10
|
export declare const TASK_PAIR_DECISIONS: readonly ["review", "unsure", "merged", "rejected", "unmerged", "attached", "new"];
|
|
@@ -11,7 +11,7 @@ import { read, write } from "./transaction.js";
|
|
|
11
11
|
import { recordTaskEvent } from "./work-tasks.js";
|
|
12
12
|
/**
|
|
13
13
|
* What became of a pair: `review` waits for the agent that reviews after a sync, `unsure` for the owner; the rest
|
|
14
|
-
* are labelled pairs (tasks
|
|
14
|
+
* are labelled pairs (tasks update, "Learn from answers") — `merged`, `attached` say one job, `rejected`, `unmerged`,
|
|
15
15
|
* `new` say two.
|
|
16
16
|
*/
|
|
17
17
|
export const TASK_PAIR_DECISIONS = ["review", "unsure", "merged", "rejected", "unmerged", "attached", "new"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.406.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|