woods 2.0.1 → 2.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +94 -7
- data/CONTRIBUTING.md +134 -19
- data/README.md +1 -1
- data/docs/AGENT_GUIDE.md +19 -0
- data/docs/AGENT_SETUP.md +22 -2
- data/docs/BACKEND_MATRIX.md +7 -0
- data/docs/CLIENT_HOOKS.md +6 -0
- data/docs/CONFIGURATION_REFERENCE.md +133 -25
- data/docs/CONSOLE_MCP_SETUP.md +82 -30
- data/docs/EMBEDDING_MODELS.md +16 -19
- data/docs/EXTRACTOR_REFERENCE.md +219 -21
- data/docs/FAQ.md +11 -25
- data/docs/GETTING_STARTED.md +7 -1
- data/docs/INCREMENTAL_EXTRACTION.md +261 -19
- data/docs/INDEX_LAYOUT.md +5 -0
- data/docs/INTERNALS.md +9 -0
- data/docs/MCP_HTTP_TRANSPORT.md +20 -15
- data/docs/MCP_SERVERS.md +87 -8
- data/docs/MCP_TOOL_COOKBOOK.md +13 -55
- data/docs/NOTION_INTEGRATION.md +7 -1
- data/docs/PUBLISHED_INDEX.md +6 -0
- data/docs/README.md +6 -1
- data/docs/RETRIEVAL_GUIDE.md +17 -0
- data/docs/SOURCE_FRESHNESS.md +157 -5
- data/docs/TOKEN_BENCHMARK.md +10 -18
- data/docs/TROUBLESHOOTING.md +70 -14
- data/docs/UNBLOCKED_INTEGRATION.md +60 -8
- data/docs/UPGRADING_TO_2.md +153 -38
- data/docs/WATCH_DAEMON.md +97 -14
- data/exe/woods-console-mcp +2 -2
- data/lib/generators/woods/templates/woods.rb.tt +2 -1
- data/lib/tasks/woods.rake +23 -7
- data/lib/tasks/woods_checks.rake +2 -2
- data/lib/woods/agent_configuration/cli.rb +1 -1
- data/lib/woods/agent_configuration/layout.rb +16 -2
- data/lib/woods/agent_configuration/plan.rb +13 -3
- data/lib/woods/agent_configuration/planner_validation.rb +4 -2
- data/lib/woods/agent_configuration/preflight.rb +5 -3
- data/lib/woods/builder.rb +17 -57
- data/lib/woods/cache/cache_middleware.rb +56 -30
- data/lib/woods/chunking/contributor_chunks.rb +119 -0
- data/lib/woods/chunking/semantic_chunker.rb +44 -21
- data/lib/woods/console/connection_manager.rb +56 -3
- data/lib/woods/console/embedded_executor.rb +30 -5
- data/lib/woods/console/rack_middleware.rb +29 -1
- data/lib/woods/dependency_graph.rb +34 -10
- data/lib/woods/embedding/fake.rb +12 -0
- data/lib/woods/embedding/indexer.rb +195 -98
- data/lib/woods/embedding/input_budget.rb +67 -0
- data/lib/woods/embedding/openai.rb +70 -20
- data/lib/woods/embedding/provider.rb +37 -25
- data/lib/woods/embedding/text_preparer.rb +76 -32
- data/lib/woods/embedding/token_counter.rb +18 -81
- data/lib/woods/embedding/vector_configuration.rb +48 -0
- data/lib/woods/extraction_identities.rb +175 -0
- data/lib/woods/extractor.rb +304 -107
- data/lib/woods/extractors/action_cable_extractor.rb +8 -3
- data/lib/woods/extractors/assigned_value_discovery.rb +74 -0
- data/lib/woods/extractors/class_declarations.rb +121 -0
- data/lib/woods/extractors/configuration_extractor.rb +11 -3
- data/lib/woods/extractors/declaration_ancestry.rb +92 -0
- data/lib/woods/extractors/event_extractor.rb +8 -0
- data/lib/woods/extractors/graphql_extractor.rb +134 -77
- data/lib/woods/extractors/job_extractor.rb +5 -1
- data/lib/woods/extractors/lib_extractor.rb +132 -15
- data/lib/woods/extractors/mailer_extractor.rb +3 -5
- data/lib/woods/extractors/manager_extractor.rb +7 -21
- data/lib/woods/extractors/migration_declaration.rb +87 -0
- data/lib/woods/extractors/migration_extractor.rb +5 -39
- data/lib/woods/extractors/phlex_extractor.rb +6 -2
- data/lib/woods/extractors/policy_extractor.rb +9 -5
- data/lib/woods/extractors/poro_extractor.rb +112 -53
- data/lib/woods/extractors/pundit_extractor.rb +11 -6
- data/lib/woods/extractors/scheduled_job_extractor.rb +45 -4
- data/lib/woods/extractors/serializer_extractor.rb +34 -22
- data/lib/woods/extractors/shared_utility_methods.rb +18 -1
- data/lib/woods/extractors/source_nesting.rb +142 -106
- data/lib/woods/extractors/standalone_module_discovery.rb +123 -0
- data/lib/woods/extractors/state_machine_extractor.rb +46 -40
- data/lib/woods/extractors/view_component_extractor.rb +9 -7
- data/lib/woods/flow_assembler.rb +4 -1
- data/lib/woods/generation.rb +25 -0
- data/lib/woods/hooks/context_hint.rb +7 -2
- data/lib/woods/mcp/bootstrapper.rb +33 -7
- data/lib/woods/mcp/config_resolver.rb +26 -7
- data/lib/woods/mcp/index_reader.rb +125 -24
- data/lib/woods/mcp/index_reader_pinning.rb +16 -0
- data/lib/woods/mcp/renderers/markdown_renderer.rb +7 -1
- data/lib/woods/mcp/renderers/plain_renderer.rb +3 -1
- data/lib/woods/mcp/search_results.rb +7 -1
- data/lib/woods/mcp/server.rb +24 -4
- data/lib/woods/module_reconciliation.rb +151 -0
- data/lib/woods/path_dispatcher.rb +7 -2
- data/lib/woods/rake_helpers.rb +43 -11
- data/lib/woods/release.rb +1 -1
- data/lib/woods/resilience/index_validator.rb +8 -3
- data/lib/woods/resilience/retryable_provider.rb +18 -1
- data/lib/woods/resolved_config.rb +68 -8
- data/lib/woods/retrieval/context_assembler.rb +3 -3
- data/lib/woods/retrieval/lexical_assembler.rb +3 -2
- data/lib/woods/retrieval/scope.rb +18 -2
- data/lib/woods/retrieval/source_evidence.rb +14 -2
- data/lib/woods/source_contributor_validation.rb +78 -0
- data/lib/woods/source_contributors.rb +116 -0
- data/lib/woods/source_inputs/handoff.rb +37 -0
- data/lib/woods/source_inputs/launcher.rb +53 -13
- data/lib/woods/source_inputs/manifest.rb +84 -3
- data/lib/woods/source_inputs/private_key.rb +44 -12
- data/lib/woods/source_inputs/scanner.rb +98 -27
- data/lib/woods/source_inputs/scopes.rb +1 -1
- data/lib/woods/source_inputs/session.rb +147 -15
- data/lib/woods/source_inputs/stable_reader.rb +127 -0
- data/lib/woods/source_inputs/status.rb +40 -8
- data/lib/woods/source_inputs/verifier.rb +28 -5
- data/lib/woods/source_path_encoding.rb +33 -0
- data/lib/woods/source_references/cache.rb +284 -0
- data/lib/woods/source_references/collector.rb +120 -0
- data/lib/woods/source_references/extraction.rb +185 -0
- data/lib/woods/source_references/inputs.rb +134 -0
- data/lib/woods/source_references/parser_adapter.rb +134 -0
- data/lib/woods/source_references/pass.rb +152 -0
- data/lib/woods/source_references/prism_adapter.rb +116 -0
- data/lib/woods/source_references/registry.rb +178 -0
- data/lib/woods/source_references/runtime_lookup.rb +127 -0
- data/lib/woods/source_references/value_class.rb +82 -0
- data/lib/woods/storage/metadata_store.rb +4 -1
- data/lib/woods/storage/qdrant.rb +2 -2
- data/lib/woods/unblocked/client.rb +12 -7
- data/lib/woods/unblocked/document_builder.rb +4 -1
- data/lib/woods/unblocked/exporter.rb +127 -37
- data/lib/woods/unblocked/sync_manifest.rb +137 -21
- data/lib/woods/unblocked/uri_migration.rb +105 -0
- data/lib/woods/util/host_guard.rb +3 -2
- data/lib/woods/version.rb +1 -1
- data/lib/woods/watch/catch_up.rb +138 -0
- data/lib/woods/watch/claim_lease.rb +150 -0
- data/lib/woods/watch/cli.rb +26 -2
- data/lib/woods/watch/daemon.rb +80 -59
- data/lib/woods/watch/installation/options.rb +1 -1
- data/lib/woods/watch/installation/receipt.rb +6 -1
- data/lib/woods/watch/managed_child.rb +1 -1
- data/lib/woods/watch/supervisor.rb +1 -1
- data/lib/woods/watch/tree_scan.rb +14 -2
- data/plugin/.claude-plugin/plugin.json +1 -1
- data/plugin/hooks/adapters/normalize.rb +3 -2
- data/plugin/hooks/woods-input-rules.sh +4 -0
- data/plugin/hooks/woods-refresh.sh +15 -7
- data/plugin/hooks/woods-session-start.sh +60 -3
- data/plugin/skills/woods-diagnose/SKILL.md +334 -11
- data/plugin/skills/woods-investigate/SKILL.md +11 -0
- data/plugin/skills/woods-mcp-config/SKILL.md +79 -8
- data/plugin/skills/woods-setup/SKILL.md +53 -6
- metadata +32 -5
|
@@ -115,7 +115,7 @@ Columns:
|
|
|
115
115
|
| `include_framework_sources` | Boolean | `true` | user-settable | Extract Rails and gem source code |
|
|
116
116
|
| `concurrent_extraction` | Boolean | `false` | user-settable | Enable parallel extraction (experimental) |
|
|
117
117
|
| `vector_store` / `metadata_store` / `graph_store` / `embedding_provider` | Symbol | n/a | preset-derived | Adapter types. Set by presets; override individually to mix stacks. |
|
|
118
|
-
| chars-per-token ratio (used by ContextAssembler, TextPreparer, Builder, cost_model) | Float | `4.0` (OpenAI) / `1.5` (Ollama) | computed |
|
|
118
|
+
| chars-per-token ratio (used by ContextAssembler, TextPreparer, Builder, cost_model) | Float | `4.0` (OpenAI) / `1.5` (Ollama) | computed | A sizing estimate derived from the active provider via `Woods::TokenUtils.chars_per_token_for(...)`, not a proof of token count. Complete embedding admission uses the provider-specific bound described below. |
|
|
119
119
|
|
|
120
120
|
## Embedding options
|
|
121
121
|
|
|
@@ -136,6 +136,26 @@ config.embedding_options = {
|
|
|
136
136
|
}
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
+
`dimensions:` explicitly requests an output width; use it to reduce a
|
|
140
|
+
`text-embedding-3-small` or `text-embedding-3-large` vector. The legacy singular
|
|
141
|
+
`dimension:` remains an alias for this explicit request; if both are supplied,
|
|
142
|
+
they must agree. `expected_dimensions:` records an expected output width
|
|
143
|
+
without requesting a reduction. Standalone snapshot restoration uses that
|
|
144
|
+
separate channel. Provider responses must match the expected width before
|
|
145
|
+
Woods accepts them.
|
|
146
|
+
|
|
147
|
+
`text-embedding-ada-002` has a fixed width of 1,536. A matching declared width
|
|
148
|
+
is accepted without sending the API's unsupported `dimensions` parameter;
|
|
149
|
+
other widths are refused before a request.
|
|
150
|
+
|
|
151
|
+
Embedding snapshots store observed `dimension` separately from
|
|
152
|
+
`requested_dimensions`. Standalone readers restore both. For legacy snapshots
|
|
153
|
+
without the latter field, Woods restores a reduction only when the model is
|
|
154
|
+
exactly `text-embedding-3-small` or `text-embedding-3-large` and the recorded
|
|
155
|
+
width is below that model's native width. It does not infer support for
|
|
156
|
+
reductions from arbitrary model names. Store and snapshot width checks still
|
|
157
|
+
apply; changing models or widths requires rebuilding a compatible vector store.
|
|
158
|
+
|
|
139
159
|
OpenAI embedding batches are sent in slices of at most 36 texts, preserving
|
|
140
160
|
input order. For inputs within the API's 8,192-token per-text limit, this stays
|
|
141
161
|
below both the 2,048-input limit and the 300,000-token total request limit.
|
|
@@ -160,14 +180,20 @@ config.embedding_options = {
|
|
|
160
180
|
|
|
161
181
|
The provider reads `model:`, `host:`, and `num_ctx:` from `embedding_options`. `num_ctx` is auto-selected from a per-model registry (`nomic-embed-text` → 2048, `bge-m3` → 8192, `mxbai-embed-large` → 512, `snowflake-arctic-embed` → 512, `snowflake-arctic-embed2` → 8192, `all-minilm` → 512). Unknown models fall back to 2048, matching Ollama's conservative embedding default. Set `num_ctx:` explicitly only when running a model with a known-larger native context that isn't in the registry yet.
|
|
162
182
|
|
|
163
|
-
|
|
183
|
+
An explicit `dimensions:` option (or its legacy `dimension:` alias) is sent to
|
|
184
|
+
Ollama and requires server/model support. A stored width is restored through
|
|
185
|
+
`expected_dimensions:` instead.
|
|
186
|
+
Discovering a width through a probe never adds `dimensions` to later requests.
|
|
164
187
|
|
|
165
|
-
**
|
|
188
|
+
**Why `num_ctx` is capped at the native context.** Ollama has an open regression ([ollama/ollama#14186](https://github.com/ollama/ollama/issues/14186)) where `options.num_ctx` does not lift the effective ceiling on `/api/embed` for models whose native context is smaller than the override. Woods advertises the native ceiling so the chunker sizes inputs to what Ollama will actually accept.
|
|
166
189
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
190
|
+
**Input counting (included in Woods 2.1).** Complete inputs include metadata
|
|
191
|
+
prefixes. Known OpenAI embedding models use a conservative byte upper bound;
|
|
192
|
+
Ollama/custom model counts remain estimates. Woods requests `truncate: false`
|
|
193
|
+
from Ollama and refuses/splits oversized inputs without dropping source.
|
|
194
|
+
Installing `tokenizers` no longer implicitly downloads BERT or establishes an
|
|
195
|
+
exact count for an unrelated model. A caller-supplied local tokenizer must match
|
|
196
|
+
the selected model. See the model guide for limitations.
|
|
171
197
|
|
|
172
198
|
See [EMBEDDING_MODELS.md](EMBEDDING_MODELS.md) for the full model comparison and the procedure for adding a new model to the registry.
|
|
173
199
|
|
|
@@ -188,6 +214,39 @@ Anything responding to `#embed` and `#embed_batch` can be assigned directly, it
|
|
|
188
214
|
config.embedding_provider = MyCompany::CustomEmbedder.new(endpoint: internal_url)
|
|
189
215
|
```
|
|
190
216
|
|
|
217
|
+
Included in Woods 2.1: snapshots capture the effective settings of injected
|
|
218
|
+
Ollama, OpenAI, and Fake instances, including through Woods' retry and embedding
|
|
219
|
+
cache wrappers. An injected instance takes precedence over unused
|
|
220
|
+
`embedding_options`. Ollama retains its model, host, context size, read timeout,
|
|
221
|
+
and vector settings; OpenAI and Fake retain their model and vector settings.
|
|
222
|
+
Capturing constructor settings makes no requests. When a live `provider:` is
|
|
223
|
+
passed to `ResolvedConfig.from_configuration`, its existing dimension-discovery
|
|
224
|
+
behavior remains available. The explicit request width remains separate from
|
|
225
|
+
the observed width.
|
|
226
|
+
|
|
227
|
+
API keys are never stored; standalone OpenAI restoration still requires
|
|
228
|
+
`OPENAI_API_KEY`. Only plain HTTP(S) origins can be persisted as Ollama hosts
|
|
229
|
+
(an optional trailing `/` is allowed). URLs containing user information, a path
|
|
230
|
+
prefix, query, or fragment are omitted and marked `requires_host_provider`.
|
|
231
|
+
Those components may be legitimate routing settings, but can also carry
|
|
232
|
+
credentials; Woods refuses to reconstruct a different endpoint by dropping them.
|
|
233
|
+
The same restriction applies when reading older snapshot endpoints.
|
|
234
|
+
|
|
235
|
+
**Reader compatibility:** snapshots marked `requires_host_provider` need a
|
|
236
|
+
supporting reader. Older schema-1 readers cannot enforce that marker and may
|
|
237
|
+
silently select defaults; do not use them for implicit restoration of these
|
|
238
|
+
snapshots. Ordinary built-in snapshots with non-secret settings remain
|
|
239
|
+
schema-1 compatible.
|
|
240
|
+
|
|
241
|
+
Custom providers and custom wrappers are also marked `requires_host_provider`;
|
|
242
|
+
Woods does not serialize arbitrary client state or infer a built-in from a custom
|
|
243
|
+
class name. Embedding in the configured host continues to work. To serve such a
|
|
244
|
+
snapshot semantically, provide an explicitly configured compatible provider in
|
|
245
|
+
the reader's host initializer. Implicit standalone restoration reports a
|
|
246
|
+
configuration error instead of choosing defaults. Explicit lexical retrieval
|
|
247
|
+
does not restore or call an embedding provider. Record the loaded revision
|
|
248
|
+
before relying on these snapshot improvements.
|
|
249
|
+
|
|
191
250
|
## Storage options
|
|
192
251
|
|
|
193
252
|
| Option | Type | Default | Description |
|
|
@@ -264,6 +323,15 @@ worktree when overriding it. Existing databases at the old configured output
|
|
|
264
323
|
path are not moved or deleted; run `woods:embed` for the selected index after
|
|
265
324
|
upgrading to populate its default metadata database.
|
|
266
325
|
|
|
326
|
+
**Included in Woods 2.1:** embedding with dump-backed vectors reconciles the
|
|
327
|
+
complete SQLite identity inventory, including records without vectors and
|
|
328
|
+
same-name units with distinct types. A full rebuild removes vanished records,
|
|
329
|
+
including when the new corpus is empty. Incremental runs retain the existing
|
|
330
|
+
empty-input and 30% purge guards. Both modes still refuse incomplete extraction
|
|
331
|
+
input before changing stores. Standalone MCP opens the index's SQLite metadata
|
|
332
|
+
before restoring type filters on hydrated vectors, using that same metadata
|
|
333
|
+
store for retrieval. No database migration or embedding format change is required.
|
|
334
|
+
|
|
267
335
|
Requires the `sqlite3` gem in your host bundle. Rails apps backed by
|
|
268
336
|
MySQL or PostgreSQL won't have it by default, selecting `:sqlite`
|
|
269
337
|
without it raises `Woods::ConfigurationError` with install
|
|
@@ -291,13 +359,6 @@ unchanged. This is a reasonable default for hosts that don't bundle `sqlite3`.
|
|
|
291
359
|
|
|
292
360
|
## Retrieval cache options
|
|
293
361
|
|
|
294
|
-
The 2.0.1 maintenance patch scopes retrieval contexts to one retriever instance.
|
|
295
|
-
Reload retires only that instance's namespace, including results still in flight;
|
|
296
|
-
already-running requests may finish against the previous corpus. Restarting starts
|
|
297
|
-
a fresh context namespace. Retired entries expire under their configured TTL or
|
|
298
|
-
backend eviction; disabling both can retain unused entries indefinitely. Embedding
|
|
299
|
-
caches remain separate and are not cleared by context invalidation.
|
|
300
|
-
|
|
301
362
|
The optional cache wraps both embedding-provider calls and assembled retrieval
|
|
302
363
|
contexts. It is disabled by default and is separate from the Index Server's
|
|
303
364
|
tool-result `_meta` cache hint.
|
|
@@ -333,9 +394,33 @@ overrides the wrapper defaults for `:embeddings` (24 hours) and `:context`
|
|
|
333
394
|
(15 minutes). `:memory` accepts `max_entries` (default 500); it ignores
|
|
334
395
|
`default_ttl` because each wrapper write supplies its domain TTL.
|
|
335
396
|
|
|
397
|
+
**Included in Woods 2.1:** retrieval contexts are scoped to one retriever
|
|
398
|
+
instance, including when Redis or Solid Cache is shared by applications or
|
|
399
|
+
worktrees. Restarting the retriever starts a fresh context namespace. Reload
|
|
400
|
+
retires only that instance's namespace, including results still in flight;
|
|
401
|
+
already running requests may finish against the previous corpus. Retired entries
|
|
402
|
+
expire according to their configured TTL or backend eviction; disabling both
|
|
403
|
+
can retain unused entries indefinitely. Embedding-vector caches are separate and survive context invalidation.
|
|
404
|
+
|
|
405
|
+
Embedding entries are scoped to the provider family, endpoint, model, requested
|
|
406
|
+
and declared widths, and relevant input options. Cache lookups never call a
|
|
407
|
+
provider's probing `dimensions` method. Keys contain hashes rather than raw
|
|
408
|
+
endpoint credentials or API keys. Malformed cached vectors and known-width
|
|
409
|
+
mismatches are refused. This configuration key format starts a fresh embedding
|
|
410
|
+
cache after upgrading; old entries expire under their existing TTLs.
|
|
411
|
+
|
|
412
|
+
Custom provider objects may expose a pure, JSON-compatible `cache_identity`
|
|
413
|
+
containing every setting that affects their vectors, plus a non-probing
|
|
414
|
+
`configured_dimensions` reader for width validation. Otherwise each cache
|
|
415
|
+
wrapper has its own namespace; cached vectors are still checked for valid
|
|
416
|
+
numeric content and consistent batch widths. Configuration changes require a
|
|
417
|
+
new provider and wrapper. A remotely replaced model under an unchanged name
|
|
418
|
+
still requires an explicit cache/model-version change and re-embedding.
|
|
419
|
+
|
|
336
420
|
`Woods::Cache.cache_key` length-prefixes every component, including a single
|
|
337
|
-
component, so different argument counts cannot share a response.
|
|
338
|
-
|
|
421
|
+
component, so different argument counts cannot share a response. This component
|
|
422
|
+
encoding is unchanged and independent of the retriever's context namespace;
|
|
423
|
+
the embedding wrapper uses the new identity above. Custom callers
|
|
339
424
|
using single-component keys must clear their affected persistent cache domain
|
|
340
425
|
when upgrading, since older unprefixed entries can alias the new encoding;
|
|
341
426
|
subsequent calls refill it normally. Namespace clearing still covers both formats.
|
|
@@ -503,22 +588,35 @@ there is no full population count to publish. Both caps must be a positive Integ
|
|
|
503
588
|
|
|
504
589
|
| Option | Type | Default | Description |
|
|
505
590
|
|--------|------|---------|-------------|
|
|
506
|
-
| `session_tracer_enabled` | Boolean | `false` | Enable session tracing middleware |
|
|
591
|
+
| `session_tracer_enabled` | Boolean | `false` | Enable session tracing middleware; set with its store in `config/application.rb` before Railtie initialization |
|
|
507
592
|
| `session_tracer_allow_production` | Boolean | `false` | Explicitly allow session tracing in `Rails.env.production?`. Without this opt-in, the Railtie warns and leaves the tracer disabled even when `session_tracer_enabled` is true. Review trace contents, retention, and access controls before enabling it. |
|
|
508
593
|
| `session_store` | Object | `nil` | Store backend: `FileStore`, `RedisStore`, or `SolidCacheStore` |
|
|
509
594
|
| `session_id_proc` | Proc | `nil` | Custom proc to extract session ID from requests |
|
|
510
595
|
| `session_exclude_paths` | Array<String> | `[]` | Path patterns to exclude from tracing |
|
|
511
596
|
|
|
597
|
+
Configure the complete tracer setup in `config/application.rb`, after Woods is
|
|
598
|
+
required and before Rails initialization. A `config/initializers/woods.rb`
|
|
599
|
+
assignment is too late to mount this middleware; Woods warns and tracing stays
|
|
600
|
+
disabled. Restart the server after changing it.
|
|
601
|
+
|
|
512
602
|
```ruby
|
|
513
|
-
|
|
603
|
+
# config/application.rb, after Bundler.require(*Rails.groups)
|
|
604
|
+
require 'woods/session_tracer/file_store'
|
|
514
605
|
|
|
515
|
-
|
|
516
|
-
config.
|
|
517
|
-
|
|
518
|
-
)
|
|
519
|
-
|
|
606
|
+
Woods.configure do |config|
|
|
607
|
+
config.session_tracer_enabled = true
|
|
608
|
+
config.session_store = Woods::SessionTracer::FileStore.new(
|
|
609
|
+
base_dir: File.expand_path('../tmp/session_traces', __dir__)
|
|
610
|
+
)
|
|
611
|
+
config.session_exclude_paths = ['/health', '/metrics', '/assets']
|
|
612
|
+
end
|
|
520
613
|
```
|
|
521
614
|
|
|
615
|
+
The Rails middleware records traces. Reading them with `session_trace` additionally
|
|
616
|
+
requires a custom Index Server process configured with the same store; the
|
|
617
|
+
packaged Index executable does not load application initializers. See
|
|
618
|
+
[conditional capabilities](MCP_SERVERS.md#conditional-index-capabilities).
|
|
619
|
+
|
|
522
620
|
### File session retention
|
|
523
621
|
|
|
524
622
|
`FileStore` accepts `ttl:` in seconds (default `nil`, expiration disabled),
|
|
@@ -689,8 +787,8 @@ deployment guide including defense layers.
|
|
|
689
787
|
| `console_mcp_enabled` | Boolean | `false` | Master switch. When `false`, stdio exits and the mounted Console middleware passes requests through to Rails. |
|
|
690
788
|
| `console_mcp_http_enabled` | Boolean | `true` | HTTP transport switch; effective only while the master switch is on. Set `false` for stdio-only use without HTTP token validation or an active HTTP endpoint. Read at request time. |
|
|
691
789
|
| `console_mcp_token` | String | `ENV['WOODS_CONSOLE_MCP_TOKEN']` or `nil` | Bearer token required on every enabled Console HTTP request. With both Console flags enabled, production boot raises on a missing token; other environments warn and requests fail closed with 401. A configured token shorter than 32 characters raises at boot while HTTP is enabled. Explicit stdio-only configurations skip HTTP token validation. Generate with `SecureRandom.hex(32)`. |
|
|
692
|
-
| `console_mcp_allowed_origins` | Array\<String\> | `%w[http://localhost http://127.0.0.1 http://[::1]]` |
|
|
693
|
-
| `console_mcp_path` | String | `/mcp/console` | URL path
|
|
790
|
+
| `console_mcp_allowed_origins` | Array\<String\> | `%w[http://localhost http://127.0.0.1 http://[::1]]` | Shared guard/SDK allowlist (included in Woods 2.1). Exact cross-origin entries include the port; portless entries also permit same-authority traffic. Include the actual browser origin and non-loopback endpoint authority. Restart after changes. |
|
|
791
|
+
| `console_mcp_path` | String | `/mcp/console` | URL path captured when Rack middleware mounts. Set a custom path in `config/application.rb` before Railtie initialization, then restart. |
|
|
694
792
|
| `console_embedded_read_tools` | Boolean | `false` | Register `console_sql` and `console_query` in supported stdio and Rack modes. |
|
|
695
793
|
| `console_blocked_tables` | Array\<String\> | `Woods::DEFAULT_CONSOLE_BLOCKED_TABLES` | TableGate denylist (case-insensitive). Bare names match every schema; qualified names (`schema.table`) match exactly. |
|
|
696
794
|
| `console_redacted_columns` | Array\<String\> | `Woods::DEFAULT_CONSOLE_REDACTED_COLUMNS` | Column names whose values are replaced with `[REDACTED]` in responses, and which are refused as aggregate, scope, find, and order inputs. |
|
|
@@ -792,6 +890,11 @@ in its finalized development environment. See [startup and installation](WATCH_D
|
|
|
792
890
|
for the generator's explicit modes, portable receipt, update/removal, and the
|
|
793
891
|
separate supervision status. Raw task settings above remain compatible.
|
|
794
892
|
|
|
893
|
+
**Included in Woods 2.1 (#591):** blank/whitespace-only idle timeouts count as
|
|
894
|
+
unset consistently. `woods-watch --recover-claim INDEX --claim-token TOKEN`
|
|
895
|
+
provides explicit recovery only for a matching abandoned managed lifetime lease;
|
|
896
|
+
see [ownership recovery](WATCH_DAEMON.md#recovering-an-abandoned-managed-claim).
|
|
897
|
+
|
|
795
898
|
### Opt-in plugin refresh hooks
|
|
796
899
|
|
|
797
900
|
These settings control the plugin shell worker. Check installed
|
|
@@ -820,6 +923,11 @@ tasks for manual refreshes; hook transport is not a general shell execution API.
|
|
|
820
923
|
|
|
821
924
|
### Extraction rake tasks
|
|
822
925
|
|
|
926
|
+
The separate `woods-extract` launcher captures before Rails configuration runs:
|
|
927
|
+
custom `output_dir` applications must supply matching `--output` or `WOODS_OUTPUT`.
|
|
928
|
+
Its implicit default is `tmp/woods`; the Woods 2.1 #591 guard refuses a finalized
|
|
929
|
+
configuration mismatch before publication. See [launcher output selection](SOURCE_FRESHNESS.md#establish-a-fresh-baseline).
|
|
930
|
+
|
|
823
931
|
| Variable | Default | Purpose |
|
|
824
932
|
|----------|---------|---------|
|
|
825
933
|
| `WOODS_OUTPUT` | `Woods.configuration.output_dir` | Overrides the output directory for extraction/watch and embedding tasks without editing the initializer; embedding also places its default SQLite metadata database there. Explicit database options take precedence. |
|
data/docs/CONSOLE_MCP_SETUP.md
CHANGED
|
@@ -212,7 +212,15 @@ and proxy rules aligned with it. Server-to-server clients normally omit
|
|
|
212
212
|
`Origin`, but a present request `Host` must still be allowed. Supporting security
|
|
213
213
|
revisions deliberately allow configured non-loopback Hosts through the SDK too;
|
|
214
214
|
2.0.0 could refuse them at that inner layer despite the Woods allowlist. This
|
|
215
|
-
widening is limited to the configured authorities and retains bearer auth.
|
|
215
|
+
widening is limited to the configured authorities and retains bearer auth. The
|
|
216
|
+
same immutable policy makes preflight acceptance agree with SDK dispatch. List exact browser ports for
|
|
217
|
+
cross-port traffic, including loopback; a portless origin additionally permits
|
|
218
|
+
same-authority traffic, not arbitrary cross-port access. Console defaults remain
|
|
219
|
+
HTTP loopback, while the Index Server defaults include HTTP and HTTPS loopback.
|
|
220
|
+
Default HTTP(S) ports are equivalent to their omitted form. An explicit list
|
|
221
|
+
replaces default browser origins, including loopback origins; add loopback entries
|
|
222
|
+
if those clients are needed. Invalid entries fail at boot naming the entry.
|
|
223
|
+
Restart after changing the list. See [HTTP origin matching](MCP_HTTP_TRANSPORT.md#browser-origins-dns-rebinding-defense).
|
|
216
224
|
|
|
217
225
|
Do not mount `Woods::Console::RackMiddleware` by itself. The Railtie composes
|
|
218
226
|
`OriginGuard`, `BearerAuth`, and the Console middleware in the supported order.
|
|
@@ -332,6 +340,21 @@ user: deploy
|
|
|
332
340
|
command: cd /app && bundle exec rake woods:console
|
|
333
341
|
```
|
|
334
342
|
|
|
343
|
+
**Included in Woods 2.1:** direct mode prefers an existing executable `bin/rake`
|
|
344
|
+
in the application's selected directory, falling back to `bundle exec rake`.
|
|
345
|
+
Relative `directory` values resolve from the launcher's initial working directory.
|
|
346
|
+
Explicit `command` settings win. Docker and SSH modes retain their remote default;
|
|
347
|
+
set `command` explicitly when that application uses another task entry point.
|
|
348
|
+
|
|
349
|
+
**Included in Woods 2.1:** the launcher rejects unknown keys, nested
|
|
350
|
+
`connection:`/`console:` sections, blank or non-string values, and options that
|
|
351
|
+
do not apply to the selected mode. Keep `mode` and `command` at the top level;
|
|
352
|
+
use `directory` for direct mode, `container` for Docker, and `host`/`user` for SSH.
|
|
353
|
+
Configure access controls and redaction in the Rails initializer, not this
|
|
354
|
+
process-launch file. Move legacy nested launch settings to the matching example
|
|
355
|
+
above. An empty file or mapping still selects the default direct command;
|
|
356
|
+
supported `command`/`directory` overrides can omit `mode` for direct execution.
|
|
357
|
+
|
|
335
358
|
Override config path with environment variable:
|
|
336
359
|
|
|
337
360
|
```bash
|
|
@@ -379,6 +402,13 @@ servers register only executable tools: the 9 Tier 1 tools by default, plus
|
|
|
379
402
|
| `console_association_count` | Count associated records for a specific record |
|
|
380
403
|
| `console_recent` | Recently created/updated records (max 50) |
|
|
381
404
|
|
|
405
|
+
**Included in Woods 2.1:** `console_association_count` returns `0` or `1` for
|
|
406
|
+
`belongs_to` and `has_one`, including missing targets. It counts the association's
|
|
407
|
+
relation, applying `scope` to the target model; collection associations retain
|
|
408
|
+
their ordinary count. Target-table and scope-column checks still apply before
|
|
409
|
+
association reads. The tool does not call a custom association getter to count
|
|
410
|
+
an already loaded model object.
|
|
411
|
+
|
|
382
412
|
### Tier 2: Domain-aware (9 tools): inventory only, not executable
|
|
383
413
|
|
|
384
414
|
| Tool | Description |
|
|
@@ -423,9 +453,21 @@ servers register only executable tools: the 9 Tier 1 tools by default, plus
|
|
|
423
453
|
|
|
424
454
|
## Configuration options
|
|
425
455
|
|
|
426
|
-
Set
|
|
456
|
+
Set the request-time flags and protection settings below in your Rails initializer.
|
|
457
|
+
A **custom endpoint path** must be set earlier, after Woods is required in
|
|
458
|
+
`config/application.rb`, because the Railtie captures it while mounting middleware:
|
|
459
|
+
|
|
460
|
+
```ruby
|
|
461
|
+
# config/application.rb, after Bundler.require(*Rails.groups)
|
|
462
|
+
Woods.configure { |config| config.console_mcp_path = '/internal/woods-console' }
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Restart after changing the path. Leaving the default `/mcp/console` requires no
|
|
466
|
+
path assignment. Session tracing also needs early configuration; follow
|
|
467
|
+
[session tracer options](CONFIGURATION_REFERENCE.md#session-tracer-options).
|
|
427
468
|
|
|
428
469
|
```ruby
|
|
470
|
+
# config/initializers/woods.rb
|
|
429
471
|
Woods.configure do |config|
|
|
430
472
|
# Master on/off switch for the Console MCP feature (Layer 0). Default: false.
|
|
431
473
|
# Applies to every transport: stdio, launcher wrapper, and Rack.
|
|
@@ -435,9 +477,6 @@ Woods.configure do |config|
|
|
|
435
477
|
# after configuring the layers below that match your threat model.
|
|
436
478
|
config.console_mcp_enabled = true
|
|
437
479
|
|
|
438
|
-
# URL path for the Rack middleware endpoint. Default: '/mcp/console'.
|
|
439
|
-
config.console_mcp_path = '/mcp/console'
|
|
440
|
-
|
|
441
480
|
# HTTP Origin + Host allow-list. Defaults to loopback only. Non-loopback
|
|
442
481
|
# Rack deployments must include their public MCP host; browser clients from
|
|
443
482
|
# another origin need that exact origin listed too.
|
|
@@ -599,6 +638,10 @@ Direct, unaliased selection of a redacted column stays allowed: the output heade
|
|
|
599
638
|
|
|
600
639
|
`console_sql` applies a stricter form of the same rule because arbitrary SQL can rename output headers. A protected identifier is accepted only as a direct, unaliased outer `SELECT` column; aliases, aggregates, predicates, CTE shapes, ordering/grouping uses, and an EAV value without its paired key are refused before adapter execution. Use `console_query` when a protected column must participate in a more complex structured query.
|
|
601
640
|
|
|
641
|
+
The security correction refuses PostgreSQL Unicode-escaped identifiers in
|
|
642
|
+
`console_sql` before execution. Use ordinary identifiers or standard quoted
|
|
643
|
+
identifiers instead. This restriction does not change the structured query tools.
|
|
644
|
+
|
|
602
645
|
`console_query`'s `having` is covered by the same oracle rule: an aggregate over a protected column (`MAX(amount) > ?`), a bare predicate on a redacted column (`salary > ?`), or a predicate on an EAV value column is refused, since repeated guesses reveal the protected value from whether a row is returned. EAV key predicates stay allowed so callers can select the rows whose paired values need redaction.
|
|
603
646
|
|
|
604
647
|
**Ships with a curated credential default list** (`Woods::DEFAULT_CONSOLE_REDACTED_COLUMNS`, 31 columns) covering Devise, Doorkeeper, Rodauth, has_secure_password, devise-two-factor, and common hand-rolled auth shapes: `password`, `password_digest`, `password_salt`, `encrypted_password`, `crypted_password`, `salt`, `otp_secret`, `encrypted_otp_secret`, `two_factor_secret`, `backup_codes`, `consumed_timestep`, `reset_password_token`, `confirmation_token`, `unlock_token`, `remember_token`, `invitation_token`, `access_token`, `refresh_token`, `auth_token`, `api_token`, `api_key`, `bearer_token`, `client_secret`, `webhook_secret`, `signing_secret`, `session_secret`, `private_key`, `encrypted_private_key`, `key_hash`, `token`, `secret`.
|
|
@@ -856,6 +899,23 @@ fail closed with `Woods::ConfigurationError`.
|
|
|
856
899
|
|
|
857
900
|
The middleware lazy-initializes the MCP server on the first request, which includes `Rails.application.eager_load!`. This can take several seconds on large apps. Subsequent requests are fast. If you want to pre-warm, call a health check endpoint that touches the middleware path at app startup.
|
|
858
901
|
|
|
902
|
+
**Included in Woods 2.1:** if that eager load raises `NameError` (including a
|
|
903
|
+
Zeitwerk naming error), HTTP Console returns a stable `503` and records one
|
|
904
|
+
`console.eager_load.failed` diagnostic. That worker remains unavailable until
|
|
905
|
+
restart; it does not serve a partially populated model registry or repeatedly
|
|
906
|
+
retry loading on incoming requests. The response and diagnostic omit the raw
|
|
907
|
+
application exception. Reproduce it in the same application environment:
|
|
908
|
+
|
|
909
|
+
```bash
|
|
910
|
+
bundle exec rails runner 'Rails.application.eager_load!'
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
Fix the application error, then restart the Rails workers. Authentication and
|
|
914
|
+
requests outside the Console mount retain their normal behavior. Other exception
|
|
915
|
+
types retain Rails' usual failure handling. The existing stdio entry point warns
|
|
916
|
+
on eager-load `NameError` and continues; its model list may be incomplete, so fix
|
|
917
|
+
the same application error there too.
|
|
918
|
+
|
|
859
919
|
### Timeout errors on large models
|
|
860
920
|
|
|
861
921
|
The default statement timeout is 5000ms (5 seconds). If you are hitting timeouts on models with millions of rows, use `scope` to narrow the query:
|
|
@@ -916,34 +976,29 @@ contains them. Confirm that a patched release is available before selecting it.
|
|
|
916
976
|
On affected versions, disable Console where these policies are required; Index MCP
|
|
917
977
|
can stay enabled because it reads the published code index separately.
|
|
918
978
|
|
|
979
|
+
## Console corrections included in 2.1
|
|
919
980
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
make an unreviewed manual mount the recommended installation path. Configure
|
|
925
|
-
allowed origins before boot and restart after changes; malformed entries fail
|
|
926
|
-
once at boot with the offending entry identified.
|
|
927
|
-
|
|
928
|
-
Protected collection values are masked as complete cells. EAV key policy checks
|
|
929
|
-
both stored and cast keys, including in predicates and ordering. Raw SQL refuses
|
|
930
|
-
ambiguous protected source identity, protected whole-row projections, positional
|
|
931
|
-
ordering that could expose protected values, and unsupported result types.
|
|
932
|
-
Explicit unaliased scalar columns and the structured tools are the recovery path.
|
|
981
|
+
Woods 2.1 includes the reviewed Console corrections also released in the
|
|
982
|
+
2.0.1 and 1.6.4 security patches. For Git/path installations, verify the loaded
|
|
983
|
+
revision and gem path. Plugin updates alone do not update the Console server.
|
|
984
|
+
See the published [security advisory](https://github.com/lost-in-the/woods/security/advisories/GHSA-wxxx-6hqc-qm8g).
|
|
933
985
|
|
|
934
|
-
SQL
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
986
|
+
The patch tightens SQL policy for adapter-specific comment and quoting forms,
|
|
987
|
+
whole-row and multi-source redaction, typed key-value records, and per-mount HTTP
|
|
988
|
+
guards. Supported structured reads remain available. Raw `console_sql` requires
|
|
989
|
+
a recognized PostgreSQL, MySQL-family, or SQLite adapter; compatible subclasses
|
|
990
|
+
are recognized by ancestry. Unsupported raw SQL receives a clear refusal without
|
|
991
|
+
disabling structured tools.
|
|
938
992
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
993
|
+
Malformed HTTP origin configuration now refuses at boot with the offending entry.
|
|
994
|
+
Fix the allowlist and restart; do not relax authentication or redaction to make a
|
|
995
|
+
refused request succeed. For stdio-only use, keep HTTP disabled.
|
|
942
996
|
|
|
943
997
|
### Read policy compatibility
|
|
944
998
|
|
|
945
|
-
These rules describe the security
|
|
946
|
-
|
|
999
|
+
These rules describe Woods 2.1 and the 2.0.1 security patch. Differences from
|
|
1000
|
+
the 1.6.4 backport are called out below. Verify the installed version and, for
|
|
1001
|
+
Git/path installations, the revision and loaded gem path.
|
|
947
1002
|
|
|
948
1003
|
- **Column alias lists:** when either `console_redacted_columns` or
|
|
949
1004
|
`console_redacted_key_values` is nonempty, `console_sql` refuses relation
|
|
@@ -976,6 +1031,3 @@ and loaded gem path until its release is published.
|
|
|
976
1031
|
expression per `select` array entry. 2.x refuses comma-combined entries that
|
|
977
1032
|
1.6.4 splits before validation. Execution and typed redaction use the same
|
|
978
1033
|
validated projection on all patched lines.
|
|
979
|
-
- **Association counts:** polymorphic `belongs_to` counts remain unsupported
|
|
980
|
-
on this maintenance line and fail closed with a generic execution error.
|
|
981
|
-
The 2.1 functional correction is not included in this backport.
|
data/docs/EMBEDDING_MODELS.md
CHANGED
|
@@ -37,8 +37,8 @@ Woods.configure do |config|
|
|
|
37
37
|
model: 'bge-m3',
|
|
38
38
|
host: 'http://localhost:11434'
|
|
39
39
|
}
|
|
40
|
-
#
|
|
41
|
-
#
|
|
40
|
+
# Model context and initial chunk sizing follow the registry.
|
|
41
|
+
# Local counts are estimates; the server enforces its actual limit.
|
|
42
42
|
end
|
|
43
43
|
```
|
|
44
44
|
|
|
@@ -64,21 +64,20 @@ override. For `nomic-embed-text` (native 2048) the server rejects inputs above
|
|
|
64
64
|
that with a `400 "the input length exceeds the context length"` regardless of
|
|
65
65
|
`num_ctx`.
|
|
66
66
|
|
|
67
|
-
Woods
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
count.
|
|
67
|
+
**Included in Woods 2.1:** Woods checks complete inputs, including their
|
|
68
|
+
metadata prefixes, and splits source without discarding characters. OpenAI's
|
|
69
|
+
known byte-BPE embedding models use a conservative UTF-8 byte upper bound at
|
|
70
|
+
8191 tokens; this may create more chunks than an exact tokenizer would.
|
|
71
|
+
Ollama and custom models use explicitly labelled estimates. Ollama requests
|
|
72
|
+
set `truncate: false`, so an actual overflow fails instead of losing source.
|
|
73
|
+
A prefix that cannot fit or one unsplittable source character refuses before
|
|
74
|
+
vector/checkpoint writes; inspect the reported unit, model and limit.
|
|
75
|
+
|
|
76
|
+
Installing `tokenizers` alone no longer downloads or selects BERT for every
|
|
77
|
+
model. Advanced callers can inject a local model-matched tokenizer into
|
|
78
|
+
`Embedding::TokenCounter`; the caller must establish that it matches the server.
|
|
79
|
+
No automatic tokenizer download occurs. These checks do not prove that estimated
|
|
80
|
+
inputs fit an arbitrary tokenizer: the provider remains authoritative.
|
|
82
81
|
|
|
83
82
|
## Adding a new model to the registry
|
|
84
83
|
|
|
@@ -112,8 +111,6 @@ If you want Woods to auto-pick `num_ctx` for a model we don't ship support for:
|
|
|
112
111
|
|
|
113
112
|
- Indexing a real-world Rails app where concern-inlined models or long service
|
|
114
113
|
objects routinely exceed 2048 tokens.
|
|
115
|
-
- You already pay the `tokenizers` gem install cost and want the tighter
|
|
116
|
-
coupling between client-side verification and server-side enforcement.
|
|
117
114
|
- Multilingual content (Arctic Embed 2 is BGE M3 with a stronger non-English
|
|
118
115
|
story).
|
|
119
116
|
|