@tangle-network/agent-knowledge 1.6.0 → 1.7.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 +37 -0
- package/dist/chunk-U6KQ4FS3.js +585 -0
- package/dist/chunk-U6KQ4FS3.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/index.d.ts +161 -0
- package/dist/memory/index.js +24 -0
- package/dist/memory/index.js.map +1 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ This package turns raw sources and generated markdown knowledge into a versionab
|
|
|
11
11
|
- [CLI](#cli) — `init` → `source-add` → `index` → `search` → `lint`
|
|
12
12
|
- [Design](#design) — the invariants (immutable sources, cited claims, deterministic graph)
|
|
13
13
|
- [Agent-Eval integration](#agent-eval-integration) — readiness bundles + release reports
|
|
14
|
+
- [Memory adapters](#memory-adapters) — generic memory contract + Neo4j Agent Memory bridge
|
|
14
15
|
- [Research loop](#research-loop) — `runKnowledgeResearchLoop` + control-loop adapter
|
|
15
16
|
- [Researcher profile](#researcher-profile) — sandbox `AgentProfile` for `runLoop`
|
|
16
17
|
- [Pluggable knowledge sources](#pluggable-knowledge-sources) — live authorities → eval re-runs
|
|
@@ -106,6 +107,10 @@ from `@tangle-network/agent-knowledge`.
|
|
|
106
107
|
|
|
107
108
|
The `/viz` subpath exports graph insight helpers without UI dependencies.
|
|
108
109
|
|
|
110
|
+
The `/memory` subpath exports an optional memory adapter contract. Use it to
|
|
111
|
+
bridge episodic or graph-native memory systems into the same source-grounded
|
|
112
|
+
readiness/eval machinery without making `agent-knowledge` own the database.
|
|
113
|
+
|
|
109
114
|
## Agent-Eval Integration
|
|
110
115
|
|
|
111
116
|
To answer whether a candidate knowledge base actually improves agent task success, run an `@tangle-network/agent-eval` improvement loop (`runImprovementLoop`) over your KB variants on a real task corpus; each run is scored into a `RunRecord`.
|
|
@@ -143,6 +148,38 @@ Pass `readiness.report` to `blockingKnowledgeEval()` from
|
|
|
143
148
|
`@tangle-network/agent-eval`; use `readiness.questions` and
|
|
144
149
|
`readiness.acquisitionPlans` to drive UI or connector workflows.
|
|
145
150
|
|
|
151
|
+
## Memory Adapters
|
|
152
|
+
|
|
153
|
+
`agent-knowledge` does not store operational memory itself. It defines the
|
|
154
|
+
contract that lets a runtime read/write memory through any backend, then turn
|
|
155
|
+
memory hits into `SourceRecord` evidence for readiness, linting, and eval gates.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import {
|
|
159
|
+
createNeo4jAgentMemoryAdapter,
|
|
160
|
+
memoryHitToSourceRecord,
|
|
161
|
+
} from '@tangle-network/agent-knowledge/memory'
|
|
162
|
+
|
|
163
|
+
const memory = createNeo4jAgentMemoryAdapter({ client: neo4jMemoryClient })
|
|
164
|
+
|
|
165
|
+
const context = await memory.getContext('What does this user prefer?', {
|
|
166
|
+
scope: { userId: 'user-123', sessionId: 'session-456' },
|
|
167
|
+
limit: 5,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const sourceRecords = context.hits.map((hit) =>
|
|
171
|
+
memoryHitToSourceRecord(hit, { scope: { userId: 'user-123' } }),
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The Neo4j adapter is runtime dependency-free: pass the real
|
|
176
|
+
`@neo4j-labs/agent-memory` client in products, or a fake client in tests. CI
|
|
177
|
+
typechecks against `@neo4j-labs/agent-memory@0.4.0` and covers the published
|
|
178
|
+
TypeScript SDK surface: `shortTerm.addMessage/searchMessages/getContext`,
|
|
179
|
+
`longTerm.addEntity/addPreference/addFact/searchEntities/searchPreferences`,
|
|
180
|
+
and `reasoning.getSimilarTraces`. Generic `search` / `getContext` and
|
|
181
|
+
snake_case bridge-style methods remain supported for non-hosted clients.
|
|
182
|
+
|
|
146
183
|
## Research Loop
|
|
147
184
|
|
|
148
185
|
Use `runKnowledgeResearchLoop()` when an agent is acting as a researcher or
|
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sha256,
|
|
3
|
+
stableId
|
|
4
|
+
} from "./chunk-YMKHCTS2.js";
|
|
5
|
+
|
|
6
|
+
// src/memory/source-record.ts
|
|
7
|
+
function memoryHitToSourceRecord(hit, options = {}) {
|
|
8
|
+
const contentHash = sha256(`${hit.uri}
|
|
9
|
+
${hit.text}`);
|
|
10
|
+
return {
|
|
11
|
+
id: stableId("src", `${hit.uri}:${contentHash}`),
|
|
12
|
+
uri: hit.uri,
|
|
13
|
+
title: hit.title ?? `${hit.kind}:${hit.id}`,
|
|
14
|
+
mediaType: "text/plain",
|
|
15
|
+
contentHash,
|
|
16
|
+
text: hit.text,
|
|
17
|
+
validUntil: hit.validUntil,
|
|
18
|
+
lastVerifiedAt: hit.lastVerifiedAt,
|
|
19
|
+
createdAt: (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
20
|
+
metadata: {
|
|
21
|
+
...hit.metadata ?? {},
|
|
22
|
+
source: "agent-memory",
|
|
23
|
+
memoryId: hit.id,
|
|
24
|
+
memoryKind: hit.kind,
|
|
25
|
+
score: hit.score,
|
|
26
|
+
normalizedScore: hit.normalizedScore,
|
|
27
|
+
confidence: hit.confidence,
|
|
28
|
+
scope: options.scope
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function memoryWriteResultToSourceRecord(result, text, options = {}) {
|
|
33
|
+
return memoryHitToSourceRecord(
|
|
34
|
+
{
|
|
35
|
+
id: result.id,
|
|
36
|
+
uri: result.uri,
|
|
37
|
+
kind: result.kind,
|
|
38
|
+
text,
|
|
39
|
+
metadata: result.metadata
|
|
40
|
+
},
|
|
41
|
+
options
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/memory/adapter.ts
|
|
46
|
+
async function defaultGetMemoryContext(adapter, query, options = {}) {
|
|
47
|
+
const hits = await adapter.search(query, options);
|
|
48
|
+
return {
|
|
49
|
+
query,
|
|
50
|
+
hits,
|
|
51
|
+
sourceRecords: hits.map((hit) => memoryHitToSourceRecord(hit, { scope: options.scope })),
|
|
52
|
+
text: renderMemoryContext(hits)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function renderMemoryContext(hits) {
|
|
56
|
+
return hits.map((hit, index) => {
|
|
57
|
+
const label = hit.title ?? `${hit.kind}:${hit.id}`;
|
|
58
|
+
const score = typeof hit.normalizedScore === "number" ? ` score=${hit.normalizedScore.toFixed(3)}` : "";
|
|
59
|
+
return [`[${index + 1}] ${label}${score}`, hit.text].join("\n");
|
|
60
|
+
}).join("\n\n");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/memory/neo4j.ts
|
|
64
|
+
function createNeo4jAgentMemoryAdapter(options) {
|
|
65
|
+
const client = options.client;
|
|
66
|
+
const id = options.id ?? "neo4j-agent-memory";
|
|
67
|
+
return {
|
|
68
|
+
id,
|
|
69
|
+
async search(query, searchOptions = {}) {
|
|
70
|
+
const sdkHits = await searchNeo4jMemory(client, query, searchOptions, id);
|
|
71
|
+
if (sdkHits.length > 0) return sdkHits;
|
|
72
|
+
const result = await callOptional(
|
|
73
|
+
client,
|
|
74
|
+
["search", "memory_search"],
|
|
75
|
+
[query, neo4jOptions(searchOptions)]
|
|
76
|
+
);
|
|
77
|
+
if (result !== void 0) return normalizeHits(result, searchOptions, id);
|
|
78
|
+
const context = await callOptional(
|
|
79
|
+
client,
|
|
80
|
+
["getContext", "get_context"],
|
|
81
|
+
[query, neo4jOptions(searchOptions)]
|
|
82
|
+
);
|
|
83
|
+
if (context !== void 0) return normalizeHits(context, searchOptions, id);
|
|
84
|
+
return [];
|
|
85
|
+
},
|
|
86
|
+
async getContext(query, searchOptions = {}) {
|
|
87
|
+
const shortTerm = nested(client, ["shortTerm", "short_term"], {});
|
|
88
|
+
const sessionId = searchOptions.scope?.sessionId;
|
|
89
|
+
if (sessionId) {
|
|
90
|
+
const conversationContext = await callOptional(
|
|
91
|
+
shortTerm,
|
|
92
|
+
["getContext", "get_context"],
|
|
93
|
+
[sessionId]
|
|
94
|
+
);
|
|
95
|
+
if (conversationContext !== void 0) {
|
|
96
|
+
const hits2 = normalizeConversationContextHits(conversationContext, searchOptions, id);
|
|
97
|
+
const text = textFromConversationContext(conversationContext) ?? (typeof conversationContext === "string" ? conversationContext : hits2.length > 0 ? renderHits(hits2) : "");
|
|
98
|
+
return {
|
|
99
|
+
query,
|
|
100
|
+
text,
|
|
101
|
+
hits: hits2,
|
|
102
|
+
sourceRecords: hits2.map(
|
|
103
|
+
(hit) => memoryHitToSourceRecord(hit, { scope: searchOptions.scope })
|
|
104
|
+
),
|
|
105
|
+
metadata: { adapter: id, rawContext: conversationContext }
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const result = await callOptional(
|
|
110
|
+
client,
|
|
111
|
+
["getContext", "get_context"],
|
|
112
|
+
[query, neo4jOptions(searchOptions)]
|
|
113
|
+
);
|
|
114
|
+
if (result === void 0) {
|
|
115
|
+
return defaultGetMemoryContext(
|
|
116
|
+
{
|
|
117
|
+
search: async () => {
|
|
118
|
+
const sdkHits = await searchNeo4jMemory(client, query, searchOptions, id);
|
|
119
|
+
if (sdkHits.length > 0) return sdkHits;
|
|
120
|
+
const searchResult = await callOptional(
|
|
121
|
+
client,
|
|
122
|
+
["search", "memory_search"],
|
|
123
|
+
[query, neo4jOptions(searchOptions)]
|
|
124
|
+
);
|
|
125
|
+
return normalizeHits(searchResult, searchOptions, id);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
query,
|
|
129
|
+
searchOptions
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (typeof result === "string") {
|
|
133
|
+
const hit = {
|
|
134
|
+
id: stableId("mem", `${id}:${query}:${result}`),
|
|
135
|
+
uri: `memory://${id}/context/${stableId("ctx", query)}`,
|
|
136
|
+
kind: "fact",
|
|
137
|
+
text: result,
|
|
138
|
+
title: "Memory context",
|
|
139
|
+
normalizedScore: 1
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
query,
|
|
143
|
+
text: result,
|
|
144
|
+
hits: [hit],
|
|
145
|
+
sourceRecords: [memoryHitToSourceRecord(hit, { scope: searchOptions.scope })],
|
|
146
|
+
metadata: { adapter: id }
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const hits = normalizeHits(result, searchOptions, id);
|
|
150
|
+
if (hits.length > 0)
|
|
151
|
+
return defaultGetMemoryContext({ search: async () => hits }, query, searchOptions);
|
|
152
|
+
return defaultGetMemoryContext({ search: async () => [] }, query, searchOptions);
|
|
153
|
+
},
|
|
154
|
+
async write(input) {
|
|
155
|
+
const result = await writeNeo4jMemory(client, input, id);
|
|
156
|
+
return {
|
|
157
|
+
...result,
|
|
158
|
+
sourceRecord: memoryWriteResultToSourceRecord(result, input.text, { scope: input.scope })
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async function writeNeo4jMemory(client, input, adapterId) {
|
|
164
|
+
const scope = input.scope ?? {};
|
|
165
|
+
const sessionId = scope.sessionId ?? input.metadata?.sessionId;
|
|
166
|
+
let result;
|
|
167
|
+
if (input.kind === "message") {
|
|
168
|
+
const shortTerm = nested(client, ["shortTerm", "short_term"], client);
|
|
169
|
+
result = await callOptional(
|
|
170
|
+
shortTerm,
|
|
171
|
+
["addMessage"],
|
|
172
|
+
[
|
|
173
|
+
sessionId,
|
|
174
|
+
input.role ?? "user",
|
|
175
|
+
input.text,
|
|
176
|
+
{
|
|
177
|
+
metadata: input.metadata,
|
|
178
|
+
conversationId: sessionId,
|
|
179
|
+
userId: scope.userId
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
);
|
|
183
|
+
if (result === void 0) {
|
|
184
|
+
result = await callRequired(
|
|
185
|
+
shortTerm,
|
|
186
|
+
["add_message"],
|
|
187
|
+
[
|
|
188
|
+
{
|
|
189
|
+
session_id: sessionId,
|
|
190
|
+
sessionId,
|
|
191
|
+
role: input.role ?? "user",
|
|
192
|
+
content: input.text,
|
|
193
|
+
user_identifier: scope.userId,
|
|
194
|
+
userIdentifier: scope.userId,
|
|
195
|
+
metadata: input.metadata
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
} else if (input.kind === "entity") {
|
|
201
|
+
const longTerm = nested(client, ["longTerm", "long_term"], client);
|
|
202
|
+
result = await callOptional(
|
|
203
|
+
longTerm,
|
|
204
|
+
["addEntity"],
|
|
205
|
+
[
|
|
206
|
+
input.entityName ?? input.title ?? input.text,
|
|
207
|
+
input.entityType ?? "ENTITY",
|
|
208
|
+
neo4jEntityOptions(input)
|
|
209
|
+
]
|
|
210
|
+
);
|
|
211
|
+
if (result === void 0) {
|
|
212
|
+
result = await callRequired(
|
|
213
|
+
longTerm,
|
|
214
|
+
["add_entity"],
|
|
215
|
+
[
|
|
216
|
+
input.entityName ?? input.title ?? input.text,
|
|
217
|
+
input.entityType ?? "ENTITY",
|
|
218
|
+
neo4jWriteOptions(input)
|
|
219
|
+
]
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
} else if (input.kind === "preference") {
|
|
223
|
+
const longTerm = nested(client, ["longTerm", "long_term"], client);
|
|
224
|
+
result = await callOptional(
|
|
225
|
+
longTerm,
|
|
226
|
+
["addPreference"],
|
|
227
|
+
[input.category ?? "general", input.text, neo4jPreferenceOptions(input)]
|
|
228
|
+
);
|
|
229
|
+
if (result === void 0) {
|
|
230
|
+
result = await callRequired(
|
|
231
|
+
longTerm,
|
|
232
|
+
["add_preference"],
|
|
233
|
+
[input.category ?? "general", input.text, neo4jWriteOptions(input)]
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
result = await callRequired(
|
|
238
|
+
nested(client, ["longTerm", "long_term"], client),
|
|
239
|
+
["addFact", "add_fact"],
|
|
240
|
+
[
|
|
241
|
+
input.subject ?? input.title ?? input.kind,
|
|
242
|
+
input.predicate ?? "states",
|
|
243
|
+
input.object ?? input.text
|
|
244
|
+
]
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const id = idFromResult(result) ?? input.id ?? stableId("mem", `${input.kind}:${input.text}`);
|
|
248
|
+
return {
|
|
249
|
+
accepted: true,
|
|
250
|
+
id,
|
|
251
|
+
uri: `memory://${adapterId}/${encodeURIComponent(id)}`,
|
|
252
|
+
kind: input.kind,
|
|
253
|
+
metadata: { rawResult: result }
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async function searchNeo4jMemory(client, query, options, adapterId) {
|
|
257
|
+
const searches = [];
|
|
258
|
+
const kinds = options.kinds;
|
|
259
|
+
const includeKind = (kind) => !kinds?.length || kinds.includes(kind);
|
|
260
|
+
const shortTerm = nested(client, ["shortTerm", "short_term"], {});
|
|
261
|
+
const longTerm = nested(client, ["longTerm", "long_term"], {});
|
|
262
|
+
const reasoning = nested(client, ["reasoning"], {});
|
|
263
|
+
if (includeKind("message")) {
|
|
264
|
+
searches.push(
|
|
265
|
+
callOptional(
|
|
266
|
+
shortTerm,
|
|
267
|
+
["searchMessages", "search_messages"],
|
|
268
|
+
[query, searchMessagesOptions(options)]
|
|
269
|
+
).then((result) => normalizeHits(result, { ...options, kinds: ["message"] }, adapterId))
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
if (includeKind("entity")) {
|
|
273
|
+
searches.push(
|
|
274
|
+
callOptional(
|
|
275
|
+
longTerm,
|
|
276
|
+
["searchEntities", "search_entities"],
|
|
277
|
+
[query, searchEntitiesOptions(options)]
|
|
278
|
+
).then((result) => normalizeHits(result, { ...options, kinds: ["entity"] }, adapterId))
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (includeKind("preference")) {
|
|
282
|
+
searches.push(
|
|
283
|
+
callOptional(
|
|
284
|
+
longTerm,
|
|
285
|
+
["searchPreferences", "search_preferences"],
|
|
286
|
+
[query, searchPreferencesOptions(options)]
|
|
287
|
+
).then((result) => normalizeHits(result, { ...options, kinds: ["preference"] }, adapterId))
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
if (includeKind("reasoning-trace")) {
|
|
291
|
+
searches.push(
|
|
292
|
+
callOptional(
|
|
293
|
+
reasoning,
|
|
294
|
+
["getSimilarTraces", "get_similar_traces"],
|
|
295
|
+
[query, similarTracesOptions(options)]
|
|
296
|
+
).then(
|
|
297
|
+
(result) => normalizeHits(result, { ...options, kinds: ["reasoning-trace"] }, adapterId)
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const hits = (await Promise.all(searches)).flat();
|
|
302
|
+
return hits.sort((a, b) => (b.normalizedScore ?? b.score ?? 0) - (a.normalizedScore ?? a.score ?? 0)).slice(0, options.limit);
|
|
303
|
+
}
|
|
304
|
+
function neo4jOptions(options) {
|
|
305
|
+
return {
|
|
306
|
+
limit: options.limit,
|
|
307
|
+
k: options.limit,
|
|
308
|
+
min_score: options.minScore,
|
|
309
|
+
minScore: options.minScore,
|
|
310
|
+
user_identifier: options.scope?.userId,
|
|
311
|
+
userIdentifier: options.scope?.userId,
|
|
312
|
+
session_id: options.scope?.sessionId,
|
|
313
|
+
sessionId: options.scope?.sessionId,
|
|
314
|
+
namespace: options.scope?.namespace,
|
|
315
|
+
tenant_id: options.scope?.tenantId,
|
|
316
|
+
tenantId: options.scope?.tenantId,
|
|
317
|
+
kinds: options.kinds,
|
|
318
|
+
metadata: options.metadata
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function searchMessagesOptions(options) {
|
|
322
|
+
return {
|
|
323
|
+
limit: options.limit,
|
|
324
|
+
sessionId: options.scope?.sessionId,
|
|
325
|
+
conversationId: options.scope?.sessionId,
|
|
326
|
+
threshold: options.minScore,
|
|
327
|
+
metadata: options.metadata
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function searchEntitiesOptions(options) {
|
|
331
|
+
return {
|
|
332
|
+
limit: options.limit,
|
|
333
|
+
type: options.metadata?.type
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function searchPreferencesOptions(options) {
|
|
337
|
+
return {
|
|
338
|
+
limit: options.limit,
|
|
339
|
+
category: options.metadata?.category
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function similarTracesOptions(options) {
|
|
343
|
+
return {
|
|
344
|
+
limit: options.limit,
|
|
345
|
+
sessionId: options.scope?.sessionId,
|
|
346
|
+
successOnly: options.metadata?.successOnly
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function neo4jWriteOptions(input) {
|
|
350
|
+
return {
|
|
351
|
+
user_identifier: input.scope?.userId,
|
|
352
|
+
userIdentifier: input.scope?.userId,
|
|
353
|
+
session_id: input.scope?.sessionId,
|
|
354
|
+
sessionId: input.scope?.sessionId,
|
|
355
|
+
namespace: input.scope?.namespace,
|
|
356
|
+
confidence: input.confidence,
|
|
357
|
+
metadata: input.metadata
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function neo4jEntityOptions(input) {
|
|
361
|
+
return {
|
|
362
|
+
description: input.metadata?.description ?? input.title
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function neo4jPreferenceOptions(input) {
|
|
366
|
+
return {
|
|
367
|
+
context: input.metadata?.context ?? input.title
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
async function callOptional(target, names, args) {
|
|
371
|
+
for (const name of names) {
|
|
372
|
+
const fn = target[name];
|
|
373
|
+
if (typeof fn === "function") return await fn.apply(target, args);
|
|
374
|
+
}
|
|
375
|
+
return void 0;
|
|
376
|
+
}
|
|
377
|
+
async function callRequired(target, names, args) {
|
|
378
|
+
const result = await callOptional(target, names, args);
|
|
379
|
+
if (result !== void 0) return result;
|
|
380
|
+
throw new Error(`Neo4j agent-memory client is missing method ${names.join(" or ")}`);
|
|
381
|
+
}
|
|
382
|
+
function nested(target, names, fallback) {
|
|
383
|
+
for (const name of names) {
|
|
384
|
+
const value = target[name];
|
|
385
|
+
if (value && typeof value === "object") return value;
|
|
386
|
+
}
|
|
387
|
+
return fallback;
|
|
388
|
+
}
|
|
389
|
+
function normalizeHits(value, options, adapterId) {
|
|
390
|
+
const rawHits = rawHitsFrom(value);
|
|
391
|
+
return rawHits.map((hit, index) => normalizeHit(hit, index, adapterId)).filter((hit) => hit !== null).filter(
|
|
392
|
+
(hit) => options.minScore === void 0 ? true : (hit.normalizedScore ?? hit.score ?? 0) >= options.minScore
|
|
393
|
+
).filter((hit) => options.kinds?.length ? options.kinds.includes(hit.kind) : true).slice(0, options.limit);
|
|
394
|
+
}
|
|
395
|
+
function normalizeHit(value, index, adapterId) {
|
|
396
|
+
if (typeof value === "string") {
|
|
397
|
+
return {
|
|
398
|
+
id: stableId("mem", value),
|
|
399
|
+
uri: `memory://${adapterId}/${stableId("mem", value)}`,
|
|
400
|
+
kind: "fact",
|
|
401
|
+
text: value,
|
|
402
|
+
normalizedScore: index === 0 ? 1 : void 0
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const obj = record(value);
|
|
406
|
+
if (!obj) return null;
|
|
407
|
+
const kind = memoryKind(stringField(obj, ["kind", "type", "label"]), obj);
|
|
408
|
+
const text = textFromHitObject(obj, kind);
|
|
409
|
+
if (!text) return null;
|
|
410
|
+
const id = stringField(obj, ["id", "memoryId", "uuid"]) ?? stableId("mem", text);
|
|
411
|
+
return {
|
|
412
|
+
id,
|
|
413
|
+
uri: stringField(obj, ["uri"]) ?? `memory://${adapterId}/${encodeURIComponent(id)}`,
|
|
414
|
+
kind,
|
|
415
|
+
text,
|
|
416
|
+
title: stringField(obj, ["title", "name", "task", "subject"]),
|
|
417
|
+
score: numberField(obj, ["score"]),
|
|
418
|
+
normalizedScore: numberField(obj, ["normalizedScore", "normalized_score"]),
|
|
419
|
+
confidence: numberField(obj, ["confidence"]),
|
|
420
|
+
createdAt: stringField(obj, ["createdAt", "created_at"]),
|
|
421
|
+
validUntil: stringField(obj, ["validUntil", "valid_until"]),
|
|
422
|
+
lastVerifiedAt: stringField(obj, ["lastVerifiedAt", "last_verified_at"]),
|
|
423
|
+
metadata: obj
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
function memoryKind(value, obj = {}) {
|
|
427
|
+
if (value === "message" || value === "entity" || value === "fact" || value === "preference" || value === "observation" || value === "reasoning-trace") {
|
|
428
|
+
return value;
|
|
429
|
+
}
|
|
430
|
+
if ("role" in obj && "content" in obj) return "message";
|
|
431
|
+
if ("name" in obj && ("type" in obj || "entityType" in obj)) return "entity";
|
|
432
|
+
if ("preference" in obj && "category" in obj) return "preference";
|
|
433
|
+
if ("task" in obj && "steps" in obj) return "reasoning-trace";
|
|
434
|
+
return "fact";
|
|
435
|
+
}
|
|
436
|
+
function rawHitsFrom(value) {
|
|
437
|
+
if (Array.isArray(value)) return value;
|
|
438
|
+
const obj = record(value);
|
|
439
|
+
if (!obj) return typeof value === "string" ? [value] : [];
|
|
440
|
+
if (Array.isArray(obj.hits)) return obj.hits;
|
|
441
|
+
if (Array.isArray(obj.results)) return obj.results;
|
|
442
|
+
if (Array.isArray(obj.messages)) return obj.messages;
|
|
443
|
+
if (Array.isArray(obj.recentMessages)) return obj.recentMessages;
|
|
444
|
+
if (Array.isArray(obj.observations)) return obj.observations;
|
|
445
|
+
if (Array.isArray(obj.reflections)) return obj.reflections;
|
|
446
|
+
if (typeof obj.content === "string" || typeof obj.text === "string") return [obj];
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
function textFromHitObject(obj, kind) {
|
|
450
|
+
const direct = stringField(obj, [
|
|
451
|
+
"text",
|
|
452
|
+
"content",
|
|
453
|
+
"body",
|
|
454
|
+
"summary",
|
|
455
|
+
"preference",
|
|
456
|
+
"description"
|
|
457
|
+
]);
|
|
458
|
+
if (direct) return direct;
|
|
459
|
+
if (kind === "entity") return stringField(obj, ["name"]);
|
|
460
|
+
if (kind === "fact") {
|
|
461
|
+
const subject = stringField(obj, ["subject"]);
|
|
462
|
+
const predicate = stringField(obj, ["predicate"]);
|
|
463
|
+
const object = stringField(obj, ["object", "obj"]);
|
|
464
|
+
if (subject && predicate && object) return `${subject} ${predicate} ${object}`;
|
|
465
|
+
}
|
|
466
|
+
if (kind === "reasoning-trace") {
|
|
467
|
+
const task = stringField(obj, ["task"]);
|
|
468
|
+
const outcome = stringField(obj, ["outcome"]);
|
|
469
|
+
return [task, outcome].filter(Boolean).join("\n") || void 0;
|
|
470
|
+
}
|
|
471
|
+
return void 0;
|
|
472
|
+
}
|
|
473
|
+
function textFromConversationContext(value) {
|
|
474
|
+
const obj = record(value);
|
|
475
|
+
if (!obj) return void 0;
|
|
476
|
+
const parts = [
|
|
477
|
+
...rawHitsFrom(obj.reflections).map((hit) => normalizeContextPart(hit)),
|
|
478
|
+
...rawHitsFrom(obj.observations).map((hit) => normalizeContextPart(hit)),
|
|
479
|
+
...rawHitsFrom(obj.recentMessages).map((hit) => normalizeContextPart(hit))
|
|
480
|
+
].filter(Boolean);
|
|
481
|
+
return parts.length > 0 ? parts.join("\n\n") : void 0;
|
|
482
|
+
}
|
|
483
|
+
function normalizeConversationContextHits(value, options, adapterId) {
|
|
484
|
+
const obj = record(value);
|
|
485
|
+
if (!obj) return normalizeHits(value, options, adapterId);
|
|
486
|
+
const raw = [
|
|
487
|
+
...rawHitsFrom(obj.reflections).map((hit) => tagContextHit(hit, "observation", "Reflection")),
|
|
488
|
+
...rawHitsFrom(obj.observations).map((hit) => tagContextHit(hit, "observation", "Observation")),
|
|
489
|
+
...rawHitsFrom(obj.recentMessages).map((hit) => tagContextHit(hit, "message"))
|
|
490
|
+
];
|
|
491
|
+
return raw.length > 0 ? normalizeHits(raw, options, adapterId) : normalizeHits(value, options, adapterId);
|
|
492
|
+
}
|
|
493
|
+
function tagContextHit(value, kind, title) {
|
|
494
|
+
return value && typeof value === "object" && !Array.isArray(value) ? { kind, title, ...value } : { kind, title, content: value };
|
|
495
|
+
}
|
|
496
|
+
function normalizeContextPart(value) {
|
|
497
|
+
if (typeof value === "string") return value;
|
|
498
|
+
const obj = record(value);
|
|
499
|
+
return obj ? textFromHitObject(obj, memoryKind(stringField(obj, ["kind", "type", "label"]), obj)) : void 0;
|
|
500
|
+
}
|
|
501
|
+
function renderHits(hits) {
|
|
502
|
+
return hits.map((hit) => hit.text).join("\n\n");
|
|
503
|
+
}
|
|
504
|
+
function idFromResult(value) {
|
|
505
|
+
const obj = record(value);
|
|
506
|
+
return obj ? stringField(obj, ["id", "memoryId", "uuid"]) : void 0;
|
|
507
|
+
}
|
|
508
|
+
function record(value) {
|
|
509
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
510
|
+
}
|
|
511
|
+
function stringField(obj, names) {
|
|
512
|
+
for (const name of names) {
|
|
513
|
+
const value = obj[name];
|
|
514
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
515
|
+
}
|
|
516
|
+
return void 0;
|
|
517
|
+
}
|
|
518
|
+
function numberField(obj, names) {
|
|
519
|
+
for (const name of names) {
|
|
520
|
+
const value = obj[name];
|
|
521
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
522
|
+
}
|
|
523
|
+
return void 0;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// src/memory/schemas.ts
|
|
527
|
+
import { z } from "zod";
|
|
528
|
+
var AgentMemoryKindSchema = z.enum([
|
|
529
|
+
"message",
|
|
530
|
+
"entity",
|
|
531
|
+
"fact",
|
|
532
|
+
"preference",
|
|
533
|
+
"observation",
|
|
534
|
+
"reasoning-trace"
|
|
535
|
+
]);
|
|
536
|
+
var AgentMemoryScopeSchema = z.object({
|
|
537
|
+
tenantId: z.string().optional(),
|
|
538
|
+
userId: z.string().optional(),
|
|
539
|
+
sessionId: z.string().optional(),
|
|
540
|
+
namespace: z.string().optional(),
|
|
541
|
+
tags: z.record(z.string(), z.string()).optional()
|
|
542
|
+
});
|
|
543
|
+
var AgentMemoryHitSchema = z.object({
|
|
544
|
+
id: z.string().min(1),
|
|
545
|
+
uri: z.string().min(1),
|
|
546
|
+
kind: AgentMemoryKindSchema,
|
|
547
|
+
text: z.string().min(1),
|
|
548
|
+
title: z.string().optional(),
|
|
549
|
+
score: z.number().optional(),
|
|
550
|
+
normalizedScore: z.number().optional(),
|
|
551
|
+
confidence: z.number().optional(),
|
|
552
|
+
createdAt: z.string().optional(),
|
|
553
|
+
validUntil: z.string().optional(),
|
|
554
|
+
lastVerifiedAt: z.string().optional(),
|
|
555
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
556
|
+
});
|
|
557
|
+
var AgentMemoryWriteInputSchema = z.object({
|
|
558
|
+
kind: AgentMemoryKindSchema,
|
|
559
|
+
text: z.string().min(1),
|
|
560
|
+
id: z.string().optional(),
|
|
561
|
+
title: z.string().optional(),
|
|
562
|
+
role: z.enum(["system", "user", "assistant", "tool"]).optional(),
|
|
563
|
+
entityName: z.string().optional(),
|
|
564
|
+
entityType: z.string().optional(),
|
|
565
|
+
category: z.string().optional(),
|
|
566
|
+
predicate: z.string().optional(),
|
|
567
|
+
subject: z.string().optional(),
|
|
568
|
+
object: z.string().optional(),
|
|
569
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
570
|
+
scope: AgentMemoryScopeSchema.optional(),
|
|
571
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
export {
|
|
575
|
+
memoryHitToSourceRecord,
|
|
576
|
+
memoryWriteResultToSourceRecord,
|
|
577
|
+
defaultGetMemoryContext,
|
|
578
|
+
renderMemoryContext,
|
|
579
|
+
createNeo4jAgentMemoryAdapter,
|
|
580
|
+
AgentMemoryKindSchema,
|
|
581
|
+
AgentMemoryScopeSchema,
|
|
582
|
+
AgentMemoryHitSchema,
|
|
583
|
+
AgentMemoryWriteInputSchema
|
|
584
|
+
};
|
|
585
|
+
//# sourceMappingURL=chunk-U6KQ4FS3.js.map
|