Skip to content

Add memory extension as a reference pi extension#37

Merged
Mic92 merged 2 commits intomasterfrom
pi
Mar 15, 2026
Merged

Add memory extension as a reference pi extension#37
Mic92 merged 2 commits intomasterfrom
pi

Conversation

@Mic92
Copy link
Collaborator

@Mic92 Mic92 commented Mar 15, 2026

Provide a cross-session recall extension backed by sediment so users have a working example of how to write and package pi extensions for opencrow. The extension is not enabled by default.

The NixOS module extensions option now accepts true to resolve a packaged extension from the flake (e.g. extensions.memory = true), keeping the existing path-based approach for custom extensions.

Sediment is packaged alongside and patched into the extension at build time so users don't need to manually add it to extraPackages.

Summary by CodeRabbit

  • New Features

    • Added Memory Extension with cross-session conversation recall powered by semantic search
    • Introduced memory_search tool to explicitly query and retrieve stored memories
    • Configuration now supports enabling bundled extensions as simple boolean values
  • Documentation

    • Added Memory Extension documentation with setup and usage instructions

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

Warning

Rate limit exceeded

@Mic92 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 38019ba9-c016-41b6-ab69-caf35aebafe4

📥 Commits

Reviewing files that changed from the base of the PR and between f624de4 and 3e45abe.

📒 Files selected for processing (7)
  • README.md
  • extensions/memory/README.md
  • extensions/memory/index.ts
  • flake.nix
  • nix/extension-memory.nix
  • nix/module.nix
  • nix/sediment/default.nix
📝 Walkthrough

Walkthrough

Introduces a new Memory Extension for opencrow that leverages sediment—a local semantic vector store—to enable cross-session memory recall. Adds the extension's TypeScript implementation, documentation, Nix build definitions for both the extension and sediment dependency, and updates the module configuration to support bundled extensions via boolean flags.

Changes

Cohort / File(s) Summary
Memory Extension Implementation
extensions/memory/index.ts, extensions/memory/README.md
New memory extension module with sediment-backed storage for persisting and recalling conversations across sessions. Implements event handlers (session_compact, agent_end, before_agent_start) and exposes a memory_search tool for explicit semantic querying. Includes configuration requirements and NixOS usage examples in documentation.
Nix Build Configuration
nix/extension-memory.nix, nix/sediment/default.nix, flake.nix
New Nix derivations to package the memory extension and build sediment binary from source. Wires sediment path substitution into the extension build and registers extension-memory package in flake outputs for all supported systems.
NixOS Module Enhancement
nix/module.nix
Extends the extensions option to accept boolean values (true for bundled extensions, false to disable, or paths for custom extensions). Introduces resolvedExtensions binding to map booleans to packaged extension paths at configuration time and updates piSettingsJson generation to use resolved values.

Sequence Diagram(s)

sequenceDiagram
    participant Agent
    participant MemoryExt as Memory Extension
    participant Sediment
    participant Store as Local Vector Store
    
    rect rgba(100, 150, 200, 0.5)
    Note over Agent,Store: Memory Recall (before_agent_start)
    Agent->>MemoryExt: Start new agent turn with prompt
    MemoryExt->>Sediment: Execute recall query
    Sediment->>Store: Search semantically similar memories
    Store-->>Sediment: Return matching memories (filtered by similarity)
    Sediment-->>MemoryExt: JSON results
    MemoryExt->>Agent: Inject recalled memories into system prompt
    end
    
    rect rgba(150, 100, 200, 0.5)
    Note over Agent,Store: Memory Storage (agent_end & session_compact)
    Agent->>MemoryExt: Turn/session ends with conversation text
    MemoryExt->>Sediment: Execute store command
    Sediment->>Store: Persist conversation/summary vectors
    Store-->>Sediment: Acknowledgment
    Sediment-->>MemoryExt: Success response
    end
    
    rect rgba(200, 150, 100, 0.5)
    Note over Agent,Store: Explicit Memory Search (memory_search tool)
    Agent->>MemoryExt: Invoke memory_search tool with query
    MemoryExt->>Sediment: Execute recall with search query
    Sediment->>Store: Semantic vector search
    Store-->>Sediment: Matching memories
    Sediment-->>MemoryExt: Formatted results
    MemoryExt->>Agent: Return search results or error
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • module: allow to specify extensions #16: Introduces the initial extensions configuration option in nix/module.nix; this PR extends that option's type system to support booleans and adds the resolution logic for bundled extensions.

