Skip to content

feat(agent-runtime): activate surreal graphs plugin memory runtime#68

Merged
yacosta738 merged 6 commits into
mainfrom
feature/memory-surreal-graphs-plugin
Feb 22, 2026
Merged

feat(agent-runtime): activate surreal graphs plugin memory runtime#68
yacosta738 merged 6 commits into
mainfrom
feature/memory-surreal-graphs-plugin

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

@yacosta738 yacosta738 commented Feb 22, 2026

This pull request introduces strict ontology validation and correction for agent responses, ensuring that outputs comply with domain-specific rules before being returned to users. It also adds a new plugin release management skill, improves plugin-backed memory initialization, and updates dependencies for WebAssembly plugin support. Below are the most important changes grouped by theme:

Strict Ontology Validation and Correction:

  • Introduced enforce_strict_memory_validation logic across agent, CLI, and channel flows to validate responses against memory ontologies, attempt automatic correction using the LLM if violations are found, and provide user-friendly fallback messages if validation fails. [1] [2] [3] [4] [5] [6] [7] [8] [9]

  • Added a new MemoryValidationResult struct to standardize ontology validation results, including validity and a list of violations.

Plugin-backed Memory Improvements:

  • Improved plugin-backed memory backend initialization to use the new resolve_memory_plugin_runtime function, handle initialization errors gracefully, and fall back to markdown memory if plugin loading fails.

  • Updated dependencies for the memory-surreal-graphs plugin to include hex, serde, serde_json, and sha2 for better WASM support.

  • Added the wasmi crate to the agent runtime for WASM execution in plugin-backed memory backends.

Documentation and Release Workflow:

  • Added a new plugin-release skill with detailed documentation for releasing Corvus runtime plugins, covering tagging, artifact publishing, signing, and Cloudflare Pages deployment. [1] [2]

Minor Updates:

  • Updated the README to fix the Tailwind CSS documentation link.

  • Exposed MemoryValidationResult in the memory module's public API.

Summary by CodeRabbit

  • New Features

    • Plugin release workflow for deploying runtime plugins.
    • Strict memory-based validation enforcing ontology rules on agent responses with automatic correction and fallback messaging.
    • Graph-backed memory backend with richer store/recall/search behavior, optional WASM-backed plugin execution, runtime plugin resolution, and resilient fallbacks.
    • Added WASM support groundwork and tooling for plugin-backed memory.
  • Documentation

    • Comprehensive Plugin Release Manager docs added.
    • Updated external Tailwind CSS link.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Feb 22, 2026

Deploying corvus-plugins with  Cloudflare Pages  Cloudflare Pages

Latest commit: 11ccc44
Status: ✅  Deploy successful!
Preview URL: https://3dd29c2d.corvus-plugins.pages.dev
Branch Preview URL: https://feature-memory-surreal-graph.corvus-plugins.pages.dev

View logs

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a WASM-based SurrealDB-backed memory plugin and host executor, exposes FFI plugin entrypoints, integrates strict ontology-based memory validation (validate → correct → re-validate) into agent flows and channels, registers a Plugin Release Manager skill, and updates runtime dependencies and plugin manifests.

Changes

Cohort / File(s) Summary
Documentation & Skill registration
.agents/AGENTS.md, .agents/skills/plugin-release/SKILL.md, README.md
Register new plugin-release skill and add full skill doc; minor README link update.
Top-level crate features & deps
clients/agent-runtime/Cargo.toml
Add optional wasmi dep, wat dev-dep, and memory-plugin feature alias.
WASM plugin crate manifest
clients/agent-runtime/plugins/memory-surreal-graphs/Cargo.toml
Add dependencies (hex, serde, serde_json, sha2) and tighten release profile.
WASM plugin implementation
clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
New full WASM plugin exposing FFI entrypoints, SurrealDB bridge, CRUD-like memory ops, host SQL interaction, and robust FFI memory management.
Memory trait, exports & gating
clients/agent-runtime/src/memory/traits.rs, clients/agent-runtime/src/memory/mod.rs
Add public MemoryValidationResult and async validate_response trait method (default permissive); gate plugin module behind memory-plugin; update exports and fallback handling.
Plugin-backed memory & host executor
clients/agent-runtime/src/memory/plugin.rs
Replace MarkdownMemory delegation with PluginWasmExecutor; implement RPC-style host↔plugin protocol, Surreal SQL host client, host-call bridge, endpoint validation, and error/fallback handling.
Resolved runtime plugin resolver
clients/agent-runtime/src/plugins/mod.rs
Add ResolvedMemoryPlugin and resolve_memory_plugin_runtime to locate/validate installed runtime plugin artifacts and manifest.
Agent validation module & integration
clients/agent-runtime/src/agent/mod.rs, clients/agent-runtime/src/agent/validation.rs, clients/agent-runtime/src/agent/agent.rs, clients/agent-runtime/src/agent/loop_.rs, clients/agent-runtime/src/channels/mod.rs
Add validation module with enforce_strict_validation; integrate strict validate→correct→revalidate flow into agent run loop, channels, and tests; update history handling.
Memory plugin runtime wiring & tests
clients/agent-runtime/src/plugins/..., clients/agent-runtime/src/security/pairing.rs
Add runtime plugin resolution helpers, endpoint/packing/WASM invocation tests, and a small formatting tweak in pairing auth.
New public APIs & FFI entrypoints
clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs, clients/agent-runtime/src/memory/traits.rs
New FFI entrypoints (alloc_v1, dealloc_v1, memory_v1, health_v1, run) and new public type MemoryValidationResult with Default and trait method.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Agent
    participant Provider as Provider/LLM
    participant MemoryPlugin as Memory Plugin (WASM)
    participant SurrealDB

    User->>Agent: user query
    Agent->>Provider: generate draft response
    Provider-->>Agent: draft response

    Agent->>MemoryPlugin: validate_response(query, draft)
    MemoryPlugin->>SurrealDB: execute ontology/rules
    SurrealDB-->>MemoryPlugin: violations / result
    MemoryPlugin-->>Agent: ValidationResult {valid, violations}

    alt valid
        Agent-->>User: return draft
    else invalid
        Agent->>Provider: system prompt -> fix violations
        Provider-->>Agent: corrected response
        Agent->>MemoryPlugin: validate_response(query, corrected)
        MemoryPlugin->>SurrealDB: re-check rules
        MemoryPlugin-->>Agent: ValidationResult
        alt corrected valid
            Agent-->>User: return corrected response
        else
            Agent-->>User: return ontology-failure message
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.68% 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
Title check ✅ Passed The PR title clearly and concisely summarizes the primary change: activating surreal graphs plugin memory runtime in the agent-runtime.
Description check ✅ Passed The PR description is comprehensive, well-organized into clear themes, and covers all major changes including validation logic, plugin-backed memory, WASM support, and documentation.

✏️ 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 feature/memory-surreal-graphs-plugin

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.

@yacosta738 yacosta738 enabled auto-merge (squash) February 22, 2026 09:13
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 22, 2026

✅ Contributor Report

User: @yacosta738
Status: Passed (12/13 metrics passed)

Metric Description Value Threshold Status
PR Merge Rate PRs merged vs closed 89% >= 30%
Repo Quality Repos with ≥100 stars 0 >= 0
Positive Reactions Positive reactions received 9 >= 1
Negative Reactions Negative reactions received 0 <= 5
Account Age GitHub account age 3039 days >= 30 days
Activity Consistency Regular activity over time 108% >= 0%
Issue Engagement Issues with community engagement 0 >= 0
Code Reviews Code reviews given to others 363 >= 0
Merger Diversity Unique maintainers who merged PRs 3 >= 0
Repo History Merge Rate Merge rate in this repo 87% >= 0%
Repo History Min PRs Previous PRs in this repo 48 >= 0
Profile Completeness Profile richness (bio, followers) 90 >= 0
Suspicious Patterns Spam-like activity detection 1 N/A

Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-02-22 to 2026-02-22

Copy link
Copy Markdown
Contributor

@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: 7

🧹 Nitpick comments (10)
.agents/skills/plugin-release/SKILL.md (2)

47-48: Replace bat with cat for portability.

bat is a third-party pager that is not installed in most CI runners or fresh developer environments. Using it here creates an unnecessary dependency for what is purely a read-only verification step.

✏️ Proposed fix
-bat clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml
+cat clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/plugin-release/SKILL.md around lines 47 - 48, The command in
the documentation uses the non-portable pager "bat
clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml"; update that command to
use the standard, universally available "cat
clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml" so CI and fresh developer
environments won’t depend on a third-party tool — edit the SKILL.md entry that
contains the "bat clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml" command
and replace "bat" with "cat".

67-69: Document tag recovery steps alongside Common Failure Case #1.

The skill describes a tag/version mismatch as the most likely failure (line 111), but there are no instructions for recovering from a pushed tag that must be retracted. A mismatched tag that already triggered publish-plugins.yml can cause confusing partial artifacts.

Consider adding a short "Recovery" subsection after the tag step, e.g.:

✏️ Proposed addition
 git push origin plugin/<plugin-id>/vX.Y.Z
+
+> **Recovery (tag/version mismatch):** If the workflow fails due to a mismatch,
+> delete the tag locally and remotely before retrying:
+> ```bash
+> git tag -d plugin/<plugin-id>/vX.Y.Z
+> git push origin --delete plugin/<plugin-id>/vX.Y.Z
+> ```
+> Fix the version in `Cargo.toml`, commit, then re-run steps 2–4.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/plugin-release/SKILL.md around lines 67 - 69, Document
recovery steps for retracting a pushed tag after the tag creation step by adding
a short "Recovery" subsection (placed after the git tag/push lines and
referenced from Common Failure Case `#1`). Explain the two-step recovery: delete
the local tag with "git tag -d plugin/<plugin-id>/vX.Y.Z", delete the remote tag
with "git push origin --delete plugin/<plugin-id>/vX.Y.Z", then fix the version
in Cargo.toml, commit, and re-run the publish steps (the workflow
publish-plugins.yml may need to be re-triggered). Reference the tag pattern
plugin/<plugin-id>/vX.Y.Z and publish-plugins.yml so readers can locate the
relevant workflow.
clients/agent-runtime/src/security/pairing.rs (1)

145-147: .any() short-circuits — not a fully constant-time scan

The change from tokens.contains(&hashed) to iter().any(constant_time_eq) makes the timing-safe intent explicit and is a net improvement. However, .any() short-circuits on the first true, so execution time still varies depending on where the matching hash lands in the HashSet's non-deterministic iteration order.

In practice this is a negligible risk (iteration order is non-predictable by an attacker, token sets are tiny, and both sides are fixed-length SHA-256 hex so standard equality would already be near-constant), but if you want to eliminate the theoretical concern entirely, a non-short-circuiting fold avoids the early exit:

♻️ Constant-time fold over all stored tokens
-        tokens
-            .iter()
-            .any(|stored| constant_time_eq(stored, &hashed))
+        tokens
+            .iter()
+            .fold(false, |found, stored| found | constant_time_eq(stored, &hashed))

