@prmichaelsen/remember-mcp 2.6.13 → 2.7.1
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 +49 -0
- package/dist/server-factory.js +13 -282
- package/dist/server.js +13 -282
- package/package.json +1 -1
- package/src/tools/update-memory.ts +19 -13
- package/src/weaviate/space-schema.ts +10 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.7.1] - 2026-02-17
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **CRITICAL: Memory_public Auto-Schema**: Let Weaviate auto-generate schema instead of manual definition
|
|
13
|
+
- Removed manual schema creation for Memory_public collection
|
|
14
|
+
- Weaviate will auto-generate schema from first insert
|
|
15
|
+
- Ensures schema matches user memory collections EXACTLY
|
|
16
|
+
- Includes nested objects (context, location) that were auto-generated in user collections
|
|
17
|
+
- Fixes issue where nested objects were rejected due to schema expecting flattened properties
|
|
18
|
+
- Spread operator now works perfectly since schemas are identical
|
|
19
|
+
|
|
20
|
+
### Root Cause
|
|
21
|
+
|
|
22
|
+
- User memory collections have auto-generated schemas with nested `context` and `location` objects
|
|
23
|
+
- Memory_public had manually defined schema with flattened properties
|
|
24
|
+
- When spreading `...originalMemory.properties`, nested objects didn't match flattened schema
|
|
25
|
+
- Weaviate rejected ALL properties due to nested object schema conflict
|
|
26
|
+
- Auto-schema approach ensures perfect compatibility
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## [2.7.0] - 2026-02-17
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- **CRITICAL: `remember_update_memory` Now Uses `replace()` Instead of `update()`**
|
|
35
|
+
- Switched from `collection.data.update()` to `collection.data.replace()`
|
|
36
|
+
- Fixes Weaviate bug where `update()` only persists if vectorized fields change
|
|
37
|
+
- Now fetches full object and merges updates before replacing
|
|
38
|
+
- Updates to non-vectorized fields (title, type, weight, tags, etc.) now persist correctly
|
|
39
|
+
- Resolves issue where updates returned success but changes weren't saved
|
|
40
|
+
- **Breaking Change**: This is a minor version bump due to behavior change (more reliable updates)
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Fetch strategy: Now fetches all properties (not just 3) since we need full object for replace
|
|
45
|
+
- Update strategy: Merge updates with existing properties, then replace entire object
|
|
46
|
+
- Logging: Changed "Calling Weaviate update" to "Calling Weaviate replace"
|
|
47
|
+
|
|
48
|
+
### Root Cause
|
|
49
|
+
|
|
50
|
+
- Weaviate has a known bug where `update()` only persists changes if at least one vectorized field is modified
|
|
51
|
+
- Our schema only vectorizes `content` and `observation`
|
|
52
|
+
- Updates to `title`, `type`, `weight`, `tags`, etc. were silently ignored
|
|
53
|
+
- `replace()` doesn't have this limitation and always persists changes
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
8
57
|
## [2.6.13] - 2026-02-17
|
|
9
58
|
|
|
10
59
|
### Changed
|
package/dist/server-factory.js
CHANGED
|
@@ -1948,9 +1948,7 @@ async function handleUpdateMemory(args, userId) {
|
|
|
1948
1948
|
const collection = getMemoryCollection(userId);
|
|
1949
1949
|
let existingMemory;
|
|
1950
1950
|
try {
|
|
1951
|
-
existingMemory = await collection.query.fetchObjectById(args.memory_id
|
|
1952
|
-
returnProperties: ["user_id", "doc_type", "version"]
|
|
1953
|
-
});
|
|
1951
|
+
existingMemory = await collection.query.fetchObjectById(args.memory_id);
|
|
1954
1952
|
} catch (fetchError) {
|
|
1955
1953
|
const fetchErrorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
1956
1954
|
logger.error("Failed to fetch memory for update:", {
|
|
@@ -1961,7 +1959,7 @@ async function handleUpdateMemory(args, userId) {
|
|
|
1961
1959
|
});
|
|
1962
1960
|
throw new Error(`Failed to fetch memory ${args.memory_id}: ${fetchErrorMsg}`);
|
|
1963
1961
|
}
|
|
1964
|
-
if (!existingMemory) {
|
|
1962
|
+
if (!existingMemory || !existingMemory.properties) {
|
|
1965
1963
|
throw new Error(`Memory not found: ${args.memory_id}. It may have been deleted or never existed.`);
|
|
1966
1964
|
}
|
|
1967
1965
|
if (existingMemory.properties.user_id !== userId) {
|
|
@@ -2034,26 +2032,31 @@ async function handleUpdateMemory(args, userId) {
|
|
|
2034
2032
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2035
2033
|
updates.updated_at = now;
|
|
2036
2034
|
updates.version = existingMemory.properties.version + 1;
|
|
2037
|
-
|
|
2035
|
+
const mergedProperties = {
|
|
2036
|
+
...existingMemory.properties,
|
|
2037
|
+
...updates
|
|
2038
|
+
};
|
|
2039
|
+
logger.info("Calling Weaviate replace", {
|
|
2038
2040
|
userId,
|
|
2039
2041
|
memoryId: args.memory_id,
|
|
2040
2042
|
updateFields: Object.keys(updates),
|
|
2041
2043
|
updateValues: updates,
|
|
2042
|
-
collectionName: `Memory_${userId}
|
|
2044
|
+
collectionName: `Memory_${userId}`,
|
|
2045
|
+
totalProperties: Object.keys(mergedProperties).length
|
|
2043
2046
|
});
|
|
2044
2047
|
try {
|
|
2045
|
-
await collection.data.
|
|
2048
|
+
await collection.data.replace({
|
|
2046
2049
|
id: args.memory_id,
|
|
2047
|
-
properties:
|
|
2050
|
+
properties: mergedProperties
|
|
2048
2051
|
});
|
|
2049
|
-
logger.info("Weaviate
|
|
2052
|
+
logger.info("Weaviate replace completed (no error thrown)", {
|
|
2050
2053
|
userId,
|
|
2051
2054
|
memoryId: args.memory_id,
|
|
2052
2055
|
updatedFields: Object.keys(updates)
|
|
2053
2056
|
});
|
|
2054
2057
|
} catch (updateError) {
|
|
2055
2058
|
const updateErrorMsg = updateError instanceof Error ? updateError.message : String(updateError);
|
|
2056
|
-
logger.error("Failed to perform Weaviate
|
|
2059
|
+
logger.error("Failed to perform Weaviate replace:", {
|
|
2057
2060
|
error: updateErrorMsg,
|
|
2058
2061
|
userId,
|
|
2059
2062
|
memoryId: args.memory_id,
|
|
@@ -3684,283 +3687,11 @@ init_space_memory();
|
|
|
3684
3687
|
init_logger();
|
|
3685
3688
|
import weaviate3 from "weaviate-client";
|
|
3686
3689
|
var PUBLIC_COLLECTION_NAME = "Memory_public";
|
|
3687
|
-
function getSpaceCollectionName(spaceId) {
|
|
3688
|
-
return `Memory_${spaceId}`;
|
|
3689
|
-
}
|
|
3690
3690
|
function isValidSpaceId(spaceId) {
|
|
3691
3691
|
return SUPPORTED_SPACES.includes(spaceId);
|
|
3692
3692
|
}
|
|
3693
|
-
async function createSpaceCollection(client2, spaceId) {
|
|
3694
|
-
const collectionName = spaceId === "public" ? PUBLIC_COLLECTION_NAME : getSpaceCollectionName(spaceId);
|
|
3695
|
-
logger.info("Creating space collection", {
|
|
3696
|
-
module: "weaviate-space-schema",
|
|
3697
|
-
collectionName,
|
|
3698
|
-
spaceId
|
|
3699
|
-
});
|
|
3700
|
-
await client2.collections.create({
|
|
3701
|
-
name: collectionName,
|
|
3702
|
-
// Vectorizer configuration
|
|
3703
|
-
vectorizers: weaviate3.configure.vectorizer.text2VecOpenAI({
|
|
3704
|
-
model: "text-embedding-3-small",
|
|
3705
|
-
// Vectorize content for semantic search
|
|
3706
|
-
sourceProperties: ["content", "observation"]
|
|
3707
|
-
}),
|
|
3708
|
-
properties: [
|
|
3709
|
-
// Discriminator
|
|
3710
|
-
{
|
|
3711
|
-
name: "doc_type",
|
|
3712
|
-
dataType: "text",
|
|
3713
|
-
description: 'Document type: "space_memory"'
|
|
3714
|
-
},
|
|
3715
|
-
// Space identity
|
|
3716
|
-
{
|
|
3717
|
-
name: "spaces",
|
|
3718
|
-
dataType: "text[]",
|
|
3719
|
-
description: 'Spaces this memory is published to (e.g., ["the_void", "dogs"])'
|
|
3720
|
-
},
|
|
3721
|
-
{
|
|
3722
|
-
name: "space_id",
|
|
3723
|
-
dataType: "text",
|
|
3724
|
-
description: "DEPRECATED: Use spaces array instead. Will be removed in v3.0.0."
|
|
3725
|
-
},
|
|
3726
|
-
{
|
|
3727
|
-
name: "author_id",
|
|
3728
|
-
dataType: "text",
|
|
3729
|
-
description: "Original author user_id (for permissions)"
|
|
3730
|
-
},
|
|
3731
|
-
{
|
|
3732
|
-
name: "ghost_id",
|
|
3733
|
-
dataType: "text",
|
|
3734
|
-
description: "Optional ghost profile ID for pseudonymous publishing"
|
|
3735
|
-
},
|
|
3736
|
-
{
|
|
3737
|
-
name: "attribution",
|
|
3738
|
-
dataType: "text",
|
|
3739
|
-
description: 'Attribution type: "user" or "ghost"'
|
|
3740
|
-
},
|
|
3741
|
-
// Discovery metadata
|
|
3742
|
-
{
|
|
3743
|
-
name: "published_at",
|
|
3744
|
-
dataType: "text",
|
|
3745
|
-
description: "When published to space (ISO 8601)"
|
|
3746
|
-
},
|
|
3747
|
-
{
|
|
3748
|
-
name: "discovery_count",
|
|
3749
|
-
dataType: "number",
|
|
3750
|
-
description: "How many times discovered"
|
|
3751
|
-
},
|
|
3752
|
-
// Memory fields (same as personal memories)
|
|
3753
|
-
{
|
|
3754
|
-
name: "content",
|
|
3755
|
-
dataType: "text",
|
|
3756
|
-
description: "Main memory content (vectorized)"
|
|
3757
|
-
},
|
|
3758
|
-
{
|
|
3759
|
-
name: "title",
|
|
3760
|
-
dataType: "text",
|
|
3761
|
-
description: "Optional short title"
|
|
3762
|
-
},
|
|
3763
|
-
{
|
|
3764
|
-
name: "summary",
|
|
3765
|
-
dataType: "text",
|
|
3766
|
-
description: "Optional brief summary"
|
|
3767
|
-
},
|
|
3768
|
-
{
|
|
3769
|
-
name: "type",
|
|
3770
|
-
dataType: "text",
|
|
3771
|
-
description: "Content type (note, event, person, etc.)"
|
|
3772
|
-
},
|
|
3773
|
-
// Scoring fields
|
|
3774
|
-
{
|
|
3775
|
-
name: "weight",
|
|
3776
|
-
dataType: "number",
|
|
3777
|
-
description: "Significance/priority (0-1)"
|
|
3778
|
-
},
|
|
3779
|
-
{
|
|
3780
|
-
name: "trust",
|
|
3781
|
-
dataType: "number",
|
|
3782
|
-
description: "Access control level (0-1)"
|
|
3783
|
-
},
|
|
3784
|
-
{
|
|
3785
|
-
name: "confidence",
|
|
3786
|
-
dataType: "number",
|
|
3787
|
-
description: "System confidence in accuracy (0-1)"
|
|
3788
|
-
},
|
|
3789
|
-
// Location fields (flattened) - MUST match user memory schema exactly
|
|
3790
|
-
{
|
|
3791
|
-
name: "location_gps_lat",
|
|
3792
|
-
dataType: "number",
|
|
3793
|
-
description: "GPS latitude"
|
|
3794
|
-
},
|
|
3795
|
-
{
|
|
3796
|
-
name: "location_gps_lng",
|
|
3797
|
-
dataType: "number",
|
|
3798
|
-
description: "GPS longitude"
|
|
3799
|
-
},
|
|
3800
|
-
{
|
|
3801
|
-
name: "location_address",
|
|
3802
|
-
dataType: "text",
|
|
3803
|
-
description: "Formatted address"
|
|
3804
|
-
},
|
|
3805
|
-
{
|
|
3806
|
-
name: "location_city",
|
|
3807
|
-
dataType: "text",
|
|
3808
|
-
description: "City name"
|
|
3809
|
-
},
|
|
3810
|
-
{
|
|
3811
|
-
name: "location_country",
|
|
3812
|
-
dataType: "text",
|
|
3813
|
-
description: "Country name"
|
|
3814
|
-
},
|
|
3815
|
-
{
|
|
3816
|
-
name: "location_source",
|
|
3817
|
-
dataType: "text",
|
|
3818
|
-
description: "Location source (gps, ip, manual, etc.)"
|
|
3819
|
-
},
|
|
3820
|
-
// Context fields (flattened) - MUST match user memory schema exactly
|
|
3821
|
-
{
|
|
3822
|
-
name: "context_conversation_id",
|
|
3823
|
-
dataType: "text",
|
|
3824
|
-
description: "Conversation ID"
|
|
3825
|
-
},
|
|
3826
|
-
{
|
|
3827
|
-
name: "context_summary",
|
|
3828
|
-
dataType: "text",
|
|
3829
|
-
description: "Brief context summary"
|
|
3830
|
-
},
|
|
3831
|
-
{
|
|
3832
|
-
name: "context_timestamp",
|
|
3833
|
-
dataType: "date",
|
|
3834
|
-
description: "Context timestamp"
|
|
3835
|
-
},
|
|
3836
|
-
// Locale fields - MUST match user memory schema exactly
|
|
3837
|
-
{
|
|
3838
|
-
name: "locale_language",
|
|
3839
|
-
dataType: "text",
|
|
3840
|
-
description: "Language code (e.g., en, es, fr)"
|
|
3841
|
-
},
|
|
3842
|
-
{
|
|
3843
|
-
name: "locale_timezone",
|
|
3844
|
-
dataType: "text",
|
|
3845
|
-
description: "Timezone (e.g., America/Los_Angeles)"
|
|
3846
|
-
},
|
|
3847
|
-
// Relationships - MUST match user memory schema exactly
|
|
3848
|
-
{
|
|
3849
|
-
name: "relationships",
|
|
3850
|
-
dataType: "text[]",
|
|
3851
|
-
description: "Array of relationship IDs"
|
|
3852
|
-
},
|
|
3853
|
-
// Access tracking - MUST match user memory schema exactly
|
|
3854
|
-
{
|
|
3855
|
-
name: "access_count",
|
|
3856
|
-
dataType: "number",
|
|
3857
|
-
description: "Total times accessed"
|
|
3858
|
-
},
|
|
3859
|
-
{
|
|
3860
|
-
name: "last_accessed_at",
|
|
3861
|
-
dataType: "date",
|
|
3862
|
-
description: "Most recent access timestamp"
|
|
3863
|
-
},
|
|
3864
|
-
// Metadata - MUST match user memory schema exactly
|
|
3865
|
-
{
|
|
3866
|
-
name: "tags",
|
|
3867
|
-
dataType: "text[]",
|
|
3868
|
-
description: "Tags for organization"
|
|
3869
|
-
},
|
|
3870
|
-
{
|
|
3871
|
-
name: "references",
|
|
3872
|
-
dataType: "text[]",
|
|
3873
|
-
description: "Source URLs"
|
|
3874
|
-
},
|
|
3875
|
-
{
|
|
3876
|
-
name: "template_id",
|
|
3877
|
-
dataType: "text",
|
|
3878
|
-
description: "Template ID if using template"
|
|
3879
|
-
},
|
|
3880
|
-
// Relationship-specific fields (for relationships in public space)
|
|
3881
|
-
{
|
|
3882
|
-
name: "memory_ids",
|
|
3883
|
-
dataType: "text[]",
|
|
3884
|
-
description: "Connected memory IDs (for relationships)"
|
|
3885
|
-
},
|
|
3886
|
-
{
|
|
3887
|
-
name: "relationship_type",
|
|
3888
|
-
dataType: "text",
|
|
3889
|
-
description: "Relationship type (for relationships)"
|
|
3890
|
-
},
|
|
3891
|
-
{
|
|
3892
|
-
name: "observation",
|
|
3893
|
-
dataType: "text",
|
|
3894
|
-
description: "Relationship observation (vectorized)"
|
|
3895
|
-
},
|
|
3896
|
-
{
|
|
3897
|
-
name: "strength",
|
|
3898
|
-
dataType: "number",
|
|
3899
|
-
description: "Relationship strength (0-1)"
|
|
3900
|
-
},
|
|
3901
|
-
// Computed fields - MUST match user memory schema exactly
|
|
3902
|
-
{
|
|
3903
|
-
name: "base_weight",
|
|
3904
|
-
dataType: "number",
|
|
3905
|
-
description: "User-specified weight"
|
|
3906
|
-
},
|
|
3907
|
-
{
|
|
3908
|
-
name: "computed_weight",
|
|
3909
|
-
dataType: "number",
|
|
3910
|
-
description: "Calculated effective weight"
|
|
3911
|
-
},
|
|
3912
|
-
// User ID field (for backwards compatibility with spread operator)
|
|
3913
|
-
{
|
|
3914
|
-
name: "user_id",
|
|
3915
|
-
dataType: "text",
|
|
3916
|
-
description: "User ID (copied from original memory, same as author_id)"
|
|
3917
|
-
},
|
|
3918
|
-
// Timestamps
|
|
3919
|
-
{
|
|
3920
|
-
name: "created_at",
|
|
3921
|
-
dataType: "text",
|
|
3922
|
-
description: "Original creation timestamp (ISO 8601)"
|
|
3923
|
-
},
|
|
3924
|
-
{
|
|
3925
|
-
name: "updated_at",
|
|
3926
|
-
dataType: "text",
|
|
3927
|
-
description: "Last update timestamp (ISO 8601)"
|
|
3928
|
-
},
|
|
3929
|
-
// Versioning
|
|
3930
|
-
{
|
|
3931
|
-
name: "version",
|
|
3932
|
-
dataType: "number",
|
|
3933
|
-
description: "Version number (increments on update)"
|
|
3934
|
-
},
|
|
3935
|
-
// Comment/threading fields (for threaded discussions in shared spaces)
|
|
3936
|
-
{
|
|
3937
|
-
name: "parent_id",
|
|
3938
|
-
dataType: "text",
|
|
3939
|
-
description: "ID of parent memory or comment (for threading)"
|
|
3940
|
-
},
|
|
3941
|
-
{
|
|
3942
|
-
name: "thread_root_id",
|
|
3943
|
-
dataType: "text",
|
|
3944
|
-
description: "Root memory ID for fetching entire thread"
|
|
3945
|
-
},
|
|
3946
|
-
{
|
|
3947
|
-
name: "moderation_flags",
|
|
3948
|
-
dataType: "text[]",
|
|
3949
|
-
description: 'Per-space moderation flags (format: "{space_id}:{flag_type}")'
|
|
3950
|
-
}
|
|
3951
|
-
]
|
|
3952
|
-
});
|
|
3953
|
-
logger.info("Space collection created successfully", {
|
|
3954
|
-
module: "weaviate-space-schema",
|
|
3955
|
-
collectionName
|
|
3956
|
-
});
|
|
3957
|
-
}
|
|
3958
3693
|
async function ensurePublicCollection(client2) {
|
|
3959
3694
|
const collectionName = PUBLIC_COLLECTION_NAME;
|
|
3960
|
-
const exists = await client2.collections.exists(collectionName);
|
|
3961
|
-
if (!exists) {
|
|
3962
|
-
await createSpaceCollection(client2, "public");
|
|
3963
|
-
}
|
|
3964
3695
|
return client2.collections.get(collectionName);
|
|
3965
3696
|
}
|
|
3966
3697
|
|
package/dist/server.js
CHANGED
|
@@ -2016,9 +2016,7 @@ async function handleUpdateMemory(args, userId) {
|
|
|
2016
2016
|
const collection = getMemoryCollection(userId);
|
|
2017
2017
|
let existingMemory;
|
|
2018
2018
|
try {
|
|
2019
|
-
existingMemory = await collection.query.fetchObjectById(args.memory_id
|
|
2020
|
-
returnProperties: ["user_id", "doc_type", "version"]
|
|
2021
|
-
});
|
|
2019
|
+
existingMemory = await collection.query.fetchObjectById(args.memory_id);
|
|
2022
2020
|
} catch (fetchError) {
|
|
2023
2021
|
const fetchErrorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
2024
2022
|
logger.error("Failed to fetch memory for update:", {
|
|
@@ -2029,7 +2027,7 @@ async function handleUpdateMemory(args, userId) {
|
|
|
2029
2027
|
});
|
|
2030
2028
|
throw new Error(`Failed to fetch memory ${args.memory_id}: ${fetchErrorMsg}`);
|
|
2031
2029
|
}
|
|
2032
|
-
if (!existingMemory) {
|
|
2030
|
+
if (!existingMemory || !existingMemory.properties) {
|
|
2033
2031
|
throw new Error(`Memory not found: ${args.memory_id}. It may have been deleted or never existed.`);
|
|
2034
2032
|
}
|
|
2035
2033
|
if (existingMemory.properties.user_id !== userId) {
|
|
@@ -2102,26 +2100,31 @@ async function handleUpdateMemory(args, userId) {
|
|
|
2102
2100
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2103
2101
|
updates.updated_at = now;
|
|
2104
2102
|
updates.version = existingMemory.properties.version + 1;
|
|
2105
|
-
|
|
2103
|
+
const mergedProperties = {
|
|
2104
|
+
...existingMemory.properties,
|
|
2105
|
+
...updates
|
|
2106
|
+
};
|
|
2107
|
+
logger.info("Calling Weaviate replace", {
|
|
2106
2108
|
userId,
|
|
2107
2109
|
memoryId: args.memory_id,
|
|
2108
2110
|
updateFields: Object.keys(updates),
|
|
2109
2111
|
updateValues: updates,
|
|
2110
|
-
collectionName: `Memory_${userId}
|
|
2112
|
+
collectionName: `Memory_${userId}`,
|
|
2113
|
+
totalProperties: Object.keys(mergedProperties).length
|
|
2111
2114
|
});
|
|
2112
2115
|
try {
|
|
2113
|
-
await collection.data.
|
|
2116
|
+
await collection.data.replace({
|
|
2114
2117
|
id: args.memory_id,
|
|
2115
|
-
properties:
|
|
2118
|
+
properties: mergedProperties
|
|
2116
2119
|
});
|
|
2117
|
-
logger.info("Weaviate
|
|
2120
|
+
logger.info("Weaviate replace completed (no error thrown)", {
|
|
2118
2121
|
userId,
|
|
2119
2122
|
memoryId: args.memory_id,
|
|
2120
2123
|
updatedFields: Object.keys(updates)
|
|
2121
2124
|
});
|
|
2122
2125
|
} catch (updateError) {
|
|
2123
2126
|
const updateErrorMsg = updateError instanceof Error ? updateError.message : String(updateError);
|
|
2124
|
-
logger.error("Failed to perform Weaviate
|
|
2127
|
+
logger.error("Failed to perform Weaviate replace:", {
|
|
2125
2128
|
error: updateErrorMsg,
|
|
2126
2129
|
userId,
|
|
2127
2130
|
memoryId: args.memory_id,
|
|
@@ -3752,283 +3755,11 @@ init_space_memory();
|
|
|
3752
3755
|
init_logger();
|
|
3753
3756
|
import weaviate3 from "weaviate-client";
|
|
3754
3757
|
var PUBLIC_COLLECTION_NAME = "Memory_public";
|
|
3755
|
-
function getSpaceCollectionName(spaceId) {
|
|
3756
|
-
return `Memory_${spaceId}`;
|
|
3757
|
-
}
|
|
3758
3758
|
function isValidSpaceId(spaceId) {
|
|
3759
3759
|
return SUPPORTED_SPACES.includes(spaceId);
|
|
3760
3760
|
}
|
|
3761
|
-
async function createSpaceCollection(client2, spaceId) {
|
|
3762
|
-
const collectionName = spaceId === "public" ? PUBLIC_COLLECTION_NAME : getSpaceCollectionName(spaceId);
|
|
3763
|
-
logger.info("Creating space collection", {
|
|
3764
|
-
module: "weaviate-space-schema",
|
|
3765
|
-
collectionName,
|
|
3766
|
-
spaceId
|
|
3767
|
-
});
|
|
3768
|
-
await client2.collections.create({
|
|
3769
|
-
name: collectionName,
|
|
3770
|
-
// Vectorizer configuration
|
|
3771
|
-
vectorizers: weaviate3.configure.vectorizer.text2VecOpenAI({
|
|
3772
|
-
model: "text-embedding-3-small",
|
|
3773
|
-
// Vectorize content for semantic search
|
|
3774
|
-
sourceProperties: ["content", "observation"]
|
|
3775
|
-
}),
|
|
3776
|
-
properties: [
|
|
3777
|
-
// Discriminator
|
|
3778
|
-
{
|
|
3779
|
-
name: "doc_type",
|
|
3780
|
-
dataType: "text",
|
|
3781
|
-
description: 'Document type: "space_memory"'
|
|
3782
|
-
},
|
|
3783
|
-
// Space identity
|
|
3784
|
-
{
|
|
3785
|
-
name: "spaces",
|
|
3786
|
-
dataType: "text[]",
|
|
3787
|
-
description: 'Spaces this memory is published to (e.g., ["the_void", "dogs"])'
|
|
3788
|
-
},
|
|
3789
|
-
{
|
|
3790
|
-
name: "space_id",
|
|
3791
|
-
dataType: "text",
|
|
3792
|
-
description: "DEPRECATED: Use spaces array instead. Will be removed in v3.0.0."
|
|
3793
|
-
},
|
|
3794
|
-
{
|
|
3795
|
-
name: "author_id",
|
|
3796
|
-
dataType: "text",
|
|
3797
|
-
description: "Original author user_id (for permissions)"
|
|
3798
|
-
},
|
|
3799
|
-
{
|
|
3800
|
-
name: "ghost_id",
|
|
3801
|
-
dataType: "text",
|
|
3802
|
-
description: "Optional ghost profile ID for pseudonymous publishing"
|
|
3803
|
-
},
|
|
3804
|
-
{
|
|
3805
|
-
name: "attribution",
|
|
3806
|
-
dataType: "text",
|
|
3807
|
-
description: 'Attribution type: "user" or "ghost"'
|
|
3808
|
-
},
|
|
3809
|
-
// Discovery metadata
|
|
3810
|
-
{
|
|
3811
|
-
name: "published_at",
|
|
3812
|
-
dataType: "text",
|
|
3813
|
-
description: "When published to space (ISO 8601)"
|
|
3814
|
-
},
|
|
3815
|
-
{
|
|
3816
|
-
name: "discovery_count",
|
|
3817
|
-
dataType: "number",
|
|
3818
|
-
description: "How many times discovered"
|
|
3819
|
-
},
|
|
3820
|
-
// Memory fields (same as personal memories)
|
|
3821
|
-
{
|
|
3822
|
-
name: "content",
|
|
3823
|
-
dataType: "text",
|
|
3824
|
-
description: "Main memory content (vectorized)"
|
|
3825
|
-
},
|
|
3826
|
-
{
|
|
3827
|
-
name: "title",
|
|
3828
|
-
dataType: "text",
|
|
3829
|
-
description: "Optional short title"
|
|
3830
|
-
},
|
|
3831
|
-
{
|
|
3832
|
-
name: "summary",
|
|
3833
|
-
dataType: "text",
|
|
3834
|
-
description: "Optional brief summary"
|
|
3835
|
-
},
|
|
3836
|
-
{
|
|
3837
|
-
name: "type",
|
|
3838
|
-
dataType: "text",
|
|
3839
|
-
description: "Content type (note, event, person, etc.)"
|
|
3840
|
-
},
|
|
3841
|
-
// Scoring fields
|
|
3842
|
-
{
|
|
3843
|
-
name: "weight",
|
|
3844
|
-
dataType: "number",
|
|
3845
|
-
description: "Significance/priority (0-1)"
|
|
3846
|
-
},
|
|
3847
|
-
{
|
|
3848
|
-
name: "trust",
|
|
3849
|
-
dataType: "number",
|
|
3850
|
-
description: "Access control level (0-1)"
|
|
3851
|
-
},
|
|
3852
|
-
{
|
|
3853
|
-
name: "confidence",
|
|
3854
|
-
dataType: "number",
|
|
3855
|
-
description: "System confidence in accuracy (0-1)"
|
|
3856
|
-
},
|
|
3857
|
-
// Location fields (flattened) - MUST match user memory schema exactly
|
|
3858
|
-
{
|
|
3859
|
-
name: "location_gps_lat",
|
|
3860
|
-
dataType: "number",
|
|
3861
|
-
description: "GPS latitude"
|
|
3862
|
-
},
|
|
3863
|
-
{
|
|
3864
|
-
name: "location_gps_lng",
|
|
3865
|
-
dataType: "number",
|
|
3866
|
-
description: "GPS longitude"
|
|
3867
|
-
},
|
|
3868
|
-
{
|
|
3869
|
-
name: "location_address",
|
|
3870
|
-
dataType: "text",
|
|
3871
|
-
description: "Formatted address"
|
|
3872
|
-
},
|
|
3873
|
-
{
|
|
3874
|
-
name: "location_city",
|
|
3875
|
-
dataType: "text",
|
|
3876
|
-
description: "City name"
|
|
3877
|
-
},
|
|
3878
|
-
{
|
|
3879
|
-
name: "location_country",
|
|
3880
|
-
dataType: "text",
|
|
3881
|
-
description: "Country name"
|
|
3882
|
-
},
|
|
3883
|
-
{
|
|
3884
|
-
name: "location_source",
|
|
3885
|
-
dataType: "text",
|
|
3886
|
-
description: "Location source (gps, ip, manual, etc.)"
|
|
3887
|
-
},
|
|
3888
|
-
// Context fields (flattened) - MUST match user memory schema exactly
|
|
3889
|
-
{
|
|
3890
|
-
name: "context_conversation_id",
|
|
3891
|
-
dataType: "text",
|
|
3892
|
-
description: "Conversation ID"
|
|
3893
|
-
},
|
|
3894
|
-
{
|
|
3895
|
-
name: "context_summary",
|
|
3896
|
-
dataType: "text",
|
|
3897
|
-
description: "Brief context summary"
|
|
3898
|
-
},
|
|
3899
|
-
{
|
|
3900
|
-
name: "context_timestamp",
|
|
3901
|
-
dataType: "date",
|
|
3902
|
-
description: "Context timestamp"
|
|
3903
|
-
},
|
|
3904
|
-
// Locale fields - MUST match user memory schema exactly
|
|
3905
|
-
{
|
|
3906
|
-
name: "locale_language",
|
|
3907
|
-
dataType: "text",
|
|
3908
|
-
description: "Language code (e.g., en, es, fr)"
|
|
3909
|
-
},
|
|
3910
|
-
{
|
|
3911
|
-
name: "locale_timezone",
|
|
3912
|
-
dataType: "text",
|
|
3913
|
-
description: "Timezone (e.g., America/Los_Angeles)"
|
|
3914
|
-
},
|
|
3915
|
-
// Relationships - MUST match user memory schema exactly
|
|
3916
|
-
{
|
|
3917
|
-
name: "relationships",
|
|
3918
|
-
dataType: "text[]",
|
|
3919
|
-
description: "Array of relationship IDs"
|
|
3920
|
-
},
|
|
3921
|
-
// Access tracking - MUST match user memory schema exactly
|
|
3922
|
-
{
|
|
3923
|
-
name: "access_count",
|
|
3924
|
-
dataType: "number",
|
|
3925
|
-
description: "Total times accessed"
|
|
3926
|
-
},
|
|
3927
|
-
{
|
|
3928
|
-
name: "last_accessed_at",
|
|
3929
|
-
dataType: "date",
|
|
3930
|
-
description: "Most recent access timestamp"
|
|
3931
|
-
},
|
|
3932
|
-
// Metadata - MUST match user memory schema exactly
|
|
3933
|
-
{
|
|
3934
|
-
name: "tags",
|
|
3935
|
-
dataType: "text[]",
|
|
3936
|
-
description: "Tags for organization"
|
|
3937
|
-
},
|
|
3938
|
-
{
|
|
3939
|
-
name: "references",
|
|
3940
|
-
dataType: "text[]",
|
|
3941
|
-
description: "Source URLs"
|
|
3942
|
-
},
|
|
3943
|
-
{
|
|
3944
|
-
name: "template_id",
|
|
3945
|
-
dataType: "text",
|
|
3946
|
-
description: "Template ID if using template"
|
|
3947
|
-
},
|
|
3948
|
-
// Relationship-specific fields (for relationships in public space)
|
|
3949
|
-
{
|
|
3950
|
-
name: "memory_ids",
|
|
3951
|
-
dataType: "text[]",
|
|
3952
|
-
description: "Connected memory IDs (for relationships)"
|
|
3953
|
-
},
|
|
3954
|
-
{
|
|
3955
|
-
name: "relationship_type",
|
|
3956
|
-
dataType: "text",
|
|
3957
|
-
description: "Relationship type (for relationships)"
|
|
3958
|
-
},
|
|
3959
|
-
{
|
|
3960
|
-
name: "observation",
|
|
3961
|
-
dataType: "text",
|
|
3962
|
-
description: "Relationship observation (vectorized)"
|
|
3963
|
-
},
|
|
3964
|
-
{
|
|
3965
|
-
name: "strength",
|
|
3966
|
-
dataType: "number",
|
|
3967
|
-
description: "Relationship strength (0-1)"
|
|
3968
|
-
},
|
|
3969
|
-
// Computed fields - MUST match user memory schema exactly
|
|
3970
|
-
{
|
|
3971
|
-
name: "base_weight",
|
|
3972
|
-
dataType: "number",
|
|
3973
|
-
description: "User-specified weight"
|
|
3974
|
-
},
|
|
3975
|
-
{
|
|
3976
|
-
name: "computed_weight",
|
|
3977
|
-
dataType: "number",
|
|
3978
|
-
description: "Calculated effective weight"
|
|
3979
|
-
},
|
|
3980
|
-
// User ID field (for backwards compatibility with spread operator)
|
|
3981
|
-
{
|
|
3982
|
-
name: "user_id",
|
|
3983
|
-
dataType: "text",
|
|
3984
|
-
description: "User ID (copied from original memory, same as author_id)"
|
|
3985
|
-
},
|
|
3986
|
-
// Timestamps
|
|
3987
|
-
{
|
|
3988
|
-
name: "created_at",
|
|
3989
|
-
dataType: "text",
|
|
3990
|
-
description: "Original creation timestamp (ISO 8601)"
|
|
3991
|
-
},
|
|
3992
|
-
{
|
|
3993
|
-
name: "updated_at",
|
|
3994
|
-
dataType: "text",
|
|
3995
|
-
description: "Last update timestamp (ISO 8601)"
|
|
3996
|
-
},
|
|
3997
|
-
// Versioning
|
|
3998
|
-
{
|
|
3999
|
-
name: "version",
|
|
4000
|
-
dataType: "number",
|
|
4001
|
-
description: "Version number (increments on update)"
|
|
4002
|
-
},
|
|
4003
|
-
// Comment/threading fields (for threaded discussions in shared spaces)
|
|
4004
|
-
{
|
|
4005
|
-
name: "parent_id",
|
|
4006
|
-
dataType: "text",
|
|
4007
|
-
description: "ID of parent memory or comment (for threading)"
|
|
4008
|
-
},
|
|
4009
|
-
{
|
|
4010
|
-
name: "thread_root_id",
|
|
4011
|
-
dataType: "text",
|
|
4012
|
-
description: "Root memory ID for fetching entire thread"
|
|
4013
|
-
},
|
|
4014
|
-
{
|
|
4015
|
-
name: "moderation_flags",
|
|
4016
|
-
dataType: "text[]",
|
|
4017
|
-
description: 'Per-space moderation flags (format: "{space_id}:{flag_type}")'
|
|
4018
|
-
}
|
|
4019
|
-
]
|
|
4020
|
-
});
|
|
4021
|
-
logger.info("Space collection created successfully", {
|
|
4022
|
-
module: "weaviate-space-schema",
|
|
4023
|
-
collectionName
|
|
4024
|
-
});
|
|
4025
|
-
}
|
|
4026
3761
|
async function ensurePublicCollection(client2) {
|
|
4027
3762
|
const collectionName = PUBLIC_COLLECTION_NAME;
|
|
4028
|
-
const exists = await client2.collections.exists(collectionName);
|
|
4029
|
-
if (!exists) {
|
|
4030
|
-
await createSpaceCollection(client2, "public");
|
|
4031
|
-
}
|
|
4032
3763
|
return client2.collections.get(collectionName);
|
|
4033
3764
|
}
|
|
4034
3765
|
|
package/package.json
CHANGED
|
@@ -130,14 +130,12 @@ export async function handleUpdateMemory(
|
|
|
130
130
|
|
|
131
131
|
const collection = getMemoryCollection(userId);
|
|
132
132
|
|
|
133
|
-
// Get existing memory
|
|
134
|
-
//
|
|
135
|
-
//
|
|
133
|
+
// Get existing memory - fetch ALL properties for replace operation
|
|
134
|
+
// We need the full object to use replace() instead of update()
|
|
135
|
+
// (Weaviate bug: update() only persists if vectorized fields change)
|
|
136
136
|
let existingMemory;
|
|
137
137
|
try {
|
|
138
|
-
existingMemory = await collection.query.fetchObjectById(args.memory_id
|
|
139
|
-
returnProperties: ['user_id', 'doc_type', 'version'],
|
|
140
|
-
});
|
|
138
|
+
existingMemory = await collection.query.fetchObjectById(args.memory_id);
|
|
141
139
|
} catch (fetchError) {
|
|
142
140
|
const fetchErrorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
143
141
|
logger.error('Failed to fetch memory for update:', {
|
|
@@ -149,7 +147,7 @@ export async function handleUpdateMemory(
|
|
|
149
147
|
throw new Error(`Failed to fetch memory ${args.memory_id}: ${fetchErrorMsg}`);
|
|
150
148
|
}
|
|
151
149
|
|
|
152
|
-
if (!existingMemory) {
|
|
150
|
+
if (!existingMemory || !existingMemory.properties) {
|
|
153
151
|
throw new Error(`Memory not found: ${args.memory_id}. It may have been deleted or never existed.`);
|
|
154
152
|
}
|
|
155
153
|
|
|
@@ -249,29 +247,37 @@ export async function handleUpdateMemory(
|
|
|
249
247
|
updates.updated_at = now;
|
|
250
248
|
updates.version = (existingMemory.properties.version as number) + 1;
|
|
251
249
|
|
|
252
|
-
//
|
|
253
|
-
|
|
250
|
+
// Merge updates with existing properties
|
|
251
|
+
// Use replace() instead of update() due to Weaviate bug where update() only
|
|
252
|
+
// persists if vectorized fields (content/observation) are changed
|
|
253
|
+
const mergedProperties = {
|
|
254
|
+
...existingMemory.properties,
|
|
255
|
+
...updates,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
logger.info('Calling Weaviate replace', {
|
|
254
259
|
userId,
|
|
255
260
|
memoryId: args.memory_id,
|
|
256
261
|
updateFields: Object.keys(updates),
|
|
257
262
|
updateValues: updates,
|
|
258
263
|
collectionName: `Memory_${userId}`,
|
|
264
|
+
totalProperties: Object.keys(mergedProperties).length,
|
|
259
265
|
});
|
|
260
266
|
|
|
261
267
|
try {
|
|
262
|
-
await collection.data.
|
|
268
|
+
await collection.data.replace({
|
|
263
269
|
id: args.memory_id,
|
|
264
|
-
properties:
|
|
270
|
+
properties: mergedProperties,
|
|
265
271
|
});
|
|
266
272
|
|
|
267
|
-
logger.info('Weaviate
|
|
273
|
+
logger.info('Weaviate replace completed (no error thrown)', {
|
|
268
274
|
userId,
|
|
269
275
|
memoryId: args.memory_id,
|
|
270
276
|
updatedFields: Object.keys(updates),
|
|
271
277
|
});
|
|
272
278
|
} catch (updateError) {
|
|
273
279
|
const updateErrorMsg = updateError instanceof Error ? updateError.message : String(updateError);
|
|
274
|
-
logger.error('Failed to perform Weaviate
|
|
280
|
+
logger.error('Failed to perform Weaviate replace:', {
|
|
275
281
|
error: updateErrorMsg,
|
|
276
282
|
userId,
|
|
277
283
|
memoryId: args.memory_id,
|
|
@@ -383,12 +383,16 @@ export async function ensurePublicCollection(
|
|
|
383
383
|
): Promise<Collection<any>> {
|
|
384
384
|
const collectionName = PUBLIC_COLLECTION_NAME;
|
|
385
385
|
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
386
|
+
// DON'T manually create the collection!
|
|
387
|
+
// Let Weaviate auto-generate the schema from the first insert.
|
|
388
|
+
// This ensures the schema matches user memory collections exactly,
|
|
389
|
+
// including nested objects (context, location) that were auto-generated.
|
|
390
|
+
//
|
|
391
|
+
// When we spread {...originalMemory.properties}, the nested objects
|
|
392
|
+
// will be preserved and Weaviate will auto-create matching schema.
|
|
393
|
+
//
|
|
394
|
+
// NOTE: Collection will be created automatically on first insert.
|
|
395
|
+
// Weaviate's auto-schema feature handles this transparently.
|
|
392
396
|
|
|
393
397
|
return client.collections.get(collectionName);
|
|
394
398
|
}
|