@knowall-ai/reverie 0.4.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.
@@ -0,0 +1,251 @@
1
+ import { guidanceTool } from './guidance-tool.js';
2
+ /**
3
+ * MCP Neo4j Agent Memory Tools
4
+ *
5
+ * Tool descriptions are kept simple to avoid breaking prompt templates in 3rd party solutions.
6
+ * Use the get_guidance tool to access detailed information about labels, relationships, and best practices.
7
+ */
8
+ export const tools = [
9
+ {
10
+ name: 'search_memories',
11
+ description: 'Hybrid keyword + semantic search across the knowledge graph. "Ben Weeks" can also find "Benjamin Weeks" and each result includes _score and _match.',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ query: {
16
+ type: 'string',
17
+ description: 'Search text to find in any property. Keyword mode matches any word, while semantic mode can surface close meanings and name variants.',
18
+ },
19
+ label: {
20
+ type: 'string',
21
+ description: 'Filter by memory label',
22
+ },
23
+ depth: {
24
+ type: 'integer',
25
+ minimum: 0,
26
+ maximum: 5,
27
+ description: 'Relationship depth to include, 0 to 5, defaults to 1',
28
+ },
29
+ order_by: {
30
+ type: 'string',
31
+ description: 'Sort order such as created_at DESC, name ASC',
32
+ },
33
+ limit: {
34
+ type: 'integer',
35
+ minimum: 1,
36
+ maximum: 200,
37
+ description: 'Maximum results to return, 1 to 200, defaults to 10',
38
+ },
39
+ since_date: {
40
+ type: 'string',
41
+ description: 'ISO date string to filter memories created after this date (e.g., "2024-01-01" or "2024-01-01T00:00:00Z")',
42
+ },
43
+ search_mode: {
44
+ type: 'string',
45
+ enum: ['hybrid', 'keyword', 'semantic', 'exact'],
46
+ description: 'Search mode: hybrid (default), keyword-only, semantic-only, or exact (case-insensitive equality on name/aliases/email: use before creating a memory).',
47
+ },
48
+ similarity_threshold: {
49
+ type: 'number',
50
+ minimum: 0,
51
+ maximum: 1,
52
+ description: 'Semantic similarity threshold, 0 to 1 inclusive, defaults to 0.4.',
53
+ },
54
+ include_archived: {
55
+ type: 'boolean',
56
+ description: 'Include memories with status "archived" (excluded by default).',
57
+ },
58
+ },
59
+ required: [],
60
+ additionalProperties: false,
61
+ },
62
+ },
63
+ {
64
+ name: 'create_memory',
65
+ description: 'Create a new memory in the knowledge graph. Consider that the memory might already exist, so Search → Create → Connect (its important to try and connect memories)',
66
+ inputSchema: {
67
+ type: 'object',
68
+ properties: {
69
+ label: {
70
+ type: 'string',
71
+ description: 'Memory label: a plain identifier, Capitalised singular by convention (Person, Place, Organization, Project, Event, Topic, Object, Animal, Concept, Meeting, Decision…). Use list_memory_labels first for consistency; dream canonicalises lowercase labels.',
72
+ },
73
+ properties: {
74
+ type: 'object',
75
+ description: 'Information to store about this memory (use "name" as primary identifier, e.g. {name: "John Smith", age: 30, occupation: "Engineer"})',
76
+ additionalProperties: true,
77
+ },
78
+ },
79
+ required: ['label', 'properties'],
80
+ },
81
+ },
82
+ {
83
+ name: 'create_connection',
84
+ description: 'Create a connection between two memories (its good to have connected memories)',
85
+ inputSchema: {
86
+ type: 'object',
87
+ properties: {
88
+ fromMemoryId: {
89
+ type: 'number',
90
+ description: 'ID of the source memory',
91
+ },
92
+ toMemoryId: {
93
+ type: 'number',
94
+ description: 'ID of the target memory',
95
+ },
96
+ type: {
97
+ type: 'string',
98
+ description: 'Relationship type such as KNOWS, WORKS_ON, LIVES_IN, HAS_SKILL, PARTICIPATES_IN',
99
+ },
100
+ properties: {
101
+ type: 'object',
102
+ description: 'Optional relationship metadata (e.g. {since: "2023-01", role: "Manager", status: "active"})',
103
+ additionalProperties: true,
104
+ },
105
+ },
106
+ required: ['fromMemoryId', 'toMemoryId', 'type'],
107
+ },
108
+ },
109
+ {
110
+ name: 'update_memory',
111
+ description: 'Update properties of an existing memory such as adding more detail or make a change when you find out something new',
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ nodeId: {
116
+ type: 'number',
117
+ description: 'ID of the memory to update',
118
+ },
119
+ properties: {
120
+ type: 'object',
121
+ description: 'Properties to update/add',
122
+ additionalProperties: true,
123
+ },
124
+ },
125
+ required: ['nodeId', 'properties'],
126
+ },
127
+ },
128
+ {
129
+ name: 'update_connection',
130
+ description: 'Update properties of an existing connection between memories',
131
+ inputSchema: {
132
+ type: 'object',
133
+ properties: {
134
+ fromMemoryId: {
135
+ type: 'number',
136
+ description: 'ID of the source memory',
137
+ },
138
+ toMemoryId: {
139
+ type: 'number',
140
+ description: 'ID of the target memory',
141
+ },
142
+ type: {
143
+ type: 'string',
144
+ description: 'Relationship type to identify which connection to update (e.g. WORKS_AT, KNOWS, MANAGES)',
145
+ },
146
+ properties: {
147
+ type: 'object',
148
+ description: 'Properties to update/add (e.g. {status: "completed", end_date: "2024-01"})',
149
+ additionalProperties: true,
150
+ },
151
+ },
152
+ required: ['fromMemoryId', 'toMemoryId', 'type', 'properties'],
153
+ },
154
+ },
155
+ {
156
+ name: 'delete_memory',
157
+ description: 'Delete a memory and all its connections (use with caution - this permanently removes the memory and all its connections)',
158
+ inputSchema: {
159
+ type: 'object',
160
+ properties: {
161
+ nodeId: {
162
+ type: 'number',
163
+ description: 'ID of the memory to delete',
164
+ },
165
+ },
166
+ required: ['nodeId'],
167
+ },
168
+ },
169
+ {
170
+ name: 'delete_connection',
171
+ description: 'Delete a specific connection between two memories (use with caution - this permanently removes the relationship)',
172
+ inputSchema: {
173
+ type: 'object',
174
+ properties: {
175
+ fromMemoryId: {
176
+ type: 'number',
177
+ description: 'ID of the source memory',
178
+ },
179
+ toMemoryId: {
180
+ type: 'number',
181
+ description: 'ID of the target memory',
182
+ },
183
+ type: {
184
+ type: 'string',
185
+ description: 'Exact relationship type to delete (e.g. WORKS_AT, KNOWS, MANAGES)',
186
+ },
187
+ },
188
+ required: ['fromMemoryId', 'toMemoryId', 'type'],
189
+ },
190
+ },
191
+ {
192
+ name: 'list_memory_labels',
193
+ description: 'List all unique memory labels currently in use with their counts (useful for getting an overview of the knowledge graph)',
194
+ inputSchema: {
195
+ type: 'object',
196
+ properties: {
197
+ include_archived: {
198
+ type: 'boolean',
199
+ description: 'Include labels of archived memories (excluded by default).',
200
+ },
201
+ },
202
+ required: [],
203
+ },
204
+ },
205
+ {
206
+ name: 'query_memories',
207
+ description: 'Run a read-only Cypher query and return up to 200 scrubbed rows.',
208
+ inputSchema: {
209
+ type: 'object',
210
+ properties: {
211
+ cypher: {
212
+ type: 'string',
213
+ description: 'Read-only Cypher to execute.',
214
+ },
215
+ params: {
216
+ type: 'object',
217
+ description: 'Optional parameter map for the Cypher query.',
218
+ additionalProperties: true,
219
+ },
220
+ },
221
+ required: ['cypher'],
222
+ additionalProperties: false,
223
+ },
224
+ },
225
+ {
226
+ name: 'memory_stats',
227
+ description: 'Summarize node, relationship, label, embedding, and orphan counts for the graph.',
228
+ inputSchema: {
229
+ type: 'object',
230
+ properties: {},
231
+ required: [],
232
+ additionalProperties: false,
233
+ },
234
+ },
235
+ {
236
+ name: 'dream',
237
+ description: 'Deterministically relabel, merge duplicates, and refresh embeddings, with an optional dry run report.',
238
+ inputSchema: {
239
+ type: 'object',
240
+ properties: {
241
+ dry_run: {
242
+ type: 'boolean',
243
+ description: 'If true, report planned changes without writing them.',
244
+ },
245
+ },
246
+ required: [],
247
+ additionalProperties: false,
248
+ },
249
+ },
250
+ guidanceTool,
251
+ ];
@@ -0,0 +1,284 @@
1
+ export const guidanceTool = {
2
+ name: 'get_guidance',
3
+ description: 'Get help on using the memory tools effectively',
4
+ inputSchema: {
5
+ type: 'object',
6
+ properties: {
7
+ topic: {
8
+ type: 'string',
9
+ description: 'Topic: connections, labels, relationships, best-practices, examples, or leave empty for all',
10
+ },
11
+ },
12
+ required: [],
13
+ },
14
+ };
15
+ export function getGuidanceContent(topic) {
16
+ const sections = {
17
+ connections: `## The Power of Connections
18
+
19
+ **Why Connections Matter**
20
+ - Isolated memories = limited utility
21
+ - Connected memories = exponential value
22
+ - Each connection adds context and meaning
23
+ - Enables discovery of hidden patterns
24
+
25
+ **Connection Strategy**
26
+ 1. Create the memory first
27
+ 2. Immediately ask: "What does this relate to?"
28
+ 3. Search for related memories
29
+ 4. Create meaningful connections
30
+ 5. Add properties to connections for context
31
+
32
+ **Example Flow**
33
+ 1. User: "John works at Google as a software engineer"
34
+ 2. **Search for John first** - he might already exist!
35
+ 3. If not found, create memory for John (person)
36
+ 4. **Search for Google** - it probably already exists
37
+ 5. If not found, create Google (organization)
38
+ 6. Connect: John -[WORKS_AT]-> Google
39
+ 7. Add properties: role="Software Engineer", since="2020"
40
+
41
+ **Connection Patterns**
42
+ - Person -> Organization (WORKS_AT, FOUNDED, OWNS)
43
+ - Person -> Person (KNOWS, MANAGES, MARRIED_TO)
44
+ - Person -> Skill (HAS_SKILL)
45
+ - Person -> Place (LIVES_IN, FROM)
46
+ - Project -> Person (LED_BY, INVOLVES)
47
+ - Event -> Person (ATTENDED_BY, ORGANIZED_BY)`,
48
+ labels: `## Common Memory Labels
49
+
50
+ Use these labels when creating memories (Capitalised singular is canonical; lowercase is accepted and canonicalised by dream):
51
+
52
+ **People & Living Things**
53
+ - person: Individual people
54
+ - animal: Pets, wildlife
55
+ - plant: Trees, flowers, crops
56
+
57
+ **Places & Locations**
58
+ - place: General locations
59
+ - organization: Companies, institutions
60
+ - vehicle: Cars, planes, boats
61
+
62
+ **Concepts & Activities**
63
+ - project: Work or personal projects
64
+ - event: Meetings, conferences, celebrations
65
+ - topic: Subjects of interest
66
+ - activity: Sports, hobbies
67
+ - skill: Abilities and expertise
68
+
69
+ **Objects & Media**
70
+ - object: Physical items
71
+ - food: Meals, ingredients
72
+ - media: Books, movies, music
73
+ - document: Files, reports
74
+ - tool: Software, hardware
75
+
76
+ **Abstract Concepts**
77
+ - idea: Thoughts, concepts
78
+ - goal: Objectives, targets
79
+ - task: To-dos, assignments
80
+ - habit: Routines, behaviors
81
+ - health: Medical, wellness`,
82
+ relationships: `## Common Relationship Types
83
+
84
+ Use UPPERCASE for relationship types:
85
+
86
+ **Personal Relationships**
87
+ - KNOWS: General acquaintance
88
+ - FRIENDS_WITH: Close friendship
89
+ - MARRIED_TO: Spouse relationship
90
+ - FAMILY_OF: Family connection
91
+
92
+ **Professional Relationships**
93
+ - WORKS_AT: Employment
94
+ - WORKS_ON: Project involvement
95
+ - COLLABORATES_WITH: Working together
96
+ - MANAGES: Leadership role
97
+ - REPORTS_TO: Reporting structure
98
+ - OWNS: Ownership
99
+
100
+ **Location Relationships**
101
+ - LIVES_IN: Residence
102
+ - LOCATED_IN: Physical location
103
+ - VISITED: Past visits
104
+ - FROM: Origin
105
+
106
+ **Skill & Knowledge**
107
+ - HAS_SKILL: Possesses ability
108
+ - TEACHES: Instructing others
109
+ - LEARNS_FROM: Student relationship
110
+
111
+ **Project & Creation**
112
+ - LEADS: Project leadership
113
+ - PARTICIPATES_IN: Involvement
114
+ - CREATED: Authorship
115
+ - USES: Utilization`,
116
+ 'best-practices': `## Best Practices
117
+
118
+ **CRITICAL: Always Create Connections**
119
+ - Isolated memories have limited value - ALWAYS look for connections
120
+ - When storing new information, immediately think: "What does this relate to?"
121
+ - A memory without connections is like a book with no catalog entry
122
+ - The graph becomes exponentially more useful with each connection
123
+
124
+ **Creating Memories - ALWAYS SEARCH FIRST**
125
+ - **CRITICAL: Always search before creating** - memories often already exist
126
+ - Search by name first: search_memories({query: "John Smith"})
127
+ - If multiple matches found, **ASK THE USER TO CONFIRM** which one they mean
128
+ - Show distinguishing details: "I found 3 people named John: John Smith (Engineer at Google), John Smith (Doctor), John Smith (Teacher). Which one did you mean?"
129
+ - If unsure about a match, describe it and ask: "I found John Smith who works at TechCorp. Is this the same person?"
130
+ - Only create new memory after confirming it's not a duplicate
131
+ - Labels are plain identifiers; Capitalised singular is canonical (Person, Organization, Concept)
132
+ - Include a 'name' property for easy identification
133
+ - 'created_at' is automatic if not provided
134
+ - IMMEDIATELY after creating, connect it to related memories
135
+
136
+ **What belongs in the graph**
137
+ - The graph is a map of entities and how they relate: people, organisations, projects, products, places, animals, concepts. It is not a notebook
138
+ - A property is a durable attribute of an entity: email, role, phone, website, birthday, a preference you'd still want next year
139
+ - Episodes (a call, an email thread, a day's events) mostly belong in your notes or diary. Store in the graph only what they taught you about an entity or a relationship, kept short
140
+ - Prefer a relationship over a property when the fact is about two entities: Ben -[USES {note: "camera on, wants fixes verified"}]-> Teams calls, rather than a paragraph on Ben
141
+ - Make an episode its own memory (e.g. meeting, decision) only when the episode itself is something you'll need to find again
142
+ - A node with dozens of properties is a smell: update_memory returns a _hint past the limit, and dream lists such nodes under "bloated". When you sleep, fold each fact-like key into a short attribute, a relationship, or your notes, then remove it
143
+
144
+ **Building Connections**
145
+ - After creating any memory, ask: "What existing memories relate to this?"
146
+ - Create connections for: people→organizations, people→projects, people→skills
147
+ - Add relationship properties for rich context (since, role, status)
148
+ - One memory can have many connections - don't limit yourself
149
+
150
+ **Searching**
151
+ - Empty query string returns all memories
152
+ - Use search_mode: "hybrid" (the default) to return keyword matches first, then semantic matches above the threshold
153
+ - Use search_mode: "keyword" to match any word of the query as a substring of any searchable content property (not timestamps, status or embedding fields), or search_mode: "semantic" for meaning-first recall
154
+ - Use search_mode: "exact" before creating: it matches only a memory whose name, alias or email equals the query, case-insensitively
155
+ - Archived memories are excluded unless include_archived: true
156
+ - Lower similarity_threshold (for example 0.35) to widen semantic matches, or raise it (for example 0.7) to be stricter
157
+ - Results include _score and _match so you can explain why something was returned
158
+ - Use label parameter to filter by type
159
+ - Increase depth to include more relationships (depth=2 or 3 for rich context)
160
+ - Default limit is 10, max is 200
161
+
162
+ **Managing Relationships**
163
+ - Use UPPERCASE for relationship types
164
+ - Add properties to relationships for context (e.g., since, role)
165
+ - Search for memory IDs before creating connections
166
+ - Use list_memory_labels to see existing labels
167
+
168
+ **Data Organization**
169
+ - Keep labels simple and consistent
170
+ - Reuse existing labels when possible
171
+ - Add descriptive properties to memories
172
+ - Use relationships instead of complex properties
173
+ - Think in graphs: nodes (memories) + edges (relationships) = knowledge`,
174
+ examples: `## Examples
175
+
176
+ **Creating a Person (WITH SEARCH FIRST)**
177
+ \`\`\`
178
+ // ALWAYS search first to avoid duplicates
179
+ const searchResult = search_memories({ query: "Alice Johnson" })
180
+
181
+ if (searchResult.length === 0) {
182
+ // Only create if not found
183
+ create_memory({
184
+ label: "person",
185
+ properties: {
186
+ name: "Alice Johnson",
187
+ occupation: "Software Engineer",
188
+ company: "Tech Corp"
189
+ }
190
+ })
191
+ } else {
192
+ // Use existing memory ID: searchResult[0].memory._id
193
+ }
194
+ \`\`\`
195
+
196
+ **Creating a Relationship**
197
+ \`\`\`
198
+ // First, find the IDs
199
+ search_memories({ query: "Alice Johnson" })
200
+ search_memories({ query: "Tech Corp" })
201
+
202
+ // Then connect them
203
+ create_connection({
204
+ fromMemoryId: 123,
205
+ toMemoryId: 456,
206
+ type: "WORKS_AT",
207
+ properties: {
208
+ role: "Senior Engineer",
209
+ since: "2020-01-15"
210
+ }
211
+ })
212
+ \`\`\`
213
+
214
+ **Handling Ambiguous Matches**
215
+ \`\`\`
216
+ // User says: "Add that Sarah works at Microsoft"
217
+ const searchResult = search_memories({ query: "Sarah" })
218
+
219
+ // Found multiple matches - ASK USER TO CONFIRM
220
+ if (searchResult.length > 1) {
221
+ // Show options to user:
222
+ // "I found 3 people named Sarah:
223
+ // 1. Sarah Johnson (Marketing Manager at TechCorp)
224
+ // 2. Sarah Chen (Data Scientist)
225
+ // 3. Sarah Williams (Designer at StartupXYZ)
226
+ // Which Sarah works at Microsoft?"
227
+ }
228
+
229
+ // If only one match but uncertain
230
+ if (searchResult.length === 1) {
231
+ // Confirm with user:
232
+ // "I found Sarah Johnson who is a Marketing Manager at TechCorp.
233
+ // Is this the Sarah who now works at Microsoft?"
234
+ }
235
+ \`\`\`
236
+
237
+ **Searching with Depth**
238
+ \`\`\`
239
+ // Find all people and their connections
240
+ search_memories({
241
+ query: "",
242
+ label: "person",
243
+ depth: 2,
244
+ limit: 50
245
+ })
246
+ \`\`\`
247
+
248
+ **Updating a Memory**
249
+ \`\`\`
250
+ update_memory({
251
+ nodeId: 123,
252
+ properties: {
253
+ occupation: "Lead Engineer",
254
+ skills: ["Python", "Neo4j", "MCP"]
255
+ }
256
+ })
257
+ \`\`\``,
258
+ };
259
+ if (!topic) {
260
+ // Return all sections
261
+ return `# Neo4j Agent Memory Guidance
262
+
263
+ ${sections.connections}
264
+
265
+ ${sections.labels}
266
+
267
+ ${sections.relationships}
268
+
269
+ ${sections['best-practices']}
270
+
271
+ ${sections.examples}`;
272
+ }
273
+ const content = sections[topic];
274
+ if (content) {
275
+ return content;
276
+ }
277
+ return `Unknown topic: '${topic}'. Available topics: connections, labels, relationships, best-practices, examples, or leave empty for all guidance.`;
278
+ }
279
+ export function isGetGuidanceArgs(args) {
280
+ if (typeof args !== 'object' || args === null)
281
+ return true; // Allow empty args
282
+ const obj = args;
283
+ return obj.topic === undefined || typeof obj.topic === 'string';
284
+ }
package/build/types.js ADDED
@@ -0,0 +1,115 @@
1
+ /** Labels and relationship types are interpolated into Cypher, so they must be plain identifiers. */
2
+ export const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
3
+ export function isIdentifier(value) {
4
+ return typeof value === 'string' && IDENTIFIER_RE.test(value);
5
+ }
6
+ /** Backtick-quote a validated identifier for use in Cypher. Throws on anything not validated. */
7
+ export function cypherIdentifier(value) {
8
+ if (!isIdentifier(value)) {
9
+ throw new Error(`Invalid Cypher identifier: ${JSON.stringify(value)}`);
10
+ }
11
+ return '`' + value + '`';
12
+ }
13
+ export const SEARCH_MAX_LIMIT = 200;
14
+ export const SEARCH_MAX_DEPTH = 5;
15
+ function isPlainObject(value) {
16
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
17
+ }
18
+ function hasOnlyKeys(value, allowed) {
19
+ return Object.keys(value).every((key) => allowed.includes(key));
20
+ }
21
+ function isIntegerInRange(value, min, max) {
22
+ return typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max;
23
+ }
24
+ const isNodeId = (value) => isIntegerInRange(value, 0, Number.MAX_SAFE_INTEGER);
25
+ export function isCreateMemoryArgs(args) {
26
+ return isPlainObject(args) &&
27
+ hasOnlyKeys(args, ['label', 'properties']) &&
28
+ isIdentifier(args.label) &&
29
+ isPlainObject(args.properties);
30
+ }
31
+ const SEARCH_KEYS = ['query', 'label', 'depth', 'order_by', 'limit', 'since_date', 'search_mode', 'similarity_threshold', 'include_archived'];
32
+ export function isSearchMemoriesArgs(args) {
33
+ if (!isPlainObject(args) || !hasOnlyKeys(args, SEARCH_KEYS))
34
+ return false;
35
+ const searchArgs = args;
36
+ if (searchArgs.query !== undefined && typeof searchArgs.query !== 'string')
37
+ return false;
38
+ if (searchArgs.label !== undefined && typeof searchArgs.label !== 'string')
39
+ return false;
40
+ if (searchArgs.depth !== undefined && !isIntegerInRange(searchArgs.depth, 0, SEARCH_MAX_DEPTH))
41
+ return false;
42
+ if (searchArgs.order_by !== undefined && typeof searchArgs.order_by !== 'string')
43
+ return false;
44
+ if (searchArgs.limit !== undefined && !isIntegerInRange(searchArgs.limit, 1, SEARCH_MAX_LIMIT))
45
+ return false;
46
+ if (searchArgs.since_date !== undefined && typeof searchArgs.since_date !== 'string')
47
+ return false;
48
+ if (searchArgs.search_mode !== undefined && !['hybrid', 'keyword', 'semantic', 'exact'].includes(searchArgs.search_mode))
49
+ return false;
50
+ if (searchArgs.include_archived !== undefined && typeof searchArgs.include_archived !== 'boolean')
51
+ return false;
52
+ if (searchArgs.similarity_threshold !== undefined) {
53
+ const threshold = searchArgs.similarity_threshold;
54
+ if (typeof threshold !== 'number' || !Number.isFinite(threshold) || threshold < 0 || threshold > 1)
55
+ return false;
56
+ }
57
+ return true;
58
+ }
59
+ export function isCreateConnectionArgs(args) {
60
+ return isPlainObject(args) &&
61
+ hasOnlyKeys(args, ['fromMemoryId', 'toMemoryId', 'type', 'properties']) &&
62
+ isNodeId(args.fromMemoryId) &&
63
+ isNodeId(args.toMemoryId) &&
64
+ isIdentifier(args.type) &&
65
+ (args.properties === undefined || isPlainObject(args.properties));
66
+ }
67
+ export function isUpdateMemoryArgs(args) {
68
+ return isPlainObject(args) &&
69
+ hasOnlyKeys(args, ['nodeId', 'properties']) &&
70
+ isNodeId(args.nodeId) &&
71
+ isPlainObject(args.properties);
72
+ }
73
+ export function isUpdateConnectionArgs(args) {
74
+ return isPlainObject(args) &&
75
+ hasOnlyKeys(args, ['fromMemoryId', 'toMemoryId', 'type', 'properties']) &&
76
+ isNodeId(args.fromMemoryId) &&
77
+ isNodeId(args.toMemoryId) &&
78
+ isIdentifier(args.type) &&
79
+ isPlainObject(args.properties);
80
+ }
81
+ export function isDeleteMemoryArgs(args) {
82
+ return isPlainObject(args) && hasOnlyKeys(args, ['nodeId']) && isNodeId(args.nodeId);
83
+ }
84
+ export function isDeleteConnectionArgs(args) {
85
+ return isPlainObject(args) &&
86
+ hasOnlyKeys(args, ['fromMemoryId', 'toMemoryId', 'type']) &&
87
+ isNodeId(args.fromMemoryId) &&
88
+ isNodeId(args.toMemoryId) &&
89
+ isIdentifier(args.type);
90
+ }
91
+ export function isListMemoryLabelsArgs(args) {
92
+ return isPlainObject(args) && hasOnlyKeys(args, ['include_archived']) &&
93
+ (args.include_archived === undefined || typeof args.include_archived === 'boolean');
94
+ }
95
+ export function isQueryMemoriesArgs(args) {
96
+ if (!isPlainObject(args) || !hasOnlyKeys(args, ['cypher', 'params']))
97
+ return false;
98
+ const queryArgs = args;
99
+ if (typeof queryArgs.cypher !== 'string' || queryArgs.cypher.trim() === '')
100
+ return false;
101
+ if (queryArgs.params !== undefined && !isPlainObject(queryArgs.params))
102
+ return false;
103
+ return true;
104
+ }
105
+ export function isMemoryStatsArgs(args) {
106
+ return isPlainObject(args) && hasOnlyKeys(args, []);
107
+ }
108
+ export function isDreamArgs(args) {
109
+ if (!isPlainObject(args) || !hasOnlyKeys(args, ['dry_run']))
110
+ return false;
111
+ const dreamArgs = args;
112
+ if (dreamArgs.dry_run !== undefined && typeof dreamArgs.dry_run !== 'boolean')
113
+ return false;
114
+ return true;
115
+ }