As a side note, switching from HashSet::contains (O(1)) to a linear scan (O(n)) is a minor performance trade-off, although negligible for the handful of tokens expected here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/security/pairing.rs` around lines 145 - 147,
Replace the short-circuiting .any(...) scan with a non-short-circuiting
reduction so every stored token is compared to hashed; in pairing.rs change the
tokens.iter().any(|stored| constant_time_eq(stored, &hashed)) usage to a fold
(or explicit loop) that XORs/ORs (accumulates) the result of constant_time_eq
for each stored token and returns the final boolean, ensuring constant_time_eq
is called for every entry (refer to the tokens variable and constant_time_eq and
hashed identifiers when making the change).
clients/agent-runtime/src/channels/mod.rs (1)

127-211: Deduplicate strict validation helper to prevent drift.
The same logic now exists in agent.rs, agent/loop_.rs, and here. Consider extracting a shared helper (e.g., in an agent or memory module) so behavior and messaging stay consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/channels/mod.rs` around lines 127 - 211, The
enforce_strict_memory_validation function duplicates validation/correction flow
found in agent.rs and agent/loop_.rs; extract that logic into a shared helper
(e.g., memory::enforce_strict_validation or
agent::validation::enforce_strict_validation) and have
enforce_strict_memory_validation, the agent.rs and agent/loop_.rs callers
delegate to it; move the calls to mem.validate_response and
provider.chat_with_system plus the standardized log and return messages
(including the exact warning strings and user-facing failure strings) into the
single helper so behavior and messages remain identical across callers.
clients/agent-runtime/src/plugins/mod.rs (1)

1935-1994: Cross‑check runtime_api between lockfile and manifest.
You validate id/version, but a mismatched runtime_api could indicate tampering or stale metadata. Failing fast here avoids surprising runtime mismatches.

♻️ Suggested check
     if manifest.version != locked.version {
         bail!(
             "Plugin manifest version mismatch for '{}': lockfile='{}', manifest='{}'",
             plugin_id,
             locked.version,
             manifest.version
         );
     }
