Skip to content

Add package retriever hooks - #280

Merged
kentcdodds merged 16 commits into
mainfrom
cursor/retriever-hooks-5d59
Apr 29, 2026
Merged

kentcdodds merged 16 commits into
mainfrom
cursor/retriever-hooks-5d59

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Apr 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add package.json#kody.retrievers manifest support for package-declared search/context retrievers.
  • Derive retriever metadata into KV manifest entries and per-retriever scope records during package refresh/delete, with a derived aggregate snapshot for compatibility/debugging.
  • Invoke retrievers for explicit search and automatic context sidecars with separate retriever_result output that is not treated as a resolvable entity.
  • Harden additive behavior so retriever import/execution failures degrade to warnings/empty results instead of failing search, memory surfacing, or package refresh/delete flows.
  • Harden KV cleanup/indexing: delimiter-safe prefixes, malformed-row guards, non-listing KV fallback, manifest-cache deletion on package removal, refresh race safety, and stale-entry filtering.
  • Escape retriever-provided markdown text and manifest metadata before surfacing it in search/memory output, including safe inline code spans for backtick-containing values.
  • Capture retriever failures in Sentry across search, context, execution, and package projection paths while preserving fail-open behavior.
  • Propagate context retriever warnings through search/memory structured output and top-level warning channels.
  • Clamp retriever-provided scores to the normal 0-1 range before ranking.
  • Parallelize agent-turn and MCP search retriever lookup with other search setup, while skipping retriever lookup for entity-only search.
  • Include memories.retrieverResults in the structured search response type and avoid empty memory headings for retriever-only context.
  • Document retriever result limits and add focused coverage for parsing, indexing, formatting, search ranking, context retrieval, stale-entry filtering, refresh race safety, concurrent package refresh safety, and fail-open context retrieval.

Testing

  • npm run lint
  • npm run typecheck
  • npx vitest run packages/worker/src/package-registry/manifest.node.test.ts packages/worker/src/package-retrievers/manifest-cache.node.test.ts packages/worker/src/mcp/tools/search.node.test.ts packages/worker/src/mcp/tools/search-format.node.test.ts packages/worker/src/mcp/tools/memory-tool-context.node.test.ts packages/worker/src/package-registry/service.node.test.ts
  • npx vitest run packages/worker/src/package-retrievers/manifest-cache.node.test.ts packages/worker/src/mcp/observability.workers.test.ts
  • npx vitest run packages/worker/src/mcp/tools/memory-tool-context.node.test.ts packages/worker/src/package-retrievers/manifest-cache.node.test.ts packages/worker/src/mcp/observability.workers.test.ts
  • npx vitest run packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • npx vitest run packages/worker/src/mcp/tools/search-format.node.test.ts packages/worker/src/mcp/tools/search.node.test.ts
  • npx vitest run packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
  • npx vitest run packages/worker/src/package-registry/service.node.test.ts packages/worker/src/package-retrievers/manifest-cache.node.test.ts packages/worker/src/mcp/tools/search-format.node.test.ts packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
  • npx vitest run packages/worker/src/mcp/tools/search-handler.node.test.ts packages/worker/src/mcp/tools/search.node.test.ts
  • git push -u origin cursor/retriever-hooks-5d59 (pre-push ran npm run test:push, including worker tests and Playwright E2E)
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Packages can declare optional retrievers; retriever results appear in search and memory/context outputs, structured payloads, and rendered Markdown with safe escaping.
    • Backend support: manifest caching, scope indexes, and a retriever-run service with provider limits and result validation (max 20).
  • Bug Fixes

    • Retriever execution failures are logged/reported and surfaced as warnings without blocking responses.
  • Documentation

    • Added docs describing package-owned retrievers and configuration location.
  • Tests

    • New unit and E2E tests for retrievers, formatting, caching, and failure handling.

@coderabbitai

coderabbitai Bot commented Apr 28, 2026 •

Copy link
Copy Markdown

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 package-owned retrievers end-to-end: manifest/schema, KV-backed per-scope manifest cache, retriever execution service, integration into registration/refresh, search/memory/agent tool flows, result validation/limits (≤20), tests, and documentation.

Changes

Cohort / File(s) Summary
Documentation
docs/contributing/packages-and-manifests.md
Documents kody.retrievers config, discovery, execution contract (params/results), and runtime validation (max 20 results).
Registry Types & Manifest
packages/worker/src/package-registry/types.ts, packages/worker/src/package-registry/manifest.ts, packages/worker/src/package-registry/manifest.node.test.ts
Adds retriever schema/types, validation for scopes, listPackageRetrievers projection entries, and tests ensuring correct parsing and rejection of empty scopes.
Package Retriever Types
packages/worker/src/package-retrievers/types.ts
New domain types and Zod schemas for retriever scopes, manifest-cache entries, scope indexes, retriever result/output shapes, and output validation (max 20 results).
Manifest Cache Implementation & Tests
packages/worker/src/package-retrievers/manifest-cache.ts, packages/worker/src/package-retrievers/manifest-cache.node.test.ts
Implements KV-backed, versioned manifest cache and per-scope index keys with refresh/remove/list APIs, legacy-index fallback, staleness handling, pagination, and comprehensive tests.
Retriever Execution Service
packages/worker/src/package-retrievers/service.ts
Adds runPackageRetrievers to load cached entries, fetch saved package/artifact, execute retriever exports with read-only storage context, enforce time/limit clamps, validate/truncate outputs, aggregate/sort by score, and surface warnings/errors.
Package Registry Integration
packages/worker/src/package-registry/service.ts, packages/worker/src/package-registry/service.node.test.ts
Wires cache refresh/removal into refreshSavedPackageProjection/deleteSavedPackageProjection with error-tolerant calls and tests asserting cache interactions and failure tolerance.
Search System Integration
packages/worker/src/mcp/tools/search.ts, packages/worker/src/mcp/tools/search-format.ts, packages/worker/src/mcp/tools/search.node.test.ts, packages/worker/src/mcp/tools/search-format.node.test.ts
searchUnified accepts optional retrieverResults, builds retriever_result candidates, includes retriever metadata in package search fields/aliases, renders retriever results in markdown/structured output, and updates telemetry; tests added/adjusted.
Memory & Agent Tool Integration
packages/worker/src/mcp/tools/memory-tool-context.ts, packages/worker/src/mcp/tools/memory-tool-context.node.test.ts, packages/worker/src/agent-turn/tools.ts
Adds retrieverResults to memory summaries, runs retrievers concurrently with memory surfacing, changes empty-content logic to consider both sources, includes retriever results in markdown and structured payloads, and converts retriever failures into warnings.
Markdown Safety Utilities
packages/worker/src/mcp/tools/markdown-safety.ts
Adds helpers to escape Markdown and format inline code safely (handles embedded backticks).
Tests & Misc
multiple ...node.test.ts files across worker packages
New/updated tests covering formatting, search/memory integration, manifest parsing, cache behavior, and service execution; mocks updated to include retriever-cache hooks.

