fix(context,compress-synthetic): conversation observations render with full content#974
fix(context,compress-synthetic): conversation observations render with full content#974projectedanx wants to merge 4 commits into
Conversation
Bumps the minor-and-patch group with 3 updates: [@anthropic-ai/sdk](https://github.com/anthropics/anthropic-sdk-typescript), iii-sdk and [tsdown](https://github.com/rolldown/tsdown). Updates `@anthropic-ai/sdk` from 0.100.1 to 0.102.0 - [Release notes](https://github.com/anthropics/anthropic-sdk-typescript/releases) - [Changelog](https://github.com/anthropics/anthropic-sdk-typescript/blob/main/CHANGELOG.md) - [Commits](anthropics/anthropic-sdk-typescript@sdk-v0.100.1...sdk-v0.102.0) Updates `iii-sdk` from 0.11.2 to 0.19.0 Updates `tsdown` from 0.21.10 to 0.22.2 - [Release notes](https://github.com/rolldown/tsdown/releases) - [Commits](rolldown/tsdown@v0.21.10...v0.22.2) --- updated-dependencies: - dependency-name: "@anthropic-ai/sdk" dependency-version: 0.102.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: iii-sdk dependency-version: 0.19.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: tsdown dependency-version: 0.22.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…-and-patch-ede7075900 deps(deps): bump the minor-and-patch group with 3 updates
…h full content
The Hermes plugin (and any integration that uses tool_name: "conversation"
as a generic catch-all for user/assistant turns) stored observations via
the synthetic-compression path with type="other" and title="conversation".
The memory-context renderer then produced lines like:
- [other] conversation: <narrative>
which were easy to misread as 'type=conversation, empty content' and broke
the user's mental model of what was being injected into the system prompt.
This change has three parts:
1. inferType() now recognizes conversation-shaped tool names
("conversation", "chat", "prompt", "message", "turn") and returns
the correct type. New observations are stored with type="conversation"
and a meaningful title derived from the user prompt or tool input,
instead of the literal tool name.
2. The context renderer now has a defensive fallback: when the stored
title is empty, equals the type, or is one of a small set of generic
tool names, it uses the subtitle or the first 80 chars of the
narrative as the title. This handles observations that were stored
before the inferType fix landed.
3. The filter is tightened to drop observations with empty narrative,
so they never reach the renderer and produce dangling-colon lines
like '- [other] conversation: '.
The renderer also strips a title prefix from the narrative when they
overlap, to avoid the user message appearing twice on the same line.
Fixes the issue where the 100+ Hermes-plugin conversation observations
were rendering as empty bullets in the system prompt's memory-context
block across all six of the user's CLIs.
…s-in-memory-context fix(context,compress-synthetic): conversation observations render with full content
|
@projectedanx is attempting to deploy a commit to the rohitg00's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughUpdated package versions and adjusted observation text handling. Synthetic compression now classifies conversation-like tool names and chooses titles from prompt or input text. Context rendering now skips empty narratives and applies fallback title and narrative formatting. ChangesObservation text updates
Dependency refresh
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/functions/context.ts (1)
177-179: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace explanatory comment blocks with clearer helper naming.
This TS block adds “what/why” comments; repo guideline asks to avoid that style in source.
As per coding guidelines, "In TypeScript source code, avoid code comments explaining WHAT — use clear naming instead".
Also applies to: 191-198, 221-222
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/functions/context.ts` around lines 177 - 179, Remove the explanatory WHAT/WHY comment blocks in context.ts and instead make the intent obvious through naming and structure in the relevant helpers around the narrative filtering and rendering path; update the affected logic in the areas of the observation filter and renderer preparation (including the related blocks near the referenced helper code) so the code is self-explanatory without inline comments.Source: Coding guidelines
src/functions/compress-synthetic.ts (1)
23-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove “what/why” inline comment blocks and encode intent in names/helpers.
The new explanatory blocks are verbose and conflict with the TS guideline for this repo. Prefer extracting small helpers with descriptive names instead of narrative comments.
As per coding guidelines, "In TypeScript source code, avoid code comments explaining WHAT — use clear naming instead".
Also applies to: 108-112
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/functions/compress-synthetic.ts` around lines 23 - 34, The inline narrative comments in compress-synthetic.ts should be removed and the intent expressed through clearer naming or a small helper instead. Refactor the normalization and conversation-name handling in the synthetic compressor logic into descriptive helpers (for example around the toolName normalization and the conversation/tool-name classification branch) so the code reads clearly without WHAT/WHY comment blocks, and apply the same cleanup to the related comment block in the later section noted in the review.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 67: The CLI install path is hardcoding an outdated iii-sdk version and
overriding the dependency declared in package.json. Update the iii-sdk
install/version reference in src/cli.ts within the CLI install logic to use the
same 0.19.0 version already declared, so the runtime dependency stays
consistent. Use the identifiers around the install command in the CLI entry flow
to locate and change the version string.
In `@src/functions/context.ts`:
- Around line 180-185: The fallback renderer in context.ts is unreachable for
observations without titles because the important filter currently requires
o.title before the new compatibility path can run. Update the filtering logic in
the observations processing block so title-less records can still pass through
when they meet the other criteria, and keep the fallback handling in the same
context flow so older records are rendered instead of being dropped.
- Around line 207-210: The generic-title check in context.ts is doing a direct
GENERIC_TITLES.has(o.title) lookup, so it misses titles with different casing or
extra whitespace; update the title normalization used in the titleIsGeneric
logic before the set lookup. Use the existing title handling around
o.title/o.type to trim and lowercase (or otherwise normalize consistently) and
then compare against GENERIC_TITLES so values like “Conversation” and “
conversation ” correctly fall back.
---
Nitpick comments:
In `@src/functions/compress-synthetic.ts`:
- Around line 23-34: The inline narrative comments in compress-synthetic.ts
should be removed and the intent expressed through clearer naming or a small
helper instead. Refactor the normalization and conversation-name handling in the
synthetic compressor logic into descriptive helpers (for example around the
toolName normalization and the conversation/tool-name classification branch) so
the code reads clearly without WHAT/WHY comment blocks, and apply the same
cleanup to the related comment block in the later section noted in the review.
In `@src/functions/context.ts`:
- Around line 177-179: Remove the explanatory WHAT/WHY comment blocks in
context.ts and instead make the intent obvious through naming and structure in
the relevant helpers around the narrative filtering and rendering path; update
the affected logic in the areas of the observation filter and renderer
preparation (including the related blocks near the referenced helper code) so
the code is self-explanatory without inline comments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e8f40e74-acf6-48ad-bdd5-0ae40be83122
📒 Files selected for processing (3)
package.jsonsrc/functions/compress-synthetic.tssrc/functions/context.ts
| "@clack/prompts": "^1.2.0", | ||
| "dotenv": "^17.4.2", | ||
| "iii-sdk": "0.11.2", | ||
| "iii-sdk": "0.19.0", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify all hardcoded iii-sdk version pins and compare with package.json
rg -n --type ts 'iii-sdk@' src
cat package.json | jq '.dependencies["iii-sdk"]'Repository: rohitg00/agentmemory
Length of output: 425
Hardcoded iii-sdk version in CLI overrides declared dependency.
package.json declares iii-sdk as 0.19.0, but src/cli.ts:2312-2322 explicitly runs install iii-sdk@0.11.2. This forces a downgrade or conflict in runtime dependencies.
Update the hardcoded version in src/cli.ts to match the 0.19.0 specified in package.json.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` at line 67, The CLI install path is hardcoding an outdated
iii-sdk version and overriding the dependency declared in package.json. Update
the iii-sdk install/version reference in src/cli.ts within the CLI install logic
to use the same 0.19.0 version already declared, so the runtime dependency stays
consistent. Use the identifiers around the install command in the CLI entry flow
to locate and change the version string.
| const important = observations.filter( | ||
| (o) => o.title && o.importance >= 5, | ||
| (o) => | ||
| o.title && | ||
| o.importance >= 5 && | ||
| (o.narrative || "").trim().length > 0, | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Fallback path is unreachable for title-less observations.
Line 182 requires o.title, so observations with empty titles are filtered out before your new fallback renderer can recover them. That breaks the stated compatibility path for older records.
Proposed fix
const important = observations.filter(
(o) =>
- o.title &&
o.importance >= 5 &&
(o.narrative || "").trim().length > 0,
);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const important = observations.filter( | |
| (o) => o.title && o.importance >= 5, | |
| (o) => | |
| o.title && | |
| o.importance >= 5 && | |
| (o.narrative || "").trim().length > 0, | |
| ); | |
| const important = observations.filter( | |
| (o) => | |
| o.importance >= 5 && | |
| (o.narrative || "").trim().length > 0, | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/functions/context.ts` around lines 180 - 185, The fallback renderer in
context.ts is unreachable for observations without titles because the important
filter currently requires o.title before the new compatibility path can run.
Update the filtering logic in the observations processing block so title-less
records can still pass through when they meet the other criteria, and keep the
fallback handling in the same context flow so older records are rendered instead
of being dropped.
| const titleIsGeneric = | ||
| !o.title || | ||
| o.title === o.type || | ||
| GENERIC_TITLES.has(o.title); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Generic-title detection should normalize before set lookup.
GENERIC_TITLES.has(o.title) is case-sensitive and doesn’t trim, so values like "Conversation" or " conversation " bypass fallback unexpectedly.
Proposed fix
-const titleIsGeneric =
- !o.title ||
- o.title === o.type ||
- GENERIC_TITLES.has(o.title);
+const normalizedTitle = (o.title || "").trim().toLowerCase();
+const titleIsGeneric =
+ !normalizedTitle ||
+ normalizedTitle === o.type ||
+ GENERIC_TITLES.has(normalizedTitle);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const titleIsGeneric = | |
| !o.title || | |
| o.title === o.type || | |
| GENERIC_TITLES.has(o.title); | |
| const normalizedTitle = (o.title || "").trim().toLowerCase(); | |
| const titleIsGeneric = | |
| !normalizedTitle || | |
| normalizedTitle === o.type || | |
| GENERIC_TITLES.has(normalizedTitle); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/functions/context.ts` around lines 207 - 210, The generic-title check in
context.ts is doing a direct GENERIC_TITLES.has(o.title) lookup, so it misses
titles with different casing or extra whitespace; update the title normalization
used in the titleIsGeneric logic before the set lookup. Use the existing title
handling around o.title/o.type to trim and lowercase (or otherwise normalize
consistently) and then compare against GENERIC_TITLES so values like
“Conversation” and “ conversation ” correctly fall back.
The Hermes plugin (and any integration using
tool_name: "conversation"as a generic catch-all for user/assistant turns) stored conversation observations withtype: "other"andtitle: "conversation". The memory-context renderer then produced lines like:These were easy to misread as
type=conversation, empty content— and they were the reason the system prompt's<agentmemory-context>block was showing 100+ "empty" conversation entries across the user's six CLIs.This PR fixes the issue at three layers.
Root cause
The synthetic-compression path (
buildSyntheticCompressioninsrc/functions/compress-synthetic.ts) is the default compression path (AGENTMEMORY_AUTO_COMPRESS=false, the default since v0.8.8). It runs every non-vision observation throughbuildSyntheticCompressionwhen LLM compression is disabled.Two issues in this path:
inferType()only returns"conversation"whenhookType === "prompt_submit". The Hermes plugin useshookType: "post_tool_use", so the type falls through to"other".buildSyntheticCompressiontitle istruncate(toolName || "observation", 80). When the tool name is the literal string"conversation", the title becomes"conversation"— useless in the rendered output.The renderer's existing filter is
o.title && o.importance >= 5. It does NOT check whethertitle === typeor whether the title is a generic tool name. So observations with the bad title/title-collision slip through and produce visually-broken lines.Fix
Part 1 —
inferType()insrc/functions/compress-synthetic.ts: Added an explicit check for conversation-shaped tool names ("conversation","chat","prompt","message","turn") that returns"conversation". New observations are now correctly classified.Part 2 — Title derivation in
buildSyntheticCompression: When the tool name is generic ("conversation","other","observation","tool"), the title is now derived from the user prompt or tool input, not the literal tool name.Part 3 — Defensive renderer in
src/functions/context.ts: When the stored title is empty, equals the type, or is one of a small set of generic names, the renderer now falls back to using the subtitle or first 80 chars of the narrative as the title. This handles observations stored before the inferType fix landed. The renderer also strips a title prefix from the narrative when they overlap, to avoid the user message appearing twice on the same line.Part 4 — Tighter filter: Observations with empty narrative are now filtered out at the source, so they never reach the renderer and produce dangling-colon lines like
- [other] conversation:.Reproduction
sync_turnwhich sends:{"hookType": "post_tool_use", "data": {"tool_name": "conversation", "tool_input": "<user msg>", "tool_output": "<assistant msg>"}}<agentmemory-context>block.- [other] conversation: <narrative>(looks like a type label with no content).- [conversation] <user msg first 80 chars>: <rest of narrative>(correctly typed with meaningful title and full content).Test plan
POST /agentmemory/contextwith the same session/project as a previous test. Verify the rendered lines have meaningful titles and full content.POST /agentmemory/observewithtool_name: "conversation",tool_input: "hello",tool_output: "world". Verify it hastype: "conversation",title: <first 80 chars of "hello">,narrative: "hello | world".AGENTMEMORY_AUTO_COMPRESS=true. Verify the LLM-compressed observation has type and title that don't collide.Compatibility
Background
This bug was identified while debugging why the Hermes
agentmemoryplugin was producing empty memory-context blocks. The plugin'ssync_turnhook had been silently storing observations for months, but the renderer was misformatting them so the system prompt showed- [other] conversation: <content>— easy to misread as a broken type label.The reporter is an independent AI researcher running SCOS (Sovereign Cognitive OS) — a sovereignty infrastructure for AI — across six LLM CLIs (Codex, Claude Code, Gemini CLI, Jules, Antigravity, OpenCode). Without the fix, none of those CLIs could see the persistent context that the agentmemory plugin was storing. With the fix, all six share the same persistent conversation history.
Summary by CodeRabbit