+
+    if manifest.runtime_api != locked.runtime_api {
+        bail!(
+            "Plugin manifest runtime_api mismatch for '{}': lockfile='{}', manifest='{}'",
+            plugin_id,
+            locked.runtime_api,
+            manifest.runtime_api
+        );
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/plugins/mod.rs` around lines 1935 - 1994, Add a
runtime_api consistency check in resolve_memory_plugin_runtime: after
deserializing PluginManifest into manifest and after
validate_plugin_version(...) compare manifest.runtime_api with the
locked.runtime_api (the value on the locked struct returned by
resolve_memory_plugin) and bail with a clear error if they differ (similar to
the existing id/version mismatch branches); include plugin_id,
locked.runtime_api and manifest.runtime_api in the error message and keep
returning Err via bail! so mismatched runtime_api fails fast before constructing
ResolvedMemoryPlugin.
clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs (1)

340-358: Cache schema initialization to avoid repeated SQL on every request.
ensure_schema runs multiple DEFINE statements per call; a one‑time guard avoids needless DB round‑trips and reduces latency.

♻️ Possible approach
 fn ensure_schema() -> std::result::Result<(), String> {
-    let statements = [
-        "DEFINE TABLE memory_entries SCHEMALESS;",
-        "DEFINE TABLE memory_edges SCHEMALESS;",
-        "DEFINE TABLE ontology_terms SCHEMALESS;",
-        "DEFINE TABLE ontology_rules SCHEMALESS;",
-        "DEFINE INDEX idx_memory_entries_key ON TABLE memory_entries COLUMNS key UNIQUE;",
-        "DEFINE INDEX idx_memory_entries_category ON TABLE memory_entries COLUMNS category;",
-        "DEFINE INDEX idx_memory_entries_session ON TABLE memory_entries COLUMNS session_id;",
-        "DEFINE INDEX idx_memory_edges_relation ON TABLE memory_edges COLUMNS relation_type;",
-        "DEFINE INDEX idx_memory_edges_to ON TABLE memory_edges COLUMNS to_id;",
-    ];
-
-    for statement in statements {
-        let _ = sql_query(statement, None, Some(3_000));
-    }
-
-    Ok(())
+    static SCHEMA_INIT: std::sync::OnceLock<()> = std::sync::OnceLock::new();
+    SCHEMA_INIT.get_or_try_init(|| {
+        let statements = [
+            "DEFINE TABLE memory_entries SCHEMALESS;",
+            "DEFINE TABLE memory_edges SCHEMALESS;",
+            "DEFINE TABLE ontology_terms SCHEMALESS;",
+            "DEFINE TABLE ontology_rules SCHEMALESS;",
+            "DEFINE INDEX idx_memory_entries_key ON TABLE memory_entries COLUMNS key UNIQUE;",
+            "DEFINE INDEX idx_memory_entries_category ON TABLE memory_entries COLUMNS category;",
+            "DEFINE INDEX idx_memory_entries_session ON TABLE memory_entries COLUMNS session_id;",
+            "DEFINE INDEX idx_memory_edges_relation ON TABLE memory_edges COLUMNS relation_type;",
+            "DEFINE INDEX idx_memory_edges_to ON TABLE memory_edges COLUMNS to_id;",
+        ];
+
+        for statement in statements {
+            let _ = sql_query(statement, None, Some(3_000));
+        }
+
+        Ok(())
+    })?;
+    Ok(())
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
340 - 358, ensure_schema currently issues the DEFINE statements on every call;
change it to run the block only once by adding a thread-safe one-time guard
(e.g., std::sync::Once or once_cell::sync::OnceCell) around the loop that calls
sql_query so subsequent calls return immediately; keep the existing statements
array and sql_query calls inside the one-time closure and propagate or log any
error from the initial run so failures are visible (reference ensure_schema and
sql_query to locate where to add the Once/OnceCell guard).
clients/agent-runtime/src/memory/traits.rs (1)

16-21: Avoid a misleading Default for MemoryValidationResult.
#[derive(Default)] makes valid = false, which can accidentally mark responses invalid if MemoryValidationResult::default() is used. Consider a custom Default returning valid: true or remove Default to avoid misuse.

♻️ Suggested change
-#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct MemoryValidationResult {
     pub valid: bool,
     #[serde(default)]
     pub violations: Vec<String>,
 }
+
+impl Default for MemoryValidationResult {
+    fn default() -> Self {
+        Self {
+            valid: true,
+            violations: Vec::new(),
+        }
+    }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/traits.rs` around lines 16 - 21, The derived
Default for MemoryValidationResult is misleading because it yields valid =
false; update the struct by removing Default from the derive list and provide an
explicit impl Default for MemoryValidationResult that returns valid: true and
violations: empty Vec (or alternatively drop Default entirely to avoid
accidental use); locate the MemoryValidationResult definition and replace the
derive(Default) with a custom impl Default that sets valid = true and violations
= Vec::new() so callers using MemoryValidationResult::default() get a valid
result.
clients/agent-runtime/src/memory/plugin.rs (2)

925-967: Tests cover the essential helpers but lack coverage for the core WASM execution path.

The category roundtrip, endpoint normalization, security validation, and status packing tests are good. Consider adding integration tests (even with a minimal stub WASM module) for invoke_sync in a follow-up to validate the full alloc → call → dealloc lifecycle.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 925 - 967, Tests
currently exercise helpers but miss the WASM execution lifecycle; add an
integration test that exercises invoke_sync to validate alloc → call → dealloc
and result handling. Create a minimal stub WASM module (or embed a tiny WASM
binary) that exports alloc, dealloc, and the target function, then in the test
call invoke_sync with that module, verify the alloc returns a pointer, the call
returns expected output, and dealloc frees memory without leaks or panics;
target functions and helpers to touch are invoke_sync, alloc, dealloc, and any
memory marshalling helpers used by invoke_sync so the full roundtrip is covered.

566-612: host_sql_v1 — the synchronous blocking HTTP call is safe here but consider logging at warn level for transport errors.

Since this runs inside spawn_blocking, the blocking HTTP call is acceptable. However, the transport error on line 532-541 is silently returned as a JSON error to the plugin. A tracing::warn! here would help operators diagnose connectivity issues with SurrealDB.

💡 Add warn-level logging for SQL transport errors
             Err(error) => {
+                tracing::warn!("SurrealDB SQL transport error: {error}");
                 return json!({
                     "ok": false,
                     "status": 0,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 566 - 612, In
host_sql_v1, when the closure returns Err(error) you should emit a warn-level
log with the error details to aid diagnosing SurrealDB transport/connectivity
issues; add a tracing::warn! (or warn!(...) via use tracing::warn) in the
Err(error) arm—e.g. tracing::warn!(error = %error, "host_sql_v1 transport error
contacting SurrealDB")—before building the JSON error payload so operators see
the transport error context; keep the existing JSON/error response behavior
unchanged.
clients/agent-runtime/src/memory/mod.rs (1)

113-147: Consider surfacing the fallback more visibly when the user explicitly configured surreal-graphs.

The tracing::warn! on line 126-128 is appropriate, but when a user explicitly sets backend: "surreal-graphs", silently degrading to markdown could cause confusion (e.g., no graph ontology features, data not persisted to SurrealDB). Consider logging at error level or including a startup banner hint so the fallback is harder to miss.

The overall approach — resolve plugin runtime, attempt init, fallback on failure — is sound and well-structured.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/mod.rs` around lines 113 - 147, The current
fallback to Markdown when MemoryBackendKind::SurrealGraphs fails is only traced
at warn level; change the fallback logging in the SurrealGraphs match arm (the
branches handling Ok(Some(plugin_runtime)) -> Err(error), Ok(None), and
Err(error)) to a more prominent error or startup-visible message so users who
explicitly configured surreal-graphs notice the degradation. Update the tracing
messages around PluginBackedMemory::new and the branches referencing
crate::plugins::OFFICIAL_SURREAL_GRAPHS_PLUGIN_ID and MarkdownMemory::new to use
tracing::error! (or emit an additional startup banner/hint) that clearly states
the backend fell back to markdown, warns that SurrealDB features/persistence
will not be available, and suggests installing/trusting the plugin ID. Ensure
the messages include the plugin id and the originating error details where
available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/Cargo.toml`:
- Around line 83-84: The wasmi dependency in Cargo.toml is using default
features which bloats binaries; update the wasmi entry (identify the wasmi
dependency line) to disable default features and explicitly enable only needed
features (e.g., set default-features = false and features = ["std"]) and
remove/avoid the "wat" feature unless the codebase actually parses WAT text;
ensure the dependency line matches the project's pattern used for
prost/surrealdb.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/Cargo.toml`:
- Around line 24-28: Update the Cargo.toml dependency entries for serde and
serde_json to disable default features and enable only what's needed for WASM
size; specifically set serde = { version = "1.0", default-features = false,
features = ["derive", "alloc"] } and serde_json = { version = "1.0",
default-features = false, features = ["alloc"] } so the json! macro and basic
(de)serialization remain available while removing preserve_order,
arbitrary_precision, float_roundtrip, unbounded_depth and raw_value.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 159-197: In dispatch, before calling alloc_v1, check the encoded
response size (encoded.len()) against the plugin manifest's max_output_bytes (or
a defined MAX_OUTPUT_BYTES constant) and return a non-zero status if it exceeds
the limit; this prevents allocating oversized responses from execute() for
functions like list/recall—so compute encoded (or better: compute/serialize to a
size-limited buffer), compare to max_output_bytes, and only call
alloc_v1(resp_len_i32) when within the limit, returning an appropriate error
code and avoiding dealloc_v1 calls when allocation was never performed.

In `@clients/agent-runtime/src/agent/agent.rs`:
- Around line 355-444: Add unit tests exercising
Agent::enforce_strict_memory_validation covering: (1) initial
memory.validate_response returning invalid with violations and
provider.chat_with_system supplying a corrected string that then validates true
(assert the method returns the corrected text), (2) provider.chat_with_system
error path (assert the fallback error message contains the original
violations_text), (3) post-correction validation returning still-invalid (assert
returned message includes checked violations), and (4) memory.validate_response
errors on initial or post-correction calls (assert the function returns the
appropriate unavailable/ontology-failed messages). Use mocks/stubs for
memory.validate_response and provider.chat_with_system to simulate each scenario
and assert exact returned strings from enforce_strict_memory_validation to lock
behavior; reference the enforce_strict_memory_validation method,
Memory::validate_response, and Provider::chat_with_system when locating code to
test.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 461-470: SurrealSqlClient's auto-derived Debug exposes sensitive
fields (password, token); replace #[derive(Debug)] with a manual impl of
std::fmt::Debug for SurrealSqlClient that prints non-sensitive fields (sql_url,
namespace, database, username) but redacts or omits password and token (e.g.,
show "<redacted>" or "REDACTED"); update/remove the Debug derive on any upstream
types (HostState, PluginWasmExecutorInner) if they still derive Debug
transitively from SurrealSqlClient so that only non-sensitive data is printed
when those types are formatted.
- Around line 778-800: The validate_endpoint_security function currently accepts
"ws" and "wss" unconditionally, letting endpoints that will later normalize to
http/https bypass the HTTP loopback restriction; update
validate_endpoint_security (checking scheme, host, allow_http_loopback) to treat
"ws" like "http" and "wss" like "https" when allow_http_loopback is relevant: if
scheme == "ws" or "wss" and allow_http_loopback is false, enforce
is_loopback_host(host) (bail with the same messages used for "http" when
non-loopback), while preserving the existing behavior for secure schemes;
reference the scheme, host, allow_http_loopback, is_loopback_host, and
normalize_http_endpoint logic when making the change.
- Around line 342-350: The code currently compiles the WASM module on every
invoke_sync by calling Engine::new/Module::new with self.inner.wasm_bytes;
instead, compile once in PluginWasmExecutor::new and store the resulting Engine
and Module on PluginWasmExecutorInner (e.g., add engine: Engine and module:
Module fields), then in invoke_sync only create a fresh Store (mutable
per-invocation) and instantiate from the cached module; update
PluginWasmExecutor::new to build WasmiConfig/Engine and Module::new(&engine,
&self.inner.wasm_bytes) once and remove the per-invoke compilation in
invoke_sync.

---

Nitpick comments:
In @.agents/skills/plugin-release/SKILL.md:
- Around line 47-48: The command in the documentation uses the non-portable
pager "bat clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml"; update that
command to use the standard, universally available "cat
clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml" so CI and fresh developer
environments won’t depend on a third-party tool — edit the SKILL.md entry that
contains the "bat clients/agent-runtime/plugins/<plugin-dir>/Cargo.toml" command
and replace "bat" with "cat".
- Around line 67-69: Document recovery steps for retracting a pushed tag after
the tag creation step by adding a short "Recovery" subsection (placed after the
git tag/push lines and referenced from Common Failure Case `#1`). Explain the
two-step recovery: delete the local tag with "git tag -d
plugin/<plugin-id>/vX.Y.Z", delete the remote tag with "git push origin --delete
plugin/<plugin-id>/vX.Y.Z", then fix the version in Cargo.toml, commit, and
re-run the publish steps (the workflow publish-plugins.yml may need to be
re-triggered). Reference the tag pattern plugin/<plugin-id>/vX.Y.Z and
publish-plugins.yml so readers can locate the relevant workflow.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 340-358: ensure_schema currently issues the DEFINE statements on
every call; change it to run the block only once by adding a thread-safe
one-time guard (e.g., std::sync::Once or once_cell::sync::OnceCell) around the
loop that calls sql_query so subsequent calls return immediately; keep the
existing statements array and sql_query calls inside the one-time closure and
propagate or log any error from the initial run so failures are visible
(reference ensure_schema and sql_query to locate where to add the Once/OnceCell
guard).

In `@clients/agent-runtime/src/channels/mod.rs`:
- Around line 127-211: The enforce_strict_memory_validation function duplicates
validation/correction flow found in agent.rs and agent/loop_.rs; extract that
logic into a shared helper (e.g., memory::enforce_strict_validation or
agent::validation::enforce_strict_validation) and have
enforce_strict_memory_validation, the agent.rs and agent/loop_.rs callers
delegate to it; move the calls to mem.validate_response and
provider.chat_with_system plus the standardized log and return messages
(including the exact warning strings and user-facing failure strings) into the
single helper so behavior and messages remain identical across callers.

In `@clients/agent-runtime/src/memory/mod.rs`:
- Around line 113-147: The current fallback to Markdown when
MemoryBackendKind::SurrealGraphs fails is only traced at warn level; change the
fallback logging in the SurrealGraphs match arm (the branches handling
Ok(Some(plugin_runtime)) -> Err(error), Ok(None), and Err(error)) to a more
prominent error or startup-visible message so users who explicitly configured
surreal-graphs notice the degradation. Update the tracing messages around
PluginBackedMemory::new and the branches referencing
crate::plugins::OFFICIAL_SURREAL_GRAPHS_PLUGIN_ID and MarkdownMemory::new to use
tracing::error! (or emit an additional startup banner/hint) that clearly states
the backend fell back to markdown, warns that SurrealDB features/persistence
will not be available, and suggests installing/trusting the plugin ID. Ensure
the messages include the plugin id and the originating error details where
available.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 925-967: Tests currently exercise helpers but miss the WASM
execution lifecycle; add an integration test that exercises invoke_sync to
validate alloc → call → dealloc and result handling. Create a minimal stub WASM
module (or embed a tiny WASM binary) that exports alloc, dealloc, and the target
function, then in the test call invoke_sync with that module, verify the alloc
returns a pointer, the call returns expected output, and dealloc frees memory
without leaks or panics; target functions and helpers to touch are invoke_sync,
alloc, dealloc, and any memory marshalling helpers used by invoke_sync so the
full roundtrip is covered.
- Around line 566-612: In host_sql_v1, when the closure returns Err(error) you
should emit a warn-level log with the error details to aid diagnosing SurrealDB
transport/connectivity issues; add a tracing::warn! (or warn!(...) via use
tracing::warn) in the Err(error) arm—e.g. tracing::warn!(error = %error,
"host_sql_v1 transport error contacting SurrealDB")—before building the JSON
error payload so operators see the transport error context; keep the existing
JSON/error response behavior unchanged.

In `@clients/agent-runtime/src/memory/traits.rs`:
- Around line 16-21: The derived Default for MemoryValidationResult is
misleading because it yields valid = false; update the struct by removing
Default from the derive list and provide an explicit impl Default for
MemoryValidationResult that returns valid: true and violations: empty Vec (or
alternatively drop Default entirely to avoid accidental use); locate the
MemoryValidationResult definition and replace the derive(Default) with a custom
impl Default that sets valid = true and violations = Vec::new() so callers using
MemoryValidationResult::default() get a valid result.

In `@clients/agent-runtime/src/plugins/mod.rs`:
- Around line 1935-1994: Add a runtime_api consistency check in
resolve_memory_plugin_runtime: after deserializing PluginManifest into manifest
and after validate_plugin_version(...) compare manifest.runtime_api with the
locked.runtime_api (the value on the locked struct returned by
resolve_memory_plugin) and bail with a clear error if they differ (similar to
the existing id/version mismatch branches); include plugin_id,
locked.runtime_api and manifest.runtime_api in the error message and keep
returning Err via bail! so mismatched runtime_api fails fast before constructing
ResolvedMemoryPlugin.

In `@clients/agent-runtime/src/security/pairing.rs`:
- Around line 145-147: Replace the short-circuiting .any(...) scan with a
non-short-circuiting reduction so every stored token is compared to hashed; in
pairing.rs change the tokens.iter().any(|stored| constant_time_eq(stored,
&hashed)) usage to a fold (or explicit loop) that XORs/ORs (accumulates) the
result of constant_time_eq for each stored token and returns the final boolean,
ensuring constant_time_eq is called for every entry (refer to the tokens
variable and constant_time_eq and hashed identifiers when making the change).

Comment thread clients/agent-runtime/Cargo.toml Outdated
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/Cargo.toml
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/src/agent/agent.rs
Comment thread clients/agent-runtime/src/memory/plugin.rs Outdated
Comment thread clients/agent-runtime/src/memory/plugin.rs Outdated
Comment thread clients/agent-runtime/src/memory/plugin.rs
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Feb 22, 2026

Deploying corvus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 11ccc44
Status: ✅  Deploy successful!
Preview URL: https://52274bd5.corvus-42x.pages.dev
Branch Preview URL: https://feature-memory-surreal-graph.corvus-42x.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@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: 9

🧹 Nitpick comments (10)
.agents/skills/plugin-release/SKILL.md (2)

21-42: Add a clean working-tree precondition.

The preconditions and the "Verify local state" step lack an explicit requirement that the working tree must be clean before tagging. Without this guard, an agent could create a tag over uncommitted or unstaged changes, resulting in a release whose Cargo.toml version differs from what was actually built.

✏️ Proposed additions

Add to the Preconditions list:

 2. Plugin version in `Cargo.toml` matches tag semver.
 3. Plugin builds for `wasm32-wasip1`.
+4. Working tree is clean (`git status` shows no uncommitted changes).
-4. Catalog deployment secrets are configured in GitHub:
+5. Catalog deployment secrets are configured in GitHub:

And tighten Step 1:

 ### 1) Verify local state
 
 ```bash
 git status
 git log --oneline -10

+Stop if git status reports any uncommitted or staged changes.


</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.agents/skills/plugin-release/SKILL.md around lines 21 - 42, Update the
SKILL.md preconditions to require a clean working tree before tagging by adding
a bullet under "Preconditions" that the git working tree must have no
uncommitted or staged changes, and modify the "Verify local state" step (the git
status/git log block) to include an explicit directive: "Stop if git status
reports any uncommitted or staged changes." Reference the "Preconditions"
section and the "Verify local state" step when making the edits so the guard is
clearly visible to agents following the release flow.


</details>

---

`131-137`: **Consider adding a verification command for the runtime install check.**

Given that this PR enforces signature verification for remote plugins (per the commit notes), the last two checklist items could be more actionable with concrete commands:

<details>
<summary>✏️ Proposed addition</summary>

```diff
-- [ ] Runtime can install plugin via `corvus plugins install <plugin-id>`
-- [ ] Runtime verification passes for the installed plugin
+- [ ] Runtime can install plugin via `corvus plugins install <plugin-id>`
+- [ ] Runtime verification passes: `corvus plugins verify <plugin-id>`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/plugin-release/SKILL.md around lines 131 - 137, Update the
two checklist lines "Runtime can install plugin via corvus plugins install
<plugin-id>" and "Runtime verification passes for the installed plugin" to
include a concrete verification command sequence: instruct the releaser to run
the install command for the plugin ID and then run the runtime's verification
command (e.g., corvus plugins verify <plugin-id> or equivalent) and check for a
zero exit code and expected verification output; reference the checklist lines
verbatim so the text is replaced/extended with the concrete install + verify
commands and an instruction to assert the signature verification succeeded.
clients/agent-runtime/Cargo.toml (2)

83-84: Gate wasmi behind an optional feature flag, consistent with other specialized backends.

Every other heavy or environment-specific dependency in this file is optional and feature-gated (memory-surreal, probe, rag-pdf, browser-native, hardware). wasmi — a full WASM interpreter — is currently unconditional, meaning it's compiled into every build including minimal/size-optimized ones, working against the project's opt-level = "z" / lto = "fat" release philosophy.

Consider introducing a memory-plugin feature (mirroring memory-surreal):

♻️ Proposed refactor
-# WASM execution for plugin-backed memory backends
-wasmi = { version = "0.40", default-features = false, features = ["std"] }
+# WASM execution for plugin-backed memory backends (optional; enable with --features memory-plugin)
+wasmi = { version = "0.40", optional = true, default-features = false, features = ["std"] }

And in [features]:

 # memory-surreal = SurrealDB-backed memory backend
 memory-surreal = ["dep:surrealdb"]
+# memory-plugin = WASM plugin-backed memory backend
+memory-plugin = ["dep:wasmi"]

Based on learnings: "Preserve release-size profile assumptions in Cargo.toml and avoid adding heavy dependencies unless clearly justified" and "Do not add heavy dependencies for minor convenience; justify new crate additions."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/Cargo.toml` around lines 83 - 84, The wasmi dependency
should be made optional and moved behind a feature flag (suggested name:
memory-plugin) similar to other backends; change the wasmi entry to include
optional = true and remove unconditional compile, then add a new feature in the
[features] table (e.g., "memory-plugin" = ["wasmi"]) and ensure any crates
referencing wasmi (search for references to wasmi or the WASM execution plugin
code) are conditionalized with cfg(feature = "memory-plugin") or moved behind
the feature so builds without memory-plugin don’t compile wasmi.

198-198: Align wat version with the project's exact-pin convention for dev-dependencies.

The existing dev-deps (tempfile = "=3.25.0", criterion = { version = "=0.5.1", ... }) use exact version pins for reproducibility. wat = "1" uses a broad range and may silently pick up minor updates.

♻️ Proposed fix
-wat = "1"
+wat = "=1.219.1"   # pin to the version resolved in Cargo.lock for reproducibility
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/Cargo.toml` at line 198, The dev-dependency "wat" is
using a permissive range (wat = "1") whereas the repo pins dev-dependencies to
exact versions; change the "wat" entry in Cargo.toml from wat = "1" to an
exact-pin form (e.g., wat = "=X.Y.Z") to match the project's convention—use the
exact version found in Cargo.lock or the desired exact release instead of a
range so it aligns with other dev-deps like "tempfile" and "criterion".
clients/agent-runtime/src/agent/mod.rs (1)

8-8: Prefer pub(crate) for validation unless external crates need the module.

Since enforce_strict_validation is pub(crate) anyway, making the module pub(crate) keeps the API surface tighter without reducing in-crate usability.

Proposed change
-pub mod validation;
+pub(crate) mod validation;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/agent/mod.rs` at line 8, The module declaration
`validation` is exported publicly but its key item `enforce_strict_validation`
is already `pub(crate)`, so tighten the API by changing the module visibility
from public to crate-only: replace the `pub mod validation;` declaration with a
`pub(crate) mod validation;` and update any external usages if they exist so
only in-crate code relies on `validation::enforce_strict_validation` (no change
needed for internal callers).
clients/agent-runtime/src/memory/traits.rs (1)

99-113: Use MemoryValidationResult::default() in the trait default implementation.
Avoids repeating the same “valid + empty violations” construction.

Proposed change
     async fn validate_response(
         &self,
         _user_query: &str,
         _response: &str,
         _session_id: Option<&str>,
     ) -> anyhow::Result<MemoryValidationResult> {
-        Ok(MemoryValidationResult {
-            valid: true,
-            violations: Vec::new(),
-        })
+        Ok(MemoryValidationResult::default())
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/traits.rs` around lines 99 - 113, The
default implementation of validate_response currently constructs
MemoryValidationResult manually; replace that manual construction in the
validate_response default method with MemoryValidationResult::default() so it
returns Ok(MemoryValidationResult::default()) instead of building { valid: true,
violations: Vec::new() }, updating the async fn validate_response in the trait
to use the default constructor.
clients/agent-runtime/src/memory/plugin.rs (3)

469-480: Minor: username is not redacted in Debug output.

Line 475 prints username in the clear. Depending on the deployment, database usernames may be considered sensitive (e.g., shared or personal accounts). Consider redacting it consistently with password and token, or leave as-is if usernames are considered non-secret in this context.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 469 - 480, The Debug
implementation for SurrealSqlClient exposes the username field in cleartext;
update the impl of std::fmt::Debug for SurrealSqlClient to redact the username
like password and token (e.g., replace the .field("username", &self.username)
with a redacted placeholder), ensuring username is not printed while keeping
sql_url, namespace, and database as before.

209-225: validate_response defaults to valid=true when the plugin omits the field — fail-open by design?

Line 212: unwrap_or(true) means a plugin that returns an RPC response without a "valid" key will be treated as passing validation. If this is intentional (graceful degradation), a tracing::debug! when the field is absent would help during troubleshooting. If validation should be strict, unwrap_or(false) or an explicit error would be safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 209 - 225, The code
currently treats a missing "valid" field as true via let valid =
result.get("valid").and_then(Value::as_bool).unwrap_or(true), which causes a
fail-open behavior; change this to fail-closed by using unwrap_or(false) (or
return an explicit error) so missing validation is not treated as passing, and
update the surrounding logic that constructs MemoryValidationResult to reflect
this; if you intend graceful degradation instead, detect the absence of the
"valid" key (e.g., check result.get("valid").is_none()) and emit a
tracing::debug! noting the missing field before defaulting, while keeping the
MemoryValidationResult { valid, violations } construction intact.

314-332: Timeout on spawn_blocking does not cancel the in-flight task.

When tokio::time::timeout fires (Line 323), the spawn_blocking task keeps running — Tokio cannot cancel blocking threads. If the WASM plugin makes a long-running SQL call, the thread stays occupied until completion. This is an inherent limitation of spawn_blocking and not easily fixable, but be aware that under sustained timeouts you could exhaust the blocking thread pool.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 314 - 332, The
current use of tokio::task::spawn_blocking in invoke_with_entrypoint with
tokio::time::timeout does not cancel the underlying blocking thread when the
timeout fires (spawn_blocking / join handle cannot stop a blocking syscall), so
long-running WASM/SQL calls can exhaust the blocking thread pool; to fix,
refactor the blocking work out of spawn_blocking by making invoke_sync async (or
adding an async wrapper like invoke_async) and use tokio::spawn (or an
async-compatible plugin call) so the timed-out task can be aborted, or move
plugin execution to an external process that can be killed; update
invoke_with_entrypoint to call the new async entrypoint (e.g., invoke_async /
invoke_entrypoint_async) and await it with tokio::time::timeout instead of using
spawn_blocking, and remove or document the spawn_blocking usage to avoid
threadpool exhaustion.
clients/agent-runtime/src/memory/mod.rs (1)

320-328: Duplicate test coverage for surreal-graphs fallback.

factory_surreal_graphs_without_installed_plugin_falls_back_to_markdown (Line 320) and factory_surreal_graphs_fallback_when_plugin_missing (Line 414) exercise the same code path with identical setup and assertions. Consider removing one.

Also applies to: 414-423

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/mod.rs` around lines 320 - 328, Two tests
duplicate the same coverage:
factory_surreal_graphs_without_installed_plugin_falls_back_to_markdown and
factory_surreal_graphs_fallback_when_plugin_missing both construct a
MemoryConfig with backend "surreal-graphs", call create_memory(...), and assert
mem.name() == "markdown"; remove one of them (or consolidate into a single test)
to eliminate duplicate coverage and keep the clearer name (pick one of the two
function names) while leaving the remaining test unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/plugin-release/SKILL.md:
- Around line 64-85: Move the sentence "This triggers `publish-plugins.yml`." so
it immediately follows the git push command in the "Create release tag" section
(right after the two lines that run `git tag -a plugin/<plugin-id>/vX.Y.Z -m
"Release <plugin-id> vX.Y.Z"` and `git push origin plugin/<plugin-id>/vX.Y.Z`)
instead of remaining at the end of the recovery block; update SKILL.md in the
"Create release tag" subsection to place that sentence before the "#### Recovery
(if tag pushed with wrong version)" heading so the workflow trigger is clearly
associated with the initial push.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 115-136: alloc_v1/dealloc_v1 currently allocate a Vec with
capacity=size but dealloc_v1 uses a parameter named len which is misleading and
must equal the original capacity; update the ABI and implementation so
dealloc_v1's second parameter is clearly the original capacity (e.g., rename len
-> cap) and validate it matches expectations before calling
Vec::from_raw_parts(ptr as *mut u8, 0, cap as usize); ensure alloc_v1's
allocation and returned pointer semantics remain capacity-based and document the
contract in the function comments so callers pass the same cap used at
allocation time.
- Around line 728-816: In sql_query, before calling unsafe {
slice::from_raw_parts(response_ptr as *const u8, len) } ensure you validate that
len <= capacity (and that response_ptr > 0) to avoid UB if the host returns more
bytes than allocated; if len > capacity, deallocate the buffer
(dealloc_v1(response_ptr, i32::try_from(capacity).unwrap_or(i32::MAX))) and
return an Err with a clear message (e.g., "host sql response length exceeds
capacity"), or treat it as a resize signal similar to status=1, but always
perform the dealloc and proper error/resize handling in the sql_query function
to prevent unsafe reads.
- Around line 636-697: validate_response currently swallows DB/query errors by
calling sql_rows(...).unwrap_or_default(), which makes validation silently pass
when the rules lookup fails; change validate_response (and its callers if
necessary) to propagate query errors instead of defaulting: call
sql_rows(&rules_query, None, Some(5_000)) and map any Err into the function Err
return (using anyhow or thiserror as our project convention), returning an Err
with context like "failed querying ontology_rules" rather than treating it as an
empty rule set; update the function's Result error type accordingly and ensure
callers handle the propagated error.
- Around line 461-501: The expansion query builds ids as a Value::Array of
Value::String (expanded_ids -> id_list) which produces plain strings like
"memory_entries:abc" that SurrealDB 2.x won’t match against the record id
column; change the construction of the IN list used by expanded_query so each id
is emitted as a proper record literal or record type (e.g., convert each
expanded_id into a record literal like r"memory_entries:abc" or use
type::record(...) syntax) instead of plain strings — inspect
surreal_string_literal() for a helper or add a helper for record literals and
use it when building expanded_query (the code paths to change are where
expanded_ids is mapped into id_list and where expanded_query is formatted,
leaving sql_rows, row_to_entry and CandidateEntry usage unchanged).

In `@clients/agent-runtime/src/agent/loop_.rs`:
- Around line 1595-1604: The assistant message pushed by run_tool_call_loop is
being validated afterwards by enforce_strict_memory_validation, so history
(e.g., the interactive history object referenced as history or mem) still
contains the unvalidated response; update the same history entry after
validation by replacing the original assistant message with the
validated/corrected response (the value returned by
enforce_strict_memory_validation, currently assigned to response/final_output)
before returning from run_tool_call_loop so subsequent turns use the corrected
text (ensure you locate the push in run_tool_call_loop and replace or update
that specific assistant message rather than appending a new one).

In `@clients/agent-runtime/src/agent/validation.rs`:
- Around line 12-86: Several tracing::warn! calls in the error branches for
mem.validate_response, provider.chat_with_system, and the post-correction
validation are interpolating the raw error ({error}) which can leak sensitive
provider/memory details; replace those warn logs with generic messages that do
not include the error string (e.g., "Memory validation failed" / "Ontology
correction pass failed" / "Post-correction ontology validation failed") and, if
you need diagnostic info, send a redacted or non-sensitive representation to
tracing::debug! or use a helper like redact_error(error) before logging; update
the tracing::warn! invocations around mem.validate_response,
provider.chat_with_system, and the final match arm accordingly so no raw error
content is logged.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 993-997: The test
reject_non_loopback_wss_when_http_loopback_is_disabled is asserting that a wss
endpoint is rejected, but per the updated validation wss (TLS) should be
accepted; change the test to call parse_endpoint("wss://example.com/rpc") and
then assert validate_endpoint_security(&endpoint, false) returns Ok (e.g., use
.unwrap() or assert!(...is_ok())) instead of unwrap_err and the "non-loopback"
assertion so it expects success from validate_endpoint_security.
- Around line 809-830: The "wss" match arm incorrectly treats wss as insecure —
update the scheme handling so "wss" is accepted the same as "https": remove the
extra non-loopback restriction in the "wss" arm and ensure it returns Ok(())
unconditionally (matching the "https" arm), and fix the error message text if
any remaining checks reference "http" for a TLS scheme; update or delete the
test reject_non_loopback_wss_when_http_loopback_is_disabled accordingly. Use the
existing symbols allow_http_loopback and is_loopback_host and modify the "wss"
match arm in the scheme validation block to mirror the "https" behavior.

---

Nitpick comments:
In @.agents/skills/plugin-release/SKILL.md:
- Around line 21-42: Update the SKILL.md preconditions to require a clean
working tree before tagging by adding a bullet under "Preconditions" that the
git working tree must have no uncommitted or staged changes, and modify the
"Verify local state" step (the git status/git log block) to include an explicit
directive: "Stop if `git status` reports any uncommitted or staged changes."
Reference the "Preconditions" section and the "Verify local state" step when
making the edits so the guard is clearly visible to agents following the release
flow.
- Around line 131-137: Update the two checklist lines "Runtime can install
plugin via corvus plugins install <plugin-id>" and "Runtime verification passes
for the installed plugin" to include a concrete verification command sequence:
instruct the releaser to run the install command for the plugin ID and then run
the runtime's verification command (e.g., corvus plugins verify <plugin-id> or
equivalent) and check for a zero exit code and expected verification output;
reference the checklist lines verbatim so the text is replaced/extended with the
concrete install + verify commands and an instruction to assert the signature
verification succeeded.

In `@clients/agent-runtime/Cargo.toml`:
- Around line 83-84: The wasmi dependency should be made optional and moved
behind a feature flag (suggested name: memory-plugin) similar to other backends;
change the wasmi entry to include optional = true and remove unconditional
compile, then add a new feature in the [features] table (e.g., "memory-plugin" =
["wasmi"]) and ensure any crates referencing wasmi (search for references to
wasmi or the WASM execution plugin code) are conditionalized with cfg(feature =
"memory-plugin") or moved behind the feature so builds without memory-plugin
don’t compile wasmi.
- Line 198: The dev-dependency "wat" is using a permissive range (wat = "1")
whereas the repo pins dev-dependencies to exact versions; change the "wat" entry
in Cargo.toml from wat = "1" to an exact-pin form (e.g., wat = "=X.Y.Z") to
match the project's convention—use the exact version found in Cargo.lock or the
desired exact release instead of a range so it aligns with other dev-deps like
"tempfile" and "criterion".

In `@clients/agent-runtime/src/agent/mod.rs`:
- Line 8: The module declaration `validation` is exported publicly but its key
item `enforce_strict_validation` is already `pub(crate)`, so tighten the API by
changing the module visibility from public to crate-only: replace the `pub mod
validation;` declaration with a `pub(crate) mod validation;` and update any
external usages if they exist so only in-crate code relies on
`validation::enforce_strict_validation` (no change needed for internal callers).

In `@clients/agent-runtime/src/memory/mod.rs`:
- Around line 320-328: Two tests duplicate the same coverage:
factory_surreal_graphs_without_installed_plugin_falls_back_to_markdown and
factory_surreal_graphs_fallback_when_plugin_missing both construct a
MemoryConfig with backend "surreal-graphs", call create_memory(...), and assert
mem.name() == "markdown"; remove one of them (or consolidate into a single test)
to eliminate duplicate coverage and keep the clearer name (pick one of the two
function names) while leaving the remaining test unchanged.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 469-480: The Debug implementation for SurrealSqlClient exposes the
username field in cleartext; update the impl of std::fmt::Debug for
SurrealSqlClient to redact the username like password and token (e.g., replace
the .field("username", &self.username) with a redacted placeholder), ensuring
username is not printed while keeping sql_url, namespace, and database as
before.
- Around line 209-225: The code currently treats a missing "valid" field as true
via let valid = result.get("valid").and_then(Value::as_bool).unwrap_or(true),
which causes a fail-open behavior; change this to fail-closed by using
unwrap_or(false) (or return an explicit error) so missing validation is not
treated as passing, and update the surrounding logic that constructs
MemoryValidationResult to reflect this; if you intend graceful degradation
instead, detect the absence of the "valid" key (e.g., check
result.get("valid").is_none()) and emit a tracing::debug! noting the missing
field before defaulting, while keeping the MemoryValidationResult { valid,
violations } construction intact.
- Around line 314-332: The current use of tokio::task::spawn_blocking in
invoke_with_entrypoint with tokio::time::timeout does not cancel the underlying
blocking thread when the timeout fires (spawn_blocking / join handle cannot stop
a blocking syscall), so long-running WASM/SQL calls can exhaust the blocking
thread pool; to fix, refactor the blocking work out of spawn_blocking by making
invoke_sync async (or adding an async wrapper like invoke_async) and use
tokio::spawn (or an async-compatible plugin call) so the timed-out task can be
aborted, or move plugin execution to an external process that can be killed;
update invoke_with_entrypoint to call the new async entrypoint (e.g.,
invoke_async / invoke_entrypoint_async) and await it with tokio::time::timeout
instead of using spawn_blocking, and remove or document the spawn_blocking usage
to avoid threadpool exhaustion.

In `@clients/agent-runtime/src/memory/traits.rs`:
- Around line 99-113: The default implementation of validate_response currently
constructs MemoryValidationResult manually; replace that manual construction in
the validate_response default method with MemoryValidationResult::default() so
it returns Ok(MemoryValidationResult::default()) instead of building { valid:
true, violations: Vec::new() }, updating the async fn validate_response in the
trait to use the default constructor.

Comment thread .agents/skills/plugin-release/SKILL.md Outdated
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/src/agent/loop_.rs
Comment thread clients/agent-runtime/src/agent/validation.rs
Comment thread clients/agent-runtime/src/memory/plugin.rs
Comment thread clients/agent-runtime/src/memory/plugin.rs Outdated
Copy link
Copy Markdown
Contributor

@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: 5

🧹 Nitpick comments (7)
clients/agent-runtime/src/memory/plugin.rs (4)

756-763: category_to_string duplicates the Display impl on MemoryCategory.

MemoryCategory::Display in traits.rs (Lines 46-55) produces identical output. You could replace calls to category_to_string(&cat) with cat.to_string() and remove this function.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 756 - 763,
category_to_string duplicates the Display impl for MemoryCategory; remove the
category_to_string function and replace its call sites with the existing Display
implementation by calling to_string() on MemoryCategory (e.g., replace
category_to_string(&cat) with cat.to_string()); ensure no remaining references
to category_to_string exist and run cargo build/tests to confirm compilation
after deleting the function in plugin.rs.

871-888: Consider the same bounds check in read_memory_bytes for defense-in-depth.

While callers of read_memory_bytes already validate resp_len against max_output_bytes, adding a linear-memory bounds check here would guard against future call sites that might skip the external validation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 871 - 888, Add an
explicit linear-memory bounds check inside read_memory_bytes to defend against
out-of-range reads: after validating ptr and len non-negative and converting len
to usize, compute the end offset (ptr as usize + len_usize) and compare it
against the plugin memory's current byte length (use wasmi Memory API such as
memory.size/store page size conversion or the appropriate method to get current
memory size) and return an error/bail!("out-of-bounds memory access") if the end
offset exceeds memory length; keep the existing read call and error context
intact.

309-317: Unnecessary string clones on every invocation for entrypoint names.

self.inner.memory_entrypoint.clone() and self.inner.health_entrypoint.clone() allocate on every call. Since the executor is Clone (via Arc<Inner>), you could pass the Arc into the blocking task and read the entrypoint from inner inside. Alternatively, store entrypoints as Arc<str> for cheap cloning.

As per coding guidelines: "Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 309 - 317, The
current invoke_memory and invoke_health methods clone the entrypoint strings
every call (self.inner.memory_entrypoint.clone(),
self.inner.health_entrypoint.clone()); instead avoid per-call allocations by
either passing the Arc<Inner> into invoke_with_entrypoint and reading
inner.memory_entrypoint / inner.health_entrypoint inside the blocking task, or
change the stored types to Arc<str> so cloning is cheap; update invoke_memory
and invoke_health to stop calling clone() on memory_entrypoint/health_entrypoint
and instead forward the Arc<Inner> (self.inner) or use Arc<str> fields, and
adjust invoke_with_entrypoint signature/usage accordingly to read the entrypoint
from Inner at execution time (reference: methods invoke_memory, invoke_health,
invoke_with_entrypoint and field inner.memory_entrypoint /
inner.health_entrypoint).

591-625: host_sql_v1 error response includes raw error.to_string() sent back to the WASM plugin.

Lines 613 and 619-621: the raw error (which may include hostname/port from connection failures) is both logged and included in the JSON payload returned to the plugin. Since plugins must be trusted (per the resolution flow in mod.rs), and are sandboxed without network access, this is low risk — but worth noting for defense-in-depth.

As per coding guidelines: "clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 591 - 625,
host_sql_v1 currently includes the raw error.to_string() in the JSON payload and
logs it directly; remove raw error material from both the transport response and
high-level logs. Change the Err branch that builds payload and tracing::warn to:
(1) log only a non-sensitive, generic message via tracing::warn (or include a
sanitized/hashed error id produced by a new helper like sanitize_error(error) if
you need correlation), and (2) populate the JSON payload with a generic
error.message (e.g. "internal server error" or the error code "HOST_SQL_ERROR")
instead of error.to_string(); keep detailed error details only in a
secure/internal debug log or a scrubbed/sanitized form if required. Ensure you
update the construction site around host_sql_v1, the outcome Err arm, and the
serde_json::to_vec call that creates the response.
clients/agent-runtime/src/agent/validation.rs (1)

36-41: Consider bounding the correction prompt size.

If candidate or violations_text is very large, the correction prompt could exceed provider token limits or become expensive. Consider truncating them to a reasonable ceiling before formatting the prompt.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/agent/validation.rs` around lines 36 - 41, The
correction_prompt construction can blow up if candidate or violations_text are
huge; modify the code around correction_prompt to truncate candidate and
violations_text to a safe ceiling before formatting (e.g., implement a helper
like truncate_to_max_chars_or_tokens and apply it to candidate and
violations_text), keeping user_query intact or also capped if needed, and then
build correction_prompt from the truncated strings; ensure the truncation
preserves the end or start of the text as appropriate and document the chosen
max length constant.
clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs (2)

251-338: Destructure request before the match to avoid the six args.clone() calls.

Each arm calls request.args.clone() and request.request_id.clone(). Destructuring first lets the arms take args and request_id by move.

♻️ Proposed refactor
-    let result = match request.op.as_str() {
-        "store" => {
-            let args: StoreArgs =
-                serde_json::from_value(request.args.clone()).map_err(|error| {
-                    (
-                        request.request_id.clone(),
-                        "INVALID_ARGS".to_string(),
-                        format!("store args invalid: {error}"),
-                    )
-                })?;
+    let PluginRequest { request_id, op, args } = request;
+    let result = match op.as_str() {
+        "store" => {
+            let args: StoreArgs = serde_json::from_value(args).map_err(|error| {
+                    (
+                        request_id.clone(),
+                        "INVALID_ARGS".to_string(),
+                        format!("store args invalid: {error}"),
+                    )
+                })?;
             // ... repeat for the other arms, replacing request.args.clone() → args
             //     and request.request_id.clone() → request_id.clone()

Based on learnings: avoid unnecessary allocations and clones to maintain performance and efficiency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
251 - 338, Destructure the incoming Request before the match to avoid repeatedly
cloning; e.g., bind let Request { op, args, request_id, .. } = request; (or
similar) and then match on op (or op.as_str()) using the moved args and
request_id so each branch uses args and request_id by value instead of calling
request.args.clone() and request.request_id.clone() in the "store", "recall",
"get", "list", "forget", and "validate_response" arms; ensure any further uses
that need owned values take ownership or clone only when truly necessary.

600-602: Hard-coded LIMIT 1000 can silently hit MAX_OUTPUT_BYTES and return error code 8.

ListArgs has no limit field. With content-heavy entries, 1000 rows easily exceeds 256 KB, and callers receive a bare integer error code with no message. Consider adding an optional limit (defaulting to a safe constant ≤ MAX_RECALL_LIMIT) to ListArgs, similar to RecallArgs.

♻️ Proposed refactor
 #[derive(Debug, Deserialize)]
 struct ListArgs {
     #[serde(default)]
     category: Option<String>,
     #[serde(default)]
     session_id: Option<String>,
+    #[serde(default)]
+    limit: Option<usize>,
 }
-    let query =
-        format!("SELECT * FROM memory_entries{where_clause} ORDER BY updated_at DESC LIMIT 1000;");
+    let limit = args.limit.unwrap_or(MAX_RECALL_LIMIT).clamp(1, MAX_RECALL_LIMIT);
+    let query =
+        format!("SELECT * FROM memory_entries{where_clause} ORDER BY updated_at DESC LIMIT {limit};");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
600 - 602, The query uses a hard-coded "LIMIT 1000" which can exceed
MAX_OUTPUT_BYTES and cause silent error code 8; add an optional limit field to
ListArgs (like RecallArgs) with a default safe constant (<= MAX_RECALL_LIMIT),
validate/cap incoming limits against MAX_RECALL_LIMIT, and replace the
hard-coded "LIMIT 1000" in the query construction (the `query` string that
selects from `memory_entries`) with the validated limit; ensure callers that
omit the new ListArgs.limit get the default and keep using `sql_rows(&query,
None, Some(6_000))?` unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/plugin-release/SKILL.md:
- Around line 94-101: Clarify whether the <version> placeholder in the artifact
path artifacts/<plugin-id>/<version>/<plugin-id>.wasm uses the v-prefixed tag
(v<semver>, e.g., v0.2.0) or the bare semver from Cargo.toml (e.g., 0.2.0);
update SKILL.md to explicitly state which format the release workflow produces
(match the tag format used in the repository’s release/tagging) and give one
concrete example using artifacts/<plugin-id>/... with the correct form so
operators know whether to look for artifacts/<plugin-id>/v0.2.0/... or
artifacts/<plugin-id>/0.2.0/....
- Line 95: Clarify the ambiguity in the artifact path
`artifacts/<plugin-id>/<version>/<plugin-id>.wasm` by stating whether
`<version>` includes the `v` prefix used in the tag format `v<semver>` or the
bare semver from `Cargo.toml`; update SKILL.md near the listed artifact path
(and optionally add a parenthetical example) to explicitly say that `<version>`
is the bare semver (e.g., `0.2.0`) and not the tag-prefixed form `v0.2.0`.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 627-640: The forget_memory function currently ignores errors from
the two SQL deletions (the sql_query calls for delete_entry_query and
delete_edges_query) and always returns Ok(true); change this so you capture and
handle the Result from each sql_query: call sql_query for delete_entry_query and
propagate errors (using ? or matching) or return Ok(false) when deletions report
failure, then do the same for the delete_edges_query; ensure the function
returns an error or false on failure instead of unconditionally Ok(true) so
callers get accurate deletion status.
- Around line 224-228: The current sequence calls write_i32(resp_ptr_out,
resp_ptr) then write_i32(resp_len_out, resp_len_i32) and if the second fails the
host has already received resp_ptr; instead, pre-validate both output slots
before writing: add/use a small helper to assert both resp_ptr_out and
resp_len_out are writable (e.g., validate_write_i32_slot or similar) and if
validation fails dealloc_v1(resp_ptr, resp_len_i32) and return the error,
otherwise perform both write_i32 calls; reference: write_i32, resp_ptr_out,
resp_len_out, dealloc_v1, resp_ptr, resp_len_i32.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 920-937: The function read_caller_memory_bytes currently allocates
bytes from the untrusted len before validating against the caller's linear
memory; change it to first get the caller memory size via
memory.data_size(caller), verify ptr as usize <= data_size (bail if out of
bounds), compute available = data_size - ptr as usize, cap len_usize =
len_usize.min(available) (or bail if you prefer zero-length), then allocate
vec![0u8; len_usize] and proceed to memory.read; apply the same defensive cap to
read_memory_bytes as well.

---

Duplicate comments:
In @.agents/skills/plugin-release/SKILL.md:
- Line 74: The placement of the line "This triggers `publish-plugins.yml`." is
correct — it should remain immediately after the `git push` command and before
the Recovery block; no code change required, just mark the review comment as
resolved and remove the duplicate reviewer tag or duplicate comment entry if
present in the review thread referencing that exact string.

---

Nitpick comments:
In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 251-338: Destructure the incoming Request before the match to
avoid repeatedly cloning; e.g., bind let Request { op, args, request_id, .. } =
request; (or similar) and then match on op (or op.as_str()) using the moved args
and request_id so each branch uses args and request_id by value instead of
calling request.args.clone() and request.request_id.clone() in the "store",
"recall", "get", "list", "forget", and "validate_response" arms; ensure any
further uses that need owned values take ownership or clone only when truly
necessary.
- Around line 600-602: The query uses a hard-coded "LIMIT 1000" which can exceed
MAX_OUTPUT_BYTES and cause silent error code 8; add an optional limit field to
ListArgs (like RecallArgs) with a default safe constant (<= MAX_RECALL_LIMIT),
validate/cap incoming limits against MAX_RECALL_LIMIT, and replace the
hard-coded "LIMIT 1000" in the query construction (the `query` string that
selects from `memory_entries`) with the validated limit; ensure callers that
omit the new ListArgs.limit get the default and keep using `sql_rows(&query,
None, Some(6_000))?` unchanged.

In `@clients/agent-runtime/src/agent/validation.rs`:
- Around line 36-41: The correction_prompt construction can blow up if candidate
or violations_text are huge; modify the code around correction_prompt to
truncate candidate and violations_text to a safe ceiling before formatting
(e.g., implement a helper like truncate_to_max_chars_or_tokens and apply it to
candidate and violations_text), keeping user_query intact or also capped if
needed, and then build correction_prompt from the truncated strings; ensure the
truncation preserves the end or start of the text as appropriate and document
the chosen max length constant.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 756-763: category_to_string duplicates the Display impl for
MemoryCategory; remove the category_to_string function and replace its call
sites with the existing Display implementation by calling to_string() on
MemoryCategory (e.g., replace category_to_string(&cat) with cat.to_string());
ensure no remaining references to category_to_string exist and run cargo
build/tests to confirm compilation after deleting the function in plugin.rs.
- Around line 871-888: Add an explicit linear-memory bounds check inside
read_memory_bytes to defend against out-of-range reads: after validating ptr and
len non-negative and converting len to usize, compute the end offset (ptr as
usize + len_usize) and compare it against the plugin memory's current byte
length (use wasmi Memory API such as memory.size/store page size conversion or
the appropriate method to get current memory size) and return an
error/bail!("out-of-bounds memory access") if the end offset exceeds memory
length; keep the existing read call and error context intact.
- Around line 309-317: The current invoke_memory and invoke_health methods clone
the entrypoint strings every call (self.inner.memory_entrypoint.clone(),
self.inner.health_entrypoint.clone()); instead avoid per-call allocations by
either passing the Arc<Inner> into invoke_with_entrypoint and reading
inner.memory_entrypoint / inner.health_entrypoint inside the blocking task, or
change the stored types to Arc<str> so cloning is cheap; update invoke_memory
and invoke_health to stop calling clone() on memory_entrypoint/health_entrypoint
and instead forward the Arc<Inner> (self.inner) or use Arc<str> fields, and
adjust invoke_with_entrypoint signature/usage accordingly to read the entrypoint
from Inner at execution time (reference: methods invoke_memory, invoke_health,
invoke_with_entrypoint and field inner.memory_entrypoint /
inner.health_entrypoint).
- Around line 591-625: host_sql_v1 currently includes the raw error.to_string()
in the JSON payload and logs it directly; remove raw error material from both
the transport response and high-level logs. Change the Err branch that builds
payload and tracing::warn to: (1) log only a non-sensitive, generic message via
tracing::warn (or include a sanitized/hashed error id produced by a new helper
like sanitize_error(error) if you need correlation), and (2) populate the JSON
payload with a generic error.message (e.g. "internal server error" or the error
code "HOST_SQL_ERROR") instead of error.to_string(); keep detailed error details
only in a secure/internal debug log or a scrubbed/sanitized form if required.
Ensure you update the construction site around host_sql_v1, the outcome Err arm,
and the serde_json::to_vec call that creates the response.

Comment thread .agents/skills/plugin-release/SKILL.md
Comment thread .agents/skills/plugin-release/SKILL.md Outdated
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs Outdated
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/src/memory/plugin.rs
Copy link
Copy Markdown
Contributor

@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: 8

🧹 Nitpick comments (6)
clients/agent-runtime/src/agent/validation.rs (2)

25-32: Error details are fully discarded — safe but consider tracing::debug! for diagnostics.

All three error branches correctly avoid logging sensitive content per the coding guidelines. However, binding the error and logging it at debug! level would preserve diagnostic value for local troubleshooting without exposing it in production warn-level logs.

This is a minor observability tradeoff and fine to leave as-is if the team prefers minimal log surface.

Also applies to: 71-78, 98-102

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/agent/validation.rs` around lines 25 - 32, Bind the
error returned by mem.validate_response (replace Err(_) with Err(err)) and emit
the error details at debug level (e.g., tracing::debug!("Memory validation
failed: {:?}", err)); keep the existing tracing::warn! message and the same
user-facing return string intact. Apply the same change to the other
validate_response match sites referenced (the blocks around the other Err(_)
cases) so all three places log diagnostics at debug level while still not
exposing error details at warn level.

38-47: Duplicated violations formatting logic.

The pattern of formatting Vec<String> violations into a bulleted list (check if empty → map with "- " prefix → join with "\n") appears twice. A small helper would reduce duplication.

Proposed helper
+fn format_violations(violations: &[String], fallback: &str) -> String {
+    if violations.is_empty() {
+        format!("- {fallback}")
+    } else {
+        violations.iter().map(|v| format!("- {v}")).collect::<Vec<_>>().join("\n")
+    }
+}

Then use it in both call sites:

-    let violations_text = if validation.violations.is_empty() {
-        "- unknown ontology violation".to_string()
-    } else {
-        validation
-            .violations
-            .iter()
-            .map(|item| format!("- {item}"))
-            .collect::<Vec<_>>()
-            .join("\n")
-    };
+    let violations_text = format_violations(&validation.violations, "unknown ontology violation");

Also applies to: 82-92

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/agent/validation.rs` around lines 38 - 47, Extract
the repeated "check empty → prefix each item with \"- \" → join with \"\n\""
logic into a small helper function (e.g. format_bulleted_violations or
format_violations) and use it wherever you currently build violations_text from
validation.violations; specifically replace the logic that constructs
violations_text (which inspects validation.violations and maps to "- {item}"
then joins) and the similar block used later (the other call site that formats
validation.violations) with calls to the new helper so the formatting is
centralized.
clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs (4)

505-545: extract_terms(query) is called twice with the same input in recall_memory.

The first call drives the ontology expansion (line 505) and the second builds the term set for scoring (line 545). Both operate on the same query string. Computing once and reusing avoids the redundant allocation.

♻️ Proposed refactor
+    let query_terms = extract_terms(query);
+
     let mut expanded_ids = HashSet::new();
-    for term in extract_terms(query) {
+    for term in &query_terms {
         ...
     }
     ...
-    let terms = extract_terms(query);
+    let terms = query_terms;
     let mut ranked: Vec<EntryOut> = candidates
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
505 - 545, The function recall_memory currently calls extract_terms(query) twice
— once for ontology expansion and again later for scoring — causing redundant
allocation; compute let terms = extract_terms(query) once before the first loop
and reuse that same terms variable in both the ontology-expansion loop (the for
term in ... block that builds term_hash/term_record_id and runs edge_lookup) and
the later scoring logic instead of calling extract_terms(query) again, ensuring
you remove the second call and reference the precomputed terms wherever needed
(look for extract_terms usages inside recall_memory to replace).

233-237: write_i32 error branch (lines 233–237) is unreachable after the pre-validation above.

validate_write_i32_slot (lines 226–231) already rejects ptr <= 0; write_i32 checks the same condition before performing copy_nonoverlapping, which traps rather than returning an Err in WASM when the address is out-of-bounds. The second dealloc + return block is dead code. Removing it reduces noise and makes it clear that the allocation is only conditionally freed.

♻️ Proposed cleanup
-    if write_i32(resp_ptr_out, resp_ptr).is_err() || write_i32(resp_len_out, resp_len_i32).is_err()
-    {
-        dealloc_v1(resp_ptr, resp_len_i32);
-        return 7;
-    }
+    // Safety: both slot pointers validated above.
+    let _ = write_i32(resp_ptr_out, resp_ptr);
+    let _ = write_i32(resp_len_out, resp_len_i32);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
233 - 237, The error-handling branch that calls dealloc_v1(resp_ptr,
resp_len_i32) and returns 7 after attempting write_i32 is dead/unnecessary
because validate_write_i32_slot already rejects non-positive pointers and
write_i32 will trap on out-of-bounds rather than return Err; remove that
unreachable if-branch and its dealloc+return so the allocation free is only done
in the existing conditional paths and the code relies on validate_write_i32_slot
and write_i32 behavior; update/remove references to resp_ptr/resp_len_out in
that branch and keep a single clear allocation-free path.

433-442: Prefer parameterized queries via vars over string-interpolated literals throughout.

HostSqlRequest already carries a vars: Option<Value> field, but every call-site passes vars: None and builds queries by interpolating surreal_string_literal(…) values. While the JSON-based escaping is safe against injection, parameterized queries are the idiomatic approach: they remove the escaping surface entirely, produce cleaner SQL, and make queries easier to test and inspect. The same pattern applies to all sql_query / sql_rows call sites in this file (recall_memory, forget_memory, etc.).

Example for store_memory:

♻️ Illustrative refactor for one query
-    let upsert_query = format!(
-        "UPSERT type::thing(\"memory_entries\", {id}) CONTENT {{ key: {key}, ... }};",
-        id = surreal_string_literal(&key_hash),
-        key = surreal_string_literal(key),
-        ...
-    );
-    sql_query(&upsert_query, None, Some(5_000))?;
+    let upsert_query = "UPSERT type::thing('memory_entries', $id) CONTENT { key: $key, content: $content, category: $category, timestamp: $ts, updated_at: $ts, session_id: $sid };";
+    sql_query(upsert_query, Some(json!({
+        "id": key_hash,
+        "key": key,
+        "content": content,
+        "category": category,
+        "ts": now,
+        "sid": args.session_id,
+    })), Some(5_000))?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
433 - 442, The upsert in store_memory (the formatted upsert_query using
surreal_string_literal) should be converted to a parameterized query: replace
the interpolated values with named placeholders (e.g., $id, $key, $content,
$category, $timestamp, $updated_at, $session_id), build a serde_json::Value map
for these variables and pass it as HostSqlRequest.vars: Some(vars) instead of
vars: None, and stop calling surreal_string_literal for those fields; apply the
same refactor pattern to the other sql_query/sql_rows call sites in this file
(recall_memory, forget_memory, etc.) so all queries use vars and placeholders
and avoid string interpolation.

391-415: Schema initialisation failure is permanent for the lifetime of the WASM instance.

OnceLock::get_or_init runs the closure exactly once and caches the outcome. If the DB is transiently unavailable during the first request, every subsequent request for this instance returns SCHEMA_INIT_FAILED without retrying — the only recovery path is to restart (or reload) the WASM instance.

Consider documenting this restart-required failure mode explicitly in the plugin manifest or in the error message returned to the caller, so the host can detect it and evict/reload the instance rather than retrying indefinitely on the same broken one.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
391 - 415, The schema init currently caches any failure permanently via
SCHEMA_INIT_RESULT.get_or_init in ensure_schema, so update ensure_schema (and
the closure passed to get_or_init) to wrap and return errors that explicitly
state this is a permanent WASM-instance-level initialization failure and
recommend host eviction/reload (e.g., include text like "schema bootstrap
failed; this instance must be restarted/evicted"), and ensure sql_query error
branches (the closure used by get_or_init) propagate that augmented message so
callers of ensure_schema can detect the restart-required condition; reference
ensure_schema, SCHEMA_INIT_RESULT, get_or_init, and sql_query when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/plugin-release/SKILL.md:
- Around line 76-88: Update the "Recovery (if tag pushed with wrong version)"
section in SKILL.md to include cleanup steps for the race window when
publish-plugins.yml ran before the tag was deleted: instruct operators to
inspect the GitHub Actions run for the wrong tag, remove any published
Cloudflare Pages artifact at the immutable path (e.g.,
artifacts/<plugin-id>/<wrong-version>/) via the Cloudflare Pages dashboard or
Pages API, and revert any catalog.json or manifest upsert that points to the
wrong version before re-tagging and republishing; place this note adjacent to
the existing git tag delete instructions so it's visible during recovery.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 361-370: read_input currently allows req_ptr == 0 which leads to
unsafe reads from address 0; make its null-pointer check consistent with other
functions by rejecting ptr <= 0. Update read_input to return Err when req_ptr <=
0 (matching write_i32 and validate_write_i32_slot behavior) and include the same
error message format ("negative request pointer/length" or similar) so callers
of read_input, write_i32, and validate_write_i32_slot behave consistently when
given a zero or negative pointer.
- Around line 527-531: In recall_memory, expanded_query is missing the session
filter so ontology-expanded ids can return entries from other sessions; modify
the expanded_query construction (the format call that builds "SELECT * FROM
memory_entries WHERE id IN [{ids}] LIMIT {limit};") to include the same
session_filter predicate used by keyword_query (and incorporate session_id when
present) — e.g., append the session_filter condition to the WHERE clause or
inject the session predicate into the format args so expanded_query enforces the
session constraint just like keyword_query does.
- Around line 714-715: The SELECT used to build rules_query in validate_response
currently appends session_filter which excludes global rules (those without
session_id) because NONE = "…" is false; update the query construction in
validate_response (the rules_query/ session_filter logic) so the WHERE clause
allows either session_id IS NONE OR session_id = "<session>" while still
requiring enabled = true (i.e., build WHERE enabled = true AND (session_id IS
NONE OR session_id = "<session>") LIMIT 200) so global ontology_rules are
returned alongside per-session rules.
- Around line 458-465: store_memory currently issues a CREATE for memory_edges
which inserts a new auto-ID row each call; change the edge creation so it
upserts a deterministic edge ID (or does a SELECT-and-CREATE-if-not-exists) to
prevent duplicate edges. In the store_memory logic that builds edge_query (the
variable named edge_query near the sql_query call) compute a deterministic ID
from the edge key components (e.g., concatenate relation_type, from_id, to_id
and session_id using surreal_string_literal/option_literal helpers), then emit
an UPSERT (or UPSERT type::thing(deterministic_id) / SELECT ... THEN CREATE IF
NOT EXISTS) for memory_edges with that deterministic ID and the same CONTENT
payload, and keep the existing sql_query call and timeout. Reference
functions/vars: store_memory, edge_query, memory_edges, surreal_string_literal,
option_literal, recall_memory (ensure recall still works with dedup).
- Around line 858-863: In the status==1 (resize) path you're setting capacity =
len even when len == 0 which causes repeated zero-length allocations and
exhausting retries; change the resize logic so that if len == 0 you pick a
non-zero capacity (e.g. capacity = std::cmp::max(1, len) or capacity =
capacity.saturating_add(1)) before continuing, and only dealloc_v1(response_ptr,
...) when a non-zero capacity was previously allocated; update the block that
touches response_ptr, dealloc_v1, capacity, and len so capacity never becomes
zero on a resize retry.

In `@clients/agent-runtime/src/agent/validation.rs`:
- Around line 49-57: The correction prompt currently truncates candidate and
violations_text but not user_query, which can let very large queries bloat the
prompt; add a new truncation limit constant (e.g., MAX_PROMPT_USER_QUERY_CHARS)
and apply truncate_for_prompt to user_query (create a truncated_user_query
variable) and use that truncated_user_query in the format! call that builds
correction_prompt (refer to truncate_for_prompt and the correction_prompt
construction in validation.rs).

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 325-343: The semaphore permit (obtained via
invocation_gate.acquire_owned()) is being moved into the spawn_blocking closure
(let _permit = permit) so it is held until the blocking task finishes even if
tokio::time::timeout returns; remove moving the permit into the closure so the
permit stays in the async scope and is dropped on timeout. Concretely, in the
code around invocation_gate.acquire_owned(), keep the permit variable in the
outer async scope and do not reference or move it into the closure passed to
tokio::task::spawn_blocking (remove or avoid the line let _permit = permit
inside that closure); leave only the executor and request moved into the closure
so timeout will drop the permit on error while the blocking invoke_sync call
(executor.invoke_sync(...)) continues independently.

---

Duplicate comments:
In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 827-836: The error messages in the "ws" match arm of the SurrealDB
URL validation reuse text referring to "http", which is misleading for WebSocket
URLs; update the bail!() messages inside the "ws" arm (in
clients/agent-runtime/src/memory/plugin.rs) to reference "ws" or "WebSocket"
instead of "http" — e.g., change "http is disabled" to "ws is disabled" (or
"WebSocket is disabled") and adjust the non-loopback message to say "refusing
insecure SurrealDB URL over ws for non-loopback host '{host}'" so both checks
correctly describe the ws scheme.

---

Nitpick comments:
In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 505-545: The function recall_memory currently calls
extract_terms(query) twice — once for ontology expansion and again later for
scoring — causing redundant allocation; compute let terms = extract_terms(query)
once before the first loop and reuse that same terms variable in both the
ontology-expansion loop (the for term in ... block that builds
term_hash/term_record_id and runs edge_lookup) and the later scoring logic
instead of calling extract_terms(query) again, ensuring you remove the second
call and reference the precomputed terms wherever needed (look for extract_terms
usages inside recall_memory to replace).
- Around line 233-237: The error-handling branch that calls dealloc_v1(resp_ptr,
resp_len_i32) and returns 7 after attempting write_i32 is dead/unnecessary
because validate_write_i32_slot already rejects non-positive pointers and
write_i32 will trap on out-of-bounds rather than return Err; remove that
unreachable if-branch and its dealloc+return so the allocation free is only done
in the existing conditional paths and the code relies on validate_write_i32_slot
and write_i32 behavior; update/remove references to resp_ptr/resp_len_out in
that branch and keep a single clear allocation-free path.
- Around line 433-442: The upsert in store_memory (the formatted upsert_query
using surreal_string_literal) should be converted to a parameterized query:
replace the interpolated values with named placeholders (e.g., $id, $key,
$content, $category, $timestamp, $updated_at, $session_id), build a
serde_json::Value map for these variables and pass it as HostSqlRequest.vars:
Some(vars) instead of vars: None, and stop calling surreal_string_literal for
those fields; apply the same refactor pattern to the other sql_query/sql_rows
call sites in this file (recall_memory, forget_memory, etc.) so all queries use
vars and placeholders and avoid string interpolation.
- Around line 391-415: The schema init currently caches any failure permanently
via SCHEMA_INIT_RESULT.get_or_init in ensure_schema, so update ensure_schema
(and the closure passed to get_or_init) to wrap and return errors that
explicitly state this is a permanent WASM-instance-level initialization failure
and recommend host eviction/reload (e.g., include text like "schema bootstrap
failed; this instance must be restarted/evicted"), and ensure sql_query error
branches (the closure used by get_or_init) propagate that augmented message so
callers of ensure_schema can detect the restart-required condition; reference
ensure_schema, SCHEMA_INIT_RESULT, get_or_init, and sql_query when making the
change.

In `@clients/agent-runtime/src/agent/validation.rs`:
- Around line 25-32: Bind the error returned by mem.validate_response (replace
Err(_) with Err(err)) and emit the error details at debug level (e.g.,
tracing::debug!("Memory validation failed: {:?}", err)); keep the existing
tracing::warn! message and the same user-facing return string intact. Apply the
same change to the other validate_response match sites referenced (the blocks
around the other Err(_) cases) so all three places log diagnostics at debug
level while still not exposing error details at warn level.
- Around line 38-47: Extract the repeated "check empty → prefix each item with
\"- \" → join with \"\n\"" logic into a small helper function (e.g.
format_bulleted_violations or format_violations) and use it wherever you
currently build violations_text from validation.violations; specifically replace
the logic that constructs violations_text (which inspects validation.violations
and maps to "- {item}" then joins) and the similar block used later (the other
call site that formats validation.violations) with calls to the new helper so
the formatting is centralized.

Comment thread .agents/skills/plugin-release/SKILL.md
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs Outdated
Comment thread clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs
Comment thread clients/agent-runtime/src/agent/validation.rs
Comment thread clients/agent-runtime/src/memory/plugin.rs
Copy link
Copy Markdown
Contributor

@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 (5)
clients/agent-runtime/src/memory/plugin.rs (3)

488-510: Credential redaction in Debug impl looks good.

Manual Debug implementation correctly redacts password and token. Note that username (line 505) is also redacted — typically usernames aren't considered secrets. Consider showing the actual username value (like sql_url, namespace, database) to improve debuggability, while keeping password and token redacted.

♻️ Optional: show username for better diagnostics
 impl std::fmt::Debug for SurrealSqlClient {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         f.debug_struct("SurrealSqlClient")
             .field("sql_url", &self.sql_url)
             .field("namespace", &self.namespace)
             .field("database", &self.database)
-            .field("username", &"<redacted>")
+            .field("username", &self.username)
             .field("password", &"<redacted>")
             .field("token", &"<redacted>")
             .finish_non_exhaustive()
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 488 - 510, The Debug
implementation for SurrealSqlClient currently redacts the username like
password/token; update the impl for SurrealSqlClient (the fmt method) to print
the actual username value instead of the literal "<redacted>" (e.g., change
.field("username", &"<redacted>") to .field("username", &self.username)) while
keeping password and token redacted and preserving finish_non_exhaustive().

933-960: Same silent-truncation concern applies to read_caller_memory_bytes.

The requested_len.min(available) on line 954 silently truncates plugin-provided request data. A plugin claiming a request length larger than its own linear memory is a programming error — failing explicitly would surface bugs faster.

♻️ Bail on out-of-bounds instead of silent truncation
     #[allow(clippy::cast_sign_loss)]
     let requested_len = len as usize;
     let available = data_size - ptr_usize;
-    let len_usize = requested_len.min(available);
+    if requested_len > available {
+        bail!(
+            "host import request length ({requested_len}) exceeds available memory ({available}) at offset {ptr_usize}"
+        );
+    }
+    let len_usize = requested_len;
     let mut bytes = vec![0u8; len_usize];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 933 - 960, The
function read_caller_memory_bytes silently truncates when a plugin requests more
bytes than remain in its linear memory; change the logic to detect requested_len
> available and return an error (bail!) instead of using
requested_len.min(available). Specifically, in read_caller_memory_bytes (and
where ptr_usize and requested_len are computed) replace the min-based truncation
with an explicit check that bails with a clear message like "requested
out-of-bounds read" when requested_len exceeds available, so the subsequent
memory.read call only runs with a verified in-bounds length.

874-901: read_memory_bytes silently truncates when requested_len > available.

If the plugin reports a response length larger than the remaining linear memory from ptr, this function silently returns fewer bytes (line 895). Downstream JSON deserialization will fail with a confusing parse error rather than a clear bounds-violation message. Consider bailing instead of truncating, since a mismatch here signals a buggy/corrupt plugin.

♻️ Bail on out-of-bounds instead of silent truncation
     #[allow(clippy::cast_sign_loss)]
     let requested_len = len as usize;
     let available = data_size - ptr_usize;
-    let len_usize = requested_len.min(available);
+    if requested_len > available {
+        bail!(
+            "plugin response length ({requested_len}) exceeds available memory ({available}) at offset {ptr_usize}"
+        );
+    }
+    let len_usize = requested_len;
     let mut bytes = vec![0u8; len_usize];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/memory/plugin.rs` around lines 874 - 901, The
function read_memory_bytes currently silently truncates when the plugin's
requested_len exceeds available bytes; update read_memory_bytes to detect if
requested_len (len as usize) > available and return an explicit error (bail!)
instead of using requested_len.min(available) and returning fewer bytes. Locate
the ptr_usize/requested_len/available logic in read_memory_bytes and replace the
min-based truncation with a bounds check that bails with a clear out-of-bounds
message (include requested and available sizes for clarity) before calling
memory.read.
clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs (2)

639-684: forget_memory edge-delete failure blocks the overall operation.

The entry delete (line 661-668) correctly checks success. However, edge deletion (lines 674-681) also returns Ok(false) on failure, even though the entry itself has already been deleted. This is stricter than necessary — orphaned edges from a past-deleted entry are harmless (they won't match anything in recall_memory), and a transient DB hiccup during edge cleanup would cause the caller to see deleted=false despite the entry being gone.

Consider making edge cleanup best-effort:

♻️ Make edge cleanup best-effort
     let delete_edges_query = format!(
         "DELETE memory_edges WHERE from_id = {id} OR to_id = {id};",
         id = surreal_string_literal(&entry_record_id),
     );
-    let delete_edges_payload = sql_query(&delete_edges_query, None, Some(4_000))?;
-    if !delete_edges_payload
-        .get("ok")
-        .and_then(Value::as_bool)
-        .unwrap_or(false)
-    {
-        return Ok(false);
-    }
+    // Edge cleanup is best-effort; orphaned edges are harmless.
+    let _ = sql_query(&delete_edges_query, None, Some(4_000));
 
     Ok(true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
639 - 684, In forget_memory, the edge deletion (delete_edges_query /
delete_edges_payload via sql_query) currently aborts and returns Ok(false) if
the edge-delete response isn't "ok", which incorrectly makes the whole operation
fail even after the entry has been removed; change this to best-effort: after
running sql_query for delete_edges_query, do not return Ok(false) on a non-ok
payload — instead log the failure (or ignore it) and proceed to return Ok(true)
since the primary entry deletion (delete_entry_query) already succeeded. Ensure
you update the handling around delete_edges_payload so failures do not cause the
function to report the deletion as unsuccessful.

913-938: extract_terms excludes all tokens shorter than 4 characters.

The minimum-length filter on line 924 drops potentially important short domain terms (e.g., "API", "SQL", "GPU", "RSS", "JWT"). If the ontology contains such terms, they'll never match during recall or validation.

A lower bound of 3 might be more appropriate, or consider allowing short terms if they appear in the ontology.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs` around lines
913 - 938, The extract_terms function currently filters out any token shorter
than 4, which drops valid short domain tokens; change the length check from if
normalized.len() < 4 to if normalized.len() < 3 to allow 3-char tokens, and
optionally extend extract_terms to accept a whitelist of ontology terms (e.g.,
add a parameter ontology_terms: &HashSet<String>) and move the length check
after a whitelist membership test so tokens present in ontology_terms are
preserved even if shorter than 3; keep references to MAX_TERMS, stop_words, and
the unique HashSet behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/plugin-release/SKILL.md:
- Around line 100-103: The docs in SKILL.md list signature and certificate
filenames without their artifact directories, which is misleading; update the
two entries that currently show `<plugin-id>.wasm.sig` and
`<plugin-id>.wasm.pem` to include the full generated paths
`artifacts/<plugin-id>/<version>/<plugin-id>.wasm.sig` and
`artifacts/<plugin-id>/<version>/<plugin-id>.wasm.pem` so they match the actual
workflow output, ensuring any mention of those filenames elsewhere in the file
is similarly updated.

---

Nitpick comments:
In `@clients/agent-runtime/plugins/memory-surreal-graphs/src/lib.rs`:
- Around line 639-684: In forget_memory, the edge deletion (delete_edges_query /
delete_edges_payload via sql_query) currently aborts and returns Ok(false) if
the edge-delete response isn't "ok", which incorrectly makes the whole operation
fail even after the entry has been removed; change this to best-effort: after
running sql_query for delete_edges_query, do not return Ok(false) on a non-ok
payload — instead log the failure (or ignore it) and proceed to return Ok(true)
since the primary entry deletion (delete_entry_query) already succeeded. Ensure
you update the handling around delete_edges_payload so failures do not cause the
function to report the deletion as unsuccessful.
- Around line 913-938: The extract_terms function currently filters out any
token shorter than 4, which drops valid short domain tokens; change the length
check from if normalized.len() < 4 to if normalized.len() < 3 to allow 3-char
tokens, and optionally extend extract_terms to accept a whitelist of ontology
terms (e.g., add a parameter ontology_terms: &HashSet<String>) and move the
length check after a whitelist membership test so tokens present in
ontology_terms are preserved even if shorter than 3; keep references to
MAX_TERMS, stop_words, and the unique HashSet behavior unchanged.

In `@clients/agent-runtime/src/memory/plugin.rs`:
- Around line 488-510: The Debug implementation for SurrealSqlClient currently
redacts the username like password/token; update the impl for SurrealSqlClient
(the fmt method) to print the actual username value instead of the literal
"<redacted>" (e.g., change .field("username", &"<redacted>") to
.field("username", &self.username)) while keeping password and token redacted
and preserving finish_non_exhaustive().
- Around line 933-960: The function read_caller_memory_bytes silently truncates
when a plugin requests more bytes than remain in its linear memory; change the
logic to detect requested_len > available and return an error (bail!) instead of
using requested_len.min(available). Specifically, in read_caller_memory_bytes
(and where ptr_usize and requested_len are computed) replace the min-based
truncation with an explicit check that bails with a clear message like
"requested out-of-bounds read" when requested_len exceeds available, so the
subsequent memory.read call only runs with a verified in-bounds length.
- Around line 874-901: The function read_memory_bytes currently silently
truncates when the plugin's requested_len exceeds available bytes; update
read_memory_bytes to detect if requested_len (len as usize) > available and
return an explicit error (bail!) instead of using requested_len.min(available)
and returning fewer bytes. Locate the ptr_usize/requested_len/available logic in
read_memory_bytes and replace the min-based truncation with a bounds check that
bails with a clear out-of-bounds message (include requested and available sizes
for clarity) before calling memory.read.

Comment thread .agents/skills/plugin-release/SKILL.md Outdated
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.

2 participants