@prmichaelsen/remember-mcp 3.17.2 → 3.17.4
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/AGENT.md +1 -1
- package/agent/commands/acp.init.md +92 -4
- package/dist/server-factory.js +101 -25
- package/dist/server.js +101 -25
- package/package.json +4 -3
package/AGENT.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
> **🤖 Agent Directive**: If you are reading this file, the command `@acp-init` has been invoked. Follow the steps below to execute this command.
|
|
4
4
|
|
|
5
5
|
**Namespace**: acp
|
|
6
|
-
**Version**: 1.
|
|
6
|
+
**Version**: 1.1.0
|
|
7
7
|
**Created**: 2026-02-16
|
|
8
|
-
**Last Updated**: 2026-
|
|
8
|
+
**Last Updated**: 2026-03-09
|
|
9
9
|
**Status**: Active
|
|
10
10
|
**Scripts**: None
|
|
11
11
|
|
|
@@ -17,6 +17,38 @@
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
## Arguments
|
|
21
|
+
|
|
22
|
+
| Argument | Aliases | Description |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `--quick` | `-q` | Fast init: skips version checks, source file review, and documentation sync. Equivalent to `--skip checks,files,sync` |
|
|
25
|
+
| `--skip <items>` | | Comma-separated list of steps to skip. Valid items: `checks`, `sessions`, `docs`, `global`, `keys`, `files`, `sync`, `progress` |
|
|
26
|
+
|
|
27
|
+
### Skip Items Reference
|
|
28
|
+
|
|
29
|
+
| Item | Steps Skipped | Description |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `checks` | Step 1 | ACP version update check |
|
|
32
|
+
| `sessions` | Step 1.5 | Session registration and sibling display |
|
|
33
|
+
| `docs` | Step 2 | Reading agent documentation (progress, designs, milestones, tasks, patterns) |
|
|
34
|
+
| `global` | Step 2.5 | Global package discovery |
|
|
35
|
+
| `keys` | Step 2.8 | Key file index reading |
|
|
36
|
+
| `files` | Steps 3-4 | Source file identification and review |
|
|
37
|
+
| `sync` | Steps 5-6 | Documentation drift detection and stale doc updates |
|
|
38
|
+
| `progress` | Step 7 | Progress tracking updates |
|
|
39
|
+
|
|
40
|
+
### Argument Parsing
|
|
41
|
+
|
|
42
|
+
Arguments are parsed from the user's invocation using natural language matching:
|
|
43
|
+
- `@acp-init --quick` or `@acp-init -q`
|
|
44
|
+
- `@acp-init --skip checks,sync`
|
|
45
|
+
- `@acp-init --quick --skip sessions` (quick mode plus additional skips)
|
|
46
|
+
- `@acp-init --skip checks,files,sync,progress` (granular control)
|
|
47
|
+
|
|
48
|
+
When `--quick` is combined with `--skip`, the skip sets are merged (union).
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
20
52
|
## What This Command Does
|
|
21
53
|
|
|
22
54
|
This command performs a comprehensive initialization of the agent's context for working on an ACP-structured project. It checks for ACP updates, reads all documentation in the `agent/` directory, reviews key source files to understand the current implementation, updates any stale documentation, and refreshes progress tracking.
|
|
@@ -39,6 +71,8 @@ Unlike `@acp-status` which only reads progress.yaml, or `@acp-proceed` which foc
|
|
|
39
71
|
|
|
40
72
|
### 1. Check for ACP Updates
|
|
41
73
|
|
|
74
|
+
**Skip item**: `checks` | **Skipped by**: `--quick`
|
|
75
|
+
|
|
42
76
|
Check if newer version of ACP is available.
|
|
43
77
|
|
|
44
78
|
**Actions**:
|
|
@@ -51,6 +85,8 @@ Check if newer version of ACP is available.
|
|
|
51
85
|
|
|
52
86
|
### 1.5. Register Session and Show Siblings (Optional)
|
|
53
87
|
|
|
88
|
+
**Skip item**: `sessions`
|
|
89
|
+
|
|
54
90
|
Register this agent session and display any active sibling sessions.
|
|
55
91
|
|
|
56
92
|
**Actions**:
|
|
@@ -70,6 +106,8 @@ Active Sessions: 2 others
|
|
|
70
106
|
|
|
71
107
|
### 2. Read All Agent Documentation
|
|
72
108
|
|
|
109
|
+
**Skip item**: `docs`
|
|
110
|
+
|
|
73
111
|
Load complete context from the agent/ directory.
|
|
74
112
|
|
|
75
113
|
**Actions**:
|
|
@@ -85,6 +123,8 @@ Load complete context from the agent/ directory.
|
|
|
85
123
|
|
|
86
124
|
### 2.5. Discover Global Packages (Optional)
|
|
87
125
|
|
|
126
|
+
**Skip item**: `global`
|
|
127
|
+
|
|
88
128
|
Check for globally installed ACP packages.
|
|
89
129
|
|
|
90
130
|
**Actions**:
|
|
@@ -119,6 +159,8 @@ Check for globally installed ACP packages.
|
|
|
119
159
|
|
|
120
160
|
### 2.8. Read Key Files from Index
|
|
121
161
|
|
|
162
|
+
**Skip item**: `keys`
|
|
163
|
+
|
|
122
164
|
Load critical project files from the key file index.
|
|
123
165
|
|
|
124
166
|
**Actions**:
|
|
@@ -147,6 +189,8 @@ Load critical project files from the key file index.
|
|
|
147
189
|
|
|
148
190
|
### 3. Identify Key Source Files
|
|
149
191
|
|
|
192
|
+
**Skip item**: `files` | **Skipped by**: `--quick`
|
|
193
|
+
|
|
150
194
|
Determine which source files are most important to review.
|
|
151
195
|
|
|
152
196
|
**Actions**:
|
|
@@ -160,6 +204,8 @@ Determine which source files are most important to review.
|
|
|
160
204
|
|
|
161
205
|
### 4. Review Key Source Files
|
|
162
206
|
|
|
207
|
+
**Skip item**: `files` | **Skipped by**: `--quick`
|
|
208
|
+
|
|
163
209
|
Read important source files to understand current implementation.
|
|
164
210
|
|
|
165
211
|
**Actions**:
|
|
@@ -174,6 +220,8 @@ Read important source files to understand current implementation.
|
|
|
174
220
|
|
|
175
221
|
### 5. Identify Documentation Drift
|
|
176
222
|
|
|
223
|
+
**Skip item**: `sync` | **Skipped by**: `--quick`
|
|
224
|
+
|
|
177
225
|
Compare documentation with actual implementation.
|
|
178
226
|
|
|
179
227
|
**Actions**:
|
|
@@ -187,6 +235,8 @@ Compare documentation with actual implementation.
|
|
|
187
235
|
|
|
188
236
|
### 6. Update Stale Documentation
|
|
189
237
|
|
|
238
|
+
**Skip item**: `sync` | **Skipped by**: `--quick`
|
|
239
|
+
|
|
190
240
|
Refresh outdated documentation to match current state.
|
|
191
241
|
|
|
192
242
|
**Actions**:
|
|
@@ -200,6 +250,8 @@ Refresh outdated documentation to match current state.
|
|
|
200
250
|
|
|
201
251
|
### 7. Update Progress Tracking
|
|
202
252
|
|
|
253
|
+
**Skip item**: `progress`
|
|
254
|
+
|
|
203
255
|
Refresh progress.yaml with latest status.
|
|
204
256
|
|
|
205
257
|
**Actions**:
|
|
@@ -227,6 +279,26 @@ Provide comprehensive status report.
|
|
|
227
279
|
|
|
228
280
|
**Expected Outcome**: User has complete context and knows what to do next
|
|
229
281
|
|
|
282
|
+
### 9. Display Usage Tip
|
|
283
|
+
|
|
284
|
+
Show a helpful tip about init flags when no flags were used.
|
|
285
|
+
|
|
286
|
+
**Actions**:
|
|
287
|
+
- If the user invoked `@acp-init` **without** `--quick` or `--skip`, display the following tip at the end of the output:
|
|
288
|
+
```
|
|
289
|
+
Tip: Use `@acp-init --quick` to skip version checks, source file review, and doc sync for faster startup. Use `--skip <items>` to skip individual steps (e.g. `--skip checks,files`).
|
|
290
|
+
```
|
|
291
|
+
- If the user already used `--quick` or `--skip`, do **not** display the tip (they already know about it).
|
|
292
|
+
|
|
293
|
+
**Expected Outcome**: Users discover the faster init modes naturally
|
|
294
|
+
|
|
295
|
+
### Handling Skipped Steps
|
|
296
|
+
|
|
297
|
+
When a step is skipped (via `--quick` or `--skip`), the agent should:
|
|
298
|
+
1. **Not execute** any actions for that step
|
|
299
|
+
2. **Not display** the step's section header or output block
|
|
300
|
+
3. Simply omit the step silently — no "skipped" messages needed unless the agent chooses to show a compact summary of what was skipped at the top of the output
|
|
301
|
+
|
|
230
302
|
---
|
|
231
303
|
|
|
232
304
|
## Verification
|
|
@@ -372,6 +444,22 @@ Ready to proceed with task-2 completion.
|
|
|
372
444
|
|
|
373
445
|
**Result**: Complete onboarding - reads all documentation, understands architecture from source code, gets current status, ready to contribute immediately
|
|
374
446
|
|
|
447
|
+
### Example 4: Quick Init
|
|
448
|
+
|
|
449
|
+
**Context**: Returning to a familiar project, just need docs and status
|
|
450
|
+
|
|
451
|
+
**Invocation**: `@acp-init --quick`
|
|
452
|
+
|
|
453
|
+
**Result**: Skips version checks, source file review, and doc sync. Reads agent documentation, key files, reports status — fast startup in ~10 seconds
|
|
454
|
+
|
|
455
|
+
### Example 5: Selective Skip
|
|
456
|
+
|
|
457
|
+
**Context**: Want everything except version checks and session registration
|
|
458
|
+
|
|
459
|
+
**Invocation**: `@acp-init --skip checks,sessions`
|
|
460
|
+
|
|
461
|
+
**Result**: Full init minus the two skipped steps. All docs read, files reviewed, sync performed, status reported
|
|
462
|
+
|
|
375
463
|
---
|
|
376
464
|
|
|
377
465
|
## Related Commands
|
|
@@ -450,9 +538,9 @@ Ready to proceed with task-2 completion.
|
|
|
450
538
|
|
|
451
539
|
**Namespace**: acp
|
|
452
540
|
**Command**: init
|
|
453
|
-
**Version**: 1.
|
|
541
|
+
**Version**: 1.1.0
|
|
454
542
|
**Created**: 2026-02-16
|
|
455
|
-
**Last Updated**: 2026-
|
|
543
|
+
**Last Updated**: 2026-03-09
|
|
456
544
|
**Status**: Active
|
|
457
545
|
**Compatibility**: ACP 1.0.3+
|
|
458
546
|
**Author**: ACP Project
|
package/dist/server-factory.js
CHANGED
|
@@ -3857,8 +3857,8 @@ var RelationshipService = class {
|
|
|
3857
3857
|
}
|
|
3858
3858
|
// ── Create ──────────────────────────────────────────────────────────
|
|
3859
3859
|
async create(input) {
|
|
3860
|
-
if (input.memory_ids.length <
|
|
3861
|
-
throw new Error("At least
|
|
3860
|
+
if (input.memory_ids.length < 1) {
|
|
3861
|
+
throw new Error("At least 1 memory ID is required to create a relationship");
|
|
3862
3862
|
}
|
|
3863
3863
|
const validated = await this.validateMemoryIds(input.memory_ids);
|
|
3864
3864
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4669,24 +4669,76 @@ var SpaceService = class {
|
|
|
4669
4669
|
const parentId = String(originalMemory.properties.parent_id ?? "");
|
|
4670
4670
|
const threadRootId = String(originalMemory.properties.thread_root_id ?? parentId);
|
|
4671
4671
|
const contentPreview = String(originalMemory.properties.content ?? "").slice(0, 200);
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4672
|
+
let parentOwnerId = "";
|
|
4673
|
+
try {
|
|
4674
|
+
const parentMemory = await fetchMemoryWithAllProperties(this.userCollection, parentId);
|
|
4675
|
+
if (parentMemory) {
|
|
4676
|
+
const parentUserId = String(parentMemory.properties.user_id ?? "");
|
|
4677
|
+
if (parentUserId && parentUserId !== this.userId) {
|
|
4678
|
+
parentOwnerId = parentUserId;
|
|
4679
|
+
} else if (parentUserId === this.userId) {
|
|
4680
|
+
parentOwnerId = this.userId;
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
if (!parentOwnerId) {
|
|
4684
|
+
const publicCollection = await ensurePublicCollection(this.weaviateClient);
|
|
4685
|
+
const directHit = await fetchMemoryWithAllProperties(publicCollection, parentId);
|
|
4686
|
+
if (directHit) {
|
|
4687
|
+
parentOwnerId = String(directHit.properties.author_id ?? directHit.properties.user_id ?? "");
|
|
4688
|
+
}
|
|
4689
|
+
if (!parentOwnerId) {
|
|
4690
|
+
const filter = publicCollection.filter.byProperty("original_memory_id").equal(parentId);
|
|
4691
|
+
const result = await publicCollection.query.fetchObjects({ filters: filter, limit: 1 });
|
|
4692
|
+
if (result.objects.length > 0) {
|
|
4693
|
+
parentOwnerId = String(result.objects[0].properties.author_id ?? "");
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
if (!parentOwnerId) {
|
|
4698
|
+
for (const groupId of groups) {
|
|
4699
|
+
try {
|
|
4700
|
+
const groupCollectionName = getCollectionName(CollectionType.GROUPS, groupId);
|
|
4701
|
+
const groupCollection = this.weaviateClient.collections.get(groupCollectionName);
|
|
4702
|
+
const gDirect = await fetchMemoryWithAllProperties(groupCollection, parentId);
|
|
4703
|
+
if (gDirect) {
|
|
4704
|
+
parentOwnerId = String(gDirect.properties.author_id ?? gDirect.properties.user_id ?? "");
|
|
4705
|
+
break;
|
|
4706
|
+
}
|
|
4707
|
+
const gFilter = groupCollection.filter.byProperty("original_memory_id").equal(parentId);
|
|
4708
|
+
const gResult = await groupCollection.query.fetchObjects({ filters: gFilter, limit: 1 });
|
|
4709
|
+
if (gResult.objects.length > 0) {
|
|
4710
|
+
parentOwnerId = String(gResult.objects[0].properties.author_id ?? "");
|
|
4711
|
+
break;
|
|
4712
|
+
}
|
|
4713
|
+
} catch {
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4675
4716
|
}
|
|
4717
|
+
} catch (err2) {
|
|
4718
|
+
this.logger.warn("Failed to resolve parent owner for comment event", { parentId, err: err2 });
|
|
4676
4719
|
}
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4720
|
+
if (!parentOwnerId) {
|
|
4721
|
+
this.logger.warn("Skipping comment webhook \u2014 could not resolve parent_owner_id", { parentId, memoryId: request.payload.memory_id });
|
|
4722
|
+
} else {
|
|
4723
|
+
if (successfulPublications.some((p) => p.startsWith("spaces:"))) {
|
|
4724
|
+
for (const spaceId of spaces) {
|
|
4725
|
+
await this.eventBus.emit({ type: "comment.published_to_space", memory_id: request.payload.memory_id, parent_id: parentId, thread_root_id: threadRootId, content_preview: contentPreview, space_id: spaceId, owner_id: this.userId, parent_owner_id: parentOwnerId }, actor);
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
const publishedGroups = groups.filter((g) => successfulPublications.some((p) => p === `group: ${g}`));
|
|
4729
|
+
for (const groupId of publishedGroups) {
|
|
4730
|
+
await this.eventBus.emit({ type: "comment.published_to_group", memory_id: request.payload.memory_id, parent_id: parentId, thread_root_id: threadRootId, content_preview: contentPreview, group_id: groupId, owner_id: this.userId, parent_owner_id: parentOwnerId }, actor);
|
|
4731
|
+
}
|
|
4680
4732
|
}
|
|
4681
4733
|
} else {
|
|
4682
4734
|
if (successfulPublications.some((p) => p.startsWith("spaces:"))) {
|
|
4683
4735
|
for (const spaceId of spaces) {
|
|
4684
|
-
this.eventBus.emit({ type: "memory.published_to_space", memory_id: request.payload.memory_id, title, space_id: spaceId, owner_id: this.userId }, actor);
|
|
4736
|
+
await this.eventBus.emit({ type: "memory.published_to_space", memory_id: request.payload.memory_id, title, space_id: spaceId, owner_id: this.userId }, actor);
|
|
4685
4737
|
}
|
|
4686
4738
|
}
|
|
4687
4739
|
const publishedGroups = groups.filter((g) => successfulPublications.some((p) => p === `group: ${g}`));
|
|
4688
4740
|
for (const groupId of publishedGroups) {
|
|
4689
|
-
this.eventBus.emit({ type: "memory.published_to_group", memory_id: request.payload.memory_id, title, group_id: groupId, owner_id: this.userId }, actor);
|
|
4741
|
+
await this.eventBus.emit({ type: "memory.published_to_group", memory_id: request.payload.memory_id, title, group_id: groupId, owner_id: this.userId }, actor);
|
|
4690
4742
|
}
|
|
4691
4743
|
}
|
|
4692
4744
|
}
|
|
@@ -4783,7 +4835,7 @@ var SpaceService = class {
|
|
|
4783
4835
|
targets.push({ kind: "group", id: groupId });
|
|
4784
4836
|
}
|
|
4785
4837
|
if (targets.length > 0) {
|
|
4786
|
-
this.eventBus.emit({ type: "memory.retracted", memory_id: request.payload.memory_id, owner_id: this.userId, targets }, { type: "user", id: this.userId });
|
|
4838
|
+
await this.eventBus.emit({ type: "memory.retracted", memory_id: request.payload.memory_id, owner_id: this.userId, targets }, { type: "user", id: this.userId });
|
|
4787
4839
|
}
|
|
4788
4840
|
}
|
|
4789
4841
|
return {
|
|
@@ -5487,13 +5539,28 @@ var SpaceService = class {
|
|
|
5487
5539
|
} catch {
|
|
5488
5540
|
}
|
|
5489
5541
|
}
|
|
5490
|
-
if (
|
|
5491
|
-
return {
|
|
5542
|
+
if (props) {
|
|
5543
|
+
return {
|
|
5544
|
+
space_ids: Array.isArray(props.space_ids) ? props.space_ids : [],
|
|
5545
|
+
group_ids: Array.isArray(props.group_ids) ? props.group_ids : []
|
|
5546
|
+
};
|
|
5492
5547
|
}
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5548
|
+
try {
|
|
5549
|
+
const collectionName = await this.memoryIndex.lookup(memoryId);
|
|
5550
|
+
if (collectionName) {
|
|
5551
|
+
const collection = this.weaviateClient.collections.get(collectionName);
|
|
5552
|
+
const obj = await fetchMemoryWithAllProperties(collection, memoryId);
|
|
5553
|
+
if (obj) {
|
|
5554
|
+
const p = obj.properties;
|
|
5555
|
+
return {
|
|
5556
|
+
space_ids: Array.isArray(p.space_ids) ? p.space_ids : [],
|
|
5557
|
+
group_ids: Array.isArray(p.group_ids) ? p.group_ids : []
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
} catch {
|
|
5562
|
+
}
|
|
5563
|
+
return { space_ids: [], group_ids: [] };
|
|
5497
5564
|
}
|
|
5498
5565
|
};
|
|
5499
5566
|
|
|
@@ -5719,7 +5786,8 @@ var INITIAL_PERCEPTION = {
|
|
|
5719
5786
|
import { createHmac } from "node:crypto";
|
|
5720
5787
|
function signWebhookPayload(webhookId, timestamp, body, secret) {
|
|
5721
5788
|
const content = `${webhookId}.${timestamp}.${body}`;
|
|
5722
|
-
const
|
|
5789
|
+
const secretBytes = Buffer.from(secret, "base64");
|
|
5790
|
+
const hmac = createHmac("sha256", secretBytes).update(content).digest("base64");
|
|
5723
5791
|
return `v1,${hmac}`;
|
|
5724
5792
|
}
|
|
5725
5793
|
|
|
@@ -5745,7 +5813,7 @@ var BatchedWebhookService = class {
|
|
|
5745
5813
|
this.timeoutMs = config2.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
5746
5814
|
this.onError = config2.onError;
|
|
5747
5815
|
}
|
|
5748
|
-
emit(event, actor) {
|
|
5816
|
+
async emit(event, actor) {
|
|
5749
5817
|
const ownerId = event.owner_id;
|
|
5750
5818
|
const endpoints = this.resolveEndpoint(ownerId);
|
|
5751
5819
|
if (endpoints.length === 0) {
|
|
@@ -5756,6 +5824,7 @@ var BatchedWebhookService = class {
|
|
|
5756
5824
|
return;
|
|
5757
5825
|
}
|
|
5758
5826
|
const envelope = this.buildEnvelope(event, actor);
|
|
5827
|
+
const flushPromises = [];
|
|
5759
5828
|
for (const endpoint of endpoints) {
|
|
5760
5829
|
const url = endpoint.url;
|
|
5761
5830
|
let buffer = this.buffers.get(url);
|
|
@@ -5765,13 +5834,16 @@ var BatchedWebhookService = class {
|
|
|
5765
5834
|
}
|
|
5766
5835
|
buffer.envelopes.push(envelope);
|
|
5767
5836
|
if (buffer.envelopes.length >= this.maxBatchSize) {
|
|
5768
|
-
this.flush(url);
|
|
5837
|
+
flushPromises.push(this.flush(url));
|
|
5769
5838
|
} else if (!buffer.timer) {
|
|
5770
5839
|
buffer.timer = setTimeout(() => this.flush(url), this.flushIntervalMs);
|
|
5771
5840
|
}
|
|
5772
5841
|
}
|
|
5842
|
+
if (flushPromises.length > 0) {
|
|
5843
|
+
await Promise.all(flushPromises);
|
|
5844
|
+
}
|
|
5773
5845
|
}
|
|
5774
|
-
flush(url) {
|
|
5846
|
+
async flush(url) {
|
|
5775
5847
|
const buffer = this.buffers.get(url);
|
|
5776
5848
|
if (!buffer || buffer.envelopes.length === 0)
|
|
5777
5849
|
return;
|
|
@@ -5782,19 +5854,23 @@ var BatchedWebhookService = class {
|
|
|
5782
5854
|
}
|
|
5783
5855
|
buffer.envelopes = [];
|
|
5784
5856
|
buffer.timer = null;
|
|
5785
|
-
|
|
5857
|
+
try {
|
|
5858
|
+
await this.sendBatch(url, endpoint, envelopes);
|
|
5859
|
+
} catch (err2) {
|
|
5786
5860
|
this.logger.error?.("[BatchedWebhookService] batch delivery failed", {
|
|
5787
5861
|
error: err2,
|
|
5788
5862
|
url,
|
|
5789
5863
|
count: envelopes.length
|
|
5790
5864
|
});
|
|
5791
5865
|
this.onError?.(err2, envelopes);
|
|
5792
|
-
}
|
|
5866
|
+
}
|
|
5793
5867
|
}
|
|
5794
|
-
dispose() {
|
|
5868
|
+
async dispose() {
|
|
5869
|
+
const promises = [];
|
|
5795
5870
|
for (const url of this.buffers.keys()) {
|
|
5796
|
-
this.flush(url);
|
|
5871
|
+
promises.push(this.flush(url));
|
|
5797
5872
|
}
|
|
5873
|
+
await Promise.all(promises);
|
|
5798
5874
|
}
|
|
5799
5875
|
async sendBatch(url, endpoint, envelopes) {
|
|
5800
5876
|
const batchId = v4_default();
|
package/dist/server.js
CHANGED
|
@@ -3861,8 +3861,8 @@ var RelationshipService = class {
|
|
|
3861
3861
|
}
|
|
3862
3862
|
// ── Create ──────────────────────────────────────────────────────────
|
|
3863
3863
|
async create(input) {
|
|
3864
|
-
if (input.memory_ids.length <
|
|
3865
|
-
throw new Error("At least
|
|
3864
|
+
if (input.memory_ids.length < 1) {
|
|
3865
|
+
throw new Error("At least 1 memory ID is required to create a relationship");
|
|
3866
3866
|
}
|
|
3867
3867
|
const validated = await this.validateMemoryIds(input.memory_ids);
|
|
3868
3868
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4673,24 +4673,76 @@ var SpaceService = class {
|
|
|
4673
4673
|
const parentId = String(originalMemory.properties.parent_id ?? "");
|
|
4674
4674
|
const threadRootId = String(originalMemory.properties.thread_root_id ?? parentId);
|
|
4675
4675
|
const contentPreview = String(originalMemory.properties.content ?? "").slice(0, 200);
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4676
|
+
let parentOwnerId = "";
|
|
4677
|
+
try {
|
|
4678
|
+
const parentMemory = await fetchMemoryWithAllProperties(this.userCollection, parentId);
|
|
4679
|
+
if (parentMemory) {
|
|
4680
|
+
const parentUserId = String(parentMemory.properties.user_id ?? "");
|
|
4681
|
+
if (parentUserId && parentUserId !== this.userId) {
|
|
4682
|
+
parentOwnerId = parentUserId;
|
|
4683
|
+
} else if (parentUserId === this.userId) {
|
|
4684
|
+
parentOwnerId = this.userId;
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
if (!parentOwnerId) {
|
|
4688
|
+
const publicCollection = await ensurePublicCollection(this.weaviateClient);
|
|
4689
|
+
const directHit = await fetchMemoryWithAllProperties(publicCollection, parentId);
|
|
4690
|
+
if (directHit) {
|
|
4691
|
+
parentOwnerId = String(directHit.properties.author_id ?? directHit.properties.user_id ?? "");
|
|
4692
|
+
}
|
|
4693
|
+
if (!parentOwnerId) {
|
|
4694
|
+
const filter = publicCollection.filter.byProperty("original_memory_id").equal(parentId);
|
|
4695
|
+
const result = await publicCollection.query.fetchObjects({ filters: filter, limit: 1 });
|
|
4696
|
+
if (result.objects.length > 0) {
|
|
4697
|
+
parentOwnerId = String(result.objects[0].properties.author_id ?? "");
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
if (!parentOwnerId) {
|
|
4702
|
+
for (const groupId of groups) {
|
|
4703
|
+
try {
|
|
4704
|
+
const groupCollectionName = getCollectionName(CollectionType.GROUPS, groupId);
|
|
4705
|
+
const groupCollection = this.weaviateClient.collections.get(groupCollectionName);
|
|
4706
|
+
const gDirect = await fetchMemoryWithAllProperties(groupCollection, parentId);
|
|
4707
|
+
if (gDirect) {
|
|
4708
|
+
parentOwnerId = String(gDirect.properties.author_id ?? gDirect.properties.user_id ?? "");
|
|
4709
|
+
break;
|
|
4710
|
+
}
|
|
4711
|
+
const gFilter = groupCollection.filter.byProperty("original_memory_id").equal(parentId);
|
|
4712
|
+
const gResult = await groupCollection.query.fetchObjects({ filters: gFilter, limit: 1 });
|
|
4713
|
+
if (gResult.objects.length > 0) {
|
|
4714
|
+
parentOwnerId = String(gResult.objects[0].properties.author_id ?? "");
|
|
4715
|
+
break;
|
|
4716
|
+
}
|
|
4717
|
+
} catch {
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4679
4720
|
}
|
|
4721
|
+
} catch (err2) {
|
|
4722
|
+
this.logger.warn("Failed to resolve parent owner for comment event", { parentId, err: err2 });
|
|
4680
4723
|
}
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4724
|
+
if (!parentOwnerId) {
|
|
4725
|
+
this.logger.warn("Skipping comment webhook \u2014 could not resolve parent_owner_id", { parentId, memoryId: request.payload.memory_id });
|
|
4726
|
+
} else {
|
|
4727
|
+
if (successfulPublications.some((p) => p.startsWith("spaces:"))) {
|
|
4728
|
+
for (const spaceId of spaces) {
|
|
4729
|
+
await this.eventBus.emit({ type: "comment.published_to_space", memory_id: request.payload.memory_id, parent_id: parentId, thread_root_id: threadRootId, content_preview: contentPreview, space_id: spaceId, owner_id: this.userId, parent_owner_id: parentOwnerId }, actor);
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
const publishedGroups = groups.filter((g) => successfulPublications.some((p) => p === `group: ${g}`));
|
|
4733
|
+
for (const groupId of publishedGroups) {
|
|
4734
|
+
await this.eventBus.emit({ type: "comment.published_to_group", memory_id: request.payload.memory_id, parent_id: parentId, thread_root_id: threadRootId, content_preview: contentPreview, group_id: groupId, owner_id: this.userId, parent_owner_id: parentOwnerId }, actor);
|
|
4735
|
+
}
|
|
4684
4736
|
}
|
|
4685
4737
|
} else {
|
|
4686
4738
|
if (successfulPublications.some((p) => p.startsWith("spaces:"))) {
|
|
4687
4739
|
for (const spaceId of spaces) {
|
|
4688
|
-
this.eventBus.emit({ type: "memory.published_to_space", memory_id: request.payload.memory_id, title, space_id: spaceId, owner_id: this.userId }, actor);
|
|
4740
|
+
await this.eventBus.emit({ type: "memory.published_to_space", memory_id: request.payload.memory_id, title, space_id: spaceId, owner_id: this.userId }, actor);
|
|
4689
4741
|
}
|
|
4690
4742
|
}
|
|
4691
4743
|
const publishedGroups = groups.filter((g) => successfulPublications.some((p) => p === `group: ${g}`));
|
|
4692
4744
|
for (const groupId of publishedGroups) {
|
|
4693
|
-
this.eventBus.emit({ type: "memory.published_to_group", memory_id: request.payload.memory_id, title, group_id: groupId, owner_id: this.userId }, actor);
|
|
4745
|
+
await this.eventBus.emit({ type: "memory.published_to_group", memory_id: request.payload.memory_id, title, group_id: groupId, owner_id: this.userId }, actor);
|
|
4694
4746
|
}
|
|
4695
4747
|
}
|
|
4696
4748
|
}
|
|
@@ -4787,7 +4839,7 @@ var SpaceService = class {
|
|
|
4787
4839
|
targets.push({ kind: "group", id: groupId });
|
|
4788
4840
|
}
|
|
4789
4841
|
if (targets.length > 0) {
|
|
4790
|
-
this.eventBus.emit({ type: "memory.retracted", memory_id: request.payload.memory_id, owner_id: this.userId, targets }, { type: "user", id: this.userId });
|
|
4842
|
+
await this.eventBus.emit({ type: "memory.retracted", memory_id: request.payload.memory_id, owner_id: this.userId, targets }, { type: "user", id: this.userId });
|
|
4791
4843
|
}
|
|
4792
4844
|
}
|
|
4793
4845
|
return {
|
|
@@ -5491,13 +5543,28 @@ var SpaceService = class {
|
|
|
5491
5543
|
} catch {
|
|
5492
5544
|
}
|
|
5493
5545
|
}
|
|
5494
|
-
if (
|
|
5495
|
-
return {
|
|
5546
|
+
if (props) {
|
|
5547
|
+
return {
|
|
5548
|
+
space_ids: Array.isArray(props.space_ids) ? props.space_ids : [],
|
|
5549
|
+
group_ids: Array.isArray(props.group_ids) ? props.group_ids : []
|
|
5550
|
+
};
|
|
5496
5551
|
}
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5552
|
+
try {
|
|
5553
|
+
const collectionName = await this.memoryIndex.lookup(memoryId);
|
|
5554
|
+
if (collectionName) {
|
|
5555
|
+
const collection = this.weaviateClient.collections.get(collectionName);
|
|
5556
|
+
const obj = await fetchMemoryWithAllProperties(collection, memoryId);
|
|
5557
|
+
if (obj) {
|
|
5558
|
+
const p = obj.properties;
|
|
5559
|
+
return {
|
|
5560
|
+
space_ids: Array.isArray(p.space_ids) ? p.space_ids : [],
|
|
5561
|
+
group_ids: Array.isArray(p.group_ids) ? p.group_ids : []
|
|
5562
|
+
};
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
} catch {
|
|
5566
|
+
}
|
|
5567
|
+
return { space_ids: [], group_ids: [] };
|
|
5501
5568
|
}
|
|
5502
5569
|
};
|
|
5503
5570
|
|
|
@@ -5723,7 +5790,8 @@ var INITIAL_PERCEPTION = {
|
|
|
5723
5790
|
import { createHmac } from "node:crypto";
|
|
5724
5791
|
function signWebhookPayload(webhookId, timestamp, body, secret) {
|
|
5725
5792
|
const content = `${webhookId}.${timestamp}.${body}`;
|
|
5726
|
-
const
|
|
5793
|
+
const secretBytes = Buffer.from(secret, "base64");
|
|
5794
|
+
const hmac = createHmac("sha256", secretBytes).update(content).digest("base64");
|
|
5727
5795
|
return `v1,${hmac}`;
|
|
5728
5796
|
}
|
|
5729
5797
|
|
|
@@ -5749,7 +5817,7 @@ var BatchedWebhookService = class {
|
|
|
5749
5817
|
this.timeoutMs = config3.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
5750
5818
|
this.onError = config3.onError;
|
|
5751
5819
|
}
|
|
5752
|
-
emit(event, actor) {
|
|
5820
|
+
async emit(event, actor) {
|
|
5753
5821
|
const ownerId = event.owner_id;
|
|
5754
5822
|
const endpoints = this.resolveEndpoint(ownerId);
|
|
5755
5823
|
if (endpoints.length === 0) {
|
|
@@ -5760,6 +5828,7 @@ var BatchedWebhookService = class {
|
|
|
5760
5828
|
return;
|
|
5761
5829
|
}
|
|
5762
5830
|
const envelope = this.buildEnvelope(event, actor);
|
|
5831
|
+
const flushPromises = [];
|
|
5763
5832
|
for (const endpoint of endpoints) {
|
|
5764
5833
|
const url = endpoint.url;
|
|
5765
5834
|
let buffer = this.buffers.get(url);
|
|
@@ -5769,13 +5838,16 @@ var BatchedWebhookService = class {
|
|
|
5769
5838
|
}
|
|
5770
5839
|
buffer.envelopes.push(envelope);
|
|
5771
5840
|
if (buffer.envelopes.length >= this.maxBatchSize) {
|
|
5772
|
-
this.flush(url);
|
|
5841
|
+
flushPromises.push(this.flush(url));
|
|
5773
5842
|
} else if (!buffer.timer) {
|
|
5774
5843
|
buffer.timer = setTimeout(() => this.flush(url), this.flushIntervalMs);
|
|
5775
5844
|
}
|
|
5776
5845
|
}
|
|
5846
|
+
if (flushPromises.length > 0) {
|
|
5847
|
+
await Promise.all(flushPromises);
|
|
5848
|
+
}
|
|
5777
5849
|
}
|
|
5778
|
-
flush(url) {
|
|
5850
|
+
async flush(url) {
|
|
5779
5851
|
const buffer = this.buffers.get(url);
|
|
5780
5852
|
if (!buffer || buffer.envelopes.length === 0)
|
|
5781
5853
|
return;
|
|
@@ -5786,19 +5858,23 @@ var BatchedWebhookService = class {
|
|
|
5786
5858
|
}
|
|
5787
5859
|
buffer.envelopes = [];
|
|
5788
5860
|
buffer.timer = null;
|
|
5789
|
-
|
|
5861
|
+
try {
|
|
5862
|
+
await this.sendBatch(url, endpoint, envelopes);
|
|
5863
|
+
} catch (err2) {
|
|
5790
5864
|
this.logger.error?.("[BatchedWebhookService] batch delivery failed", {
|
|
5791
5865
|
error: err2,
|
|
5792
5866
|
url,
|
|
5793
5867
|
count: envelopes.length
|
|
5794
5868
|
});
|
|
5795
5869
|
this.onError?.(err2, envelopes);
|
|
5796
|
-
}
|
|
5870
|
+
}
|
|
5797
5871
|
}
|
|
5798
|
-
dispose() {
|
|
5872
|
+
async dispose() {
|
|
5873
|
+
const promises = [];
|
|
5799
5874
|
for (const url of this.buffers.keys()) {
|
|
5800
|
-
this.flush(url);
|
|
5875
|
+
promises.push(this.flush(url));
|
|
5801
5876
|
}
|
|
5877
|
+
await Promise.all(promises);
|
|
5802
5878
|
}
|
|
5803
5879
|
async sendBatch(url, endpoint, envelopes) {
|
|
5804
5880
|
const batchId = v4_default();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prmichaelsen/remember-mcp",
|
|
3
|
-
"version": "3.17.
|
|
3
|
+
"version": "3.17.4",
|
|
4
4
|
"description": "Multi-tenant memory system MCP server with vector search and relationships",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"test:all": "npm test && npm run test:e2e",
|
|
36
36
|
"lint": "eslint src/**/*.ts",
|
|
37
37
|
"typecheck": "tsc --noEmit",
|
|
38
|
-
"prepublishOnly": "npm run clean && npm run build"
|
|
38
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
39
|
+
"update:core": "npm i @prmichaelsen/remember-core@latest && git add package* && git commit -m \"bump: remember-core\" && npm version patch && npm publish"
|
|
39
40
|
},
|
|
40
41
|
"keywords": [
|
|
41
42
|
"mcp",
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"@google-cloud/vision": "^5.3.4",
|
|
52
53
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
53
54
|
"@prmichaelsen/firebase-admin-sdk-v8": "^2.2.0",
|
|
54
|
-
"@prmichaelsen/remember-core": "^0.
|
|
55
|
+
"@prmichaelsen/remember-core": "^0.56.3",
|
|
55
56
|
"dotenv": "^16.4.5",
|
|
56
57
|
"uuid": "^13.0.0",
|
|
57
58
|
"weaviate-client": "^3.2.0"
|