Sequence Diagram(s)

sequenceDiagram
    participant Tool as "Search / Memory Tool"
    participant Service as "Package Retriever Service"
    participant Cache as "KV Manifest Cache"
    participant Registry as "Package Registry"
    participant Loader as "Bundle Loader"
    participant Executor as "Module Executor"

    Tool->>Service: runPackageRetrievers({ baseUrl, userId, scope, query, ... })
    activate Service

    Service->>Cache: listPackageRetrieversForScope({ userId, scope, limit })
    Cache-->>Service: [retriever entries]

    loop per retriever entry
        Service->>Registry: fetch savedPackage & entity source
        Registry-->>Service: savedPackage, source
        Service->>Loader: load published bundle for entryPoint
        Loader-->>Service: bundled module
        Service->>Executor: execute retriever({ params: { query, scope, memoryContext, limit, conversationId } })
        Executor-->>Service: { results: [...] } or error
        Service->>Service: validate/normalize/truncate results (≤20)
    end

    Service->>Service: aggregate results, sort by score
    Service-->>Tool: { results, warnings }
    deactivate Service
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through manifests, found retrievers galore,

Caching in KV, then loading bundles from store,
Twenty trimmed answers, tidy and bright,
Search and memory now share their light,
A rabbit applauds—what a clever chore! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add package retriever hooks' accurately summarizes the main change: introducing package-declared retrievers as a new feature with associated manifest support, KV storage, search/context integration, and execution infrastructure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/retriever-hooks-5d59

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.

@kentcdodds
kentcdodds marked this pull request as ready for review April 28, 2026 23:11
@github-actions

github-actions Bot commented Apr 28, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-280.kentcdodds.workers.dev

Worker: kody-pr-280
D1: kody-pr-280-db
KV: kody-pr-280-oauth-kv

Mocks:

Comment thread package.json Outdated
Comment thread packages/worker/src/package-retrievers/service.ts
Comment thread packages/worker/src/package-runtime/published-bundle-artifacts.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/worker/src/mcp/tools/search.ts (1)

1673-1679: ⚠️ Potential issue | 🟠 Major

maxResponseSize no longer bounds responses once retriever memories are attached.

Line 1678 adds memories.retrieverResults, but Line 1765 only trims matches. If the memory-side retriever payload is large, the formatted response can still exceed maxResponseSize even after all matches are dropped. The trim step needs to shed memory-side retriever data too.

Also applies to: 1765-1768

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

In `@packages/worker/src/mcp/tools/search.ts` around lines 1673 - 1679, The
response trimming only drops matches but leaves large retriever-side payloads
attached via searchMemories (constructed from memoryToolContext.memories and
specifically memories.retrieverResults), so maxResponseSize can still be
exceeded; update the trimming logic in the code paths around where matches are
truncated (the block that reduces matches when enforcing maxResponseSize) to
also remove or truncate memory-side retriever data by clearing or replacing
memoryToolContext.memories.retrieverResults (and any retriever payloads nested
on searchMemories) when you drop matches — e.g., set retrieverResults = [] or
strip heavy fields from each retrieverResult whenever you perform the matches
trim so the formatted response is actually bounded by maxResponseSize.
🧹 Nitpick comments (2)
packages/worker/src/mcp/tools/memory-tool-context.node.test.ts (1)

44-83: Consider using beforeEach for mock setup.

The test manually calls setupMemoryContextMocks() at the start. If more tests are added to this file, using beforeEach would ensure consistent mock state and avoid accidental state leakage between tests.

♻️ Suggested refactor
+import { beforeEach, expect, test, vi } from 'vitest'
-import { expect, test, vi } from 'vitest'

 // ... mock setup ...

