@miller-tech/uap 1.175.13 → 1.178.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/dist/.tsbuildinfo +1 -1
- package/dist/benchmarks/harness-card.d.ts +86 -0
- package/dist/benchmarks/harness-card.d.ts.map +1 -0
- package/dist/benchmarks/harness-card.js +153 -0
- package/dist/benchmarks/harness-card.js.map +1 -0
- package/dist/benchmarks/paired/report.d.ts +15 -0
- package/dist/benchmarks/paired/report.d.ts.map +1 -1
- package/dist/benchmarks/paired/report.js +12 -0
- package/dist/benchmarks/paired/report.js.map +1 -1
- package/dist/benchmarks/paired/types.d.ts +1 -1
- package/dist/bin/cli.js +27 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/cli/bench.d.ts.map +1 -1
- package/dist/cli/bench.js +64 -3
- package/dist/cli/bench.js.map +1 -1
- package/dist/cli/harness.d.ts +3 -2
- package/dist/cli/harness.d.ts.map +1 -1
- package/dist/cli/harness.js +81 -0
- package/dist/cli/harness.js.map +1 -1
- package/dist/cli/memory.d.ts +7 -1
- package/dist/cli/memory.d.ts.map +1 -1
- package/dist/cli/memory.js +165 -0
- package/dist/cli/memory.js.map +1 -1
- package/dist/delivery/agentic-executor.d.ts +76 -2
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +231 -29
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/dist/delivery/edit-match.d.ts +134 -0
- package/dist/delivery/edit-match.d.ts.map +1 -0
- package/dist/delivery/edit-match.js +331 -0
- package/dist/delivery/edit-match.js.map +1 -0
- package/dist/memory/dynamic-retrieval.d.ts.map +1 -1
- package/dist/memory/dynamic-retrieval.js +57 -0
- package/dist/memory/dynamic-retrieval.js.map +1 -1
- package/dist/memory/reconstruct-ingest.d.ts +88 -0
- package/dist/memory/reconstruct-ingest.d.ts.map +1 -0
- package/dist/memory/reconstruct-ingest.js +165 -0
- package/dist/memory/reconstruct-ingest.js.map +1 -0
- package/dist/memory/reconstruct-store.d.ts +143 -0
- package/dist/memory/reconstruct-store.d.ts.map +1 -0
- package/dist/memory/reconstruct-store.js +312 -0
- package/dist/memory/reconstruct-store.js.map +1 -0
- package/dist/memory/reconstruct.d.ts +272 -0
- package/dist/memory/reconstruct.d.ts.map +1 -0
- package/dist/memory/reconstruct.js +552 -0
- package/dist/memory/reconstruct.js.map +1 -0
- package/dist/policies/schemas/policy.d.ts +2 -2
- package/dist/self-harness/evidence-proposer.d.ts +58 -0
- package/dist/self-harness/evidence-proposer.d.ts.map +1 -0
- package/dist/self-harness/evidence-proposer.js +132 -0
- package/dist/self-harness/evidence-proposer.js.map +1 -0
- package/dist/self-harness/manifest.d.ts +130 -0
- package/dist/self-harness/manifest.d.ts.map +1 -0
- package/dist/self-harness/manifest.js +247 -0
- package/dist/self-harness/manifest.js.map +1 -0
- package/dist/self-harness/mods.d.ts +57 -3
- package/dist/self-harness/mods.d.ts.map +1 -1
- package/dist/self-harness/mods.js +72 -0
- package/dist/self-harness/mods.js.map +1 -1
- package/dist/self-harness/orchestrator.d.ts +27 -0
- package/dist/self-harness/orchestrator.d.ts.map +1 -1
- package/dist/self-harness/orchestrator.js +47 -4
- package/dist/self-harness/orchestrator.js.map +1 -1
- package/dist/self-harness/pending.d.ts +17 -3
- package/dist/self-harness/pending.d.ts.map +1 -1
- package/dist/self-harness/pending.js +26 -4
- package/dist/self-harness/pending.js.map +1 -1
- package/dist/self-harness/profile.d.ts +17 -1
- package/dist/self-harness/profile.d.ts.map +1 -1
- package/dist/self-harness/profile.js +11 -4
- package/dist/self-harness/profile.js.map +1 -1
- package/dist/self-harness/run.d.ts +19 -0
- package/dist/self-harness/run.d.ts.map +1 -1
- package/dist/self-harness/run.js +43 -4
- package/dist/self-harness/run.js.map +1 -1
- package/dist/self-harness/transfer.d.ts.map +1 -1
- package/dist/self-harness/transfer.js +2 -0
- package/dist/self-harness/transfer.js.map +1 -1
- package/dist/self-harness/validate.d.ts +17 -6
- package/dist/self-harness/validate.d.ts.map +1 -1
- package/dist/self-harness/validate.js +73 -8
- package/dist/self-harness/validate.js.map +1 -1
- package/dist/telemetry/tool-calls.d.ts +82 -0
- package/dist/telemetry/tool-calls.d.ts.map +1 -0
- package/dist/telemetry/tool-calls.js +280 -0
- package/dist/telemetry/tool-calls.js.map +1 -0
- package/dist/telemetry/tool-failure.d.ts +71 -0
- package/dist/telemetry/tool-failure.d.ts.map +1 -0
- package/dist/telemetry/tool-failure.js +123 -0
- package/dist/telemetry/tool-failure.js.map +1 -0
- package/docs/plans/harness-engineering-uplift-2026-07-31.md +280 -0
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiring active reconstruction to UAP's real memory stores (harness plan E3).
|
|
3
|
+
*
|
|
4
|
+
* Resolves where the graph lives, fills it from the stores UAP already writes
|
|
5
|
+
* (short-term SQLite AND the long-term semantic store), and exposes the entry
|
|
6
|
+
* point callers use: `recallActive`.
|
|
7
|
+
*
|
|
8
|
+
* The graph is a DERIVED index, never a source of truth: it can be deleted and
|
|
9
|
+
* rebuilt at any time. (It is not side-effect free, though — opening the
|
|
10
|
+
* short-term store runs `ensureShortTermSchema`, which creates indexes and can
|
|
11
|
+
* backfill FTS. That means a build needs a writable store.)
|
|
12
|
+
*
|
|
13
|
+
* COVERAGE IS THE CORRECTNESS PROPERTY HERE. The passive path queries short-term
|
|
14
|
+
* FTS *and* the long-term semantic store. If active recall indexed only the
|
|
15
|
+
* short-term store — a rolling ~50-entry window — then routing a query here
|
|
16
|
+
* would silently drop every durable memory the project has, which is a
|
|
17
|
+
* regression wearing a feature's clothes. So ingestion covers both tiers, and
|
|
18
|
+
* `recallActive` reports what it covered.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync } from 'node:fs';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { loadUapConfig } from '../utils/config-loader.js';
|
|
23
|
+
import { MemoryGraph, reconstruct, heuristicPolicy, activeReconstructionEnabled, } from './reconstruct.js';
|
|
24
|
+
import { ingestItems, itemKey } from './reconstruct-ingest.js';
|
|
25
|
+
/** Where the derived graph lives, alongside the stores it indexes. */
|
|
26
|
+
export function memoryGraphPath(cwd) {
|
|
27
|
+
return join(cwd, 'agents', 'data', 'memory', 'memory-graph.db');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve short-term store + project id the way `uap memory query` does.
|
|
31
|
+
*
|
|
32
|
+
* Reading these from the config rather than hardcoding defaults matters: a
|
|
33
|
+
* project with a configured store path, or any project not literally named
|
|
34
|
+
* "project", was otherwise indexing zero rows while reporting success — because
|
|
35
|
+
* `getRecent` filters on `project_id`.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveStoreConfig(cwd, opts = {}) {
|
|
38
|
+
const config = loadUapConfig(cwd);
|
|
39
|
+
return {
|
|
40
|
+
shortTermPath: opts.shortTermPath ||
|
|
41
|
+
config?.memory?.shortTerm?.path ||
|
|
42
|
+
join(cwd, 'agents', 'data', 'memory', 'short_term.db'),
|
|
43
|
+
projectId: opts.projectId ?? config?.project?.name ?? 'project',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function openMemoryGraph(cwd) {
|
|
47
|
+
return new MemoryGraph(memoryGraphPath(cwd));
|
|
48
|
+
}
|
|
49
|
+
export function memoryGraphExists(cwd) {
|
|
50
|
+
return existsSync(memoryGraphPath(cwd));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read UAP's short-term store into `IngestItem`s.
|
|
54
|
+
*
|
|
55
|
+
* Isolated and best-effort: a missing or unreadable store yields an empty list
|
|
56
|
+
* rather than failing the build, because the graph is derived and a partial
|
|
57
|
+
* index is more useful than none.
|
|
58
|
+
*/
|
|
59
|
+
export async function itemsFromShortTerm(cwd, opts = {}) {
|
|
60
|
+
const { shortTermPath: dbPath, projectId } = resolveStoreConfig(cwd, opts);
|
|
61
|
+
if (!existsSync(dbPath))
|
|
62
|
+
return [];
|
|
63
|
+
const { SQLiteShortTermMemory } = await import('./short-term/sqlite.js');
|
|
64
|
+
let store = null;
|
|
65
|
+
try {
|
|
66
|
+
store = new SQLiteShortTermMemory({
|
|
67
|
+
dbPath,
|
|
68
|
+
projectId,
|
|
69
|
+
maxEntries: opts.limit ?? 2000,
|
|
70
|
+
});
|
|
71
|
+
const rows = (await store.getRecent(opts.limit ?? 2000)) ?? [];
|
|
72
|
+
return rows.map((r) => ({
|
|
73
|
+
id: `st:${r.id ?? itemKey({ text: r.content })}`,
|
|
74
|
+
text: r.content,
|
|
75
|
+
type: r.type,
|
|
76
|
+
occurredAt: r.timestamp,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
// A throw in getRecent used to skip close(), leaking the handle (plus WAL and
|
|
84
|
+
// SHM refs) once per query, since recall refreshes on every call.
|
|
85
|
+
try {
|
|
86
|
+
await store?.close();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* ignore */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Read the LONG-TERM semantic store into ingest items.
|
|
95
|
+
*
|
|
96
|
+
* This tier holds everything durable — `memory store`, `sync-files`,
|
|
97
|
+
* `prepopulate`. Omitting it would make active recall a strict downgrade from
|
|
98
|
+
* the passive path it replaces. Best-effort: an unreachable Qdrant yields [] and
|
|
99
|
+
* the caller reports reduced coverage rather than failing.
|
|
100
|
+
*/
|
|
101
|
+
export async function itemsFromLongTerm(cwd, limit = 2000) {
|
|
102
|
+
try {
|
|
103
|
+
const config = loadUapConfig(cwd);
|
|
104
|
+
const endpoint = config?.memory?.longTerm?.endpoint || 'localhost:6333';
|
|
105
|
+
const url = /^https?:\/\//.test(endpoint) ? endpoint : `http://${endpoint}`;
|
|
106
|
+
const collection = config?.memory?.longTerm?.collection || 'agent_memory';
|
|
107
|
+
const apiKey = config?.memory?.longTerm?.qdrantCloud?.apiKey || process.env.QDRANT_API_KEY;
|
|
108
|
+
const { getQdrantClientClass } = await import('../utils/lazy-imports.js');
|
|
109
|
+
const QdrantClientClass = await getQdrantClientClass();
|
|
110
|
+
// The client defaults to a 300_000ms timeout. On the AGENT path this call
|
|
111
|
+
// is awaited during context assembly, so a black-holed endpoint (firewall
|
|
112
|
+
// drop, not a fast ECONNREFUSED) would block a model turn for five minutes.
|
|
113
|
+
const timeout = Number(process.env.UAP_MEMORY_QDRANT_TIMEOUT_MS) || 3000;
|
|
114
|
+
const client = new QdrantClientClass({ url, apiKey, checkCompatibility: false, timeout });
|
|
115
|
+
const collections = await client.getCollections();
|
|
116
|
+
const names = [collection, `${collection}_prepopulated`].filter((c) => collections.collections.some((col) => col.name === c));
|
|
117
|
+
const items = [];
|
|
118
|
+
for (const name of names) {
|
|
119
|
+
// Scroll the payloads only — the graph indexes text, not vectors.
|
|
120
|
+
let offset = undefined;
|
|
121
|
+
while (items.length < limit) {
|
|
122
|
+
const page = await client.scroll(name, {
|
|
123
|
+
limit: Math.min(256, limit - items.length),
|
|
124
|
+
with_payload: true,
|
|
125
|
+
with_vector: false,
|
|
126
|
+
offset,
|
|
127
|
+
});
|
|
128
|
+
for (const point of page.points ?? []) {
|
|
129
|
+
const payload = (point.payload ?? {});
|
|
130
|
+
const text = String(payload.content ?? '');
|
|
131
|
+
if (!text)
|
|
132
|
+
continue;
|
|
133
|
+
items.push({
|
|
134
|
+
id: `lt:${name}:${String(point.id)}`,
|
|
135
|
+
text,
|
|
136
|
+
type: String(payload.type ?? 'knowledge'),
|
|
137
|
+
tags: Array.isArray(payload.tags) ? payload.tags : undefined,
|
|
138
|
+
occurredAt: typeof payload.timestamp === 'string' ? payload.timestamp : undefined,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
offset = page.next_page_offset;
|
|
142
|
+
if (!offset)
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return items;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Build or refresh the graph from the stores.
|
|
154
|
+
*
|
|
155
|
+
* Incremental by default: source keys already ingested are skipped, so this is
|
|
156
|
+
* cheap to re-run and safe to call before a query.
|
|
157
|
+
*/
|
|
158
|
+
export async function buildMemoryGraph(cwd, opts = {}) {
|
|
159
|
+
const graph = openMemoryGraph(cwd);
|
|
160
|
+
try {
|
|
161
|
+
if (!graph.acquireBuildLock(Date.now())) {
|
|
162
|
+
// Another process is building. Duplicating that work would insert every
|
|
163
|
+
// item a second time under fresh ids — permanent, since the ledger then
|
|
164
|
+
// reports them indexed.
|
|
165
|
+
return {
|
|
166
|
+
ingested: 0, skipped: 0, storedKeys: [], cues: 0, tags: 0, bridgingTags: 0,
|
|
167
|
+
considered: 0, graphPath: memoryGraphPath(cwd),
|
|
168
|
+
coverage: { shortTerm: 0, longTerm: 0 },
|
|
169
|
+
skippedConcurrentBuild: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const shortTerm = await itemsFromShortTerm(cwd, opts);
|
|
173
|
+
const longTerm = opts.includeLongTerm === false ? [] : await itemsFromLongTerm(cwd, opts.limit);
|
|
174
|
+
const items = [...shortTerm, ...longTerm, ...(opts.extra ?? [])];
|
|
175
|
+
if (opts.rebuild) {
|
|
176
|
+
// A real rebuild TRUNCATES. Clearing only the in-memory dedupe set left
|
|
177
|
+
// every row in place and re-inserted the whole corpus under fresh ids, so
|
|
178
|
+
// two rebuilds meant every memory twice — permanently, since the ledger
|
|
179
|
+
// then reported them as already indexed.
|
|
180
|
+
graph.clear();
|
|
181
|
+
}
|
|
182
|
+
const seen = opts.rebuild ? new Set() : graph.ingestedKeys();
|
|
183
|
+
const result = ingestItems(graph, items, { seen });
|
|
184
|
+
// Mark ONLY what was stored — see IngestResult.storedKeys.
|
|
185
|
+
graph.markIngested(result.storedKeys);
|
|
186
|
+
// Heartbeat REGARDLESS of whether anything was stored. Deriving staleness
|
|
187
|
+
// from stored keys means a refresh that finds nothing new never advances the
|
|
188
|
+
// clock, so the TTL never clears and the index rebuilds every retrieval.
|
|
189
|
+
graph.markRefreshed(new Date().toISOString());
|
|
190
|
+
return {
|
|
191
|
+
...result,
|
|
192
|
+
considered: items.length,
|
|
193
|
+
graphPath: memoryGraphPath(cwd),
|
|
194
|
+
coverage: { shortTerm: shortTerm.length, longTerm: longTerm.length },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
graph.releaseBuildLock();
|
|
199
|
+
graph.close();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Recall by ACTIVE RECONSTRUCTION: traverse the graph while reasoning, pruning
|
|
204
|
+
* weak paths before they cost context, instead of one passive top-k pull.
|
|
205
|
+
*
|
|
206
|
+
* This is the caller area E was missing. It is invoked by `uap memory query
|
|
207
|
+
* --active` and by anything that opts in; passive retrieval remains the default
|
|
208
|
+
* until the paired bench measures the lift on our own suite.
|
|
209
|
+
*/
|
|
210
|
+
export async function recallActive(cwd, query, opts = {}) {
|
|
211
|
+
const build = (await shouldRefresh(cwd, opts))
|
|
212
|
+
? await buildMemoryGraph(cwd, opts.buildOptions)
|
|
213
|
+
: undefined;
|
|
214
|
+
const graph = openMemoryGraph(cwd);
|
|
215
|
+
try {
|
|
216
|
+
if (graph.isEmpty()) {
|
|
217
|
+
return {
|
|
218
|
+
context: [],
|
|
219
|
+
steps: [],
|
|
220
|
+
converged: false,
|
|
221
|
+
stopReason: 'exhausted',
|
|
222
|
+
graphEmpty: true,
|
|
223
|
+
build,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const result = reconstruct(graph, query, {
|
|
227
|
+
policy: opts.policy ?? heuristicPolicy(),
|
|
228
|
+
maxSteps: opts.maxSteps,
|
|
229
|
+
maxContext: opts.maxContext,
|
|
230
|
+
});
|
|
231
|
+
return { ...result, graphEmpty: false, build };
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
graph.close();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/** Decide whether this recall should rebuild the index first. */
|
|
238
|
+
async function shouldRefresh(cwd, opts) {
|
|
239
|
+
if (opts.refresh === false)
|
|
240
|
+
return false;
|
|
241
|
+
if (opts.refresh !== 'if-stale')
|
|
242
|
+
return true;
|
|
243
|
+
if (!memoryGraphExists(cwd))
|
|
244
|
+
return true;
|
|
245
|
+
const graph = openMemoryGraph(cwd);
|
|
246
|
+
try {
|
|
247
|
+
const last = graph.lastRefreshedAt() ?? graph.lastIngestedAt();
|
|
248
|
+
if (!last)
|
|
249
|
+
return true;
|
|
250
|
+
// SQLite datetime('now') is UTC without a zone marker; parse it as such.
|
|
251
|
+
const lastMs = Date.parse(last.endsWith('Z') ? last : `${last.replace(' ', 'T')}Z`);
|
|
252
|
+
if (!Number.isFinite(lastMs))
|
|
253
|
+
return true;
|
|
254
|
+
const ttlMs = (opts.staleAfterSeconds ?? 900) * 1000;
|
|
255
|
+
return Date.now() - lastMs > ttlMs;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
graph.close();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Should a caller use active reconstruction?
|
|
266
|
+
*
|
|
267
|
+
* Both conditions must hold: the operator opted in AND the graph actually has
|
|
268
|
+
* content. Routing to an empty graph would silently return nothing where
|
|
269
|
+
* passive retrieval would have answered — a regression dressed as a feature.
|
|
270
|
+
*/
|
|
271
|
+
export function shouldUseActiveRecall(cwd, graph) {
|
|
272
|
+
if (!activeReconstructionEnabled())
|
|
273
|
+
return false;
|
|
274
|
+
if (!memoryGraphExists(cwd))
|
|
275
|
+
return false;
|
|
276
|
+
if (graph) {
|
|
277
|
+
try {
|
|
278
|
+
return !graph.isEmpty();
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const owned = openMemoryGraph(cwd);
|
|
285
|
+
const graphRef = owned;
|
|
286
|
+
try {
|
|
287
|
+
// Cheap probe, not stats(): this runs on EVERY `uap memory query` and every
|
|
288
|
+
// harness-card render, and stats() is five aggregates over the triples table.
|
|
289
|
+
return !graphRef.isEmpty();
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
owned.close();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* The retrieval mode actually in force, for the harness disclosure card.
|
|
300
|
+
*
|
|
301
|
+
* One implementation so the bench card and `uap harness card` cannot disagree —
|
|
302
|
+
* two call sites computing this independently is how a card starts lying.
|
|
303
|
+
*/
|
|
304
|
+
export function describeMemoryMode(cwd) {
|
|
305
|
+
try {
|
|
306
|
+
return shouldUseActiveRecall(cwd) ? 'active reconstruction' : 'semantic retrieval';
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return 'semantic retrieval';
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=reconstruct-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconstruct-store.js","sourceRoot":"","sources":["../../src/memory/reconstruct-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EACL,WAAW,EACX,WAAW,EACX,eAAe,EACf,2BAA2B,GAG5B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,OAAO,EAAsC,MAAM,yBAAyB,CAAC;AAEnG,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAW,EACX,OAAqB,EAAE;IAEvB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO;QACL,aAAa,EACX,IAAI,CAAC,aAAa;YAClB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI;YAC/B,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,CAAC;QACxD,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,MAAM,EAAE,OAAO,EAAE,IAAI,IAAI,SAAS;KAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,IAAI,WAAW,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AA+BD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAW,EACX,OAAqB,EAAE;IAEvB,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3E,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACzE,IAAI,KAAK,GAAsD,IAAI,CAAC;IACpE,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,qBAAqB,CAAC;YAChC,MAAM;YACN,SAAS;YACT,UAAU,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;SAC/B,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE;YAChD,IAAI,EAAE,CAAC,CAAC,OAAO;YACf,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,UAAU,EAAE,CAAC,CAAC,SAAS;SACxB,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,8EAA8E;QAC9E,kEAAkE;QAClE,IAAI,CAAC;YACH,MAAM,KAAK,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAW,EAAE,KAAK,GAAG,IAAI;IAC/D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,IAAI,gBAAgB,CAAC;QACxE,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,QAAQ,EAAE,CAAC;QAC5E,MAAM,UAAU,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,IAAI,cAAc,CAAC;QAC1E,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAE3F,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;QAC1E,MAAM,iBAAiB,GAAG,MAAM,oBAAoB,EAAE,CAAC;QACvD,0EAA0E;QAC1E,0EAA0E;QAC1E,4EAA4E;QAC5E,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,IAAI,IAAI,CAAC;QACzE,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1F,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,CAAC,UAAU,EAAE,GAAG,UAAU,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAC5E,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,GAAqB,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CACxE,CAAC;QAEF,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,kEAAkE;YAClE,IAAI,MAAM,GAAiE,SAAS,CAAC;YACrF,OAAO,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;gBAC5B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;oBACrC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;oBAC1C,YAAY,EAAE,IAAI;oBAClB,WAAW,EAAE,KAAK;oBAClB,MAAM;iBACP,CAAC,CAAC;gBACH,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;oBACtC,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAA4B,CAAC;oBACjE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;oBAC3C,IAAI,CAAC,IAAI;wBAAE,SAAS;oBACpB,KAAK,CAAC,IAAI,CAAC;wBACT,EAAE,EAAE,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;wBACpC,IAAI;wBACJ,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;wBACzC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,OAAO,CAAC,IAAiB,CAAC,CAAC,CAAC,SAAS;wBAC1E,UAAU,EAAE,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;qBAClF,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,GAAG,IAAI,CAAC,gBAAiC,CAAC;gBAChD,IAAI,CAAC,MAAM;oBAAE,MAAM;YACrB,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,OAAqB,EAAE;IAEvB,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;YACxC,wEAAwE;YACxE,wEAAwE;YACxE,wBAAwB;YACxB,OAAO;gBACL,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;gBAC1E,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,eAAe,CAAC,GAAG,CAAC;gBAC9C,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE;gBACvC,sBAAsB,EAAE,IAAI;aAC7B,CAAC;QACJ,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAChG,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,wEAAwE;YACxE,0EAA0E;YAC1E,wEAAwE;YACxE,yCAAyC;YACzC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,EAAU,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QACrE,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,2DAA2D;QAC3D,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtC,0EAA0E;QAC1E,6EAA6E;QAC7E,yEAAyE;QACzE,KAAK,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9C,OAAO;YACL,GAAG,MAAM;YACT,UAAU,EAAE,KAAK,CAAC,MAAM;YACxB,SAAS,EAAE,eAAe,CAAC,GAAG,CAAC;YAC/B,QAAQ,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE;SACrE,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,gBAAgB,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACH,CAAC;AA+BD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,KAAa,EACb,OAAsB,EAAE;IAExB,MAAM,KAAK,GAAG,CAAC,MAAM,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5C,CAAC,CAAC,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC;QAChD,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,EAAE;gBACT,SAAS,EAAE,KAAK;gBAChB,UAAU,EAAE,WAAW;gBACvB,UAAU,EAAE,IAAI;gBAChB,KAAK;aACN,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE;YACvC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,eAAe,EAAE;YACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CAAC;QACH,OAAO,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACH,CAAC;AAED,iEAAiE;AACjE,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,IAAmB;IAC3D,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QAC/D,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,iBAAiB,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC;QACrD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,KAAmB;IACpE,IAAI,CAAC,2BAA2B,EAAE;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,KAAK,CAAC;IACvB,IAAI,CAAC;QACH,4EAA4E;QAC5E,8EAA8E;QAC9E,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,CAAC;QACH,OAAO,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,oBAAoB,CAAC;IAC9B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active memory reconstruction over a Cue–Tag–Content graph
|
|
3
|
+
* (harness plan area E, 2026-07-31).
|
|
4
|
+
*
|
|
5
|
+
* UAP's memory retrieval is passive: embed the query, pull the top-k, reason
|
|
6
|
+
* once. "Memory is Reconstructed, Not Retrieved" (arXiv 2606.06036) shows that
|
|
7
|
+
* paradigm is strictly less expressive than letting the model traverse memory
|
|
8
|
+
* WHILE it reasons — it proves active policies solve a binary-tree
|
|
9
|
+
* needle-in-a-haystack with zero error where passive policies need an
|
|
10
|
+
* exponential budget — and measures the practical gap at up to +23% on LoCoMo
|
|
11
|
+
* with 118k tokens per query against A-Mem's 632k and LangMem's 3.26M.
|
|
12
|
+
*
|
|
13
|
+
* THE TAG LAYER IS THE CONTRIBUTION. Their ablation: cue -> content direct
|
|
14
|
+
* indexing recalls ~65%; cue -> TAG -> content with multi-turn reasoning recalls
|
|
15
|
+
* ~90%. A "graph memory" that skips the associative tag bridge gets none of the
|
|
16
|
+
* benefit, so the schema here makes tags mandatory rather than an optimisation.
|
|
17
|
+
*
|
|
18
|
+
* Off by default. The literature's numbers are theirs, not ours: this ships
|
|
19
|
+
* behind `UAP_MEMORY_ACTIVE=1` until `uap bench paired` measures the lift on our
|
|
20
|
+
* own suite (harness plan constraint 3).
|
|
21
|
+
*/
|
|
22
|
+
/** Which memory layer a content node lives in (MRAgent's three granularities). */
|
|
23
|
+
export type MemoryLayer =
|
|
24
|
+
/** Event-specific, temporally ordered. */
|
|
25
|
+
'episodic'
|
|
26
|
+
/** Stable facts: preferences, attributes, decisions. */
|
|
27
|
+
| 'semantic'
|
|
28
|
+
/** Topic nodes summarising recurring patterns — the top-down entry point. */
|
|
29
|
+
| 'abstraction';
|
|
30
|
+
export interface ContentNode {
|
|
31
|
+
id: number;
|
|
32
|
+
layer: MemoryLayer;
|
|
33
|
+
text: string;
|
|
34
|
+
/** ISO timestamp of the source episode, when known. */
|
|
35
|
+
occurredAt?: string;
|
|
36
|
+
}
|
|
37
|
+
/** One (cue, tag, content) triple — the graph's only edge type. */
|
|
38
|
+
export interface Triple {
|
|
39
|
+
cue: string;
|
|
40
|
+
tag: string;
|
|
41
|
+
contentId: number;
|
|
42
|
+
}
|
|
43
|
+
export interface ReconstructionStep {
|
|
44
|
+
/** 1-based step index. */
|
|
45
|
+
step: number;
|
|
46
|
+
/** Cues the model chose to expand this step. */
|
|
47
|
+
expandedCues: string[];
|
|
48
|
+
/** Tags reached from those cues. */
|
|
49
|
+
tags: string[];
|
|
50
|
+
/** Content ids admitted into the reconstructed context. */
|
|
51
|
+
kept: number[];
|
|
52
|
+
/** Content ids reached but judged IRRELEVANT. */
|
|
53
|
+
pruned: number[];
|
|
54
|
+
/**
|
|
55
|
+
* Content ids the policy kept but that did not fit the context budget.
|
|
56
|
+
* Separate from `pruned`: "judged irrelevant" and "budget full" are different
|
|
57
|
+
* outcomes and want different remedies.
|
|
58
|
+
*/
|
|
59
|
+
dropped: number[];
|
|
60
|
+
/** Whether the model declared the evidence sufficient after this step. */
|
|
61
|
+
sufficient: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface ReconstructionResult {
|
|
64
|
+
/** Content, in the order it was admitted — this is what goes to the model. */
|
|
65
|
+
context: ContentNode[];
|
|
66
|
+
steps: ReconstructionStep[];
|
|
67
|
+
/** True when the loop stopped because evidence sufficed, not because it ran out of steps. */
|
|
68
|
+
converged: boolean;
|
|
69
|
+
/** Why the traversal stopped, so callers can give accurate advice. */
|
|
70
|
+
stopReason: 'sufficient' | 'context-full' | 'exhausted' | 'step-budget';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The reasoning seam. Injected so the traversal policy is testable without a
|
|
74
|
+
* model, and so the same graph can be driven by a local or a frontier model.
|
|
75
|
+
*/
|
|
76
|
+
export interface ReconstructionPolicy {
|
|
77
|
+
/**
|
|
78
|
+
* Choose which of the currently-active cues to expand next. Returning [] ends
|
|
79
|
+
* the traversal.
|
|
80
|
+
*/
|
|
81
|
+
selectCues(query: string, activeCues: string[], context: ContentNode[]): string[];
|
|
82
|
+
/**
|
|
83
|
+
* Semantic routing: which reached contents are worth keeping, and is the
|
|
84
|
+
* accumulated evidence now enough to answer?
|
|
85
|
+
*
|
|
86
|
+
* `hops` carries how each candidate was reached. A router that ignores it can
|
|
87
|
+
* only judge lexical similarity to the query — and then the tag bridge, which
|
|
88
|
+
* is the whole point of the structure, buys nothing: the traversal reaches the
|
|
89
|
+
* associated memory and the router throws it away for sharing no words with
|
|
90
|
+
* the question.
|
|
91
|
+
*/
|
|
92
|
+
route(query: string, candidates: ContentNode[], context: ContentNode[], hops?: HopContext): RouteDecision;
|
|
93
|
+
}
|
|
94
|
+
export interface RouteDecision {
|
|
95
|
+
keep: number[];
|
|
96
|
+
sufficient: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Candidates declined ONLY because a per-step cap was reached, not on the
|
|
99
|
+
* merits. The traversal keeps these reachable by a later, stronger bridge
|
|
100
|
+
* instead of blacklisting them like a genuine rejection.
|
|
101
|
+
*/
|
|
102
|
+
deferred?: number[];
|
|
103
|
+
}
|
|
104
|
+
/** How the traversal reached this step's candidates. */
|
|
105
|
+
export interface HopContext {
|
|
106
|
+
/** Candidate id -> the tags it was reached through. */
|
|
107
|
+
tagsFor: Map<number, string[]>;
|
|
108
|
+
/**
|
|
109
|
+
* Tags that have produced admitted evidence in ANY prior step — accumulated,
|
|
110
|
+
* not per-step. A bridge proves itself once; requiring it to re-prove itself
|
|
111
|
+
* every step is what stops a second hop from ever landing, since by
|
|
112
|
+
* definition the far side of a bridge does not resemble the query.
|
|
113
|
+
*/
|
|
114
|
+
productiveTags: ReadonlySet<string>;
|
|
115
|
+
/**
|
|
116
|
+
* How many contents each tag links. Specificity is the honest discriminator
|
|
117
|
+
* for an associative admission: a tag on 3 memories asserts a real relation, a
|
|
118
|
+
* tag on 300 asserts almost nothing. Lexical overlap cannot serve here — the
|
|
119
|
+
* far side of a bridge shares no words with the query by construction.
|
|
120
|
+
*/
|
|
121
|
+
tagSize: (tag: string) => number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Zero-LLM policy: lexical overlap for routing, breadth-first for expansion.
|
|
125
|
+
*
|
|
126
|
+
* Not a toy — it is the honest default. An active traversal driven by a cheap
|
|
127
|
+
* heuristic is still active (it prunes on accumulated evidence, which is the
|
|
128
|
+
* mechanism the paper isolates); wiring an LLM in raises the ceiling but is not
|
|
129
|
+
* required for the structure to pay off, and a deterministic policy is what
|
|
130
|
+
* makes the loop unit-testable.
|
|
131
|
+
*/
|
|
132
|
+
export declare function heuristicPolicy(opts?: {
|
|
133
|
+
keepThreshold?: number;
|
|
134
|
+
sufficientAt?: number;
|
|
135
|
+
maxExpandPerStep?: number;
|
|
136
|
+
/** Max tag-bridged admissions per step. 0 disables associative recall. */
|
|
137
|
+
maxAssociativePerStep?: number;
|
|
138
|
+
/**
|
|
139
|
+
* A tag linking more contents than this is too broad to justify admitting a
|
|
140
|
+
* candidate that has no lexical relation to the query at all.
|
|
141
|
+
*/
|
|
142
|
+
maxBridgeTagSize?: number;
|
|
143
|
+
}): ReconstructionPolicy;
|
|
144
|
+
/**
|
|
145
|
+
* The Cue–Tag–Content graph.
|
|
146
|
+
*
|
|
147
|
+
* Its own SQLite file rather than a table in the semantic store: the traversal
|
|
148
|
+
* is a different access pattern (many small keyed hops, no vector math) and
|
|
149
|
+
* keeping it separate means enabling the feature cannot slow the existing
|
|
150
|
+
* retrieval path.
|
|
151
|
+
*/
|
|
152
|
+
export declare class MemoryGraph {
|
|
153
|
+
private db;
|
|
154
|
+
constructor(dbPath: string);
|
|
155
|
+
/**
|
|
156
|
+
* Add one memory item and its (cue, tag) index entries.
|
|
157
|
+
*
|
|
158
|
+
* Tags are REQUIRED: an item indexed by cue alone is the ~65%-recall
|
|
159
|
+
* configuration the paper ablates away. Callers with no tags should supply a
|
|
160
|
+
* coarse one (the layer name) rather than none.
|
|
161
|
+
*/
|
|
162
|
+
addContent(params: {
|
|
163
|
+
layer: MemoryLayer;
|
|
164
|
+
text: string;
|
|
165
|
+
occurredAt?: string;
|
|
166
|
+
cues: string[];
|
|
167
|
+
tags: string[];
|
|
168
|
+
}): number;
|
|
169
|
+
/** φ_{c→g}: the associative tags a cue activates. */
|
|
170
|
+
tagsForCue(cue: string): string[];
|
|
171
|
+
/** φ_{(c,g)→v}: contents reachable from a cue THROUGH a specific tag. */
|
|
172
|
+
contentsFor(cue: string, tag: string): ContentNode[];
|
|
173
|
+
/**
|
|
174
|
+
* Tags too broad to be bridges: attached to >= 60% of all content. Reuses the
|
|
175
|
+
* discriminativeness rule `stats()` already applies, because a tag that links
|
|
176
|
+
* everything relates nothing — and on the short-term tier EVERY item is tagged
|
|
177
|
+
* with one of ~6 type values, so without this the "bridge" is the whole store.
|
|
178
|
+
*/
|
|
179
|
+
hubTags(): Set<string>;
|
|
180
|
+
private tagSizeCache;
|
|
181
|
+
/** How many distinct contents a tag links. Cached — hot in the route loop. */
|
|
182
|
+
tagSize(tag: string): number;
|
|
183
|
+
/** Cues that co-occur with a tag — how traversal discovers NEW clues. */
|
|
184
|
+
cuesForTag(tag: string): string[];
|
|
185
|
+
/** Source keys already ingested, for incremental rebuilds. */
|
|
186
|
+
ingestedKeys(): Set<string>;
|
|
187
|
+
/** Record that a refresh COMPLETED, whether or not it stored anything. */
|
|
188
|
+
markRefreshed(nowIso: string): void;
|
|
189
|
+
/** ISO timestamp of the last completed refresh, or null if never refreshed. */
|
|
190
|
+
lastRefreshedAt(): string | null;
|
|
191
|
+
/**
|
|
192
|
+
* Best-effort single-flight guard around a build.
|
|
193
|
+
*
|
|
194
|
+
* Two agents retrieving at once both see a stale graph, both build, and both
|
|
195
|
+
* insert the same items under fresh ids — permanent duplicates, since the
|
|
196
|
+
* ledger then reports them indexed. Returns false when another build holds the
|
|
197
|
+
* lock; a lock older than `staleMs` is taken over so a crashed build cannot
|
|
198
|
+
* wedge refreshes forever.
|
|
199
|
+
*/
|
|
200
|
+
acquireBuildLock(nowMs: number, staleMs?: number): boolean;
|
|
201
|
+
releaseBuildLock(): void;
|
|
202
|
+
/** ISO timestamp of the most recent ingest, or null if never built. */
|
|
203
|
+
lastIngestedAt(): string | null;
|
|
204
|
+
/** Mark source keys as ingested. */
|
|
205
|
+
markIngested(keys: Iterable<string>): void;
|
|
206
|
+
/**
|
|
207
|
+
* Cheap emptiness probe. `stats()` runs five aggregates including two
|
|
208
|
+
* COUNT(DISTINCT) and a GROUP BY — far too expensive for the "should I route
|
|
209
|
+
* here" check that runs on every single query.
|
|
210
|
+
*/
|
|
211
|
+
isEmpty(): boolean;
|
|
212
|
+
/** Delete everything — the truncate half of a real rebuild. */
|
|
213
|
+
clear(): void;
|
|
214
|
+
/** Shape of the graph, for `uap memory graph status` and honest reporting. */
|
|
215
|
+
stats(): {
|
|
216
|
+
contents: number;
|
|
217
|
+
cues: number;
|
|
218
|
+
tags: number;
|
|
219
|
+
triples: number;
|
|
220
|
+
bridgingTags: number;
|
|
221
|
+
};
|
|
222
|
+
/** Every cue in the graph — the seed set when the query matches nothing. */
|
|
223
|
+
allCues(): string[];
|
|
224
|
+
close(): void;
|
|
225
|
+
}
|
|
226
|
+
export interface ReconstructOptions {
|
|
227
|
+
/** Hard step cap. The paper's runs converge in 3–4; 5 leaves headroom. */
|
|
228
|
+
maxSteps?: number;
|
|
229
|
+
policy?: ReconstructionPolicy;
|
|
230
|
+
/** Seed cues. Omitted -> derived from the query's own tokens. */
|
|
231
|
+
seedCues?: string[];
|
|
232
|
+
/**
|
|
233
|
+
* Hard ceiling on admitted context. `sufficient` only stops FURTHER steps —
|
|
234
|
+
* everything kept in the current step is already in context — so without this
|
|
235
|
+
* one step over a well-connected graph can admit thousands of nodes and blow
|
|
236
|
+
* the token budget this whole approach exists to protect.
|
|
237
|
+
*/
|
|
238
|
+
maxContext?: number;
|
|
239
|
+
/** Seed cues to draw when the query matches nothing. Default 32. */
|
|
240
|
+
maxSeeds?: number;
|
|
241
|
+
/**
|
|
242
|
+
* Hard cap on cues expanded per step, enforced HERE rather than trusted to the
|
|
243
|
+
* policy. The policy seam exists for an LLM to plug into, and an LLM will
|
|
244
|
+
* happily return 200 cues (or cues that were never active), reproducing the
|
|
245
|
+
* full-scan this bound was added to prevent.
|
|
246
|
+
*/
|
|
247
|
+
maxExpandPerStep?: number;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Reconstruct an answer context by traversing the graph while reasoning.
|
|
251
|
+
*
|
|
252
|
+
* The loop is the paper's three-step iteration: select actions over the active
|
|
253
|
+
* set, traverse (cue -> tags -> contents), then route/prune and update state.
|
|
254
|
+
* The essential difference from retrieval is that step N+1's expansion depends
|
|
255
|
+
* on what step N actually found — cues discovered mid-traversal become new
|
|
256
|
+
* entry points, and weak paths are killed before they consume context.
|
|
257
|
+
*/
|
|
258
|
+
export declare function reconstruct(graph: MemoryGraph, query: string, options?: ReconstructOptions): ReconstructionResult;
|
|
259
|
+
/**
|
|
260
|
+
* Seed cues = query tokens present in the graph.
|
|
261
|
+
*
|
|
262
|
+
* When nothing matches we fall back to a BOUNDED sample rather than every cue
|
|
263
|
+
* in the graph: an off-topic query would otherwise load the entire content
|
|
264
|
+
* table on step 1, which is the opposite of what this is for.
|
|
265
|
+
*/
|
|
266
|
+
export declare function seedCuesFromQuery(graph: MemoryGraph, query: string, maxSeeds?: number): string[];
|
|
267
|
+
/**
|
|
268
|
+
* Is active reconstruction enabled? Off by default — the lift is measured on our
|
|
269
|
+
* own suite before it becomes the default retrieval path.
|
|
270
|
+
*/
|
|
271
|
+
export declare function activeReconstructionEnabled(): boolean;
|
|
272
|
+
//# sourceMappingURL=reconstruct.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconstruct.d.ts","sourceRoot":"","sources":["../../src/memory/reconstruct.ts"],"names":[],"mappings":"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"}
|