@neo4j-labs/agent-memory 0.3.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/CHANGELOG.md +79 -0
- package/LICENSE +190 -0
- package/README.md +154 -0
- package/dist/chunk-ASQMU7YC.js +58 -0
- package/dist/chunk-ASQMU7YC.js.map +1 -0
- package/dist/chunk-TGBKROHO.js +226 -0
- package/dist/chunk-TGBKROHO.js.map +1 -0
- package/dist/client-DSqbWQoa.d.ts +551 -0
- package/dist/index-qfRrdQNP.d.ts +42 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +1313 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/langchain.d.ts +56 -0
- package/dist/integrations/langchain.js +69 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/mastra.d.ts +56 -0
- package/dist/integrations/mastra.js +65 -0
- package/dist/integrations/mastra.js.map +1 -0
- package/dist/integrations/strands.d.ts +239 -0
- package/dist/integrations/strands.js +413 -0
- package/dist/integrations/strands.js.map +1 -0
- package/dist/mcp/index.d.ts +53 -0
- package/dist/mcp/index.js +256 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/middleware/vercel-ai.d.ts +86 -0
- package/dist/middleware/vercel-ai.js +107 -0
- package/dist/middleware/vercel-ai.js.map +1 -0
- package/dist/testing.d.ts +37 -0
- package/dist/testing.js +4 -0
- package/dist/testing.js.map +1 -0
- package/package.json +86 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
import { extractRequestId, supportsUserAgentHeader, defaultUserAgent, BridgeTransport } from './chunk-TGBKROHO.js';
|
|
2
|
+
export { VERSION } from './chunk-TGBKROHO.js';
|
|
3
|
+
import { ConnectionError, AuthenticationError, NotSupportedError, TransportError, ValidationError } from './chunk-ASQMU7YC.js';
|
|
4
|
+
export { AuthenticationError, ConnectionError, MemoryError, NotFoundError, NotSupportedError, TransportError, ValidationError } from './chunk-ASQMU7YC.js';
|
|
5
|
+
|
|
6
|
+
// src/auth/index.ts
|
|
7
|
+
function toApiKey(w) {
|
|
8
|
+
return {
|
|
9
|
+
id: w.id,
|
|
10
|
+
label: w.label,
|
|
11
|
+
scopes: w.scopes ?? [],
|
|
12
|
+
workspaceId: w.workspace_id,
|
|
13
|
+
createdAt: w.created_at,
|
|
14
|
+
expiresAt: w.expires_at,
|
|
15
|
+
key: w.key
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
var AuthClient = class {
|
|
19
|
+
constructor(transport) {
|
|
20
|
+
this.transport = transport;
|
|
21
|
+
}
|
|
22
|
+
/** List API keys for a workspace (no plaintext). */
|
|
23
|
+
async listApiKeys(workspaceId) {
|
|
24
|
+
const wire = await this.transport.request("list_api_keys", {
|
|
25
|
+
workspace_id: workspaceId
|
|
26
|
+
});
|
|
27
|
+
return (wire ?? []).map(toApiKey);
|
|
28
|
+
}
|
|
29
|
+
/** Create a new API key. The plaintext value is returned only once. */
|
|
30
|
+
async createApiKey(input) {
|
|
31
|
+
const wire = await this.transport.request("create_api_key", {
|
|
32
|
+
label: input.label,
|
|
33
|
+
scopes: input.scopes,
|
|
34
|
+
workspace_id: input.workspaceId
|
|
35
|
+
});
|
|
36
|
+
return toApiKey(wire);
|
|
37
|
+
}
|
|
38
|
+
/** Revoke (delete) an API key by id. */
|
|
39
|
+
async revokeApiKey(keyId) {
|
|
40
|
+
await this.transport.request("revoke_api_key", { key_id: keyId });
|
|
41
|
+
}
|
|
42
|
+
/** Reveal the plaintext value of a stored API key. */
|
|
43
|
+
async revealApiKey(keyId, workspaceId) {
|
|
44
|
+
const wire = await this.transport.request("reveal_api_key", {
|
|
45
|
+
key_id: keyId,
|
|
46
|
+
workspace_id: workspaceId
|
|
47
|
+
});
|
|
48
|
+
return toApiKey(wire);
|
|
49
|
+
}
|
|
50
|
+
/** Exchange a refresh token for a fresh access/refresh pair. */
|
|
51
|
+
async refreshAccessToken(refreshToken) {
|
|
52
|
+
const wire = await this.transport.request("refresh_access_token", {
|
|
53
|
+
refresh_token: refreshToken
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
accessToken: wire.access_token,
|
|
57
|
+
refreshToken: wire.refresh_token,
|
|
58
|
+
expiresIn: wire.expires_in
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/long-term/index.ts
|
|
64
|
+
function toEntity(w) {
|
|
65
|
+
return {
|
|
66
|
+
id: w.id,
|
|
67
|
+
name: w.name,
|
|
68
|
+
type: w.type,
|
|
69
|
+
subtype: w.subtype,
|
|
70
|
+
description: w.description,
|
|
71
|
+
embedding: w.embedding,
|
|
72
|
+
canonicalName: w.canonical_name,
|
|
73
|
+
createdAt: w.created_at ?? "",
|
|
74
|
+
updatedAt: w.updated_at,
|
|
75
|
+
confidence: w.confidence,
|
|
76
|
+
sourceStage: w.source_stage,
|
|
77
|
+
relationships: w.relationships?.map(toRelRef)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function toRelRef(w) {
|
|
81
|
+
return {
|
|
82
|
+
id: w.id,
|
|
83
|
+
type: w.type,
|
|
84
|
+
targetId: w.target_id,
|
|
85
|
+
targetName: w.target_name,
|
|
86
|
+
properties: w.properties
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function toPreference(w) {
|
|
90
|
+
return {
|
|
91
|
+
id: w.id,
|
|
92
|
+
category: w.category,
|
|
93
|
+
preference: w.preference,
|
|
94
|
+
context: w.context,
|
|
95
|
+
embedding: w.embedding
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function toFact(w) {
|
|
99
|
+
return {
|
|
100
|
+
id: w.id,
|
|
101
|
+
subject: w.subject,
|
|
102
|
+
predicate: w.predicate,
|
|
103
|
+
object: w.object,
|
|
104
|
+
embedding: w.embedding
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function toRelationship(w) {
|
|
108
|
+
return {
|
|
109
|
+
id: w.id,
|
|
110
|
+
sourceId: w.source_id,
|
|
111
|
+
targetId: w.target_id,
|
|
112
|
+
relationshipType: w.relationship_type,
|
|
113
|
+
properties: w.properties ?? {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function toMention(w) {
|
|
117
|
+
return {
|
|
118
|
+
conversationId: w.conversation_id,
|
|
119
|
+
messageId: w.message_id,
|
|
120
|
+
content: w.content,
|
|
121
|
+
timestamp: w.timestamp
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function toGraphNode(w) {
|
|
125
|
+
return { id: w.id, name: w.name, type: w.type };
|
|
126
|
+
}
|
|
127
|
+
function toGraphEdge(w) {
|
|
128
|
+
return { id: w.id, source: w.source, target: w.target, type: w.type };
|
|
129
|
+
}
|
|
130
|
+
var LongTermMemory = class {
|
|
131
|
+
constructor(transport) {
|
|
132
|
+
this.transport = transport;
|
|
133
|
+
}
|
|
134
|
+
// ---- Silver tier (bridge) ----------------------------------------------
|
|
135
|
+
async addEntity(name, entityType, options) {
|
|
136
|
+
const wire = await this.transport.request("add_entity", {
|
|
137
|
+
name,
|
|
138
|
+
entity_type: entityType,
|
|
139
|
+
type: entityType,
|
|
140
|
+
description: options?.description
|
|
141
|
+
});
|
|
142
|
+
return toEntity(wire);
|
|
143
|
+
}
|
|
144
|
+
async addPreference(category, preference, options) {
|
|
145
|
+
const wire = await this.transport.request("add_preference", {
|
|
146
|
+
category,
|
|
147
|
+
preference,
|
|
148
|
+
context: options?.context
|
|
149
|
+
});
|
|
150
|
+
return toPreference(wire);
|
|
151
|
+
}
|
|
152
|
+
async addFact(subject, predicate, obj) {
|
|
153
|
+
const wire = await this.transport.request("add_fact", {
|
|
154
|
+
subject,
|
|
155
|
+
predicate,
|
|
156
|
+
obj
|
|
157
|
+
});
|
|
158
|
+
return toFact(wire);
|
|
159
|
+
}
|
|
160
|
+
async searchEntities(query, options) {
|
|
161
|
+
const wire = await this.transport.request("search_entities", {
|
|
162
|
+
query,
|
|
163
|
+
type: options?.type,
|
|
164
|
+
limit: options?.limit ?? 10
|
|
165
|
+
});
|
|
166
|
+
return wire.map(toEntity);
|
|
167
|
+
}
|
|
168
|
+
async searchPreferences(query, options) {
|
|
169
|
+
const wire = await this.transport.request("search_preferences", {
|
|
170
|
+
query,
|
|
171
|
+
category: options?.category,
|
|
172
|
+
limit: options?.limit ?? 10
|
|
173
|
+
});
|
|
174
|
+
return wire.map(toPreference);
|
|
175
|
+
}
|
|
176
|
+
async getEntityByName(name) {
|
|
177
|
+
const wire = await this.transport.request("get_entity_by_name", {
|
|
178
|
+
name
|
|
179
|
+
});
|
|
180
|
+
return wire ? toEntity(wire) : null;
|
|
181
|
+
}
|
|
182
|
+
async getRelatedEntities(entityId, options) {
|
|
183
|
+
const wire = await this.transport.request("get_related_entities", {
|
|
184
|
+
entity_id: entityId,
|
|
185
|
+
relationship_type: options?.relationshipType,
|
|
186
|
+
depth: options?.depth ?? 1
|
|
187
|
+
});
|
|
188
|
+
return wire.map(toEntity);
|
|
189
|
+
}
|
|
190
|
+
async addRelationship(sourceId, targetId, relationshipType, options) {
|
|
191
|
+
const wire = await this.transport.request("add_relationship", {
|
|
192
|
+
source_id: sourceId,
|
|
193
|
+
target_id: targetId,
|
|
194
|
+
relationship_type: relationshipType,
|
|
195
|
+
properties: options?.properties
|
|
196
|
+
});
|
|
197
|
+
return toRelationship(wire);
|
|
198
|
+
}
|
|
199
|
+
async mergeDuplicateEntities(sourceId, targetId, options) {
|
|
200
|
+
const wire = await this.transport.request("merge_duplicate_entities", {
|
|
201
|
+
source_id: sourceId,
|
|
202
|
+
target_id: targetId,
|
|
203
|
+
canonical_name: options?.canonicalName
|
|
204
|
+
});
|
|
205
|
+
return toEntity(wire);
|
|
206
|
+
}
|
|
207
|
+
// ---- Volume 5 / hosted-native methods -----------------------------------
|
|
208
|
+
/** List all entities, optionally filtered by entity type. */
|
|
209
|
+
async listEntities(options) {
|
|
210
|
+
const wire = await this.transport.request("list_entities", {
|
|
211
|
+
type: options?.type,
|
|
212
|
+
limit: options?.limit
|
|
213
|
+
});
|
|
214
|
+
return wire.map(toEntity);
|
|
215
|
+
}
|
|
216
|
+
/** Fetch one entity (with relationships) by id. */
|
|
217
|
+
async getEntity(entityId) {
|
|
218
|
+
const wire = await this.transport.request("get_entity", {
|
|
219
|
+
entity_id: entityId
|
|
220
|
+
});
|
|
221
|
+
return toEntity(wire);
|
|
222
|
+
}
|
|
223
|
+
/** Update an existing entity's name and/or description.
|
|
224
|
+
*
|
|
225
|
+
* The hosted PUT /v1/entities/{id} returns `{status: "updated"}` rather
|
|
226
|
+
* than the full entity, so when the response lacks an `id` we follow up
|
|
227
|
+
* with a GET to keep the SDK contract — "update returns the updated
|
|
228
|
+
* Entity". Bridge transports return the entity directly, so we tolerate
|
|
229
|
+
* both shapes.
|
|
230
|
+
*/
|
|
231
|
+
async updateEntity(entityId, options) {
|
|
232
|
+
const wire = await this.transport.request(
|
|
233
|
+
"update_entity",
|
|
234
|
+
{
|
|
235
|
+
entity_id: entityId,
|
|
236
|
+
name: options.name,
|
|
237
|
+
description: options.description
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
if (wire && typeof wire === "object" && "id" in wire && wire.id) {
|
|
241
|
+
return toEntity(wire);
|
|
242
|
+
}
|
|
243
|
+
return this.getEntity(entityId);
|
|
244
|
+
}
|
|
245
|
+
/** Delete an entity and its relationships. */
|
|
246
|
+
async deleteEntity(entityId) {
|
|
247
|
+
await this.transport.request("delete_entity", { entity_id: entityId });
|
|
248
|
+
}
|
|
249
|
+
/** Score an entity 0-1 and optionally mark it human-confirmed. */
|
|
250
|
+
async setEntityFeedback(entityId, options) {
|
|
251
|
+
const result = await this.transport.request(
|
|
252
|
+
"set_entity_feedback",
|
|
253
|
+
{
|
|
254
|
+
entity_id: entityId,
|
|
255
|
+
user_score: options.userScore,
|
|
256
|
+
confirmed: options.confirmed
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
return { id: result.id, updated: result.updated };
|
|
260
|
+
}
|
|
261
|
+
/** All cross-conversation mentions of this entity. */
|
|
262
|
+
async getEntityHistory(entityId) {
|
|
263
|
+
const wire = await this.transport.request("get_entity_history", {
|
|
264
|
+
entity_id: entityId
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
entityId: wire.entity_id,
|
|
268
|
+
mentions: (wire.mentions ?? []).map(toMention)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/** Merge `sourceId` into `targetId`, leaving a SAME_AS provenance link. */
|
|
272
|
+
async mergeEntities(sourceId, targetId) {
|
|
273
|
+
const wire = await this.transport.request("merge_entities", {
|
|
274
|
+
source_id: sourceId,
|
|
275
|
+
target_id: targetId
|
|
276
|
+
});
|
|
277
|
+
return { sourceId: wire.source_id, targetId: wire.target_id, status: wire.status };
|
|
278
|
+
}
|
|
279
|
+
/** Full-graph view of all entities + edges. Pair with NVL for visualization. */
|
|
280
|
+
async getEntityGraph() {
|
|
281
|
+
const wire = await this.transport.request("get_entity_graph", {});
|
|
282
|
+
return {
|
|
283
|
+
nodes: (wire.nodes ?? []).map(toGraphNode),
|
|
284
|
+
edges: (wire.edges ?? []).map(toGraphEdge)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// src/query/index.ts
|
|
290
|
+
var QueryConsole = class {
|
|
291
|
+
constructor(transport) {
|
|
292
|
+
this.transport = transport;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Execute a read-only Cypher query.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* const r = await client.query.cypher({
|
|
299
|
+
* cypher: "MATCH (e:Entity) RETURN e.name AS name LIMIT $n",
|
|
300
|
+
* params: { n: 10 },
|
|
301
|
+
* });
|
|
302
|
+
*/
|
|
303
|
+
async cypher(input) {
|
|
304
|
+
const wire = await this.transport.request("cypher_query", {
|
|
305
|
+
cypher: input.cypher,
|
|
306
|
+
params: input.params ?? {}
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
columns: wire.columns ?? [],
|
|
310
|
+
rows: wire.rows ?? [],
|
|
311
|
+
stats: wire.stats
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
// src/reasoning/index.ts
|
|
317
|
+
function toToolCall(w) {
|
|
318
|
+
return {
|
|
319
|
+
id: w.id,
|
|
320
|
+
toolName: w.tool_name ?? w.toolName ?? "",
|
|
321
|
+
arguments: w.arguments ?? (w.input ? safeParseObject(w.input) : {}) ?? {},
|
|
322
|
+
result: w.result ?? w.output,
|
|
323
|
+
status: w.status ?? "success",
|
|
324
|
+
durationMs: w.duration_ms ?? w.durationMs,
|
|
325
|
+
error: w.error
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function safeParseObject(input) {
|
|
329
|
+
try {
|
|
330
|
+
const parsed = JSON.parse(input);
|
|
331
|
+
return typeof parsed === "object" && parsed !== null ? parsed : { value: parsed };
|
|
332
|
+
} catch {
|
|
333
|
+
return { raw: input };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function toStep(w) {
|
|
337
|
+
return {
|
|
338
|
+
id: w.id,
|
|
339
|
+
traceId: w.trace_id ?? "",
|
|
340
|
+
stepNumber: w.step_number ?? 0,
|
|
341
|
+
thought: w.thought,
|
|
342
|
+
action: w.action,
|
|
343
|
+
observation: w.observation,
|
|
344
|
+
toolCalls: (w.tool_calls ?? []).map(toToolCall)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function toTrace(w) {
|
|
348
|
+
return {
|
|
349
|
+
id: w.id,
|
|
350
|
+
sessionId: w.session_id ?? "",
|
|
351
|
+
task: w.task ?? "",
|
|
352
|
+
steps: (w.steps ?? []).map(toStep),
|
|
353
|
+
outcome: w.outcome,
|
|
354
|
+
success: w.success,
|
|
355
|
+
startedAt: w.started_at ?? "",
|
|
356
|
+
completedAt: w.completed_at
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function toToolStats(w) {
|
|
360
|
+
return {
|
|
361
|
+
name: w.name,
|
|
362
|
+
totalCalls: w.total_calls,
|
|
363
|
+
successfulCalls: w.successful_calls,
|
|
364
|
+
failedCalls: w.failed_calls,
|
|
365
|
+
successRate: w.success_rate,
|
|
366
|
+
avgDurationMs: w.avg_duration_ms
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function toAgentStep(w) {
|
|
370
|
+
return {
|
|
371
|
+
id: w.id,
|
|
372
|
+
conversationId: w.conversation_id,
|
|
373
|
+
reasoning: w.reasoning,
|
|
374
|
+
actionTaken: w.action_taken,
|
|
375
|
+
result: w.result,
|
|
376
|
+
createdAt: w.created_at ?? ""
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
var ReasoningMemory = class {
|
|
380
|
+
constructor(transport) {
|
|
381
|
+
this.transport = transport;
|
|
382
|
+
}
|
|
383
|
+
// ---- Silver tier (bridge) ----------------------------------------------
|
|
384
|
+
async startTrace(sessionId, task) {
|
|
385
|
+
const wire = await this.transport.request("start_trace", {
|
|
386
|
+
session_id: sessionId,
|
|
387
|
+
task
|
|
388
|
+
});
|
|
389
|
+
return toTrace(wire);
|
|
390
|
+
}
|
|
391
|
+
async addStep(traceId, options) {
|
|
392
|
+
const wire = await this.transport.request("add_step", {
|
|
393
|
+
trace_id: traceId,
|
|
394
|
+
thought: options?.thought,
|
|
395
|
+
action: options?.action,
|
|
396
|
+
observation: options?.observation
|
|
397
|
+
});
|
|
398
|
+
return toStep(wire);
|
|
399
|
+
}
|
|
400
|
+
async recordToolCall(stepId, toolName, args, options) {
|
|
401
|
+
const wire = await this.transport.request("record_tool_call", {
|
|
402
|
+
step_id: stepId,
|
|
403
|
+
tool_name: toolName,
|
|
404
|
+
arguments: args,
|
|
405
|
+
input: typeof args === "string" ? args : JSON.stringify(args),
|
|
406
|
+
result: options?.result,
|
|
407
|
+
output: typeof options?.result === "string" ? options.result : void 0,
|
|
408
|
+
status: options?.status ?? "success",
|
|
409
|
+
duration_ms: options?.durationMs,
|
|
410
|
+
error: options?.error
|
|
411
|
+
});
|
|
412
|
+
return toToolCall(wire);
|
|
413
|
+
}
|
|
414
|
+
async completeTrace(traceId, options) {
|
|
415
|
+
const wire = await this.transport.request("complete_trace", {
|
|
416
|
+
trace_id: traceId,
|
|
417
|
+
outcome: options?.outcome,
|
|
418
|
+
success: options?.success
|
|
419
|
+
});
|
|
420
|
+
return toTrace(wire);
|
|
421
|
+
}
|
|
422
|
+
async getTraceWithSteps(traceId) {
|
|
423
|
+
const wire = await this.transport.request("get_trace_with_steps", {
|
|
424
|
+
trace_id: traceId
|
|
425
|
+
});
|
|
426
|
+
return wire ? toTrace(wire) : null;
|
|
427
|
+
}
|
|
428
|
+
async listTraces(options) {
|
|
429
|
+
const wire = await this.transport.request("list_traces", {
|
|
430
|
+
session_id: options?.sessionId,
|
|
431
|
+
limit: options?.limit ?? 100
|
|
432
|
+
});
|
|
433
|
+
return wire.map(toTrace);
|
|
434
|
+
}
|
|
435
|
+
async getToolStats(toolName) {
|
|
436
|
+
const wire = await this.transport.request("get_tool_stats", {
|
|
437
|
+
tool_name: toolName
|
|
438
|
+
});
|
|
439
|
+
return wire.map(toToolStats);
|
|
440
|
+
}
|
|
441
|
+
async getSimilarTraces(task, options) {
|
|
442
|
+
const wire = await this.transport.request("get_similar_traces", {
|
|
443
|
+
task,
|
|
444
|
+
limit: options?.limit ?? 5,
|
|
445
|
+
success_only: options?.successOnly ?? true
|
|
446
|
+
});
|
|
447
|
+
return wire.map(toTrace);
|
|
448
|
+
}
|
|
449
|
+
// ---- Volume 5 / hosted-native methods -----------------------------------
|
|
450
|
+
/** Record one reasoning step under a conversation (hosted REACT model). */
|
|
451
|
+
async recordStep(input) {
|
|
452
|
+
const wire = await this.transport.request("record_step", {
|
|
453
|
+
conversation_id: input.conversationId,
|
|
454
|
+
reasoning: input.reasoning,
|
|
455
|
+
action_taken: input.actionTaken,
|
|
456
|
+
result: input.result
|
|
457
|
+
});
|
|
458
|
+
return toAgentStep(wire);
|
|
459
|
+
}
|
|
460
|
+
/** List all reasoning steps for one conversation. */
|
|
461
|
+
async listSteps(conversationId) {
|
|
462
|
+
const wire = await this.transport.request("list_steps", {
|
|
463
|
+
conversation_id: conversationId
|
|
464
|
+
});
|
|
465
|
+
return wire.map(toAgentStep);
|
|
466
|
+
}
|
|
467
|
+
/** Detailed step explanation with tool calls and influenced entities. */
|
|
468
|
+
async explainStep(stepId) {
|
|
469
|
+
const wire = await this.transport.request("explain_step", {
|
|
470
|
+
step_id: stepId
|
|
471
|
+
});
|
|
472
|
+
return {
|
|
473
|
+
...toAgentStep(wire),
|
|
474
|
+
toolCalls: (wire.tool_calls ?? []).map(toToolCall),
|
|
475
|
+
influencedEntities: (wire.influenced_entities ?? []).map((e) => ({
|
|
476
|
+
id: String(e["id"] ?? ""),
|
|
477
|
+
name: String(e["name"] ?? ""),
|
|
478
|
+
type: String(e["type"] ?? ""),
|
|
479
|
+
description: e["description"],
|
|
480
|
+
createdAt: String(e["created_at"] ?? "")
|
|
481
|
+
}))
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/** Full reasoning trace for a conversation (steps + tool calls). */
|
|
485
|
+
async getTraceByConversation(conversationId) {
|
|
486
|
+
const wire = await this.transport.request(
|
|
487
|
+
"get_trace_by_conversation",
|
|
488
|
+
{ conversation_id: conversationId }
|
|
489
|
+
);
|
|
490
|
+
return {
|
|
491
|
+
conversationId: wire.conversation_id,
|
|
492
|
+
steps: (wire.steps ?? []).map(toAgentStep),
|
|
493
|
+
toolCalls: (wire.tool_calls ?? []).map(toToolCall)
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
/** All reasoning steps that influenced an entity's creation.
|
|
497
|
+
*
|
|
498
|
+
* Hosted REST returns the chain under `provenance`; bridge / older
|
|
499
|
+
* responses use `steps`. Accept either.
|
|
500
|
+
*/
|
|
501
|
+
async getEntityProvenance(entityId) {
|
|
502
|
+
const wire = await this.transport.request("get_entity_provenance", { entity_id: entityId });
|
|
503
|
+
const rawSteps = wire.steps ?? wire.provenance ?? [];
|
|
504
|
+
return {
|
|
505
|
+
entityId: wire.entity_id,
|
|
506
|
+
steps: rawSteps.map(toAgentStep)
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/short-term/index.ts
|
|
512
|
+
function toMessage(w) {
|
|
513
|
+
return {
|
|
514
|
+
id: w.id,
|
|
515
|
+
role: w.role ?? "user",
|
|
516
|
+
content: w.content,
|
|
517
|
+
timestamp: w.timestamp ?? w.created_at ?? "",
|
|
518
|
+
embedding: w.embedding,
|
|
519
|
+
metadata: w.metadata ?? {},
|
|
520
|
+
conversationId: w.conversation_id
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function toConversation(w) {
|
|
524
|
+
return {
|
|
525
|
+
id: w.id,
|
|
526
|
+
sessionId: w.session_id ?? w.id,
|
|
527
|
+
messages: (w.messages ?? []).map(toMessage),
|
|
528
|
+
messageCount: w.message_count,
|
|
529
|
+
title: w.title,
|
|
530
|
+
createdAt: w.created_at ?? "",
|
|
531
|
+
updatedAt: w.updated_at,
|
|
532
|
+
workspaceId: w.workspace_id,
|
|
533
|
+
userId: w.user_id,
|
|
534
|
+
metadata: w.metadata
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function toSessionInfo(w) {
|
|
538
|
+
return {
|
|
539
|
+
sessionId: w.session_id ?? w.id ?? "",
|
|
540
|
+
messageCount: w.message_count ?? 0,
|
|
541
|
+
createdAt: w.created_at,
|
|
542
|
+
updatedAt: w.updated_at
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function toObservation(w) {
|
|
546
|
+
return {
|
|
547
|
+
id: w.id,
|
|
548
|
+
conversationId: w.conversation_id,
|
|
549
|
+
content: w.content,
|
|
550
|
+
windowStart: w.window_start,
|
|
551
|
+
windowEnd: w.window_end,
|
|
552
|
+
createdAt: w.created_at
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function toReflection(w) {
|
|
556
|
+
return {
|
|
557
|
+
id: w.id,
|
|
558
|
+
conversationId: w.conversation_id,
|
|
559
|
+
content: w.content,
|
|
560
|
+
createdAt: w.created_at
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
var ShortTermMemory = class {
|
|
564
|
+
constructor(transport) {
|
|
565
|
+
this.transport = transport;
|
|
566
|
+
}
|
|
567
|
+
// ---- Bronze tier (bridge) ----------------------------------------------
|
|
568
|
+
async addMessage(sessionId, role, content, options) {
|
|
569
|
+
const wire = await this.transport.request("add_message", {
|
|
570
|
+
session_id: sessionId,
|
|
571
|
+
role,
|
|
572
|
+
content,
|
|
573
|
+
metadata: options?.metadata
|
|
574
|
+
});
|
|
575
|
+
return toMessage(wire);
|
|
576
|
+
}
|
|
577
|
+
async getConversation(sessionId, options) {
|
|
578
|
+
const wire = await this.transport.request("get_conversation", {
|
|
579
|
+
session_id: sessionId,
|
|
580
|
+
limit: options?.limit
|
|
581
|
+
});
|
|
582
|
+
return toConversation(wire);
|
|
583
|
+
}
|
|
584
|
+
async searchMessages(query, options) {
|
|
585
|
+
const wire = await this.transport.request("search_messages", {
|
|
586
|
+
query,
|
|
587
|
+
session_id: options?.sessionId,
|
|
588
|
+
limit: options?.limit ?? 10,
|
|
589
|
+
threshold: options?.threshold ?? 0.7
|
|
590
|
+
});
|
|
591
|
+
return wire.map(toMessage);
|
|
592
|
+
}
|
|
593
|
+
async listSessions(options) {
|
|
594
|
+
const wire = await this.transport.request("list_sessions", {
|
|
595
|
+
limit: options?.limit ?? 100
|
|
596
|
+
});
|
|
597
|
+
return wire.map(toSessionInfo);
|
|
598
|
+
}
|
|
599
|
+
async deleteMessage(messageId) {
|
|
600
|
+
const result = await this.transport.request("delete_message", {
|
|
601
|
+
message_id: messageId
|
|
602
|
+
});
|
|
603
|
+
return result.deleted;
|
|
604
|
+
}
|
|
605
|
+
async clearSession(sessionId) {
|
|
606
|
+
await this.transport.request("clear_session", { session_id: sessionId });
|
|
607
|
+
}
|
|
608
|
+
// ---- Volume 5 / hosted-native methods -----------------------------------
|
|
609
|
+
/** Create a new conversation (hosted service). */
|
|
610
|
+
async createConversation(options) {
|
|
611
|
+
const wire = await this.transport.request("create_conversation", {
|
|
612
|
+
user_id: options.userId,
|
|
613
|
+
metadata: options.metadata
|
|
614
|
+
});
|
|
615
|
+
return toConversation(wire);
|
|
616
|
+
}
|
|
617
|
+
/** List conversations the API key has access to. */
|
|
618
|
+
async listConversations(options) {
|
|
619
|
+
const wire = await this.transport.request("list_conversations", {
|
|
620
|
+
limit: options?.limit,
|
|
621
|
+
userId: options?.userId
|
|
622
|
+
});
|
|
623
|
+
return wire.map(toConversation);
|
|
624
|
+
}
|
|
625
|
+
/** Fetch conversation metadata (no messages). */
|
|
626
|
+
async getConversationMetadata(conversationId) {
|
|
627
|
+
const wire = await this.transport.request("get_conversation_metadata", {
|
|
628
|
+
conversation_id: conversationId
|
|
629
|
+
});
|
|
630
|
+
return toConversation(wire);
|
|
631
|
+
}
|
|
632
|
+
/** Delete a conversation and all its messages. */
|
|
633
|
+
async deleteConversation(conversationId) {
|
|
634
|
+
await this.transport.request("delete_conversation", { conversation_id: conversationId });
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Three-tier conversational context (reflections + observations + recent
|
|
638
|
+
* messages). The richest input you can hand an LLM about a conversation.
|
|
639
|
+
*/
|
|
640
|
+
async getContext(conversationId) {
|
|
641
|
+
const wire = await this.transport.request("get_context", {
|
|
642
|
+
conversation_id: conversationId
|
|
643
|
+
});
|
|
644
|
+
return {
|
|
645
|
+
reflections: (wire.reflections ?? []).map(toReflection),
|
|
646
|
+
observations: (wire.observations ?? []).map(toObservation),
|
|
647
|
+
recentMessages: (wire.recent_messages ?? []).map(toMessage)
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
/** Bulk-add up to 100 messages in one request. */
|
|
651
|
+
async bulkAddMessages(conversationId, messages) {
|
|
652
|
+
if (messages.length > 100) {
|
|
653
|
+
throw new Error("bulkAddMessages accepts a maximum of 100 messages per call.");
|
|
654
|
+
}
|
|
655
|
+
const wire = await this.transport.request("bulk_add_messages", {
|
|
656
|
+
conversation_id: conversationId,
|
|
657
|
+
messages
|
|
658
|
+
});
|
|
659
|
+
return wire.map(toMessage);
|
|
660
|
+
}
|
|
661
|
+
/** Auto-generated message-window summaries. */
|
|
662
|
+
async getObservations(conversationId, options) {
|
|
663
|
+
const wire = await this.transport.request("get_observations", {
|
|
664
|
+
conversation_id: conversationId,
|
|
665
|
+
limit: options?.limit
|
|
666
|
+
});
|
|
667
|
+
return wire.map(toObservation);
|
|
668
|
+
}
|
|
669
|
+
/** Higher-level reflections derived from observations. */
|
|
670
|
+
async getReflections(conversationId) {
|
|
671
|
+
const wire = await this.transport.request("get_reflections", {
|
|
672
|
+
conversation_id: conversationId
|
|
673
|
+
});
|
|
674
|
+
return wire.map(toReflection);
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// src/transport/casing.ts
|
|
679
|
+
var SNAKE_RE = /_([a-z0-9])/g;
|
|
680
|
+
var CAMEL_RE = /([A-Z])/g;
|
|
681
|
+
function snakeKey(key) {
|
|
682
|
+
return key.replace(CAMEL_RE, (_, c) => `_${c.toLowerCase()}`);
|
|
683
|
+
}
|
|
684
|
+
function camelKey(key) {
|
|
685
|
+
return key.replace(SNAKE_RE, (_, c) => c.toUpperCase());
|
|
686
|
+
}
|
|
687
|
+
function snakeToCamel(value) {
|
|
688
|
+
if (Array.isArray(value)) {
|
|
689
|
+
return value.map((v) => snakeToCamel(v));
|
|
690
|
+
}
|
|
691
|
+
if (value !== null && typeof value === "object") {
|
|
692
|
+
const out = {};
|
|
693
|
+
for (const [k, v] of Object.entries(value)) {
|
|
694
|
+
out[camelKey(k)] = snakeToCamel(v);
|
|
695
|
+
}
|
|
696
|
+
return out;
|
|
697
|
+
}
|
|
698
|
+
return value;
|
|
699
|
+
}
|
|
700
|
+
function camelToSnake(value) {
|
|
701
|
+
if (Array.isArray(value)) {
|
|
702
|
+
return value.map((v) => camelToSnake(v));
|
|
703
|
+
}
|
|
704
|
+
if (value !== null && typeof value === "object") {
|
|
705
|
+
const out = {};
|
|
706
|
+
for (const [k, v] of Object.entries(value)) {
|
|
707
|
+
out[snakeKey(k)] = camelToSnake(v);
|
|
708
|
+
}
|
|
709
|
+
return out;
|
|
710
|
+
}
|
|
711
|
+
return value;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// src/transport/rest.ts
|
|
715
|
+
function trimTrailingSlashes(s) {
|
|
716
|
+
let end = s.length;
|
|
717
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
|
|
718
|
+
return s.slice(0, end);
|
|
719
|
+
}
|
|
720
|
+
function nowMs() {
|
|
721
|
+
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
722
|
+
return performance.now();
|
|
723
|
+
}
|
|
724
|
+
return Date.now();
|
|
725
|
+
}
|
|
726
|
+
function snakeToCamelKey(s) {
|
|
727
|
+
return s.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
728
|
+
}
|
|
729
|
+
var ROUTES = {
|
|
730
|
+
// Lifecycle ----------------------------------------------------------------
|
|
731
|
+
setup: "noop",
|
|
732
|
+
teardown: "noop",
|
|
733
|
+
// Hosted has no global clear; we delete every conversation owned by the API
|
|
734
|
+
// key. This is best-effort — see clearAllData() for the implementation.
|
|
735
|
+
clear_all_data: "noop",
|
|
736
|
+
// Short-Term — legacy bridge methods (mapped where a clean REST equivalent
|
|
737
|
+
// exists; bridge sessionId is treated as the conversationId UUID).
|
|
738
|
+
add_message: {
|
|
739
|
+
method: "POST",
|
|
740
|
+
path: "/conversations/{sessionId}/messages",
|
|
741
|
+
pathParams: ["sessionId"],
|
|
742
|
+
hasBody: true
|
|
743
|
+
},
|
|
744
|
+
get_conversation: {
|
|
745
|
+
method: "GET",
|
|
746
|
+
path: "/conversations/{sessionId}/messages",
|
|
747
|
+
pathParams: ["sessionId"],
|
|
748
|
+
queryParams: ["limit"],
|
|
749
|
+
shape: (raw, p) => {
|
|
750
|
+
const messages = raw?.messages ?? raw ?? [];
|
|
751
|
+
return {
|
|
752
|
+
id: p["sessionId"],
|
|
753
|
+
session_id: p["sessionId"],
|
|
754
|
+
messages,
|
|
755
|
+
created_at: null
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
list_sessions: {
|
|
760
|
+
method: "GET",
|
|
761
|
+
path: "/conversations",
|
|
762
|
+
queryParams: ["limit"],
|
|
763
|
+
shape: (raw) => {
|
|
764
|
+
const conversations = raw?.conversations ?? [];
|
|
765
|
+
return conversations.map((c) => {
|
|
766
|
+
const conv = c;
|
|
767
|
+
return {
|
|
768
|
+
session_id: conv["id"],
|
|
769
|
+
message_count: conv["messageCount"] ?? 0,
|
|
770
|
+
created_at: conv["createdAt"],
|
|
771
|
+
updated_at: conv["updatedAt"]
|
|
772
|
+
};
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
search_messages: {
|
|
777
|
+
method: "POST",
|
|
778
|
+
path: "/conversations/{sessionId}/search",
|
|
779
|
+
pathParams: ["sessionId"],
|
|
780
|
+
hasBody: true,
|
|
781
|
+
shape: (raw) => raw?.messages ?? []
|
|
782
|
+
},
|
|
783
|
+
clear_session: {
|
|
784
|
+
method: "DELETE",
|
|
785
|
+
path: "/conversations/{sessionId}",
|
|
786
|
+
pathParams: ["sessionId"]
|
|
787
|
+
},
|
|
788
|
+
delete_message: "unsupported",
|
|
789
|
+
// Long-Term — legacy mapped methods
|
|
790
|
+
add_entity: {
|
|
791
|
+
method: "POST",
|
|
792
|
+
path: "/entities",
|
|
793
|
+
hasBody: true
|
|
794
|
+
},
|
|
795
|
+
search_entities: {
|
|
796
|
+
method: "POST",
|
|
797
|
+
path: "/entities/search",
|
|
798
|
+
hasBody: true,
|
|
799
|
+
shape: (raw) => raw?.entities ?? []
|
|
800
|
+
},
|
|
801
|
+
add_preference: "unsupported",
|
|
802
|
+
add_fact: "unsupported",
|
|
803
|
+
search_preferences: "unsupported",
|
|
804
|
+
get_entity_by_name: "unsupported",
|
|
805
|
+
get_related_entities: "unsupported",
|
|
806
|
+
add_relationship: "unsupported",
|
|
807
|
+
merge_duplicate_entities: "unsupported",
|
|
808
|
+
// Reasoning — legacy not directly representable in REST
|
|
809
|
+
start_trace: "unsupported",
|
|
810
|
+
add_step: "unsupported",
|
|
811
|
+
record_tool_call: {
|
|
812
|
+
method: "POST",
|
|
813
|
+
path: "/reasoning/tool-calls",
|
|
814
|
+
hasBody: true
|
|
815
|
+
},
|
|
816
|
+
complete_trace: "unsupported",
|
|
817
|
+
get_trace_with_steps: "unsupported",
|
|
818
|
+
list_traces: "unsupported",
|
|
819
|
+
get_tool_stats: "unsupported",
|
|
820
|
+
get_similar_traces: "unsupported",
|
|
821
|
+
// ---- Hosted-native methods (Volume 5 / Platinum tier) --------------------
|
|
822
|
+
create_conversation: {
|
|
823
|
+
method: "POST",
|
|
824
|
+
path: "/conversations",
|
|
825
|
+
hasBody: true
|
|
826
|
+
},
|
|
827
|
+
list_conversations: {
|
|
828
|
+
method: "GET",
|
|
829
|
+
path: "/conversations",
|
|
830
|
+
queryParams: ["limit", "user_id"],
|
|
831
|
+
shape: (raw) => raw?.conversations ?? raw
|
|
832
|
+
},
|
|
833
|
+
get_conversation_metadata: {
|
|
834
|
+
method: "GET",
|
|
835
|
+
path: "/conversations/{conversationId}",
|
|
836
|
+
pathParams: ["conversationId"]
|
|
837
|
+
},
|
|
838
|
+
delete_conversation: {
|
|
839
|
+
method: "DELETE",
|
|
840
|
+
path: "/conversations/{conversationId}",
|
|
841
|
+
pathParams: ["conversationId"]
|
|
842
|
+
},
|
|
843
|
+
get_context: {
|
|
844
|
+
method: "GET",
|
|
845
|
+
path: "/conversations/{conversationId}/context",
|
|
846
|
+
pathParams: ["conversationId"]
|
|
847
|
+
},
|
|
848
|
+
bulk_add_messages: {
|
|
849
|
+
method: "POST",
|
|
850
|
+
path: "/conversations/{conversationId}/messages/bulk",
|
|
851
|
+
pathParams: ["conversationId"],
|
|
852
|
+
hasBody: true,
|
|
853
|
+
shape: (raw) => raw?.messages ?? raw
|
|
854
|
+
},
|
|
855
|
+
get_observations: {
|
|
856
|
+
method: "GET",
|
|
857
|
+
path: "/conversations/{conversationId}/observations",
|
|
858
|
+
pathParams: ["conversationId"],
|
|
859
|
+
queryParams: ["limit"],
|
|
860
|
+
shape: (raw) => raw?.observations ?? raw
|
|
861
|
+
},
|
|
862
|
+
get_reflections: {
|
|
863
|
+
method: "GET",
|
|
864
|
+
path: "/conversations/{conversationId}/reflections",
|
|
865
|
+
pathParams: ["conversationId"],
|
|
866
|
+
shape: (raw) => raw?.reflections ?? raw
|
|
867
|
+
},
|
|
868
|
+
list_entities: {
|
|
869
|
+
method: "GET",
|
|
870
|
+
path: "/entities",
|
|
871
|
+
queryParams: ["type", "limit"],
|
|
872
|
+
shape: (raw) => raw?.entities ?? raw
|
|
873
|
+
},
|
|
874
|
+
get_entity: {
|
|
875
|
+
method: "GET",
|
|
876
|
+
path: "/entities/{entityId}",
|
|
877
|
+
pathParams: ["entityId"]
|
|
878
|
+
},
|
|
879
|
+
update_entity: {
|
|
880
|
+
method: "PUT",
|
|
881
|
+
path: "/entities/{entityId}",
|
|
882
|
+
pathParams: ["entityId"],
|
|
883
|
+
hasBody: true
|
|
884
|
+
},
|
|
885
|
+
delete_entity: {
|
|
886
|
+
method: "DELETE",
|
|
887
|
+
path: "/entities/{entityId}",
|
|
888
|
+
pathParams: ["entityId"]
|
|
889
|
+
},
|
|
890
|
+
set_entity_feedback: {
|
|
891
|
+
method: "PUT",
|
|
892
|
+
path: "/entities/{entityId}/feedback",
|
|
893
|
+
pathParams: ["entityId"],
|
|
894
|
+
hasBody: true
|
|
895
|
+
},
|
|
896
|
+
get_entity_history: {
|
|
897
|
+
method: "GET",
|
|
898
|
+
path: "/entities/{entityId}/history",
|
|
899
|
+
pathParams: ["entityId"]
|
|
900
|
+
},
|
|
901
|
+
merge_entities: {
|
|
902
|
+
method: "POST",
|
|
903
|
+
path: "/entities/{sourceId}/merge",
|
|
904
|
+
pathParams: ["sourceId"],
|
|
905
|
+
hasBody: true
|
|
906
|
+
},
|
|
907
|
+
get_entity_graph: {
|
|
908
|
+
method: "GET",
|
|
909
|
+
path: "/entities/graph"
|
|
910
|
+
},
|
|
911
|
+
explain_step: {
|
|
912
|
+
method: "GET",
|
|
913
|
+
path: "/reasoning/explain/{stepId}",
|
|
914
|
+
pathParams: ["stepId"]
|
|
915
|
+
},
|
|
916
|
+
get_trace_by_conversation: {
|
|
917
|
+
method: "GET",
|
|
918
|
+
path: "/reasoning/trace/{conversationId}",
|
|
919
|
+
pathParams: ["conversationId"]
|
|
920
|
+
},
|
|
921
|
+
get_entity_provenance: {
|
|
922
|
+
method: "GET",
|
|
923
|
+
path: "/reasoning/provenance/{entityId}",
|
|
924
|
+
pathParams: ["entityId"]
|
|
925
|
+
},
|
|
926
|
+
record_step: {
|
|
927
|
+
method: "POST",
|
|
928
|
+
path: "/reasoning/steps",
|
|
929
|
+
hasBody: true
|
|
930
|
+
},
|
|
931
|
+
list_steps: {
|
|
932
|
+
method: "GET",
|
|
933
|
+
path: "/reasoning/steps",
|
|
934
|
+
queryParams: ["conversation_id"],
|
|
935
|
+
shape: (raw) => raw?.steps ?? raw
|
|
936
|
+
},
|
|
937
|
+
cypher_query: {
|
|
938
|
+
method: "POST",
|
|
939
|
+
path: "/query",
|
|
940
|
+
hasBody: true
|
|
941
|
+
},
|
|
942
|
+
// Auth
|
|
943
|
+
list_api_keys: {
|
|
944
|
+
method: "GET",
|
|
945
|
+
path: "/auth/api-keys",
|
|
946
|
+
queryParams: ["workspace_id"],
|
|
947
|
+
shape: (raw) => {
|
|
948
|
+
const r = raw;
|
|
949
|
+
return r?.keys ?? r?.api_keys ?? raw;
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
create_api_key: {
|
|
953
|
+
method: "POST",
|
|
954
|
+
path: "/auth/api-keys",
|
|
955
|
+
hasBody: true
|
|
956
|
+
},
|
|
957
|
+
revoke_api_key: {
|
|
958
|
+
method: "DELETE",
|
|
959
|
+
path: "/auth/api-keys/{keyId}",
|
|
960
|
+
pathParams: ["keyId"]
|
|
961
|
+
},
|
|
962
|
+
reveal_api_key: {
|
|
963
|
+
method: "GET",
|
|
964
|
+
path: "/auth/api-keys/{keyId}/reveal",
|
|
965
|
+
pathParams: ["keyId"],
|
|
966
|
+
queryParams: ["workspace_id"]
|
|
967
|
+
},
|
|
968
|
+
refresh_access_token: {
|
|
969
|
+
method: "POST",
|
|
970
|
+
path: "/auth/refresh",
|
|
971
|
+
hasBody: true
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
var RestTransport = class {
|
|
975
|
+
endpoint;
|
|
976
|
+
apiKey;
|
|
977
|
+
tokenProvider;
|
|
978
|
+
timeout;
|
|
979
|
+
headers;
|
|
980
|
+
logger;
|
|
981
|
+
constructor(options) {
|
|
982
|
+
this.endpoint = trimTrailingSlashes(options.endpoint);
|
|
983
|
+
this.apiKey = options.apiKey;
|
|
984
|
+
this.tokenProvider = options.tokenProvider;
|
|
985
|
+
this.timeout = options.timeout ?? 3e4;
|
|
986
|
+
this.headers = options.headers ?? {};
|
|
987
|
+
this.logger = options.logger;
|
|
988
|
+
}
|
|
989
|
+
async connect() {
|
|
990
|
+
const url = `${this.endpoint}/conversations?limit=1`;
|
|
991
|
+
const start = nowMs();
|
|
992
|
+
this.emit({ kind: "request", method: "connect", url, httpMethod: "GET" });
|
|
993
|
+
let response;
|
|
994
|
+
try {
|
|
995
|
+
response = await fetch(url, {
|
|
996
|
+
method: "GET",
|
|
997
|
+
headers: await this.buildHeaders(),
|
|
998
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
999
|
+
});
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
const durationMs2 = nowMs() - start;
|
|
1002
|
+
if (error instanceof TypeError) {
|
|
1003
|
+
const err = new ConnectionError(
|
|
1004
|
+
`Failed to connect to ${this.endpoint}: ${error.message}`,
|
|
1005
|
+
{ cause: error }
|
|
1006
|
+
);
|
|
1007
|
+
this.emit({ kind: "error", method: "connect", url, durationMs: durationMs2, message: err.message });
|
|
1008
|
+
throw err;
|
|
1009
|
+
}
|
|
1010
|
+
if (error instanceof DOMException && error.name === "TimeoutError") {
|
|
1011
|
+
const err = new ConnectionError(
|
|
1012
|
+
`Connection to ${this.endpoint} timed out after ${this.timeout}ms`,
|
|
1013
|
+
{ cause: error }
|
|
1014
|
+
);
|
|
1015
|
+
this.emit({ kind: "error", method: "connect", url, durationMs: durationMs2, message: err.message });
|
|
1016
|
+
throw err;
|
|
1017
|
+
}
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1020
|
+
const durationMs = nowMs() - start;
|
|
1021
|
+
const requestId = extractRequestId(response.headers);
|
|
1022
|
+
if (response.status === 401 || response.status === 403) {
|
|
1023
|
+
const err = new AuthenticationError(
|
|
1024
|
+
`Authentication failed against ${this.endpoint}: ${response.status} ${response.statusText}`,
|
|
1025
|
+
{ requestId }
|
|
1026
|
+
);
|
|
1027
|
+
this.emit({
|
|
1028
|
+
kind: "error",
|
|
1029
|
+
method: "connect",
|
|
1030
|
+
url,
|
|
1031
|
+
status: response.status,
|
|
1032
|
+
requestId,
|
|
1033
|
+
durationMs,
|
|
1034
|
+
message: err.message
|
|
1035
|
+
});
|
|
1036
|
+
throw err;
|
|
1037
|
+
}
|
|
1038
|
+
if (!response.ok && response.status >= 500) {
|
|
1039
|
+
const err = new ConnectionError(
|
|
1040
|
+
`Server error from ${this.endpoint}: ${response.status} ${response.statusText}`,
|
|
1041
|
+
{ requestId }
|
|
1042
|
+
);
|
|
1043
|
+
this.emit({
|
|
1044
|
+
kind: "error",
|
|
1045
|
+
method: "connect",
|
|
1046
|
+
url,
|
|
1047
|
+
status: response.status,
|
|
1048
|
+
requestId,
|
|
1049
|
+
durationMs,
|
|
1050
|
+
message: err.message
|
|
1051
|
+
});
|
|
1052
|
+
throw err;
|
|
1053
|
+
}
|
|
1054
|
+
this.emit({
|
|
1055
|
+
kind: "response",
|
|
1056
|
+
method: "connect",
|
|
1057
|
+
url,
|
|
1058
|
+
status: response.status,
|
|
1059
|
+
requestId,
|
|
1060
|
+
durationMs
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
async close() {
|
|
1064
|
+
}
|
|
1065
|
+
async request(method, params) {
|
|
1066
|
+
const route = ROUTES[method];
|
|
1067
|
+
if (!route) {
|
|
1068
|
+
throw new NotSupportedError(
|
|
1069
|
+
`Method '${method}' is not implemented by RestTransport. Use BridgeTransport for full TCK conformance, or call a hosted-native method.`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
if (route === "noop") return void 0;
|
|
1073
|
+
if (route === "unsupported") {
|
|
1074
|
+
throw new NotSupportedError(
|
|
1075
|
+
`Method '${method}' has no equivalent in the hosted Neo4j Agent Memory REST API. It is supported by BridgeTransport only.`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
const original = params ?? {};
|
|
1079
|
+
const camelParams = snakeToCamel(original);
|
|
1080
|
+
let path = route.path;
|
|
1081
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
1082
|
+
for (const name of route.pathParams ?? []) {
|
|
1083
|
+
const v = camelParams[name];
|
|
1084
|
+
if (v === void 0 || v === null || v === "") {
|
|
1085
|
+
throw new TransportError(
|
|
1086
|
+
`Missing required path parameter '${name}' for method '${method}'`,
|
|
1087
|
+
400,
|
|
1088
|
+
camelParams
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
path = path.replace(`{${name}}`, encodeURIComponent(String(v)));
|
|
1092
|
+
consumed.add(name);
|
|
1093
|
+
}
|
|
1094
|
+
const queryEntries = [];
|
|
1095
|
+
for (const name of route.queryParams ?? []) {
|
|
1096
|
+
let v = original[name];
|
|
1097
|
+
if (v === void 0 || v === null) {
|
|
1098
|
+
const camel = snakeToCamelKey(name);
|
|
1099
|
+
v = camelParams[camel];
|
|
1100
|
+
}
|
|
1101
|
+
if (v !== void 0 && v !== null) {
|
|
1102
|
+
queryEntries.push([name, String(v)]);
|
|
1103
|
+
consumed.add(name);
|
|
1104
|
+
consumed.add(snakeToCamelKey(name));
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
const query = queryEntries.length ? "?" + queryEntries.map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&") : "";
|
|
1108
|
+
let body;
|
|
1109
|
+
if (route.hasBody) {
|
|
1110
|
+
const bodyObj = {};
|
|
1111
|
+
for (const [k, v] of Object.entries(camelParams)) {
|
|
1112
|
+
if (!consumed.has(k) && v !== void 0 && v !== null) {
|
|
1113
|
+
bodyObj[k] = v;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
body = JSON.stringify(bodyObj);
|
|
1117
|
+
}
|
|
1118
|
+
const url = `${this.endpoint}${path}${query}`;
|
|
1119
|
+
const start = nowMs();
|
|
1120
|
+
this.emit({ kind: "request", method, url, httpMethod: route.method });
|
|
1121
|
+
let response;
|
|
1122
|
+
try {
|
|
1123
|
+
response = await fetch(url, {
|
|
1124
|
+
method: route.method,
|
|
1125
|
+
headers: await this.buildHeaders(route.hasBody),
|
|
1126
|
+
body,
|
|
1127
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
1128
|
+
});
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
const durationMs2 = nowMs() - start;
|
|
1131
|
+
if (error instanceof TypeError) {
|
|
1132
|
+
const err = new ConnectionError(
|
|
1133
|
+
`Request to ${url} failed: ${error.message}`,
|
|
1134
|
+
{ cause: error }
|
|
1135
|
+
);
|
|
1136
|
+
this.emit({ kind: "error", method, url, durationMs: durationMs2, message: err.message });
|
|
1137
|
+
throw err;
|
|
1138
|
+
}
|
|
1139
|
+
throw error;
|
|
1140
|
+
}
|
|
1141
|
+
const requestId = extractRequestId(response.headers);
|
|
1142
|
+
const durationMs = nowMs() - start;
|
|
1143
|
+
if (response.status === 401 || response.status === 403) {
|
|
1144
|
+
const err = new AuthenticationError(
|
|
1145
|
+
`Authentication failed: ${response.status} ${response.statusText}`,
|
|
1146
|
+
{ requestId }
|
|
1147
|
+
);
|
|
1148
|
+
this.emit({
|
|
1149
|
+
kind: "error",
|
|
1150
|
+
method,
|
|
1151
|
+
url,
|
|
1152
|
+
status: response.status,
|
|
1153
|
+
requestId,
|
|
1154
|
+
durationMs,
|
|
1155
|
+
message: err.message
|
|
1156
|
+
});
|
|
1157
|
+
throw err;
|
|
1158
|
+
}
|
|
1159
|
+
if (response.status === 204) {
|
|
1160
|
+
this.emit({ kind: "response", method, url, status: 204, requestId, durationMs });
|
|
1161
|
+
return void 0;
|
|
1162
|
+
}
|
|
1163
|
+
const text = await response.text();
|
|
1164
|
+
if (!response.ok) {
|
|
1165
|
+
let errorBody;
|
|
1166
|
+
try {
|
|
1167
|
+
errorBody = JSON.parse(text);
|
|
1168
|
+
} catch {
|
|
1169
|
+
errorBody = text;
|
|
1170
|
+
}
|
|
1171
|
+
const errMsg = typeof errorBody === "object" && errorBody !== null && "error" in errorBody ? String(errorBody["error"]) : `HTTP ${response.status}`;
|
|
1172
|
+
const err = new TransportError(
|
|
1173
|
+
`${method} failed: ${errMsg}`,
|
|
1174
|
+
response.status,
|
|
1175
|
+
errorBody,
|
|
1176
|
+
{ requestId }
|
|
1177
|
+
);
|
|
1178
|
+
this.emit({
|
|
1179
|
+
kind: "error",
|
|
1180
|
+
method,
|
|
1181
|
+
url,
|
|
1182
|
+
status: response.status,
|
|
1183
|
+
requestId,
|
|
1184
|
+
durationMs,
|
|
1185
|
+
message: err.message
|
|
1186
|
+
});
|
|
1187
|
+
throw err;
|
|
1188
|
+
}
|
|
1189
|
+
this.emit({ kind: "response", method, url, status: response.status, requestId, durationMs });
|
|
1190
|
+
if (!text) return void 0;
|
|
1191
|
+
let parsed = JSON.parse(text);
|
|
1192
|
+
if (route.shape) parsed = route.shape(parsed, camelParams);
|
|
1193
|
+
return camelToSnake(parsed);
|
|
1194
|
+
}
|
|
1195
|
+
emit(event) {
|
|
1196
|
+
if (!this.logger) return;
|
|
1197
|
+
try {
|
|
1198
|
+
this.logger(event);
|
|
1199
|
+
} catch {
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
async buildHeaders(includeContentType = false) {
|
|
1203
|
+
const headers = {};
|
|
1204
|
+
const canSendUserAgent = supportsUserAgentHeader();
|
|
1205
|
+
for (const [key, value] of Object.entries(this.headers)) {
|
|
1206
|
+
if (key.toLowerCase() === "user-agent" && !canSendUserAgent) continue;
|
|
1207
|
+
headers[key] = value;
|
|
1208
|
+
}
|
|
1209
|
+
if (canSendUserAgent && !Object.keys(headers).some((key) => key.toLowerCase() === "user-agent")) {
|
|
1210
|
+
headers["User-Agent"] = defaultUserAgent();
|
|
1211
|
+
}
|
|
1212
|
+
if (includeContentType) headers["Content-Type"] = "application/json";
|
|
1213
|
+
const token = this.tokenProvider ? await this.tokenProvider() : this.apiKey;
|
|
1214
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
1215
|
+
return headers;
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// src/client.ts
|
|
1220
|
+
var DEFAULT_ENDPOINT = "https://memory.neo4jlabs.com/v1";
|
|
1221
|
+
var MemoryClient = class {
|
|
1222
|
+
/** Short-term (conversational) memory operations. */
|
|
1223
|
+
shortTerm;
|
|
1224
|
+
/** Long-term (entity / preference / fact / graph) memory operations. */
|
|
1225
|
+
longTerm;
|
|
1226
|
+
/** Reasoning (trace / step / tool call / provenance) memory operations. */
|
|
1227
|
+
reasoning;
|
|
1228
|
+
/** Read-only Cypher query console (hosted service only). */
|
|
1229
|
+
query;
|
|
1230
|
+
/** API-key & OAuth management (hosted service only). */
|
|
1231
|
+
auth;
|
|
1232
|
+
transport;
|
|
1233
|
+
constructor(optionsOrTransport = {}) {
|
|
1234
|
+
if (isTransport(optionsOrTransport)) {
|
|
1235
|
+
this.transport = optionsOrTransport;
|
|
1236
|
+
} else {
|
|
1237
|
+
this.transport = new LazyConnectTransport(createTransport(optionsOrTransport));
|
|
1238
|
+
}
|
|
1239
|
+
this.shortTerm = new ShortTermMemory(this.transport);
|
|
1240
|
+
this.longTerm = new LongTermMemory(this.transport);
|
|
1241
|
+
this.reasoning = new ReasoningMemory(this.transport);
|
|
1242
|
+
this.query = new QueryConsole(this.transport);
|
|
1243
|
+
this.auth = new AuthClient(this.transport);
|
|
1244
|
+
}
|
|
1245
|
+
async connect() {
|
|
1246
|
+
await this.transport.connect();
|
|
1247
|
+
}
|
|
1248
|
+
async close() {
|
|
1249
|
+
await this.transport.close();
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
function isTransport(obj) {
|
|
1253
|
+
return typeof obj === "object" && obj !== null && "request" in obj && typeof obj.request === "function";
|
|
1254
|
+
}
|
|
1255
|
+
function pickTransport(endpoint, mode) {
|
|
1256
|
+
if (mode === "bridge" || mode === "rest") return mode;
|
|
1257
|
+
return /\/v\d+\b/.test(endpoint) ? "rest" : "bridge";
|
|
1258
|
+
}
|
|
1259
|
+
function resolveApiKey(option) {
|
|
1260
|
+
if (option !== void 0) return option;
|
|
1261
|
+
if (typeof process === "undefined" || !process.env) return void 0;
|
|
1262
|
+
return process.env.MEMORY_API_KEY;
|
|
1263
|
+
}
|
|
1264
|
+
function createTransport(options) {
|
|
1265
|
+
const endpoint = options.endpoint;
|
|
1266
|
+
const apiKey = resolveApiKey(options.apiKey);
|
|
1267
|
+
const choice = pickTransport(endpoint ?? DEFAULT_ENDPOINT, options.transport);
|
|
1268
|
+
if (choice === "rest") {
|
|
1269
|
+
return new RestTransport({
|
|
1270
|
+
endpoint: endpoint ?? DEFAULT_ENDPOINT,
|
|
1271
|
+
apiKey,
|
|
1272
|
+
tokenProvider: options.tokenProvider,
|
|
1273
|
+
timeout: options.timeout,
|
|
1274
|
+
headers: options.headers,
|
|
1275
|
+
logger: options.logger
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
if (!endpoint) {
|
|
1279
|
+
throw new ValidationError("endpoint must be provided for bridge transport.");
|
|
1280
|
+
}
|
|
1281
|
+
return new BridgeTransport({
|
|
1282
|
+
endpoint,
|
|
1283
|
+
apiKey,
|
|
1284
|
+
timeout: options.timeout,
|
|
1285
|
+
headers: options.headers,
|
|
1286
|
+
logger: options.logger
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
var LazyConnectTransport = class {
|
|
1290
|
+
constructor(inner) {
|
|
1291
|
+
this.inner = inner;
|
|
1292
|
+
}
|
|
1293
|
+
connectPromise = null;
|
|
1294
|
+
async request(method, params) {
|
|
1295
|
+
return this.inner.request(method, params);
|
|
1296
|
+
}
|
|
1297
|
+
async connect() {
|
|
1298
|
+
if (!this.connectPromise) {
|
|
1299
|
+
this.connectPromise = this.inner.connect().catch((err) => {
|
|
1300
|
+
this.connectPromise = null;
|
|
1301
|
+
throw err;
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
return this.connectPromise;
|
|
1305
|
+
}
|
|
1306
|
+
async close() {
|
|
1307
|
+
return this.inner.close();
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
|
|
1311
|
+
export { AuthClient, LongTermMemory, MemoryClient, QueryConsole, ReasoningMemory, RestTransport, ShortTermMemory };
|
|
1312
|
+
//# sourceMappingURL=index.js.map
|
|
1313
|
+
//# sourceMappingURL=index.js.map
|