@atrib/recall 0.3.2 → 0.5.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 +68 -7
- package/dist/aggregations.d.ts +114 -0
- package/dist/aggregations.js +267 -0
- package/dist/aggregations.js.map +1 -0
- package/dist/graph.d.ts +63 -0
- package/dist/graph.js +217 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +136 -4
- package/dist/index.js +526 -36
- package/dist/index.js.map +1 -1
- package/dist/scoring.d.ts +109 -0
- package/dist/scoring.js +126 -0
- package/dist/scoring.js.map +1 -0
- package/package.json +1 -1
package/dist/graph.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Local graph derivation + BFS for the recall semantic surface.
|
|
4
|
+
*
|
|
5
|
+
* Wires the §3.2.4 derived-graph shape that the recall_walk handler and
|
|
6
|
+
* the rank_by='causal_distance' path need, but scoped to the four
|
|
7
|
+
* load-bearing edge types for "what records causally inform this one":
|
|
8
|
+
*
|
|
9
|
+
* CHAIN_PRECEDES (weight 1): direct chain link.
|
|
10
|
+
* chain_root != genesisChainRoot(context_id) implies a prior record
|
|
11
|
+
* whose canonical hash equals chain_root sans "sha256:" prefix.
|
|
12
|
+
*
|
|
13
|
+
* INFORMED_BY (weight 1): explicit record-to-record reference (D041).
|
|
14
|
+
* Records carry an `informed_by` field whose entries are record_hashes
|
|
15
|
+
* of records consulted during construction.
|
|
16
|
+
*
|
|
17
|
+
* ANNOTATES (weight 2): an annotation record points at a target record
|
|
18
|
+
* (D058). Edge runs annotation -> target.
|
|
19
|
+
*
|
|
20
|
+
* REVISES (weight 2): a revision record supersedes a target record
|
|
21
|
+
* (D059). Edge runs revision -> target.
|
|
22
|
+
*
|
|
23
|
+
* Weights come from the Layer 1 design: direct causality (chain +
|
|
24
|
+
* informed_by) is weight 1; annotation/revision relationships are
|
|
25
|
+
* weight 2 (less direct: the agent's later judgment on a record vs the
|
|
26
|
+
* record's own causal inputs).
|
|
27
|
+
*
|
|
28
|
+
* SESSION_PRECEDES + SESSION_PARALLEL + CONVERGES_ON + CROSS_SESSION
|
|
29
|
+
* are deliberately omitted at Layer 1. They encode temporal/sibling
|
|
30
|
+
* structure, not causal-ancestor structure — useful for the public
|
|
31
|
+
* graph-node /v1/graph view but not for "what should the agent re-read
|
|
32
|
+
* before re-attempting a similar action". The graph-node service
|
|
33
|
+
* (services/graph-node/src/graph-builder.ts) is the spec-faithful
|
|
34
|
+
* full §3.2.4 derivation; this module is its Layer 1 subset.
|
|
35
|
+
*
|
|
36
|
+
* PROVENANCE_OF (D044) is also omitted at Layer 1 — it operates on the
|
|
37
|
+
* cross-session genesis anchor via a 16-byte-truncated token, which
|
|
38
|
+
* doesn't compose naturally with the within-mirror BFS path. A future
|
|
39
|
+
* release that wires cross-session PROVENANCE_OF onto provenance_token
|
|
40
|
+
* inputs would extend this graph.
|
|
41
|
+
*
|
|
42
|
+
* The graph is built once per recall() call from the LoadedRecord array
|
|
43
|
+
* and discarded; no caching. Layer 2 (sqlite-vec sidecar) materializes
|
|
44
|
+
* the graph alongside the embedding store.
|
|
45
|
+
*/
|
|
46
|
+
import { genesisChainRoot } from '@atrib/mcp';
|
|
47
|
+
/**
|
|
48
|
+
* Layer 1 BFS edge weights. Direct causal links (CHAIN_PRECEDES,
|
|
49
|
+
* INFORMED_BY) are weight 1; annotation/revision relationships are
|
|
50
|
+
* weight 2. The recall_walk handler accepts an edge_types filter that
|
|
51
|
+
* intersects with these four; weights apply only when an edge type
|
|
52
|
+
* passes the filter.
|
|
53
|
+
*/
|
|
54
|
+
export const EDGE_WEIGHTS = {
|
|
55
|
+
CHAIN_PRECEDES: 1,
|
|
56
|
+
INFORMED_BY: 1,
|
|
57
|
+
ANNOTATES: 2,
|
|
58
|
+
REVISES: 2,
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Build the local Layer 1 graph from loaded records. Returns adjacency
|
|
62
|
+
* map keyed by record_hash. Records present in the loaded set but with
|
|
63
|
+
* no incident edges still appear as map keys with empty arrays — this
|
|
64
|
+
* keeps the BFS path consistent (graph.has(anchor) is still true even
|
|
65
|
+
* when the anchor has no neighbors).
|
|
66
|
+
*
|
|
67
|
+
* Time complexity: O(N) for the chain index pass + O(E) for edge
|
|
68
|
+
* emission. N = loaded.length, E = sum of informed_by entries + chain
|
|
69
|
+
* links + annotation+revision edges.
|
|
70
|
+
*/
|
|
71
|
+
export function buildLocalGraph(loaded) {
|
|
72
|
+
const graph = new Map();
|
|
73
|
+
for (const lr of loaded) {
|
|
74
|
+
if (!graph.has(lr.record_hash))
|
|
75
|
+
graph.set(lr.record_hash, []);
|
|
76
|
+
}
|
|
77
|
+
const addEdge = (from, edge) => {
|
|
78
|
+
const list = graph.get(from);
|
|
79
|
+
if (list)
|
|
80
|
+
list.push(edge);
|
|
81
|
+
else
|
|
82
|
+
graph.set(from, [edge]);
|
|
83
|
+
};
|
|
84
|
+
const addUndirected = (a, b, type) => {
|
|
85
|
+
const weight = EDGE_WEIGHTS[type];
|
|
86
|
+
addEdge(a, { type, target: b, weight });
|
|
87
|
+
addEdge(b, { type, target: a, weight });
|
|
88
|
+
};
|
|
89
|
+
// Index by canonical-hash-hex so CHAIN_PRECEDES lookups (chain_root
|
|
90
|
+
// sans "sha256:") can resolve to the prior record's record_hash.
|
|
91
|
+
const byHashHex = new Map(); // hex -> record_hash form
|
|
92
|
+
for (const lr of loaded) {
|
|
93
|
+
const hex = lr.record_hash.startsWith('sha256:')
|
|
94
|
+
? lr.record_hash.slice('sha256:'.length)
|
|
95
|
+
: lr.record_hash;
|
|
96
|
+
byHashHex.set(hex, lr.record_hash);
|
|
97
|
+
}
|
|
98
|
+
for (const lr of loaded) {
|
|
99
|
+
// CHAIN_PRECEDES: skip genesis records (chain_root == genesisChainRoot(context_id)).
|
|
100
|
+
const genesis = genesisChainRoot(lr.record.context_id);
|
|
101
|
+
if (lr.record.chain_root !== genesis) {
|
|
102
|
+
const expected = lr.record.chain_root.startsWith('sha256:')
|
|
103
|
+
? lr.record.chain_root.slice('sha256:'.length)
|
|
104
|
+
: lr.record.chain_root;
|
|
105
|
+
const prior = byHashHex.get(expected);
|
|
106
|
+
if (prior) {
|
|
107
|
+
addUndirected(lr.record_hash, prior, 'CHAIN_PRECEDES');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// INFORMED_BY: explicit references.
|
|
111
|
+
const informedBy = lr.record.informed_by;
|
|
112
|
+
if (Array.isArray(informedBy)) {
|
|
113
|
+
for (const ref of informedBy) {
|
|
114
|
+
if (typeof ref !== 'string')
|
|
115
|
+
continue;
|
|
116
|
+
// Only emit when the referenced record is present in the mirror.
|
|
117
|
+
// Cross-mirror references will be resolved by Layer 2 (cache or
|
|
118
|
+
// log-side lookup) once that ships.
|
|
119
|
+
if (graph.has(ref) || byHashHex.has(stripPrefix(ref))) {
|
|
120
|
+
addUndirected(lr.record_hash, ref, 'INFORMED_BY');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ANNOTATES: annotation record -> target. content.annotates lives in
|
|
125
|
+
// _local.content on a D062 envelope; bare-record annotations have no
|
|
126
|
+
// body to read (the §8.1 posture); skip those.
|
|
127
|
+
if (lr.content && typeof lr.content === 'object') {
|
|
128
|
+
const c = lr.content;
|
|
129
|
+
if (typeof c.annotates === 'string' &&
|
|
130
|
+
lr.record.event_type === 'https://atrib.dev/v1/types/annotation') {
|
|
131
|
+
addUndirected(lr.record_hash, c.annotates, 'ANNOTATES');
|
|
132
|
+
}
|
|
133
|
+
if (typeof c.revises === 'string' &&
|
|
134
|
+
lr.record.event_type === 'https://atrib.dev/v1/types/revision') {
|
|
135
|
+
addUndirected(lr.record_hash, c.revises, 'REVISES');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return graph;
|
|
140
|
+
}
|
|
141
|
+
function stripPrefix(hash) {
|
|
142
|
+
return hash.startsWith('sha256:') ? hash.slice('sha256:'.length) : hash;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* BFS-shortest-path distances from `start` over the local graph.
|
|
146
|
+
* Returns a map record_hash -> distance (weighted). Records with no path
|
|
147
|
+
* to `start` are omitted (rather than mapped to Infinity) so callers
|
|
148
|
+
* iterate only reachable nodes.
|
|
149
|
+
*
|
|
150
|
+
* The traversal honors `edgeTypes` when provided: only edges whose type
|
|
151
|
+
* is in the set are followed. When edgeTypes is undefined or empty,
|
|
152
|
+
* ALL edge types are followed.
|
|
153
|
+
*
|
|
154
|
+
* `maxDepth` caps the traversal at that hop-count (NOT cumulative
|
|
155
|
+
* weight). Set to Infinity (the default) to traverse the full reachable
|
|
156
|
+
* subgraph. Useful for recall_walk's depth parameter.
|
|
157
|
+
*
|
|
158
|
+
* Since edge weights differ (1 or 2), the traversal uses Dijkstra (with
|
|
159
|
+
* a simple O(V^2) min-extraction since the candidate sets are small at
|
|
160
|
+
* Layer 1). This produces correct shortest paths in weighted graphs.
|
|
161
|
+
*/
|
|
162
|
+
export function shortestDistances(graph, start, edgeTypes, maxHops = Number.POSITIVE_INFINITY) {
|
|
163
|
+
if (!graph.has(start))
|
|
164
|
+
return new Map();
|
|
165
|
+
const dist = new Map();
|
|
166
|
+
const hops = new Map();
|
|
167
|
+
dist.set(start, 0);
|
|
168
|
+
hops.set(start, 0);
|
|
169
|
+
const visited = new Set();
|
|
170
|
+
while (true) {
|
|
171
|
+
// O(V) min extraction. Layer 1 graphs are small (hundreds of
|
|
172
|
+
// records); a heap is overkill.
|
|
173
|
+
let node;
|
|
174
|
+
let minDist = Number.POSITIVE_INFINITY;
|
|
175
|
+
for (const [k, d] of dist) {
|
|
176
|
+
if (visited.has(k))
|
|
177
|
+
continue;
|
|
178
|
+
if (d < minDist) {
|
|
179
|
+
minDist = d;
|
|
180
|
+
node = k;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (node === undefined)
|
|
184
|
+
break;
|
|
185
|
+
visited.add(node);
|
|
186
|
+
const nodeHops = hops.get(node) ?? 0;
|
|
187
|
+
if (nodeHops >= maxHops)
|
|
188
|
+
continue;
|
|
189
|
+
for (const edge of graph.get(node) ?? []) {
|
|
190
|
+
if (edgeTypes && edgeTypes.size > 0 && !edgeTypes.has(edge.type))
|
|
191
|
+
continue;
|
|
192
|
+
const candidate = minDist + edge.weight;
|
|
193
|
+
const existing = dist.get(edge.target);
|
|
194
|
+
if (existing === undefined || candidate < existing) {
|
|
195
|
+
dist.set(edge.target, candidate);
|
|
196
|
+
hops.set(edge.target, nodeHops + 1);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return dist;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Walk the graph from `start`, returning every reachable record_hash
|
|
204
|
+
* within `maxHops` hops, filtered to the requested edge_types. Result
|
|
205
|
+
* is sorted by ascending distance from start. Used by the recall_walk
|
|
206
|
+
* MCP tool to surface the local causal neighborhood of an anchor.
|
|
207
|
+
*/
|
|
208
|
+
export function walkFrom(graph, start, edgeTypes, maxHops = 3) {
|
|
209
|
+
const dist = shortestDistances(graph, start, edgeTypes, maxHops);
|
|
210
|
+
// Drop the start node itself (distance 0) — the agent asked for
|
|
211
|
+
// adjacent records, not a re-echo of the anchor.
|
|
212
|
+
return [...dist.entries()]
|
|
213
|
+
.filter(([h]) => h !== start)
|
|
214
|
+
.map(([record_hash, distance]) => ({ record_hash, distance }))
|
|
215
|
+
.sort((a, b) => a.distance - b.distance);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import type { AtribRecord } from '@atrib/mcp';
|
|
3
|
+
export type ImportanceLabel = 'critical' | 'high' | 'medium' | 'low' | 'noise';
|
|
4
|
+
export declare const IMPORTANCE_NUMERIC: Record<ImportanceLabel, number>;
|
|
5
|
+
export declare const ATRIB_RECALL_ALPHA: number;
|
|
6
|
+
export declare const ATRIB_RECALL_BETA: number;
|
|
7
|
+
export declare const ATRIB_RECALL_GAMMA: number;
|
|
8
|
+
export declare const ATRIB_RECALL_TAU_DAYS: number;
|
|
9
|
+
import type { AnnotationSummary as AggAnnotationSummary } from './aggregations.js';
|
|
3
10
|
export declare function loadRecords(path: string): AtribRecord[];
|
|
4
11
|
/**
|
|
5
12
|
* Load every `*.jsonl` file in `dir` and merge their records. Files that
|
|
@@ -11,7 +18,7 @@ export declare function loadRecords(path: string): AtribRecord[];
|
|
|
11
18
|
* mirror namespace; every producer running under one identity writes a
|
|
12
19
|
* file there with the convention `<producer>-<agent>.jsonl`. Scanning the
|
|
13
20
|
* directory unifies recall across producers without recall having to know
|
|
14
|
-
* the naming scheme
|
|
21
|
+
* the naming scheme - any producer that follows §5.9 just shows up.
|
|
15
22
|
*/
|
|
16
23
|
export declare function loadRecordsFromDir(dir: string): {
|
|
17
24
|
records: AtribRecord[];
|
|
@@ -19,7 +26,92 @@ export declare function loadRecordsFromDir(dir: string): {
|
|
|
19
26
|
};
|
|
20
27
|
interface RecallArgs {
|
|
21
28
|
context_id?: string;
|
|
22
|
-
event_type?: 'tool_call' | 'transaction';
|
|
29
|
+
event_type?: 'tool_call' | 'transaction' | 'annotation' | 'revision';
|
|
30
|
+
/**
|
|
31
|
+
* Optional exact match on `record.content_id` (`sha256:<64-hex>`). Per spec
|
|
32
|
+
* §1.2.2, content_id is `sha256(serverUrl + ":" + toolName)`. Filtering by
|
|
33
|
+
* content_id groups all records emitted by the same tool on the same MCP
|
|
34
|
+
* server. Useful for "all calls to this tool, ever." Coarser than tool_name
|
|
35
|
+
* because two tools on different servers share no content_id even if their
|
|
36
|
+
* names match.
|
|
37
|
+
*/
|
|
38
|
+
content_id?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Optional exact match on the §8.2 disclosed `tool_name`. Records that did
|
|
41
|
+
* NOT opt in to tool-name disclosure (the §8.1 default posture) carry no
|
|
42
|
+
* tool_name field and are excluded from results when this filter is set.
|
|
43
|
+
* Use this to query by human-readable name (e.g. tool_name="Edit") across
|
|
44
|
+
* MCP servers, when the producer disclosed it.
|
|
45
|
+
*/
|
|
46
|
+
tool_name?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Optional exact match on `record.args_hash` (`sha256:<64-hex>`). Per spec
|
|
49
|
+
* §8.3, args_hash commits to canonical args bytes. Salted (D045) and plain
|
|
50
|
+
* forms hash identically on the wire; this filter does not distinguish
|
|
51
|
+
* them. Most useful for replay detection (same args, same hash) and for
|
|
52
|
+
* agent-side keyed lookup when the agent computes a probe hash over a
|
|
53
|
+
* normalized {tool, target} dict.
|
|
54
|
+
*/
|
|
55
|
+
args_hash?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Layer 1 filter (NEW in 0.5.0): minimum annotation importance. Records
|
|
58
|
+
* are ranked by max(annotation.importance) where annotations are D058
|
|
59
|
+
* records pointing at this record. Records with no annotations at all
|
|
60
|
+
* have importance=0 and are EXCLUDED from results when min_importance is
|
|
61
|
+
* set. Use this to surface only records the agent or its critique loop
|
|
62
|
+
* has marked as worth attention.
|
|
63
|
+
*/
|
|
64
|
+
min_importance?: ImportanceLabel;
|
|
65
|
+
/**
|
|
66
|
+
* Layer 1 filter (NEW in 0.5.0): OR-match against annotation topic tags.
|
|
67
|
+
* Records are kept if AT LEAST ONE annotation pointing at them carries
|
|
68
|
+
* AT LEAST ONE of the listed topics. Records with no annotations or no
|
|
69
|
+
* topic-overlap are excluded. Topics come from D058 annotation content.
|
|
70
|
+
*/
|
|
71
|
+
topic_tags?: string[];
|
|
72
|
+
/**
|
|
73
|
+
* Layer 1 filter (NEW in 0.5.0): hide records superseded by D059 revision
|
|
74
|
+
* records. Default false (records remain visible even if a later revision
|
|
75
|
+
* supersedes them; the response carries `superseded_by` so the agent can
|
|
76
|
+
* see). Set true to filter superseded records out of the response entirely.
|
|
77
|
+
*/
|
|
78
|
+
include_revised?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Layer 1 filter (NEW in 0.5.0): minimum count of distinct cross-attesting
|
|
81
|
+
* signers per D052. Useful for transaction records that must carry >= 2
|
|
82
|
+
* signers; also useful as a credibility filter when querying multi-agent
|
|
83
|
+
* substrate. Records below the threshold are excluded.
|
|
84
|
+
*/
|
|
85
|
+
min_signers?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Layer 1 ranking (NEW in 0.5.0): how to order results before paging.
|
|
88
|
+
* 'timestamp' (default, backward-compatible): newest first.
|
|
89
|
+
* 'relevance': Park et al. 2023 weighted-sum scoring with annotation-derived
|
|
90
|
+
* importance (NO embedding component until Layer 2 ships; falls back to BM25
|
|
91
|
+
* over summary+topics if rank_anchor query is provided).
|
|
92
|
+
* 'causal_distance': BFS shortest path in the §3.2.4 derived graph from
|
|
93
|
+
* `rank_anchor` (which must be a record_hash). Edge weights per design.
|
|
94
|
+
*/
|
|
95
|
+
rank_by?: 'timestamp' | 'relevance' | 'causal_distance';
|
|
96
|
+
/**
|
|
97
|
+
* Layer 1 ranking (NEW in 0.5.0): the anchor for non-timestamp rank_by
|
|
98
|
+
* modes. For rank_by='causal_distance', this MUST be a record_hash
|
|
99
|
+
* (`sha256:<64-hex>`); records are ranked by BFS shortest path from
|
|
100
|
+
* the anchor. For rank_by='relevance' with an optional query string,
|
|
101
|
+
* pass the query here as a free-form text string (Layer 2 will use it
|
|
102
|
+
* for embedding similarity; Layer 1 falls back to BM25-style scoring
|
|
103
|
+
* over summary+topics).
|
|
104
|
+
*/
|
|
105
|
+
rank_anchor?: string;
|
|
106
|
+
/**
|
|
107
|
+
* New in 0.5.0: table-of-contents response shape. When true,
|
|
108
|
+
* each record returned is a one-line entry shape (record_hash, tool_name,
|
|
109
|
+
* summary, importance, topic_tags, timestamp, superseded_by). Cheap to
|
|
110
|
+
* scan, ~40-80 tokens per entry; agent expands on demand via
|
|
111
|
+
* `recall(content_id=..., compact=false)` or `recall_walk(...)`. Used at
|
|
112
|
+
* SessionStart for the auto-injected scaffold.
|
|
113
|
+
*/
|
|
114
|
+
toc?: boolean;
|
|
23
115
|
limit?: number;
|
|
24
116
|
offset?: number;
|
|
25
117
|
/**
|
|
@@ -35,11 +127,23 @@ interface RecallArgs {
|
|
|
35
127
|
*/
|
|
36
128
|
include_unverified?: boolean;
|
|
37
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Aggregated annotation summary attached to a record per Layer 1. Same
|
|
132
|
+
* shape as the canonical AnnotationSummary exported from aggregations.ts;
|
|
133
|
+
* aliased here so the response types in this file don't have to drag the
|
|
134
|
+
* aggregations module into their import surface.
|
|
135
|
+
*/
|
|
136
|
+
type AnnotationSummary = AggAnnotationSummary;
|
|
38
137
|
/**
|
|
39
138
|
* The shape returned to the agent. Each record is annotated with
|
|
40
|
-
* signature_verified
|
|
139
|
+
* signature_verified - true if the local Ed25519 signature check passed.
|
|
41
140
|
* In compact mode the heavy fields (signature, content_id, chain_root,
|
|
42
141
|
* spec_version) are dropped; the verified status is preserved.
|
|
142
|
+
*
|
|
143
|
+
* Layer 1 (0.5.0) adds optional `annotations` (max_importance + topics from
|
|
144
|
+
* any D058 annotations pointing at this record) and `superseded_by` (record
|
|
145
|
+
* hashes of any D059 revision records whose `revises` field equals this
|
|
146
|
+
* record's hash).
|
|
43
147
|
*/
|
|
44
148
|
type RecallRecordCompact = {
|
|
45
149
|
event_type: AtribRecord['event_type'];
|
|
@@ -48,9 +152,37 @@ type RecallRecordCompact = {
|
|
|
48
152
|
timestamp: number;
|
|
49
153
|
signature_verified: boolean;
|
|
50
154
|
session_token?: string;
|
|
155
|
+
/**
|
|
156
|
+
* §8.2 disclosed tool name. Included in compact mode when present so a
|
|
157
|
+
* caller filtering by tool_name sees the value back in the response (the
|
|
158
|
+
* common pattern: filter by tool_name -> render results, want the name
|
|
159
|
+
* visible). Records without tool_name disclosure (the §8.1 default
|
|
160
|
+
* posture) omit this field as they always do.
|
|
161
|
+
*/
|
|
162
|
+
tool_name?: string;
|
|
163
|
+
/** New in 0.5.0: aggregated annotation summary. */
|
|
164
|
+
annotations?: AnnotationSummary;
|
|
165
|
+
/** New in 0.5.0: record hashes of D059 revisions superseding this record. */
|
|
166
|
+
superseded_by?: string[];
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* TOC entry: the smallest cheap-to-scan shape (~40-80 tokens). Used at
|
|
170
|
+
* SessionStart auto-inject to surface a candidate set the agent can
|
|
171
|
+
* expand on demand via recall(content_id=...) or recall_walk.
|
|
172
|
+
*/
|
|
173
|
+
export type RecallRecordToc = {
|
|
174
|
+
record_hash?: string;
|
|
175
|
+
tool_name?: string;
|
|
176
|
+
summary?: string;
|
|
177
|
+
importance?: ImportanceLabel;
|
|
178
|
+
topic_tags?: string[];
|
|
179
|
+
timestamp: number;
|
|
180
|
+
superseded_by?: string[];
|
|
51
181
|
};
|
|
52
182
|
type RecallRecordFull = AtribRecord & {
|
|
53
183
|
signature_verified: boolean;
|
|
184
|
+
annotations?: AnnotationSummary;
|
|
185
|
+
superseded_by?: string[];
|
|
54
186
|
};
|
|
55
187
|
export interface RecallResult {
|
|
56
188
|
total: number;
|
|
@@ -73,7 +205,7 @@ export interface RecallResult {
|
|
|
73
205
|
record_file: string;
|
|
74
206
|
log_origin: string;
|
|
75
207
|
pagination_caveat: string;
|
|
76
|
-
records: RecallRecordFull[] | RecallRecordCompact[];
|
|
208
|
+
records: RecallRecordFull[] | RecallRecordCompact[] | RecallRecordToc[];
|
|
77
209
|
}
|
|
78
210
|
/**
|
|
79
211
|
* Discover and load records per the mirror-discovery contract:
|