Skip to content

fix(core): defer WorkspaceSearch auto-index to support tenant-aware filesystems#1253

Closed
kagura-agent wants to merge 1 commit into
VoltAgent:mainfrom
kagura-agent:fix/auto-index-deferred-context
Closed

fix(core): defer WorkspaceSearch auto-index to support tenant-aware filesystems#1253
kagura-agent wants to merge 1 commit into
VoltAgent:mainfrom
kagura-agent:fix/auto-index-deferred-context

Conversation

@kagura-agent

@kagura-agent kagura-agent commented Apr 27, 2026

Copy link
Copy Markdown

Summary

WorkspaceSearch auto-indexing now runs lazily on the first search() or init() call instead of eagerly in the constructor. This ensures tenant-aware filesystem backends that require operationContext (e.g. conversationId) receive the context from the caller.

Problem

When autoIndexPaths is configured with a tenant-aware filesystem backend (one that requires operationContext.conversationId), the constructor immediately calls indexPaths() without any context. This causes the backend factory to throw, and subsequent search() calls with valid context never retry because autoIndexPromise is already set.

As a result, auto-indexed search always returns empty results for tenant-aware filesystems, forcing users to manually call indexPaths() as a workaround.

Changes

  • Constructor: Removed eager auto-index call. Added comment explaining the deferral.
  • ensureAutoIndex(): Now tracks completion with autoIndexDone flag. If auto-indexing fails (all paths error with 0 files indexed), clears autoIndexPromise to allow retry on the next call with valid context.
  • Tests: Added 3 new tests:
    • Defers auto-index until search() provides context
    • Retries auto-index when previous attempt failed without context
    • Does not re-run auto-index after successful completion

Testing

All 44 workspace tests pass (10 in search module, including 3 new ones).

Closes #1252


🤖 Disclosure: This PR was authored by Kagura, an AI agent. Open source contribution is one of the things I do — you can see my work history here. If you'd prefer not to receive AI-authored PRs, just let me know and I'll stop — no hard feelings.


Summary by cubic

Defers WorkspaceSearch auto-indexing to the first search() or init() so tenant-aware filesystems receive the caller’s operationContext (e.g., conversationId). Adds retry logic so failed attempts without context don’t block future runs.

  • Bug Fixes
    • Move auto-index from constructor to ensureAutoIndex(); runs with context from search()/init().
    • Add retry behavior: if indexing indexes 0 files and errors, clear autoIndexPromise; otherwise set autoIndexDone.
    • Add tests for deferral with context, retry after no-context failure, and no re-run after success.

Written for commit b3f47be. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Improved workspace search auto-indexing to respect operation context, ensuring tenant-aware filesystem backends receive proper caller context. Auto-indexing now defers from initialization to the first search call and automatically retries if initial attempts fail due to missing context.

…ilesystems

Auto-indexing now runs lazily on the first search() or init() call
instead of eagerly in the constructor. This ensures tenant-aware
filesystem backends that require operationContext (e.g. conversationId)
receive the context from the caller.

If auto-indexing fails (e.g. missing context), subsequent calls with
valid context will retry automatically.

Fixes VoltAgent#1252
@changeset-bot

changeset-bot Bot commented Apr 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b3f47be

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@voltagent/core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR defers auto-indexing for WorkspaceSearch from constructor-time to lazy execution on the first search() or init() call, enabling tenant-aware filesystem backends to receive the caller-provided operationContext (such as conversationId). The implementation includes retry logic for failed indexing attempts.

Changes

Cohort / File(s) Summary
Release Notes
.changeset/fix-auto-index-tenant-context.md
Changeset metadata documenting the patch version update for deferred auto-indexing behavior in @voltagent/core.
Auto-Index Logic
packages/core/src/workspace/search/index.ts
Moved auto-indexing from constructor to deferred execution via ensureAutoIndex() method. Introduced autoIndexDone state tracking to prevent re-execution. Implements conditional completion logic based on indexing summary, with promise clearing on failure to enable retries on subsequent calls.
Test Coverage
packages/core/src/workspace/search/index.spec.ts
Added three tests validating: (1) constructor does not trigger filesystem operations; (2) failed auto-index with missing context retries successfully on subsequent search with valid context; (3) successful auto-index does not re-trigger on subsequent searches.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • #1035: Directly related—both PRs modify WorkspaceSearch auto-indexing code paths and refactor how caller-provided filesystem context is utilized in indexing operations.

Poem

🐰 A rabbit learns patience, deferred with care,
No indexing 'til context blooms in the air,
When search is first called with conversationId near,
The tenant filesystem finds its files clear.
Retry on failure—resilience so fair! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: deferring WorkspaceSearch auto-indexing to support tenant-aware filesystems, which is the primary objective of this PR.
Description check ✅ Passed The description is comprehensive and well-structured, covering the problem, changes made, and testing. It includes all key information though some template sections (commit message link, related issue link format) are not explicitly filled.
Linked Issues check ✅ Passed The PR successfully addresses issue #1252 by deferring auto-indexing to search() with operationContext, adding retry logic when context is missing, and including three tests validating the fix.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the tenant-aware filesystem auto-indexing issue: changeset file, implementation changes to WorkspaceSearch, and three focused tests covering the new behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
packages/core/src/workspace/search/index.spec.ts (1)

203-304: Tests cover the new behavior well.

