@avantf/dsh-mem 0.3.1 → 0.4.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/README.md +89 -28
- package/cordis.patch.yml +17 -0
- package/lib/client.js +155 -14
- package/lib/engine/mem/config/config_files.d.ts +26 -0
- package/lib/engine/mem/config/domains.d.ts +20 -0
- package/lib/engine/mem/config/loader.d.ts +35 -0
- package/lib/engine/mem/config/paths.d.ts +53 -0
- package/lib/engine/mem/db/chunk.d.ts +26 -0
- package/lib/engine/mem/db/conn.d.ts +13 -0
- package/lib/engine/mem/db/dao/chunks.d.ts +150 -0
- package/lib/engine/mem/db/dao/contradictions.d.ts +98 -0
- package/lib/engine/mem/db/dao/documents.d.ts +51 -0
- package/lib/engine/mem/db/dao/entities.d.ts +67 -0
- package/lib/engine/mem/db/dao/facts.d.ts +472 -0
- package/lib/engine/mem/db/dao/stats.d.ts +14 -0
- package/lib/engine/mem/db/dao/triples.d.ts +80 -0
- package/lib/engine/mem/db/hash.d.ts +2 -0
- package/lib/engine/mem/db/knowledge.d.ts +17 -0
- package/lib/engine/mem/db/migrations.d.ts +71 -0
- package/lib/engine/mem/db/port.d.ts +57 -0
- package/lib/engine/mem/db/schema.d.ts +26 -0
- package/lib/engine/mem/db/sqlite.d.ts +89 -0
- package/lib/engine/mem/db/store.d.ts +35 -0
- package/lib/engine/mem/db/tokenizer.d.ts +61 -0
- package/lib/engine/mem/db/vectors.d.ts +50 -0
- package/lib/engine/mem/dispatch.d.ts +51 -0
- package/lib/engine/mem/entities/extract.d.ts +84 -0
- package/lib/engine/mem/eval/loader.d.ts +19 -0
- package/lib/engine/mem/eval/metrics.d.ts +25 -0
- package/lib/engine/mem/eval/runner.d.ts +10 -0
- package/lib/engine/mem/eval/write_metrics.d.ts +88 -0
- package/lib/engine/mem/git.d.ts +73 -0
- package/lib/engine/mem/hrr/algebra.d.ts +30 -0
- package/lib/engine/mem/hrr/atoms.d.ts +22 -0
- package/lib/engine/mem/hrr/encode.d.ts +32 -0
- package/lib/engine/mem/hrr/index.d.ts +3 -0
- package/lib/engine/mem/index.d.ts +27 -0
- package/lib/engine/mem/lifecycle/contradiction.d.ts +156 -0
- package/lib/engine/mem/lifecycle/index.d.ts +5 -0
- package/lib/engine/mem/lifecycle/maintenance.d.ts +48 -0
- package/lib/engine/mem/lifecycle/presence.d.ts +29 -0
- package/lib/engine/mem/lifecycle/tick.d.ts +43 -0
- package/lib/engine/mem/lifecycle/trust.d.ts +90 -0
- package/lib/engine/mem/modelBootstrap.d.ts +207 -0
- package/lib/engine/mem/router.d.ts +51 -0
- package/lib/engine/mem/runtime.d.ts +232 -0
- package/lib/engine/mem/store/common.d.ts +82 -0
- package/lib/engine/mem/store/doc_files.d.ts +125 -0
- package/lib/engine/mem/store/document_text.d.ts +47 -0
- package/lib/engine/mem/store/floors.d.ts +191 -0
- package/lib/engine/mem/store/hybrid.d.ts +232 -0
- package/lib/engine/mem/store/ingest_guard.d.ts +25 -0
- package/lib/engine/mem/store/knowledge.d.ts +483 -0
- package/lib/engine/mem/store/lexical.d.ts +67 -0
- package/lib/engine/mem/store/memory.d.ts +543 -0
- package/lib/engine/mem/store/source_picker.d.ts +43 -0
- package/lib/engine/mem-contract/config.d.ts +129 -0
- package/lib/engine/mem-contract/env.d.ts +31 -0
- package/lib/engine/mem-contract/family.d.ts +25 -0
- package/lib/engine/mem-contract/index.d.ts +8 -0
- package/lib/engine/mem-contract/log.d.ts +29 -0
- package/lib/engine/mem-contract/remote.d.ts +80 -0
- package/lib/engine/mem-contract/text.d.ts +92 -0
- package/lib/engine/mem-contract/tools.d.ts +284 -0
- package/lib/engine/mem-contract/types.d.ts +615 -0
- package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +47 -0
- package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +174 -0
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +121 -0
- package/lib/engine/mem-retrieval/adapters/local_numpy.d.ts +28 -0
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +52 -0
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +10 -0
- package/lib/engine/mem-retrieval/adapters/warm_gate.d.ts +46 -0
- package/lib/engine/mem-retrieval/budget.d.ts +30 -0
- package/lib/engine/mem-retrieval/fusion.d.ts +53 -0
- package/lib/engine/mem-retrieval/index.d.ts +14 -0
- package/lib/engine/mem-retrieval/interfaces.d.ts +82 -0
- package/lib/engine/mem-retrieval/log.d.ts +11 -0
- package/lib/engine/mem-retrieval/registry.d.ts +23 -0
- package/lib/engine/mem-retrieval/rerank.d.ts +13 -0
- package/lib/engine/mem-retrieval/stats.d.ts +64 -0
- package/lib/engine/mem-retrieval/text_budget.d.ts +52 -0
- package/lib/index.js +1773 -473
- package/lib/interface-version.json +2 -2
- package/lib/types/client/domains.d.ts +5 -4
- package/lib/types/client/domains.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/ingest.d.ts +1 -1
- package/lib/types/client/wire.d.ts +36 -0
- package/lib/types/client/wire.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +3 -2
- package/lib/types/envinit.d.ts +2 -19
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +1 -1
- package/lib/types/index.d.ts +8 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/open.d.ts +61 -11
- package/lib/types/open.d.ts.map +1 -1
- package/lib/types/prompt.d.ts +19 -1
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/provision.d.ts +1 -1
- package/lib/types/provision.d.ts.map +1 -1
- package/lib/types/reconcile.d.ts +64 -0
- package/lib/types/reconcile.d.ts.map +1 -0
- package/lib/types/remote.d.ts +53 -0
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts +35 -0
- package/lib/types/render.d.ts.map +1 -0
- package/lib/types/tool_schema.d.ts +3 -13
- package/lib/types/tool_schema.d.ts.map +1 -1
- package/package.json +24 -15
- package/lib/client.js.map +0 -1
- package/lib/types/client/domains.js +0 -25
- package/lib/types/client/domains.js.map +0 -1
- package/lib/types/client/index.js +0 -1371
- package/lib/types/client/index.js.map +0 -1
- package/lib/types/client/ingest.js +0 -30
- package/lib/types/client/ingest.js.map +0 -1
- package/lib/types/client/paging.js +0 -36
- package/lib/types/client/paging.js.map +0 -1
- package/lib/types/client/summarize.js +0 -79
- package/lib/types/client/summarize.js.map +0 -1
- package/lib/types/data_home.js +0 -26
- package/lib/types/data_home.js.map +0 -1
- package/lib/types/envinit-bootstrap.js +0 -333
- package/lib/types/envinit.js +0 -402
- package/lib/types/envinit.js.map +0 -1
- package/lib/types/hints.js +0 -53
- package/lib/types/hints.js.map +0 -1
- package/lib/types/index.js +0 -755
- package/lib/types/index.js.map +0 -1
- package/lib/types/inject.js +0 -14
- package/lib/types/inject.js.map +0 -1
- package/lib/types/interface_gate.js +0 -84
- package/lib/types/interface_gate.js.map +0 -1
- package/lib/types/open.js +0 -133
- package/lib/types/open.js.map +0 -1
- package/lib/types/prompt.js +0 -148
- package/lib/types/prompt.js.map +0 -1
- package/lib/types/provision.js +0 -177
- package/lib/types/provision.js.map +0 -1
- package/lib/types/remote.js +0 -247
- package/lib/types/remote.js.map +0 -1
- package/lib/types/tool_schema.js +0 -149
- package/lib/types/tool_schema.js.map +0 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relational access port — the ONE place the engine is allowed to say "a database".
|
|
3
|
+
*
|
|
4
|
+
* Stores, lifecycle code and DAOs depend on this interface, never on a driver class, so the
|
|
5
|
+
* database is ONE adapter (`sqlite.ts`, over the runtime's own `node:sqlite`) rather than a
|
|
6
|
+
* driver smeared through 50 call sites — and the contract that adapter owes is written down in that
|
|
7
|
+
* file. This is deliberately NOT a query builder or an ORM: the SQL stays SQL (FTS5,
|
|
8
|
+
* `INDEXED BY`, partial indexes and `julianday` are load-bearing — see DESIGN §19), and the
|
|
9
|
+
* port only fixes the *shape* of the conversation.
|
|
10
|
+
*
|
|
11
|
+
* The surface is five methods on purpose: `prepare / exec / transaction / pragma / close` is
|
|
12
|
+
* the entire driver API this project uses (104 `prepare`, 11 `exec`, 9 `transaction`, 6
|
|
13
|
+
* `pragma`, 4 `close` call sites, and none of the statement extras such as
|
|
14
|
+
* `pluck`/`raw`/`iterate`/`columns`/`function`/`loadExtension`).
|
|
15
|
+
*
|
|
16
|
+
* SYNCHRONOUS by design: `persistFact` runs contradiction detection inside its transaction
|
|
17
|
+
* and the lifecycle tick is one synchronous pass, so an async driver does not fit this port.
|
|
18
|
+
* That is a decision, not an oversight.
|
|
19
|
+
*/
|
|
20
|
+
export interface DbRunResult {
|
|
21
|
+
changes: number;
|
|
22
|
+
lastInsertRowid: number | bigint;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A prepared statement. Rows are asserted by the caller.
|
|
26
|
+
*
|
|
27
|
+
* Params are either positional (`?`, one argument each — `run(a, b)`) or a single object
|
|
28
|
+
* binding the statement's named parameters (`:step`, `@step` and `$step` all match the key
|
|
29
|
+
* `step`, with or without the prefix). Both forms are forwarded to the driver untouched, so a
|
|
30
|
+
* DAO can keep a long predicate readable instead of counting `?` positions — `facts.ts` uses
|
|
31
|
+
* the named form for the settlement clock and the trust diagnostics.
|
|
32
|
+
*/
|
|
33
|
+
export interface DbStatement<Row = unknown> {
|
|
34
|
+
all(...params: unknown[]): Row[];
|
|
35
|
+
get(...params: unknown[]): Row | undefined;
|
|
36
|
+
run(...params: unknown[]): DbRunResult;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A transaction runner: call it to execute inside a transaction, or pick a lock mode.
|
|
40
|
+
*
|
|
41
|
+
* `immediate()` is load-bearing, not a convenience: the presence clock and the lifecycle
|
|
42
|
+
* tick do a cross-process read-modify-write, and only `BEGIN IMMEDIATE` takes the write lock
|
|
43
|
+
* before the first read. A port without it would have silently degraded those paths.
|
|
44
|
+
*/
|
|
45
|
+
export interface DbTransaction<T> {
|
|
46
|
+
(): T;
|
|
47
|
+
immediate(): T;
|
|
48
|
+
deferred(): T;
|
|
49
|
+
exclusive(): T;
|
|
50
|
+
}
|
|
51
|
+
export interface Db {
|
|
52
|
+
prepare<Row = unknown>(sql: string): DbStatement<Row>;
|
|
53
|
+
exec(sql: string): void;
|
|
54
|
+
transaction<T>(fn: () => T): DbTransaction<T>;
|
|
55
|
+
pragma(source: string): unknown;
|
|
56
|
+
close(): void;
|
|
57
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory DB schema.
|
|
3
|
+
*
|
|
4
|
+
* `DDL` is the BASE schema (migration 1) and stays idempotent: databases created before the
|
|
5
|
+
* versioning mechanism existed report `user_version = 0` while already holding these tables,
|
|
6
|
+
* so adopting them must be a no-op plus a version stamp (see `migrations.ts`). Later schema
|
|
7
|
+
* changes belong in a numbered migration — `ONE_OPEN_CONTRADICTION_ROW_SQL` below is v2.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DDL: string[];
|
|
10
|
+
/**
|
|
11
|
+
* v2 — at most one OPEN row per contradiction pair.
|
|
12
|
+
*
|
|
13
|
+
* The in-process `suppressedPairs` dedupe cannot see another process, and the dsh host, the CLI
|
|
14
|
+
* and the MCP server all open this same database. Resolved rows are exempt, so a pair may
|
|
15
|
+
* legitimately be re-opened after it was retired (archive → restore → re-detect). The DELETE
|
|
16
|
+
* runs first because a database written by an older build may already hold duplicates for an
|
|
17
|
+
* open pair, and `CREATE UNIQUE INDEX` would then refuse to build at all.
|
|
18
|
+
*/
|
|
19
|
+
export declare const ONE_OPEN_CONTRADICTION_ROW_SQL: string[];
|
|
20
|
+
/**
|
|
21
|
+
* The external-content FTS5 table. Created separately from {@link DDL} because the
|
|
22
|
+
* tokenizer is chosen at open time by the runtime self-check (`resolveFtsTokenizer`)
|
|
23
|
+
* — a build without `trigram` must not make the store unopenable (DESIGN §16).
|
|
24
|
+
*/
|
|
25
|
+
export declare function factsFtsTableDdl(tokenizer: string): string;
|
|
26
|
+
export declare const FTS_TRIGGERS: string[];
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Db } from './port.js';
|
|
2
|
+
/**
|
|
3
|
+
* The slice of the `node:sqlite` builtin this port uses, declared structurally.
|
|
4
|
+
*
|
|
5
|
+
* Not imported from `@types/node`: the module lives behind its own release line (its typings appeared
|
|
6
|
+
* part-way through the Node 22 line) and the engine must type-check against whatever `@types/node` a
|
|
7
|
+
* consumer's build resolves. Declaring the four signatures the port actually calls keeps that
|
|
8
|
+
* independent.
|
|
9
|
+
*/
|
|
10
|
+
interface RawNodeStatement {
|
|
11
|
+
all(...params: unknown[]): unknown[];
|
|
12
|
+
get(...params: unknown[]): unknown;
|
|
13
|
+
run(...params: unknown[]): {
|
|
14
|
+
changes: number | bigint;
|
|
15
|
+
lastInsertRowid: number | bigint;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Present from Node v22.15 / v23.11 (see {@link sqliteProbe}, which refuses a runtime without it).
|
|
19
|
+
* Optional in the type so this file still compiles against an older `@types/node`.
|
|
20
|
+
*/
|
|
21
|
+
setAllowUnknownNamedParameters?(enabled: boolean): void;
|
|
22
|
+
}
|
|
23
|
+
interface NodeSqliteDatabase {
|
|
24
|
+
prepare(sql: string): RawNodeStatement;
|
|
25
|
+
exec(sql: string): void;
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The builtin's module shape, as far as this adapter is concerned.
|
|
30
|
+
*
|
|
31
|
+
* Exported for {@link probeModule}'s callers — the contract spec hands it a fake.
|
|
32
|
+
*/
|
|
33
|
+
export interface NodeSqliteModule {
|
|
34
|
+
DatabaseSync: new (path: string) => NodeSqliteDatabase;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether this runtime can actually SERVE the stores through `node:sqlite`, or why it cannot.
|
|
38
|
+
*
|
|
39
|
+
* Constructing a throwaway `:memory:` handle is only the first half. A runtime can expose the module
|
|
40
|
+
* and still be unusable for this engine in two ways that would otherwise surface much later, as a DDL
|
|
41
|
+
* error or as a broken lifecycle tick in production:
|
|
42
|
+
*
|
|
43
|
+
* - **no FTS5** — both schemas create `fts5` virtual tables the moment a store is opened, so a build
|
|
44
|
+
* without the module cannot hold a store at all;
|
|
45
|
+
* - **no `setAllowUnknownNamedParameters`** (Node v22.15 / v23.11) — `node:sqlite` throws on a named
|
|
46
|
+
* parameter the statement does not use, while the DAOs deliberately pass one params object to
|
|
47
|
+
* statements that use subsets of it. Without the switch, `FactsDao.purgeArchived` takes the whole
|
|
48
|
+
* lifecycle tick down.
|
|
49
|
+
*
|
|
50
|
+
* The probe reports the FIRST of these that applies as one line; `undefined` means "this runtime can
|
|
51
|
+
* serve a store". It is NOT a dry run of the caller's database: a file that is corrupt, locked or
|
|
52
|
+
* written by a newer schema must still fail loudly in {@link openSqlite} (and mount DEGRADED with its
|
|
53
|
+
* own reason) rather than being reported as a driver problem.
|
|
54
|
+
*/
|
|
55
|
+
export declare function sqliteProbe(): string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* The capability half of {@link sqliteProbe}: what a loaded `node:sqlite` module must be able to do.
|
|
58
|
+
*
|
|
59
|
+
* Exported — and taking the module as an argument — so the contract spec can hand it a FAKE and
|
|
60
|
+
* assert each refusal by name. That is not a driver seam: it is the only way to exercise "this
|
|
61
|
+
* runtime's builtin cannot serve a store" on a machine whose builtin can, and the failure path is
|
|
62
|
+
* exactly the one nobody sees until it happens on a user's host.
|
|
63
|
+
*/
|
|
64
|
+
export declare function probeModule(module: NodeSqliteModule): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* A one-line, NEVER-THROWING description for the startup log.
|
|
67
|
+
*
|
|
68
|
+
* The runtime logs this before it opens the stores, so it must not be the thing that turns "this
|
|
69
|
+
* runtime cannot serve the builtin" into a crash before the caller's own error handling runs. The
|
|
70
|
+
* version is part of the line on purpose: the store files stay per-host (a Desktop HOME and a WSL
|
|
71
|
+
* HOME are different machines' worth of data), so the SQLite version is the one fact that tells an
|
|
72
|
+
* operator WHICH build is answering — and the fact to quote when a query behaves differently across
|
|
73
|
+
* two hosts. A refused runtime is reported as such, so this line and the later DEGRADED mount agree.
|
|
74
|
+
*/
|
|
75
|
+
export declare function describeSqlite(): string;
|
|
76
|
+
/**
|
|
77
|
+
* Open (or create) a SQLite database behind the port. `':memory:'` is a valid path.
|
|
78
|
+
*
|
|
79
|
+
* The runtime's verdict is checked HERE, not only by the startup log: a runtime whose builtin cannot
|
|
80
|
+
* serve a store (no FTS5, or no tolerance for unused named parameters — see {@link sqliteProbe}) must
|
|
81
|
+
* fail this open, so the caller's own error handling degrades the plugin with that reason instead of
|
|
82
|
+
* the engine discovering it half-way through a schema migration or a lifecycle tick.
|
|
83
|
+
*
|
|
84
|
+
* `verdict` is a TEST seam, and deliberately not a driver seam: there is one implementation and it
|
|
85
|
+
* cannot be replaced at runtime. It exists so the refusal path can be asserted on a machine whose
|
|
86
|
+
* builtin missions — the path nobody sees until a user hits it.
|
|
87
|
+
*/
|
|
88
|
+
export declare function openSqlite(path: string, verdict?: () => string | undefined): Db;
|
|
89
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Db } from './port.js';
|
|
2
|
+
import { type Migration, type MigrationResult } from './migrations.js';
|
|
3
|
+
import type { FtsTokenizer } from './tokenizer.js';
|
|
4
|
+
/** One store's schema: only the migration steps and a label for diagnostics. */
|
|
5
|
+
export interface StoreSchema {
|
|
6
|
+
readonly describe: string;
|
|
7
|
+
readonly migrations: readonly Migration[];
|
|
8
|
+
}
|
|
9
|
+
export interface OpenedStore {
|
|
10
|
+
readonly db: Db;
|
|
11
|
+
/** Which steps ran on THIS open (`from`/`to` make a stale store obvious in a log line). */
|
|
12
|
+
readonly migration: MigrationResult;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* One read-only diagnostic for the startup log, built entirely from the open's already-returned
|
|
16
|
+
* result — it must never touch the database, block, or throw.
|
|
17
|
+
*
|
|
18
|
+
* It is defined for both states (`schema up to date (9)` and `schema upgraded 8 → 9 (applied: 9
|
|
19
|
+
* contradiction-resolved-indexes)`), but callers are expected to EMIT only the upgraded form: an
|
|
20
|
+
* up-to-date store is the normal case and does not deserve a line on every boot. The helper exists
|
|
21
|
+
* so that state is still assertable and so both stores phrase it identically.
|
|
22
|
+
*/
|
|
23
|
+
export declare function describeMigrationOutcome(schema: StoreSchema, migration: MigrationResult): string;
|
|
24
|
+
/** Whether this open actually applied anything (`false` = the store was already current). */
|
|
25
|
+
export declare function wasUpgraded(migration: MigrationResult): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Open (or create) a store database: directory, PRAGMAs, then every migration newer than
|
|
28
|
+
* the file's `user_version`. A failure closes the handle — an open handle keeps the WAL and
|
|
29
|
+
* its locks alive, and a half-open store would be worse than none.
|
|
30
|
+
*/
|
|
31
|
+
export declare function openStoreDb(opts: {
|
|
32
|
+
path: string;
|
|
33
|
+
schema: StoreSchema;
|
|
34
|
+
tokenizer: FtsTokenizer;
|
|
35
|
+
}): OpenedStore;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Db } from './port.js';
|
|
2
|
+
/** FTS5 tokenizer actually used to build the `*_fts` tables. */
|
|
3
|
+
export type FtsTokenizer = 'trigram' | 'unicode61';
|
|
4
|
+
/** Whether this SQLite build supports the CJK-aware FTS5 `trigram` tokenizer. */
|
|
5
|
+
export declare function trigramAvailable(): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Choose the tokenizer for newly created FTS tables — DESIGN §16 mitigation.
|
|
8
|
+
*
|
|
9
|
+
* A build without `trigram` must not make the store unopenable: fall back to
|
|
10
|
+
* `unicode61` (CJK recall degrades to exact/whole-token matching) and say so once.
|
|
11
|
+
* This has to run BEFORE the schema DDL, because `CREATE VIRTUAL TABLE … TOKENIZE
|
|
12
|
+
* trigram` on such a build throws.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveFtsTokenizer(): FtsTokenizer;
|
|
15
|
+
/**
|
|
16
|
+
* The tokenizer an EXISTING FTS table was actually built with, or `null` if there is no such table.
|
|
17
|
+
*
|
|
18
|
+
* Read from `sqlite_master` — not from a stored flag, and not from this build's probe. `CREATE
|
|
19
|
+
* VIRTUAL TABLE IF NOT EXISTS` never rebuilds an existing table, so what the index contains was
|
|
20
|
+
* decided by the DDL that ran when the database was first created: possibly years ago, possibly on
|
|
21
|
+
* another machine, possibly against a SQLite build with different FTS5 support. Querying it with
|
|
22
|
+
* the OTHER convention is silent zero recall — a trigram table cannot match a whole-token phrase and
|
|
23
|
+
* a unicode61 table cannot match a 3-gram, and neither raises an error. The store therefore asks the
|
|
24
|
+
* database which tokenizer it has, and builds MATCH expressions for THAT one.
|
|
25
|
+
*/
|
|
26
|
+
export declare function detectFtsTokenizer(db: Db, table: string): FtsTokenizer | null;
|
|
27
|
+
/**
|
|
28
|
+
* Say something when a database's FTS table is WEAKER than what this SQLite build could give it.
|
|
29
|
+
*
|
|
30
|
+
* Only that direction is worth a line. The reverse (a trigram table opened by a build without
|
|
31
|
+
* trigram support) loses nothing: the queries are built for the table that exists, so recall is
|
|
32
|
+
* exactly what it was. But a unicode61 table opened by a trigram-capable build means CJK full-text
|
|
33
|
+
* recall is limited to whole-token matches — a real, invisible degradation that an operator can fix,
|
|
34
|
+
* so it is reported once with the remedy named.
|
|
35
|
+
*
|
|
36
|
+
* @param label store label for the message
|
|
37
|
+
* @param table the FTS table whose DDL was read
|
|
38
|
+
* @param detected what {@link detectFtsTokenizer} found (`null` = the table does not exist yet)
|
|
39
|
+
* @param remedy how to rebuild that table, or `null` when no operator-facing command does
|
|
40
|
+
*/
|
|
41
|
+
export declare function reportFtsTokenizerDrift(label: string, table: string, detected: FtsTokenizer | null, remedy: string | null): void;
|
|
42
|
+
/**
|
|
43
|
+
* The most OR phrases one query may expand to.
|
|
44
|
+
*
|
|
45
|
+
* 64 is far past any query a person types (a 64-character CJK run), and it is the ceiling that keeps a
|
|
46
|
+
* pathological input from turning `MATCH` into minutes of synchronous mission inside the host process.
|
|
47
|
+
*/
|
|
48
|
+
export declare const MAX_FTS_PHRASES = 64;
|
|
49
|
+
/**
|
|
50
|
+
* Build an FTS5 MATCH expression for the tokenizer the tables were created with.
|
|
51
|
+
*
|
|
52
|
+
* - `trigram`: CJK tokens become OR-joined 3-grams (a phrase needs ≥3 chars);
|
|
53
|
+
* latin tokens are quoted whole (≥3 chars — the trigram minimum).
|
|
54
|
+
* - `unicode61` (fallback build): whole-token phrases; CJK is NOT n-grammed
|
|
55
|
+
* (trigram phrases can never match a unicode61 table, so emitting them there
|
|
56
|
+
* would mean silent zero recall — the old duplicated builders did exactly that).
|
|
57
|
+
*
|
|
58
|
+
* One implementation for memory + knowledge (the two copies had already drifted
|
|
59
|
+
* once when the tokenizer fallback landed).
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildFtsQuery(query: string, tokenizer?: FtsTokenizer): string | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { VectorStore } from '../../mem-retrieval/index.js';
|
|
2
|
+
/** Little-endian Float32 ↔ BLOB helpers for persisted vectors (cross-platform stable). */
|
|
3
|
+
export declare function float32ToBytes(vec: Float32Array): Buffer;
|
|
4
|
+
export declare function bytesToFloat32(buf: Uint8Array | null | undefined): Float32Array | null;
|
|
5
|
+
/**
|
|
6
|
+
* Identity of a VECTOR SPACE: which backend, which model, how wide.
|
|
7
|
+
*
|
|
8
|
+
* Persisted beside every vector (`facts.embedding_model`, `doc_chunks.embedding_model`) so
|
|
9
|
+
* "these vectors came from a different model" is detectable. It used to record only the
|
|
10
|
+
* backend name, which made a same-width model swap (bge-small-zh → bge-m3, both 512d)
|
|
11
|
+
* indistinguishable from "already encoded": the two spaces then shared one index and were
|
|
12
|
+
* ranked against each other.
|
|
13
|
+
*
|
|
14
|
+
* The vector STORE is deliberately not part of the identity: numpy vs hnswlib indexes the
|
|
15
|
+
* same vectors, so switching it must not trigger re-encoding (that is `reloadIndex`).
|
|
16
|
+
*/
|
|
17
|
+
export declare function vectorSpaceId(backend: string, model: string, dim: number): string;
|
|
18
|
+
/**
|
|
19
|
+
* Rebuild a live vstore from persisted vector BLOBs — the restart-recovery path
|
|
20
|
+
* shared by both stores (the two copies had already drifted: memory warned on
|
|
21
|
+
* dim mismatches, knowledge dropped them silently).
|
|
22
|
+
*
|
|
23
|
+
* The rows come from the owning store's DAO (DESIGN §19): this helper decodes and
|
|
24
|
+
* filters them, it never runs SQL itself.
|
|
25
|
+
*
|
|
26
|
+
* @param rows - `id` (fact/chunk id) + `vec` (BLOB), as read back from the store.
|
|
27
|
+
* @param label - store name used in the dim-mismatch warning.
|
|
28
|
+
*/
|
|
29
|
+
export declare function reloadVectorIndex(vstore: VectorStore, rows: readonly {
|
|
30
|
+
id: number;
|
|
31
|
+
vec: Uint8Array | null;
|
|
32
|
+
}[], label: string): {
|
|
33
|
+
loaded: number;
|
|
34
|
+
skipped: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* On-disk ANN snapshot PREFIX for a store's vector index (the adapter appends
|
|
38
|
+
* `.<fingerprint>.hnsw`), or `null` when the database has no file to sit beside.
|
|
39
|
+
*
|
|
40
|
+
* The VECTOR SPACE is part of the name, not just of the rows: a snapshot is a serialized graph, so
|
|
41
|
+
* loading one built from another model (or width) would silently serve vectors that describe
|
|
42
|
+
* different text. Embedding the space means a model swap looks for a different file, and the
|
|
43
|
+
* adapter's fingerprint covers everything else (see `HnswlibVectorStore.snapshotPath`).
|
|
44
|
+
*
|
|
45
|
+
* The space is `encodeURIComponent`-escaped rather than sanitized: lossy replacement mapped `a/b`
|
|
46
|
+
* and `a_b` onto the SAME name, which is exactly the collision this path exists to prevent.
|
|
47
|
+
* `:memory:` (or any non-file path) returns `null` — otherwise the snapshot is written into the
|
|
48
|
+
* process working directory under a `:memory:.…` name.
|
|
49
|
+
*/
|
|
50
|
+
export declare function vectorCachePath(dbPath: string, space: string): string | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The model-facing tool dispatch, shared by EVERY surface that answers a contract tool key: the DSH
|
|
3
|
+
* plugin's tool runner, the plugin's Remote gateway (the operator UI) and the MCP server.
|
|
4
|
+
*
|
|
5
|
+
* It lives here rather than in each adapter because the key set is owned by the contract's
|
|
6
|
+
* `TOOL_SPECS`, and an adapter that hand-writes its own switch rots silently when a spec is added.
|
|
7
|
+
* That is a measured failure, not a hypothetical: `kb_add`/`kb_list`/`kb_remove`/`kb_reindex` were
|
|
8
|
+
* advertised by the MCP server's `tools/list` while its dispatcher still knew only the five older
|
|
9
|
+
* keys, so all four answered `unknown tool key` — a tool that is listed, callable, and always
|
|
10
|
+
* broken. One table keyed by the contract, plus {@link supportsToolKey} so a spec test can assert
|
|
11
|
+
* "every advertised key dispatches" instead of trusting a hand-maintained list.
|
|
12
|
+
*
|
|
13
|
+
* `ToolSpec.key` is typed `string` (not a literal union), so exhaustiveness cannot be checked at
|
|
14
|
+
* compile time — the spec assertion is the gate.
|
|
15
|
+
*/
|
|
16
|
+
import type { KbAddRequest } from '../mem-contract/index.js';
|
|
17
|
+
import type { AvantfRuntime } from './runtime.js';
|
|
18
|
+
/**
|
|
19
|
+
* The default page for a model-facing `kb_list`.
|
|
20
|
+
*
|
|
21
|
+
* Without one, `kb_list` on a large library read and parsed EVERY managed file (resolving a path
|
|
22
|
+
* needs the file's frontmatter to detect collisions) to answer a question nobody asked in full.
|
|
23
|
+
*/
|
|
24
|
+
export declare const KB_LIST_DEFAULT_LIMIT = 50;
|
|
25
|
+
/**
|
|
26
|
+
* The `kb_add` handler: a thin shell over the runtime's ADD-ONLY face (`rt.kbAdd`), which shares the
|
|
27
|
+
* store's plan/classify/dispatch entry with the UI's 入库 (`rt.kb`) and differs only in the mode.
|
|
28
|
+
*
|
|
29
|
+
* This is the fix for a measured failure: told to "update the KB", a model called the old all-in-one
|
|
30
|
+
* tool with a *new* title, which silently created a sibling document (《… · 补遗》) while the
|
|
31
|
+
* original stayed behind — two overlapping documents, and the reader left to reconcile them.
|
|
32
|
+
* Replacing is now an explicit file edit.
|
|
33
|
+
*
|
|
34
|
+
* The refusal itself lives in the STORE (the plan stage), not here: a pre-check in this handler used
|
|
35
|
+
* to derive the title as `req.title ?? req.source`, which is exactly the fallback the store no
|
|
36
|
+
* longer uses — so once the store derives an untitled paste from its body, a local pre-check would
|
|
37
|
+
* check a different identity than the one the store actually writes.
|
|
38
|
+
*/
|
|
39
|
+
export declare function kbAdd(rt: AvantfRuntime, data: KbAddRequest): Promise<unknown>;
|
|
40
|
+
/** Whether {@link dispatchToolKey} can answer this contract tool key. */
|
|
41
|
+
export declare function supportsToolKey(key: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Run one already-validated tool call.
|
|
44
|
+
*
|
|
45
|
+
* The caller owns validation (each surface parses with its own `ToolSpec.input` so the error
|
|
46
|
+
* envelope matches its protocol) and owns the model-facing shaping
|
|
47
|
+
* ({@link modelFacingToolResult}); this is the raw runtime call in between. An unknown key THROWS
|
|
48
|
+
* rather than returning a payload, so a surface cannot mistake it for a successful empty result —
|
|
49
|
+
* both adapters route the throw through their structured error envelope.
|
|
50
|
+
*/
|
|
51
|
+
export declare function dispatchToolKey(rt: AvantfRuntime, key: string, args: Record<string, unknown>): Promise<unknown>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity + SPO triples extraction. Uses nodejieba (optional dependency) for POS;
|
|
3
|
+
* degrades gracefully to a character/regex fallback when unavailable, exactly like
|
|
4
|
+
* the `jieba_available()` guard.
|
|
5
|
+
*
|
|
6
|
+
* Note: nodejieba's `tag()` returns `{ word, tag }` objects, and its POS flag set
|
|
7
|
+
* differs from Python jieba (e.g. proper nouns → `nr`, 名动词 → `vn`).
|
|
8
|
+
*/
|
|
9
|
+
interface ExtractedEntity {
|
|
10
|
+
name: string;
|
|
11
|
+
type: string;
|
|
12
|
+
method: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ExtractedTriple {
|
|
15
|
+
subj: string;
|
|
16
|
+
pred: string;
|
|
17
|
+
obj: string;
|
|
18
|
+
confidence: number;
|
|
19
|
+
source: string;
|
|
20
|
+
}
|
|
21
|
+
type PosToken = {
|
|
22
|
+
word: string;
|
|
23
|
+
tag: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Attempts made so far — the mechanism, not the wall time.
|
|
27
|
+
*
|
|
28
|
+
* A wall-clock assertion would be machine-dependent, but "how many parses ran" is exact and is
|
|
29
|
+
* what {@link loadJieba}'s promise-level memo exists to keep at one. The case that matters is
|
|
30
|
+
* CONCURRENCY: a result-level memo looks correct to any sequential caller and only shows up when
|
|
31
|
+
* two of them overlap.
|
|
32
|
+
*/
|
|
33
|
+
export declare function jiebaLoadAttempts(): number;
|
|
34
|
+
export declare function jiebaAvailable(): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Version of the extraction RULES below (accepted tags, stop words, triple patterns).
|
|
37
|
+
*
|
|
38
|
+
* Persisted per chunk so a reindex can skip rows whose text is unchanged while still
|
|
39
|
+
* rebuilding the ones produced by an older rule set. Bump this whenever a change here would
|
|
40
|
+
* yield different entities/triples for the same text — it is the only thing that separates
|
|
41
|
+
* "already extracted" from "extracted by the previous version" (DESIGN §20).
|
|
42
|
+
*/
|
|
43
|
+
export declare const ENTITY_EXTRACTOR_VERSION = 1;
|
|
44
|
+
export interface TriplePattern {
|
|
45
|
+
subj?: string;
|
|
46
|
+
pred?: string;
|
|
47
|
+
obj?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* POS-tag `text` ONCE, or `null` when nodejieba is unavailable (callers fall back to regex).
|
|
51
|
+
*
|
|
52
|
+
* Exists so one write can drive BOTH extractors from a single `jieba.tag()` call: `add`/`update`
|
|
53
|
+
* used to tag the same content twice (once for entities, once for triples), which is pure repeated
|
|
54
|
+
* mission — measured ~1.1 ms per 1000 characters per pass.
|
|
55
|
+
*/
|
|
56
|
+
export declare function tagText(text: string): Promise<PosToken[] | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Entities from ALREADY-TAGGED tokens (`null` = tagger unavailable → regex fallback on `text`).
|
|
59
|
+
*
|
|
60
|
+
* The token array is passed in rather than re-derived, so a caller that needs entities AND triples
|
|
61
|
+
* pays for one tagging pass (see `tagText`).
|
|
62
|
+
*/
|
|
63
|
+
export declare function entitiesFromTokens(tokens: PosToken[] | null, text: string): ExtractedEntity[];
|
|
64
|
+
/** Extract entity names using nodejieba POS when available, else a regex fallback. */
|
|
65
|
+
export declare function extractEntities(text: string): Promise<ExtractedEntity[]>;
|
|
66
|
+
/**
|
|
67
|
+
* SPO triples from ALREADY-TAGGED tokens (`null` = tagger unavailable → no triples, as before).
|
|
68
|
+
*
|
|
69
|
+
* `pos` is exported alongside `entitiesFromTokens` so the write path can tag once and drive both.
|
|
70
|
+
*/
|
|
71
|
+
export declare function triplesFromTokens(rawTokens: PosToken[] | null): ExtractedTriple[];
|
|
72
|
+
/** Extract a few SPO triples via a simplified SVO heuristic. Returns [] when jieba unavailable. */
|
|
73
|
+
export declare function extractTriples(text: string): Promise<ExtractedTriple[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Parse a natural-language question into a direction-aware TriplePattern where
|
|
76
|
+
* interrogative slots are wildcards (`undefined`).
|
|
77
|
+
* `parse_query_pattern`: predicates anchor to the interrogative, and no literal
|
|
78
|
+
* interrogative word fills a slot.
|
|
79
|
+
*
|
|
80
|
+
* 「李娜管理谁」 → { subj: '李娜', pred: '管理' }
|
|
81
|
+
* 「谁管理李娜」 → { pred: '管理', obj: '李娜' }
|
|
82
|
+
*/
|
|
83
|
+
export declare function parseQueryPattern(query: string): Promise<TriplePattern>;
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface EvalQuery {
|
|
2
|
+
query: string;
|
|
3
|
+
k: number;
|
|
4
|
+
expected_ids: number[];
|
|
5
|
+
must_include: number[];
|
|
6
|
+
must_exclude: number[];
|
|
7
|
+
}
|
|
8
|
+
export interface EvalCase {
|
|
9
|
+
id: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
setup_facts: string[];
|
|
12
|
+
queries: EvalQuery[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse the 29-query eval fixture. Each line is one case with
|
|
16
|
+
* `setup_facts` (ordered) and `queries` whose id lists are indices into setup_facts.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadEvalCases(path: string): EvalCase[];
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Retrieval-quality metrics (pure functions, backend-agnostic). */
|
|
2
|
+
export declare function precisionAtK(actual: number[], expected: number[], k: number): number;
|
|
3
|
+
export declare function recallAtK(actual: number[], expected: number[], k: number): number;
|
|
4
|
+
export declare function reciprocalRank(actual: number[], expected: number[]): number;
|
|
5
|
+
export interface PerQueryMetrics {
|
|
6
|
+
query: string;
|
|
7
|
+
k: number;
|
|
8
|
+
precision_at_k: number;
|
|
9
|
+
recall_at_k: number;
|
|
10
|
+
reciprocal_rank: number;
|
|
11
|
+
actual_ids: number[];
|
|
12
|
+
expected_ids: number[];
|
|
13
|
+
must_include_satisfied: boolean;
|
|
14
|
+
must_exclude_satisfied: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface AggregateMetrics {
|
|
17
|
+
n_queries: number;
|
|
18
|
+
mean_precision_at_k: number;
|
|
19
|
+
mean_recall_at_k: number;
|
|
20
|
+
mrr: number;
|
|
21
|
+
empty_rate: number;
|
|
22
|
+
must_include_pass_rate: number;
|
|
23
|
+
must_exclude_pass_rate: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function aggregate(perQuery: PerQueryMetrics[]): AggregateMetrics;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type PerQueryMetrics, type AggregateMetrics } from './metrics.js';
|
|
2
|
+
import type { EvalCase } from './loader.js';
|
|
3
|
+
/** A retrieve function over the case's facts, returning indices in relevance order. */
|
|
4
|
+
type RetrieveFn = (query: string, k: number, facts: string[]) => Promise<number[]>;
|
|
5
|
+
interface EvalReport {
|
|
6
|
+
perQuery: PerQueryMetrics[];
|
|
7
|
+
summary: AggregateMetrics;
|
|
8
|
+
}
|
|
9
|
+
export declare function evaluateCases(cases: EvalCase[], retrieve: RetrieveFn): Promise<EvalReport>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-side evaluation: the deterministic half of the memory lifecycle (DESIGN §20).
|
|
3
|
+
*
|
|
4
|
+
* The retrieval suite grades what comes OUT of the store; nothing graded what goes IN. Write
|
|
5
|
+
* behavior is where the invariants live (exact dedupe, one revision per supersede step,
|
|
6
|
+
* conflicts logged and retired, archive/restore round trips), and unlike answer quality it
|
|
7
|
+
* needs no model and no judge.
|
|
8
|
+
*
|
|
9
|
+
* TWO metrics, deliberately orthogonal (the reference design's split, and the reason it works):
|
|
10
|
+
*
|
|
11
|
+
* - `action_success` — the STRUCTURAL operation the case exists for happened (a revision was
|
|
12
|
+
* archived, a pair was opened, a pair was closed). Counts only; it never looks at content.
|
|
13
|
+
* - `information_integrity` — every statement survives EXACTLY ONCE (live, or archived when the
|
|
14
|
+
* case says it was retired). Content only; it never looks at counts.
|
|
15
|
+
*
|
|
16
|
+
* Keeping them apart is the point: an update that archives the old revision but loses a fact it
|
|
17
|
+
* carried passes the action metric and fails integrity, and a single blended score would hide
|
|
18
|
+
* which half broke. Both are pure functions over an observation, so the grader is deterministic
|
|
19
|
+
* and needs no live store — only the scenarios that produce the observations do.
|
|
20
|
+
*
|
|
21
|
+
* The identity token is supplied by the observation, NOT parsed out of the fact text. Carrying
|
|
22
|
+
* it in the text looks natural and is a trap: the token ends up inside the subject/predicate/
|
|
23
|
+
* object the detector parses, and the case silently stops exercising the signal it exists for
|
|
24
|
+
* (measured: an in-text token suppressed the polarity conflict in 4 of 5 placements). A label
|
|
25
|
+
* the store never interprets — `category` — is stable across extractor changes.
|
|
26
|
+
*/
|
|
27
|
+
/** What a case is about. `expect_action` is structural only; `live` is content only. */
|
|
28
|
+
export interface WriteCase {
|
|
29
|
+
id: string;
|
|
30
|
+
/** Identity tokens the LIVE corpus must carry exactly once. */
|
|
31
|
+
live: string[];
|
|
32
|
+
/** Tokens that must no longer be live (their statements were superseded/retracted). */
|
|
33
|
+
retired?: string[];
|
|
34
|
+
/** Structural claims about the state after the scenario. */
|
|
35
|
+
expect_action?: {
|
|
36
|
+
/** Rows in the supersede chain of the fact the scenario follows. */
|
|
37
|
+
revision_chain?: number;
|
|
38
|
+
/**
|
|
39
|
+
* OPEN conflicts naming the facts THIS case names (`live` + `retired`) — never the whole
|
|
40
|
+
* store: the scenarios share one, so a store-wide count would make a case's verdict depend
|
|
41
|
+
* on which cases ran before it. That is not hypothetical — a store-wide
|
|
42
|
+
* `archived_reason_count` held only because its case ran first.
|
|
43
|
+
*/
|
|
44
|
+
open_conflicts?: number;
|
|
45
|
+
/** Facts whose archive reason is this value. Omit together with the count to assert "any". */
|
|
46
|
+
archived_reason?: string;
|
|
47
|
+
/**
|
|
48
|
+
* How many archived facts carry `archived_reason` (default 1), or — when `archived_reason`
|
|
49
|
+
* is omitted — how many carry ANY reason. The standalone form is what "nothing was
|
|
50
|
+
* archived" needs, and it must actually be checked: gating the count on `archived_reason`
|
|
51
|
+
* made the standalone form a silent no-op.
|
|
52
|
+
*
|
|
53
|
+
* Counted over the facts THIS CASE names (`live` + `retired`), never over the whole store:
|
|
54
|
+
* the scenarios share one store, so a store-wide count would make a case's result depend on
|
|
55
|
+
* which cases ran before it.
|
|
56
|
+
*/
|
|
57
|
+
archived_reason_count?: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** What the store actually looks like after the scenario ran. */
|
|
61
|
+
export interface WriteObservation {
|
|
62
|
+
/** Identity tokens seen in ACTIVE facts, with their occurrence count. */
|
|
63
|
+
live: Map<string, number>;
|
|
64
|
+
/** Identity tokens seen in ARCHIVED facts. */
|
|
65
|
+
archived: Set<string>;
|
|
66
|
+
/** Archive reason per fact, tagged with the fact's identity token (see `WriteCase.live`). */
|
|
67
|
+
archive_reasons: {
|
|
68
|
+
token: string;
|
|
69
|
+
reason: string;
|
|
70
|
+
}[];
|
|
71
|
+
open_conflicts: number;
|
|
72
|
+
/** Length of the supersede chain rooted at the scenario's fact, when the case asks. */
|
|
73
|
+
revision_chain?: number;
|
|
74
|
+
}
|
|
75
|
+
interface WriteCaseResult {
|
|
76
|
+
id: string;
|
|
77
|
+
action_success: boolean;
|
|
78
|
+
information_integrity: boolean;
|
|
79
|
+
/** Human-readable explanation, so a failure says WHICH half broke and how. */
|
|
80
|
+
detail: string;
|
|
81
|
+
}
|
|
82
|
+
export interface WriteReport {
|
|
83
|
+
cases: WriteCaseResult[];
|
|
84
|
+
action_success_rate: number;
|
|
85
|
+
information_integrity_rate: number;
|
|
86
|
+
}
|
|
87
|
+
export declare function gradeWriteCases(cases: WriteCase[], observations: Map<string, WriteObservation>): WriteReport;
|
|
88
|
+
export {};
|