@wirecat/cli-core 0.499.0 → 0.500.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 CHANGED
@@ -7,6 +7,26 @@ shape of this file.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.500.0 — 31.10.2026
11
+
12
+ ### Added
13
+
14
+ - **A memory or fact can be forgotten for good.** `store.memories.remove(ref)` and `store.facts.remove(ref)` erase the
15
+ row, every link from or to it, its tags, and its words, pieces and vectors in the search indexes, in one write. The
16
+ words indexes are merged and freed pages zeroed (`secure_delete`), so the text is not left in the file; the
17
+ write-ahead log is emptied when no reader holds it. A record that replaced it no longer names it (`supersedes:
18
+ null`). Removing a fact also erases the soft-deleted memory it was migrated from in version 2, which still held its
19
+ text. Both answer `{ ref, removed: true, links }`, where `links` counts the links erased.
20
+ - **`store.facts.reject(ref)`** turns down a proposed fact: `FACT_STATUSES` gains `rejected`.
21
+ - **`store.memories.sources(ref)` and `store.facts.sources(ref)`** read each evidence link back as an `EvidenceSource`:
22
+ `ref`, `kind`, `title`, `sender`, `at`, `excerpt` (280 characters at most), `tool` and `missing`. A source deleted
23
+ since is still listed, `missing: true` with no excerpt, where `evidence` leaves it out; `ref` is `null` when its row
24
+ is gone from the store.
25
+ - **`Memory` and `Fact` carry `subjectName` and `subjectIsOwner`.** A person is named by their name, else the
26
+ owner's alias for them, else an identity's name; a project or organization by its name. The owner has no name in the
27
+ store, so `subjectName` is `null` and `subjectIsOwner` is `true`. The `memory` and `fact` entities gain optional
28
+ `subject_name`, `subject_is_owner` and `sources`.
29
+
10
30
  ## 0.499.0 — 31.10.2026
11
31
 
12
32
  ### Added
@@ -1,5 +1,5 @@
1
1
  export declare const FACT_TYPES: readonly ["fact", "statement", "question", "decision", "promise"];
2
- export declare const FACT_STATUSES: readonly ["proposed", "accepted", "superseded", "reversed"];
2
+ export declare const FACT_STATUSES: readonly ["proposed", "accepted", "superseded", "reversed", "rejected"];
3
3
  export declare const MEMORY_KINDS: readonly ["summary", "digest", "preference"];
4
4
  export declare const MEMORY_STATUSES: readonly ["proposed", "confirmed", "stale", "superseded"];
5
5
  export declare const MEMORY_SCOPES: readonly ["personal", "work"];
@@ -1,6 +1,6 @@
1
1
  import { TASK_KINDS } from "../tasks/index.js";
2
2
  export const FACT_TYPES = ["fact", "statement", "question", "decision", "promise"];
3
- export const FACT_STATUSES = ["proposed", "accepted", "superseded", "reversed"];
3
+ export const FACT_STATUSES = ["proposed", "accepted", "superseded", "reversed", "rejected"];
4
4
  export const MEMORY_KINDS = ["summary", "digest", "preference"];
5
5
  export const MEMORY_STATUSES = ["proposed", "confirmed", "stale", "superseded"];
6
6
  export const MEMORY_SCOPES = ["personal", "work"];
