@wirecat/cli-core 0.559.0 → 0.561.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 +74 -0
- package/dist/background/schedules.d.ts +2 -0
- package/dist/calendar/busy.d.ts +11 -0
- package/dist/calendar/busy.js +14 -1
- package/dist/calendar/cli/index.d.ts +9 -3
- package/dist/calendar/cli/index.js +14 -7
- package/dist/calendar/cli/local-times.d.ts +5 -0
- package/dist/calendar/cli/local-times.js +39 -0
- package/dist/calendar/mcp/index.js +1 -1
- package/dist/calendar/reads.d.ts +11 -2
- package/dist/calendar/reads.js +34 -18
- package/dist/calendar/writes.js +3 -44
- package/dist/cli/effects.d.ts +13 -2
- package/dist/cli/effects.js +23 -2
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.js +2 -2
- package/dist/cli/meeting-findings-input.d.ts +3 -0
- package/dist/cli/meeting-findings-input.js +4 -0
- package/dist/cli/messenger/conversation-answers.d.ts +3 -0
- package/dist/cli/messenger/message-answers.d.ts +3 -0
- package/dist/cli/messenger/message-writes.d.ts +6 -0
- package/dist/cli/schedule/schedule-command.d.ts +6 -1
- package/dist/cli/schedule/schedule-command.js +24 -2
- package/dist/entities/contact-context.d.ts +6 -0
- package/dist/entities/conversation-results.d.ts +6 -0
- package/dist/entities/evidence.d.ts +24 -0
- package/dist/entities/index.d.ts +190 -0
- package/dist/entities/index.js +3 -0
- package/dist/entities/listener.d.ts +12 -0
- package/dist/entities/mail-findings.d.ts +116 -0
- package/dist/entities/mail-findings.js +68 -0
- package/dist/entities/message-context.d.ts +9 -0
- package/dist/entities/message-write.d.ts +12 -0
- package/dist/entities/message.d.ts +6 -0
- package/dist/entities/message.js +6 -0
- package/dist/entities/store-records.d.ts +3 -0
- package/dist/errors/reasons.js +3 -0
- package/dist/mail/cli/marks.d.ts +1 -0
- package/dist/mail/cli/marks.js +11 -4
- package/dist/mail/cli/review.js +10 -2
- package/dist/mail/findings.d.ts +55 -0
- package/dist/mail/findings.js +140 -0
- package/dist/mail/index.d.ts +2 -1
- package/dist/mail/index.js +2 -1
- package/dist/mail/marks.d.ts +10 -3
- package/dist/mail/marks.js +34 -5
- package/dist/meetings/findings-rules.d.ts +24 -0
- package/dist/meetings/findings-rules.js +136 -116
- package/dist/meetings/index.d.ts +2 -1
- package/dist/meetings/index.js +2 -1
- package/dist/sends/index.d.ts +1 -0
- package/dist/sends/index.js +1 -0
- package/dist/sends/keyed-guard.d.ts +12 -0
- package/dist/sends/keyed-guard.js +49 -0
- package/dist/services/proposals/kinds/mail-findings.d.ts +8 -0
- package/dist/services/proposals/kinds/mail-findings.js +87 -0
- package/dist/services/proposals/registry.js +3 -1
- package/dist/services/proposals/types.d.ts +2 -0
- package/dist/store/decisions.d.ts +1 -1
- package/dist/store/decisions.js +1 -1
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.js +1 -0
- package/dist/store/mail-findings.d.ts +51 -0
- package/dist/store/mail-findings.js +1 -0
- package/dist/store/path.d.ts +2 -0
- package/dist/store/path.js +21 -1
- package/dist/store/sqlite/email-activity.d.ts +8 -0
- package/dist/store/sqlite/email-activity.js +31 -5
- package/dist/store/sqlite/findings-core.d.ts +1 -0
- package/dist/store/sqlite/findings-core.js +1 -1
- package/dist/store/sqlite/mail-findings.d.ts +3 -0
- package/dist/store/sqlite/mail-findings.js +333 -0
- package/dist/store/sqlite/manifest.js +1 -0
- package/dist/store/sqlite/memories.d.ts +4 -1
- package/dist/store/sqlite/memories.js +48 -43
- package/dist/store/sqlite/migrations.generated.js +6 -0
- package/dist/store/sqlite/open-store.d.ts +1 -1
- package/dist/store/sqlite/open-store.js +4 -1
- package/dist/store/sqlite/open.js +2 -1
- package/dist/store/sqlite/proposed-actions.js +2 -1
- package/dist/store/sqlite/tasks.d.ts +2 -0
- package/dist/store/sqlite/tasks.js +26 -24
- package/dist/store/sqlite/test-accounts.d.ts +8 -0
- package/dist/store/sqlite/test-accounts.js +31 -0
- package/dist/store/sqlite/work-tasks.d.ts +1 -0
- package/dist/store/sqlite/work-tasks.js +6 -0
- package/dist/store/store.d.ts +3 -0
- package/dist/store/store.js +5 -0
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
4
4
|
import { DEFAULT_DECISION_POLICY } from "../decisions.js";
|
|
5
5
|
import { migrate } from "../migrations.js";
|
|
6
6
|
import { storeCapable } from "../open.js";
|
|
7
|
-
import { storePath } from "../path.js";
|
|
7
|
+
import { refuseOwnerStoreInTests, storePath } from "../path.js";
|
|
8
8
|
import { sharedConfigPath } from "../shared-config.js";
|
|
9
9
|
import { backfillNormalized, pendingNormalization } from "./backfill.js";
|
|
10
10
|
import { fillFiles } from "./files.js";
|
|
@@ -18,6 +18,7 @@ import { assertReadCompatible } from "./read-compatibility.js";
|
|
|
18
18
|
import { stemsState } from "./stems.js";
|
|
19
19
|
import { fillStems, followDefaultStemmers } from "./stems-fill.js";
|
|
20
20
|
import { moveSuggestedLinks } from "./suggested-links.js";
|
|
21
|
+
import { purgeTestAccounts } from "./test-accounts.js";
|
|
21
22
|
import { fillVectorIndexes } from "./vector-index.js";
|
|
22
23
|
export const BACKFILL_ON_OPEN = 5_000;
|
|
23
24
|
const NOT_WRITABLE = new Set(["EACCES", "EPERM", "EROFS"]);
|
|
@@ -39,6 +40,7 @@ export const openStoreContext = async ({ path, env, now = Date.now, command, ver
|
|
|
39
40
|
throw new CliError("validation_error", "readOnly must be a boolean");
|
|
40
41
|
await storeCapable();
|
|
41
42
|
const file = path ?? storePath(env);
|
|
43
|
+
refuseOwnerStoreInTests(file);
|
|
42
44
|
if (readOnly) {
|
|
43
45
|
if (!statSync(file, { throwIfNoEntry: false })?.isFile())
|
|
44
46
|
throw new CliError("not_found", "The store does not exist as a file; read-only opening does not create it");
|
|
@@ -59,6 +61,7 @@ export const openStoreContext = async ({ path, env, now = Date.now, command, ver
|
|
|
59
61
|
fillJoinKeys(database);
|
|
60
62
|
moveSuggestedLinks(database, now);
|
|
61
63
|
movePaymentFacts({ ...opened, now, decisions });
|
|
64
|
+
purgeTestAccounts({ ...opened, now, decisions }, warn);
|
|
62
65
|
const pending = pendingNormalization(database);
|
|
63
66
|
if (pending > 0 && pending <= BACKFILL_ON_OPEN)
|
|
64
67
|
backfillNormalized(database);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// biome-ignore-all lint/plugin/no-sqlite-driver: cli-core owns the store, so it opens the driver itself
|
|
2
1
|
import { PRAGMAS } from "../driver.js";
|
|
2
|
+
import { refuseOwnerStoreInTests } from "../path.js";
|
|
3
3
|
import { quietSqliteWarning } from "../sqlite-warning.js";
|
|
4
4
|
/**
|
|
5
5
|
* One SQLite connection, seen two ways: the `CacheDatabase` seam the hand-written SQL and the
|
|
@@ -8,6 +8,7 @@ import { quietSqliteWarning } from "../sqlite-warning.js";
|
|
|
8
8
|
* `../open.ts` for why a static import breaks the other runtime.
|
|
9
9
|
*/
|
|
10
10
|
export const openSqlite = async (path, { readOnly = false } = {}) => {
|
|
11
|
+
refuseOwnerStoreInTests(path);
|
|
11
12
|
const opened = "Bun" in globalThis ? await openUnderBun(path, readOnly) : await openUnderNode(path, readOnly);
|
|
12
13
|
try {
|
|
13
14
|
opened.database.exec(readOnly ? "PRAGMA busy_timeout=5000; PRAGMA foreign_keys=ON; PRAGMA query_only=ON" : PRAGMAS);
|
|
@@ -2,6 +2,7 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
2
2
|
import { MEETING_FINDINGS_KIND } from "../../meetings/index.js";
|
|
3
3
|
import { assertMayDecide, deciderAuthor } from "../decisions.js";
|
|
4
4
|
import { EVIDENCE_FINDINGS_KIND } from "../evidence-findings.js";
|
|
5
|
+
import { MAIL_FINDINGS_KIND } from "../mail-findings.js";
|
|
5
6
|
import { proposedBySql } from "./actors.js";
|
|
6
7
|
import { authorActor } from "./memories.js";
|
|
7
8
|
import { requiredThing } from "./notes.js";
|
|
@@ -15,7 +16,7 @@ const json = (value) => (value == null ? null : JSON.parse(String(value)));
|
|
|
15
16
|
* A findings batch closed with nothing accepted is stored `approved`, as older builds read it; it reads as
|
|
16
17
|
* `rejected`, the decision the owner made.
|
|
17
18
|
*/
|
|
18
|
-
const STATUS = `CASE WHEN p.status = 'approved' AND p.kind IN ('${MEETING_FINDINGS_KIND}', '${EVIDENCE_FINDINGS_KIND}') ` +
|
|
19
|
+
const STATUS = `CASE WHEN p.status = 'approved' AND p.kind IN ('${MEETING_FINDINGS_KIND}', '${EVIDENCE_FINDINGS_KIND}', '${MAIL_FINDINGS_KIND}') ` +
|
|
19
20
|
"AND json_array_length(p.result, '$.accepted') = 0 THEN 'rejected' ELSE p.status END";
|
|
20
21
|
const NAMED = `SELECT p.*, ${STATUS} AS shown_status, b.name AS proposer_name, b.kind AS proposer_kind FROM proposed_actions p ` +
|
|
21
22
|
"LEFT JOIN bots b ON p.proposed_by_type = 'bot' AND b.id = p.proposed_by_id";
|
|
@@ -36,4 +36,6 @@ export interface StoreTaskStore extends Omit<Required<TaskStore>, "reviewDuplica
|
|
|
36
36
|
closeSuggestion: CloseSuggestion | null;
|
|
37
37
|
}[]>;
|
|
38
38
|
}
|
|
39
|
+
/** The inbox project of a task account (`telegram:100`), made the first time `create` asks for it. */
|
|
40
|
+
export declare const inboxProject: ({ database, now }: Pick<StoreContext, "database" | "now">, account: string, create: boolean) => number | undefined;
|
|
39
41
|
export declare const taskStoreOver: (context: StoreContext) => StoreTaskStore;
|
|
@@ -18,32 +18,34 @@ export { inboxKey, linkInbox, taskAccountOf } from "./task-keys.js";
|
|
|
18
18
|
export { taskIdOf, taskRowOf } from "./things.js";
|
|
19
19
|
const COLUMNS = "t.id AS row_id, t.package_id, t.source_locator, t.source_kind, t.source_group, t.type, t.status, t.close_reason, t.source, t.created_at, t.due_at, t.closed_at, " +
|
|
20
20
|
"t.closed_by_type, t.closed_by_id, t.key, t.category, t.claimed_by, t.claimed_until, p.name AS account";
|
|
21
|
+
/** The inbox project of a task account (`telegram:100`), made the first time `create` asks for it. */
|
|
22
|
+
export const inboxProject = ({ database, now }, account, create) => {
|
|
23
|
+
const key = inboxKey(account);
|
|
24
|
+
const found = database.prepare("SELECT id FROM projects WHERE key = ?").get(key);
|
|
25
|
+
if (!create)
|
|
26
|
+
return found ? Number(found.id) : undefined;
|
|
27
|
+
const [provider, external] = [account.slice(0, account.indexOf(":")), account.slice(account.indexOf(":") + 1)];
|
|
28
|
+
const owning = database
|
|
29
|
+
.prepare("SELECT id FROM accounts WHERE provider = ? AND external_id = ?")
|
|
30
|
+
.get(provider, external);
|
|
31
|
+
const accountId = owning ? Number(owning.id) : null;
|
|
32
|
+
// A task can arrive before its messenger saved the account; the project learns it on a later task.
|
|
33
|
+
if (found) {
|
|
34
|
+
if (accountId !== null)
|
|
35
|
+
database
|
|
36
|
+
.prepare("UPDATE projects SET account_id = ? WHERE id = ? AND account_id IS NULL")
|
|
37
|
+
.run(accountId, Number(found.id));
|
|
38
|
+
return Number(found.id);
|
|
39
|
+
}
|
|
40
|
+
const at = now();
|
|
41
|
+
return Number(database
|
|
42
|
+
.prepare("INSERT INTO projects (key, name, name_folded, description, type, account_id, scope, tasks_count, status, created_at, updated_at) " +
|
|
43
|
+
"VALUES (?, ?, ?, ?, 'personal', ?, 'personal', 0, 'active', ?, ?) RETURNING id")
|
|
44
|
+
.get(key, account, folded(account), `What waits on the owner in ${account}`, accountId, at, at)?.id);
|
|
45
|
+
};
|
|
21
46
|
export const taskStoreOver = (context) => {
|
|
22
47
|
const { database, now } = context;
|
|
23
|
-
const inbox = (account, create) =>
|
|
24
|
-
const key = inboxKey(account);
|
|
25
|
-
const found = database.prepare("SELECT id FROM projects WHERE key = ?").get(key);
|
|
26
|
-
if (!create)
|
|
27
|
-
return found ? Number(found.id) : undefined;
|
|
28
|
-
const [provider, external] = [account.slice(0, account.indexOf(":")), account.slice(account.indexOf(":") + 1)];
|
|
29
|
-
const owning = database
|
|
30
|
-
.prepare("SELECT id FROM accounts WHERE provider = ? AND external_id = ?")
|
|
31
|
-
.get(provider, external);
|
|
32
|
-
const accountId = owning ? Number(owning.id) : null;
|
|
33
|
-
// A task can arrive before its messenger saved the account; the project learns it on a later task.
|
|
34
|
-
if (found) {
|
|
35
|
-
if (accountId !== null)
|
|
36
|
-
database
|
|
37
|
-
.prepare("UPDATE projects SET account_id = ? WHERE id = ? AND account_id IS NULL")
|
|
38
|
-
.run(accountId, Number(found.id));
|
|
39
|
-
return Number(found.id);
|
|
40
|
-
}
|
|
41
|
-
const at = now();
|
|
42
|
-
return Number(database
|
|
43
|
-
.prepare("INSERT INTO projects (key, name, name_folded, description, type, account_id, scope, tasks_count, status, created_at, updated_at) " +
|
|
44
|
-
"VALUES (?, ?, ?, ?, 'personal', ?, 'personal', 0, 'active', ?, ?) RETURNING id")
|
|
45
|
-
.get(key, account, folded(account), `What waits on the owner in ${account}`, accountId, at, at)?.id);
|
|
46
|
-
};
|
|
48
|
+
const inbox = (account, create) => inboxProject(context, account, create);
|
|
47
49
|
// A task made here has no package id and may be a type the package does not know; the package never sees it.
|
|
48
50
|
const select = (where) => database.prepare(`SELECT ${COLUMNS} FROM tasks t JOIN projects p ON p.id = t.project_id WHERE t.package_id IS NOT NULL AND ${where}`);
|
|
49
51
|
const toTask = (row) => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { StoreContext } from "./open.js";
|
|
2
|
+
/** Set by store version 48 and deleted once the purge ran. */
|
|
3
|
+
export declare const TEST_ACCOUNTS_TO_PURGE = "test_accounts_to_purge";
|
|
4
|
+
/**
|
|
5
|
+
* Removes every account of the test provider, as `cache clear` removes one. A failure keeps the key for the next open
|
|
6
|
+
* and only warns: a leftover test account must never stop the owner's store from opening.
|
|
7
|
+
*/
|
|
8
|
+
export declare const purgeTestAccounts: (context: StoreContext, warn: (line: string) => void) => number;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { purgeAccount } from "./accounts.js";
|
|
2
|
+
import { write } from "./transaction.js";
|
|
3
|
+
/** Set by store version 48 and deleted once the purge ran. */
|
|
4
|
+
export const TEST_ACCOUNTS_TO_PURGE = "test_accounts_to_purge";
|
|
5
|
+
/** The fake messenger of `src/testing/shared-program.ts`; no tool writes it. */
|
|
6
|
+
const TEST_PROVIDER = "chat";
|
|
7
|
+
/**
|
|
8
|
+
* Removes every account of the test provider, as `cache clear` removes one. A failure keeps the key for the next open
|
|
9
|
+
* and only warns: a leftover test account must never stop the owner's store from opening.
|
|
10
|
+
*/
|
|
11
|
+
export const purgeTestAccounts = (context, warn) => {
|
|
12
|
+
const { database } = context;
|
|
13
|
+
if (!database.prepare("SELECT 1 AS found FROM store_settings WHERE key = ?").get(TEST_ACCOUNTS_TO_PURGE))
|
|
14
|
+
return 0;
|
|
15
|
+
try {
|
|
16
|
+
return write(database, () => {
|
|
17
|
+
const pks = database
|
|
18
|
+
.prepare("SELECT id FROM accounts WHERE provider = ? ORDER BY id")
|
|
19
|
+
.all(TEST_PROVIDER)
|
|
20
|
+
.map((row) => Number(row.id));
|
|
21
|
+
for (const pk of pks)
|
|
22
|
+
purgeAccount(context, pk);
|
|
23
|
+
database.prepare("DELETE FROM store_settings WHERE key = ?").run(TEST_ACCOUNTS_TO_PURGE);
|
|
24
|
+
return pks.length;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
warn(`could not remove the test accounts from the store; the next open tries again: ${String(error)}`);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -232,6 +232,7 @@ export declare const checkRevision: (row: Row, expected: number | undefined) =>
|
|
|
232
232
|
type WorkTaskOperations = Omit<WorkTasks, "create" | "assign"> & {
|
|
233
233
|
create(input: NewWorkTask, by: Author): TaskDetails;
|
|
234
234
|
assign(id: string, assignment: TaskAssignment, by: Author, check?: RevisionCheck): TaskDetails;
|
|
235
|
+
editNow(id: string, fields: TaskFields, by: Author): TaskDetails;
|
|
235
236
|
};
|
|
236
237
|
export declare const workTaskOperationsOver: (context: StoreContext) => WorkTaskOperations;
|
|
237
238
|
export declare const workTasksOver: (context: StoreContext) => WorkTasks;
|
|
@@ -396,6 +396,12 @@ export const workTaskOperationsOver = (context) => {
|
|
|
396
396
|
});
|
|
397
397
|
return details(task);
|
|
398
398
|
},
|
|
399
|
+
/** `edit` inside the caller's write, so a decision that edits a task rolls back with it. */
|
|
400
|
+
editNow: (id, fields, by) => {
|
|
401
|
+
const task = required(id);
|
|
402
|
+
write(database, () => edit(task, fields, authorActor(context, by).actor));
|
|
403
|
+
return details(task);
|
|
404
|
+
},
|
|
399
405
|
move: async (id, move, by, check = {}) => {
|
|
400
406
|
const task = required(id);
|
|
401
407
|
return write(database, () => {
|
package/dist/store/store.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { type ConversationsStore } from "./conversations.js";
|
|
|
9
9
|
import { type CountersStore } from "./counters.js";
|
|
10
10
|
import { type DecisionPolicy } from "./decisions.js";
|
|
11
11
|
import type { EvidenceFindingsStore } from "./evidence-findings.js";
|
|
12
|
+
import type { MailFindingsStore } from "./mail-findings.js";
|
|
12
13
|
import type { MeetingFindingsStore } from "./meeting-findings.js";
|
|
13
14
|
import { type MentionsStore } from "./mentions.js";
|
|
14
15
|
import { type MessagesStore } from "./messages.js";
|
|
@@ -94,6 +95,8 @@ export interface MessageStore {
|
|
|
94
95
|
readonly meetingFindings: MeetingFindingsStore;
|
|
95
96
|
/** Cited facts and follow-ups about one project from stored messages, mail and documents, for review. */
|
|
96
97
|
readonly evidenceFindings: EvidenceFindingsStore;
|
|
98
|
+
/** What an agent read in new mail, cited and staged per mailbox; only the owner decides it. */
|
|
99
|
+
readonly mailFindings: MailFindingsStore;
|
|
97
100
|
/** What any ref names, a page of them at a time: read once per page, never once per row. */
|
|
98
101
|
readonly refs: RefsStore;
|
|
99
102
|
readonly facts: FactsStore;
|
package/dist/store/store.js
CHANGED
|
@@ -28,6 +28,7 @@ import { financeStoreOver } from "./sqlite/finance.js";
|
|
|
28
28
|
import { involvementStoreOver } from "./sqlite/involvements.js";
|
|
29
29
|
import { knowledgeStoreOver } from "./sqlite/knowledge.js";
|
|
30
30
|
import { logPruner } from "./sqlite/log-pruning.js";
|
|
31
|
+
import { mailFindingsStoreOver } from "./sqlite/mail-findings.js";
|
|
31
32
|
import { meetingFindingsStoreOver } from "./sqlite/meeting-findings.js";
|
|
32
33
|
import { meetingVectorsOver } from "./sqlite/meeting-vectors.js";
|
|
33
34
|
import { meetingStoreOver } from "./sqlite/meetings.js";
|
|
@@ -108,6 +109,7 @@ const storeOver = (context) => {
|
|
|
108
109
|
const calendar = once(() => ({ ...eventStoreOver(context), ...eventSearchOver(context) }));
|
|
109
110
|
const meetingFindings = once(() => meetingFindingsStoreOver(context));
|
|
110
111
|
const evidenceFindings = once(() => evidenceFindingsStoreOver(context));
|
|
112
|
+
const mailFindings = once(() => mailFindingsStoreOver(context));
|
|
111
113
|
const refs = {
|
|
112
114
|
summaries: async (texts, options) => refSummariesOf(context.database, texts, options),
|
|
113
115
|
};
|
|
@@ -211,6 +213,9 @@ const storeOver = (context) => {
|
|
|
211
213
|
get evidenceFindings() {
|
|
212
214
|
return evidenceFindings();
|
|
213
215
|
},
|
|
216
|
+
get mailFindings() {
|
|
217
|
+
return mailFindings();
|
|
218
|
+
},
|
|
214
219
|
get facts() {
|
|
215
220
|
return facts();
|
|
216
221
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.561.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",
|