@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 +20 -0
- package/dist/domain/knowledge.d.ts +1 -1
- package/dist/domain/knowledge.js +1 -1
- package/dist/entities/fact.d.ts +15 -1
- package/dist/entities/fact.js +6 -1
- package/dist/entities/fields.d.ts +11 -0
- package/dist/entities/fields.js +11 -0
- package/dist/entities/index.d.ts +25 -1
- package/dist/entities/memory.d.ts +14 -0
- package/dist/entities/memory.js +6 -1
- package/dist/store/index.d.ts +1 -0
- package/dist/store/project-transfer/request.d.ts +1 -1
- package/dist/store/project-transfer/schema.d.ts +2 -2
- package/dist/store/project-transfer/snapshot-model.d.ts +2 -2
- package/dist/store/sqlite/evidence-sources.d.ts +23 -0
- package/dist/store/sqlite/evidence-sources.js +59 -0
- package/dist/store/sqlite/facts.d.ts +16 -0
- package/dist/store/sqlite/facts.js +39 -5
- package/dist/store/sqlite/memories.d.ts +28 -0
- package/dist/store/sqlite/memories.js +62 -3
- package/dist/store/sqlite/project-transfer/snapshot.d.ts +1 -1
- package/package.json +1 -1
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"];
|
package/dist/domain/knowledge.js
CHANGED
|
@@ -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"];
|
package/dist/entities/fact.d.ts
CHANGED
|
@@ -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>;
|
package/dist/entities/fact.js
CHANGED
|
@@ -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>]>;
|
package/dist/entities/fields.js
CHANGED
|
@@ -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,
|
package/dist/entities/index.d.ts
CHANGED
|
@@ -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`">]>;
|
package/dist/entities/memory.js
CHANGED
|
@@ -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,
|
package/dist/store/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|