@karmaniverous/jeeves-watcher-openclaw 0.16.1 → 0.18.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +16 -13
- package/dist/index.js +2329 -708
- package/dist/scripts/build-skills.d.ts +1 -1
- package/dist/skills/jeeves-watcher/SKILL.md +230 -136
- package/dist/src/auth.test.d.ts +5 -0
- package/dist/src/catalogTools.d.ts +96 -0
- package/dist/src/catalogTools.test.d.ts +5 -0
- package/dist/src/constants.d.ts +1 -1
- package/dist/src/distSmoke.test.d.ts +5 -0
- package/dist/src/helpers.d.ts +15 -3
- package/dist/src/manifest.test.d.ts +1 -1
- package/dist/src/promptContext.d.ts +10 -10
- package/dist/src/skillFiles.d.ts +14 -0
- package/dist/src/test/surfaceDiff.d.ts +27 -0
- package/dist/src/test/toolRenames.d.ts +13 -0
- package/dist/src/toolGating.d.ts +8 -6
- package/dist/src/toolSurface.test.d.ts +5 -0
- package/dist/src/watcherComponent.d.ts +3 -2
- package/openclaw.plugin.json +20 -11
- package/package.json +18 -13
- package/dist/src/apiTool.d.ts +0 -37
- package/dist/src/toolTestHarness.d.ts +0 -23
- package/dist/src/vcsTools.d.ts +0 -7
- package/dist/src/vcsTools.test.d.ts +0 -1
- package/dist/src/watcherTools.d.ts +0 -15
- package/dist/src/watcherTools.test.d.ts +0 -1
- /package/dist/src/{apiTool.test.d.ts → skillFiles.test.d.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jeeves-watcher
|
|
3
3
|
description: >
|
|
4
|
-
Semantic search
|
|
4
|
+
Semantic search of the indexed archive: recall prior context, find documents, scan, enrich metadata, watcher config, reindex, indexing issues, file history.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# jeeves-watcher — Search, Discovery & Administration
|
|
@@ -12,9 +12,13 @@ The watcher is an HTTP API running as a background service (typically NSSM on Wi
|
|
|
12
12
|
|
|
13
13
|
**Default port:** 1936 (configurable via `api.port` in watcher config) **Non-default port:** If the watcher runs on a different port, the user must set `plugins.entries.jeeves-watcher-openclaw.config.apiUrl` in `openclaw.json`. The plugin cannot auto-discover a non-default port.
|
|
14
14
|
|
|
15
|
-
**
|
|
15
|
+
**API key:** If the watcher sets `api.apiKey`, every route except `GET /status` and `GET /openapi.json` needs `Authorization: Bearer <key>`. The tools send it from `plugins.entries.jeeves-watcher-openclaw.config.apiKey` (or the `JEEVES_WATCHER_API_KEY` env var). From `watcher_config`, `watcher_config_apply`, `watcher_search`, `watcher_scan` and `watcher_show_vcs_file`, a missing or wrong key comes back as `HTTP 401: {"error":"Unauthorized","code":"unauthorized"}` (see [Error Handling](#error-handling) for the fix). From the other tools it reads `HTTP 401 unauthorized: the watcher API requires an API key. Provide it via plugins.entries.jeeves-watcher-openclaw.config.apiKey or the JEEVES_WATCHER_API_KEY env var.` (or, when a key was sent, `... rejected the API key. Check ... against the service's api.apiKey.`). When calling the API directly, add the header yourself. Never echo the key.
|
|
16
16
|
|
|
17
|
-
**
|
|
17
|
+
**Tool errors:** Tool error text is prefixed `Error: `. Every `watcher_*` tool is built from the watcher's endpoint catalog and reports a non-2xx response as `HTTP <status>: <body>`, with the server's JSON body verbatim: read its `code` (and `details` or `issues`) there, e.g. `HTTP 400: {"error":"Validation failed","code":"invalid_request",...}` or `HTTP 503: {"error":"Scan in progress","code":"scan_in_progress"}`. Branch on the code, not the message. The tools reject parameters that don't match their schema before sending anything. Each call waits up to 120 s; a timeout does not cancel a state change on the service, so check the outcome (e.g. `watcher_check_vcs_exclusion` after `watcher_exclude_vcs_path`) before retrying. An unreachable service reads `Service not reachable at <url>.` with a hint to set `apiUrl`. (`watcher_service` comes from the shared Jeeves core toolset and words its own errors.)
|
|
18
|
+
|
|
19
|
+
**Health check:** `GET /status` returns `name`, `version`, `uptime`, `status` (`healthy`/`degraded`/`unhealthy`), and a `health` object containing `collection` (`name`, `pointCount`, `dimensions`, `systemIndexes`), `reindex` progress, `initialScan` progress, `embedding` (provider health), and (when version tracking is on) `vcs` (`roots`, `pending`, `trippedRoots`, `oldestTrippedAt`). `status` is `degraded` (still HTTP 200) when embeddings are failing or a version-control root has tripped.
|
|
20
|
+
|
|
21
|
+
**Mental model:** The `watcher_*` tools are thin HTTP wrappers. Each tool call translates to an HTTP request to the watcher API. When tools are available, use them. When they're not (e.g., different session, plugin not loaded), you can hit the API directly. Replace `<PORT>` below with the configured port (default 1936; check `plugins.entries.jeeves-watcher-openclaw.config.apiUrl` in `openclaw.json` if overridden), and add `-H "Authorization: Bearer <key>"` when the service sets `api.apiKey`:
|
|
18
22
|
|
|
19
23
|
```
|
|
20
24
|
# Health check
|
|
@@ -34,25 +38,30 @@ curl http://127.0.0.1:<PORT>/config
|
|
|
34
38
|
| Endpoint | Method | Purpose |
|
|
35
39
|
| --- | --- | --- |
|
|
36
40
|
| `/status` | GET | Health check, uptime, collection stats |
|
|
41
|
+
| `/openapi.json` | GET | OpenAPI 3.1 document of the catalog routes (no key needed) |
|
|
37
42
|
| `/search` | POST | Semantic search (main query interface) |
|
|
38
|
-
| `/config` | GET |
|
|
43
|
+
| `/config` | GET | Public resolved config (allow-listed keys plus `issues`); optional `?path=<jsonpath>` filter |
|
|
39
44
|
| `/config/validate` | POST | Validate candidate config |
|
|
40
45
|
| `/config/apply` | POST | Apply config changes |
|
|
41
46
|
| `/reindex` | POST | Trigger reindex |
|
|
42
|
-
| `/metadata` | POST | Enrich document metadata |
|
|
47
|
+
| `/metadata` | POST | Enrich document metadata (file path or source reference) |
|
|
43
48
|
| `/scan` | POST | Filter-only point query (no embeddings) |
|
|
44
|
-
| `/walk` | POST |
|
|
45
|
-
| `/issues` | GET | Runtime
|
|
49
|
+
| `/walk` | POST | Watched-file listing with glob intersection |
|
|
50
|
+
| `/issues` | GET | Runtime processing failures (file paths and source references) |
|
|
51
|
+
| `/documents` | POST | Notify changed source documents (`{ source, rule }`) |
|
|
52
|
+
| `/documents/delete` | POST | Delete source documents by reference or filter |
|
|
46
53
|
| `/rules/register` | POST | Register virtual inference rules (auto-triggers rules reindex) |
|
|
47
|
-
| `/rules/unregister` | DELETE | Remove virtual rules by source |
|
|
54
|
+
| `/rules/unregister` | DELETE | Remove virtual rules by source (`{ source }` body) |
|
|
48
55
|
| `/rules/unregister/:source` | DELETE | Remove virtual rules by source (parameterized) |
|
|
49
|
-
| `/rules/reapply` | POST | Re-apply rules to
|
|
56
|
+
| `/rules/reapply` | POST | Re-apply rules to indexed files matching `globs` |
|
|
50
57
|
| `/points/delete` | POST | Delete points matching a Qdrant filter |
|
|
51
|
-
| `/render` | POST | Render a
|
|
52
|
-
| `/search/facets` | GET | Get search facet values |
|
|
53
|
-
| `/
|
|
58
|
+
| `/render` | POST | Render a document (file path or source reference) through its rule |
|
|
59
|
+
| `/search/facets` | GET | Get search facet definitions and values (`?rules=a,b` scopes them to those rules) |
|
|
60
|
+
| `/search/facets/counts` | POST | Count values of indexed fields over a filter, each document once; counts are approximate (`{ fields, filter?, rules?, limit? }`); `{ min, max }` for number/date fields; `503 not_available` while its system indexes build or past its 10 s budget |
|
|
61
|
+
| `/rebuild-metadata` | POST | Rebuild the enrichment store from Qdrant file point payloads |
|
|
54
62
|
| `/config/schema` | GET | Get config JSON schema |
|
|
55
|
-
| `/config/match` | POST |
|
|
63
|
+
| `/config/match` | POST | Test file paths against inference rules and watch scope |
|
|
64
|
+
| `/vcs/*` | GET/POST | Version tracking (`status`, `history`, `show`, `diff`, `check-exclusion`, `revert`, `exclude`) |
|
|
56
65
|
|
|
57
66
|
**If the watcher is unreachable:** Check the service status (`nssm status jeeves-watcher` on Windows), check the configured port in the watcher config file, and check logs for startup errors.
|
|
58
67
|
|
|
@@ -82,9 +91,9 @@ You have access to a **semantic archive** of your human's working world. Documen
|
|
|
82
91
|
|
|
83
92
|
**STOP and ask for a human decision before:**
|
|
84
93
|
|
|
85
|
-
- **Renaming, moving, or reorganizing watched directories.** The watcher has **move detection** enabled by default (`watch.moveDetection.enabled: true`). When a file is moved,
|
|
94
|
+
- **Renaming, moving, or reorganizing watched directories.** The watcher has **move detection** enabled by default (`watch.moveDetection.enabled: true`). When a file is moved, the change source reports an unlink and an add; the `MoveCorrelator` buffers the unlink, matches it with the add via content hash, and processes it as a zero-embedding move (point ID remap, metadata re-inference, enrichment migration). Under the default parcel source, a renamed or moved directory is paired as a whole (at least 90% of its files at the same relative path and size) and each file becomes such a move. **No re-embedding cost for moves.** However, if a rename is blocked (e.g., by a file lock from the watcher or another service), **do NOT work around it by creating new directories and copying/moving files**. That creates duplicate embeddings at the new paths while the old paths still exist. Instead: stop the blocking service, perform the rename, restart.
|
|
86
95
|
- **Changing `watch.paths` to add large directory trees.** Adding a new watch root triggers initial indexing of every matching file under it.
|
|
87
|
-
- **Running `scope: "full"` reindex.**
|
|
96
|
+
- **Running `scope: "full"` reindex.** Every watched file is re-extracted (a file whose text to embed changed, or that was never indexed, is re-embedded) and every indexed source document is retrieved through its callback and always re-embedded. Use `scope: "rules"` for inference rule changes (no file re-embedding; only metadata reapplication).
|
|
88
97
|
- **Any bulk file operation** (mass copy, mass move, template-based file generation) under watched paths.
|
|
89
98
|
|
|
90
99
|
**The right instinct when blocked:** If a filesystem operation fails because a service has a lock, the correct response is to stop the service, do the operation, and restart — NOT to find a creative workaround. Creative workarounds in the presence of a live watcher are how you accidentally trigger re-embedding of tens of thousands of files.
|
|
@@ -102,7 +111,7 @@ The plugin is a standard OpenClaw plugin. On a Jeeves box, `jeeves install watch
|
|
|
102
111
|
If the watcher service is already running and healthy:
|
|
103
112
|
|
|
104
113
|
1. **Search** — use `watcher_search` with a natural language query and optional metadata filters
|
|
105
|
-
2. **Read source** — use `read` (standard file read) with `file_path` from search results for full document content
|
|
114
|
+
2. **Read source** — use `read` (standard file read) with `file_path` from search results for full document content. Source documents (points carrying `source`, such as `app://chat/thread/42`, and no `file_path`) are not files: their content comes from the source's callback (`POST /render` with `{ "source": "<ref>" }`).
|
|
106
115
|
|
|
107
116
|
## Bootstrap (First-Time Setup)
|
|
108
117
|
|
|
@@ -112,13 +121,13 @@ When the plugin loads and the watcher service is NOT yet set up, drive the entir
|
|
|
112
121
|
|
|
113
122
|
### Step 1: Check Node.js
|
|
114
123
|
|
|
115
|
-
Verify Node.js is installed and version ≥ 22.
|
|
124
|
+
Verify Node.js is installed and version ≥ 24.22.2:
|
|
116
125
|
|
|
117
126
|
```bash
|
|
118
127
|
node --version
|
|
119
128
|
```
|
|
120
129
|
|
|
121
|
-
If missing or too old, guide the user to install Node.js 22.
|
|
130
|
+
If missing or too old, guide the user to install Node.js 24.22.2 or later from https://nodejs.org or via their package manager.
|
|
122
131
|
|
|
123
132
|
### Step 2: Install Qdrant
|
|
124
133
|
|
|
@@ -281,10 +290,10 @@ Then generate a starter config file. Example minimal config:
|
|
|
281
290
|
}
|
|
282
291
|
```
|
|
283
292
|
|
|
284
|
-
Write the config to a sensible location (e.g., `~/.config/jeeves-watcher.config.json` on Linux, or alongside the user's workspace). Validate with:
|
|
293
|
+
Write the config to a sensible location (e.g., `~/.config/jeeves-watcher.config.json` on Linux, or alongside the user's workspace). Validate it against the config schema (offline; no running service needed) with:
|
|
285
294
|
|
|
286
295
|
```bash
|
|
287
|
-
jeeves-watcher validate -c <config-path>
|
|
296
|
+
jeeves-watcher config validate -c <config-path>
|
|
288
297
|
```
|
|
289
298
|
|
|
290
299
|
### Step 6: Register and Start as a Service
|
|
@@ -316,9 +325,9 @@ sudo systemctl enable --now jeeves-watcher
|
|
|
316
325
|
**Windows (NSSM):**
|
|
317
326
|
|
|
318
327
|
```powershell
|
|
319
|
-
jeeves-watcher service install
|
|
328
|
+
jeeves-watcher service install -c <config-path>
|
|
320
329
|
# Or manually:
|
|
321
|
-
nssm install jeeves-watcher
|
|
330
|
+
nssm install jeeves-watcher (Get-Command jeeves-watcher).Source start -c <config-path>
|
|
322
331
|
nssm set jeeves-watcher AppEnvironmentExtra GOOGLE_API_KEY=<key>
|
|
323
332
|
nssm start jeeves-watcher
|
|
324
333
|
```
|
|
@@ -337,7 +346,7 @@ Call `watcher_status` (or `curl http://127.0.0.1:1936/status`). Confirm:
|
|
|
337
346
|
- Qdrant collection exists with expected dimensions (3072)
|
|
338
347
|
- Point count is increasing (initial indexing in progress)
|
|
339
348
|
|
|
340
|
-
If the point count is 0 after a minute, check `
|
|
349
|
+
If the point count is 0 after a minute, check `watcher_list_issues` for processing failures.
|
|
341
350
|
|
|
342
351
|
### Step 8: Orientation
|
|
343
352
|
|
|
@@ -359,70 +368,83 @@ On sessions after bootstrap is complete:
|
|
|
359
368
|
|
|
360
369
|
## Tools
|
|
361
370
|
|
|
371
|
+
The plugin registers 18 tools: `watcher_status`, `watcher_config`, `watcher_config_apply`, `watcher_service`, `watcher_search`, `watcher_set_metadata`, `watcher_validate_config`, `watcher_reindex`, `watcher_scan`, `watcher_list_issues`, `watcher_walk`, and the seven version-control tools (see [Version Tracking](#version-tracking)).
|
|
372
|
+
|
|
362
373
|
### `watcher_search`
|
|
363
374
|
|
|
364
|
-
Semantic search over indexed documents.
|
|
375
|
+
Semantic search over indexed documents (`POST /search`).
|
|
365
376
|
|
|
366
|
-
- `query` (string, required) — natural language search query
|
|
367
|
-
- `limit` (number, optional) — max results, default 10
|
|
368
|
-
- `offset` (number, optional) — skip N results for pagination
|
|
377
|
+
- `query` (string, required) — natural language search query (non-empty)
|
|
378
|
+
- `limit` (number, optional) — max results (positive integer), default 10
|
|
379
|
+
- `offset` (number, optional) — skip N results for pagination (non-negative integer)
|
|
369
380
|
- `filter` (object, optional) — Qdrant filter for metadata filtering
|
|
370
381
|
|
|
371
|
-
|
|
382
|
+
Returns an array of `{ id, score, payload }` ordered by score (see [Search Result Shape](#search-result-shape)). A filter Qdrant rejects is `400 invalid_request`; an exhausted embedding budget is `503 embedding_unavailable`.
|
|
383
|
+
|
|
384
|
+
### `watcher_set_metadata`
|
|
372
385
|
|
|
373
|
-
Set or update metadata on a document.
|
|
386
|
+
Set or update metadata on a document (`POST /metadata`).
|
|
374
387
|
|
|
375
|
-
- `path` (string, required) — file path of the document
|
|
388
|
+
- `path` (string, required) — file path of the document (absolute or watched path)
|
|
376
389
|
- `metadata` (object, required) — key-value metadata to merge
|
|
377
390
|
|
|
391
|
+
Returns `{ ok: true, matched_rules }`. If the document is busy (being indexed) for 10 s, the tool reports `503 not_available` with `Retry-After: 10` and changes nothing; retry after 10 s. The tool addresses files only; to enrich a source document, call `POST /metadata` directly with `{ "source": "<name>://<id>", "metadata": { ... } }` (exactly one of `path` or `source`).
|
|
392
|
+
|
|
378
393
|
### `watcher_status`
|
|
379
394
|
|
|
380
|
-
Service health check. Returns `name`, `version`, `uptime`, `status`, and `health` object
|
|
395
|
+
Service health check (`GET /status`, never needs the API key). Returns `name`, `version`, `uptime`, `status`, and a `health` object: `collection` (`name`, `pointCount`, `dimensions`, and `systemIndexes`: the state of each index the service creates itself, e.g. `{ "chunk_index": "ready", "file_path": "ready", "matched_rules": "building" }`; `building`, `ready`, `conflict` or `failed`), `embedding` (`state` `ok` or `failing`, `lastSuccessAt`, `lastError` `{ at, code }`, `failingSince`, `backoffUntil`; codes only, never provider text), `reindex` (`active`; while active also `scope`, `startedAt`, `filesProcessed`, `totalFiles`), `initialScan`, and `vcs` when version tracking is on: `roots`, `pending` (changes not yet committed), `trippedRoots` (roots whose commits keep failing) and `oldestTrippedAt`.
|
|
381
396
|
|
|
382
397
|
After a service restart, the `health.initialScan` field shows scan progress:
|
|
383
398
|
|
|
384
|
-
- `active: true` — filesystem walk in progress; `filesMatched` and `filesEnqueued` grow until chokidar completes
|
|
399
|
+
- `active: true` — filesystem walk in progress; `filesMatched` and `filesEnqueued` grow until the initial walk (or chokidar's scan) completes
|
|
385
400
|
- `active: false` with `completedAt`/`durationMs` — scan finished
|
|
386
401
|
|
|
387
402
|
Use this to determine if the service is still initializing after a restart.
|
|
388
403
|
|
|
389
404
|
### `watcher_config`
|
|
390
405
|
|
|
391
|
-
Query the effective runtime config via JSONPath. Returns the full resolved merged document when no path is provided.
|
|
406
|
+
Query the effective runtime config via JSONPath (`GET /config`). Returns the full resolved merged document when no path is provided.
|
|
392
407
|
|
|
393
408
|
- `path` (string, optional) — JSONPath expression
|
|
394
409
|
|
|
395
|
-
|
|
410
|
+
The document holds only the allow-listed keys `description`, `watch`, `configWatch`, `search`, `schemas`, `inferenceRules`, `mapHelpers`, `templateHelpers`, `maps`, `templates`, plus `issues` (the current issues). `api`, `sources`, `embedding`, `vectorStore` (and their keys) are never included. Each rule carries `source` (`config`, or the registering source of a virtual rule) and `values` (the runtime values index). File references (`map`, `maps`, `templates`) and named schema references are always resolved. An invalid JSONPath is `400 invalid_request`.
|
|
411
|
+
|
|
412
|
+
### `watcher_validate_config`
|
|
396
413
|
|
|
397
|
-
Validate config and optionally test file paths.
|
|
414
|
+
Validate config and optionally test file paths (`POST /config/validate`).
|
|
398
415
|
|
|
399
416
|
- `config` (object, optional) — candidate config (partial or full). Omit to validate current config.
|
|
400
417
|
- `testPaths` (string[], optional) — file paths to test against the config
|
|
401
418
|
|
|
402
|
-
Partial configs
|
|
419
|
+
Partial configs are merged into the running config shallowly: each top-level key replaces the running value, except `inferenceRules`, which merge by rule name. This differs from `watcher_config_apply` (deep merge), so pass a whole top-level object (e.g. all of `watch`) when validating a change inside it. Returns `{ valid: false, errors: [{ path, message }] }`, or `{ valid: true, warnings?, testResults? }`, where each test result is `{ path, matchedRules, metadata, error? }` (`error: "File not found"` when the path cannot be read). Validation problems come back in a `200` body, so this is the place to see them in full.
|
|
403
420
|
|
|
404
421
|
### `watcher_config_apply`
|
|
405
422
|
|
|
406
|
-
Apply config changes
|
|
423
|
+
Apply config changes (`POST /config/apply`, sent as `{ patch: config }`).
|
|
407
424
|
|
|
408
425
|
- `config` (object, required) — full or partial config to apply
|
|
426
|
+
- `replace` (boolean, optional) — replace the whole config with `config` instead of merging
|
|
427
|
+
|
|
428
|
+
Partial configs are deep-merged into the config file the service runs from (objects recursively, arrays replaced, `inferenceRules` by rule name), so `{}` or a single key is valid. The merged result is validated and written to disk, then the `configWatch.reindex` scope (default `issues`) runs; that reindex covers files only. With `configWatch.enabled` (default `true`) the service picks up the written file without a restart, including new/removed watch paths (the filesystem watcher is rebuilt), inference rule changes, move detection settings, and gitignore filter updates.
|
|
409
429
|
|
|
410
|
-
|
|
430
|
+
Returns `{ applied: true, config }`. `config` is redacted through the same allow-list as `GET /config`: `api`, `sources` and provider keys are never echoed. When the file was written but the post-write apply step failed, the body also carries `warning` (a string describing the failure). An invalid merged config is a tool error reading `HTTP 400: {"error":"Config validation failed","code":"invalid_request","issues":[...]}`, so run `watcher_validate_config` first. A config file the service cannot read or write is `HTTP 500` with the generic `internal_error` body, the cause in the service log.
|
|
411
431
|
|
|
412
432
|
### `watcher_reindex`
|
|
413
433
|
|
|
414
|
-
Trigger a reindex operation. Non-prune scopes return a `plan` object showing blast area
|
|
434
|
+
Trigger a reindex operation (`POST /reindex`). Non-prune scopes return a `plan` object showing the blast area; the live run reuses the same targets. A live prune (non-dry-run) returns immediately without a plan.
|
|
415
435
|
|
|
416
436
|
**Parameters:**
|
|
417
437
|
|
|
418
|
-
- `scope` (string, optional) — Reindex scope.
|
|
419
|
-
- `"rules"` — Re-apply inference rules to
|
|
420
|
-
- `"full"` — Re-extract
|
|
421
|
-
- `"issues"` — Re-process
|
|
422
|
-
- `"path"` — Re-
|
|
423
|
-
- `"prune"` — Delete Qdrant points for files no longer in watch scope (removed paths, gitignored files,
|
|
424
|
-
- `path` (string,
|
|
425
|
-
- `dryRun` (boolean, optional) — When `true`, compute and return the
|
|
438
|
+
- `scope` (string, optional) — Reindex scope. The tool sends `"rules"` when omitted (the HTTP API's own default is `"issues"`). One of:
|
|
439
|
+
- `"rules"` — Re-apply inference rules to every watched file (no re-embedding; changed values written, keys the rules no longer project removed). With `path`, only files matching those globs. Without `path`, every indexed source document is also retrieved through its callback and its stored rule re-applied: unchanged content gets a metadata-only update, and a document is re-embedded only if its rendered text changed.
|
|
440
|
+
- `"full"` — Re-extract and re-process every watched file (a file is re-embedded when the text to embed changed or it was never indexed), and retrieve and always re-embed every indexed source document. Clears the runtime values index first.
|
|
441
|
+
- `"issues"` — Re-process the documents in `watcher_list_issues`: failed files, and failed source references (with their stored rule, or the `rule` recorded on the issue when the document was never indexed).
|
|
442
|
+
- `"path"` — Re-process a specific file or all files under a directory. Requires `path`. Each target must be watched and not gitignored (`403 path_not_watched`) and must exist (`404 not_found`). Files only.
|
|
443
|
+
- `"prune"` — Delete Qdrant points for files no longer in watch scope (removed paths, gitignored files, deleted files). No re-embedding. Pure cleanup. Never selects source points (`source` set, no `file_path`).
|
|
444
|
+
- `path` (string | string[], optional) — Target file(s) or directories for `"path"` (required there), or globs narrowing `"rules"`. Ignored by other scopes.
|
|
445
|
+
- `dryRun` (boolean, optional) — When `true`, compute and return the plan without executing.
|
|
446
|
+
|
|
447
|
+
The tool has no `source` parameter. To re-process specific source references, call `POST /reindex` directly with `{ "scope": "path", "source": ["<name>://<id>", ...] }` (at most 1000): they are enqueued on the notify queue, are not tracked by `GET /status`, and send no completion callback. A reindex triggered by a config change (`watcher_config_apply`, config-file reload, rule registration) covers files only.
|
|
426
448
|
|
|
427
449
|
**Response (normal):**
|
|
428
450
|
|
|
@@ -434,7 +456,7 @@ Trigger a reindex operation. Non-prune scopes return a `plan` object showing bla
|
|
|
434
456
|
"total": 148000,
|
|
435
457
|
"toProcess": 148000,
|
|
436
458
|
"toDelete": 0,
|
|
437
|
-
"byRoot": { "
|
|
459
|
+
"byRoot": { "/data": 95000, "/srv/config": 3000 }
|
|
438
460
|
}
|
|
439
461
|
}
|
|
440
462
|
```
|
|
@@ -449,31 +471,34 @@ Trigger a reindex operation. Non-prune scopes return a `plan` object showing bla
|
|
|
449
471
|
"total": 562000,
|
|
450
472
|
"toProcess": 0,
|
|
451
473
|
"toDelete": 2300,
|
|
452
|
-
"byRoot": { "
|
|
474
|
+
"byRoot": { "/srv/app/node_modules": 1800, "/srv/app/.cache": 500 }
|
|
453
475
|
}
|
|
454
476
|
}
|
|
455
477
|
```
|
|
456
478
|
|
|
457
479
|
**Plan fields:**
|
|
458
480
|
|
|
459
|
-
- `total` —
|
|
460
|
-
- `toProcess` —
|
|
481
|
+
- `total` — Points scrolled (prune), or files plus source documents covered (other scopes).
|
|
482
|
+
- `toProcess` — Files plus source documents to process (0 for prune).
|
|
461
483
|
- `toDelete` — Points to delete (prune only, 0 for others).
|
|
462
|
-
- `byRoot` —
|
|
484
|
+
- `byRoot` — Files grouped by watch root (for prune: orphaned files), plus one entry per source name for source documents. Shows where the impact concentrates.
|
|
485
|
+
- `incomplete` — `true` when the prune scroll failed part-way and the plan is partial.
|
|
486
|
+
|
|
487
|
+
**Progress:** a live run (every scope except `path` with `source`) is tracked in `watcher_status` under `health.reindex` (`filesProcessed` / `totalFiles`, counting files plus source documents). A failing file or source document is logged and recorded; the rest of the run continues.
|
|
463
488
|
|
|
464
489
|
**Guidance:**
|
|
465
490
|
|
|
466
491
|
- Use `dryRun: true` before any large-blast operation to preview impact.
|
|
467
|
-
- `prune` is safe — it only deletes orphaned points, never re-embeds. Use after changing watch paths, fixing gitignore, or cleaning up stale data.
|
|
492
|
+
- `prune` is safe — it only deletes orphaned file points, never re-embeds. Use after changing watch paths, fixing gitignore, or cleaning up stale data.
|
|
468
493
|
- `prune` is NOT triggered by config-watch auto-reindex (too dangerous for auto-trigger).
|
|
469
494
|
|
|
470
495
|
### `watcher_scan`
|
|
471
496
|
|
|
472
|
-
Filter-only point query without vector search. Use for structural queries where the question has no semantic dimension.
|
|
497
|
+
Filter-only point query without vector search (`POST /scan`). Use for structural queries where the question has no semantic dimension.
|
|
473
498
|
|
|
474
499
|
- `filter` (object, required) — Qdrant filter object. Required to prevent accidental full-collection scans.
|
|
475
|
-
- `limit` (number, optional) — page size, default 100
|
|
476
|
-
- `cursor` (string, optional) — opaque cursor from previous response for pagination
|
|
500
|
+
- `limit` (number, optional) — page size (integer 1 to 1000), default 100
|
|
501
|
+
- `cursor` (string or number, optional) — opaque cursor from previous response for pagination
|
|
477
502
|
- `fields` (string[], optional) — payload fields to return (projection)
|
|
478
503
|
- `countOnly` (boolean, optional) — if true, return `{ count }` instead of points
|
|
479
504
|
|
|
@@ -481,8 +506,13 @@ Filter-only point query without vector search. Use for structural queries where
|
|
|
481
506
|
|
|
482
507
|
```json
|
|
483
508
|
{
|
|
484
|
-
"points": [
|
|
485
|
-
|
|
509
|
+
"points": [
|
|
510
|
+
{
|
|
511
|
+
"id": "uuid",
|
|
512
|
+
"payload": { "file_path": "/data/tickets/PROJ-123.json" }
|
|
513
|
+
}
|
|
514
|
+
],
|
|
515
|
+
"cursor": "opaque string, number or null"
|
|
486
516
|
}
|
|
487
517
|
```
|
|
488
518
|
|
|
@@ -496,7 +526,7 @@ Filter-only point query without vector search. Use for structural queries where
|
|
|
496
526
|
|
|
497
527
|
- No `query` parameter — does NOT use embeddings
|
|
498
528
|
- No `score` field — results are unranked filter matches
|
|
499
|
-
- Cursor-based pagination (not offset-based)
|
|
529
|
+
- Cursor-based pagination (not offset-based); `cursor` is `null` on the last page
|
|
500
530
|
- Zero cost per call beyond Qdrant's filtered scroll
|
|
501
531
|
|
|
502
532
|
**Pagination pattern:**
|
|
@@ -512,29 +542,29 @@ do {
|
|
|
512
542
|
|
|
513
543
|
### `watcher_service`
|
|
514
544
|
|
|
515
|
-
Manage the watcher background service (install, uninstall, start, stop, restart, check status).
|
|
545
|
+
Manage the watcher background service (install, uninstall, start, stop, restart, check status). No HTTP call.
|
|
516
546
|
|
|
517
547
|
- `action` (string, required) — one of: `install`, `uninstall`, `start`, `stop`, `restart`, `status`
|
|
518
548
|
|
|
519
|
-
Returns the service manager's response. On Windows uses NSSM, on Linux
|
|
549
|
+
Returns the service manager's response. On Windows uses NSSM, on Linux systemd, on macOS launchd. Only `install` needs `configRoot` (see [Plugin Installation](#plugin-installation)).
|
|
520
550
|
|
|
521
|
-
### `
|
|
551
|
+
### `watcher_list_issues`
|
|
522
552
|
|
|
523
|
-
Get runtime
|
|
553
|
+
Get runtime processing failures (`GET /issues`). Returns `{ count, issues: { [key]: IssueRecord[] } }`: `count` is the number of documents with issues, and each key is a file path or a source reference (`<name>://<id>`). Each `IssueRecord` has `type` (one of the [issue codes](#error-categories)), `message`, `timestamp`, and optionally `operation`, `property`, `rule`, `rules`, `types`, `details`.
|
|
524
554
|
|
|
525
555
|
### `watcher_walk`
|
|
526
556
|
|
|
527
|
-
|
|
557
|
+
List watched files matching globs (`POST /walk`). Returns matching file paths from the watcher's in-memory list of watched files across all configured watch roots.
|
|
528
558
|
|
|
529
|
-
- `globs` (string[], required) — glob patterns to intersect with watch paths
|
|
559
|
+
- `globs` (string[], required) — glob patterns to intersect with watch paths (non-empty; matched case-insensitively, dotfiles included)
|
|
530
560
|
|
|
531
561
|
**Response:**
|
|
532
562
|
|
|
533
563
|
```json
|
|
534
564
|
{
|
|
535
|
-
"paths": ["
|
|
565
|
+
"paths": ["/data/foo/.meta/meta.json", "/data/bar/.meta/meta.json"],
|
|
536
566
|
"matchedCount": 2,
|
|
537
|
-
"scannedRoots": ["
|
|
567
|
+
"scannedRoots": ["/data", "/srv/config"]
|
|
538
568
|
}
|
|
539
569
|
```
|
|
540
570
|
|
|
@@ -542,12 +572,12 @@ Walk watched filesystem paths with glob intersection. Returns matching file path
|
|
|
542
572
|
|
|
543
573
|
- Discover files matching a pattern across all watched directories (e.g., `["**/.meta/meta.json"]`)
|
|
544
574
|
- Enumerate files before rule registration to understand scope
|
|
545
|
-
- Find files that aren't yet indexed (no Qdrant dependency
|
|
575
|
+
- Find files that aren't yet indexed (no Qdrant dependency)
|
|
546
576
|
|
|
547
577
|
**Key differences from `watcher_scan`:**
|
|
548
578
|
|
|
549
|
-
-
|
|
550
|
-
- No embedding or indexing required
|
|
579
|
+
- Lists the files the filesystem watcher tracks, not the Qdrant index
|
|
580
|
+
- No embedding or indexing required, but the initial filesystem scan must be complete: until then the tool returns `HTTP 503 scan_in_progress` (check `health.initialScan` in `watcher_status`)
|
|
551
581
|
- Returns file paths only (no metadata, no vectors)
|
|
552
582
|
- Applies `watch.ignored` and gitignore filtering automatically
|
|
553
583
|
|
|
@@ -580,7 +610,7 @@ Walk watched filesystem paths with glob intersection. Returns matching file path
|
|
|
580
610
|
**Path prefix matching:**
|
|
581
611
|
|
|
582
612
|
```json
|
|
583
|
-
{ "must": [{ "key": "file_path", "match": { "text": "
|
|
613
|
+
{ "must": [{ "key": "file_path", "match": { "text": "/data/tickets" } }] }
|
|
584
614
|
```
|
|
585
615
|
|
|
586
616
|
**Count files in a domain (no point data transferred):**
|
|
@@ -660,15 +690,21 @@ Each result from `watcher_search` contains:
|
|
|
660
690
|
| Field | Type | Description |
|
|
661
691
|
| --- | --- | --- |
|
|
662
692
|
| `id` | string | Qdrant point ID |
|
|
663
|
-
| `score` | number | Similarity score
|
|
664
|
-
| `payload.file_path` | string |
|
|
693
|
+
| `score` | number | Similarity (or hybrid fused) score; higher = more relevant |
|
|
694
|
+
| `payload.file_path` | string | Normalized file path (file points only) |
|
|
695
|
+
| `payload.source` | string | Source reference `<name>://<id>` (source points only) |
|
|
696
|
+
| `payload.source_rule` | string | Designated rule of a source document (source points only) |
|
|
665
697
|
| `payload.chunk_text` | string | The matched text chunk |
|
|
666
|
-
| `payload.chunk_index` | number |
|
|
667
|
-
| `payload.total_chunks` | number | Total chunks for this
|
|
668
|
-
| `payload.
|
|
698
|
+
| `payload.chunk_index` | number | Zero-based chunk position within the document |
|
|
699
|
+
| `payload.total_chunks` | number | Total chunks for this document |
|
|
700
|
+
| `payload.line_start` | number | 1-indexed line where the chunk starts |
|
|
701
|
+
| `payload.line_end` | number | 1-indexed line where the chunk ends |
|
|
702
|
+
| `payload.created_at` | number | Creation timestamp (unix seconds) |
|
|
703
|
+
| `payload.modified_at` | number | Modification timestamp (unix seconds) |
|
|
704
|
+
| `payload.content_hash` | string | SHA-256 hash of the embedded text |
|
|
669
705
|
| `payload.matched_rules` | string[] | Names of inference rules that matched |
|
|
670
706
|
|
|
671
|
-
Additional metadata fields depend on the deployment's inference rules (e.g., `domain`, `status`, `author`). Use `watcher_config` to discover available fields.
|
|
707
|
+
`file_path` is not always present: a source point (a document indexed through a source callback) carries a non-empty `source` and no `file_path`. A file point may also carry a `source` written by its own rules; it is still a file point. Additional metadata fields depend on the deployment's inference rules (e.g., `domain`, `status`, `author`). Use `watcher_config` to discover available fields.
|
|
672
708
|
|
|
673
709
|
## Query Planning (Per Search Task)
|
|
674
710
|
|
|
@@ -678,10 +714,9 @@ Identify relevant rule(s) from the orientation model, then retrieve their schema
|
|
|
678
714
|
|
|
679
715
|
```
|
|
680
716
|
watcher_config: path="$.inferenceRules[?(@.name=='jira-issue')].schema"
|
|
681
|
-
resolve=["files","globals"]
|
|
682
717
|
```
|
|
683
718
|
|
|
684
|
-
Returns the
|
|
719
|
+
Returns the rule's schema array with named schema references and file references already resolved: properties, types, `set` expressions, `uiHint`, `enum`, etc.
|
|
685
720
|
|
|
686
721
|
**For select/multiselect fields without `enum` in schema:**
|
|
687
722
|
|
|
@@ -704,7 +739,7 @@ Use `uiHint` to determine filter construction strategy. **This table is explicit
|
|
|
704
739
|
| `text` | `{ "key": "<field>", "match": { "text": "<value>" } }` | Substring/keyword match |
|
|
705
740
|
| `select` | `{ "key": "<field>", "match": { "value": "<enum_value>" } }` | Exact match; use `enum` values from schema or runtime values index |
|
|
706
741
|
| `multiselect` | `{ "key": "<field>", "match": { "value": "<enum_value>" } }` | Any-element match on array field; use `enum` or runtime values index |
|
|
707
|
-
| `date` | `{ "key": "<field>", "range": { "gte": <unix_ts>, "lt": <unix_ts> } }` | Range filter against integer fields holding Unix timestamps (seconds). Source dates should be normalized in config via `{{toUnix ...}}` in `set` expressions.
|
|
742
|
+
| `date` | `{ "key": "<field>", "range": { "gte": <unix_ts>, "lt": <unix_ts> } }` | Range filter against integer fields holding Unix timestamps (seconds). Source dates should be normalized in config via `{{toUnix ...}}` in `set` expressions. Either bound optional for open-ended ranges (e.g., "after January" → `gte` only) |
|
|
708
743
|
| `number` | `{ "key": "<field>", "range": { "gte": <n>, "lte": <n> } }` | Either bound optional for open-ended ranges |
|
|
709
744
|
| `check` | `{ "key": "<field>", "match": { "value": true } }` | Boolean match |
|
|
710
745
|
| _(absent)_ | Do not use in filters | Internal bookkeeping field, not intended for search |
|
|
@@ -768,7 +803,7 @@ Use `scoreThresholds` from config (queried during orientation). Values are deplo
|
|
|
768
803
|
|
|
769
804
|
### Chunk Grouping
|
|
770
805
|
|
|
771
|
-
Multiple results with the same `file_path` are chunks of one document. Read the full file for complete context.
|
|
806
|
+
Multiple results with the same `file_path` (or, for source documents, the same `source`) are chunks of one document. Read the full file for complete context.
|
|
772
807
|
|
|
773
808
|
### Schema Lookup
|
|
774
809
|
|
|
@@ -776,27 +811,25 @@ Use `matched_rules` on results to look up applicable schemas for metadata interp
|
|
|
776
811
|
|
|
777
812
|
```
|
|
778
813
|
watcher_config: path="$.inferenceRules[?(@.name=='jira-issue')].schema"
|
|
779
|
-
resolve=["files","globals"]
|
|
780
814
|
```
|
|
781
815
|
|
|
782
816
|
### Full Context
|
|
783
817
|
|
|
784
|
-
Search gives you chunks; use `read` with `file_path` for the complete document.
|
|
818
|
+
Search gives you chunks; use `read` with `file_path` for the complete document. A source document has no file: render it with `POST /render` and `{ "source": "<ref>" }`, which retrieves it through its callback.
|
|
785
819
|
|
|
786
820
|
---
|
|
787
821
|
|
|
788
822
|
## Path Testing
|
|
789
823
|
|
|
790
|
-
When uncertain whether a file
|
|
824
|
+
When uncertain whether a file would be indexed, test it against the rules. `watcher_validate_config` with `testPaths` (and no `config`) runs the current rules against each path and returns `{ path, matchedRules, metadata }` per path (`error: "File not found"` when the file cannot be read):
|
|
791
825
|
|
|
792
826
|
```
|
|
793
|
-
|
|
827
|
+
watcher_validate_config: testPaths=["/data/tickets/PROJ-123.json"]
|
|
794
828
|
```
|
|
795
829
|
|
|
796
|
-
Or
|
|
830
|
+
Or call `POST /config/match` directly with `{ "paths": [...] }`. It returns `{ matches: [{ rules, watched }] }`, one entry per path, without reading the files:
|
|
797
831
|
|
|
798
|
-
-
|
|
799
|
-
- Empty `rules` array means no inference rules match
|
|
832
|
+
- `rules` lists the matching rule names; an empty array means no inference rules match
|
|
800
833
|
- `watched: false` means the path falls outside watch paths or is excluded by ignore patterns
|
|
801
834
|
|
|
802
835
|
---
|
|
@@ -809,31 +842,40 @@ Each inference rule has:
|
|
|
809
842
|
|
|
810
843
|
- `name` (required) — unique identifier
|
|
811
844
|
- `description` (required) — human-readable purpose
|
|
812
|
-
- `match` — JSON Schema
|
|
813
|
-
- `schema` — array of named schema references and/or inline schema objects with `set` templates
|
|
814
|
-
- `map` (optional) — named
|
|
815
|
-
- `template` (optional) — named
|
|
845
|
+
- `match` (required) — JSON Schema matched against file attributes (e.g. a picomatch `glob` on `file.path`)
|
|
846
|
+
- `schema` (optional) — array of named schema references and/or inline schema objects with `set` templates
|
|
847
|
+
- `map` (optional) — JsonMap transform (inline, named map, or `.json` file path)
|
|
848
|
+
- `template` (optional) — Handlebars template (inline, named template, or `.hbs`/`.handlebars` file path)
|
|
849
|
+
- `render` (optional) — declarative frontmatter + Markdown renderer (mutually exclusive with `template`)
|
|
850
|
+
- `renderAs` (optional) — output extension (1-10 lowercase alphanumerics); requires `template` or `render`
|
|
851
|
+
|
|
852
|
+
### Authoring Notes
|
|
853
|
+
|
|
854
|
+
- **`maps` entries are bare strings or inline maps.** A string value is a file path, loaded and parsed as a JsonMap at startup. A `{ path, description }` object is kept as-is and never loaded from the file, so it silently does nothing useful. (`mapHelpers` and `templateHelpers` are different: `{ path, description }` objects are their documented form.)
|
|
855
|
+
- **Metadata is derived with `set`, not `attributes`.** Each schema property's `set` template produces its value. There is no `attributes` key in the config schema; older configs and examples that use one need rewriting.
|
|
856
|
+
- **`render.frontmatter` accepts picomatch globs and `!` exclusions** alongside explicit keys, e.g. `["meta_id", "*", "!_*", "!chunk_*"]`. Explicit names keep their listed order; keys matched by a glob follow, sorted alphabetically; a key matching an exclusion is dropped.
|
|
857
|
+
- **Virtual rules have a startup window.** Virtual rules are registered at runtime by other services (`POST /rules/register`, e.g. jeeves-meta), so they are absent during the initial file scan after a watcher start. Files indexed in that window lack virtual-rule metadata until the registration's automatic rules reindex runs, or until `POST /rules/reapply` covers them. If metadata from a virtual rule is missing right after a restart, check whether its service has registered yet.
|
|
816
858
|
|
|
817
859
|
### Config Workflow
|
|
818
860
|
|
|
819
861
|
1. Edit config (or build partial config object)
|
|
820
|
-
2. Validate: `
|
|
821
|
-
3. Apply: `watcher_config_apply` — validates, writes, triggers reindex
|
|
822
|
-
4. Monitor: `
|
|
862
|
+
2. Validate: `watcher_validate_config` with optional `testPaths` for dry-run preview
|
|
863
|
+
3. Apply: `watcher_config_apply` — validates, writes, triggers the `configWatch.reindex` reindex
|
|
864
|
+
4. Monitor: `watcher_list_issues` for runtime processing failures
|
|
823
865
|
|
|
824
866
|
### Reindex Concurrency
|
|
825
867
|
|
|
826
|
-
Reindex operations process files concurrently using `reindex.concurrency` (default 50). This applies to
|
|
868
|
+
Reindex operations process files and source documents concurrently using `reindex.concurrency` (default 50). This applies to every scope, to issue-source discovery, and to the stored-rule lookups of `POST /reindex` with `source`. The watcher's incremental processing (file events and source notifications) uses a separate `embedding.concurrency` setting.
|
|
827
869
|
|
|
828
|
-
Progress is reported via `watcher_status` (`reindex.filesProcessed` / `reindex.totalFiles
|
|
870
|
+
Progress is reported via `watcher_status` (`health.reindex.filesProcessed` / `health.reindex.totalFiles`, counting files plus source documents).
|
|
829
871
|
|
|
830
872
|
### When to Reindex
|
|
831
873
|
|
|
832
|
-
- **Rules scope** (`"rules"`): Changed rule matching patterns, set expressions, schema mappings. No re-embedding needed.
|
|
833
|
-
- **Full scope** (`"full"`):
|
|
834
|
-
- **Issues scope** (`"issues"`): After fixing the root cause of
|
|
835
|
-
- **Path scope** (`"path"`): Edited files in a specific directory and want
|
|
836
|
-
- **Prune scope** (`"prune"`): After changing `watch.paths`, adding gitignore rules, or discovering stale/orphaned points (e.g., indexed `node_modules`). Deletes points for out-of-scope files. Always `dryRun: true` first to preview.
|
|
874
|
+
- **Rules scope** (`"rules"`): Changed rule matching patterns, set expressions, schema mappings. No file re-embedding needed.
|
|
875
|
+
- **Full scope** (`"full"`): Added watch paths, broad schema restructuring, or source documents whose vectors must be rebuilt. Re-extracts every file (re-embedding the ones whose text to embed changed) and re-embeds every source document.
|
|
876
|
+
- **Issues scope** (`"issues"`): After fixing the root cause of processing failures (permissions, encoding, file format, a source outage). Re-processes only the failed files and source documents.
|
|
877
|
+
- **Path scope** (`"path"`): Edited files in a specific directory, or a single file looks stale, and you want them re-processed without a full reindex. A file whose text to embed is unchanged is not re-embedded.
|
|
878
|
+
- **Prune scope** (`"prune"`): After changing `watch.paths`, adding gitignore rules, or discovering stale/orphaned points (e.g., indexed `node_modules`). Deletes points for out-of-scope or deleted files. Always `dryRun: true` first to preview.
|
|
837
879
|
|
|
838
880
|
---
|
|
839
881
|
|
|
@@ -842,19 +884,50 @@ Progress is reported via `watcher_status` (`reindex.filesProcessed` / `reindex.t
|
|
|
842
884
|
### Escalation Path
|
|
843
885
|
|
|
844
886
|
1. `watcher_status` — is the service healthy? Is a reindex running? Is the initial scan still active?
|
|
845
|
-
2. `
|
|
846
|
-
3. `watcher_config` with `$.issues` — same
|
|
887
|
+
2. `watcher_list_issues` — which documents are failing and why?
|
|
888
|
+
3. `watcher_config` with `$.issues` — the same issue records via JSONPath
|
|
847
889
|
4. Check logs at the configured log path
|
|
848
890
|
|
|
849
891
|
### Error Categories
|
|
850
892
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
- `
|
|
854
|
-
- `
|
|
893
|
+
Every issue record's `type` is one of these codes:
|
|
894
|
+
|
|
895
|
+
- `type_collision` — conflicting metadata types across rules (includes `property`, `rules[]`, `types[]`)
|
|
896
|
+
- `interpolation_error` — template or `set` expression rendering failed (includes `property`, `rule`; `details` holds per-step failures)
|
|
897
|
+
- `extraction_error` — reading, rule application or chunking failed
|
|
898
|
+
- `embedding_error` — the embedding provider failed
|
|
899
|
+
- `upsert_error` — the vector store write failed
|
|
900
|
+
- `match_failed` — a source document's designated rule did not match (`details` holds the AJV errors)
|
|
901
|
+
- `rule_missing` — a source document's designated rule was not found
|
|
902
|
+
- `no_content` — nothing to embed
|
|
903
|
+
- `source_unavailable` — the source callback failed
|
|
904
|
+
- `unsupported_content_type` — the source callback returned a non-JSON body
|
|
905
|
+
- `too_large` — the document is over a `processing` ceiling (`maxFileBytes`, `maxChunksPerFile` or `maxTextBytes`); it is not embedded and its points are deleted. Raise the ceiling and run `reindex` with scope `issues` to restore it
|
|
906
|
+
|
|
907
|
+
Source-document issues always carry `rule` (the designated rule of the failed run).
|
|
855
908
|
|
|
856
909
|
**Issues are self-healing:** resolved on successful re-process. The issues file always represents the current set of unresolved problems: a live todo list.
|
|
857
910
|
|
|
911
|
+
### Change Source
|
|
912
|
+
|
|
913
|
+
`watcher_status` reports the change source under `health.changeSource` (`@parcel/watcher` by default, chokidar on request or when polling):
|
|
914
|
+
|
|
915
|
+
- **Service refuses to start, log names `@parcel/watcher failed to load`:** the platform binary is missing or broken. Reinstall without `--omit=optional`, or set `watch.changeSource: chokidar` (the manual switch; a reload rebuilds the watcher with one full initial scan).
|
|
916
|
+
- **Rising `reconcileEvents`:** the source is missing events and reconciliation is finding them. **Rising `resubscribes`:** subscriptions keep failing (Linux). **Rising `renewFailures`:** a Linux renewal could not unsubscribe; restart the service. **`unavailableRoots` above 0:** a watch root is missing or unreadable (an unmounted share); its files keep their points until it returns. **High `pendingStability`, or a long `lastReconcileDurationMs` / `lastRenewDurationMs`:** a large write burst or a slow disk; a long renewal pauses event handling for every root.
|
|
917
|
+
- **Linux, a file in a moved-in or renamed folder is indexed late:** changes inside folders nested in a newly created or moved-in tree, or in a directory renamed within a root, are missed until the root's subscription is renewed at the next periodic reconcile, up to `watch.reconcileIntervalMs` (15 minutes by default) late. Expected; shorten the interval if the latency matters. The files moved in with the tree are indexed at once.
|
|
918
|
+
- **Linux, events lost under a write burst with nothing in the log:** parcel cannot detect an inotify queue overflow. Check `sysctl fs.inotify.max_queued_events` (recommended 1048576) and `fs.inotify.max_user_watches` (the start log warns at 90%); the periodic reconcile finds what was lost (`reconcileEvents` rises).
|
|
919
|
+
- **Windows, files in deep folders indexed only by reconciliation:** expected. Under parcel, creating or changing a file whose path is 260 characters or more produces no change event at either `LongPathsEnabled` setting, so it is indexed at the next periodic reconcile, up to `watch.reconcileIntervalMs` late. Nothing to fix; check that `watch.reconcileIntervalMs` is not 0. (`LongPathsEnabled = 1` is still recommended for other tools; it is a host change for the owner.)
|
|
920
|
+
- **Windows, `lastRestartCause` set:** under parcel the service never unsubscribes on Windows (`@parcel/watcher` 2.6.0 crashes the process there), so a reload that rebuilds the watcher, a subscription error or a vanished watch root exits with code 75 and the service manager restarts it. `watch-config-changed`, `subscription-error` or `root-gone` says why, `lastRestartAt` when. If the service stays down after such an exit, its service manager is not restarting it on exit (NSSM: `AppExit Default Restart`).
|
|
921
|
+
|
|
922
|
+
### Index Check at Start
|
|
923
|
+
|
|
924
|
+
After the start walk the service deletes the indexed documents of files gone from disk (deleted or moved while it was down). `watcher_status` reports it under `health.indexCheck` (`state`, `at`, `durationMs`, `checked`, `removed`, `skippedRoots`, `capped`):
|
|
925
|
+
|
|
926
|
+
- **`capped` above 0:** under some watch root more than 10% of the indexed documents (once more than 20 are missing) or more than 1,000 were missing from disk or filters, so nothing was deleted there; the log names the root. Run `watcher_reindex` with scope `prune` and `dryRun: true`, check the plan, then run it.
|
|
927
|
+
- **`state: failed`:** the index could not be listed (Qdrant down), or the service stopped first; it runs again at the next start.
|
|
928
|
+
- **`skippedRoots` above 0:** a root was unavailable, unwatched, gone, or its start walk failed; its deletions wait for the next start or a `prune`.
|
|
929
|
+
- **Files that still exist but are now ignored** keep their points: that is `prune`'s job. `watch.indexCheckOnStart: false` turns the check off.
|
|
930
|
+
|
|
858
931
|
---
|
|
859
932
|
|
|
860
933
|
## Helper Management
|
|
@@ -874,25 +947,24 @@ The following helpers are available in every JsonMap `lib` context without any h
|
|
|
874
947
|
|
|
875
948
|
#### `fetchSiblings(filePath, options?)`
|
|
876
949
|
|
|
877
|
-
Retrieve extracted text from neighboring files in the same directory. Useful for contextual embedding — e.g., injecting surrounding email messages into a thread member's embedding for better semantic search.
|
|
950
|
+
Retrieve extracted text from neighboring files in the same directory with the same extension. Useful for contextual embedding — e.g., injecting surrounding email messages into a thread member's embedding for better semantic search.
|
|
878
951
|
|
|
879
952
|
**Parameters:**
|
|
880
953
|
|
|
881
|
-
- `filePath` (string) — the current file path (
|
|
882
|
-
- `options` (object, optional):
|
|
954
|
+
- `filePath` (string) — the current file path (in a rule's `map`, `$.input.file.path`)
|
|
955
|
+
- `options` (object, optional; JsonMap `params` are path strings, so a call from a config `map` uses the defaults):
|
|
883
956
|
- `before` (number, default 3) — number of preceding siblings to include
|
|
884
957
|
- `after` (number, default 1) — number of following siblings to include
|
|
885
958
|
- `sort` (`"name"` | `"mtime"`, default `"name"`) — sort order for determining neighbor position
|
|
886
959
|
|
|
887
|
-
**Returns:** `string[]` — extracted text from sibling files, in sort order. Files that fail extraction are silently skipped.
|
|
960
|
+
**Returns:** `string[]` — extracted text from sibling files, in sort order. Files that fail extraction are silently skipped. For a source document (a source rule's `map`) there is no file reader, so it returns `[]`.
|
|
888
961
|
|
|
889
|
-
**Example** (in a
|
|
962
|
+
**Example** (in a rule's `map`):
|
|
890
963
|
|
|
891
964
|
```json
|
|
892
965
|
{
|
|
893
966
|
"context": {
|
|
894
|
-
"$
|
|
895
|
-
"$args": ["$file_path", { "before": 2, "after": 1 }]
|
|
967
|
+
"$": { "method": "$.lib.fetchSiblings", "params": "$.input.file.path" }
|
|
896
968
|
}
|
|
897
969
|
}
|
|
898
970
|
```
|
|
@@ -901,14 +973,15 @@ Retrieve extracted text from neighboring files in the same directory. Useful for
|
|
|
901
973
|
|
|
902
974
|
## Enrichment
|
|
903
975
|
|
|
904
|
-
Use `
|
|
976
|
+
Use `watcher_set_metadata` to tag documents after analysis (e.g., `reviewed: true`, project labels).
|
|
905
977
|
|
|
906
|
-
**Enrichments are durable.** Stored in a SQLite database (`<stateDir>/enrichments.sqlite`), enrichments survive full reindexes. When the watcher re-processes a file, enrichments are merged with inference rule output using composable semantics: scalar fields overwrite, array fields union+deduplicate.
|
|
978
|
+
**Enrichments are durable.** Stored in a SQLite database (`<stateDir>/enrichments.sqlite`, default `stateDir` `.jeeves-metadata`), enrichments survive full reindexes. Enriching a file that is not indexed yet stores the enrichment, which applies once the file is indexed. When the watcher re-processes a file, enrichments are merged with inference rule output using composable semantics: scalar fields overwrite, array fields union+deduplicate.
|
|
907
979
|
|
|
908
|
-
**Metadata is validated against the file's matched rule schemas
|
|
980
|
+
**Metadata is validated against the file's matched rule schemas** (for a source document, its designated rule's schema). Only declared property types are checked; unknown properties and `null` values pass. The tool reports a failure as `HTTP 400 invalid_request: Validation failed`; the structured `details` are in the HTTP response body:
|
|
909
981
|
|
|
910
982
|
```json
|
|
911
983
|
{
|
|
984
|
+
"code": "invalid_request",
|
|
912
985
|
"error": "Validation failed",
|
|
913
986
|
"details": [
|
|
914
987
|
{
|
|
@@ -926,56 +999,56 @@ Use `watcher_enrich` to tag documents after analysis (e.g., `reviewed: true`, pr
|
|
|
926
999
|
|
|
927
1000
|
## Version Tracking
|
|
928
1001
|
|
|
929
|
-
The watcher includes built-in history and undo for your watched files. When enabled, every change to a watched file is automatically versioned. You can view the history of any file or folder, see exactly what changed between versions, retrieve old versions, and undo changes — all through the
|
|
1002
|
+
The watcher includes built-in history and undo for your watched files. When enabled, every change to a watched file is automatically versioned. You can view the history of any file or folder, see exactly what changed between versions, retrieve old versions, and undo changes — all through the version-control tools below.
|
|
930
1003
|
|
|
931
1004
|
### Tools
|
|
932
1005
|
|
|
933
1006
|
| Tool | Purpose |
|
|
934
1007
|
| --- | --- |
|
|
935
|
-
| `
|
|
936
|
-
| `
|
|
937
|
-
| `
|
|
938
|
-
| `
|
|
939
|
-
| `
|
|
940
|
-
| `
|
|
941
|
-
| `
|
|
1008
|
+
| `watcher_get_vcs_status` | Check version tracking health: enabled state, tracked roots, remote sync status |
|
|
1009
|
+
| `watcher_list_vcs_history` | View change history for a file or folder, with optional date range and limit |
|
|
1010
|
+
| `watcher_show_vcs_file` | Retrieve the content of a file at a specific past version |
|
|
1011
|
+
| `watcher_diff_vcs` | Show what changed between two versions, or between a past version and now |
|
|
1012
|
+
| `watcher_revert_vcs` | Undo changes by restoring files to a specific past version |
|
|
1013
|
+
| `watcher_exclude_vcs_path` | Exclude a path from version history (it stays in search), or remove the exclusion; untracks matching files |
|
|
1014
|
+
| `watcher_check_vcs_exclusion` | Check whether git ignores a path and why (rule, file, line) |
|
|
942
1015
|
|
|
943
1016
|
### Usage Patterns
|
|
944
1017
|
|
|
945
1018
|
**View recent changes to a file:**
|
|
946
1019
|
|
|
947
1020
|
```
|
|
948
|
-
|
|
1021
|
+
watcher_list_vcs_history: glob="/data/tickets/PROJ-123.json", limit=10
|
|
949
1022
|
```
|
|
950
1023
|
|
|
951
1024
|
**See what changed between two versions:**
|
|
952
1025
|
|
|
953
1026
|
```
|
|
954
|
-
|
|
1027
|
+
watcher_diff_vcs: glob="/data/tickets/PROJ-123.json", commit="abc1234"
|
|
955
1028
|
```
|
|
956
1029
|
|
|
957
1030
|
**Restore a file to a previous version:**
|
|
958
1031
|
|
|
959
1032
|
```
|
|
960
|
-
|
|
1033
|
+
watcher_revert_vcs: glob="/data/tickets/PROJ-123.json", commit="abc1234"
|
|
961
1034
|
```
|
|
962
1035
|
|
|
963
1036
|
**View old content without restoring:**
|
|
964
1037
|
|
|
965
1038
|
```
|
|
966
|
-
|
|
1039
|
+
watcher_show_vcs_file: path="/data/tickets/PROJ-123.json", commit="abc1234"
|
|
967
1040
|
```
|
|
968
1041
|
|
|
969
1042
|
**Exclude files that don't need versioning** (e.g., Jira issues that are read-only syncs):
|
|
970
1043
|
|
|
971
1044
|
```
|
|
972
|
-
|
|
1045
|
+
watcher_exclude_vcs_path: glob="/data/tickets/**/*.json"
|
|
973
1046
|
```
|
|
974
1047
|
|
|
975
1048
|
**Check if a file is excluded and why:**
|
|
976
1049
|
|
|
977
1050
|
```
|
|
978
|
-
|
|
1051
|
+
watcher_check_vcs_exclusion: path="/data/tickets/PROJ-123.json"
|
|
979
1052
|
```
|
|
980
1053
|
|
|
981
1054
|
### Enabling Version Tracking
|
|
@@ -986,9 +1059,17 @@ Squash retention activates by default when VCS is enabled, even without explicit
|
|
|
986
1059
|
|
|
987
1060
|
### Excluding Paths
|
|
988
1061
|
|
|
989
|
-
Not all watched files need version history. For example, if Jira issues are synced read-only, there's nothing to "undo." Use `
|
|
1062
|
+
Not all watched files need version history. For example, if Jira issues are synced read-only, there's nothing to "undo." Use `watcher_exclude_vcs_path` to stop tracking specific paths.
|
|
1063
|
+
|
|
1064
|
+
The exclusion is VCS-only: the pattern goes to the root's `.git/info/exclude`, so the files stop being versioned but stay indexed and searchable.
|
|
990
1065
|
|
|
991
|
-
|
|
1066
|
+
- **Destructive.** Adding a pattern untracks the tracked files it matches, in one commit: their history ends at their last committed version (the files stay on disk). `remove: true` deletes the pattern but does not restore history; the files are versioned again from their next change.
|
|
1067
|
+
- **One gitignore pattern, root-anchored.** The pattern is written as `/` plus the glob relative to the root, so `/data/tickets/*.json` (root `/data`) matches only directly under `tickets/`; use `**/` for any depth. Pass `root` to give a root-relative glob (`tickets/**/*.json`, without a leading `/`: with `root`, a glob starting with `/` is a filesystem path and must lie under the root).
|
|
1068
|
+
- **Brackets are a character class.** `Report [draft].docx` matches `Report d.docx`; escape them to name the file: `Report \[draft\].docx`.
|
|
1069
|
+
- **Refused (`400`):** a newline, a leading `!` or `#`, braces, a pattern matching the whole root (`*`, `**`, `**/*`, ...), or a glob outside the given `root`.
|
|
1070
|
+
- **More than 100 files** to untrack need `confirm: true`; without it the tool reports `409 confirm_required` with the count and changes nothing. Check the count with the user before confirming.
|
|
1071
|
+
- **Busy root.** If version control is busy on the root for 30 s the tool reports `503 not_available`; retry after 30 s.
|
|
1072
|
+
- `watcher_check_vcs_exclusion` reports `excluded: false` for a tracked file even when a human `.gitignore` matches it (git never reports tracked paths as ignored); exclude it to untrack it.
|
|
992
1073
|
|
|
993
1074
|
### Score Interpretation
|
|
994
1075
|
|
|
@@ -1004,6 +1085,19 @@ If the watcher is unreachable:
|
|
|
1004
1085
|
- Fall back to direct `read` for known file paths
|
|
1005
1086
|
- Do not retry silently in a loop
|
|
1006
1087
|
|
|
1088
|
+
If a tool fails with `HTTP 401` (`unauthorized`):
|
|
1089
|
+
|
|
1090
|
+
- The watcher sets `api.apiKey` and the plugin sent no key, or a different one.
|
|
1091
|
+
- Ask the user to set `plugins.entries.jeeves-watcher-openclaw.config.apiKey` in `openclaw.json` (or the `JEEVES_WATCHER_API_KEY` environment variable of the gateway) to the service's `api.apiKey`. The tools read it on every call, so no restart is needed.
|
|
1092
|
+
- Never ask for, print or guess the key. `GET /status` and `GET /openapi.json` never need it.
|
|
1093
|
+
|
|
1094
|
+
If a tool or call fails with `HTTP 503` (`not_available`) and a `Retry-After` header:
|
|
1095
|
+
|
|
1096
|
+
- `POST /metadata` (`watcher_set_metadata`), `POST /rules/reapply` and `POST /documents/delete` wait at most 10 s for a document that is being indexed, then answer `503` with `Retry-After: 10`. A single-document request changed nothing; a multi-document request kept the documents it already changed. All three are idempotent: retry the same request after 10 s.
|
|
1097
|
+
- `watcher_exclude_vcs_path` waits 30 s for a busy version-control root.
|
|
1098
|
+
|
|
1099
|
+
If a tool fails with `HTTP 500: {"error":"Response validation failed","code":"internal_error"}`, the watcher produced a response that doesn't match its own schema: a server bug. Report it with the request you made; retrying won't help.
|
|
1100
|
+
|
|
1007
1101
|
If tools are unavailable (plugin not loaded in this session):
|
|
1008
1102
|
|
|
1009
1103
|
- The watcher API is still accessible via direct HTTP calls
|
|
@@ -1012,8 +1106,8 @@ If tools are unavailable (plugin not loaded in this session):
|
|
|
1012
1106
|
|
|
1013
1107
|
**CLI Fallbacks:**
|
|
1014
1108
|
|
|
1015
|
-
- `jeeves-watcher status` — check if the service is running
|
|
1016
|
-
- `jeeves-watcher validate
|
|
1109
|
+
- `jeeves-watcher status [-p <port>]` — check if the service is running (`GET /status`)
|
|
1110
|
+
- `jeeves-watcher config validate -c <path>` — validate a config file against the schema (offline)
|
|
1017
1111
|
- Restart via NSSM (Windows) or systemctl (Linux)
|
|
1018
1112
|
|
|
1019
1113
|
---
|