fix(openclaw): saveMemories posts PersonalAddRequest envelope with top-level user_id (closes #237)#244
Open
Fearvox wants to merge 1 commit into
Open
Conversation
…top-level user_id (closes EverMind-AI#237) POST /api/v1/memories returned HTTP 422 ("Field required: user_id") because saveMemories sent flat, per-message bodies with no top-level user_id. The v1 PersonalAddRequest DTO is an envelope: a required top-level user_id plus a messages[] list of MessageItem, and each MessageItem requires an integer unix-milliseconds timestamp. Rewrite saveMemories to send a single batched envelope { user_id, session_id?, messages: [...] } and align each item with the MessageItem DTO: - timestamp: required unix-ms integer (the old create_time ISO string was ignored by the converter, leaving timestamp missing -> 422). - sender_id: set to user_id only for role=user; omitted for role=assistant so the backend derives a distinct id (personal-scene converter rejects assistant turns whose sender_id equals user_id). - session_id: plumbed through from the OpenClaw sessionKey (engine.js) for conversation isolation; optional in the DTO. Verified live against a running EverCore on :1995: the old flat body returns 422 "Field required: user_id"; the new envelope returns 200 {"status":"accumulated","message":"Messages accepted"}. Adds test/save-memories.test.js (node --test, offline) asserting the POST body carries a top-level user_id, a session_id, a messages array with integer timestamps and no legacy create_time, and the correct per-role sender_id semantics; plus that an empty batch sends nothing. Scope: api.js + the engine.js sessionId plumbing + its regression test. Does not bundle the separate api.pluginConfig forwarding fix (EverMind-AI#150/EverMind-AI#139), which ships as its own PR. Prior art adopted: PR EverMind-AI#189 (kevinwon) converted saveMemories to the same top-level-user_id batch envelope; PR EverMind-AI#128 (Void Freud) set user_id at the top level in the same saveMemories path. Closes EverMind-AI#237. Co-authored-by: kevinwon <2725361+ww-k@users.noreply.github.com> Co-authored-by: Void Freud <246163318+voidfreud@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
POST /api/v1/memoriesreturned HTTP 422 "Field required: user_id" becausesaveMemoriessent flat, per-message bodies with no top-leveluser_id. The v1PersonalAddRequestDTO is an envelope: a required top-leveluser_idplusa
messages[]list ofMessageItem, and eachMessageItemrequires an integerunix-milliseconds
timestamp. This is the exact defect in #237.This PR rewrites
saveMemoriesto send a single batched envelope and align eachitem with the DTO:
create_timeISO string wasignored by the converter, leaving
timestampmissing → 422.user_idonly forrole=user; omitted forrole=assistantso the backend derives a distinct id (the personal-sceneconverter rejects assistant turns whose
sender_idequalsuser_id).sessionKey(engine.js)for conversation isolation; optional in the DTO.
Why
Without a top-level
user_idthe request never passes validation, so the pluginsilently persisted nothing — every turn 422'd. Posting one batched envelope
also replaces the previous one-request-per-message loop.
Verification
Live against a running EverCore (
:1995):HTTP 422{"message":"Field required: user_id"}HTTP 200{"status":"accumulated","message":"Messages accepted"}The real
saveMemories()from this branch was also driven against the livebackend end-to-end and the POST was accepted (2xx).
Offline regression — adds
test/save-memories.test.js(node --test, nolive stack): asserts a single batched POST to
/api/v1/memorieswhose bodycarries a top-level
user_id, asession_id, amessagesarray with integertimestamps and no legacycreate_time, and the correct per-rolesender_idsemantics; plus that an empty batch sends nothing.
cd methods/EverCore/examples/openclaw-plugin node --test test/save-memories.test.jsResult: 2 passed.
Scope
Surgical:
src/api.js(the envelope rewrite) + thesrc/engine.jsone-linesessionIdplumbing + its regression test. Deliberately does not bundle theseparate
api.pluginConfigforwarding fix (#150), which ships as its own PR.Closes #237.
Credit
Prior art adopted:
saveMemoriesto the same top-level-user_idbatch envelope.
user_idat the top level in the samesaveMemoriespath.Co-authored-by: kevinwon 2725361+ww-k@users.noreply.github.com
Co-authored-by: Void Freud 246163318+voidfreud@users.noreply.github.com
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
🤖 Generated with Claude Code