+beforeEach(() => {
+	setupMemoryContextMocks()
+})
+
 test('loadRelevantMemoriesForTool returns context retriever results alongside memories', async () => {
-	setupMemoryContextMocks()
 	const result = await loadRelevantMemoriesForTool({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/tools/memory-tool-context.node.test.ts` around lines
44 - 83, The test currently calls setupMemoryContextMocks() inline; move that
call into a beforeEach block so mocks are reset before every test to prevent
state leakage—add a beforeEach(() => setupMemoryContextMocks()) at the top of
the test suite containing the test that calls loadRelevantMemoriesForTool, and
remove the inline setupMemoryContextMocks() call from the test; ensure any other
tests in the same file rely on the shared beforeEach and that
mockModule.runPackageRetrievers expectations remain unchanged.
packages/worker/src/package-retrievers/manifest-cache.ts (1)

199-207: Old revisioned manifest blobs are never removed from KV.

Refresh writes a new manifest object under a revision-specific key, and delete only rewrites the scope indexes. That leaves orphaned manifest-cache entries behind indefinitely, so storage usage grows on every package refresh/delete cycle. Consider deleting superseded manifest keys during refresh and cleanup.

Also applies to: 254-281

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

In `@packages/worker/src/package-retrievers/manifest-cache.ts` around lines 199 -
207, The code writes revisioned manifest blobs via
buildPackageRetrieverManifestCacheKey and getRetrieverKv.put but never removes
superseded manifest keys (manifestKey), leaving orphaned KV entries; update the
refresh and delete flows (the code around the manifest write and the sections
referenced ~254-281) to enumerate previous revision keys from your scope/index,
call getRetrieverKv().delete on each superseded
buildPackageRetrieverManifestCacheKey(...) (or directly delete the old
manifestKey) when replacing or deleting a package, and ensure deletion happens
after successful write/removal so orphaned manifest-cache entries are cleaned
up.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/contributing/packages-and-manifests.md`:
- Around line 103-106: Update the Retriever return contract text to explicitly
state the runtime-enforced maximum of 20 results: mention that the returned
object must be { results: [...] } with at most 20 items (each item containing
id, title, summary, optional details/score/source/url/metadata), and note that
payloads with >20 items will be rejected at validation time; also add a short
recommendation to paginate or truncate results in the Retriever implementation
(referencing the "Retriever exports" and "results" contract) so package authors
avoid deployment-time validation failures.

In `@packages/worker/src/agent-turn/tools.ts`:
- Around line 85-98: The call to runPackageRetrievers (and its dynamic import)
can throw and currently can abort the entire search tool; wrap the dynamic
import and the runPackageRetrievers(...) call in a try/catch, logging a warning
(not an error) on failures and setting retrieverSearch to an empty/neutral
result (e.g., empty hits/[] and no-op metadata) so downstream code can continue;
ensure the shape of the fallback matches what the rest of the search tool
expects (reference runPackageRetrievers, retrieverSearch, and
resolveSearchMemoryContext) so types/consumers aren’t broken.

In `@packages/worker/src/mcp/tools/search-format.ts`:
- Around line 419-429: parseEntityRef currently allows the 'retriever_result'
type but SearchEntityDetail has no retriever_result variant and
resolveEntityDetail/formatEntityDetailMarkdown lack handlers, causing unresolved
references; either remove 'retriever_result' from the allowed types in
parseEntityRef to prevent parsing it as resolvable, or add a retriever_result
case to SearchEntityDetail and implement its handling in resolveEntityDetail and
formatEntityDetailMarkdown (implement resolver logic to fetch the retriever
result detail and a formatter to render it) so parsed retriever_result refs can
be resolved and formatted consistently with other entity types.

In `@packages/worker/src/mcp/tools/search.ts`:
- Around line 1572-1590: The call to runPackageRetrievers (inside the async
branch that sets retrieverRun) can throw and currently bubbles up, so wrap the
await runPackageRetrievers(...) invocation in a try/catch; on error return a
fallback object like { results: [], warnings: [String(error) || 'retriever
error'] } so retrieverRun is always an object and the subsequent
warnings.push(...retrieverRun.warnings) and search flow continue; locate the
async IIFE that references runPackageRetrievers, resolveSearchMemoryContext,
userId and replace the direct await with a guarded try/catch returning the
empty-results object on failure.

In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 212-249: The read-modify-write in readScopeIndex -> build
nextRetrievers -> writeScopeIndex is racy and can drop concurrent updates;
change to an atomic update or lower contention: either implement optimistic
compare-and-swap by reading a version/updatedAt from readScopeIndex
(existing.updatedAt or add a version field) and pass it to writeScopeIndex so
the write fails if the index changed, retrying the merge, or refactor the
storage shape to per-retriever records (instead of one list) so add/remove
operations are independent; apply the same fix for the other occurrence around
lines 264-280 that uses existing.retrievers, nextRetrievers and
retrieverScopeIndexVersion.
- Around line 296-324: The current code slices index.retrievers early (slice(0,
input.limit ?? 10)), which can drop valid retrievers if earlier entries are
stale; remove that early slice and iterate all index.retrievers, performing the
KV lookup via getRetrieverKv(...) and buildPackageRetrieverManifestCacheKey(...)
and validating cached objects against
retrieverManifestCacheVersion/PackageRetrieverManifestCache/userId/packageId/revision
as you already do, collect matching retrievers that include input.scope, then
after flattening manifests.flat() apply the limit once (e.g., result =
manifests.flat().slice(0, input.limit ?? 10)) before returning so the requested
limit is applied to valid, filtered retrievers rather than to raw index entries.

In `@packages/worker/src/package-retrievers/service.ts`:
- Around line 104-108: The cache validation should only accept entries whose
published_commit matches the requested revision: replace the current condition
that compares input.entry.revision against (source.published_commit ??
source.indexed_commit ?? source.updated_at) with a strict comparison to
source.published_commit alone (i.e., treat missing published_commit as a
mismatch). Locate the check in service.ts (the if block that currently uses the
nullish-fallback chain) and change it to only compare source.published_commit to
input.entry.revision; this aligns with normalizeRevision() in manifest-cache.ts
which only stores published_commit.
- Around line 253-281: The code currently exposes raw retriever exception text
(outcome.reason) in console logs and in the user-facing warning pushed to
warnings inside the package retriever flow; change the console.error payload and
the warning string pushed in the block that handles failure (referencing
outcome.reason, entry.packageId, entry.kodyId, entry.retrieverKey, input.scope,
and warnings) to use a fixed, non-sensitive message (e.g., "package retriever
failed" plus retriever identifiers) while keeping
Sentry.captureException(outcome.reason, {...}) unchanged so the full error is
recorded only in Sentry; ensure no raw error message or stack is concatenated
into logs or warnings.

In `@packages/worker/src/package-runtime/published-bundle-artifacts.ts`:
- Around line 356-383: The retriever loop rebuilds and re-persists module
artifacts already handled by the generic manifest.exports logic, causing
redundant builds and writes; modify the logic in the retriever iteration
(listPackageRetrievers and its use of retriever.exportName) to first deduplicate
export names (or consult the already-handled set from the manifest.exports pass)
and only call buildModuleBundle and persistPublishedBundleArtifact for
exportNames that were not already processed; alternatively extract the shared
"resolve export target + persist module artifact" steps into a helper used by
both flows and ensure you skip or no-op for exportNames present in the
manifest.exports pass to avoid repeated builds and DB/KV churn.

---

Outside diff comments:
In `@packages/worker/src/mcp/tools/search.ts`:
- Around line 1673-1679: The response trimming only drops matches but leaves
large retriever-side payloads attached via searchMemories (constructed from
memoryToolContext.memories and specifically memories.retrieverResults), so
maxResponseSize can still be exceeded; update the trimming logic in the code
paths around where matches are truncated (the block that reduces matches when
enforcing maxResponseSize) to also remove or truncate memory-side retriever data
by clearing or replacing memoryToolContext.memories.retrieverResults (and any
retriever payloads nested on searchMemories) when you drop matches — e.g., set
retrieverResults = [] or strip heavy fields from each retrieverResult whenever
you perform the matches trim so the formatted response is actually bounded by
maxResponseSize.

---

Nitpick comments:
In `@packages/worker/src/mcp/tools/memory-tool-context.node.test.ts`:
- Around line 44-83: The test currently calls setupMemoryContextMocks() inline;
move that call into a beforeEach block so mocks are reset before every test to
prevent state leakage—add a beforeEach(() => setupMemoryContextMocks()) at the
top of the test suite containing the test that calls
loadRelevantMemoriesForTool, and remove the inline setupMemoryContextMocks()
call from the test; ensure any other tests in the same file rely on the shared
beforeEach and that mockModule.runPackageRetrievers expectations remain
unchanged.

In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 199-207: The code writes revisioned manifest blobs via
buildPackageRetrieverManifestCacheKey and getRetrieverKv.put but never removes
superseded manifest keys (manifestKey), leaving orphaned KV entries; update the
refresh and delete flows (the code around the manifest write and the sections
referenced ~254-281) to enumerate previous revision keys from your scope/index,
call getRetrieverKv().delete on each superseded
buildPackageRetrieverManifestCacheKey(...) (or directly delete the old
manifestKey) when replacing or deleting a package, and ensure deletion happens
after successful write/removal so orphaned manifest-cache entries are cleaned
up.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2368bc48-165a-4769-9b33-3d01d653f743

📥 Commits

Reviewing files that changed from the base of the PR and between 562e598 and 16e5fc2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • docs/contributing/packages-and-manifests.md
  • package.json
  • packages/worker/src/agent-turn/tools.ts
  • packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
  • packages/worker/src/mcp/tools/memory-tool-context.ts
  • packages/worker/src/mcp/tools/search-format.node.test.ts
  • packages/worker/src/mcp/tools/search-format.ts
  • packages/worker/src/mcp/tools/search.node.test.ts
  • packages/worker/src/mcp/tools/search.ts
  • packages/worker/src/package-registry/manifest.node.test.ts
  • packages/worker/src/package-registry/manifest.ts
  • packages/worker/src/package-registry/service.node.test.ts
  • packages/worker/src/package-registry/service.ts
  • packages/worker/src/package-registry/types.ts
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/package-retrievers/manifest-cache.ts
  • packages/worker/src/package-retrievers/service.ts
  • packages/worker/src/package-retrievers/types.ts
  • packages/worker/src/package-runtime/published-bundle-artifacts.ts

Comment on lines +103 to +106
Retriever exports receive `params` with `query`, `scope`, `memoryContext`,
`limit`, and `conversationId`, and return `{ "results": [...] }` where each
result has `id`, `title`, `summary`, optional `details`, optional `score`,
optional `source`, optional `url`, and optional `metadata`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document the enforced 20-result cap.

The return contract here says { results: [...] }, but the runtime schema rejects payloads with more than 20 items. Without that limit in the docs, package authors can implement retrievers that fail validation only after deployment.

Suggested docs tweak
-Retriever exports receive `params` with `query`, `scope`, `memoryContext`,
-`limit`, and `conversationId`, and return `{ "results": [...] }` where each
+Retriever exports receive `params` with `query`, `scope`, `memoryContext`,
+`limit`, and `conversationId`, and return `{ "results": [...] }` with at most
+20 results, where each
 result has `id`, `title`, `summary`, optional `details`, optional `score`,
 optional `source`, optional `url`, and optional `metadata`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/contributing/packages-and-manifests.md` around lines 103 - 106, Update
the Retriever return contract text to explicitly state the runtime-enforced
maximum of 20 results: mention that the returned object must be { results: [...]
} with at most 20 items (each item containing id, title, summary, optional
details/score/source/url/metadata), and note that payloads with >20 items will
be rejected at validation time; also add a short recommendation to paginate or
truncate results in the Retriever implementation (referencing the "Retriever
exports" and "results" contract) so package authors avoid deployment-time
validation failures.

Comment thread packages/worker/src/agent-turn/tools.ts Outdated
Comment thread packages/worker/src/mcp/tools/search-format.ts
Comment thread packages/worker/src/mcp/tools/search.ts Outdated
Comment thread packages/worker/src/package-retrievers/manifest-cache.ts Outdated
Comment thread packages/worker/src/package-retrievers/manifest-cache.ts Outdated
Comment thread packages/worker/src/package-retrievers/service.ts
Comment thread packages/worker/src/package-retrievers/service.ts Outdated
Comment thread packages/worker/src/package-runtime/published-bundle-artifacts.ts Outdated
Comment thread packages/worker/src/mcp/tools/memory-tool-context.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/worker/src/mcp/tools/search-format.ts (1)

435-449: ⚠️ Potential issue | 🟠 Major

Align structured memory contract with retriever sidecar support.

formatSearchMarkdown now accepts memories.retrieverResults (Line 448), but SearchResultStructuredContent.memories still omits that field. This creates type drift and makes the new field easy to drop in typed call paths.

💡 Suggested contract update
 export type SearchResultStructuredContent = {
   ...
   memories?: {
     surfaced: Array<{
       id: string
       category: string | null
       status: string
       subject: string
       summary: string
       details: string
       tags: Array<string>
       updatedAt: string
     }>
     suppressedCount: number
     retrievalQuery: string
+    retrieverResults?: Array<PackageRetrieverSurfaceResult>
   }
   ...
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/tools/search-format.ts` around lines 435 - 449, The
structured memory contract needs to include the same optional retrieverResults
field that formatSearchMarkdown accepts: update the
SearchResultStructuredContent type (the memories property) to add
retrieverResults?: Array<PackageRetrieverSurfaceResult> (matching the shape used
by formatSearchMarkdown) so typed call paths cannot drop the field; ensure
imports/reference to PackageRetrieverSurfaceResult are added where
SearchResultStructuredContent is declared and run TS checks to verify
compatibility with formatSearchMarkdown and any callers that construct memories
objects.
♻️ Duplicate comments (1)
packages/worker/src/package-retrievers/manifest-cache.ts (1)

329-342: ⚠️ Potential issue | 🔴 Critical

Avoid concurrent delete/put against the same scope-entry keys.

At Line 329, deletes and writes are dispatched together. When a retriever key already exists, both operations target the same key; if delete completes after put, the refreshed entry is lost.

🔧 Suggested fix (serialize delete then write)
-		await Promise.all([
-			...existingScopeEntryKeys.map(
-				async (key) => await getRetrieverKv(input.env).delete(key),
-			),
-			...registeredRetrievers
-				.filter((retriever) => retriever.scopes.includes(scope))
-				.map((retriever) =>
-					writeScopeEntry({
-						env: input.env,
-						scope,
-						entry: toScopeIndexEntry(retriever),
-					}),
-				),
-		])
+		await Promise.all(
+			existingScopeEntryKeys.map(
+				async (key) => await getRetrieverKv(input.env).delete(key),
+			),
+		)
+		await Promise.all(
+			registeredRetrievers
+				.filter((retriever) => retriever.scopes.includes(scope))
+				.map((retriever) =>
+					writeScopeEntry({
+						env: input.env,
+						scope,
+						entry: toScopeIndexEntry(retriever),
+					}),
+				),
+		)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/package-retrievers/manifest-cache.ts` around lines 329 -
342, Concurrent deletes and writes against the same scope-entry keys can race
and lose updates; change the logic so you first delete all keys (use
getRetrieverKv(input.env).delete for each key from existingScopeEntryKeys and
await Promise.all on those deletes) and only after those deletions complete
perform the writes for registeredRetrievers (calling writeScopeEntry for each
toScopeIndexEntry(retriever) where retriever.scopes includes scope and await
Promise.all on the writes); keep references to existingScopeEntryKeys,
registeredRetrievers, getRetrieverKv, writeScopeEntry, toScopeIndexEntry, scope
and input.env when implementing the two-step serialized flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/worker/src/mcp/tools/search-format.ts`:
- Around line 435-449: The structured memory contract needs to include the same
optional retrieverResults field that formatSearchMarkdown accepts: update the
SearchResultStructuredContent type (the memories property) to add
retrieverResults?: Array<PackageRetrieverSurfaceResult> (matching the shape used
by formatSearchMarkdown) so typed call paths cannot drop the field; ensure
imports/reference to PackageRetrieverSurfaceResult are added where
SearchResultStructuredContent is declared and run TS checks to verify
compatibility with formatSearchMarkdown and any callers that construct memories
objects.

---

Duplicate comments:
In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 329-342: Concurrent deletes and writes against the same
scope-entry keys can race and lose updates; change the logic so you first delete
all keys (use getRetrieverKv(input.env).delete for each key from
existingScopeEntryKeys and await Promise.all on those deletes) and only after
those deletions complete perform the writes for registeredRetrievers (calling
writeScopeEntry for each toScopeIndexEntry(retriever) where retriever.scopes
includes scope and await Promise.all on the writes); keep references to
existingScopeEntryKeys, registeredRetrievers, getRetrieverKv, writeScopeEntry,
toScopeIndexEntry, scope and input.env when implementing the two-step serialized
flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1eaf89d5-e81b-46f4-9129-d6b6436f6e9b

📥 Commits

Reviewing files that changed from the base of the PR and between 16e5fc2 and 83f7de9.

📒 Files selected for processing (7)
  • docs/contributing/packages-and-manifests.md
  • packages/worker/src/agent-turn/tools.ts
  • packages/worker/src/mcp/tools/search-format.ts
  • packages/worker/src/mcp/tools/search.ts
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/package-retrievers/manifest-cache.ts
  • packages/worker/src/package-retrievers/service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/contributing/packages-and-manifests.md

Comment thread packages/worker/src/package-retrievers/manifest-cache.ts
Comment thread packages/worker/src/agent-turn/tools.ts Outdated
Comment thread packages/worker/src/package-retrievers/manifest-cache.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/worker/src/mcp/tools/memory-tool-context.ts (1)

50-57: Preserve minimal error context in fail-open logging.

The fail-open path is good, but dropping the exception details (Line 56) makes retriever incidents hard to diagnose.

Suggested tweak
 	} catch (error) {
-		console.error(
+		console.warn(
 			JSON.stringify({
 				message: 'package context retrievers unavailable',
+				error:
+					error instanceof Error ? error.message : String(error),
 			}),
 		)
-		void error
 		return {
 			results: [],
 			warnings: [],
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/tools/memory-tool-context.ts` around lines 50 - 57,
In the catch(error) block inside memory-tool-context.ts (the fail-open path for
retrievers), change the console.error call so it preserves minimal error
context: include error message and stack (e.g., add fields like error:
String(error) or message: (error instanceof Error ? error.message :
String(error)) and stack: (error instanceof Error ? error.stack : undefined))
into the JSON being logged, then return the same fail-open object as before;
keep the existing void error removal of linter noise but ensure the logged JSON
contains the error details for diagnostics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 170-188: The current fallback in listScopeEntryKeys constructs
keys from readLegacyScopeIndex when kv.list is missing, which can miss new
writes because writeLegacyScopeIndex rebuilds the legacy index via
readScopeEntries (circular dependency); remove the typeof kv.list fallback
branch from listScopeEntryKeys so it always relies on the primary kv.list
behavior and instead centralize the legacy fallback inside readScopeEntries:
when kv.list is unavailable, have readScopeEntries call readLegacyScopeIndex and
return entries (and let buildPackageRetrieverScopeEntryKey still be used when
deriving keys there), ensuring writeLegacyScopeIndex continues to rebuild via
readScopeEntries without causing stale snapshots.
- Around line 243-250: The second filter currently assumes entry.scopes is an
array and calls entry.scopes.includes(input.scope), which can throw for
malformed KV rows; update the predicate to guard the property first (e.g. ensure
typeof entry === 'object' && entry !== null && Array.isArray(entry.scopes'))
and/or add a type guard that narrows PackageRetrieverIndexEntry to entries where
scopes is an array of strings, then only call
entry.scopes.includes(input.scope); reference the existing filter chain and the
symbols PackageRetrieverIndexEntry, entry.scopes, input.userId and input.scope
when making the change.
- Around line 365-392: removePackageRetrieverManifestCacheEntries currently only
clears scope index entries but leaves package-retriever-manifest:* rows in KV;
after deleting existingScopeEntryKeys (and before/after calling
writeLegacyScopeIndex), also enumerate and delete manifest records for the
package by using getRetrieverKv(input.env).list(...) with a prefix for the
package retriever manifests (e.g.
"package-retriever-manifest:{userId}:{packageId}:" or otherwise filter by
packageId) and then await Promise.all over the returned keys to call
.delete(key). Keep existing calls to listScopeEntryKeys, getRetrieverKv, and
writeLegacyScopeIndex but add this additional manifest-key listing+deletion step
inside removePackageRetrieverManifestCacheEntries so no orphaned
package-retriever-manifest entries remain.

---

Nitpick comments:
In `@packages/worker/src/mcp/tools/memory-tool-context.ts`:
- Around line 50-57: In the catch(error) block inside memory-tool-context.ts
(the fail-open path for retrievers), change the console.error call so it
preserves minimal error context: include error message and stack (e.g., add
fields like error: String(error) or message: (error instanceof Error ?
error.message : String(error)) and stack: (error instanceof Error ? error.stack
: undefined)) into the JSON being logged, then return the same fail-open object
as before; keep the existing void error removal of linter noise but ensure the
logged JSON contains the error details for diagnostics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9acd241f-96bb-4c1d-9645-8260b61dcc70

📥 Commits

Reviewing files that changed from the base of the PR and between 83f7de9 and 1eae13c.

📒 Files selected for processing (5)
  • packages/worker/src/agent-turn/tools.ts
  • packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
  • packages/worker/src/mcp/tools/memory-tool-context.ts
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/package-retrievers/manifest-cache.ts

Comment thread packages/worker/src/package-retrievers/manifest-cache.ts
Comment thread packages/worker/src/package-retrievers/manifest-cache.ts Outdated
Comment thread packages/worker/src/package-retrievers/manifest-cache.ts
Comment thread packages/worker/src/mcp/tools/search-format.ts
Comment thread packages/worker/src/mcp/tools/memory-tool-context.ts
Comment thread packages/worker/src/package-registry/service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/worker/src/package-retrievers/manifest-cache.ts (2)

321-417: ⚠️ Potential issue | 🟠 Major

Refresh and delete can still resurrect the same package.

These two flows snapshot current keys first and then mutate KV independently. If removePackageRetrieverManifestCacheEntries overlaps a refresh for the same package, a late refresh can rewrite the manifest/scope entries after delete has already listed the old keys, making a deleted package discoverable again. This still needs package-level coordination or a tombstone/version check before writes.

Also applies to: 420-461

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

In `@packages/worker/src/package-retrievers/manifest-cache.ts` around lines 321 -
417, Refresh flow can race with removePackageRetrieverManifestCacheEntries and
re-create a deleted package; add a tombstone/version check to prevent
resurrection by making delete write a tombstone and refresh abort if tombstone
exists or version mismatches. Concretely: in
removePackageRetrieverManifestCacheEntries create/update a package-level
tombstone key (e.g., using buildPackageRetrieverManifestCacheKey or a new
buildPackageRetrieverTombstoneKey) storing revision or timestamp; in
refreshPackageRetrieverManifestCache, before putting manifestKey and before
writing scope entries (writeScopeEntry / writeLegacyScopeIndex), read that
tombstone and abort/return null if the tombstone indicates deletion for the same
or newer revision/timestamp; ensure the same tombstone convention is used in
both refreshPackageRetrieverManifestCache and
removePackageRetrieverManifestCacheEntries so writes are conditional on
tombstone absence or older version.

420-460: ⚠️ Potential issue | 🟠 Major

Non-listing KV still leaks manifest-cache rows on delete.

manifestCacheKeys is sourced from listManifestCacheKeys, but that helper returns [] whenever kv.list is unavailable (Line 211). In the fallback mode this file otherwise supports, package removal never deletes any package-retriever-manifest:* rows, so deleted-package metadata is left behind indefinitely. This needs a non-list cleanup path too, which likely means storing package/revision references somewhere deletions can read without kv.list.

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

In `@packages/worker/src/package-retrievers/manifest-cache.ts` around lines 420 -
460, removePackageRetrieverManifestCacheEntries currently relies solely on
listManifestCacheKeys (which returns [] when kv.list is unavailable), so in
fallback environments no package-retriever-manifest:* rows get deleted; add a
non-list cleanup path by deriving and deleting manifest keys deterministically
from stored package/revision references: ensure the manifest creation path
stores per-package revision references (e.g., via a new writeManifestIndex or by
augmenting writeLegacyScopeIndex/readScopeEntries) and update
removePackageRetrieverManifestCacheEntries to, if listManifestCacheKeys returns
empty, enumerate revisions for the given packageId from that index and delete
keys using the known key pattern with getRetrieverKv(env).delete(key); keep
existing scope cleanup (readScopeEntries, listScopeEntryKeys,
writeLegacyScopeIndex) but add this fallback deletion so manifests are always
removed even when kv.list is unsupported.
🧹 Nitpick comments (1)
packages/worker/src/mcp/tools/memory-tool-context.ts (1)

50-56: Keep the caught error in the log payload.

This path only logs a static message and then drops error, so retriever import/runtime regressions will be very hard to triage from worker logs. Please include at least the error name/message/stack while still returning empty results.

Possible change
 	} catch (error) {
 		console.error(
 			JSON.stringify({
 				message: 'package context retrievers unavailable',
+				error:
+					error instanceof Error
+						? {
+								name: error.name,
+								message: error.message,
+								stack: error.stack,
+							}
+						: String(error),
 			}),
 		)
-		void error
 		return {
 			results: [],
 			warnings: [],
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/tools/memory-tool-context.ts` around lines 50 - 56,
The catch block in memory-tool-context.ts currently logs only a static message
and discards the caught error; update that catch (in the package context
retriever import/runtime path) to include the error details (error.name,
error.message and error.stack) in the JSON payload passed to console.error
(e.g., add fields like errorName, errorMessage, errorStack), remove the no-op
"void error", and preserve the existing behavior of returning empty results from
the surrounding function so failures are logged with useful diagnostics while
still returning the empty context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/mcp/tools/search-format.ts`:
- Around line 486-493: The retriever-provided fields
(input.memories.retrieverResults[].title, .summary, .details, .source, .url) are
injected verbatim into markdown; update search-format.ts to escape or safely
quote these fields before pushing them into lines (e.g., sanitize or
markdown-escape title and summary in the loop that builds "## Relevant retriever
results"), reusing the existing escape/quote helper implemented in
memory-tool-context.ts (import and call that helper for all affected fields),
and apply the same change to the other block referenced (the code around the
590-602 region) so all retriever outputs are consistently escaped.

In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 499-523: The manifest handling assumes cached.retrievers is a
well-formed array and will throw if it's malformed; update the block in the
Promise.all map (around getRetrieverKv / buildPackageRetrieverManifestCacheKey
and the cached variable) to validate cached.retrievers before iterating: check
Array.isArray(cached.retrievers) and that each entry has the expected properties
(retrieverKey truthy and scopes is an array) and if the check fails return [] so
the code fails open; apply the same defensive checks before calling .filter and
.includes (using a guard like typeof retriever.retrieverKey === 'string' and
Array.isArray(retriever.scopes')) to avoid runtime exceptions.

---

Duplicate comments:
In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 321-417: Refresh flow can race with
removePackageRetrieverManifestCacheEntries and re-create a deleted package; add
a tombstone/version check to prevent resurrection by making delete write a
tombstone and refresh abort if tombstone exists or version mismatches.
Concretely: in removePackageRetrieverManifestCacheEntries create/update a
package-level tombstone key (e.g., using buildPackageRetrieverManifestCacheKey
or a new buildPackageRetrieverTombstoneKey) storing revision or timestamp; in
refreshPackageRetrieverManifestCache, before putting manifestKey and before
writing scope entries (writeScopeEntry / writeLegacyScopeIndex), read that
tombstone and abort/return null if the tombstone indicates deletion for the same
or newer revision/timestamp; ensure the same tombstone convention is used in
both refreshPackageRetrieverManifestCache and
removePackageRetrieverManifestCacheEntries so writes are conditional on
tombstone absence or older version.
- Around line 420-460: removePackageRetrieverManifestCacheEntries currently
relies solely on listManifestCacheKeys (which returns [] when kv.list is
unavailable), so in fallback environments no package-retriever-manifest:* rows
get deleted; add a non-list cleanup path by deriving and deleting manifest keys
deterministically from stored package/revision references: ensure the manifest
creation path stores per-package revision references (e.g., via a new
writeManifestIndex or by augmenting writeLegacyScopeIndex/readScopeEntries) and
update removePackageRetrieverManifestCacheEntries to, if listManifestCacheKeys
returns empty, enumerate revisions for the given packageId from that index and
delete keys using the known key pattern with getRetrieverKv(env).delete(key);
keep existing scope cleanup (readScopeEntries, listScopeEntryKeys,
writeLegacyScopeIndex) but add this fallback deletion so manifests are always
removed even when kv.list is unsupported.

---

Nitpick comments:
In `@packages/worker/src/mcp/tools/memory-tool-context.ts`:
- Around line 50-56: The catch block in memory-tool-context.ts currently logs
only a static message and discards the caught error; update that catch (in the
package context retriever import/runtime path) to include the error details
(error.name, error.message and error.stack) in the JSON payload passed to
console.error (e.g., add fields like errorName, errorMessage, errorStack),
remove the no-op "void error", and preserve the existing behavior of returning
empty results from the surrounding function so failures are logged with useful
diagnostics while still returning the empty context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d44bdcd3-353f-4d0c-bccd-a6c3dc9a774b

📥 Commits

Reviewing files that changed from the base of the PR and between 1eae13c and 9b75cc9.

📒 Files selected for processing (5)
  • packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
  • packages/worker/src/mcp/tools/memory-tool-context.ts
  • packages/worker/src/mcp/tools/search-format.ts
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/package-retrievers/manifest-cache.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/mcp/tools/memory-tool-context.node.test.ts

Comment thread packages/worker/src/mcp/tools/search-format.ts
Comment thread packages/worker/src/package-retrievers/manifest-cache.ts
Comment thread packages/worker/src/mcp/tools/markdown-safety.ts
Comment thread packages/worker/src/mcp/tools/markdown-safety.ts Outdated
Comment thread packages/worker/src/mcp/tools/memory-tool-context.ts Outdated
Comment thread packages/worker/src/package-registry/service.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/worker/src/mcp/tools/memory-tool-context.ts (1)

68-80: ⚠️ Potential issue | 🟠 Major

Stop hiding the retriever env requirement behind as Env.

These APIs still accept an env shape that omits BUNDLE_ARTIFACTS_KV, then cast it to Env before invoking the retriever service. A caller can satisfy the current type and silently get no retriever context because runPackageRetrievers falls back to empty results when that binding is missing. Make the retriever KV dependency explicit in the input/helper types instead of masking it with a cast.

Also applies to: 97-104, 122-129, 146-152

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

In `@packages/worker/src/mcp/tools/memory-tool-context.ts` around lines 68 - 80,
The code hides a required retriever binding by casting the input env to Env
before calling runPackageRetrievers; make the dependency explicit by updating
the input type for loadRelevantMemoriesForTool (and the helper usages at the
other noted sites) to require BUNDLE_ARTIFACTS_KV (e.g., change env: Pick<Env,
'APP_DB' | 'AI'> & Partial<Pick<Env, 'CAPABILITY_VECTOR_INDEX'>> to include
BUNDLE_ARTIFACTS_KV), remove the unsafe cast to Env, and pass the strongly-typed
env directly into runPackageRetrievers so callers cannot omit the retriever KV.
Ensure the same change is applied to the other referenced functions/usages
(lines ~97-104, 122-129, 146-152) that currently perform the cast.
🧹 Nitpick comments (2)
packages/worker/src/package-retrievers/manifest-cache.ts (2)

303-305: Consider batching KV reads for large entry sets.

Promise.all on all entry keys creates unbounded concurrent KV requests. While KV handles concurrent reads well, consider batching (e.g., chunks of 50) if you expect users to have many retrievers, to avoid potential rate limiting.

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

In `@packages/worker/src/package-retrievers/manifest-cache.ts` around lines 303 -
305, The current Promise.all over entryKeys (which calls kv.get for each key)
can create unbounded concurrent KV reads; modify the code that populates
storedEntries to read keys in bounded batches (e.g., chunks of 50) instead of
one large Promise.all. Implement a simple batching loop or helper (chunking
entryKeys into arrays of size 50), for each chunk call
Promise.all([...chunk].map(key => kv.get(key, 'json'))), await each batch, and
concatenate results into storedEntries so the rest of the logic that uses
storedEntries remains unchanged.

506-544: Optimization opportunity: fetch manifests incrementally to match limit.

The current implementation fetches all manifests before applying the limit at line 543. For users with many retrievers, this could be inefficient. Consider fetching in batches until the limit is satisfied, though the current approach correctly prioritizes correctness over performance.

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

In `@packages/worker/src/package-retrievers/manifest-cache.ts` around lines 506 -
544, The function listPackageRetrieversForScope currently reads all scope
entries then fetches every manifest before applying the limit; change it to
fetch manifests incrementally (sequentially or in small batches) until the
requested limit is reached to avoid unnecessary work. Iterate over the
references returned by readScopeEntries and for each one call getRetrieverKv(...
buildPackageRetrieverManifestCacheKey(...), 'json'), validate the manifest
against retrieverManifestCacheVersion and the cached fields, filter entries to
PackageRetrieverManifestCacheEntry that match reference.retrieverKey and include
input.scope, push those into a result array, and stop fetching more manifests
once result.length >= (input.limit ?? 10); finally return result.slice(0,
input.limit ?? 10). Ensure behavior and validations (version, userId, packageId,
revision, Array.isArray(retrievers)) remain identical to the existing checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/mcp/tools/memory-tool-context.ts`:
- Around line 85-117: The current call to runContextPackageRetrievers (and the
similar call at lines 134-159) discards retrieverResult.warnings; update the
return shapes and mapping in memory-tool-context.ts so retriever warnings are
propagated to consumers (e.g., include retrieverWarnings or merge into existing
warnings on MemoryToolSummary). Concretely, modify the Promise.all result
handling for surfaceRelevantMemories and runContextPackageRetrievers to capture
retrieverResult.warnings and include them in the returned object (alongside
retrieverResults and suppressedCount / retrievalQuery), and apply the same
change to the other block that mirrors this behavior so partial retriever
failures are not dropped.

In `@packages/worker/src/mcp/tools/search-format.ts`:
- Around line 893-898: The emitted retriever metadata in the package-detail
markdown should escape free text and format manifest-derived code spans: update
the block that builds the "## Retrievers" lines (the loop over retrievers where
retriever.key, retriever.exportName, retriever.description and retriever.scopes
are used) to call escapeMarkdownText for retriever.description and
formatMarkdownInlineCode for each code span (retriever.key, retriever.exportName
and each scope string) so backticks and other markdown characters are safely
encoded; locate the code in search-format.ts (the lines pushing into lines
within the search({ entity: ... }) related function) and replace raw
interpolations with those helper calls.

---

Outside diff comments:
In `@packages/worker/src/mcp/tools/memory-tool-context.ts`:
- Around line 68-80: The code hides a required retriever binding by casting the
input env to Env before calling runPackageRetrievers; make the dependency
explicit by updating the input type for loadRelevantMemoriesForTool (and the
helper usages at the other noted sites) to require BUNDLE_ARTIFACTS_KV (e.g.,
change env: Pick<Env, 'APP_DB' | 'AI'> & Partial<Pick<Env,
'CAPABILITY_VECTOR_INDEX'>> to include BUNDLE_ARTIFACTS_KV), remove the unsafe
cast to Env, and pass the strongly-typed env directly into runPackageRetrievers
so callers cannot omit the retriever KV. Ensure the same change is applied to
the other referenced functions/usages (lines ~97-104, 122-129, 146-152) that
currently perform the cast.

---

Nitpick comments:
In `@packages/worker/src/package-retrievers/manifest-cache.ts`:
- Around line 303-305: The current Promise.all over entryKeys (which calls
kv.get for each key) can create unbounded concurrent KV reads; modify the code
that populates storedEntries to read keys in bounded batches (e.g., chunks of
50) instead of one large Promise.all. Implement a simple batching loop or helper
(chunking entryKeys into arrays of size 50), for each chunk call
Promise.all([...chunk].map(key => kv.get(key, 'json'))), await each batch, and
concatenate results into storedEntries so the rest of the logic that uses
storedEntries remains unchanged.
- Around line 506-544: The function listPackageRetrieversForScope currently
reads all scope entries then fetches every manifest before applying the limit;
change it to fetch manifests incrementally (sequentially or in small batches)
until the requested limit is reached to avoid unnecessary work. Iterate over the
references returned by readScopeEntries and for each one call getRetrieverKv(...
buildPackageRetrieverManifestCacheKey(...), 'json'), validate the manifest
against retrieverManifestCacheVersion and the cached fields, filter entries to
PackageRetrieverManifestCacheEntry that match reference.retrieverKey and include
input.scope, push those into a result array, and stop fetching more manifests
once result.length >= (input.limit ?? 10); finally return result.slice(0,
input.limit ?? 10). Ensure behavior and validations (version, userId, packageId,
revision, Array.isArray(retrievers)) remain identical to the existing checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 442dd286-e108-413b-8e95-740c3c0d1a75

📥 Commits

Reviewing files that changed from the base of the PR and between 9b75cc9 and f34abe7.

📒 Files selected for processing (9)
  • packages/worker/src/mcp/tools/markdown-safety.ts
  • packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
  • packages/worker/src/mcp/tools/memory-tool-context.ts
  • packages/worker/src/mcp/tools/search-format.node.test.ts
  • packages/worker/src/mcp/tools/search-format.ts
  • packages/worker/src/package-registry/service.node.test.ts
  • packages/worker/src/package-registry/service.ts
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/package-retrievers/manifest-cache.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/worker/src/mcp/tools/search-format.node.test.ts
  • packages/worker/src/mcp/tools/memory-tool-context.node.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/worker/src/package-registry/service.ts
  • packages/worker/src/package-retrievers/manifest-cache.node.test.ts
  • packages/worker/src/package-registry/service.node.test.ts

Comment thread packages/worker/src/mcp/tools/memory-tool-context.ts
Comment thread packages/worker/src/mcp/tools/search-format.ts
Comment thread packages/worker/src/mcp/tools/memory-tool-context.ts
Comment thread packages/worker/src/agent-turn/tools.ts Outdated
Comment thread packages/worker/src/mcp/tools/search.ts Outdated
Comment thread packages/worker/src/mcp/tools/search.ts Outdated
Comment thread packages/worker/src/package-registry/service.ts
Comment thread packages/worker/src/mcp/tools/search.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cab8623. Configure here.

match: {
type: 'retriever_result' as const,
...result,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Retriever results type mismatch with SearchMatch union

Low Severity

The retriever_result variant of SearchMatch declares source: string (required) and details: string | null, but buildRetrieverResultCandidates constructs the match by spreading PackageRetrieverSurfaceResult which has source?: string (possibly undefined) and details?: string (possibly undefined, not null). At runtime source can be undefined, contradicting the type. Downstream code in formatMatchBlock and toSlimStructuredMatches uses ?? fallbacks that mask the mismatch, but any new consumer trusting the SearchMatch type would not guard against undefined.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cab8623. Configure here.

userId,
}),
retrieverRunPromise,
])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Retriever promise runs unnecessarily for entity-only searches

Low Severity

The retrieverRunPromise is started before checking args.entity, so entity-only lookups pay the cost of invoking package retrievers (DB queries, KV reads, module execution) even though the retriever results are discarded in the entity branch's early return. The condition could additionally check !args.entity to avoid launching retriever work that will never be used.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cab8623. Configure here.

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