Skip to content

fix(memory-core): register memory tools independently to prevent coupled failure#52639

Closed
frankekn wants to merge 4 commits intoopenclaw:mainfrom
frankekn:frank/pr-50198-replacement
Closed

fix(memory-core): register memory tools independently to prevent coupled failure#52639
frankekn wants to merge 4 commits intoopenclaw:mainfrom
frankekn:frank/pr-50198-replacement

Conversation

@frankekn
Copy link
Copy Markdown
Contributor

Summary

  • Problem: memory-core registered memory_search and memory_get in one factory, so if either tool was unavailable the factory returned null and both tools disappeared from new sessions.
  • Why it matters: a partial memory-tool failure should not silently remove the other working memory tool.
  • What changed: split the two tool registrations in extensions/memory-core/index.ts, keep the PR's debug log in src/plugins/tools.ts, add a regression test for independent registration, and add the required changelog line.
  • What did NOT change (scope boundary): no runtime semantics changed outside memory tool registration; the extra Telegram test-helper updates only keep current main green for check/test after rebasing the replacement branch.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

New sessions no longer lose both memory_search and memory_get when only one of the memory tool factories is unavailable.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local pnpm workspace
  • Model/provider: n/a
  • Integration/channel (if any): memory-core plugin registration path
  • Relevant config (redacted): default plugin runtime on current origin/main

Steps

  1. Review extensions/memory-core/index.ts on current main: one registerTool(...) factory creates both tools and returns null if either tool is unavailable.
  2. Run the new regression test that forces one tool factory to return null.
  3. Run full repository gates on the replacement branch rebased onto current origin/main.

Expected

  • memory_search stays registered when memory_get is unavailable.
  • Full repo gates pass on the replacement branch.

Actual

  • Replacement branch preserves partial registration and full repo gates pass.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Commands run:

  • pnpm build
  • pnpm check
  • pnpm test

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: independent memory_search/memory_get registration via the new regression in extensions/memory-core/index.test.ts, plus full build/check/test on the rebased replacement branch.
  • Edge cases checked: one tool factory returning null no longer suppresses the other tool; changelog entry is appended at the tail of ## Unreleased -> ### Changes.
  • What you did not verify: live external channel/session reproduction beyond code-path and test coverage.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert the two replacement-PR commits.
  • Files/config to restore: extensions/memory-core/index.ts, src/plugins/tools.ts, extensions/memory-core/index.test.ts, CHANGELOG.md.
  • Known bad symptoms reviewers should watch for: either memory tool disappearing from new sessions when only the other tool is unavailable.

Risks and Mitigations

  • Risk: the replacement branch carries minimal Telegram test-helper updates that are not part of the user-facing bug fix.
    • Mitigation: those edits are limited to test scaffolding, were only needed to keep current-main gates green after rebase, and do not alter runtime behavior.

artwalker and others added 2 commits March 23, 2026 12:57
…led failure

The memory-core plugin registered both memory_search and memory_get in a
single factory that returned null if either tool creation failed. This
caused both tools to silently disappear from new sessions when only one
tool's prerequisites were unavailable.

Split into two independent registerTool calls so each tool's availability
is evaluated separately. Also add debug logging when a plugin tool factory
returns null to aid future diagnosis.

Fixes openclaw#50173

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR fixes a coupling bug in extensions/memory-core/index.ts where memory_search and memory_get were registered through a single factory that returned null (suppressing both tools) whenever either tool was unavailable. The fix splits them into two independent registerTool calls, so a failure in one factory no longer silently removes the other from new sessions. Accompanying changes add a debug log in src/plugins/tools.ts for null-returning factories, a targeted regression test, a changelog entry, and minimal Telegram test-harness updates to keep existing gates green after rebasing onto main.

Key changes:

  • extensions/memory-core/index.ts: Two separate registerTool factories replace the single coupled one — the core fix.
  • src/plugins/tools.ts: Adds a log.debug line when a named tool factory returns null, improving observability.
  • extensions/memory-core/index.test.ts: New regression test validates independent registration (one factory returning null does not suppress the other). Minor: the new test is nested under the describe("buildPromptSection") block even though it tests plugin.register() — worth moving to its own describe group.
  • CHANGELOG.md: Entry is placed in ### Changes rather than ### Fixes; since this is a bug fix, the entry should be under ### Fixes to communicate accurate upgrade risk.
  • Telegram test helpers: Several files add loadWebMedia: vi.fn() to test harness objects — strictly test-scaffolding to accommodate a new field on TelegramBotDeps introduced on main.

