* feat(storage): optimize glob func
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* feat(rgafs): implement paged glob traversal without full tree materialization
* fix(localfs): offload blocking fs operations to spawn_blocking
* feat(glob): cap glob api default node_limit at 256
* feat(sdk): add node_limit options for glob in python and go SDKs
AddResource unconditionally attached args: {} to every request body. Against
instances that predate #2549 (which added the args field to the resources
route under model_config=ConfigDict(extra="forbid")), the empty args object
is rejected with "body.args: Extra inputs are not permitted", so add-resource
fails entirely on older / hosted endpoints.
Only attach args when arguments were actually provided (len(opts.Args) > 0),
mirroring the Python SDK _compact_request_body (#2834) and the Rust CLI
compact_request_body (#2799). Other fields, including directly_upload_media,
are unchanged. Scope is args-only, matching #2834.
Tests cover the three arg states: omitted when unset, omitted when explicitly
empty (server defaults args to {} on this create route, so absent and
present-but-empty are equivalent), and forwarded when populated.
* feat(auth): support seeded API key generation
Allow admin key issuance flows to accept an optional seed so clients can derive predictable user API keys when needed, while preserving random generation by default.
* fix(go-sdk): preserve explicit empty seed payloads
Use pointer seed options so callers can distinguish omitted seeds from explicit empty seeds, matching the Admin API behavior.
* feat(sdk/go): add LevelLimit to GrepOptions
The server validates+consumes level_limit (default 10) and the Python client
exposes it, but the Go SDK could not send it, so Go callers were silently
pinned to the default traversal depth. Add LevelLimit *int, forwarded only when
set (mirrors NodeLimit).
* feat(sdk/go): forward grep level_limit when set
* test(sdk/go): cover grep level_limit forwarding and nil-omit
#2813 promoted account-shared skills (viking://agent/skills) to a
first-class, scoped capability and shipped a target_uri param across the
Python SDK (all 7 skill methods), the Rust CLI, and the server skills
router. The Go SDK skill methods were the only client left without it, so
Go adopters cannot scope skill operations to user-vs-agent roots and must
drop to Python/CLI.
Add an optional TargetURI field to the six skill option structs plus a new
DeleteSkillOptions, and thread target_uri through every skill method
matching the Python SDK + server placement exactly:
- query param: ListSkills, GetSkill, DeleteSkill
- JSON body: AddSkill, FindSkills, ValidateSkill, UpdateSkill
target_uri is sent only when set (nil omits it), mirroring the Python
"if target_uri is not None" semantics and the server default-root fallback.
Like the Python SDK, skills send target_uri as-is (no client-side
normalization; only find/search normalize).
DeleteSkill previously took no options; it now takes a variadic
opts ...*DeleteSkillOptions so existing DeleteSkill(ctx, name) callers keep
compiling. The change is source-compatible across the module.
Adds TestSkillRequestsScopeTargetURI (asserts query-vs-body placement for
all 7 methods) and TestSkillRequestsOmitTargetURIWhenUnset (asserts omit
for all 7). go test ./... and go vet ./... pass.
The Python SDK's find/search/add_resource unconditionally attach optional
fields to their request bodies — `args` as an empty `{}` and
`tags`/`score_threshold`/`filter`/`context_type`/`session_id` as `null` — even
when the caller never set them. OpenViking kernels use
`model_config = ConfigDict(extra="forbid")` on these routes, so any instance
that predates a field rejects the whole request with
`body.<field>: Extra inputs are not permitted`. This breaks the SDK (and every
SDK consumer, e.g. vikingbot via `ov.AsyncHTTPClient`) against older instances:
`add_resource` fails on `body.args` against a pre-#2549 instance, and `find`
fails on `body.tags` against a strict pre-#2706 instance.
This is the SDK-side counterpart of #2799, which fixed the same anti-pattern in
the Rust CLI but left the Python SDK untouched.
- Add `_compact_request_body()` and apply it in find/search/add_resource: drop
null-valued keys and an empty `args` object before sending. Scoped to these
read/create routes only, where a missing optional field and an explicit null
are equivalent — not applied globally, since null may mean "clear" on a future
update/PATCH route. Mirrors the CLI's `compact_request_body`.
Tests: unit tests for `_compact_request_body` and for find/search/add_resource
omitting unset optional fields while keeping explicitly-provided ones.
* chore: clear unused files
* fix(tests): fix unit test
* refactor(auth): introduce plugin-based authentication architecture
Replace the monolithic `openviking/server/auth.py` with an extensible
plugin-based auth system. This refactor extracts the three built-in modes
(`dev`, `api_key`, `trusted`) into separate `AuthPlugin` implementations,
adds a registry for third-party plugins, and preserves all existing behavior
while enabling custom authentication backends (e.g. LDAP, OIDC, mTLS).
Key changes:
- **New public API**: `AuthPlugin` (ABC) and `register_auth_plugin` decorator.
- **New registry**: `AuthPluginRegistry` supports runtime registration.
- **Built-in plugins**: `DevAuthPlugin`, `ApiKeyAuthPlugin`, `TrustedAuthPlugin`.
- **Config change**: `auth_mode` widened from `Literal` to `str` for custom modes.
- **Validation delegated**: `validate_server_config()` now delegates to the active
plugin's `validate_config()`, preserving existing validation semantics.
- **Router compatibility**: All existing `require_*` decorators and `resolve_identity`
/ `get_request_context` dependencies remain unchanged. Routers import the same
symbols from `openviking.server.auth`.
- **Tests**: `conftest.py` manually wires the DevAuthPlugin in ASGI tests (lifespan
not triggered). `test_auth.py` expanded with plugin registration and validation tests.
- **Docs**: `04-authentication.md` (en/zh) updated with plugin registration examples.
Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com>
* fix(tests): fix trusted mode test
* fix(tests): fix unit test
* fix(cli): remove unexisted transaction observer
* docs: update skills definition
* docs: update skills definition
* docs: update skills definition
* docs: update skills definition
* fix(skills): now we allow viking://agent/skills again, and optimize CLI for skills
* docs(skills): use -p instead of --parent in agent skills examples
Align the `ov skills add` examples in the context-types and viking-uri
docs with the short flag `-p` introduced for `ov skills list/find/show`,
so all four user-facing examples consistently demonstrate the short form
when targeting `viking://agent/skills`.
Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com>
* fix(tests): error check for api key
* fix(tests): unit test wait until resource not busy
* fix(tests): unit test wait until resource not busy
* fix(sdk): args form in skills find
* fix(skills): pass target uri in request body
---------
Co-authored-by: claude-sonnet-4-6 <noreply@anthropic.com>
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
* feat(grep): integrate VikingDB bm25 keyword search for grep engine
* fix(grep): address CI review feedback: max-size eviction to _count_cache, use Literal, Split regex alternation into individual keywords for bm25 (max 10)
* fix(schema): use dynamic __version__ for schema_version and handle dev suffixes in version comparison
* fix(schema): upsert data to vikingdb lack of content
* chore: add benchmark for retrieval
* fix(grep): vikingdb return 200 and no results means no matching content, not necessary to fallback to local fs
* fix(benchmark): sub uri args; add report
* refactor: code format by ruff
* optimize: move grep config (engine and switch_to_remote_threshold) to ov.conf
* optimize: auto adapt remote_return_limit by agg API; rm unnecessary params in keywords search
* fix: adjust benchmark scripts
* fix(grep): store full content for BM25; use PathScope depth; reduce redundant API calls
* refactor: new benchmark
* fix: step1 add resource by real code data
* feat(benchmark): split grep benchmark into effectiveness/performance suites with async reindex
* optimize (benchmark): adjust keywords and ground truth for testing
* fix: truncate 64KB for content field
* optimize: effectiveness add resource plainly
* optimize: change param use of SearchByKeywords from "keywords" to "query"
* optimize(benchmark): refactor effectiveness scripts
* optimize: ensure raw data for content field
* optimize: fulltext analyzer's stop-words only use symbols
* fix: adapt to new ov cli for benchmark
* optimize: reuse file content to avoid re-read AGFS file
* optimize: tune grep vikingdb defaults and refresh bm25 benchmark scripts
* optimize: benchmark client timeout
* update README
* fix: rm unused param
* fix: default values in docs
* optimize: increase truncate byte size to 1MB for content field for VikingDB
* fix(logger): harden queued stream logging (#2786)
* fix(logger): replace StreamHandler with QueueHandler+QueueListener to prevent thread deadlock
When log.output='stdout' (default) and the server is managed by systemd,
concurrent log writes can deadlock because logging.StreamHandler holds a
thread lock across stream.flush() which blocks on systemd-piped file I/O.
During session.commit() phase 2, multiple async coroutines (memory
extraction, summarization) concurrently call logger.info()/warning()
with large payloads. The first thread's flush() blocks on the pipe,
while all subsequent threads block on handler.acquire() forever.
This permanently silences the server log and prevents _write_done_file()
from executing, leaving phase 2 hanging without .done.
Fix: use QueueHandler + QueueListener from stdlib logging.handlers
(Python 3.2+). QueueHandler.emit() does queue.put(record) with no lock
or I/O, returning immediately. QueueListener has a dedicated single
thread as the sole consumer touching the real StreamHandler, making
lock contention impossible.
Changes in _create_log_handler(): stdout/stderr branches now create
a shared QueueListener with unbounded queue, returning QueueHandler
instances to callers. _build_standard_handler() delegates formatter
and filter setup to the real handler in the listener thread.
Closes: #2752
* fix(logger): harden queued stream logging
---------
Co-authored-by: njuboy11 <njuboy11@users.noreply.github.com>
---------
Co-authored-by: Qin Haojie <qinhaojie.exe@bytedance.com>
Co-authored-by: njuboy11 <njuboy11@users.noreply.github.com>
set_tags shipped stack-wide in #2706 (server, Python SDK, Rust CLI,
openviking_cli) but the Go SDK was the only client surface left without it.
Add Client.SetTags mirroring the Write convention so Go users can write
retrieval tags (replace/append, recursive over directories) without dropping
to Python or the CLI.
Server FindRequest/SearchRequest accept and use agent_id/agent_uri to
scope retrieval to a peer (_ctx_with_legacy_actor_peer), but the Go SDK
could only set a client-global X-OpenViking-Actor-Peer header and had no
per-call selector. Add AgentID/AgentURI to FindOptions/SearchOptions
(appended for unkeyed-literal compat, mirroring #2703) and forward them
via setString (omit-empty) in Find and Search; add request-body tests.