Skip to content

fix(openclaw-plugin): store memories via content/write API instead of session messages#2169

Open
chdlc wants to merge 1 commit into
volcengine:mainfrom
chdlc:fix/openclaw-plugin-store-direct-write
Open

fix(openclaw-plugin): store memories via content/write API instead of session messages#2169
chdlc wants to merge 1 commit into
volcengine:mainfrom
chdlc:fix/openclaw-plugin-store-direct-write

Conversation

@chdlc
Copy link
Copy Markdown

@chdlc chdlc commented May 21, 2026

Description

The memory_store command (_tool_remember) was posting memory text as session messages that depend on commit-time VLM extraction to persist. When extraction_enabled: false (no VLM configured), the extraction pipeline never processes these messages, and memories are silently lost.

Replace with a direct POST to /api/v1/content/write?mode=create, which creates the file, stores the content, and queues vector indexing in a single API call — no VLM extraction dependency, immediate write confirmation, and immediate error reporting.

Related Issue

Addresses the same pattern identified in hermes-agent PR #29733 — session-commit-based memory storage silently fails when extraction is disabled.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • examples/openclaw-plugin/client.ts: Added contentWrite(uri, content, mode, agentId) method — POST to /api/v1/content/write
  • examples/openclaw-plugin/index.ts: Rewrote memory_store tool:
    • Removed session-based flow (addSessionMessage + commitSession)
    • Now calls contentWrite directly with URI viking://user/{userId}/memories/{subdir}/mem_{uuid}.md
    • Accepts category parameter (preference, entity, event, case, pattern) mapped to subdirectories
    • Responds immediately with byte count and indexing confirmation
    • Errors are reported inline instead of silently dropping memories
  • examples/openclaw-plugin/tests/ut/tools.test.ts: Updated memory_store tests:
    • Verify content/write call with correct URI shape
    • Verify category→subdirectory mapping (e.g., eventevents/)
    • Verify error handling for empty text input

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have tested this on the following platforms:
    • Linux

Test results: 24/24 test files, 468 tests passed.
TypeScript compiles cleanly with tsc --noEmit.

Checklist

  • My code follows the project's coding style
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • All tests pass

Additional Notes

The same content/write endpoint is already used by other parts of the OpenViking ecosystem for durable writes. This change makes memory_store consistent with that pattern, removing the runtime dependency on VLM extraction for memory persistence. When VLM extraction is enabled, the pipeline still processes the new file asynchronously as normal.

… session messages

_tool_remember (memory_store) was posting memories as session messages
that depend on commit-time VLM extraction to persist. With
extraction_enabled: false (no VLM configured), the extraction pipeline
never processes these messages — memories are silently lost.

Replace with direct POST to /api/v1/content/write?mode=create,
which creates the file, stores the content, and queues vector indexing
in a single API call. Error reporting is immediate — no silent failures.

- Maps memory_store category to viking:// subdirectory
- Generates UUID-based URIs via randomUUID().replace(/-/g, '').slice(0, 12)
- Returns byte count in confirmation message
- Updates tests to verify content/write call, category mapping, and error handling
@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🏅 Score: 90
🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

Missing Content-Type Header

contentWrite sends JSON body without setting 'Content-Type: application/json' header. This may cause the server to reject or misparse the request.

return this.request(
  "/api/v1/content/write",
  {
    method: "POST",
    body: JSON.stringify({ uri, content, mode }),
  },
  agentId,
);
Non-Standard Memory Category

Added 'case' category which is not in the standard 6 memory categories (PREFERENCES, ENTITIES, PATTERNS, EVENTS, TOOLS, SKILLS). Verify if this is intentional and supported by the backend.

        Type.Literal("case"),
        Type.Literal("pattern"),
      ],
      { description: "Memory category, maps to subdirectory (default: preference)" },
    ),
  ),
}),
async execute(_toolCallId: string, params: Record<string, unknown>) {
  if (isBypassedSession(ctx)) {
    return makeBypassedToolResult("memory_store");
  }
  const session = resolvePluginSessionRouting(ctx);
  const { text } = params as { text: string };
  const category =
    typeof (params as { category?: string }).category === "string"
      ? (params as { category: string }).category
      : "preference";

  if (!text?.trim()) {
    return {
      content: [{ type: "text", text: "text is required" }],
      details: { action: "failed", error: "text is required" },
    };
  }

  const subdirMap: Record<string, string> = {
    preference: "preferences",
    entity: "entities",
    event: "events",
    case: "cases",
    pattern: "patterns",
  };
  const subdir = subdirMap[category] ?? "preferences";

@github-actions
Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

@wlff123
Copy link
Copy Markdown
Contributor

wlff123 commented May 23, 2026

Thanks for the fix. I agree it addresses the immediate failure case, but I don’t think we should merge it as-is.

Directly writing raw text via content/write bypasses the memory v2 schema pipeline, including structured fields, expected paths, merge behavior, and MEMORY_FIELDS metadata. This may make manually stored memories inconsistent with commit-generated memories.

For now, I suggest returning a clear tool error when the LLM/extraction pipeline is unavailable, instead of bypassing the normal memory pipeline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

2 participants