Confidence Score: 5/5

  • Safe to merge; the fix is minimal and surgically targeted, with a regression test and no runtime semantic changes outside memory-tool registration.
  • The core change is a straightforward split of two coupled registrations into independent ones — low blast radius, well-tested, and backward compatible. The only open items are a P2 changelog section placement and a P2 test organization concern, neither of which affects correctness or runtime behaviour.
  • No files require special attention; the two P2 style issues (changelog section and test describe grouping) are non-blocking.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/memory-core/index.test.ts
Line: 33

Comment:
**Test nested under wrong describe block**

The new regression test exercises `plugin.register(api)`, not `buildPromptSection`. Nesting it inside the `describe("buildPromptSection", ...)` block makes it harder to find and slightly misleading. Consider moving it to its own top-level block, e.g. `describe("plugin.register", ...)`, to keep test intent clear.

```suggestion
describe("plugin registration", () => {
  it("registers memory tools independently so one unavailable tool does not suppress the other", () => {
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: CHANGELOG.md
Line: 83

Comment:
**Changelog entry in wrong section**

This is a bug fix (coupled failure causing silent tool loss), so it belongs under `### Fixes` rather than `### Changes`. Placing it in `### Changes` may mislead consumers who rely on changelog sections to assess upgrade risk.

```suggestion
- Memory/core tools: register `memory_search` and `memory_get` independently so one unavailable memory tool no longer suppresses the other in new sessions. (#50198) Thanks @artwalker.
```
(Move this line to the `### Fixes` section instead.)

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: cover independent memory-core regis..." | Re-trigger Greptile

@frankekn frankekn self-assigned this Mar 23, 2026
@frankekn
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98145de58c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 23, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🔵 Low Log forging / terminal control injection via unsanitized pluginId and tool names in debug log

1. 🔵 Log forging / terminal control injection via unsanitized pluginId and tool names in debug log

Property Value
Severity Low
CWE CWE-117
Location src/plugins/tools.ts:107-110

Description

The plugin tool resolution path logs plugin-controlled strings without output neutralization:

  • entry.pluginId originates from the plugin manifest (openclaw.plugin.json), which is only .trim()'d and can still contain control characters/newlines.
  • entry.names originates from plugin-controlled registration metadata (registerTool(..., { name/names })) and is only .trim()'d.
  • These values are interpolated into a console log line. In non-JSON console styles, embedded \n/\r (or ANSI escape sequences) can:
    • forge additional log lines (log injection / log forging)
    • manipulate terminal output (e.g., hide text, spoof severity/prefixes)
    • leak plugin/tool inventory into shared logs when debug logging is enabled

Vulnerable code:

log.debug(
  `plugin tool factory returned null (${entry.pluginId}): [${entry.names.join(", ")}]`,
);

Recommendation

Avoid interpolating untrusted/plugin-controlled identifiers directly into console log lines.

Option A (preferred): log structured fields and let the logger encode them (and keep the message constant):

log.debug("plugin tool factory returned null", {
  pluginId: sanitizeForLog(entry.pluginId),
  toolNames: entry.names.map(sanitizeForLog),
});

Option B: sanitize before interpolation by stripping control characters and ANSI escapes:

function sanitizeForLog(value: string): string {// remove CR/LF and other control chars; optionally strip ANSI escapes too
  return value
    .replace(/[\r\n\t\0\x08\f\v]/g, " ")
    .replace(/[\x00-\x1F\x7F]/g, "");
}

log.debug(
  `plugin tool factory returned null (${sanitizeForLog(entry.pluginId)}): ` +
    `[${entry.names.map(sanitizeForLog).join(", ")}]`,
);

Additionally consider applying stricter validation on manifest.id and registered tool names (e.g., disallow control characters) at load/registration time.


Analyzed PR: #52639 at commit b3bb2d2

Last updated on: 2026-03-23T05:58:39Z

@frankekn
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3bb2d2883

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


### Fixes

- Memory/core tools: register `memory_search` and `memory_get` independently so one unavailable memory tool no longer suppresses the other in new sessions. (#50198) Thanks @artwalker.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move changelog fix entry to end of section

This new bullet is inserted at the top of the active ### Fixes block, but AGENTS.md requires changelog entries to be appended at the end of the target section. Keeping it at the top breaks the repo’s documented release-note ordering and increases avoidable merge conflicts when multiple fixes land in parallel, so this line should be moved to the tail of the current ### Fixes list.

Useful? React with 👍 / 👎.

@frankekn
Copy link
Copy Markdown
Contributor Author

Superseded by #52668, rebuilt cleanly on the latest main to avoid rebasing the old PR into conflicts. Please review and merge #52668 instead.

@frankekn frankekn closed this Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram extensions: memory-core Extension: memory-core maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: memory_search tool not injected in new sessions despite plugin loaded

2 participants