@avantf/dsh-mem 0.8.1 → 0.10.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 +42 -43
- package/lib/client.js +1181 -327
- package/lib/engine/graph-recall/index.d.ts +2 -2
- package/lib/engine/graph-recall/traverse.d.ts +2 -2
- package/lib/engine/graph-recall/types.d.ts +1 -1
- package/lib/engine/mem/config/config_files.d.ts +1 -1
- package/lib/engine/mem/db/dao/facts.d.ts +80 -82
- package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +6 -13
- package/lib/engine/mem/defaults.d.ts +4 -1
- package/lib/engine/mem/entities/extract.d.ts +34 -14
- package/lib/engine/mem/facts_io.d.ts +4 -0
- package/lib/engine/mem/index.d.ts +1 -0
- package/lib/engine/mem/interfaces/repositories.d.ts +5 -19
- package/lib/engine/mem/lifecycle/maintenance.d.ts +8 -4
- package/lib/engine/mem/lifecycle/tick.d.ts +11 -10
- package/lib/engine/mem/lifecycle/trust.d.ts +24 -37
- package/lib/engine/mem/modelBootstrap.d.ts +29 -0
- package/lib/engine/mem/runtime.d.ts +4 -8
- package/lib/engine/mem/store/facts_restore.d.ts +8 -6
- package/lib/engine/mem/store/graph_reader.d.ts +2 -2
- package/lib/engine/mem/store/memory.d.ts +98 -37
- package/lib/engine/mem/store/recall_policy.d.ts +10 -0
- package/lib/engine/mem/store/time_filter.d.ts +1 -1
- package/lib/engine/mem/trustHeartbeat.d.ts +1 -1
- package/lib/engine/mem-contract/config.d.ts +144 -9
- package/lib/engine/mem-contract/index.d.ts +2 -0
- package/lib/engine/mem-contract/time.d.ts +75 -0
- package/lib/engine/mem-contract/tools.d.ts +9 -15
- package/lib/engine/mem-contract/trust_curve.d.ts +119 -0
- package/lib/engine/mem-contract/types.d.ts +73 -12
- package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +10 -0
- package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +28 -0
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +48 -6
- package/lib/engine/mem-retrieval/interfaces.d.ts +22 -2
- package/lib/engine/mem-retrieval/text_budget.d.ts +31 -0
- package/lib/index.js +2349 -665
- package/lib/interface-version.json +2 -2
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/paging.d.ts +58 -8
- package/lib/types/client/paging.d.ts.map +1 -1
- package/lib/types/client/time_filter.d.ts +54 -0
- package/lib/types/client/time_filter.d.ts.map +1 -0
- package/lib/types/command.d.ts.map +1 -1
- package/lib/types/deps_audit.d.ts +88 -0
- package/lib/types/deps_audit.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +1 -1
- package/lib/types/envinit.d.ts +224 -4
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +58 -0
- package/lib/types/hints.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts.map +1 -1
- package/lib/types/runtime_deps_resource.d.ts +29 -0
- package/lib/types/runtime_deps_resource.d.ts.map +1 -0
- package/package.json +26 -24
- package/res/runtime-deps.yaml +99 -0
|
@@ -8,6 +8,135 @@ import { z } from 'zod';
|
|
|
8
8
|
* past the window SILENTLY, so every derivation has to agree on the number.
|
|
9
9
|
*/
|
|
10
10
|
export declare const DEFAULT_MODEL_WINDOW_TOKENS = 512;
|
|
11
|
+
/** One audit entry. `kind` decides which probe runs, and which of `specifier`/`item` is required. */
|
|
12
|
+
export declare const DependencyAuditEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
13
|
+
id: z.ZodString;
|
|
14
|
+
severity: z.ZodEnum<{
|
|
15
|
+
info: "info";
|
|
16
|
+
warning: "warning";
|
|
17
|
+
degraded: "degraded";
|
|
18
|
+
}>;
|
|
19
|
+
when: z.ZodEnum<{
|
|
20
|
+
degraded: "degraded";
|
|
21
|
+
failed: "failed";
|
|
22
|
+
missing: "missing";
|
|
23
|
+
}>;
|
|
24
|
+
min_vectors: z.ZodOptional<z.ZodNumber>;
|
|
25
|
+
body: z.ZodString;
|
|
26
|
+
kind: z.ZodLiteral<"module">;
|
|
27
|
+
specifier: z.ZodString;
|
|
28
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
29
|
+
id: z.ZodString;
|
|
30
|
+
severity: z.ZodEnum<{
|
|
31
|
+
info: "info";
|
|
32
|
+
warning: "warning";
|
|
33
|
+
degraded: "degraded";
|
|
34
|
+
}>;
|
|
35
|
+
when: z.ZodEnum<{
|
|
36
|
+
degraded: "degraded";
|
|
37
|
+
failed: "failed";
|
|
38
|
+
missing: "missing";
|
|
39
|
+
}>;
|
|
40
|
+
min_vectors: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
body: z.ZodString;
|
|
42
|
+
kind: z.ZodLiteral<"runtime-deps">;
|
|
43
|
+
item: z.ZodString;
|
|
44
|
+
}, z.core.$strip>], "kind">;
|
|
45
|
+
/** One configured audit entry, discriminated by `kind`. */
|
|
46
|
+
export type DependencyAuditEntry = z.infer<typeof DependencyAuditEntrySchema>;
|
|
47
|
+
/**
|
|
48
|
+
* One `runtime-deps` declaration as SHIPPED WITH THE PLUGIN (`packages/plugin/res/runtime-deps.yaml`).
|
|
49
|
+
*
|
|
50
|
+
* WHY IT IS A RESOURCE AND NOT USER CONFIG. The package set, the sources, the attempts, the verify
|
|
51
|
+
* list and the audit sentences are facts about THIS BUILD: which module the local backend imports,
|
|
52
|
+
* which native bindings this release looks for, what a session should be told when one of them is
|
|
53
|
+
* absent. Leaving them to `configs/common.yaml` made every deployment responsible for keeping that
|
|
54
|
+
* list correct — a stale copy would install a module no code loads, or silently drop the audit — and
|
|
55
|
+
* the shipped defaults were only ever the plugin's own declaration anyway. The operator keeps the one
|
|
56
|
+
* thing that really is a per-deployment decision (the two `enabled` switches); the DATA travels in the
|
|
57
|
+
* package, next to the code that reads it.
|
|
58
|
+
*
|
|
59
|
+
* Validation is strict on purpose: the file is read from the INSTALL directory at mount, and a
|
|
60
|
+
* malformed one is a packaging bug. The plugin logs it and degrades (no self-heal item, no audit)
|
|
61
|
+
* rather than throwing out of the mount — see `packages/plugin/src/runtime_deps_resource.ts`.
|
|
62
|
+
*
|
|
63
|
+
* The schema is the SINGLE definition of the file's shape: the loader validates with it, and the
|
|
64
|
+
* plugin maps it to the base provider's objects field by field (the base never reads YAML).
|
|
65
|
+
*/
|
|
66
|
+
export declare const RuntimeDepsResourceSchema: z.ZodObject<{
|
|
67
|
+
version: z.ZodLiteral<1>;
|
|
68
|
+
semantic: z.ZodObject<{
|
|
69
|
+
packages: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
70
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
71
|
+
id: z.ZodString;
|
|
72
|
+
registry: z.ZodDefault<z.ZodString>;
|
|
73
|
+
cache: z.ZodDefault<z.ZodString>;
|
|
74
|
+
}, z.core.$strip>>;
|
|
75
|
+
attempts: z.ZodArray<z.ZodObject<{
|
|
76
|
+
source: z.ZodString;
|
|
77
|
+
manager: z.ZodOptional<z.ZodEnum<{
|
|
78
|
+
npm: "npm";
|
|
79
|
+
pnpm: "pnpm";
|
|
80
|
+
}>>;
|
|
81
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
82
|
+
allowScripts: z.ZodOptional<z.ZodBoolean>;
|
|
83
|
+
}, z.core.$strip>>;
|
|
84
|
+
verify: z.ZodArray<z.ZodString>;
|
|
85
|
+
}, z.core.$strip>;
|
|
86
|
+
native: z.ZodObject<{
|
|
87
|
+
packages: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
88
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
89
|
+
id: z.ZodString;
|
|
90
|
+
registry: z.ZodDefault<z.ZodString>;
|
|
91
|
+
cache: z.ZodDefault<z.ZodString>;
|
|
92
|
+
}, z.core.$strip>>;
|
|
93
|
+
attempts: z.ZodArray<z.ZodObject<{
|
|
94
|
+
source: z.ZodString;
|
|
95
|
+
manager: z.ZodOptional<z.ZodEnum<{
|
|
96
|
+
npm: "npm";
|
|
97
|
+
pnpm: "pnpm";
|
|
98
|
+
}>>;
|
|
99
|
+
env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
100
|
+
allowScripts: z.ZodOptional<z.ZodBoolean>;
|
|
101
|
+
}, z.core.$strip>>;
|
|
102
|
+
verify: z.ZodArray<z.ZodString>;
|
|
103
|
+
}, z.core.$strip>;
|
|
104
|
+
audit: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
105
|
+
id: z.ZodString;
|
|
106
|
+
severity: z.ZodEnum<{
|
|
107
|
+
info: "info";
|
|
108
|
+
warning: "warning";
|
|
109
|
+
degraded: "degraded";
|
|
110
|
+
}>;
|
|
111
|
+
when: z.ZodEnum<{
|
|
112
|
+
degraded: "degraded";
|
|
113
|
+
failed: "failed";
|
|
114
|
+
missing: "missing";
|
|
115
|
+
}>;
|
|
116
|
+
min_vectors: z.ZodOptional<z.ZodNumber>;
|
|
117
|
+
body: z.ZodString;
|
|
118
|
+
kind: z.ZodLiteral<"module">;
|
|
119
|
+
specifier: z.ZodString;
|
|
120
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
121
|
+
id: z.ZodString;
|
|
122
|
+
severity: z.ZodEnum<{
|
|
123
|
+
info: "info";
|
|
124
|
+
warning: "warning";
|
|
125
|
+
degraded: "degraded";
|
|
126
|
+
}>;
|
|
127
|
+
when: z.ZodEnum<{
|
|
128
|
+
degraded: "degraded";
|
|
129
|
+
failed: "failed";
|
|
130
|
+
missing: "missing";
|
|
131
|
+
}>;
|
|
132
|
+
min_vectors: z.ZodOptional<z.ZodNumber>;
|
|
133
|
+
body: z.ZodString;
|
|
134
|
+
kind: z.ZodLiteral<"runtime-deps">;
|
|
135
|
+
item: z.ZodString;
|
|
136
|
+
}, z.core.$strip>], "kind">>;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
/** The parsed `res/runtime-deps.yaml`. */
|
|
139
|
+
export type RuntimeDepsResource = z.infer<typeof RuntimeDepsResourceSchema>;
|
|
11
140
|
/** The merged runtime config shared by the memory store and the retrieval stack. */
|
|
12
141
|
export declare const ConfigSchema: z.ZodObject<{
|
|
13
142
|
dataHome: z.ZodDefault<z.ZodString>;
|
|
@@ -20,6 +149,12 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
20
149
|
cache_dir: z.ZodDefault<z.ZodString>;
|
|
21
150
|
auto_download: z.ZodDefault<z.ZodBoolean>;
|
|
22
151
|
auto_migrate: z.ZodDefault<z.ZodBoolean>;
|
|
152
|
+
self_heal: z.ZodPrefault<z.ZodObject<{
|
|
153
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
154
|
+
}, z.core.$strip>>;
|
|
155
|
+
native_self_heal: z.ZodPrefault<z.ZodObject<{
|
|
156
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
157
|
+
}, z.core.$strip>>;
|
|
23
158
|
}, z.core.$strip>>;
|
|
24
159
|
vectorStore: z.ZodPrefault<z.ZodObject<{
|
|
25
160
|
backend: z.ZodDefault<z.ZodString>;
|
|
@@ -47,20 +182,20 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
47
182
|
}, z.core.$strip>>;
|
|
48
183
|
trust: z.ZodPrefault<z.ZodObject<{
|
|
49
184
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
50
|
-
|
|
51
|
-
|
|
185
|
+
base_lifetime_days: z.ZodDefault<z.ZodNumber>;
|
|
186
|
+
max_retention_days: z.ZodDefault<z.ZodNumber>;
|
|
187
|
+
decay_shape: z.ZodDefault<z.ZodEnum<{
|
|
188
|
+
linear: "linear";
|
|
189
|
+
accelerating: "accelerating";
|
|
190
|
+
}>>;
|
|
191
|
+
decay_curvature: z.ZodDefault<z.ZodNumber>;
|
|
52
192
|
forget_threshold: z.ZodDefault<z.ZodNumber>;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
recall_delta: z.ZodDefault<z.ZodNumber>;
|
|
193
|
+
recall_gain_below_base: z.ZodDefault<z.ZodNumber>;
|
|
194
|
+
recall_gain_above_base: z.ZodDefault<z.ZodNumber>;
|
|
56
195
|
recall_daily_cap: z.ZodDefault<z.ZodNumber>;
|
|
57
196
|
recall_marginal_decay: z.ZodDefault<z.ZodNumber>;
|
|
58
|
-
recall_ceiling: z.ZodDefault<z.ZodNumber>;
|
|
59
|
-
feedback_delta: z.ZodDefault<z.ZodNumber>;
|
|
60
|
-
feedback_daily_cap: z.ZodDefault<z.ZodNumber>;
|
|
61
197
|
inherit_trust_on_update: z.ZodDefault<z.ZodBoolean>;
|
|
62
198
|
purge_skips_pinned: z.ZodDefault<z.ZodBoolean>;
|
|
63
|
-
idle_calendar_days: z.ZodDefault<z.ZodNumber>;
|
|
64
199
|
tick_max_facts: z.ZodDefault<z.ZodNumber>;
|
|
65
200
|
presence: z.ZodPrefault<z.ZodObject<{
|
|
66
201
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export * from './config.js';
|
|
3
|
+
export * from './trust_curve.js';
|
|
3
4
|
export * from './family.js';
|
|
4
5
|
export * from './tools.js';
|
|
5
6
|
export * from './remote.js';
|
|
6
7
|
export * from './log.js';
|
|
7
8
|
export * from './env.js';
|
|
8
9
|
export * from './text.js';
|
|
10
|
+
export * from './time.js';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE place a stored timestamp turns into a string a human (or a model) reads.
|
|
3
|
+
*
|
|
4
|
+
* ── THE BUG THIS MODULE EXISTS FOR ───────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* `facts.created_at` / `updated_at` / `archived_at` (and `contradiction_log.detected_at`, and the
|
|
7
|
+
* retrieval-health stamp) are written by SQLite's `DEFAULT CURRENT_TIMESTAMP`, which is **UTC**, and
|
|
8
|
+
* `store/memory.ts` used to hand the raw text up to every surface. Two consequences, both reported as
|
|
9
|
+
* "记忆时间显示不对":
|
|
10
|
+
*
|
|
11
|
+
* 1. the UI printed `2026-10-28 11:58:48` as if it were a local wall clock (Asia/Shanghai really
|
|
12
|
+
* meant `19:58:48`), because a zone-less string cannot say otherwise;
|
|
13
|
+
* 2. the MODEL received the same zone-less string next to `event_date`, which is a calendar DATE
|
|
14
|
+
* with no time and no zone. Two different kinds of value, one shape — the model had to guess
|
|
15
|
+
* which one it was looking at, and it cannot.
|
|
16
|
+
*
|
|
17
|
+
* ── THE SPLIT THIS MODULE ENFORCES ───────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* Storage stays UTC and stays byte-identical (`facts_io` export/import/snapshot is a compatibility
|
|
20
|
+
* surface and is deliberately NOT routed through here). The fix is in the two DISPLAY faces:
|
|
21
|
+
*
|
|
22
|
+
* - {@link formatOffsetIso} — the MODEL face (`mem_recall` hits, `mem_admin list/detail/
|
|
23
|
+
* contradictions`, `mem_admin stats`, the CLI and MCP that print those payloads): an ISO-8601
|
|
24
|
+
* instant **with an explicit offset**, e.g. `2026-10-28T19:58:48+08:00`, rendered in the host's
|
|
25
|
+
* local zone. The offset is part of the string, so the model needs no conversion and the value is
|
|
26
|
+
* never ambiguous again — while `event_date` stays `2026-10-28`, a calendar date with no zone.
|
|
27
|
+
* - {@link formatLocalHuman} — the HUMAN face (the 记忆 tab): `2026-10-28 19:58`, the same instant,
|
|
28
|
+
* local, to the minute.
|
|
29
|
+
*
|
|
30
|
+
* Both read the input through the SAME parser ({@link parseUtcTs}) and the same instant, so the two
|
|
31
|
+
* faces cannot disagree. The UI deliberately formats the model face's OWN string rather than
|
|
32
|
+
* re-deriving one from the raw column: the input it gets already carries an offset, `parseUtcTs`
|
|
33
|
+
* leaves an explicit offset alone, and there is therefore exactly ONE parse rule and ONE pair of
|
|
34
|
+
* renderers in the repository — not a second opinion in the browser.
|
|
35
|
+
*
|
|
36
|
+
* Dependency-free on purpose: this module is reachable from the DSH client bundle through the
|
|
37
|
+
* `@avantf/mem-contract/time` subpath (the bundle must not pull in the tool schemas / zod). It has no
|
|
38
|
+
* DSH host and no DB, so the engine, the CLI and the MCP face can all use it.
|
|
39
|
+
*
|
|
40
|
+
* @module contract/time
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* `'YYYY-MM-DD HH:MM:SS'` (SQLite UTC) or ISO → epoch ms; null/garbage → null.
|
|
44
|
+
*
|
|
45
|
+
* A stamp with NO zone designator is read as UTC, not as local time. `Date.parse` treats a zone-less
|
|
46
|
+
* ISO string as LOCAL, and everything in this store is written in UTC (`CURRENT_TIMESTAMP`, and
|
|
47
|
+
* {@link formatUtcTs}), so handing one over as-is would shift every quota window and every decay
|
|
48
|
+
* measurement by the host's UTC offset — a fact reinforced at 23:00 UTC+8 would look reinforced 8
|
|
49
|
+
* hours earlier, and the 24h cap would open or close at the wrong moment. An explicit `Z` or
|
|
50
|
+
* `±HH:MM` is left alone (that is what makes a `formatOffsetIso` output safe to feed back in).
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseUtcTs(ts: string | null | undefined): number | null;
|
|
53
|
+
/** Epoch ms → `'YYYY-MM-DD HH:MM:SS'` (the SQLite `CURRENT_TIMESTAMP` shape, UTC). */
|
|
54
|
+
export declare function formatUtcTs(ms: number): string;
|
|
55
|
+
/**
|
|
56
|
+
* The MODEL face: an instant as ISO-8601 with an explicit offset, in the host's local zone
|
|
57
|
+
* (`2026-10-28T19:58:48+08:00`).
|
|
58
|
+
*
|
|
59
|
+
* `null` — never `''` and never a fabricated stamp — when the value is absent or unreadable, so a
|
|
60
|
+
* caller keeps its own "unknown" spelling (`''` for `created_at`, `null` for `updated_at`) instead of
|
|
61
|
+
* rendering an empty time. `+00:00` (not `Z`) on a UTC host: the shape MUST carry a sign, which is
|
|
62
|
+
* what a caller regex-checks.
|
|
63
|
+
*/
|
|
64
|
+
export declare function formatOffsetIso(ts: string | null | undefined): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* The HUMAN face: the same instant as local wall-clock text to the minute (`2026-10-28 19:58`).
|
|
67
|
+
*
|
|
68
|
+
* Seconds are deliberately dropped: the list/detail lines and the health line are read at a glance,
|
|
69
|
+
* and `HH:MM` is the whole useful resolution there. `null` for an absent/unreadable value (same
|
|
70
|
+
* contract as {@link formatOffsetIso}), so a caller prints its own 「未知」/`—` placeholder.
|
|
71
|
+
*
|
|
72
|
+
* NOT for `event_date` / `valid_from`: those are calendar dates the CALLER wrote (`2026-10-28`), with
|
|
73
|
+
* no time and no zone. Running one through here would invent a midnight in the host's zone.
|
|
74
|
+
*/
|
|
75
|
+
export declare function formatLocalHuman(ts: string | null | undefined): string | null;
|
|
@@ -91,7 +91,7 @@ export declare function modelFacingToolResult(toolKey: string, action: unknown,
|
|
|
91
91
|
*/
|
|
92
92
|
export declare const EXPORT_STATUSES: readonly ["active", "archived", "all"];
|
|
93
93
|
export type ExportStatus = StatusFilter;
|
|
94
|
-
export declare const REMEMBER_ACTIONS: readonly ["add", "update", "remove"
|
|
94
|
+
export declare const REMEMBER_ACTIONS: readonly ["add", "update", "remove"];
|
|
95
95
|
export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
96
96
|
action: z.ZodLiteral<"add">;
|
|
97
97
|
content: z.ZodString;
|
|
@@ -100,10 +100,8 @@ export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
100
100
|
note: "note";
|
|
101
101
|
}>>;
|
|
102
102
|
title: z.ZodOptional<z.ZodString>;
|
|
103
|
-
ttl_days: z.ZodOptional<z.ZodNumber>;
|
|
104
103
|
source_ref: z.ZodOptional<z.ZodString>;
|
|
105
104
|
event_date: z.ZodOptional<z.ZodString>;
|
|
106
|
-
valid_until: z.ZodOptional<z.ZodString>;
|
|
107
105
|
relations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
108
106
|
subject: z.ZodString;
|
|
109
107
|
predicate: z.ZodString;
|
|
@@ -119,10 +117,8 @@ export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
119
117
|
}>>;
|
|
120
118
|
title: z.ZodOptional<z.ZodString>;
|
|
121
119
|
retitle: z.ZodOptional<z.ZodLiteral<true>>;
|
|
122
|
-
ttl_days: z.ZodOptional<z.ZodNumber>;
|
|
123
120
|
source_ref: z.ZodOptional<z.ZodString>;
|
|
124
121
|
event_date: z.ZodOptional<z.ZodString>;
|
|
125
|
-
valid_until: z.ZodOptional<z.ZodString>;
|
|
126
122
|
relations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
127
123
|
subject: z.ZodString;
|
|
128
124
|
predicate: z.ZodString;
|
|
@@ -132,12 +128,6 @@ export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
132
128
|
action: z.ZodLiteral<"remove">;
|
|
133
129
|
fact_id: z.ZodNumber;
|
|
134
130
|
reason: z.ZodOptional<z.ZodString>;
|
|
135
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
136
|
-
action: z.ZodLiteral<"helpful">;
|
|
137
|
-
fact_id: z.ZodNumber;
|
|
138
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
139
|
-
action: z.ZodLiteral<"unhelpful">;
|
|
140
|
-
fact_id: z.ZodNumber;
|
|
141
131
|
}, z.core.$strip>], "action">;
|
|
142
132
|
/**
|
|
143
133
|
* The runtime/CLI-visible write request. The base zod union keeps `kind` optional so the CLI's
|
|
@@ -146,9 +136,13 @@ export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
146
136
|
*/
|
|
147
137
|
export type RememberRequest = z.infer<typeof RememberUnion>;
|
|
148
138
|
/**
|
|
149
|
-
* The writes the model may make
|
|
150
|
-
*
|
|
151
|
-
*
|
|
139
|
+
* The writes the model may make — `MODEL_REMEMBER_ACTIONS` and `REMEMBER_ACTIONS` are the same three
|
|
140
|
+
* again since the operator-side `helpful` / `unhelpful` actions were removed: a vote on a fact was a
|
|
141
|
+
* way to move its retention clock, and a model able to vote could "refresh" a fact by voting on it.
|
|
142
|
+
*
|
|
143
|
+
* Permanent is set by ONE route only: an explicit `pin` from a human (`mem_admin` / CLI / settings
|
|
144
|
+
* page). Do NOT re-add an action that promotes or demotes a fact from a write path — that is the
|
|
145
|
+
* behaviour the retention policy exists to prevent.
|
|
152
146
|
*/
|
|
153
147
|
export declare const MODEL_REMEMBER_ACTIONS: readonly ["add", "update", "remove"];
|
|
154
148
|
export declare const ModelRememberUnion: z.ZodDiscriminatedUnion<readonly z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], string>;
|
|
@@ -341,7 +335,7 @@ export declare const TOOL_SPECS: ToolSpec[];
|
|
|
341
335
|
*
|
|
342
336
|
* Descriptions are authored per action ("action=update 时必填"), so taking the
|
|
343
337
|
* first branch's text verbatim told a model that `fact_id` only matters for
|
|
344
|
-
* `update` although `remove
|
|
338
|
+
* `update` although `remove` requires it too. The action-clause
|
|
345
339
|
* is stripped and replaced by the derived set of actions that make the field
|
|
346
340
|
* required.
|
|
347
341
|
*
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The trust decay curve — PURE functions, no IO, no config object of its own.
|
|
3
|
+
*
|
|
4
|
+
* This file is the ONE implementation of "how much trust is left after N active days" and of every
|
|
5
|
+
* inverse of it. `core/lifecycle/trust.ts` (read path, recall reinforcement), the tick's settle
|
|
6
|
+
* statement (`db/dao/facts.ts`) and the config's load-time validation (`config.ts`) all delegate
|
|
7
|
+
* here, so a change of shape cannot leave one of them behind (docs/TRUST_MODEL.md §2.2, DESIGN §18).
|
|
8
|
+
*
|
|
9
|
+
* It lives in the CONTRACT package because the load-time validation needs the same math, and the
|
|
10
|
+
* dependency direction is core → contract, never the reverse.
|
|
11
|
+
*
|
|
12
|
+
* ## The two shapes
|
|
13
|
+
*
|
|
14
|
+
* `linear` (TRUST_MODEL D7, the P3 equivalent tier): trust falls by a constant amount per active
|
|
15
|
+
* day, `slope = BASE_TRUST / base_lifetime_days` ⇒ a fact that starts at `BASE_TRUST` reaches 0
|
|
16
|
+
* after exactly `base_lifetime_days` active days.
|
|
17
|
+
*
|
|
18
|
+
* `accelerating` (the v2 target shape): `dT/dt = −λ·(a + 1 − T)` — the closer to 1, the slower the
|
|
19
|
+
* decay, so "almost permanent" facts really linger. Closed form (plan §3.1):
|
|
20
|
+
*
|
|
21
|
+
* T(t) = (1 + a) − ((1 + a) − T₀)·e^(λt) λ = ln((1 + a)/(1 + a − BASE_TRUST)) / B
|
|
22
|
+
*
|
|
23
|
+
* Note the POSITIVE exponent: `u = a + 1 − T` grows, so `e^(λt)` is the decay direction here (the
|
|
24
|
+
* plan writes it this way, and the closed-form test pins it). `a = decay_curvature`.
|
|
25
|
+
*
|
|
26
|
+
* ## Units
|
|
27
|
+
*
|
|
28
|
+
* `days` is ALWAYS the ACTIVE-DAY counter (`avantf_stats.trust_clock`), never wall time.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* The frozen anchor (TRUST_MODEL D20): a new fact's initial trust, the trust value the lifetime
|
|
32
|
+
* anchor is defined at (`daysToZero(BASE_TRUST) === base_lifetime_days`), and the divider between
|
|
33
|
+
* the two recall-reinforcement segments. One constant on purpose — the three uses must never be
|
|
34
|
+
* configurable apart, or "a new fact lives 90 active days" stops being true.
|
|
35
|
+
*/
|
|
36
|
+
export declare const BASE_TRUST = 0.5;
|
|
37
|
+
/** The decay shapes `decay_shape` accepts. */
|
|
38
|
+
export declare const DECAY_SHAPES: readonly ["linear", "accelerating"];
|
|
39
|
+
export type DecayShape = (typeof DECAY_SHAPES)[number];
|
|
40
|
+
/** The curve parameters, flattened from the `trust` config section (no config import: structural). */
|
|
41
|
+
export interface TrustCurve {
|
|
42
|
+
shape: DecayShape;
|
|
43
|
+
/** `decay_curvature` — the floor term `a` of the accelerating shape; unused by `linear`. */
|
|
44
|
+
curvature: number;
|
|
45
|
+
/** `base_lifetime_days` — the active days `BASE_TRUST → 0` takes. */
|
|
46
|
+
baseDays: number;
|
|
47
|
+
}
|
|
48
|
+
/** Read the curve out of the `trust` section (the single mapping from keys to math). */
|
|
49
|
+
export declare function curveOf(trust: {
|
|
50
|
+
decay_shape: DecayShape;
|
|
51
|
+
decay_curvature: number;
|
|
52
|
+
base_lifetime_days: number;
|
|
53
|
+
}): TrustCurve;
|
|
54
|
+
/**
|
|
55
|
+
* Trust units lost per active day — the LINEAR slope, `BASE_TRUST / base_lifetime_days`.
|
|
56
|
+
*
|
|
57
|
+
* `linear` is the only shape with a constant slope; for `accelerating` this returns the LOCAL slope
|
|
58
|
+
* at the anchor `T = BASE_TRUST` (`λ·(BASE_TRUST + a)`). It exists as the building block of the
|
|
59
|
+
* `linear` branch of {@link trustAfterDays} / {@link ceilingForDays} and for the tests that pin the
|
|
60
|
+
* anchor; it is deliberately NOT a scalar stand-in for the curve — anything that needs "how much
|
|
61
|
+
* trust is left after N days" uses {@link trustAfterDays}, and anything that needs "N days from a
|
|
62
|
+
* threshold" uses {@link ceilingForDays}. (An earlier caller — `trust_diagnose.forgetting_soon` —
|
|
63
|
+
* used it as a linear rate and was wrong once `accelerating` became the default.)
|
|
64
|
+
*/
|
|
65
|
+
export declare function slopeOf(shape: DecayShape, curvature: number, baseDays: number): number;
|
|
66
|
+
/**
|
|
67
|
+
* `λ = ln((1 + a) / (1 + a − BASE_TRUST)) / baseDays` — pinned so the anchor holds:
|
|
68
|
+
* `λ·baseDays = ln((1 + a)/(0.5 + a))`, i.e. `daysToZero(BASE_TRUST) === baseDays` for ANY `a`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function lambdaFor(curvature: number, baseDays: number): number;
|
|
71
|
+
/**
|
|
72
|
+
* `T − days·slope` (linear) or the closed form `(1 + a) − ((1 + a) − T)·e^(λ·days)`.
|
|
73
|
+
*
|
|
74
|
+
* UNCLAMPED on purpose: callers clamp to `[0, 1]` themselves (`effectiveTrust`'s read path and the
|
|
75
|
+
* settle write both must clamp, and a shared clamp here would hide a caller that forgot).
|
|
76
|
+
*/
|
|
77
|
+
export declare function trustAfterDays(T: number, days: number, cfg: TrustCurve): number;
|
|
78
|
+
/**
|
|
79
|
+
* Active days between two trust values, `t(T0 → T1)`.
|
|
80
|
+
*
|
|
81
|
+
* `linear`: `(T0 − T1) / slope`; `accelerating`: `ln((1 + a − T1)/(1 + a − T0)) / λ`. Both are
|
|
82
|
+
* signed the natural way (a RISING target yields a negative number of days), so the formula is
|
|
83
|
+
* usable as an inverse without special cases. Returns `NaN` only for an out-of-domain input
|
|
84
|
+
* (`T > 1 + a` on the accelerating branch), which the config validation makes unreachable.
|
|
85
|
+
*/
|
|
86
|
+
export declare function daysBetween(T0: number, T1: number, cfg: TrustCurve): number;
|
|
87
|
+
/** Active days until the value reaches 0 — `remainingDays`'s whole body. */
|
|
88
|
+
export declare function daysToZero(T: number, cfg: TrustCurve): number;
|
|
89
|
+
/**
|
|
90
|
+
* The trust ceiling a `max_retention_days` promises: after being reinforced up to `ceiling`, a fact
|
|
91
|
+
* survives `days` more active days.
|
|
92
|
+
*
|
|
93
|
+
* `linear`: `days · slope` (= `days / (2·baseDays)`); `accelerating`: `(1 + a)(1 − e^(−λ·days))`.
|
|
94
|
+
* This is an INVERSION (`daysToZero(ceilingForDays(R)) === R`), not a configuration value — the
|
|
95
|
+
* config has no `recall_ceiling` key any more (D16).
|
|
96
|
+
*/
|
|
97
|
+
export declare function ceilingForDays(days: number, cfg: TrustCurve): number;
|
|
98
|
+
/**
|
|
99
|
+
* The inverse of {@link ceilingForDays}: how many active days a given ceiling is worth. Used by the
|
|
100
|
+
* deprecated-key warning (`config/loader.ts`) to translate an old `recall_ceiling` value, and by the
|
|
101
|
+
* validation messages.
|
|
102
|
+
*/
|
|
103
|
+
export declare function retentionDaysForCeiling(ceiling: number, cfg: TrustCurve): number;
|
|
104
|
+
/**
|
|
105
|
+
* `R_max` of the accelerating shape — the days at which the ceiling reaches 1 (`ceiling → ∞`).
|
|
106
|
+
*
|
|
107
|
+
* R_max = baseDays · ln((1 + a)/a) / ln((1 + a)/(1 + a − BASE_TRUST))
|
|
108
|
+
*
|
|
109
|
+
* `max_retention_days` must stay strictly below it, or a reinforced fact would stop aging like
|
|
110
|
+
* `pinned` does (D14: permanent is set only by a person).
|
|
111
|
+
*/
|
|
112
|
+
export declare function RMAX(curvature: number, baseDays: number): number;
|
|
113
|
+
/**
|
|
114
|
+
* The shape-aware upper bound on `max_retention_days`: the days at which the ceiling hits 1.
|
|
115
|
+
*
|
|
116
|
+
* `linear`: `2·baseDays` (the ceiling is `days/(2·baseDays)`); `accelerating`: {@link RMAX}. Both
|
|
117
|
+
* are STRICT bounds — at the bound the ceiling is exactly 1, which `superRefine` rejects.
|
|
118
|
+
*/
|
|
119
|
+
export declare function retentionDaysLimit(shape: DecayShape, curvature: number, baseDays: number): number;
|
|
@@ -74,6 +74,25 @@ export type StatusFilter = (typeof STATUS_FILTERS)[number];
|
|
|
74
74
|
* SEARCH query shares the bound because it is compared against the stored title with `=`/`LIKE`.
|
|
75
75
|
*/
|
|
76
76
|
export declare const TITLE_MAX_CHARS = 120;
|
|
77
|
+
/**
|
|
78
|
+
* ── TWO KINDS OF TIME, TWO SHAPES (the "记忆时间显示不对" fix) ────────────────────────────────────
|
|
79
|
+
*
|
|
80
|
+
* Every INSTANT in a payload (`created_at`, `updated_at`, `archived_at`, `pinned_at`,
|
|
81
|
+
* `last_reinforced_at`, `bonus_window_at`, `detected_at`, the health stamp) is an ISO-8601 instant
|
|
82
|
+
* **with an explicit offset in the host's local zone** — `2026-10-28T19:58:48+08:00` — produced by
|
|
83
|
+
* `@avantf/mem-contract/time`'s `formatOffsetIso`. Storage is unchanged (SQLite `CURRENT_TIMESTAMP`
|
|
84
|
+
* is UTC, and the `facts_io` export/import/snapshot document keeps the SQLite text), so this is a
|
|
85
|
+
* RENDERING rule, not a storage one: the model gets an unambiguous instant it never has to convert,
|
|
86
|
+
* and a UI can `Date.parse` it directly.
|
|
87
|
+
*
|
|
88
|
+
* `event_date` / `valid_from` is the OTHER kind: a calendar DATE the caller wrote (`2026-10-28`),
|
|
89
|
+
* with no time and no zone. It is never passed through the instant renderer, and the two must not be
|
|
90
|
+
* confused: `created_at` answers "when was this recorded", `event_date` answers "as of which day is
|
|
91
|
+
* this true".
|
|
92
|
+
*
|
|
93
|
+
* A UI that shows these to a human formats them through `formatLocalHuman` (the same module): local
|
|
94
|
+
* wall clock to the minute, `2026-10-28 19:58`.
|
|
95
|
+
*/
|
|
77
96
|
export interface FactSummary {
|
|
78
97
|
fact_id: number;
|
|
79
98
|
content: string;
|
|
@@ -95,8 +114,15 @@ export interface FactSummary {
|
|
|
95
114
|
pinned: boolean;
|
|
96
115
|
/** Remaining ACTIVE days before `forgot`; null when pinned / non-active / trust disabled. */
|
|
97
116
|
remaining_days: number | null;
|
|
117
|
+
/**
|
|
118
|
+
* @deprecated No writer since `helpful` / `unhelpful` were removed (2026-10-28). The column stays
|
|
119
|
+
* because it is part of the export/import/snapshot format, so an old document still round-trips;
|
|
120
|
+
* the value is whatever the row last carried. Do not build new logic on it.
|
|
121
|
+
*/
|
|
98
122
|
helpful_count: number;
|
|
123
|
+
/** When the memory was first recorded — an INSTANT, ISO-8601 with the host's offset. */
|
|
99
124
|
created_at: string;
|
|
125
|
+
/** When it was archived — an INSTANT (ISO-8601 with offset), `null` while active. */
|
|
100
126
|
archived_at: string | null;
|
|
101
127
|
archive_reason: string | null;
|
|
102
128
|
}
|
|
@@ -152,15 +178,21 @@ export interface FactDetail extends FactSummary {
|
|
|
152
178
|
relations: FactRelation[];
|
|
153
179
|
/** Active-day clock value at the last settle. */
|
|
154
180
|
settle_clock: number;
|
|
181
|
+
/** When it was pinned — an INSTANT (ISO-8601 with offset), `null` when never pinned. */
|
|
155
182
|
pinned_at: string | null;
|
|
183
|
+
/** When trust was last raised by a recall — an INSTANT (ISO-8601 with offset). */
|
|
156
184
|
last_reinforced_at: string | null;
|
|
157
185
|
/**
|
|
158
186
|
* When this row was last CHANGED (edit/pin/unpin/reinforce/archive/restore). Retrieval and the
|
|
159
187
|
* daily settle do not write it, so it is not `last_retrieved_at`; `null` on a legacy row.
|
|
188
|
+
*
|
|
189
|
+
* An INSTANT (ISO-8601 with the host's offset) — compare it with `created_at`, never with
|
|
190
|
+
* `valid_from`, which is a calendar date.
|
|
160
191
|
*/
|
|
161
192
|
updated_at: string | null;
|
|
162
193
|
/** Effective reinforcement events in the current 24h window (recall + feedback). */
|
|
163
194
|
bonus_count: number;
|
|
195
|
+
/** Start of that window — an INSTANT (ISO-8601 with offset), `null` when no window is open. */
|
|
164
196
|
bonus_window_at: string | null;
|
|
165
197
|
/**
|
|
166
198
|
* P-08: where this fact came from, as recorded rows. Empty when the writer gave no `source_ref`
|
|
@@ -168,12 +200,13 @@ export interface FactDetail extends FactSummary {
|
|
|
168
200
|
*/
|
|
169
201
|
sources: FactSourceView[];
|
|
170
202
|
/**
|
|
171
|
-
* P-07: when the fact became true
|
|
172
|
-
*
|
|
173
|
-
*
|
|
203
|
+
* P-07/P-13: when the fact became true (the caller's `event_date`) — a CALENDAR DATE
|
|
204
|
+
* (`YYYY-MM-DD`), with no time and no zone, exactly as the caller wrote it. `null` = unknown. It is
|
|
205
|
+
* the `event` time axis — NOT a validity window: v2 removed the "known end" (`valid_until`)
|
|
206
|
+
* entirely, so "this is no longer true" is expressed by `update` or `archive`, never by a deadline
|
|
207
|
+
* field. Do not compare it with `created_at`/`updated_at`: those are instants.
|
|
174
208
|
*/
|
|
175
209
|
valid_from: string | null;
|
|
176
|
-
valid_to: string | null;
|
|
177
210
|
/**
|
|
178
211
|
* P-07: the fact that supersedes this one, DERIVED from the existing `supersedes_id` link
|
|
179
212
|
* (reverse lookup) — no redundant column. `null` when nothing supersedes it.
|
|
@@ -203,12 +236,15 @@ export interface RecallHit {
|
|
|
203
236
|
* When this hit was first recorded: a fact's revision chain keeps the ORIGINAL row's
|
|
204
237
|
* `created_at` across a supersede (`mem_remember update`), so this is when the memory was
|
|
205
238
|
* first asserted, not when its current text was written (see `updated_at`).
|
|
239
|
+
*
|
|
240
|
+
* An INSTANT: ISO-8601 with the host's explicit offset (`2026-10-28T19:58:48+08:00`), so no
|
|
241
|
+
* conversion is needed and it can never be mistaken for `valid_from`'s calendar date.
|
|
206
242
|
*/
|
|
207
243
|
created_at: string;
|
|
208
244
|
/**
|
|
209
245
|
* When this row was last CHANGED — an edit, pin/unpin, reinforcement, archive or restore.
|
|
210
246
|
* Retrieval and the daily settle deliberately do not write it, so it never means "last
|
|
211
|
-
* retrieved"; a copy can carry `null` on a legacy row.
|
|
247
|
+
* retrieved"; a copy can carry `null` on a legacy row. An INSTANT (ISO-8601 with offset).
|
|
212
248
|
*/
|
|
213
249
|
updated_at: string | null;
|
|
214
250
|
/**
|
|
@@ -235,12 +271,16 @@ export interface RecallHit {
|
|
|
235
271
|
*/
|
|
236
272
|
final?: number;
|
|
237
273
|
/**
|
|
238
|
-
* P-07/P-13: the fact's event time
|
|
239
|
-
*
|
|
240
|
-
*
|
|
274
|
+
* P-07/P-13: the fact's event time, serialized ONLY when non-null (the same convention as
|
|
275
|
+
* {@link RecallHit.truncated}). `null` means "unknown", and omitting the key keeps every pre-P-07
|
|
276
|
+
* envelope byte-identical. Display only — no leg filters on it.
|
|
277
|
+
*
|
|
278
|
+
* A CALENDAR DATE (`YYYY-MM-DD`), not an instant: the caller wrote it, it has no time and no zone,
|
|
279
|
+
* and it is deliberately NOT rendered in the host's zone. `created_at`/`updated_at` on the same
|
|
280
|
+
* hit are instants with an offset — the two answer different questions ("as of which day is this
|
|
281
|
+
* true" vs "when was it recorded").
|
|
241
282
|
*/
|
|
242
283
|
valid_from?: string;
|
|
243
|
-
valid_to?: string;
|
|
244
284
|
/**
|
|
245
285
|
* The fact's title, when it has one (journal entries normally do, notes usually do not).
|
|
246
286
|
*
|
|
@@ -277,9 +317,14 @@ export interface TitleCandidate {
|
|
|
277
317
|
matched_title: string;
|
|
278
318
|
memory_kind: MemoryKind;
|
|
279
319
|
status: FactStatus;
|
|
320
|
+
/** When the memory was first recorded — an INSTANT (ISO-8601 with the host's offset). */
|
|
280
321
|
created_at: string;
|
|
322
|
+
/** When it was last changed — an INSTANT (ISO-8601 with offset), `null` when never revised. */
|
|
281
323
|
updated_at: string | null;
|
|
282
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* The fact's event time (`valid_from`), when it has one — the journal axis that tells two entries
|
|
326
|
+
* apart. A CALENDAR DATE (`YYYY-MM-DD`), never an instant: `created_at` is the instant.
|
|
327
|
+
*/
|
|
283
328
|
event_date?: string;
|
|
284
329
|
/** The first characters of the content, for judging "same transaction or not"; absent when `include_excerpt` is false. */
|
|
285
330
|
excerpt?: string;
|
|
@@ -306,9 +351,20 @@ export type ExactSearchResult = ExactPageEnvelope & {
|
|
|
306
351
|
* The window IS the question: no literal pass, no relation plan, no fuzzy legs. `hits` are the
|
|
307
352
|
* active facts inside the window, newest first along `time.field`, and `score` is the same constant
|
|
308
353
|
* 1 the exact page reports — there is no relevance number here to compare with anything.
|
|
354
|
+
*
|
|
355
|
+
* A window is a LIST, so its page is bounded like every other page (default 50, cap 50) and the
|
|
356
|
+
* envelope has to say how much of the list is not on it: {@link TimeSearchResult.remaining} is that
|
|
357
|
+
* number, spelled out instead of left for the caller to derive from `truncated`.
|
|
309
358
|
*/
|
|
310
359
|
export type TimeSearchResult = ExactPageEnvelope & {
|
|
311
360
|
mode: 'time';
|
|
361
|
+
/**
|
|
362
|
+
* How many rows of the window this answer does NOT show: `total - (offset + hits.length)`, never
|
|
363
|
+
* negative. `0` means the window is exhausted (either it fits, or the caller reached its last
|
|
364
|
+
* page). It is the count a caller needs to continue — `truncated` alone says "there is more"
|
|
365
|
+
* without saying how much.
|
|
366
|
+
*/
|
|
367
|
+
remaining: number;
|
|
312
368
|
hits: RecallHit[];
|
|
313
369
|
};
|
|
314
370
|
/**
|
|
@@ -369,7 +425,9 @@ export interface RelationEvidence {
|
|
|
369
425
|
/** The memory layer, so a caller knows whether it is reading a journal or a note. */
|
|
370
426
|
memory_kind: MemoryKind;
|
|
371
427
|
title?: string;
|
|
428
|
+
/** When the memory was first recorded — an INSTANT (ISO-8601 with the host's offset). */
|
|
372
429
|
created_at: string;
|
|
430
|
+
/** When it was last changed — an INSTANT (ISO-8601 with offset), `null` when never revised. */
|
|
373
431
|
updated_at: string | null;
|
|
374
432
|
/** Set when the output budget shortened `text` (never when the evidence itself was dropped). */
|
|
375
433
|
truncated?: boolean;
|
|
@@ -436,9 +494,11 @@ export interface FactEvidence {
|
|
|
436
494
|
/** `active` for every row this stage returns; carried so the field is explicit, not implied. */
|
|
437
495
|
status: FactStatus;
|
|
438
496
|
title?: string;
|
|
439
|
-
/** The fact's event time (`valid_from`), when it has one. */
|
|
497
|
+
/** The fact's event time (`valid_from`) — a CALENDAR DATE (`YYYY-MM-DD`), when it has one. */
|
|
440
498
|
event_date?: string;
|
|
499
|
+
/** When the memory was first recorded — an INSTANT (ISO-8601 with the host's offset). */
|
|
441
500
|
created_at: string;
|
|
501
|
+
/** When it was last changed — an INSTANT (ISO-8601 with offset), `null` when never revised. */
|
|
442
502
|
updated_at: string | null;
|
|
443
503
|
/** Set when the output budget shortened `content` (never when the fact itself was dropped). */
|
|
444
504
|
truncated?: boolean;
|
|
@@ -591,6 +651,7 @@ export interface ContradictionRecord {
|
|
|
591
651
|
fact_a: number;
|
|
592
652
|
fact_b: number;
|
|
593
653
|
score: number;
|
|
654
|
+
/** When the pair was detected — an INSTANT (ISO-8601 with the host's offset). */
|
|
594
655
|
detected_at: string;
|
|
595
656
|
content_a: string;
|
|
596
657
|
content_b: string;
|
|
@@ -684,6 +745,7 @@ export interface RetrievalHealthSummary {
|
|
|
684
745
|
legs_capped: number;
|
|
685
746
|
/** Candidates removed by a leg's relevance floor (see `RetrievalHealth`). */
|
|
686
747
|
candidates_dropped_by_floor: number;
|
|
748
|
+
/** When a counter last moved — an INSTANT (ISO-8601 with the host's offset), `null` before any. */
|
|
687
749
|
updated_at: string | null;
|
|
688
750
|
by_kind: Record<string, KindHealth>;
|
|
689
751
|
}
|
|
@@ -777,7 +839,6 @@ export interface TrustDiagnostic {
|
|
|
777
839
|
forgetting_soon: number;
|
|
778
840
|
reinforced_today: number;
|
|
779
841
|
bonus_granted_today: number;
|
|
780
|
-
idle_candidates: number;
|
|
781
842
|
archived_by_reason: Record<string, number>;
|
|
782
843
|
oldest_settle_clock: number | null;
|
|
783
844
|
/**
|