@polyweave/state-search (1.0.7)
Installation
@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/npm install @polyweave/state-search@1.0.7"@polyweave/state-search": "1.0.7"About this package
@polyweave/state-search
Composable filter algebra, recursive tree traversal, async tool routing, and DSL query parsing over Polyweave state stores.
Synopsis
import {
createSleuthRetriever,
createTreeSearchHarness,
createTreeSearchToolDuplex,
traverseTree,
createTreeClient,
createTreeSearchTool,
createMultiStateQueryTool,
createStateSearchTooling,
andFilters,
orFilters,
notFilter,
graphFilter,
treeFilter,
textFilter,
predicateFilter,
temporalFilter,
spatialFilter,
leafScope,
citesDoc,
writtenBetween,
locatedIn,
parseFilter,
FilterParseError,
FilterErrorLayer,
createAsyncToolRouter
} from '@polyweave/state-search';
const retriever = createSleuthRetriever({
multiState: myMultiStateStore,
defaultLimit: 10
});
retriever({ query: 'machine learning' }, (err, results) => {
console.log('Results:', results);
});
Install
npm install @polyweave/state-search
Why
State-search provides a composable filter algebra over polyweave state stores, bridging multi-state document/text/graph/tree stores with a unified query interface. It supports AND/OR/NOT filter composition, DSL parsing, programmatic filter construction, recursive tree traversal with bt-harness navigation, async tool routing for deferred execution, and a SLEUTH-compatible retriever with embedding support. Use it for structured search, filtering, traversal, and agent-driven exploration over federated state backends.
API
Sleuth Retriever (sleuth-retriever.js)
createSleuthRetriever(options) → retrieverFunction
Creates a SLEUTH-compatible retriever for multi-state stores.
createSleuthRetriever(options: object) → (payload: object, done: function) → void
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.multiState |
multi-state store | Yes | — | Multi-state store with search(); must expose typeof multiState.search === 'function' |
options.textStore |
text store | No | — | Optional text store for page content resolution via textStore.get() |
options.embeddingRunner |
embedding runner | No | — | Optional embedding runner with runEmbedding(text, callback) for vector search |
options.resolvePage |
function |
No | — | Custom page resolver: (entry, done) |
options.defaultLimit |
number |
No | 5 |
Default result limit |
options.useProvidedPages |
boolean |
No | true |
If true and payload.pages is non-empty, return those pages directly |
options.useEmbeddings |
boolean |
No | false |
If true, enrich query with embedding before search |
options.queryMode |
string |
No | — | Query mode passed through to multiState.search() |
options.filters |
filter object | No | — | Default filters applied to all searches |
options.filterBuilder |
function |
No | — | Dynamic filter builder: ({ query, payload }) → filter; overrides filters |
options.candidateStores |
string[] |
No | — | Candidate stores passed through to multiState.search() |
options.candidateLimit |
number |
No | — | Candidate limit passed through to multiState.search() |
options.planner |
boolean |
No | true |
Whether to enable the query planner |
Throws: Error('Sleuth retriever requires a multiState store with search().')
The returned retriever function accepts:
| Payload | Type | Description |
|---|---|---|
payload.query / payload.question / payload |
string |
Search query |
payload.pages |
any[] |
Provided pages (returned directly if useProvidedPages is true) |
payload.topK |
number |
Result limit override |
payload.filters |
filter object | Additional filters (combined with default via AND) |
payload.useEmbeddings |
boolean |
Per-call override for embedding enrichment |
Filter System (query-builder.js)
andFilters(...filters) → filter | null
Combines filters with AND logic. Flattens nested arrays. Single filter is returned unwrapped.
Returns: { op: 'and', filters: [...] } or the sole filter, or null if no filters.
orFilters(...filters) → filter | null
Combines filters with OR logic. Same semantics as andFilters.
notFilter(filter) → filter | null
Negates a filter. Returns null if filter is falsy.
Returns: { op: 'not', filter }
graphFilter({ entity?, relation?, temporal?, spatial? }) → filter
Matches documents in a fact graph. Fields are optional; at least one should be provided.
Returns: { source: 'graph', entity, relation, temporal, spatial }
treeFilter({ treeId?, path?, prefix?, nodeId? }) → filter
Matches documents in a tree store by path or node.
Returns: { source: 'tree', treeId, path, prefix: boolean, nodeId }
textFilter(query: string | object) → filter
Full-text match filter. When passed a string, creates { source: 'text', query }. When passed an object, spreads it.
| Parameter | Type | Description |
|---|---|---|
query |
string |
object |
predicateFilter({ name?, predicate?, args? }) → filter
Field-level predicate filter.
Returns: { source: 'predicate', name, predicate, args }
temporalFilter({ relation?, start?, end? }) → filter
Time-range filter.
Returns: { temporal: { relation, start, end } }
spatialFilter({ relation?, minX?, minY?, maxX?, maxY? }) → filter
Spatial bounding box filter.
Returns: { spatial: { relation, minX, minY, maxX, maxY } }
leafScope({ treeId?, path?, nodeId? }) → filter
Scope filter for leaf node descendants. Wraps treeFilter with prefix: true.
citesDoc(docId: string) → filter
Filter for documents citing a given document. Wraps graphFilter with relation: 'cites'.
writtenBetween(start: any, end: any) → filter
Convenience temporal filter. Wraps graphFilter with relation: 'written_at' and temporal range.
locatedIn(bounds: object) → filter
Convenience spatial filter. Wraps graphFilter with relation: 'located_in' and spatial bounds.
Filter DSL Parser (query-parser.js)
parseFilter(text: string) → filter
Parses a DSL filter string into a filter object using a Peggy grammar.
Throws: FilterParseError on parse failure or semantic validation failure.
DSL grammar examples:
text("climate change")
and(text("neural"), graph(entity="doc:1", relation="cites"))
or(tree(treeId="t1"), not(predicate(name="spam")))
text(query="search", limit=5)
FilterParseError
Error class for filter parsing failures. Extends Error.
| Property | Type | Description |
|---|---|---|
name |
'FilterParseError' |
Error name |
layer |
'syntax' |
'semantic' |
location |
{ line, column } |
null |
expected |
string[] |
null |
youWrote |
string |
null |
rule |
string |
null |
fix |
string |
null |
cause |
Error |
null |
format(sourceText) |
function |
Formats error with source context |
FilterErrorLayer
Enum: { SYNTAX: 'syntax', SEMANTIC: 'semantic' }
Error Utilities (query-errors.js)
getLineColumnFromLocation(location) → { line, column, length } | null
Extracts line/column from a Peggy-style location object. Handles both { start: { line, column }, end: { line, column } } and { line, column } formats.
extractYouWrote(sourceText: string, location) → string | null
Extracts the source text fragment at a given location.
Tree Search (tree-search.js)
createTreeClient(store: duplex) → { query }
Creates a callback-based client for a tree store. Handles TREE_RESULT frame routing via streamId.
Returns: { query(content: object, callback: (err, result) => void) } — the content object is merged into a TREE_QUERY frame with a generated streamId.
traverseTree(options, done) → void
Recursive tree traversal with user-guided child selection.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.treeStore |
duplex | Yes | — | Tree store to traverse |
options.treeId |
string | Yes | — | Tree identifier |
options.query |
string | — | '' |
Query string passed to chooseChild |
options.chooseChild |
function |
Yes | — | (query, labels, context, callback) → void — called with candidate child labels; callback receives (err, index) where -1 = stop, 0..n = descend to that child |
options.labeler |
function |
No | defaultLabeler |
(candidate) → string — labels a candidate for chooseChild |
options.leafSelector |
function |
No | defaultLeafSelector |
`(result) → { text, citations } |
options.maxDepth |
number |
No | 12 |
Maximum recursion depth |
Error callbacks: traverseTree returns Error via done if treeStore/treeId missing or chooseChild is not a function.
defaultLabeler(candidate) → string
Default node labeler: returns first 80 chars of candidate.data.text for paragraphs, or candidate.data.summary preview for other nodes.
defaultLeafSelector(result) → { text, citations } | null
Default leaf selector: finds first node with data.kind === 'paragraph', returns its text and citations.
Tree Search Harness (tree-search-harness.js)
createTreeSearchHarness(options) → bt-harness
Creates a bt-harness that navigates a hierarchical summary tree using an LLM agent.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.treeStore |
duplex | Yes | — | Tree store to navigate |
options.treeId |
string | Yes | — | Tree identifier |
options.query |
string | No | '' |
Search query |
options.maxDepth |
number | No | 12 |
Maximum recursion depth |
options.apiKey |
string | Yes | — | API key for LLM calls |
options.agentModel |
string | No | 'deepseek-v4-flash' |
Model for the navigation agent |
options.evalModel |
string | No | 'deepseek-v4-flash' |
Model for the evaluator |
options.agentSystem |
string | No | — | Custom agent system prompt |
options.evalSystem |
string | No | — | Custom evaluator system prompt |
options.createAdapter |
function | No | — | Custom adapter factory |
options.adapterConfig |
object | No | — | Adapter factory configuration |
options.timeoutMs |
number | No | 120000 |
Harness timeout |
options._debug / options.debug |
boolean | No | false |
Debug output |
Returns: bt-harness object ({ source, sink, valid, errors, warnings, symbolics, ... }) or { valid: false, errors: [...] } if treeStore/treeId missing.
createTreeSearchToolDuplex(options) → duplex
Wraps createTreeSearchHarness into a push-stream duplex tool with TOOL_CALL/TICK/CANCEL/ABORT frame handling.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.name |
string | No | 'tree_recursive_search' |
Tool name |
options.description |
string | No | — | Tool description |
options.treeStore |
duplex | Yes | — | Tree store |
options.treeId |
string | Yes | — | Tree identifier |
options.maxDepth |
number | No | 12 |
Maximum depth |
options.apiKey |
string | Yes | — | API key |
options.agentModel |
string | No | 'deepseek-v4-flash' |
Agent model |
options.evalModel |
string | No | 'deepseek-v4-flash' |
Eval model |
options.debug |
boolean | No | false |
Debug flag |
options.timeoutMs |
number | No | 120000 |
Timeout |
options.createAdapter |
function | No | — | Custom adapter factory |
options.adapterConfig |
object | No | — | Adapter factory configuration |
Frame protocol:
| Input frame | Description |
|---|---|
TOOL_CALL { query } |
Start tree search with the given query |
TICK |
Advance the harness one tick |
CANCEL |
Cancel current harness |
ABORT |
Abort current harness |
DESCRIBE |
Returns DESCRIPTION with tool schema |
| Output frame | Description |
|---|---|
TOOL_RESULT { text, path, leaf } |
Search completed |
TOOL_RESULT { error } |
Search failed or harness invalid |
DESCRIPTION |
Response to DESCRIBE |
Error conditions:
TOOL_CALLwhile running: returnsTOOL_RESULT { error: 'Already running' }- Invalid harness: returns
TOOL_RESULT { error }with joined error messages - BT failure: returns
TOOL_RESULT { error }with failure reason - BT error: returns
TOOL_RESULT { error }with error message
Tooling (tooling.js)
createTreeSearchTool(options) → { tool, handler }
Creates a simple (non-harness) tree search tool and handler pair.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.name |
string | No | 'tree_recursive_search' |
Tool name |
options.description |
string | No | — | Tool description |
options.treeStore |
duplex | Yes | — | Tree store |
options.treeId |
string | Yes | — | Tree identifier |
options.chooseChild |
function | Yes | — | Child selection function |
options.labeler |
function | No | — | Node labeler |
options.leafSelector |
function | No | — | Leaf content selector |
options.maxDepth |
number | No | — | Maximum depth |
createMultiStateQueryTool(options) → { tool, handler }
Creates a multi-state query tool.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.name |
string | No | 'multistore_query' |
Tool name |
options.description |
string | No | — | Tool description |
options.multiState |
multi-state store | Yes | — | Multi-state store |
options.defaultLimit |
number | No | 5 |
Default result limit |
createStateSearchTooling(options) → { tools, handlers }
Combines tree search and multi-state query tooling into a unified tools + handlers map.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.treeStore |
duplex | No | — | If provided, creates a tree search tool |
options.treeId |
string | No | — | Required with treeStore |
options.chooseChild |
function | No | — | Required with treeStore |
options.labeler |
function | No | — | Node labeler |
options.leafSelector |
function | No | — | Leaf selector |
options.maxDepth |
number | No | — | Maximum depth |
options.multiState |
multi-state store | No | — | If provided, creates a multi-state query tool |
options.defaultLimit |
number | No | — | Default result limit |
options.treeTool |
object | No | — | Overrides for the tree search tool config |
options.multiTool |
object | No | — | Overrides for the multi-state query tool config |
Async Tool Router (async-tool-router.js)
createAsyncToolRouter(options)
Re-exported from @polyweave/tools. Creates an async tool router for deferred tool execution with concurrency control.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
options.tools |
Map<string, function> |
Yes | — | Map of tool name to handler function |
options.timeoutMs |
number |
No | — | Default timeout for async tools |
options.maxConcurrency |
number |
No | — | Maximum concurrent tool executions |
Returns: { route(toolCall, callback), cancel(requestId), close }
Tests
npm test
Covers: sleuth retriever creation and search with/without embeddings/embedding runners, filter composition (AND/OR/NOT) and all filter types, filter DSL parsing and error handling (syntax, semantic, repair suggestions), tree search traversal with custom labelers/leafSelectors, tree search harness creation and tick-driven execution, async tool routing with concurrency control, and bt-harness tree search integration.
Requirements
- Node.js 22+
- ESM only
Dependencies: @polyweave/bt-harness, @polyweave/tools, peggy
Peer dependencies: @polyweave/core, @polyweave/state
Dependencies
Dependencies
| ID | Version |
|---|---|
| @polyweave/bt-harness | * |
| @polyweave/tools | * |
| peggy | ^4.0.2 |
Development Dependencies
| ID | Version |
|---|---|
| @rigor/core | * |
Peer Dependencies
| ID | Version |
|---|---|
| @polyweave/core | * |