@zenera/neo 1.1.12 → 1.1.15
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 +12 -4
- package/dist/embeddings/limiter.js +14 -3
- package/dist/inspect/client.d.ts +1 -1
- package/dist/inspect/client.js +399 -1
- package/dist/inspect/css.d.ts +1 -1
- package/dist/inspect/css.js +49 -0
- package/dist/inspect/index.d.ts +3 -3
- package/dist/inspect/index.js +1 -1
- package/dist/inspect/page.js +16 -0
- package/dist/inspect/report.d.ts +25 -0
- package/dist/inspect/report.js +45 -0
- package/dist/memory/report.d.ts +12 -0
- package/dist/memory/report.js +32 -14
- package/dist/memory/tools.js +30 -3
- package/dist/memory/types.d.ts +3 -2
- package/dist/trajectory.d.ts +1 -1
- package/package.json +1 -1
package/dist/inspect/report.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildMemorySlice } from "../memory/report.js";
|
|
1
2
|
import { collectPayloads } from "../payload.js";
|
|
2
3
|
// ---------------------------------------------------------------------------
|
|
3
4
|
// Run inspector — a single HTML file that explains a run
|
|
@@ -12,6 +13,9 @@ import { collectPayloads } from "../payload.js";
|
|
|
12
13
|
* a report into a file no editor will open. */
|
|
13
14
|
export const MAX_BLOB_BYTES = 512 * 1024;
|
|
14
15
|
export const MERMAID_URL = 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
16
|
+
/** Deliberately meaner than the memory page's own cap: this file already holds
|
|
17
|
+
* a whole run, and one remembered artifact should not decide whether it opens. */
|
|
18
|
+
export const MAX_MEMORY_FILE_BYTES = 64 * 1024;
|
|
15
19
|
/** Base64 data uris only: the charset ends at the closing JSON quote. */
|
|
16
20
|
const DATA_URL = /data:[\w.+-]+\/[\w.+-]+;base64,[A-Za-z0-9+/=]+/g;
|
|
17
21
|
/** Below this a uri is cheaper to leave in place than to indirect through. */
|
|
@@ -57,6 +61,47 @@ export async function buildRunReport(state, payloads, opts = {}) {
|
|
|
57
61
|
truncated,
|
|
58
62
|
media,
|
|
59
63
|
architecture: opts.architecture,
|
|
64
|
+
memory: opts.memory ? await remembered(opts.memory, state, media, opts) : undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Every memory node the run touched, branches included. */
|
|
68
|
+
function memoryIds(nodes, into) {
|
|
69
|
+
for (const node of nodes) {
|
|
70
|
+
if (node.type === 'memory_recall' || node.type === 'memory_op') {
|
|
71
|
+
for (const n of node.nodes) {
|
|
72
|
+
into.add(n.id);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else if (node.type === 'join') {
|
|
76
|
+
for (const branch of node.branches) {
|
|
77
|
+
memoryIds(branch.nodes, into);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return into;
|
|
82
|
+
}
|
|
83
|
+
async function remembered(store, state, media, opts) {
|
|
84
|
+
const ids = memoryIds(state.trajectory, new Set());
|
|
85
|
+
if (!ids.size) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
let slice;
|
|
89
|
+
try {
|
|
90
|
+
slice = await buildMemorySlice(store, [...ids], {
|
|
91
|
+
maxContentBytes: opts.maxMemoryFileBytes ?? MAX_MEMORY_FILE_BYTES,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// A memory that cannot be read is a view the page does without; it is
|
|
96
|
+
// not a reason to lose the record of the run.
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
dir: store.dir,
|
|
101
|
+
// Remembered images go through the same table as the run's own, so a
|
|
102
|
+
// picture the agent both saw and filed is carried once.
|
|
103
|
+
nodes: slice.nodes.map((n) => (n.image ? { ...n, image: hoistMedia(n.image, media) } : n)),
|
|
104
|
+
edges: slice.edges,
|
|
60
105
|
};
|
|
61
106
|
}
|
|
62
107
|
//# sourceMappingURL=report.js.map
|
package/dist/memory/report.d.ts
CHANGED
|
@@ -53,5 +53,17 @@ export interface MemoryReportOptions {
|
|
|
53
53
|
/** override the clock, for tests */
|
|
54
54
|
now?: () => string;
|
|
55
55
|
}
|
|
56
|
+
/** Part of a memory, for a caller that already knows which part it wants. */
|
|
57
|
+
export interface MemorySlice {
|
|
58
|
+
nodes: ReportNode[];
|
|
59
|
+
/** only the edges with both ends inside the slice */
|
|
60
|
+
edges: MemoryEdge[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The nodes named, built exactly as the whole graph is — same inlining, same
|
|
64
|
+
* excuses, same degree, which is counted over every edge rather than over the
|
|
65
|
+
* slice: a node's degree is a fact about the memory, not about the cut.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildMemorySlice(store: MemoryStore, ids: readonly string[], opts?: Pick<MemoryReportOptions, 'maxContentBytes'>): Promise<MemorySlice>;
|
|
56
68
|
export declare function buildMemoryReport(store: MemoryStore, opts?: MemoryReportOptions): Promise<MemoryReport>;
|
|
57
69
|
//# sourceMappingURL=report.d.ts.map
|
package/dist/memory/report.js
CHANGED
|
@@ -25,25 +25,26 @@ const MIME = {
|
|
|
25
25
|
webp: 'image/webp',
|
|
26
26
|
svg: 'image/svg+xml',
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* The nodes named, built exactly as the whole graph is — same inlining, same
|
|
30
|
+
* excuses, same degree, which is counted over every edge rather than over the
|
|
31
|
+
* slice: a node's degree is a fact about the memory, not about the cut.
|
|
32
|
+
*/
|
|
33
|
+
export async function buildMemorySlice(store, ids, opts = {}) {
|
|
34
|
+
const graph = store.graph;
|
|
35
|
+
const all = graph.edges();
|
|
36
|
+
const wanted = new Set(ids);
|
|
37
|
+
return {
|
|
38
|
+
nodes: await inline(store, graph.nodes().filter((n) => wanted.has(n.id)), degrees(all), graph.superseded(), opts.maxContentBytes ?? MAX_CONTENT_BYTES),
|
|
39
|
+
edges: all.filter((e) => wanted.has(e.source) && wanted.has(e.target)),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
28
42
|
export async function buildMemoryReport(store, opts = {}) {
|
|
29
43
|
const graph = store.graph;
|
|
30
44
|
const nodes = graph.nodes();
|
|
31
45
|
const edges = graph.edges();
|
|
32
|
-
const stale = graph.superseded();
|
|
33
46
|
const limit = opts.maxContentBytes ?? MAX_CONTENT_BYTES;
|
|
34
|
-
const
|
|
35
|
-
for (const e of edges) {
|
|
36
|
-
degree.set(e.source, (degree.get(e.source) ?? 0) + 1);
|
|
37
|
-
degree.set(e.target, (degree.get(e.target) ?? 0) + 1);
|
|
38
|
-
}
|
|
39
|
-
// Files are read in parallel: one memory can hold hundreds, and they are
|
|
40
|
-
// independent.
|
|
41
|
-
const built = await Promise.all(nodes.map(async (n) => ({
|
|
42
|
-
...plain(n),
|
|
43
|
-
stale: stale.has(n.id),
|
|
44
|
-
degree: degree.get(n.id) ?? 0,
|
|
45
|
-
...(n.file ? await body(store.dir, n.file, limit) : {}),
|
|
46
|
-
})));
|
|
47
|
+
const built = await inline(store, nodes, degrees(edges), graph.superseded(), limit);
|
|
47
48
|
const vectors = store.vectors;
|
|
48
49
|
return {
|
|
49
50
|
title: opts.title ?? 'Memory',
|
|
@@ -59,6 +60,23 @@ export async function buildMemoryReport(store, opts = {}) {
|
|
|
59
60
|
audiences: [...new Set(nodes.flatMap((n) => n.audience))].sort(),
|
|
60
61
|
};
|
|
61
62
|
}
|
|
63
|
+
function degrees(edges) {
|
|
64
|
+
const degree = new Map();
|
|
65
|
+
for (const e of edges) {
|
|
66
|
+
degree.set(e.source, (degree.get(e.source) ?? 0) + 1);
|
|
67
|
+
degree.set(e.target, (degree.get(e.target) ?? 0) + 1);
|
|
68
|
+
}
|
|
69
|
+
return degree;
|
|
70
|
+
}
|
|
71
|
+
/** Files are read in parallel: one memory can hold hundreds, independently. */
|
|
72
|
+
async function inline(store, nodes, degree, stale, limit) {
|
|
73
|
+
return Promise.all(nodes.map(async (n) => ({
|
|
74
|
+
...plain(n),
|
|
75
|
+
stale: stale.has(n.id),
|
|
76
|
+
degree: degree.get(n.id) ?? 0,
|
|
77
|
+
...(n.file ? await body(store.dir, n.file, limit) : {}),
|
|
78
|
+
})));
|
|
79
|
+
}
|
|
62
80
|
/** The node's own fields, without the graph attributes graphology adds. */
|
|
63
81
|
function plain(n) {
|
|
64
82
|
return {
|
package/dist/memory/tools.js
CHANGED
|
@@ -101,8 +101,23 @@ export function memoryTools(opts) {
|
|
|
101
101
|
additionalProperties: false,
|
|
102
102
|
},
|
|
103
103
|
execute: async (args, tc) => {
|
|
104
|
+
const opId = memoryOpId(tc.state.runId, tc.callId);
|
|
104
105
|
const loaded = await index.load(args.ids, binding.sees, tc.services.clock);
|
|
105
|
-
|
|
106
|
+
// Recorded because this is the only path that puts a whole body
|
|
107
|
+
// in the context: a recall block carries a clipped line.
|
|
108
|
+
const spec = {
|
|
109
|
+
kind: 'op',
|
|
110
|
+
op: 'load',
|
|
111
|
+
opId,
|
|
112
|
+
nodes: loaded.map((l) => ({
|
|
113
|
+
id: l.node.id,
|
|
114
|
+
kind: l.node.kind,
|
|
115
|
+
revision: l.node.revision,
|
|
116
|
+
})),
|
|
117
|
+
edges: [],
|
|
118
|
+
files: loaded.filter((l) => l.content !== undefined).length,
|
|
119
|
+
};
|
|
120
|
+
return withEffects(loaded.map((l) => ({
|
|
106
121
|
id: l.node.id,
|
|
107
122
|
kind: l.node.kind,
|
|
108
123
|
text: l.node.text,
|
|
@@ -113,7 +128,7 @@ export function memoryTools(opts) {
|
|
|
113
128
|
? { path: l.node.file.path, bytes: l.node.file.bytes }
|
|
114
129
|
: undefined,
|
|
115
130
|
content: l.content,
|
|
116
|
-
}));
|
|
131
|
+
})), { kind: 'memory_op', spec });
|
|
117
132
|
},
|
|
118
133
|
}),
|
|
119
134
|
];
|
|
@@ -271,6 +286,11 @@ export function memoryTools(opts) {
|
|
|
271
286
|
const node = index.graph.get(id);
|
|
272
287
|
return node ? [{ id, kind: node.kind, revision: node.revision }] : [];
|
|
273
288
|
});
|
|
289
|
+
// Taken before the delete: afterwards nothing can say what this
|
|
290
|
+
// node hung from, and a report would draw it floating.
|
|
291
|
+
const links = new Map(args.ids
|
|
292
|
+
.flatMap((id) => index.graph.neighbors(id, binding.sees))
|
|
293
|
+
.map((e) => [`${e.source}|${e.relation}|${e.target}`, e]));
|
|
274
294
|
try {
|
|
275
295
|
await index.forget(args.ids, binding.sees);
|
|
276
296
|
}
|
|
@@ -279,7 +299,14 @@ export function memoryTools(opts) {
|
|
|
279
299
|
}
|
|
280
300
|
return withEffects(`${MEMORY_FORGET_TOOL} ${before.length} nodes`, {
|
|
281
301
|
kind: 'memory_op',
|
|
282
|
-
spec: {
|
|
302
|
+
spec: {
|
|
303
|
+
kind: 'op',
|
|
304
|
+
op: 'forget',
|
|
305
|
+
opId,
|
|
306
|
+
nodes: before,
|
|
307
|
+
edges: [...links.values()],
|
|
308
|
+
files: 0,
|
|
309
|
+
},
|
|
283
310
|
});
|
|
284
311
|
},
|
|
285
312
|
}));
|
package/dist/memory/types.d.ts
CHANGED
|
@@ -169,10 +169,11 @@ export interface MemoryRecallSpec {
|
|
|
169
169
|
}
|
|
170
170
|
export interface MemoryOpSpec {
|
|
171
171
|
kind: 'op';
|
|
172
|
-
|
|
172
|
+
/** `load` is here rather than under recall: it hands the model whole bodies, and it writes. */
|
|
173
|
+
op: 'commit' | 'forget' | 'load';
|
|
173
174
|
/** sha256(runId, callId) — deterministic, so a replay is deduplicated */
|
|
174
175
|
opId: string;
|
|
175
|
-
/** nodes this op created, changed or
|
|
176
|
+
/** nodes this op created, changed, removed or read in full */
|
|
176
177
|
nodes: {
|
|
177
178
|
id: string;
|
|
178
179
|
kind: string;
|
package/dist/trajectory.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export interface MemoryRecallNode extends NodeBase {
|
|
|
67
67
|
}
|
|
68
68
|
export interface MemoryOpNode extends NodeBase {
|
|
69
69
|
type: 'memory_op';
|
|
70
|
-
op: 'commit' | 'forget';
|
|
70
|
+
op: 'commit' | 'forget' | 'load';
|
|
71
71
|
/** sha256(runId, callId) — deterministic, so a replay is deduplicated */
|
|
72
72
|
opId: string;
|
|
73
73
|
nodes: {
|