Poem

🐰 A rabbit hops through memory's door,
Sediment whispers what came before,
Each conversation tucked away,
Recalled to guide the agent's way.
With vectors woven, true and deep—
No thought forgotten, no secret to keep! 🌾✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: adding a memory extension as a reference pi extension to the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pi
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
extensions/memory/index.ts (3)

163-175: Consider validating the limit parameter bounds.

The limit parameter accepts any number without validation. A negative or excessively large value could cause unexpected behavior in sediment. Consider clamping to a reasonable range.

♻️ Suggested validation
     async execute(_toolCallId, params, signal) {
       try {
+        const limit = Math.max(1, Math.min(params.limit ?? 5, 100));
         const raw = await sediment(
           pi,
           [
             "recall",
             params.query,
             "--limit",
-            String(params.limit ?? 5),
+            String(limit),
             "--json",
           ],
           signal,
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/memory/index.ts` around lines 163 - 175, The execute method calls
sediment with params.limit unchecked, which allows negative or huge values;
clamp and validate params.limit before the sediment call (in execute) to a sane
range (e.g., min 1, max 100 or a project-appropriate cap) and pass the clamped
value to String() for the "--limit" argument so sediment always receives a valid
positive integer; update any related variable or documentation for params.limit
and ensure the clamping logic handles non-numeric inputs (NaN) by falling back
to a default.

142-144: Consider logging the error for debugging.

The empty catch block silently swallows errors when sediment is unavailable. While graceful degradation is appropriate, logging the error at debug level would aid troubleshooting.

♻️ Suggested change
-    } catch {
+    } catch (e) {
+      console.debug("memory: recall unavailable, proceeding without memories", e);
       // sediment unavailable — proceed without memories
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/memory/index.ts` around lines 142 - 144, The catch block that
currently swallows errors when accessing "sediment" (the try/catch handling
"sediment unavailable" near where memories are loaded) should capture the thrown
error (e.g., catch (err)) and log it at debug level so failures are visible
during troubleshooting; update that catch to call the module's logger.debug (or
logger?.debug) with a short context string like "sediment unavailable" plus the
error object, falling back to console.debug if no logger exists.

62-66: Change similarity from string to number.

The sediment recall command outputs similarity as a floating-point number (0.0–1.0), not a string. The current parseFloat() call masks this type inconsistency. Update the interface and remove the unnecessary conversion:

♻️ Suggested changes
 interface RecallResult {
   content: string;
   id: string;
-  similarity: string;
+  similarity: number;
 }
   return parsed.results.filter(
-    (r) => parseFloat(r.similarity) >= MIN_SIMILARITY,
+    (r) => r.similarity >= MIN_SIMILARITY,
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/memory/index.ts` around lines 62 - 66, The RecallResult interface
currently types similarity as string while the sediment recall output is a float
(0.0–1.0); update the RecallResult declaration to use similarity: number and
remove the unnecessary parseFloat conversion where RecallResult objects are
constructed (replace parseFloat(...) usage with the original numeric value or
ensure the source value is a number) so the type matches actual runtime values.
nix/module.nix (1)

19-27: Consider adding an assertion for invalid extension names.

If a user sets extensions.typo = true for a non-existent packaged extension, they'll get a cryptic Nix error about missing attribute extension-typo. An assertion with a helpful message would improve UX.

♻️ Suggested assertion

Add to the assertions list in config:

{
  assertion = lib.all (name:
    cfg.extensions.${name} != true || 
    self.packages.${pkgs.hostPlatform.system} ? "extension-${name}"
  ) (lib.attrNames cfg.extensions);
  message = let
    invalid = lib.filter (name:
      cfg.extensions.${name} == true && 
      !(self.packages.${pkgs.hostPlatform.system} ? "extension-${name}")
    ) (lib.attrNames cfg.extensions);
  in "Unknown bundled extension(s): ${lib.concatStringsSep ", " invalid}. "
   + "Available: memory";
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nix/module.nix` around lines 19 - 27, Add a config assertion that validates
cfg.extensions entries before resolvedExtensions runs: iterate lib.attrNames
cfg.extensions and ensure for each name that if cfg.extensions.${name} == true
then self.packages.${pkgs.hostPlatform.system} has the attribute
"extension-${name}"; if the check fails, fail with a message listing the unknown
extension names and the set of available "extension-*" package names (computed
from lib.attrNames of self.packages.${pkgs.hostPlatform.system}
filtered/prefixed by "extension-"). Include this assertion in the config
assertions list so users get a clear error instead of a missing-attribute Nix
error when resolvedExtensions tries to reference a non-existent self.packages
attribute.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@extensions/memory/README.md`:
- Line 3: The README's sediment repository URL is inconsistent with the fetch
source in nix/sediment/default.nix; determine the canonical repository (either
github.com/Mic92/sediment or github.com/rendro/sediment), then update the link
in extensions/memory/README.md to match that canonical repo (or update
nix/sediment/default.nix to fetch from the README's repo if the nix file is
wrong), ensuring both files reference the identical GitHub URL so the
documentation and nix fetch are consistent.

---

Nitpick comments:
In `@extensions/memory/index.ts`:
- Around line 163-175: The execute method calls sediment with params.limit
unchecked, which allows negative or huge values; clamp and validate params.limit
before the sediment call (in execute) to a sane range (e.g., min 1, max 100 or a
project-appropriate cap) and pass the clamped value to String() for the
"--limit" argument so sediment always receives a valid positive integer; update
any related variable or documentation for params.limit and ensure the clamping
logic handles non-numeric inputs (NaN) by falling back to a default.
- Around line 142-144: The catch block that currently swallows errors when
accessing "sediment" (the try/catch handling "sediment unavailable" near where
memories are loaded) should capture the thrown error (e.g., catch (err)) and log
it at debug level so failures are visible during troubleshooting; update that
catch to call the module's logger.debug (or logger?.debug) with a short context
string like "sediment unavailable" plus the error object, falling back to
console.debug if no logger exists.
- Around line 62-66: The RecallResult interface currently types similarity as
string while the sediment recall output is a float (0.0–1.0); update the
RecallResult declaration to use similarity: number and remove the unnecessary
parseFloat conversion where RecallResult objects are constructed (replace
parseFloat(...) usage with the original numeric value or ensure the source value
is a number) so the type matches actual runtime values.

In `@nix/module.nix`:
- Around line 19-27: Add a config assertion that validates cfg.extensions
entries before resolvedExtensions runs: iterate lib.attrNames cfg.extensions and
ensure for each name that if cfg.extensions.${name} == true then
self.packages.${pkgs.hostPlatform.system} has the attribute "extension-${name}";
if the check fails, fail with a message listing the unknown extension names and
the set of available "extension-*" package names (computed from lib.attrNames of
self.packages.${pkgs.hostPlatform.system} filtered/prefixed by "extension-").
Include this assertion in the config assertions list so users get a clear error
instead of a missing-attribute Nix error when resolvedExtensions tries to
reference a non-existent self.packages attribute.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1ab9d380-4d26-49c7-b299-4ea205937ca3

📥 Commits

Reviewing files that changed from the base of the PR and between 9f6c825 and f624de4.

⛔ Files ignored due to path filters (1)
  • nix/sediment/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • extensions/memory/README.md
  • extensions/memory/index.ts
  • flake.nix
  • nix/extension-memory.nix
  • nix/module.nix
  • nix/sediment/default.nix

Users need to know how to enable, configure, and write extensions
without reading the NixOS module source.
Provide a cross-session recall extension backed by sediment so users
have a working example of how to write and package pi extensions for
opencrow. The extension is not enabled by default.

The NixOS module extensions option now accepts `true` to resolve a
packaged extension from the flake (e.g. `extensions.memory = true`),
keeping the existing path-based approach for custom extensions.

Sediment is packaged alongside and patched into the extension at build
time so users don't need to manually add it to extraPackages.
@Mic92 Mic92 merged commit a9f3091 into master Mar 15, 2026
1 check passed
@Mic92 Mic92 deleted the pi branch March 15, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant