@wirecat/cli-core 0.546.0 → 0.548.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 +173 -0
- package/dist/calendar/cli/answers.d.ts +7 -2
- package/dist/calendar/cli/answers.js +12 -4
- package/dist/calendar/cli/index.js +9 -8
- package/dist/calendar/feeds.d.ts +19 -0
- package/dist/calendar/feeds.js +52 -9
- package/dist/calendar/source.d.ts +10 -2
- package/dist/calendar/sync.d.ts +2 -0
- package/dist/calendar/sync.js +10 -6
- package/dist/calendar/testing/memory.js +39 -0
- package/dist/cli/messenger/history-query.d.ts +10 -4
- package/dist/cli/messenger/history-query.js +87 -31
- package/dist/cli/messenger/messages-command.js +10 -4
- package/dist/cli/messenger/messages-comments-command.js +4 -2
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/option-hints.d.ts +6 -0
- package/dist/cli/option-hints.js +15 -0
- package/dist/errors/reasons.js +13 -1
- package/dist/mail/cli/index.js +4 -4
- package/dist/mail/index.d.ts +2 -0
- package/dist/mail/index.js +2 -0
- package/dist/mail/partial.d.ts +34 -0
- package/dist/mail/partial.js +94 -0
- package/dist/mail/server-search.js +17 -9
- package/dist/mail/source.d.ts +13 -2
- package/dist/mail/source.js +7 -1
- package/dist/mail/sync-types.d.ts +112 -0
- package/dist/mail/sync-types.js +1 -0
- package/dist/mail/sync.d.ts +4 -106
- package/dist/mail/sync.js +49 -4
- package/dist/mail/testing/fake.d.ts +3 -1
- package/dist/mail/testing/fake.js +10 -2
- package/dist/meetings/event-store.d.ts +10 -0
- package/dist/meetings/testing/event-store-contract.js +36 -0
- package/dist/services/deps.d.ts +0 -2
- package/dist/services/deps.js +0 -2
- package/dist/services/documents/ingestion.js +1 -1
- package/dist/services/documents/knowledge.d.ts +10 -6
- package/dist/services/documents/knowledge.js +25 -11
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/messages.d.ts +2 -0
- package/dist/services/messages.js +25 -23
- package/dist/services/proposal-tasks.d.ts +3 -2
- package/dist/services/proposal-tasks.js +5 -2
- package/dist/services/proposals/ids.d.ts +5 -0
- package/dist/services/proposals/ids.js +43 -0
- package/dist/services/proposals/index.d.ts +3 -0
- package/dist/services/proposals/index.js +2 -0
- package/dist/services/proposals/kinds/action.d.ts +7 -0
- package/dist/services/proposals/kinds/action.js +45 -0
- package/dist/services/proposals/kinds/fact.d.ts +4 -0
- package/dist/services/proposals/kinds/fact.js +43 -0
- package/dist/services/proposals/kinds/findings.d.ts +7 -0
- package/dist/services/proposals/kinds/findings.js +75 -0
- package/dist/services/proposals/kinds/key-document.d.ts +9 -0
- package/dist/services/proposals/kinds/key-document.js +75 -0
- package/dist/services/proposals/kinds/memory.d.ts +4 -0
- package/dist/services/proposals/kinds/memory.js +42 -0
- package/dist/services/proposals/kinds/relationship.d.ts +10 -0
- package/dist/services/proposals/kinds/relationship.js +82 -0
- package/dist/services/proposals/kinds/shared.d.ts +25 -0
- package/dist/services/proposals/kinds/shared.js +49 -0
- package/dist/services/proposals/registry.d.ts +9 -0
- package/dist/services/proposals/registry.js +129 -0
- package/dist/services/proposals/types.d.ts +85 -0
- package/dist/services/proposals/types.js +1 -0
- package/dist/services/server-search.d.ts +7 -4
- package/dist/services/server-search.js +5 -2
- package/dist/services/tasks.js +8 -4
- package/dist/store/decisions.d.ts +65 -0
- package/dist/store/decisions.js +55 -0
- package/dist/store/evidence-findings.d.ts +6 -6
- package/dist/store/index.d.ts +4 -3
- package/dist/store/index.js +2 -1
- package/dist/store/meeting-findings.d.ts +2 -0
- package/dist/store/messages.d.ts +4 -9
- package/dist/store/sqlite/accounts.d.ts +8 -0
- package/dist/store/sqlite/accounts.js +2 -2
- package/dist/store/sqlite/actors.d.ts +13 -2
- package/dist/store/sqlite/actors.js +24 -1
- package/dist/store/sqlite/calendar-forget.d.ts +17 -0
- package/dist/store/sqlite/calendar-forget.js +68 -0
- package/dist/store/sqlite/calendar.js +8 -0
- package/dist/store/sqlite/email-locations.d.ts +16 -0
- package/dist/store/sqlite/email-locations.js +26 -0
- package/dist/store/sqlite/emails.d.ts +6 -2
- package/dist/store/sqlite/emails.js +2 -1
- package/dist/store/sqlite/evidence-findings.js +13 -3
- package/dist/store/sqlite/facts.d.ts +16 -8
- package/dist/store/sqlite/facts.js +48 -15
- package/dist/store/sqlite/findings-core.d.ts +2 -1
- package/dist/store/sqlite/findings-core.js +4 -4
- package/dist/store/sqlite/knowledge.d.ts +43 -7
- package/dist/store/sqlite/knowledge.js +55 -15
- package/dist/store/sqlite/meeting-findings.js +6 -6
- package/dist/store/sqlite/memories.d.ts +12 -8
- package/dist/store/sqlite/memories.js +33 -10
- package/dist/store/sqlite/notes.d.ts +5 -1
- package/dist/store/sqlite/notes.js +18 -2
- package/dist/store/sqlite/open-store.d.ts +4 -1
- package/dist/store/sqlite/open-store.js +8 -2
- package/dist/store/sqlite/open.d.ts +3 -0
- package/dist/store/sqlite/proposed-actions.d.ts +21 -8
- package/dist/store/sqlite/proposed-actions.js +29 -9
- package/dist/store/sqlite/reads.d.ts +13 -2
- package/dist/store/sqlite/reads.js +8 -4
- package/dist/store/sqlite/relation-proposals.d.ts +34 -0
- package/dist/store/sqlite/relation-proposals.js +114 -0
- package/dist/store/store.d.ts +3 -0
- package/dist/store/store.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Proposer } from "../../../store/decisions.js";
|
|
2
|
+
import type { ProposalFilter, ProposalRow } from "../types.js";
|
|
3
|
+
/** A stored author as a proposer, with the kind read from its bot row. */
|
|
4
|
+
export declare const proposerOfAuthor: (author: {
|
|
5
|
+
type: "person" | "bot";
|
|
6
|
+
id: string;
|
|
7
|
+
name: string | null;
|
|
8
|
+
kind: "agent" | "rule" | null;
|
|
9
|
+
}) => Proposer;
|
|
10
|
+
/** What a kind keeps of the rows it read: the filter's status and proposer, newest first, at most `limit`. */
|
|
11
|
+
export declare const kept: (rows: ProposalRow[], filter: ProposalFilter) => ProposalRow[];
|
|
12
|
+
/** The filters every store list takes, so a page is filled by the query and not cut after it. */
|
|
13
|
+
export declare const pushed: (filter: ProposalFilter) => {
|
|
14
|
+
project?: string | undefined;
|
|
15
|
+
proposedBy?: "agent" | "rule" | {
|
|
16
|
+
bot: string;
|
|
17
|
+
} | undefined;
|
|
18
|
+
limit: number;
|
|
19
|
+
};
|
|
20
|
+
/** A suggestion an agent or a rule wrote; Drive's editor links and other records stay out of the inbox. */
|
|
21
|
+
export declare const proposedLink: (provenance: string | null) => boolean;
|
|
22
|
+
export declare const projectOf: (...refs: (string | null | undefined)[]) => string | null;
|
|
23
|
+
export declare const actionStatus: (status: string) => ProposalRow["status"];
|
|
24
|
+
/** The stored statuses a filter's status reads, so the store does the filtering when it can. */
|
|
25
|
+
export declare const storedStatus: (filter: ProposalFilter) => "proposed" | "rejected" | undefined;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** A stored author as a proposer, with the kind read from its bot row. */
|
|
2
|
+
export const proposerOfAuthor = (author) => {
|
|
3
|
+
if (author.type === "person")
|
|
4
|
+
return { type: "person", id: author.id };
|
|
5
|
+
if (author.name === null)
|
|
6
|
+
return { type: "unknown" };
|
|
7
|
+
return { type: "bot", id: author.id, name: author.name, kind: author.kind ?? "agent" };
|
|
8
|
+
};
|
|
9
|
+
const byMatches = (row, by) => {
|
|
10
|
+
if (by === undefined)
|
|
11
|
+
return true;
|
|
12
|
+
const proposer = row.proposedBy;
|
|
13
|
+
if (proposer.type !== "bot")
|
|
14
|
+
return false;
|
|
15
|
+
if (by === "agent" || by === "rule")
|
|
16
|
+
return proposer.kind === by;
|
|
17
|
+
return proposer.name === by.bot;
|
|
18
|
+
};
|
|
19
|
+
/** What a kind keeps of the rows it read: the filter's status and proposer, newest first, at most `limit`. */
|
|
20
|
+
export const kept = (rows, filter) => {
|
|
21
|
+
const status = filter.status ?? "proposed";
|
|
22
|
+
return rows
|
|
23
|
+
.filter((row) => (status === "all" || row.status === status) && byMatches(row, filter.by))
|
|
24
|
+
.filter((row) => filter.project === undefined || row.project === filter.project)
|
|
25
|
+
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
26
|
+
.slice(0, filter.limit);
|
|
27
|
+
};
|
|
28
|
+
/** The filters every store list takes, so a page is filled by the query and not cut after it. */
|
|
29
|
+
export const pushed = (filter) => ({
|
|
30
|
+
...(filter.project === undefined ? {} : { project: filter.project }),
|
|
31
|
+
...(filter.by === undefined ? {} : { proposedBy: filter.by }),
|
|
32
|
+
limit: Math.min(filter.limit, 500),
|
|
33
|
+
});
|
|
34
|
+
/** A suggestion an agent or a rule wrote; Drive's editor links and other records stay out of the inbox. */
|
|
35
|
+
export const proposedLink = (provenance) => /^(agent|rule):/.test(provenance ?? "");
|
|
36
|
+
export const projectOf = (...refs) => refs.find((ref) => typeof ref === "string" && ref.startsWith("project:")) ?? null;
|
|
37
|
+
const ACTION_STATUS = {
|
|
38
|
+
proposed: "proposed",
|
|
39
|
+
approved: "confirmed",
|
|
40
|
+
executed: "confirmed",
|
|
41
|
+
failed: "confirmed",
|
|
42
|
+
rejected: "rejected",
|
|
43
|
+
};
|
|
44
|
+
export const actionStatus = (status) => ACTION_STATUS[status] ?? "proposed";
|
|
45
|
+
/** The stored statuses a filter's status reads, so the store does the filtering when it can. */
|
|
46
|
+
export const storedStatus = (filter) => {
|
|
47
|
+
const status = filter.status ?? "proposed";
|
|
48
|
+
return status === "proposed" || status === "rejected" ? status : undefined;
|
|
49
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MessageStore } from "../../store/store.js";
|
|
2
|
+
import type { ProposalRegistry } from "./types.js";
|
|
3
|
+
/** An empty registry: the host registers its kinds. `proposalRegistry` is this with the core kinds. */
|
|
4
|
+
export declare const emptyProposalRegistry: (store: Pick<MessageStore, "proposedActions" | "decisionPolicy">) => ProposalRegistry & {
|
|
5
|
+
claimed(): string[];
|
|
6
|
+
};
|
|
7
|
+
/** Every proposal kind core knows, behind one check of who may decide. */
|
|
8
|
+
/** The policy is the store's, set when it was opened, so the registry and the store methods never disagree. */
|
|
9
|
+
export declare const proposalRegistry: (store: MessageStore) => ProposalRegistry;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { mayDecide } from "../../store/decisions.js";
|
|
3
|
+
import { actionKind } from "./kinds/action.js";
|
|
4
|
+
import { factKind } from "./kinds/fact.js";
|
|
5
|
+
import { findingsKind } from "./kinds/findings.js";
|
|
6
|
+
import { keyDocumentKind } from "./kinds/key-document.js";
|
|
7
|
+
import { memoryKind } from "./kinds/memory.js";
|
|
8
|
+
import { relationshipKind } from "./kinds/relationship.js";
|
|
9
|
+
const PREFIX = /^([a-z][a-z_]*):/;
|
|
10
|
+
const refusalOf = (id, error) => {
|
|
11
|
+
if (!(error instanceof CliError))
|
|
12
|
+
throw error;
|
|
13
|
+
const code = error.code === "not_found" || error.code === "permission_error" ? error.code : "validation_error";
|
|
14
|
+
const reason = error.details?.reason;
|
|
15
|
+
return { id, code, reason: typeof reason === "string" ? reason : code, message: error.message };
|
|
16
|
+
};
|
|
17
|
+
/** An empty registry: the host registers its kinds. `proposalRegistry` is this with the core kinds. */
|
|
18
|
+
export const emptyProposalRegistry = (store) => {
|
|
19
|
+
const policy = store.decisionPolicy;
|
|
20
|
+
const kinds = [];
|
|
21
|
+
const claimed = () => kinds.flatMap((kind) => kind.stored ?? []);
|
|
22
|
+
const kindOf = async (id) => {
|
|
23
|
+
const prefix = PREFIX.exec(id)?.[1];
|
|
24
|
+
const found = prefix !== undefined
|
|
25
|
+
? kinds.find((kind) => kind.prefix === prefix)
|
|
26
|
+
: await (async () => {
|
|
27
|
+
const stored = (await store.proposedActions.get(id)).kind;
|
|
28
|
+
return (kinds.find((kind) => kind.stored?.includes(stored)) ??
|
|
29
|
+
kinds.find((kind) => kind.prefix === undefined && kind.stored === undefined));
|
|
30
|
+
})();
|
|
31
|
+
if (!found)
|
|
32
|
+
throw new CliError("not_found", `no proposal ${id}`, { reason: "proposal.unknown-kind" });
|
|
33
|
+
return found;
|
|
34
|
+
};
|
|
35
|
+
const registry = {
|
|
36
|
+
claimed,
|
|
37
|
+
register: (kind) => {
|
|
38
|
+
if (kinds.some((one) => one.kind === kind.kind))
|
|
39
|
+
throw new CliError("configuration_error", `proposal kind ${kind.kind} is registered twice`);
|
|
40
|
+
if (kind.prefix !== undefined && kinds.some((one) => one.prefix === kind.prefix))
|
|
41
|
+
throw new CliError("configuration_error", `proposal prefix ${kind.prefix} is registered twice`);
|
|
42
|
+
kinds.push(kind);
|
|
43
|
+
},
|
|
44
|
+
list: async (filter) => {
|
|
45
|
+
if (!Number.isSafeInteger(filter.limit) || filter.limit < 1 || filter.limit > 50)
|
|
46
|
+
throw new CliError("validation_error", "a proposal list takes 1–50 items");
|
|
47
|
+
if (!Number.isSafeInteger(filter.offset) || filter.offset < 0)
|
|
48
|
+
throw new CliError("validation_error", "a proposal list takes a nonnegative offset");
|
|
49
|
+
const asked = filter.kinds === undefined ? kinds : kinds.filter((kind) => filter.kinds?.includes(kind.kind));
|
|
50
|
+
const wide = { ...filter, offset: 0, limit: filter.offset + filter.limit + 1 };
|
|
51
|
+
const rows = (await Promise.all(asked.map((kind) => kind.list(wide)))).flat();
|
|
52
|
+
rows.sort((a, b) => b.createdAt.localeCompare(a.createdAt) || b.id.localeCompare(a.id));
|
|
53
|
+
const page = rows.slice(filter.offset, filter.offset + filter.limit + 1);
|
|
54
|
+
return { items: page.slice(0, filter.limit), hasMore: page.length > filter.limit };
|
|
55
|
+
},
|
|
56
|
+
get: async (id) => (await kindOf(id)).get(id),
|
|
57
|
+
decide: async (ids, decision) => {
|
|
58
|
+
const parts = new Map();
|
|
59
|
+
const whole = new Set();
|
|
60
|
+
const order = [];
|
|
61
|
+
for (const id of ids) {
|
|
62
|
+
const [batch, part] = id.split("/");
|
|
63
|
+
if (!batch)
|
|
64
|
+
continue;
|
|
65
|
+
if (!parts.has(batch)) {
|
|
66
|
+
parts.set(batch, []);
|
|
67
|
+
order.push(batch);
|
|
68
|
+
}
|
|
69
|
+
if (part === undefined)
|
|
70
|
+
whole.add(batch);
|
|
71
|
+
else
|
|
72
|
+
parts.get(batch)?.push(part);
|
|
73
|
+
}
|
|
74
|
+
const decided = [];
|
|
75
|
+
const refused = [];
|
|
76
|
+
const refuseIds = (id, message) => refused.push({ id, code: "validation_error", reason: "proposal.ids", message });
|
|
77
|
+
for (const id of order) {
|
|
78
|
+
const chosen = parts.get(id) ?? [];
|
|
79
|
+
if (chosen.length && whole.has(id)) {
|
|
80
|
+
refuseIds(id, `name ${id} or some of its parts (${id}/${chosen[0]}), not both`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const kind = await kindOf(id);
|
|
85
|
+
if (chosen.length && !kind.parts) {
|
|
86
|
+
refuseIds(id, `a ${kind.kind} proposal has no parts; name ${id} alone`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const row = await kind.get(id);
|
|
90
|
+
if (row.status !== "proposed") {
|
|
91
|
+
refused.push({
|
|
92
|
+
id,
|
|
93
|
+
code: "validation_error",
|
|
94
|
+
reason: "proposal.wrong-state",
|
|
95
|
+
message: `${id} is already ${row.status}`,
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const refusal = mayDecide(row, decision.by, policy);
|
|
100
|
+
if (refusal) {
|
|
101
|
+
refused.push(refusal);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
decided.push(await kind.decide(id, chosen.length ? { ...decision, parts: chosen } : decision));
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
refused.push(refusalOf(id, error));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { decided, refused };
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return registry;
|
|
114
|
+
};
|
|
115
|
+
/** Every proposal kind core knows, behind one check of who may decide. */
|
|
116
|
+
/** The policy is the store's, set when it was opened, so the registry and the store methods never disagree. */
|
|
117
|
+
export const proposalRegistry = (store) => {
|
|
118
|
+
const registry = emptyProposalRegistry(store);
|
|
119
|
+
for (const kind of [
|
|
120
|
+
relationshipKind(store),
|
|
121
|
+
keyDocumentKind(store),
|
|
122
|
+
factKind(store),
|
|
123
|
+
memoryKind(store),
|
|
124
|
+
findingsKind(store),
|
|
125
|
+
])
|
|
126
|
+
registry.register(kind);
|
|
127
|
+
registry.register(actionKind(store, registry.claimed));
|
|
128
|
+
return registry;
|
|
129
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Decider, Proposer, Refusal } from "../../store/decisions.js";
|
|
2
|
+
export interface ProposalRow {
|
|
3
|
+
/** `17070` for a stored proposal; `fact:512`, `memory:88`, `link:17` and `key:9` for the kinds kept elsewhere. */
|
|
4
|
+
id: string;
|
|
5
|
+
kind: string;
|
|
6
|
+
status: "proposed" | "confirmed" | "rejected";
|
|
7
|
+
project: string | null;
|
|
8
|
+
/** The reference it is about. */
|
|
9
|
+
subject: string;
|
|
10
|
+
/** The reference it links to, when it links. */
|
|
11
|
+
object: string | null;
|
|
12
|
+
/** A fact's statement or a finding count; never message text. */
|
|
13
|
+
summary: string | null;
|
|
14
|
+
proposedBy: Proposer;
|
|
15
|
+
reason: string | null;
|
|
16
|
+
/** At most 5 references. */
|
|
17
|
+
evidence: string[];
|
|
18
|
+
score: number | null;
|
|
19
|
+
signals: {
|
|
20
|
+
scorer: string;
|
|
21
|
+
weight: number;
|
|
22
|
+
why: string;
|
|
23
|
+
}[] | null;
|
|
24
|
+
parts: {
|
|
25
|
+
id: string;
|
|
26
|
+
kind: string;
|
|
27
|
+
summary: string;
|
|
28
|
+
outcome: "accepted" | "rejected" | null;
|
|
29
|
+
}[] | null;
|
|
30
|
+
createdAt: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ProposalFilter {
|
|
33
|
+
project?: string;
|
|
34
|
+
kinds?: string[];
|
|
35
|
+
by?: "agent" | "rule" | {
|
|
36
|
+
bot: string;
|
|
37
|
+
};
|
|
38
|
+
status?: ProposalRow["status"] | "all";
|
|
39
|
+
limit: number;
|
|
40
|
+
offset: number;
|
|
41
|
+
}
|
|
42
|
+
export interface Decision {
|
|
43
|
+
confirm: boolean;
|
|
44
|
+
by: Decider;
|
|
45
|
+
/** Why, kept with the decision; a memory has nowhere to keep it. */
|
|
46
|
+
reason?: string;
|
|
47
|
+
/** The findings of a batch to accept; the rest are rejected. */
|
|
48
|
+
parts?: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface Decided {
|
|
51
|
+
id: string;
|
|
52
|
+
kind: string;
|
|
53
|
+
status: "confirmed" | "rejected";
|
|
54
|
+
/** References the decision made: `relationship:2203`, `fact:530`, `task:…`. */
|
|
55
|
+
made: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* One kind of proposal. `prefix` names its own id space (`fact` for `fact:512`); a kind stored in `proposed_actions`
|
|
59
|
+
* names the stored kinds it owns in `stored` and has bare number ids. A new kind registers itself; nothing else changes.
|
|
60
|
+
*/
|
|
61
|
+
export interface ProposalKind {
|
|
62
|
+
readonly kind: string;
|
|
63
|
+
readonly prefix?: string;
|
|
64
|
+
readonly stored?: readonly string[];
|
|
65
|
+
/** Whether its proposals have parts decided on their own, as a findings batch has findings. */
|
|
66
|
+
readonly parts?: boolean;
|
|
67
|
+
/** Bounded: at most `offset + limit` rows, newest first. */
|
|
68
|
+
list(filter: ProposalFilter): Promise<ProposalRow[]>;
|
|
69
|
+
get(id: string): Promise<ProposalRow>;
|
|
70
|
+
/** Called only after `mayDecide` allowed it. */
|
|
71
|
+
decide(id: string, decision: Decision): Promise<Decided>;
|
|
72
|
+
}
|
|
73
|
+
export interface ProposalRegistry {
|
|
74
|
+
register(kind: ProposalKind): void;
|
|
75
|
+
list(filter: ProposalFilter): Promise<{
|
|
76
|
+
items: ProposalRow[];
|
|
77
|
+
hasMore: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
get(id: string): Promise<ProposalRow>;
|
|
80
|
+
/** Each id on its own: one refusal never undoes the others. */
|
|
81
|
+
decide(ids: string[], decision: Decision): Promise<{
|
|
82
|
+
decided: Decided[];
|
|
83
|
+
refused: Refusal[];
|
|
84
|
+
}>;
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,12 +2,15 @@ import type { Id } from "../domain/models.js";
|
|
|
2
2
|
import type { ServiceDeps } from "./deps.js";
|
|
3
3
|
import type { SearchQuery } from "./messages.js";
|
|
4
4
|
export declare const SERVER_SEARCH_KEY = "messages.server-search";
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* `textBytes`: a mail candidate is read up to this many bytes, its headers and text; one cut short is stored partial
|
|
7
|
+
* and the next sync reads it whole.
|
|
8
|
+
*/
|
|
6
9
|
export declare const SERVER_BOUNDS: {
|
|
7
10
|
timeMs: number;
|
|
8
11
|
perCall: number;
|
|
9
12
|
maxCalls: number;
|
|
10
|
-
|
|
13
|
+
textBytes: number;
|
|
11
14
|
};
|
|
12
15
|
export declare const DEFAULT_BACKEND = "both";
|
|
13
16
|
export type Backend = "archive" | "server" | "both";
|
|
@@ -26,8 +29,8 @@ export interface ServerSearched {
|
|
|
26
29
|
chat: string | null;
|
|
27
30
|
reason: "rate_limited" | "search_failed" | "time_or_abort_bound";
|
|
28
31
|
}[];
|
|
29
|
-
/** Candidates
|
|
30
|
-
|
|
32
|
+
/** Candidates stored from their first `SERVER_BOUNDS.textBytes`, without the attachments; a sync reads them whole. */
|
|
33
|
+
partial?: number;
|
|
31
34
|
complete: boolean;
|
|
32
35
|
}
|
|
33
36
|
export type HitSource = "archive" | "server" | "both";
|
|
@@ -2,8 +2,11 @@ import { CliError } from "@wirecat/cli-base";
|
|
|
2
2
|
import { capability } from "../ports/messenger.js";
|
|
3
3
|
import { prepareLucene } from "./messages-search.js";
|
|
4
4
|
export const SERVER_SEARCH_KEY = "messages.server-search";
|
|
5
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* `textBytes`: a mail candidate is read up to this many bytes, its headers and text; one cut short is stored partial
|
|
7
|
+
* and the next sync reads it whole.
|
|
8
|
+
*/
|
|
9
|
+
export const SERVER_BOUNDS = { timeMs: 5_000, perCall: 100, maxCalls: 3, textBytes: 262_144 };
|
|
7
10
|
export const DEFAULT_BACKEND = "both";
|
|
8
11
|
export const sourceKey = (chatId, id) => JSON.stringify([chatId, id]);
|
|
9
12
|
const merge = (one, other) => {
|
package/dist/services/tasks.js
CHANGED
|
@@ -260,7 +260,9 @@ export const tasksService = (deps, categories) => {
|
|
|
260
260
|
const link = {
|
|
261
261
|
from: (await store.tasks.details(target.id)).ref,
|
|
262
262
|
kind: "evidence",
|
|
263
|
-
origin
|
|
263
|
+
...(origin === "owner"
|
|
264
|
+
? { origin: "owner", by: "owner" }
|
|
265
|
+
: { origin: "suggested", confirmed: false }),
|
|
264
266
|
};
|
|
265
267
|
try {
|
|
266
268
|
await store.notes.addLink({ ...link, to: note ? note.ref : source });
|
|
@@ -448,17 +450,19 @@ export const tasksService = (deps, categories) => {
|
|
|
448
450
|
account: account.account,
|
|
449
451
|
chat: await storedChatId(deps.messenger, chat, store, account, { includeLeft: true }),
|
|
450
452
|
})));
|
|
451
|
-
const origin = by === "owner"
|
|
453
|
+
const origin = by === "owner"
|
|
454
|
+
? { origin: "owner", by: "owner" }
|
|
455
|
+
: { origin: "suggested", confirmed: false };
|
|
452
456
|
for (const target of [...written, ...chatRefs]) {
|
|
453
457
|
try {
|
|
454
|
-
await store.notes.addLink({ from, to: target, kind: kind, origin });
|
|
458
|
+
await store.notes.addLink({ from, to: target, kind: kind, ...origin });
|
|
455
459
|
}
|
|
456
460
|
catch (error) {
|
|
457
461
|
// Only text that names nothing typed, or a message not saved yet; a mistyped `person:9` is refused.
|
|
458
462
|
const unsaved = error instanceof CliError && error.code === "not_found" && target.startsWith("msg:");
|
|
459
463
|
if (!unsaved && !(error instanceof CliError && error.code === "validation_error"))
|
|
460
464
|
throw error;
|
|
461
|
-
await store.notes.addLink({ from, targetText: target, kind: kind, origin });
|
|
465
|
+
await store.notes.addLink({ from, targetText: target, kind: kind, ...origin });
|
|
462
466
|
}
|
|
463
467
|
}
|
|
464
468
|
if (!written.some((target) => target.startsWith("msg:")))
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who proposed something waiting for the owner. `unknown` is a suggestion that names no author, such as an
|
|
3
|
+
* unconfirmed link written without provenance: only the owner decides it.
|
|
4
|
+
*/
|
|
5
|
+
export type Proposer = {
|
|
6
|
+
type: "person";
|
|
7
|
+
id: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: "bot";
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
kind: "agent" | "rule";
|
|
13
|
+
} | {
|
|
14
|
+
type: "unknown";
|
|
15
|
+
};
|
|
16
|
+
/** Who decides: the owner, or an agent the host lets decide for the owner, with its reason. */
|
|
17
|
+
export type Decider = "owner" | {
|
|
18
|
+
bot: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
reason: string;
|
|
21
|
+
};
|
|
22
|
+
export interface DecisionPolicy {
|
|
23
|
+
/** The host's `agent_decisions`: `false` keeps every decision with the owner. */
|
|
24
|
+
agentDecisions: boolean;
|
|
25
|
+
/** A rule scorer's proposal is the owner's alone to decide. */
|
|
26
|
+
ruleProposalsOwnerOnly: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare const DEFAULT_DECISION_POLICY: DecisionPolicy;
|
|
29
|
+
export interface Refusal {
|
|
30
|
+
id: string;
|
|
31
|
+
code: "permission_error" | "validation_error" | "not_found";
|
|
32
|
+
reason: string;
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The one check every decide path runs before it confirms or rejects a proposal. `null` means allowed. Every Claude
|
|
37
|
+
* Code session is the bot `claude-code`, so comparing bot names keeps one session from deciding another's proposal.
|
|
38
|
+
*/
|
|
39
|
+
export declare const mayDecide: (row: {
|
|
40
|
+
id: string;
|
|
41
|
+
proposedBy: Proposer;
|
|
42
|
+
}, by: Decider, policy?: DecisionPolicy) => Refusal | null;
|
|
43
|
+
/** One bot whatever its letter case or spacing: `Claude-Code` is `claude-code`. */
|
|
44
|
+
export declare const botKey: (name: string) => string;
|
|
45
|
+
/**
|
|
46
|
+
* Removing or reversing a row: the owner always may; an agent only withdraws its own row while it is still
|
|
47
|
+
* proposed. Everything else stays the owner's.
|
|
48
|
+
*/
|
|
49
|
+
export declare const assertMayWithdraw: (row: {
|
|
50
|
+
id: string;
|
|
51
|
+
proposedBy: Proposer;
|
|
52
|
+
proposed: boolean;
|
|
53
|
+
}, by: Decider | Exclude<Decider, "owner"> | {
|
|
54
|
+
bot: string;
|
|
55
|
+
}) => void;
|
|
56
|
+
/** `mayDecide` for a store method: throws the refusal instead of returning it. */
|
|
57
|
+
export declare const assertMayDecide: (row: {
|
|
58
|
+
id: string;
|
|
59
|
+
proposedBy: Proposer;
|
|
60
|
+
}, by: Decider, policy?: DecisionPolicy) => void;
|
|
61
|
+
/** The author a decision is recorded under. */
|
|
62
|
+
export declare const deciderAuthor: (by: Decider) => "owner" | {
|
|
63
|
+
bot: string;
|
|
64
|
+
model?: string;
|
|
65
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
export const DEFAULT_DECISION_POLICY = { agentDecisions: true, ruleProposalsOwnerOnly: true };
|
|
3
|
+
/**
|
|
4
|
+
* The one check every decide path runs before it confirms or rejects a proposal. `null` means allowed. Every Claude
|
|
5
|
+
* Code session is the bot `claude-code`, so comparing bot names keeps one session from deciding another's proposal.
|
|
6
|
+
*/
|
|
7
|
+
export const mayDecide = (row, by, policy = DEFAULT_DECISION_POLICY) => {
|
|
8
|
+
if (by === "owner")
|
|
9
|
+
return null;
|
|
10
|
+
const refuse = (code, reason, message) => ({
|
|
11
|
+
id: row.id,
|
|
12
|
+
code,
|
|
13
|
+
reason,
|
|
14
|
+
message,
|
|
15
|
+
});
|
|
16
|
+
if (!policy.agentDecisions)
|
|
17
|
+
return refuse("permission_error", "proposals.agent-decisions-off", "agent decisions are turned off; the owner decides");
|
|
18
|
+
if (typeof by.reason !== "string" || !by.reason.trim() || by.reason.length > 2000)
|
|
19
|
+
return refuse("validation_error", "proposal.reason-required", "an agent deciding for the owner gives a reason");
|
|
20
|
+
const proposer = row.proposedBy;
|
|
21
|
+
if (proposer.type === "unknown")
|
|
22
|
+
return refuse("permission_error", "proposal.owner-only", "this proposal names no author; only the owner decides it");
|
|
23
|
+
if (proposer.type === "bot" && proposer.kind === "rule" && policy.ruleProposalsOwnerOnly)
|
|
24
|
+
return refuse("permission_error", "proposal.owner-only", `proposed by ${proposer.name}: only the owner decides it`);
|
|
25
|
+
if (proposer.type === "bot" && proposer.kind === "agent" && botKey(proposer.name) === botKey(by.bot))
|
|
26
|
+
return refuse("permission_error", "proposal.own", `${by.bot} proposed this; another decider must decide it`);
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
/** One bot whatever its letter case or spacing: `Claude-Code` is `claude-code`. */
|
|
30
|
+
export const botKey = (name) => name
|
|
31
|
+
.trim()
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[\s_]+/g, "-");
|
|
34
|
+
/**
|
|
35
|
+
* Removing or reversing a row: the owner always may; an agent only withdraws its own row while it is still
|
|
36
|
+
* proposed. Everything else stays the owner's.
|
|
37
|
+
*/
|
|
38
|
+
export const assertMayWithdraw = (row, by) => {
|
|
39
|
+
if (by === "owner")
|
|
40
|
+
return;
|
|
41
|
+
const proposer = row.proposedBy;
|
|
42
|
+
if (row.proposed && proposer.type === "bot" && proposer.kind === "agent" && botKey(proposer.name) === botKey(by.bot))
|
|
43
|
+
return;
|
|
44
|
+
throw new CliError("permission_error", `only the owner removes or reverses ${row.id}`, {
|
|
45
|
+
reason: "proposal.owner-only",
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
/** `mayDecide` for a store method: throws the refusal instead of returning it. */
|
|
49
|
+
export const assertMayDecide = (row, by, policy = DEFAULT_DECISION_POLICY) => {
|
|
50
|
+
const refusal = mayDecide(row, by, policy);
|
|
51
|
+
if (refusal)
|
|
52
|
+
throw new CliError(refusal.code, refusal.message, { reason: refusal.reason });
|
|
53
|
+
};
|
|
54
|
+
/** The author a decision is recorded under. */
|
|
55
|
+
export const deciderAuthor = (by) => by === "owner" ? "owner" : { bot: by.bot, ...(by.model === undefined ? {} : { model: by.model }) };
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { type FindingOutcome, type FindingsInput, type FindingsReceipt } from "../meetings/index.js";
|
|
2
|
+
import type { Decider } from "./decisions.js";
|
|
2
3
|
import type { EvidenceSource } from "./sqlite/evidence-sources.js";
|
|
3
4
|
export declare const EVIDENCE_FINDINGS_KIND = "evidence_findings";
|
|
4
5
|
export declare const validateEvidenceFindings: (input: FindingsInput) => FindingsInput;
|
|
5
6
|
/** Who decided: the owner, or an agent the host let decide for the owner, with its reason. */
|
|
6
|
-
export type EvidenceFindingsDecider =
|
|
7
|
-
bot: string;
|
|
8
|
-
model?: string;
|
|
9
|
-
reason: string;
|
|
10
|
-
};
|
|
7
|
+
export type EvidenceFindingsDecider = Decider;
|
|
11
8
|
export interface EvidenceFindingsReceipt extends FindingsReceipt {
|
|
12
9
|
decision: {
|
|
13
10
|
by: "owner";
|
|
11
|
+
reason?: string;
|
|
14
12
|
} | {
|
|
15
13
|
by: "agent";
|
|
16
14
|
bot: string;
|
|
@@ -31,6 +29,7 @@ export interface EvidenceFindingsBatch {
|
|
|
31
29
|
type: "person" | "bot";
|
|
32
30
|
id: string;
|
|
33
31
|
name: string | null;
|
|
32
|
+
kind: "agent" | "rule" | null;
|
|
34
33
|
};
|
|
35
34
|
decidedBy: {
|
|
36
35
|
type: "person" | "bot";
|
|
@@ -62,7 +61,8 @@ export interface EvidenceFindingsStore {
|
|
|
62
61
|
review(id: string, input: {
|
|
63
62
|
fingerprint: string;
|
|
64
63
|
accept: string[];
|
|
65
|
-
by
|
|
64
|
+
by: EvidenceFindingsDecider;
|
|
65
|
+
reason?: string;
|
|
66
66
|
}): Promise<EvidenceFindingsBatch>;
|
|
67
67
|
/** Each reference the batch cites, read back once; one gone from the store since is `missing`. */
|
|
68
68
|
sources(id: string): Promise<EvidenceSource[]>;
|
package/dist/store/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { note } from "../entities/note.js";
|
|
|
7
7
|
export type { FindingEvidence, FindingOutcome, MeetingFactFinding, MeetingFinding, MeetingFindingsInput, MeetingFindingsReceipt, MeetingTaskFinding, } from "../meetings/index.js";
|
|
8
8
|
export { CLOSED_STATES, isClosedState, PRIORITY_LABELS, type PriorityLabel, priorityLabel, priorityOf, priorityOption, TASK_STATES, type TaskState, } from "../tasks/index.js";
|
|
9
9
|
export type { AccountMail, AccountMeetings, AccountStore } from "./account-store.js";
|
|
10
|
+
export { assertMayDecide, DEFAULT_DECISION_POLICY, type Decider, type DecisionPolicy, mayDecide, type Proposer, type Refusal, } from "./decisions.js";
|
|
10
11
|
export type { DocumentTransferOptions, DocumentTransferPreview, DocumentTransferReceipt, } from "./document-transfer/index.js";
|
|
11
12
|
export { applyDocumentTransfer, previewDocumentTransfer } from "./document-transfer/index.js";
|
|
12
13
|
export type { CacheDatabase, CacheStatement, OpenDatabase, SqlValue } from "./driver.js";
|
|
@@ -30,7 +31,7 @@ export type { BotUpdate, BotUpdateStore } from "./sqlite/bot-updates.js";
|
|
|
30
31
|
export type { CatalogActor, CatalogEntry, CatalogInput, CatalogQuery, CatalogStore } from "./sqlite/catalog.js";
|
|
31
32
|
export { CATALOG_QUERY } from "./sqlite/catalog.js";
|
|
32
33
|
export type { ChatMetadata } from "./sqlite/chats.js";
|
|
33
|
-
export type { Email, EmailAddress, EmailAttachment, EmailFacts, EmailFilter, EmailInput, EmailInThread, EmailLocationInput, EmailRecipient, EmailRef, EmailThread, EmailVectorHit, FolderEmail, Mailbox, MailboxInput, MailFilter, MailStore, SenderScore, ThreadDetails, ThreadSave, } from "./sqlite/emails.js";
|
|
34
|
+
export type { Email, EmailAddress, EmailAttachment, EmailFacts, EmailFilter, EmailInput, EmailInThread, EmailLocationInput, EmailRecipient, EmailRef, EmailThread, EmailVectorHit, FolderEmail, Mailbox, MailboxInput, MailFilter, MailStore, PartialEmail, SenderScore, ThreadDetails, ThreadSave, } from "./sqlite/emails.js";
|
|
34
35
|
export { EVENT_FIELDS, type EventChunkToEmbed, type EventHit, type EventQuery, type EventSearch, type NearestEvent, } from "./sqlite/event-search.js";
|
|
35
36
|
export type { EvidenceSource } from "./sqlite/evidence-sources.js";
|
|
36
37
|
export type { Fact, FactInput, FactsStore } from "./sqlite/facts.js";
|
|
@@ -49,8 +50,8 @@ export type { NoteIndexState } from "./sqlite/note-index.js";
|
|
|
49
50
|
export { type NearestNote, NOTE_FIELDS, type NoteChunkToEmbed, type NoteHit, type NoteQuery, type NoteSearch, } from "./sqlite/note-search.js";
|
|
50
51
|
export type { FileNoteInput, Link, LinkInput, LinkListFilter, Note, NoteFolder, NotesStore, NoteTag, } from "./sqlite/notes.js";
|
|
51
52
|
export type { PrivateContact, PrivateContactNote } from "./sqlite/private-people.js";
|
|
52
|
-
export type
|
|
53
|
-
export {
|
|
53
|
+
export { PROPOSAL_STATUSES, type ProposalInput, type ProposedAction, type ProposedActionsStore, proposerOfAction, } from "./sqlite/proposed-actions.js";
|
|
54
|
+
export type { LinkDecision } from "./sqlite/relation-proposals.js";
|
|
54
55
|
export { fillSearchIndex, resetSearchIndex, type SearchIndexFill, type SearchIndexState, searchIndexState, } from "./sqlite/search-index.js";
|
|
55
56
|
export type { SyncStateSave } from "./sqlite/sync.js";
|
|
56
57
|
export type { TagCreationOptions, TagCreationReview } from "./sqlite/tag-creation.js";
|
package/dist/store/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export { FACT_STATUSES, FACT_TYPES, MEMORY_KINDS, MEMORY_SCOPES, MEMORY_STATUSES
|
|
|
2
2
|
export { createTagRedirectIndex } from "../domain/tag-redirects.js";
|
|
3
3
|
export { note } from "../entities/note.js";
|
|
4
4
|
export { CLOSED_STATES, isClosedState, PRIORITY_LABELS, priorityLabel, priorityOf, priorityOption, TASK_STATES, } from "../tasks/index.js";
|
|
5
|
+
export { assertMayDecide, DEFAULT_DECISION_POLICY, mayDecide, } from "./decisions.js";
|
|
5
6
|
export { applyDocumentTransfer, previewDocumentTransfer } from "./document-transfer/index.js";
|
|
6
7
|
export { PRAGMAS } from "./driver.js";
|
|
7
8
|
export { EVIDENCE_FINDINGS_KIND, validateEvidenceFindings, } from "./evidence-findings.js";
|
|
@@ -21,7 +22,7 @@ export { EVENT_FIELDS, } from "./sqlite/event-search.js";
|
|
|
21
22
|
export { IMPORTED_DOCUMENT_QUERY } from "./sqlite/imported-documents.js";
|
|
22
23
|
export { ORGANIZATION_KINDS, PROJECT_TYPES, SCOPES } from "./sqlite/knowledge.js";
|
|
23
24
|
export { NOTE_FIELDS, } from "./sqlite/note-search.js";
|
|
24
|
-
export { PROPOSAL_STATUSES } from "./sqlite/proposed-actions.js";
|
|
25
|
+
export { PROPOSAL_STATUSES, proposerOfAction, } from "./sqlite/proposed-actions.js";
|
|
25
26
|
export { fillSearchIndex, resetSearchIndex, searchIndexState, } from "./sqlite/search-index.js";
|
|
26
27
|
export { PROJECT_TASK_FIELDS, TASK_SORTS } from "./sqlite/task-board.js";
|
|
27
28
|
export { certaintyOf } from "./sqlite/task-close.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FindingOutcome, MeetingFindingsInput, MeetingFindingsReceipt } from "../meetings/index.js";
|
|
2
|
+
import type { Decider } from "./decisions.js";
|
|
2
3
|
import type { AccountKey } from "./types.js";
|
|
3
4
|
export interface MeetingFindingsBatch {
|
|
4
5
|
id: string;
|
|
@@ -36,5 +37,6 @@ export interface MeetingFindingsStore {
|
|
|
36
37
|
review(account: AccountKey, id: string, input: {
|
|
37
38
|
fingerprint: string;
|
|
38
39
|
accept: string[];
|
|
40
|
+
by: Decider;
|
|
39
41
|
}): Promise<MeetingFindingsBatch>;
|
|
40
42
|
}
|
package/dist/store/messages.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CounterObservations } from "../domain/counters.js";
|
|
|
2
2
|
import type { Delivery, Id, Message, Page, PersonReaction, PollVote, Reactions, Reactor, WindowedMessage } from "../domain/models.js";
|
|
3
3
|
import { type DocumentLinkMessage } from "./sqlite/document-links.js";
|
|
4
4
|
import type { StoreContext } from "./sqlite/open.js";
|
|
5
|
+
import * as reads from "./sqlite/reads.js";
|
|
5
6
|
import type { AccountKey, DeletionScope, IngestedVia, MessageFilter, SenderChatStats, StoredHit } from "./types.js";
|
|
6
7
|
export interface AccountMessages {
|
|
7
8
|
/**
|
|
@@ -40,16 +41,10 @@ export interface AccountMessages {
|
|
|
40
41
|
/** Oldest to newest, like a provider's history page. */
|
|
41
42
|
/**
|
|
42
43
|
* `threadId` keeps one forum topic's; `outgoing` only the owner's (true) or only the others' (false); `starred`
|
|
43
|
-
* only the ones the owner starred (true) or did not (false).
|
|
44
|
+
* only the ones the owner starred (true) or did not (false). `after` reads forward instead of back, and `until`
|
|
45
|
+
* ends the window; `reads.MessagePageWindow` says how.
|
|
44
46
|
*/
|
|
45
|
-
messages(key: AccountKey, chatId: Id, window:
|
|
46
|
-
limit: number;
|
|
47
|
-
before?: Id;
|
|
48
|
-
since?: string;
|
|
49
|
-
threadId?: Id;
|
|
50
|
-
outgoing?: boolean;
|
|
51
|
-
starred?: boolean;
|
|
52
|
-
}): Promise<Page<Message>>;
|
|
47
|
+
messages(key: AccountKey, chatId: Id, window: reads.MessagePageWindow): Promise<Page<Message>>;
|
|
53
48
|
/** How many stored messages the chat has, sent at `since` or later when it is given. */
|
|
54
49
|
countMessages(key: AccountKey, chatId: Id, options?: {
|
|
55
50
|
since?: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CacheDatabase } from "../driver.js";
|
|
1
2
|
import type { AccountKey, StoredAccount } from "../types.js";
|
|
2
3
|
import type { StoreContext } from "./open.js";
|
|
3
4
|
/** An account's row, made on first sight; a name only replaces what was known when there is one. */
|
|
@@ -10,3 +11,10 @@ export declare const storedAccounts: ({ orm }: StoreContext) => StoredAccount[];
|
|
|
10
11
|
export declare const storedAccount: ({ orm }: StoreContext, { provider, account }: AccountKey) => StoredAccount;
|
|
11
12
|
/** Everything the account holds, children before parents: the foreign keys are enforced. */
|
|
12
13
|
export declare const purgeAccount: ({ orm, database }: StoreContext, accountKey: number) => void;
|
|
14
|
+
export declare const unreferenced: (database: CacheDatabase, table: "identities") => Set<number>;
|
|
15
|
+
/**
|
|
16
|
+
* People only the purged account had seen go with it. An identity stays while anything else points at it
|
|
17
|
+
* (another account, a tag, a note, a link), or while its person is kept: the owner, a person something
|
|
18
|
+
* points at, or one linked to an identity that stays.
|
|
19
|
+
*/
|
|
20
|
+
export declare const dropOrphanPeople: (database: CacheDatabase, unseenBefore: Set<number>) => void;
|