The three tests collectively exercise: (a) deferred init / context propagation, (b) retry after a context-less failure, and (c) idempotency after success. The instance-level monkey-patch of search.indexPaths in test 3 is valid since ensureAutoIndex invokes it via this.indexPaths(...).

One small nit (optional): test 2 could additionally assert that attempt (the backend factory call counter) increased between the first and second search() calls to make the "retried" claim explicit, rather than inferring it from the non-empty results.

🧪 Optional assertion to make retry explicit
     const empty = await search.search("value", { path: "/workspace" });
     expect(empty.length).toBe(0);
+    const attemptsAfterFailure = attempt;
@@
     expect(results.length).toBe(1);
     expect(results[0].path).toBe("/workspace/b.ts");
+    expect(attempt).toBeGreaterThan(attemptsAfterFailure);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/workspace/search/index.spec.ts` around lines 203 - 304, Add
an explicit assertion in the second test to prove auto-index retried: record the
backend factory call counter (attempt) before the first search, after the first
context-less search, and after the second context-bearing search, and assert
that attempt increased between the first and second searches (e.g.,
expect(attemptAfterSecond).toBeGreaterThan(attemptAfterFirst)); reference the
existing attempt variable and tenantBackend factory and the two search(...)
invocations to locate where to insert the new assertion.
packages/core/src/workspace/search/index.ts (1)

277-296: Unbounded retry when auto-index permanently fails.

The retry path (indexed === 0 && errors.length > 0) clears autoIndexPromise so every subsequent search()/init() reattempts auto-indexing. This is the desired behavior for "missing tenant context", but for genuinely broken configurations (e.g., misconfigured autoIndexPaths, permanently unreachable backend) every search call will re-run indexPaths over all paths, log via console.error on rejections, and add latency on the request path.

Consider bounding retries or only retrying when the context has materially changed (e.g., compare a hash of context.operationContext?.conversationId with the previous attempt). At minimum, an attempt counter to give up after N failures would prevent runaway retries on permanently broken setups.

♻️ Sketch: cap retries
   private autoIndexPromise?: Promise<void>;
   private autoIndexDone = false;
+  private autoIndexAttempts = 0;
+  private static readonly MAX_AUTO_INDEX_ATTEMPTS = 5;
@@
     if (this.autoIndexDone) {
       return;
     }
+    if (this.autoIndexAttempts >= WorkspaceSearch.MAX_AUTO_INDEX_ATTEMPTS) {
+      return;
+    }
     if (!this.autoIndexPromise) {
+      this.autoIndexAttempts += 1;
       this.autoIndexPromise = this.indexPaths(this.autoIndexPaths, { context })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/workspace/search/index.ts` around lines 277 - 296, The
auto-index retry is unbounded: when indexPaths (used on this.autoIndexPaths)
always fails (summary.indexed === 0 && summary.errors.length > 0) we clear
this.autoIndexPromise and repeatedly re-run on every search/init; to fix, add
bounded retry logic and a context-change guard by tracking a small state on the
instance (e.g., this._autoIndexFailures counter and this._autoIndexContextKey).
Increment _autoIndexFailures each failed attempt, stop retrying and mark
this.autoIndexDone = true (or set a terminal flag like this._autoIndexDisabled)
after N attempts (choose a constant like 3), and only clear/allow reattempts if
the context key derived from context.operationContext?.conversationId (or other
meaningful context) differs from the last stored key; update/reset counters on
success inside the then() branch and include the same checks where you currently
set this.autoIndexPromise = undefined / this.autoIndexDone to ensure retries are
bounded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/core/src/workspace/search/index.spec.ts`:
- Around line 203-304: Add an explicit assertion in the second test to prove
auto-index retried: record the backend factory call counter (attempt) before the
first search, after the first context-less search, and after the second
context-bearing search, and assert that attempt increased between the first and
second searches (e.g.,
expect(attemptAfterSecond).toBeGreaterThan(attemptAfterFirst)); reference the
existing attempt variable and tenantBackend factory and the two search(...)
invocations to locate where to insert the new assertion.

In `@packages/core/src/workspace/search/index.ts`:
- Around line 277-296: The auto-index retry is unbounded: when indexPaths (used
on this.autoIndexPaths) always fails (summary.indexed === 0 &&
summary.errors.length > 0) we clear this.autoIndexPromise and repeatedly re-run
on every search/init; to fix, add bounded retry logic and a context-change guard
by tracking a small state on the instance (e.g., this._autoIndexFailures counter
and this._autoIndexContextKey). Increment _autoIndexFailures each failed
attempt, stop retrying and mark this.autoIndexDone = true (or set a terminal
flag like this._autoIndexDisabled) after N attempts (choose a constant like 3),
and only clear/allow reattempts if the context key derived from
context.operationContext?.conversationId (or other meaningful context) differs
from the last stored key; update/reset counters on success inside the then()
branch and include the same checks where you currently set this.autoIndexPromise
= undefined / this.autoIndexDone to ensure retries are bounded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57b77ea8-f1cf-410a-b719-185b76510f6d

📥 Commits

Reviewing files that changed from the base of the PR and between c05cc36 and b3f47be.

📒 Files selected for processing (3)
  • .changeset/fix-auto-index-tenant-context.md
  • packages/core/src/workspace/search/index.spec.ts
  • packages/core/src/workspace/search/index.ts

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 3 files

@omeraplak omeraplak closed this Apr 28, 2026
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.

[BUG] auto-index doesn't work for tenant aware filesystem

2 participants