@oortmem/core 0.5.1 → 0.5.2
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/dist/adapters.d.ts +37 -0
- package/dist/config.d.ts +11 -1
- package/dist/config.js +24 -1
- package/dist/display.d.ts +9 -0
- package/dist/display.js +10 -0
- package/dist/export-format.d.ts +4 -1
- package/dist/export-format.js +7 -2
- package/dist/fusion.d.ts +2 -0
- package/dist/fusion.js +3 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/judgment.d.ts +526 -0
- package/dist/judgment.js +1357 -0
- package/dist/model.d.ts +14 -1
- package/dist/observe.js +3 -1
- package/dist/recall.d.ts +2 -0
- package/dist/recall.js +2 -0
- package/dist/store-conformance.js +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import type { DecisionController, Embedder, MemoryStore } from './adapters.js';
|
|
2
|
+
import { type Grant } from './grant.js';
|
|
3
|
+
import { type Decision, type Memory, type Scope } from './model.js';
|
|
4
|
+
/**
|
|
5
|
+
* Judgment memory (CON-71): an expert's approve or reject decisions, with their reasons in their
|
|
6
|
+
* own words, kept so an agent can apply them to a new item, say why, and say when it is not sure.
|
|
7
|
+
*
|
|
8
|
+
* A judgment is a `judgment` memory whose `judgment` field holds the decision. It is written only
|
|
9
|
+
* through the judgment functions here (never inferred from a conversation), its reason is never
|
|
10
|
+
* paraphrased (the write path skips judgments), and it is pinned so it never loses its embedding
|
|
11
|
+
* to cold.
|
|
12
|
+
*
|
|
13
|
+
* Who recorded it matters. A judgment recorded by a person (the CLI, a bulk import) counts when
|
|
14
|
+
* judging. One recorded by an agent over MCP (`via: 'mcp'`) is agent-recorded and unconfirmed: it
|
|
15
|
+
* never corrects a person's judgment, and judging leaves it out until a person confirms it
|
|
16
|
+
* (`confirmJudgment`, CLI only) or the caller asks for agent-recorded judgments explicitly. One
|
|
17
|
+
* imported from a pack names an expert only as the pack states it, so judging leaves it out unless
|
|
18
|
+
* asked.
|
|
19
|
+
*/
|
|
20
|
+
/** The verdict set a judgment uses when none is given. */
|
|
21
|
+
export declare const DEFAULT_VERDICTS: readonly string[];
|
|
22
|
+
export declare const JUDGMENT_LIMITS: Readonly<{
|
|
23
|
+
summary: 500;
|
|
24
|
+
ref: 2000;
|
|
25
|
+
reason: 2000;
|
|
26
|
+
details: 4000;
|
|
27
|
+
criteria: 12;
|
|
28
|
+
criterion: 64;
|
|
29
|
+
verdicts: 12;
|
|
30
|
+
by: 128;
|
|
31
|
+
/** Rows one bulk import or pack may hold; a larger one is refused before anything is written. */
|
|
32
|
+
importRows: 5000;
|
|
33
|
+
}>;
|
|
34
|
+
/** What was judged: a summary, and where the item itself lives. */
|
|
35
|
+
export interface JudgmentSubject {
|
|
36
|
+
summary: string;
|
|
37
|
+
/** A link or id of the item (a URL, a ticket, a file path). */
|
|
38
|
+
ref?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Where a judgment came from, besides the expert who made it. `mcp` means an agent recorded it:
|
|
42
|
+
* agent-recorded, unconfirmed until a person confirms it.
|
|
43
|
+
*/
|
|
44
|
+
export type JudgmentVia = 'cli' | 'mcp' | 'import' | 'pack' | 'sdk';
|
|
45
|
+
/**
|
|
46
|
+
* A judgment that arrived in a pack: the pack it was imported from here (its digest id and the
|
|
47
|
+
* manifest's name, exporter and time, all bound by the digest) and the id the line carried. Every
|
|
48
|
+
* field comes from the pack this store imported, never from what a line claims about itself.
|
|
49
|
+
*/
|
|
50
|
+
export interface JudgmentPackOrigin {
|
|
51
|
+
/** The pack's digest id (the first 16 hex characters of `packDigest`). */
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
exportedAt: string;
|
|
55
|
+
/** The agent that exported it. */
|
|
56
|
+
exportedBy: string;
|
|
57
|
+
/** The id the judgment had in the pack (its id in the exporting store). */
|
|
58
|
+
originalId: string;
|
|
59
|
+
importedAt: string;
|
|
60
|
+
/**
|
|
61
|
+
* What the line said about an earlier pack it came from, when the exporter had imported it
|
|
62
|
+
* itself. Untrusted: the exporter could have written anything here, so nothing uses it for
|
|
63
|
+
* deduplication, attribution or judging; it is kept only to be shown as a claim.
|
|
64
|
+
*/
|
|
65
|
+
claimedEarlier?: ClaimedPackOrigin;
|
|
66
|
+
}
|
|
67
|
+
/** An earlier pack origin as a pack line claims it. Checked for shape only; never trusted. */
|
|
68
|
+
export interface ClaimedPackOrigin {
|
|
69
|
+
untrusted: true;
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
exportedAt: string;
|
|
73
|
+
exportedBy: string;
|
|
74
|
+
originalId: string;
|
|
75
|
+
}
|
|
76
|
+
/** The decision itself. Everything an expert said is kept as they said it (secrets redacted). */
|
|
77
|
+
export interface Judgment {
|
|
78
|
+
subject: JudgmentSubject;
|
|
79
|
+
/** One of `verdicts`. */
|
|
80
|
+
verdict: string;
|
|
81
|
+
/** The defined set the verdict comes from; `approved` and `rejected` by default. */
|
|
82
|
+
verdicts: string[];
|
|
83
|
+
/** The expert's own words. */
|
|
84
|
+
reason: string;
|
|
85
|
+
/** Tags the reason relies on (`no-source`, `pii`). */
|
|
86
|
+
criteria: string[];
|
|
87
|
+
domain: string;
|
|
88
|
+
/** The expert's id. */
|
|
89
|
+
by: string;
|
|
90
|
+
decidedAt: string;
|
|
91
|
+
/** How it was recorded. The agent that recorded it is the memory's author. */
|
|
92
|
+
via: JudgmentVia;
|
|
93
|
+
/** Set when it was withdrawn (and by which operator): it no longer counts anywhere. */
|
|
94
|
+
withdrawn?: {
|
|
95
|
+
at: string;
|
|
96
|
+
by: string;
|
|
97
|
+
};
|
|
98
|
+
/** Set on an agent-recorded (`mcp`) judgment a person confirmed: it then counts when judging. */
|
|
99
|
+
confirmed?: {
|
|
100
|
+
at: string;
|
|
101
|
+
by: string;
|
|
102
|
+
};
|
|
103
|
+
/** Set when its text was purged: only an audit stub (expert, domain, verdict, times) is left. */
|
|
104
|
+
purged?: {
|
|
105
|
+
at: string;
|
|
106
|
+
by: string;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Set when it was withdrawn, corrected or purged: `tombstoneKey` of the decision as it was, so no
|
|
110
|
+
* import brings the same decision back (it leaves out `decidedAt`, which a pack could shift).
|
|
111
|
+
*/
|
|
112
|
+
tombstone?: string;
|
|
113
|
+
/** Set on a judgment imported from a pack. */
|
|
114
|
+
pack?: JudgmentPackOrigin;
|
|
115
|
+
}
|
|
116
|
+
/** A judgment's decision fields, as `normaliseJudgment` returns them. */
|
|
117
|
+
export type JudgmentCore = Omit<Judgment, 'via' | 'pack' | 'withdrawn' | 'confirmed' | 'purged' | 'tombstone'>;
|
|
118
|
+
/** A judgment as a caller describes it, before checking and normalising. */
|
|
119
|
+
export interface JudgmentInput {
|
|
120
|
+
subject: string;
|
|
121
|
+
ref?: string;
|
|
122
|
+
verdict: string;
|
|
123
|
+
verdicts?: readonly string[];
|
|
124
|
+
reason: string;
|
|
125
|
+
criteria?: readonly string[];
|
|
126
|
+
domain: string;
|
|
127
|
+
by: string;
|
|
128
|
+
/** ISO time; defaults to now. */
|
|
129
|
+
decidedAt?: string;
|
|
130
|
+
}
|
|
131
|
+
/** A judgment input that cannot be recorded; the message names the field, never its value. */
|
|
132
|
+
export declare class JudgmentInputError extends Error {
|
|
133
|
+
readonly name = "JudgmentInputError";
|
|
134
|
+
}
|
|
135
|
+
/** A verdict or criterion label: lowercase, spaces as `-`. */
|
|
136
|
+
export declare function judgmentLabel(field: string, v: unknown, re?: RegExp): string;
|
|
137
|
+
/** Checks and normalises `input` (labels lowercased, text trimmed). Throws `JudgmentInputError`. */
|
|
138
|
+
export declare function normaliseJudgment(input: JudgmentInput, now: Date): JudgmentCore;
|
|
139
|
+
/**
|
|
140
|
+
* An expert id as agent-facing text may show it: letters, digits and `. _ @ + -` kept, anything
|
|
141
|
+
* else `_`, at most 64 characters. Ids are checked on the way in; this is the second line.
|
|
142
|
+
*/
|
|
143
|
+
export declare function safeExpertId(by: string): string;
|
|
144
|
+
/** The memory text recall shows for a judgment: who decided what, and why, in their words. */
|
|
145
|
+
export declare function judgmentText(j: Judgment): string;
|
|
146
|
+
/** True for a `judgment` memory that still counts: current, and not withdrawn. */
|
|
147
|
+
export declare function isActiveJudgment(m: Memory, now: Date): m is Memory & {
|
|
148
|
+
judgment: Judgment;
|
|
149
|
+
};
|
|
150
|
+
/** Recorded by an agent over MCP and not confirmed by a person. */
|
|
151
|
+
export declare function isUnconfirmedAgentJudgment(j: Pick<Judgment, 'via' | 'confirmed'>): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* The key a withdrawn, corrected or purged decision leaves behind: expert, domain, folded subject,
|
|
154
|
+
* folded reason and verdict, hashed. It leaves out `decidedAt` (a pack could shift it) and holds no
|
|
155
|
+
* text (a purged judgment keeps it).
|
|
156
|
+
*/
|
|
157
|
+
export declare function tombstoneKey(j: Pick<Judgment, 'by' | 'domain' | 'subject' | 'reason' | 'verdict'>): string;
|
|
158
|
+
export interface RecordJudgmentDeps {
|
|
159
|
+
store: MemoryStore;
|
|
160
|
+
/** Where it is written: the grant's home node, unbound to any project. */
|
|
161
|
+
grant: Grant;
|
|
162
|
+
/** The acting principal's scope (its `agentId` is the author). */
|
|
163
|
+
scope: Scope;
|
|
164
|
+
/** Embeds the judgment for dense judging; without it judging is lexical. */
|
|
165
|
+
embed?: Embedder;
|
|
166
|
+
now?: Date;
|
|
167
|
+
/** The harness or tool that recorded it, for `source`. */
|
|
168
|
+
harness?: string;
|
|
169
|
+
}
|
|
170
|
+
export interface RecordJudgmentResult {
|
|
171
|
+
/**
|
|
172
|
+
* `added`: a new judgment. `corrected`: it replaced an earlier judgment of the same item by the
|
|
173
|
+
* same expert, recorded the same way (by a person, or by an agent). `unchanged`: that earlier
|
|
174
|
+
* judgment already says this.
|
|
175
|
+
*/
|
|
176
|
+
outcome: 'added' | 'corrected' | 'unchanged';
|
|
177
|
+
memory: Memory;
|
|
178
|
+
/** The judgment it replaced (`corrected`). */
|
|
179
|
+
supersedes?: string;
|
|
180
|
+
/** Kinds of secret redacted from the text. */
|
|
181
|
+
redacted: string[];
|
|
182
|
+
/** The embedder failed: the judgment is kept, lexical-only. */
|
|
183
|
+
embedFailed?: true;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Records one judgment. When the same expert already judged the same item (same `ref` and domain)
|
|
187
|
+
* and it was recorded the same way (by a person, or by an agent over MCP), the new judgment
|
|
188
|
+
* corrects the old one (kept, retracted, linked both ways), unless it says the same thing. An
|
|
189
|
+
* agent-recorded judgment never corrects a person's: it is recorded alongside. Throws
|
|
190
|
+
* `JudgmentInputError` for bad input.
|
|
191
|
+
*/
|
|
192
|
+
export declare function recordJudgment(input: JudgmentInput, deps: RecordJudgmentDeps & {
|
|
193
|
+
via: JudgmentVia;
|
|
194
|
+
pack?: JudgmentPackOrigin;
|
|
195
|
+
corrects?: Memory;
|
|
196
|
+
}): Promise<RecordJudgmentResult>;
|
|
197
|
+
export interface ImportJudgmentsResult {
|
|
198
|
+
added: number;
|
|
199
|
+
corrected: number;
|
|
200
|
+
/** Already recorded with the same decision. */
|
|
201
|
+
unchanged: number;
|
|
202
|
+
/** The same decision was withdrawn, corrected or purged here: not brought back. */
|
|
203
|
+
tombstoned: number;
|
|
204
|
+
/** Rows a secret was redacted from. */
|
|
205
|
+
redacted: number;
|
|
206
|
+
/** Rows the embedder failed on (kept, lexical-only). */
|
|
207
|
+
embedFailed: number;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Records many judgments (a bulk import of past decisions), reading the store once and embedding
|
|
211
|
+
* in batches. Every row is checked before anything is written, and more than
|
|
212
|
+
* `JUDGMENT_LIMITS.importRows` is refused. A row matching a decision withdrawn, corrected or purged
|
|
213
|
+
* here is skipped and counted (`tombstoned`).
|
|
214
|
+
*/
|
|
215
|
+
export declare function importJudgments(rows: readonly JudgmentInput[], deps: RecordJudgmentDeps & {
|
|
216
|
+
via?: 'import' | 'sdk';
|
|
217
|
+
}): Promise<ImportJudgmentsResult>;
|
|
218
|
+
/**
|
|
219
|
+
* Withdraws a judgment: it is marked withdrawn (by which operator, when), retracted and its
|
|
220
|
+
* validity closed, so judging, recall (every time mode), export and packs stop using it at once,
|
|
221
|
+
* and its tombstone keeps imports from bringing it back. The record stays for
|
|
222
|
+
* `oort judge list --all`; `purgeJudgment` removes its text. Returns false when it was already
|
|
223
|
+
* withdrawn or is not a judgment.
|
|
224
|
+
*/
|
|
225
|
+
export declare function withdrawJudgment(store: MemoryStore, grant: Grant, m: Memory, by: string, now?: Date): Promise<boolean>;
|
|
226
|
+
/**
|
|
227
|
+
* Confirms an agent-recorded (`mcp`) judgment: a person (`by`, the operator) vouches that the
|
|
228
|
+
* expert really decided it, and judging uses it from then on. Only for a current, unconfirmed
|
|
229
|
+
* agent-recorded judgment; returns false otherwise. Never offered over MCP.
|
|
230
|
+
*/
|
|
231
|
+
export declare function confirmJudgment(store: MemoryStore, grant: Grant, m: Memory, by: string, now?: Date): Promise<boolean>;
|
|
232
|
+
/**
|
|
233
|
+
* Purges a judgment's text: withdraws it if it is not already, keeps its tombstone, and blanks
|
|
234
|
+
* the subject, ref, reason, criteria, memory text and embedding. What is left is an audit stub:
|
|
235
|
+
* the expert, domain, verdict, times, who withdrew and purged it, and the tombstone. Returns false
|
|
236
|
+
* when it was already purged or is not a judgment.
|
|
237
|
+
*/
|
|
238
|
+
export declare function purgeJudgment(store: MemoryStore, grant: Grant, m: Memory, by: string, now?: Date): Promise<boolean>;
|
|
239
|
+
/** The item to judge. */
|
|
240
|
+
export interface JudgeItem {
|
|
241
|
+
summary: string;
|
|
242
|
+
details?: string;
|
|
243
|
+
ref?: string;
|
|
244
|
+
/** Only precedents of this domain count. */
|
|
245
|
+
domain?: string;
|
|
246
|
+
/** Only this expert's precedents count. */
|
|
247
|
+
by?: string;
|
|
248
|
+
}
|
|
249
|
+
export interface JudgeOptions {
|
|
250
|
+
/** How many precedents to use. Default 5. */
|
|
251
|
+
k?: number;
|
|
252
|
+
/** The probability a prediction needs; below it the answer is "needs the expert". Default 0.7. */
|
|
253
|
+
threshold?: number;
|
|
254
|
+
/** The similarity a precedent needs to count as near. Default 0.45 (dense) or 0.25 (lexical). */
|
|
255
|
+
minSimilarity?: number;
|
|
256
|
+
/** The share of the near precedents' weight one verdict needs; below it they disagree. Default 0.6. */
|
|
257
|
+
minAgreement?: number;
|
|
258
|
+
/**
|
|
259
|
+
* Also use judgments imported from packs. Default false: a pack's experts are whoever its
|
|
260
|
+
* exporter says they are, so only judgments made here count unless the caller asks.
|
|
261
|
+
*/
|
|
262
|
+
includeImported?: boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Also use agent-recorded judgments nobody confirmed. Default false: an agent could have
|
|
265
|
+
* recorded anything in an expert's name.
|
|
266
|
+
*/
|
|
267
|
+
includeAgentRecorded?: boolean;
|
|
268
|
+
/** Leave these out (evaluation: an item never counts as its own precedent). */
|
|
269
|
+
exclude?: (m: Memory & {
|
|
270
|
+
judgment: Judgment;
|
|
271
|
+
}) => boolean;
|
|
272
|
+
}
|
|
273
|
+
export declare const JUDGE_DEFAULTS: Readonly<{
|
|
274
|
+
k: 5;
|
|
275
|
+
threshold: 0.7;
|
|
276
|
+
minSimilarityDense: 0.45;
|
|
277
|
+
minSimilarityLexical: 0.25;
|
|
278
|
+
minAgreement: 0.6;
|
|
279
|
+
/** A suggestion needs at least this many near precedents agreeing with it. */
|
|
280
|
+
minAgreeing: 2;
|
|
281
|
+
}>;
|
|
282
|
+
export interface JudgeDeps {
|
|
283
|
+
store: MemoryStore;
|
|
284
|
+
grant: Grant;
|
|
285
|
+
embed?: Embedder;
|
|
286
|
+
/** Its `judge` makes the prediction; without it judging returns precedents only. */
|
|
287
|
+
decide?: Pick<DecisionController, 'judge'>;
|
|
288
|
+
now?: Date;
|
|
289
|
+
}
|
|
290
|
+
/** One precedent as a judging result cites it. */
|
|
291
|
+
export interface Precedent {
|
|
292
|
+
id: string;
|
|
293
|
+
subject: JudgmentSubject;
|
|
294
|
+
verdict: string;
|
|
295
|
+
reason: string;
|
|
296
|
+
criteria: string[];
|
|
297
|
+
domain: string;
|
|
298
|
+
by: string;
|
|
299
|
+
decidedAt: string;
|
|
300
|
+
similarity: number;
|
|
301
|
+
/** At least `minSimilarity`: it counted. */
|
|
302
|
+
near: boolean;
|
|
303
|
+
/** Its verdict is the predicted one. */
|
|
304
|
+
agrees?: boolean;
|
|
305
|
+
/** How it was recorded (`mcp`: by an agent). */
|
|
306
|
+
via: JudgmentVia;
|
|
307
|
+
/** A person recorded it, or confirmed an agent's record of it. */
|
|
308
|
+
confirmed: boolean;
|
|
309
|
+
/** Imported from this pack: its expert is as the pack's exporter states it. */
|
|
310
|
+
pack?: {
|
|
311
|
+
id: string;
|
|
312
|
+
name: string;
|
|
313
|
+
};
|
|
314
|
+
imported?: true;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Why the answer is not a confident prediction:
|
|
318
|
+
* `no-precedents` (nothing recorded for this domain or expert), `too-far` (nothing close enough),
|
|
319
|
+
* `too-few` (fewer than two near precedents agree with the answer), `precedents-disagree` (the
|
|
320
|
+
* near precedents split), `low-confidence` (the prediction is below the threshold), `no-decider`
|
|
321
|
+
* (no Jev key: precedents only), `decider-failed` (the call failed).
|
|
322
|
+
*/
|
|
323
|
+
export type JudgeWhy = 'no-precedents' | 'too-far' | 'too-few' | 'precedents-disagree' | 'low-confidence' | 'no-decider' | 'decider-failed';
|
|
324
|
+
export interface JudgeResult {
|
|
325
|
+
/**
|
|
326
|
+
* `suggested`: a prediction at or above the threshold that at least two near precedents agree
|
|
327
|
+
* with. `needs-expert`: no prediction can be trusted (see `why`). `precedents-only`: no decider,
|
|
328
|
+
* so here are the precedents, no verdict.
|
|
329
|
+
*/
|
|
330
|
+
status: 'suggested' | 'needs-expert' | 'precedents-only';
|
|
331
|
+
why?: JudgeWhy;
|
|
332
|
+
/**
|
|
333
|
+
* A prediction, never the expert's decision: present with `suggested`, and as a lean with
|
|
334
|
+
* `low-confidence` and `too-few`.
|
|
335
|
+
*/
|
|
336
|
+
prediction?: {
|
|
337
|
+
verdict: string;
|
|
338
|
+
probability: number;
|
|
339
|
+
probabilities: Record<string, number>;
|
|
340
|
+
};
|
|
341
|
+
/** The precedents used (near ones), or the nearest ones when none is near. Nearest first. */
|
|
342
|
+
precedents: Precedent[];
|
|
343
|
+
/** The verdicts offered to the decider: those the near precedents voted for. */
|
|
344
|
+
verdicts: string[];
|
|
345
|
+
/** The near precedents' verdicts, weighted by similarity, as shares. */
|
|
346
|
+
agreement: Record<string, number>;
|
|
347
|
+
similarity: 'dense' | 'lexical';
|
|
348
|
+
threshold: number;
|
|
349
|
+
minSimilarity: number;
|
|
350
|
+
degraded?: ('embed-failed' | 'decider-failed')[];
|
|
351
|
+
decisions: Decision[];
|
|
352
|
+
/** Imported judgments that matched the filters but were left out (`includeImported` false). */
|
|
353
|
+
importedExcluded?: number;
|
|
354
|
+
/** Unconfirmed agent-recorded judgments left out (`includeAgentRecorded` false). */
|
|
355
|
+
agentRecordedExcluded?: number;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* The active judgments of `filter`'s domain and expert (when it names them). Judgments imported
|
|
359
|
+
* from packs only with `includeImported`, unconfirmed agent-recorded ones only with
|
|
360
|
+
* `includeAgentRecorded`.
|
|
361
|
+
*/
|
|
362
|
+
export declare function activeJudgments(store: MemoryStore, grant: Grant, filter: {
|
|
363
|
+
domain?: string;
|
|
364
|
+
by?: string;
|
|
365
|
+
includeImported?: boolean;
|
|
366
|
+
includeAgentRecorded?: boolean;
|
|
367
|
+
}, now: Date, embeddings?: boolean): Promise<(Memory & {
|
|
368
|
+
judgment: Judgment;
|
|
369
|
+
})[]>;
|
|
370
|
+
/**
|
|
371
|
+
* Applies the expert's judgments to `item`: finds the nearest active precedents (domain- and
|
|
372
|
+
* expert-filtered), then asks the decider for a choice over the verdicts they voted for, with the
|
|
373
|
+
* precedents and their reasons as state. It answers "needs the expert" instead of guessing when
|
|
374
|
+
* there are no precedents, none is near, fewer than two near precedents would back the answer,
|
|
375
|
+
* the near ones disagree, or the prediction is below `threshold`; and returns precedents only
|
|
376
|
+
* when there is no decider. Never writes anything.
|
|
377
|
+
*/
|
|
378
|
+
export declare function judge(item: JudgeItem, deps: JudgeDeps, opts?: JudgeOptions): Promise<JudgeResult>;
|
|
379
|
+
export declare const JUDGMENT_PACK_VERSION = 1;
|
|
380
|
+
/** The first line of a pack. */
|
|
381
|
+
export interface JudgmentPackManifest {
|
|
382
|
+
oort: 'judgment-pack';
|
|
383
|
+
version: 1;
|
|
384
|
+
name: string;
|
|
385
|
+
exportedAt: string;
|
|
386
|
+
/** The agent that exported it. */
|
|
387
|
+
exportedBy: string;
|
|
388
|
+
count: number;
|
|
389
|
+
/**
|
|
390
|
+
* sha256 over the manifest's metadata (version, name, exportedAt, exportedBy) and the judgment
|
|
391
|
+
* lines, as `packDigest` computes it. Its first 16 hex characters are the pack's id.
|
|
392
|
+
*/
|
|
393
|
+
digest: string;
|
|
394
|
+
/** The domains and experts inside. A reader checks them against the lines (they must match). */
|
|
395
|
+
domains: string[];
|
|
396
|
+
experts: string[];
|
|
397
|
+
}
|
|
398
|
+
/** One judgment in a pack: the decision and its id, without vectors or local state. */
|
|
399
|
+
export interface JudgmentPackLine {
|
|
400
|
+
id: string;
|
|
401
|
+
judgment: Omit<Judgment, 'withdrawn' | 'via' | 'pack'> & {
|
|
402
|
+
via: JudgmentVia;
|
|
403
|
+
/**
|
|
404
|
+
* Where the exporter got it, when it was itself imported: the exporter's own record, which a
|
|
405
|
+
* reader keeps only as an untrusted claim (`claimedEarlier`).
|
|
406
|
+
*/
|
|
407
|
+
pack?: Omit<JudgmentPackOrigin, 'claimedEarlier'>;
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/** Raised for a file that is not a readable judgment pack. */
|
|
411
|
+
export declare class JudgmentPackError extends Error {
|
|
412
|
+
readonly name = "JudgmentPackError";
|
|
413
|
+
}
|
|
414
|
+
/** Length caps for pack metadata. */
|
|
415
|
+
export declare const PACK_LIMITS: Readonly<{
|
|
416
|
+
name: 120;
|
|
417
|
+
exportedBy: 128;
|
|
418
|
+
id: 128;
|
|
419
|
+
time: 40;
|
|
420
|
+
}>;
|
|
421
|
+
/** What the digest binds besides the lines. */
|
|
422
|
+
export interface PackMeta {
|
|
423
|
+
name: string;
|
|
424
|
+
exportedAt: string;
|
|
425
|
+
exportedBy: string;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* The digest of a pack: hex sha256 over its metadata (version, name, exportedAt, exportedBy, as a
|
|
429
|
+
* JSON array) and then each judgment line as written. Changing the name, the exporter, the time or
|
|
430
|
+
* any line changes it.
|
|
431
|
+
*/
|
|
432
|
+
export declare function packDigest(meta: PackMeta, lines: readonly string[]): string;
|
|
433
|
+
/**
|
|
434
|
+
* A pack of `judgments` (active ones only; withdrawn and corrected ones never travel), as JSON
|
|
435
|
+
* Lines: a manifest, then one line per judgment in id order. Attribution (`by`, `decidedAt`)
|
|
436
|
+
* travels with each judgment; embeddings do not (the importer embeds them with its own model).
|
|
437
|
+
*/
|
|
438
|
+
export declare function formatJudgmentPack(judgments: readonly Memory[], meta: {
|
|
439
|
+
name: string;
|
|
440
|
+
exportedBy: string;
|
|
441
|
+
exportedAt: Date;
|
|
442
|
+
}, now?: Date): string;
|
|
443
|
+
export interface ParsedJudgmentPack {
|
|
444
|
+
manifest: JudgmentPackManifest;
|
|
445
|
+
lines: JudgmentPackLine[];
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Reads a pack and checks everything a person is shown before importing it: the manifest's
|
|
449
|
+
* metadata (plain text, length caps), the count, the digest over the metadata and the lines, every
|
|
450
|
+
* judgment as `recordJudgment` would check it, any claimed earlier origin (exact shape), and that
|
|
451
|
+
* the manifest's domains and experts are exactly those of the lines. The lines come back rebuilt
|
|
452
|
+
* from their checked fields only. The digest proves integrity, not authorship: a pack is as
|
|
453
|
+
* trustworthy as whoever handed it over. Throws `JudgmentPackError`.
|
|
454
|
+
*/
|
|
455
|
+
export declare function parseJudgmentPack(textIn: string): ParsedJudgmentPack;
|
|
456
|
+
/** A pack line's judgment as recording input. */
|
|
457
|
+
export declare function packInput(j: JudgmentPackLine['judgment']): JudgmentInput;
|
|
458
|
+
export interface ImportPackResult {
|
|
459
|
+
added: string[];
|
|
460
|
+
/**
|
|
461
|
+
* Line ids not added because they are already here: imported from this very pack before (same
|
|
462
|
+
* digest and line id), or the same exact decision is in the store, from any origin.
|
|
463
|
+
*/
|
|
464
|
+
skipped: string[];
|
|
465
|
+
/**
|
|
466
|
+
* Line ids whose decision was withdrawn, corrected or purged here (matched by its tombstone,
|
|
467
|
+
* whatever `decidedAt` the pack gives it): never brought back.
|
|
468
|
+
*/
|
|
469
|
+
tombstoned: string[];
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Imports a parsed pack into the grant's home node, reading the store once and embedding in
|
|
473
|
+
* batches. Each judgment keeps its expert and decision time, and records where it came from:
|
|
474
|
+
* `judgment.pack`, built only from this pack (its digest id, the digest-bound name, exporter and
|
|
475
|
+
* time, and the line's id). An earlier origin the line claims is kept apart as `claimedEarlier`,
|
|
476
|
+
* marked untrusted, and used for nothing.
|
|
477
|
+
*
|
|
478
|
+
* A line is skipped when this pack was imported before (same digest and line id) or the same
|
|
479
|
+
* exact decision is already here, and reported as tombstoned when its decision was withdrawn,
|
|
480
|
+
* corrected or purged here: a pack never brings back what an expert took back.
|
|
481
|
+
*/
|
|
482
|
+
export declare function importJudgmentPack(pack: ParsedJudgmentPack, deps: RecordJudgmentDeps): Promise<ImportPackResult>;
|
|
483
|
+
/** Rows of a CSV file (RFC 4180: quoted fields, `""` for a quote, newlines inside quotes). */
|
|
484
|
+
export declare function parseCsv(input: string): string[][];
|
|
485
|
+
/**
|
|
486
|
+
* Past decisions from a CSV file (a header row naming the columns: subject, verdict, reason,
|
|
487
|
+
* domain, by, and optionally ref, criteria and verdicts as `;`-separated lists, decided_at) or JSON
|
|
488
|
+
* Lines (one object per line with the same keys; lists may be arrays). `defaults` fills a missing
|
|
489
|
+
* domain or expert. More than `JUDGMENT_LIMITS.importRows` rows is refused. Each row is checked as
|
|
490
|
+
* `recordJudgment` checks it; the first bad row throws `JudgmentInputError` naming its line.
|
|
491
|
+
*/
|
|
492
|
+
export declare function parseJudgmentRows(text: string, format: 'csv' | 'jsonl', defaults?: {
|
|
493
|
+
domain?: string;
|
|
494
|
+
by?: string;
|
|
495
|
+
}): JudgmentInput[];
|
|
496
|
+
export declare const EVAL_THRESHOLDS: readonly number[];
|
|
497
|
+
/** One threshold's numbers: how much is predicted, and how often the prediction is the expert's. */
|
|
498
|
+
export interface EvalPoint {
|
|
499
|
+
threshold: number;
|
|
500
|
+
/** Items predicted at or above the threshold, as a share of all items. */
|
|
501
|
+
coverage: number;
|
|
502
|
+
/** Of those, the share whose prediction matched the expert's verdict; null when none. */
|
|
503
|
+
agreement: number | null;
|
|
504
|
+
predicted: number;
|
|
505
|
+
agreed: number;
|
|
506
|
+
}
|
|
507
|
+
export interface EvalReport {
|
|
508
|
+
/** Per domain, then `(all)` (not a valid domain label, so it never collides). */
|
|
509
|
+
domains: Record<string, {
|
|
510
|
+
items: number;
|
|
511
|
+
points: EvalPoint[];
|
|
512
|
+
why: Partial<Record<JudgeWhy, number>>;
|
|
513
|
+
}>;
|
|
514
|
+
thresholds: number[];
|
|
515
|
+
similarity: 'dense' | 'lexical' | 'mixed';
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Judges each held-out decision against the store (never recording it, and never using the item
|
|
519
|
+
* itself as a precedent: leave-one-out by item ref and by exact decision) and compares the
|
|
520
|
+
* prediction with the expert's verdict: agreement and coverage per domain at each threshold.
|
|
521
|
+
* An item counts as predicted at threshold t when judging would say `suggested` with t as the
|
|
522
|
+
* threshold. Needs a decider; without one nothing is ever predicted.
|
|
523
|
+
*/
|
|
524
|
+
export declare function evaluateJudgments(heldOut: readonly JudgmentInput[], deps: JudgeDeps, opts?: Omit<JudgeOptions, 'threshold'> & {
|
|
525
|
+
thresholds?: readonly number[];
|
|
526
|
+
}): Promise<EvalReport>;
|