@@ -4,9 +4,12 @@ export declare const fact: v.StrictObjectSchema<{
4
4
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5
5
  readonly kind: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
6
6
  readonly text: v.StringSchema<undefined>;
7
- readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed"], undefined>;
7
+ readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed", "rejected"], undefined>;
8
8
  readonly project: v.NullableSchema<v.StringSchema<undefined>, undefined>;
9
9
  readonly subject: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
10
+ /** What the subject is called; `null` for the owner and for a row with no name. */
11
+ readonly subject_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
12
+ readonly subject_is_owner: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
10
13
  readonly occurred_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
11
14
  readonly supersedes: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
12
15
  readonly author: v.StrictObjectSchema<{
@@ -24,6 +27,17 @@ export declare const fact: v.StrictObjectSchema<{
24
27
  /** Who recorded it: the owner, or an agent whose fact waits as proposed. */
25
28
  readonly origin: v.PicklistSchema<["owner", "agent"], undefined>;
26
29
  readonly evidence: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
30
+ /** With `--sources`: each piece of evidence read back. */
31
+ readonly sources: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
32
+ readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
33
+ readonly kind: v.StringSchema<undefined>;
34
+ readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
35
+ readonly sender: v.NullableSchema<v.StringSchema<undefined>, undefined>;
36
+ readonly at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
37
+ readonly excerpt: v.NullableSchema<v.StringSchema<undefined>, undefined>;
38
+ readonly tool: v.NullableSchema<v.StringSchema<undefined>, undefined>;
39
+ readonly missing: v.BooleanSchema<undefined>;
40
+ }, undefined>, undefined>, undefined>;
27
41
  readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
28
42
  readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
29
43
  }, undefined>;
@@ -1,6 +1,6 @@
1
1
  import * as v from "valibot";
2
2
  import { FACT_STATUSES, FACT_TYPES, MEMORY_SCOPES } from "../domain/knowledge.js";
3
- import { author, id, reference, text, time } from "./fields.js";
3
+ import { author, evidenceSource, id, reference, text, time } from "./fields.js";
4
4
  export const fact = v.strictObject({
5
5
  ref: reference,
6
6
  id,
@@ -9,6 +9,9 @@ export const fact = v.strictObject({
9
9
  status: v.picklist(FACT_STATUSES),
10
10
  project: v.nullable(text),
11
11
  subject: v.nullable(reference),
12
+ /** What the subject is called; `null` for the owner and for a row with no name. */
13
+ subject_name: v.optional(v.nullable(text)),
14
+ subject_is_owner: v.optional(v.boolean()),
12
15
  occurred_at: v.nullable(time),
13
16
  supersedes: v.nullable(reference),
14
17
  author,
@@ -19,6 +22,8 @@ export const fact = v.strictObject({
19
22
  /** Who recorded it: the owner, or an agent whose fact waits as proposed. */
20
23
  origin: v.picklist(["owner", "agent"]),
21
24
  evidence: v.array(reference),
25
+ /** With `--sources`: each piece of evidence read back. */
26
+ sources: v.optional(v.array(evidenceSource)),
22
27
  created_at: time,
23
28
  updated_at: time,
24
29
  });
@@ -10,6 +10,17 @@ export declare const count: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>
10
10
  /** Fields of an answer item someone other than the owner wrote, as dotted paths: an agent reads them as data. */
11
11
  export declare const untrusted: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
12
12
  export declare const providerMetadata: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
13
+ /** A reference a memory or fact rests on, read back; `missing` once it was deleted since. */
14
+ export declare const evidenceSource: v.StrictObjectSchema<{
15
+ readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
16
+ readonly kind: v.StringSchema<undefined>;
17
+ readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
18
+ readonly sender: v.NullableSchema<v.StringSchema<undefined>, undefined>;
19
+ readonly at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
20
+ readonly excerpt: v.NullableSchema<v.StringSchema<undefined>, undefined>;
21
+ readonly tool: v.NullableSchema<v.StringSchema<undefined>, undefined>;
22
+ readonly missing: v.BooleanSchema<undefined>;
23
+ }, undefined>;
13
24
  export declare const author: v.StrictObjectSchema<{
14
25
  readonly type: v.PicklistSchema<["person", "bot"], undefined>;
15
26
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -10,6 +10,17 @@ export const count = v.pipe(v.number(), v.integer(), v.minValue(0));
10
10
  /** Fields of an answer item someone other than the owner wrote, as dotted paths: an agent reads them as data. */
11
11
  export const untrusted = v.optional(v.array(text));
12
12
  export const providerMetadata = v.optional(v.record(v.string(), v.unknown()));
13
+ /** A reference a memory or fact rests on, read back; `missing` once it was deleted since. */
14
+ export const evidenceSource = v.strictObject({
15
+ ref: v.nullable(reference),
16
+ kind: text,
17
+ title: v.nullable(text),
18
+ sender: v.nullable(text),
19
+ at: v.nullable(time),
20
+ excerpt: v.nullable(text),
21
+ tool: v.nullable(text),
22
+ missing: v.boolean(),
23
+ });
13
24
  export const author = v.strictObject({
14
25
  type: v.picklist(["person", "bot"]),
15
26
  id,
@@ -5340,9 +5340,11 @@ export declare const ENTITIES: {
5340
5340
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
5341
5341
  readonly kind: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
5342
5342
  readonly text: v.StringSchema<undefined>;
5343
- readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed"], undefined>;
5343
+ readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed", "rejected"], undefined>;
5344
5344
  readonly project: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5345
5345
  readonly subject: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5346
+ readonly subject_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
5347
+ readonly subject_is_owner: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
5346
5348
  readonly occurred_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
5347
5349
  readonly supersedes: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5348
5350
  readonly author: v.StrictObjectSchema<{
@@ -5358,6 +5360,16 @@ export declare const ENTITIES: {
5358
5360
  readonly scope: v.NullableSchema<v.PicklistSchema<readonly ["personal", "work"], undefined>, undefined>;
5359
5361
  readonly origin: v.PicklistSchema<["owner", "agent"], undefined>;
5360
5362
  readonly evidence: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5363
+ readonly sources: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
5364
+ readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5365
+ readonly kind: v.StringSchema<undefined>;
5366
+ readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5367
+ readonly sender: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5368
+ readonly at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
5369
+ readonly excerpt: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5370
+ readonly tool: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5371
+ readonly missing: v.BooleanSchema<undefined>;
5372
+ }, undefined>, undefined>, undefined>;
5361
5373
  readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
5362
5374
  readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
5363
5375
  }, undefined>;
@@ -5367,6 +5379,8 @@ export declare const ENTITIES: {
5367
5379
  readonly kind: v.PicklistSchema<readonly ["summary", "digest", "preference"], undefined>;
5368
5380
  readonly text: v.StringSchema<undefined>;
5369
5381
  readonly subject: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5382
+ readonly subject_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
5383
+ readonly subject_is_owner: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
5370
5384
  readonly author: v.StrictObjectSchema<{
5371
5385
  readonly type: v.PicklistSchema<["person", "bot"], undefined>;
5372
5386
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -5378,6 +5392,16 @@ export declare const ENTITIES: {
5378
5392
  readonly scope: v.PicklistSchema<readonly ["personal", "work"], undefined>;
5379
5393
  readonly supersedes: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5380
5394
  readonly evidence: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5395
+ readonly sources: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
5396
+ readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
5397
+ readonly kind: v.StringSchema<undefined>;
5398
+ readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5399
+ readonly sender: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5400
+ readonly at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
5401
+ readonly excerpt: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5402
+ readonly tool: v.NullableSchema<v.StringSchema<undefined>, undefined>;
5403
+ readonly missing: v.BooleanSchema<undefined>;
5404
+ }, undefined>, undefined>, undefined>;
5381
5405
  readonly last_verified_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
5382
5406
  readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
5383
5407
  readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
@@ -5,6 +5,9 @@ export declare const memory: v.StrictObjectSchema<{
5
5
  readonly kind: v.PicklistSchema<readonly ["summary", "digest", "preference"], undefined>;
6
6
  readonly text: v.StringSchema<undefined>;
7
7
  readonly subject: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
8
+ /** What the subject is called; `null` for the owner and for a row with no name. */
9
+ readonly subject_name: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
10
+ readonly subject_is_owner: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
8
11
  readonly author: v.StrictObjectSchema<{
9
12
  readonly type: v.PicklistSchema<["person", "bot"], undefined>;
10
13
  readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
@@ -16,6 +19,17 @@ export declare const memory: v.StrictObjectSchema<{
16
19
  readonly scope: v.PicklistSchema<readonly ["personal", "work"], undefined>;
17
20
  readonly supersedes: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
18
21
  readonly evidence: v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
22
+ /** With `--sources`: each piece of evidence read back. */
23
+ readonly sources: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
24
+ readonly ref: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>, undefined>;
25
+ readonly kind: v.StringSchema<undefined>;
26
+ readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
27
+ readonly sender: v.NullableSchema<v.StringSchema<undefined>, undefined>;
28
+ readonly at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
29
+ readonly excerpt: v.NullableSchema<v.StringSchema<undefined>, undefined>;
30
+ readonly tool: v.NullableSchema<v.StringSchema<undefined>, undefined>;
31
+ readonly missing: v.BooleanSchema<undefined>;
32
+ }, undefined>, undefined>, undefined>;
19
33
  readonly last_verified_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
20
34
  readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
21
35
  readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
@@ -1,12 +1,15 @@
1
1
  import * as v from "valibot";
2
2
  import { MEMORY_KINDS, MEMORY_SCOPES, MEMORY_STATUSES } from "../domain/knowledge.js";
3
- import { author, id, reference, text, time } from "./fields.js";
3
+ import { author, evidenceSource, id, reference, text, time } from "./fields.js";
4
4
  export const memory = v.strictObject({
5
5
  ref: reference,
6
6
  id,
7
7
  kind: v.picklist(MEMORY_KINDS),
8
8
  text,
9
9
  subject: v.nullable(reference),
10
+ /** What the subject is called; `null` for the owner and for a row with no name. */
11
+ subject_name: v.optional(v.nullable(text)),
12
+ subject_is_owner: v.optional(v.boolean()),
10
13
  author,
11
14
  model: v.nullable(text),
12
15
  confidence: v.nullable(v.number()),
@@ -14,6 +17,8 @@ export const memory = v.strictObject({
14
17
  scope: v.picklist(MEMORY_SCOPES),
15
18
  supersedes: v.nullable(reference),
16
19
  evidence: v.array(reference),
20
+ /** With `--sources`: each piece of evidence read back. */
21
+ sources: v.optional(v.array(evidenceSource)),
17
22
  last_verified_at: v.nullable(time),
18
23
  created_at: time,
19
24
  updated_at: time,
@@ -31,6 +31,7 @@ export { CATALOG_QUERY } from "./sqlite/catalog.js";
31
31
  export type { ChatMetadata } from "./sqlite/chats.js";
32
32
  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";
33
33
  export { EVENT_FIELDS, type EventChunkToEmbed, type EventHit, type EventQuery, type EventSearch, type NearestEvent, } from "./sqlite/event-search.js";
34
+ export type { EvidenceSource } from "./sqlite/evidence-sources.js";
34
35
  export type { Fact, FactInput, FactsStore } from "./sqlite/facts.js";
35
36
  export type { FileListQuery, FilesStore, StoredFile } from "./sqlite/files.js";
36
37
  export { IMPORTED_DOCUMENT_QUERY, type ImportedDocuments } from "./sqlite/imported-documents.js";
@@ -6,7 +6,7 @@ export declare const projectAnnotationRequest: (request: ProjectAnnotationTarget
6
6
  ref: string;
7
7
  type: "decision" | "fact" | "promise" | "question" | "statement";
8
8
  statement: string;
9
- status: "accepted" | "proposed" | "reversed" | "superseded";
9
+ status: "accepted" | "proposed" | "rejected" | "reversed" | "superseded";
10
10
  subject: string | null;
11
11
  occurred_at: number | null;
12
12
  supersedes: string | null;
@@ -25,7 +25,7 @@ export declare const projectAnnotationsSchema: v.StrictObjectSchema<{
25
25
  readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>;
26
26
  readonly type: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
27
27
  readonly statement: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>;
28
- readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed"], undefined>;
28
+ readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed", "rejected"], undefined>;
29
29
  readonly subject: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
30
30
  readonly occurred_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.SafeIntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
31
31
  readonly supersedes: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
@@ -40,7 +40,7 @@ export declare const projectAnnotationsSchema: v.StrictObjectSchema<{
40
40
  ref: string;
41
41
  type: "decision" | "fact" | "promise" | "question" | "statement";
42
42
  statement: string;
43
- status: "accepted" | "proposed" | "reversed" | "superseded";
43
+ status: "accepted" | "proposed" | "rejected" | "reversed" | "superseded";
44
44
  subject: string | null;
45
45
  occurred_at: number | null;
46
46
  supersedes: string | null;
@@ -6,7 +6,7 @@ export declare const projectAnnotationSnapshotSchema: v.StrictObjectSchema<{
6
6
  readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>;
7
7
  readonly type: v.PicklistSchema<readonly ["fact", "statement", "question", "decision", "promise"], undefined>;
8
8
  readonly statement: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>;
9
- readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed"], undefined>;
9
+ readonly status: v.PicklistSchema<readonly ["proposed", "accepted", "superseded", "reversed", "rejected"], undefined>;
10
10
  readonly subject: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
11
11
  readonly occurred_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.SafeIntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
12
12
  readonly supersedes: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, number, undefined>]>, undefined>;
@@ -21,7 +21,7 @@ export declare const projectAnnotationSnapshotSchema: v.StrictObjectSchema<{
21
21
  ref: string;
22
22
  type: "decision" | "fact" | "promise" | "question" | "statement";
23
23
  statement: string;
24
- status: "accepted" | "proposed" | "reversed" | "superseded";
24
+ status: "accepted" | "proposed" | "rejected" | "reversed" | "superseded";
25
25
  subject: string | null;
26
26
  occurred_at: number | null;
27
27
  supersedes: string | null;
@@ -0,0 +1,23 @@
1
+ import type { CacheDatabase } from "../driver.js";
2
+ /** One reference a memory or fact rests on, read back whole enough to judge it without opening it. */
3
+ export interface EvidenceSource {
4
+ /** `null` when the row is gone from the store and its reference with it. */
5
+ ref: string | null;
6
+ kind: string;
7
+ title: string | null;
8
+ sender: string | null;
9
+ at: string | null;
10
+ excerpt: string | null;
11
+ /** The provider the source came through: `telegram`, `email`, `notes`, `zoom`… */
12
+ tool: string | null;
13
+ /** Deleted at its source since it was cited, or no longer in the store. */
14
+ missing: boolean;
15
+ }
16
+ /**
17
+ * Every `evidence` link from a memory or fact, in the order they were written, including one whose target was
18
+ * deleted since: `evidenceOf` drops those, so a reader would never learn the record lost a source.
19
+ */
20
+ export declare const evidenceSourcesOf: (database: CacheDatabase, from: {
21
+ type: string;
22
+ id: number;
23
+ }) => EvidenceSource[];
@@ -0,0 +1,59 @@
1
+ import { linkKind } from "./link-kinds.js";
2
+ import { referenceOfThing, stateOfThing, storedThing } from "./things.js";
3
+ const EXCERPT_CHARS = 280;
4
+ const excerptOf = (text) => {
5
+ if (text == null)
6
+ return null;
7
+ const flat = String(text).replace(/\s+/g, " ").trim();
8
+ if (!flat)
9
+ return null;
10
+ return flat.length > EXCERPT_CHARS ? `${flat.slice(0, EXCERPT_CHARS - 1)}…` : flat;
11
+ };
12
+ const iso = (value) => (value == null ? null : new Date(Number(value)).toISOString());
13
+ const textOf = (value) => (value == null || String(value).trim() === "" ? null : String(value));
14
+ const READS = {
15
+ message: "SELECT c.title, coalesce(m.sender_name, i.name) AS sender, m.sent_at AS at, m.text AS body, a.provider AS tool " +
16
+ "FROM messages m JOIN chats c ON c.id = m.chat_id JOIN accounts a ON a.id = c.account_id " +
17
+ "LEFT JOIN identities i ON i.id = m.sender_identity_id WHERE m.id = ?",
18
+ email: "SELECT e.subject AS title, coalesce(e.from_name, e.from_address) AS sender, e.sent_at AS at, " +
19
+ "coalesce(e.body_text, e.snippet) AS body, 'email' AS tool FROM emails e WHERE e.id = ?",
20
+ note: "SELECT title, NULL AS sender, updated_at AS at, body, 'notes' AS tool FROM notes WHERE id = ?",
21
+ document: "SELECT d.title, NULL AS sender, coalesce(d.external_updated_at, d.updated_at) AS at, d.body, a.provider AS tool " +
22
+ "FROM documents d JOIN accounts a ON a.id = d.account_id WHERE d.id = ?",
23
+ meeting: "SELECT m.title, NULL AS sender, m.started_at AS at, m.description AS body, a.provider AS tool " +
24
+ "FROM meetings m JOIN accounts a ON a.id = m.account_id WHERE m.id = ?",
25
+ meeting_transcript: "SELECT m.title, NULL AS sender, m.started_at AS at, NULL AS body, a.provider AS tool FROM meeting_transcripts t " +
26
+ "JOIN meetings m ON m.id = t.meeting_id JOIN accounts a ON a.id = m.account_id WHERE t.id = ?",
27
+ meeting_transcript_row: "SELECT m.title, r.speaker_name AS sender, m.started_at + coalesce(r.start_ms, 0) AS at, r.text AS body, " +
28
+ "a.provider AS tool FROM meeting_transcript_rows r JOIN meeting_transcripts t ON t.id = r.meeting_transcript_id " +
29
+ "JOIN meetings m ON m.id = t.meeting_id JOIN accounts a ON a.id = m.account_id WHERE r.id = ?",
30
+ };
31
+ const KIND = {
32
+ meeting_transcript: "meeting",
33
+ meeting_transcript_row: "meeting",
34
+ };
35
+ /**
36
+ * Every `evidence` link from a memory or fact, in the order they were written, including one whose target was
37
+ * deleted since: `evidenceOf` drops those, so a reader would never learn the record lost a source.
38
+ */
39
+ export const evidenceSourcesOf = (database, from) => database
40
+ .prepare(`SELECT to_type, to_id FROM links WHERE from_type = ? AND from_id = ? AND kind = ${linkKind("evidence")} ` +
41
+ "AND to_id IS NOT NULL ORDER BY id")
42
+ .all(from.type, from.id)
43
+ .map((link) => {
44
+ const thing = storedThing(link.to_type, link.to_id);
45
+ const state = stateOfThing(database, thing);
46
+ const read = READS[thing.type];
47
+ const row = read === undefined || state === "unavailable" ? undefined : database.prepare(read).get(thing.id);
48
+ const gone = state !== "available";
49
+ return {
50
+ ref: referenceOfThing(database, thing) ?? null,
51
+ kind: KIND[thing.type] ?? thing.type,
52
+ title: textOf(row?.title),
53
+ sender: textOf(row?.sender),
54
+ at: iso(row?.at),
55
+ excerpt: gone ? null : excerptOf(row?.body),
56
+ tool: textOf(row?.tool),
57
+ missing: gone,
58
+ };
59
+ });
@@ -1,5 +1,6 @@
1
1
  import { type FACT_STATUSES, FACT_TYPES, MEMORY_SCOPES } from "../../domain/knowledge.js";
2
2
  import type { Actor } from "./actors.js";
3
+ import { type EvidenceSource } from "./evidence-sources.js";
3
4
  import { type Author } from "./memories.js";
4
5
  import type { StoreContext } from "./open.js";
5
6
  /** What happened, as its evidence shows; its evidence is `links` of kind `evidence`. */
@@ -12,6 +13,8 @@ export interface Fact {
12
13
  status: (typeof FACT_STATUSES)[number];
13
14
  project: string | null;
14
15
  subject: string | null;
16
+ subjectName: string | null;
17
+ subjectIsOwner: boolean;
15
18
  occurredAt: string | null;
16
19
  supersedes: string | null;
17
20
  /** `name` is a bot's handle; `null` for a person. */
@@ -61,6 +64,19 @@ export interface FactsStore {
61
64
  accept(reference: string): Promise<Fact>;
62
65
  /** An accepted decision no longer holds and nothing replaced it. */
63
66
  reverse(reference: string): Promise<Fact>;
67
+ /** The owner turns down a proposed fact; it stays, marked `rejected`. */
68
+ reject(reference: string): Promise<Fact>;
69
+ /**
70
+ * Erases the fact: its row, its links, its tags, and the memory it was migrated from, which still held its text.
71
+ * A decision that replaced it no longer names it. A backup made before keeps it.
72
+ */
73
+ remove(reference: string): Promise<{
74
+ ref: string;
75
+ removed: true;
76
+ links: number;
77
+ }>;
78
+ /** Each reference it rests on, read back; one deleted since is `missing`. */
79
+ sources(reference: string): Promise<EvidenceSource[]>;
64
80
  }
65
81
  type FactOperations = {
66
82
  [K in keyof FactsStore]: FactsStore[K] extends (...args: infer A) => Promise<infer R> ? (...args: A) => R : never;
@@ -1,10 +1,10 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { FACT_TYPES, MEMORY_SCOPES } from "../../domain/knowledge.js";
3
3
  import { calendarMoment } from "../../domain/moment.js";
4
+ import { evidenceSourcesOf } from "./evidence-sources.js";
4
5
  import { linkKind } from "./link-kinds.js";
5
- import { authorActor, authorOf, evidenceOf, linkEvidence } from "./memories.js";
6
+ import { authorActor, authorOf, dropLinksOf, erase, evidenceOf, linkEvidence, subjectOf, } from "./memories.js";
6
7
  import { requiredThing } from "./notes.js";
7
- import { referenceOfThing, storedThing } from "./things.js";
8
8
  import { write } from "./transaction.js";
9
9
  const NAMED = "SELECT f.*, b.name AS author_name FROM facts f LEFT JOIN bots b ON f.author_type = 'bot' AND b.id = f.author_id";
10
10
  const iso = (value) => new Date(Number(value)).toISOString();
@@ -22,9 +22,7 @@ export const factOperationsOver = (context) => {
22
22
  statement: String(row.statement),
23
23
  status: row.status,
24
24
  project: row.project_id == null ? null : `project:${row.project_id}`,
25
- subject: row.subject_type == null
26
- ? null
27
- : (referenceOfThing(database, storedThing(row.subject_type, row.subject_id)) ?? null),
25
+ ...subjectOf(database, row),
28
26
  occurredAt: row.occurred_at == null ? null : iso(row.occurred_at),
29
27
  supersedes: row.supersedes_id == null ? null : `fact:${row.supersedes_id}`,
30
28
  author: authorOf(row),
@@ -125,8 +123,41 @@ export const factOperationsOver = (context) => {
125
123
  database.prepare("UPDATE facts SET status = 'reversed', updated_at = ? WHERE id = ?").run(now(), Number(row.id));
126
124
  return factOf(rowOf(reference));
127
125
  },
126
+ reject: (reference) => {
127
+ const row = rowOf(reference);
128
+ if (row.status !== "proposed")
129
+ throw new CliError("validation_error", `only a proposed fact can be rejected; this one is ${String(row.status)}`);
130
+ database.prepare("UPDATE facts SET status = 'rejected', updated_at = ? WHERE id = ?").run(now(), Number(row.id));
131
+ return factOf(rowOf(reference));
132
+ },
133
+ remove: (reference) => {
134
+ const row = rowOf(reference);
135
+ const id = Number(row.id);
136
+ const twin = row.source === "memory" && row.metadata != null ? migratedFrom(String(row.metadata)) : undefined;
137
+ const links = erase(database, () => {
138
+ database.prepare("UPDATE facts SET supersedes_id = NULL WHERE supersedes_id = ?").run(id);
139
+ const count = dropLinksOf(database, { type: "fact", id });
140
+ database.prepare("DELETE FROM links WHERE from_type = 'fact' AND from_id = ?").run(id);
141
+ database.prepare("DELETE FROM taggings WHERE taggable_type = 'fact' AND taggable_id = ?").run(id);
142
+ database.prepare("DELETE FROM facts WHERE id = ?").run(id);
143
+ if (twin !== undefined)
144
+ database.prepare("DELETE FROM memories WHERE id = ? AND deleted_at IS NOT NULL").run(twin);
145
+ return count;
146
+ });
147
+ return { ref: `fact:${id}`, removed: true, links };
148
+ },
149
+ sources: (reference) => evidenceSourcesOf(database, { type: "fact", id: Number(rowOf(reference).id) }),
128
150
  };
129
151
  };
152
+ const migratedFrom = (metadata) => {
153
+ try {
154
+ const { memory } = JSON.parse(metadata);
155
+ return Number.isInteger(memory) ? Number(memory) : undefined;
156
+ }
157
+ catch {
158
+ return undefined;
159
+ }
160
+ };
130
161
  export const factsStoreOver = (context) => {
131
162
  const operations = factOperationsOver(context);
132
163
  return {
@@ -135,5 +166,8 @@ export const factsStoreOver = (context) => {
135
166
  list: async (options) => operations.list(options),
136
167
  accept: async (reference) => operations.accept(reference),
137
168
  reverse: async (reference) => operations.reverse(reference),
169
+ reject: async (reference) => operations.reject(reference),
170
+ remove: async (reference) => operations.remove(reference),
171
+ sources: async (reference) => operations.sources(reference),
138
172
  };
139
173
  };
@@ -1,5 +1,6 @@
1
1
  import { MEMORY_KINDS, MEMORY_SCOPES, type MEMORY_STATUSES } from "../../domain/knowledge.js";
2
2
  import { type Actor } from "./actors.js";
3
+ import { type EvidenceSource } from "./evidence-sources.js";
3
4
  import { type NoteIndexState } from "./note-index.js";
4
5
  import type { StoreContext } from "./open.js";
5
6
  /** Who wrote it: the owner, or an agent by its bot handle and the model it ran on. */
@@ -14,6 +15,10 @@ export interface Memory {
14
15
  body: string;
15
16
  /** What it is about, as a reference; `null` for a general fact. */
16
17
  subject: string | null;
18
+ /** What the subject is called; `null` for the owner, who has no name in the store, and for a nameless row. */
19
+ subjectName: string | null;
20
+ /** The subject is the owner. */
21
+ subjectIsOwner: boolean;
17
22
  /** `name` is a bot's handle; `null` for a person. */
18
23
  author: {
19
24
  type: Actor["type"];
@@ -59,6 +64,17 @@ export interface MemoriesStore {
59
64
  confirm(reference: string): Promise<Memory>;
60
65
  /** Its evidence no longer holds. */
61
66
  markStale(reference: string): Promise<Memory>;
67
+ /**
68
+ * Erases the memory: its row, its text in the search and meaning indexes, and every link from or to it. A memory
69
+ * that replaced it no longer names it. A backup made before keeps it.
70
+ */
71
+ remove(reference: string): Promise<{
72
+ ref: string;
73
+ removed: true;
74
+ links: number;
75
+ }>;
76
+ /** Each reference it rests on, read back; one deleted since is `missing`. */
77
+ sources(reference: string): Promise<EvidenceSource[]>;
62
78
  /** Indexes what was written since, then finds memories holding every word or a longer one it starts, best first. */
63
79
  search(text: string, options?: {
64
80
  subject?: string;
@@ -82,5 +98,17 @@ export declare const evidenceOf: (context: StoreContext, from: {
82
98
  type: string;
83
99
  id: number;
84
100
  }) => string[];
101
+ /** What a memory's or fact's subject is called, for a reader who should not have to look the reference up. */
102
+ export declare const subjectOf: (database: StoreContext["database"], row: Row) => Pick<Memory, "subject" | "subjectName" | "subjectIsOwner">;
103
+ /** Counts every link from or to a row and drops the ones its delete trigger does not: those pointing at it. */
104
+ export declare const dropLinksOf: (database: StoreContext["database"], thing: {
105
+ type: string;
106
+ id: number;
107
+ }) => number;
108
+ /**
109
+ * Runs a delete so the text does not linger in the file: freed pages are zeroed, the words indexes merged so no
110
+ * segment still holds the erased words, and the write-ahead log emptied when no reader holds it.
111
+ */
112
+ export declare const erase: <T>(database: StoreContext["database"], work: () => T) => T;
85
113
  export declare const memoriesStoreOver: (context: StoreContext) => MemoriesStore;
86
114
  export {};
@@ -2,6 +2,7 @@ import { CliError } from "@wirecat/cli-base";
2
2
  import { MEMORY_KINDS, MEMORY_SCOPES } from "../../domain/knowledge.js";
3
3
  import { normalize } from "../normalize.js";
4
4
  import { botNamed, ownerPerson } from "./actors.js";
5
+ import { evidenceSourcesOf } from "./evidence-sources.js";
5
6
  import { linkKind } from "./link-kinds.js";
6
7
  import { CORPORA, corpusIndexState, drainCorpus } from "./note-index.js";
7
8
  import { requiredThing } from "./notes.js";
@@ -42,6 +43,55 @@ export const evidenceOf = (context, from) => context.database
42
43
  .prepare(`SELECT to_type, to_id FROM links WHERE from_type = ? AND from_id = ? AND kind = ${linkKind("evidence")} AND to_id IS NOT NULL ORDER BY id`)
43
44
  .all(from.type, from.id)
44
45
  .flatMap((row) => referenceOfThing(context.database, storedThing(row.to_type, row.to_id)) ?? []);
46
+ const SUBJECT_NAMES = {
47
+ person: "SELECT p.owner, coalesce(p.name, (SELECT a.name FROM aliases a WHERE a.aliasable_type = 'person' " +
48
+ "AND a.aliasable_id = p.id ORDER BY a.display DESC, a.id LIMIT 1), (SELECT i.name FROM identity_links l " +
49
+ "JOIN identities i ON i.id = l.identity_id WHERE l.person_id = p.id AND i.name IS NOT NULL ORDER BY i.id LIMIT 1)) " +
50
+ "AS name FROM persons p WHERE p.id = ?",
51
+ project: "SELECT 0 AS owner, name FROM projects WHERE id = ?",
52
+ organization: "SELECT 0 AS owner, name FROM organizations WHERE id = ?",
53
+ };
54
+ /** What a memory's or fact's subject is called, for a reader who should not have to look the reference up. */
55
+ export const subjectOf = (database, row) => {
56
+ if (row.subject_type == null)
57
+ return { subject: null, subjectName: null, subjectIsOwner: false };
58
+ const thing = storedThing(row.subject_type, row.subject_id);
59
+ const read = SUBJECT_NAMES[thing.type];
60
+ const named = read === undefined ? undefined : database.prepare(read).get(thing.id);
61
+ return {
62
+ subject: referenceOfThing(database, thing) ?? null,
63
+ subjectName: named?.name == null ? null : String(named.name),
64
+ subjectIsOwner: Number(named?.owner) === 1,
65
+ };
66
+ };
67
+ /** Counts every link from or to a row and drops the ones its delete trigger does not: those pointing at it. */
68
+ export const dropLinksOf = (database, thing) => {
69
+ const count = Number(database
70
+ .prepare("SELECT count(*) AS n FROM links WHERE (from_type = ? AND from_id = ?) OR (to_type = ? AND to_id = ?)")
71
+ .get(thing.type, thing.id, thing.type, thing.id)?.n);
72
+ database.prepare("DELETE FROM links WHERE to_type = ? AND to_id = ?").run(thing.type, thing.id);
73
+ return count;
74
+ };
75
+ /**
76
+ * Runs a delete so the text does not linger in the file: freed pages are zeroed, the words indexes merged so no
77
+ * segment still holds the erased words, and the write-ahead log emptied when no reader holds it.
78
+ */
79
+ export const erase = (database, work) => {
80
+ database.exec("PRAGMA secure_delete = ON");
81
+ try {
82
+ const done = write(database, () => {
83
+ const result = work();
84
+ for (const index of ["memory_words", "memory_stems"])
85
+ database.prepare(`INSERT INTO ${index} (${index}) VALUES ('optimize')`).run();
86
+ return result;
87
+ });
88
+ database.exec("PRAGMA wal_checkpoint(TRUNCATE)");
89
+ return done;
90
+ }
91
+ finally {
92
+ database.exec("PRAGMA secure_delete = OFF");
93
+ }
94
+ };
45
95
  export const memoriesStoreOver = (context) => {
46
96
  const { database, now } = context;
47
97
  const stemmerFor = stemmerCache();
@@ -50,9 +100,7 @@ export const memoriesStoreOver = (context) => {
50
100
  ref: `memory:${row.id}`,
51
101
  kind: row.kind,
52
102
  body: String(row.body),
53
- subject: row.subject_type == null
54
- ? null
55
- : (referenceOfThing(database, storedThing(row.subject_type, row.subject_id)) ?? null),
103
+ ...subjectOf(database, row),
56
104
  author: authorOf(row),
57
105
  model: row.model == null ? null : String(row.model),
58
106
  confidence: row.confidence == null ? null : Number(row.confidence),
@@ -127,6 +175,17 @@ export const memoriesStoreOver = (context) => {
127
175
  },
128
176
  confirm: async (reference) => setStatus(reference, "confirmed", true),
129
177
  markStale: async (reference) => setStatus(reference, "stale", false),
178
+ remove: async (reference) => {
179
+ const id = Number(rowOf(reference).id);
180
+ const links = erase(database, () => {
181
+ database.prepare("UPDATE memories SET supersedes_id = NULL WHERE supersedes_id = ?").run(id);
182
+ const count = dropLinksOf(database, { type: "memory", id });
183
+ database.prepare("DELETE FROM memories WHERE id = ?").run(id);
184
+ return count;
185
+ });
186
+ return { ref: `memory:${id}`, removed: true, links };
187
+ },
188
+ sources: async (reference) => evidenceSourcesOf(database, { type: "memory", id: Number(rowOf(reference).id) }),
130
189
  search: async (text, { subject: subjectRef, scope, limit = 20 } = {}) => {
131
190
  const subject = subjectRef === undefined ? undefined : requiredThing(database, subjectRef);
132
191
  drainCorpus(database, CORPORA.memory, stemmerFor);
@@ -7,7 +7,7 @@ export declare const projectAnnotationSnapshot: (database: CacheDatabase, projec
7
7
  ref: string;
8
8
  type: "decision" | "fact" | "promise" | "question" | "statement";
9
9
  statement: string;
10
- status: "accepted" | "proposed" | "reversed" | "superseded";
10
+ status: "accepted" | "proposed" | "rejected" | "reversed" | "superseded";
11
11
  subject: string | null;
12
12
  occurred_at: number | null;
13
13
  supersedes: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.499.0",
3
+ "version": "0.500.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",