mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-30 17:28:07 +08:00
python-sdk@0.1.9
539
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1d34e23aaa |
docs(changelog): add v0.3.17 entry (EN+ZH) (#2088)
* docs(changelog): add v0.3.17 entry (EN) * docs(changelog): add v0.3.17 entry (ZH) |
||
|
|
933ece4acb |
docs(openclaw): use canonical OpenViking plugin package (#2099)
Co-authored-by: LinQiang391 <linqiang391@users.noreply.github.com> |
||
|
|
bc8fe8be67 |
docs(openclaw): document plugin-source / plugin-package and plugin-version auto-detect from #2072 (#2077)
* docs(openclaw): document plugin-source / plugin-package and plugin-version auto-detect from #2072 (en) * docs(openclaw): document plugin-source / plugin-package and plugin-version auto-detect from #2072 (zh) |
||
|
|
debf5f2b52 |
docs(observability): document server.observability.dump_body from #2052 (#2063)
* docs(observability): document server.observability.dump_body from #2052 * docs(observability): document server.observability.dump_body from #2052 (EN) |
||
|
|
9ebfd59342 |
feat(metrics): add vikingbot feedback observability (#2037)
* feat(metrics): add vikingbot feedback observability * fix(bot): align feedback observability contracts * fix(metrics): decouple feedback collector bootstrap |
||
|
|
f45b22a9a8 |
fix: backup vlm token usage stat (#2068)
* ov vlm circuit_breaker * ov vlm failover * fix: ov vlm failover * fix: ov vlm failover |
||
|
|
910e9dc1c9 |
docs: replace indigo favicons with crystal sailboat (#2067)
Update favicon-32.png, apple-touch-icon.png, and favicon.ico in both docs/images/ and openviking/console/static/. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d1c0730484 |
docs: update readme logo to crystal sailboat (#2066)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
42484bf91d |
fix(plugin/codex): default-on assistant capture, recommend env vars over ov.conf for tuning (#2065)
Two related fixes to plugin tuning ergonomics: 1. `captureAssistantTurns` defaults to true (mirrors claude-code-memory-plugin). A memory plugin that only captures the user side of every turn extracts half the conversation and produces noticeably worse memories. Operators who want the old user-only behavior can still set `OPENVIKING_CAPTURE_ASSISTANT_TURNS=0` or `codex.captureAssistantTurns=false`. 2. README + agent-integrations docs (zh+en) now recommend `OPENVIKING_*` environment variables in shell rc as the primary way to tune the plugin. The previous docs claimed the tuning block lived in `ovcli.conf`, but `scripts/config.mjs` only reads `codex.*` from `ov.conf` — and `ov.conf` is server-scope, so per-machine plugin tuning doesn't belong there anyway. The legacy `ov.conf` path is acknowledged and kept working for backward compat, but de-emphasized. |
||
|
|
8f9e1c0d97 |
docs(api): document observer.filesystem from #2045 (#2057)
* docs(api): document observer.filesystem from #2045 * docs(api): document observer.filesystem from #2045 * docs(metrics): list filesystem component from #2045 * docs(metrics): list filesystem component from #2045 |
||
|
|
77b604a641 |
fix(storage): 优化路径锁与语义刷新并发 (#2029)
* fix(storage): refine path lock semantic refresh concurrency
Use exact path locks for source commits, tree locks only for lifecycle and schema scopes, and coalesce derived semantic writes to avoid stale summary overwrites under concurrent resource and memory updates.
* chore: split benchmark changes into separate PR
* chore: keep semantic refresh design notes out of docs
* style: format lock changes
* fix: preserve resource lifecycle locks
* Revert "fix: preserve resource lifecycle locks"
This reverts commit
|
||
|
|
e80b0a0ff8 |
docs(vlm): document backup config for automatic failover from #2040 (#2042)
* docs(vlm): document backup config for automatic failover (#2040) * docs(vlm): mirror backup config doc to zh (#2040) |
||
|
|
185bce8934 |
docs(design): consolidate openclaw plugin docs into single design doc (#2043)
Replace openclaw-integration.md and openclaw-context-engine-refactor.md with openclaw-plugin-design.md covering the three main chains (assemble, afterTurn, compact), session mapping, tool registry, and config reference. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
c73a32f270 |
fix(plugin/codex): allow empty api_key (unauthenticated local OV) (#2023)
* fix(plugin/codex): allow empty api_key (unauthenticated local OV) Reported: with an ovcli.conf that has no `api_key` (typical local OV without auth), the plugin would not start cleanly. Root cause: .mcp.json ships with `bearer_token_env_var: "OPENVIKING_API_KEY"`, and when that env var resolves to an empty string at codex launch (because ovcli.conf has no key), Codex interprets it as "auth configured but not provided" and falls back to its OAuth dance — which then fails against an OV that doesn't speak OAuth. Hook side is unaffected: scripts/config.mjs already gates the Bearer header on `if (cfg.apiKey)`, so empty api_key → no Authorization header sent → OV accepts in unauth mode. Verified end-to-end with auto-recall against `http://127.0.0.1:1933` and an empty-key ovcli.conf. Fix: at install time, detect whether ANY api_key is configured (env or ovcli.conf) and conditionally render `.mcp.json` *with or without* `bearer_token_env_var`: - api_key present → keep `bearer_token_env_var: "OPENVIKING_API_KEY"` - api_key absent → drop the field entirely (Codex will then just hit OV without Authorization and treat 200 as success) Implementation uses node (already required) to read/edit the cached .mcp.json as proper JSON rather than sed, so we don't have to worry about field-position-dependent regexes. Installer footer now also reports the resolved auth mode so the user sees `MCP auth: Bearer (OPENVIKING_API_KEY)` vs `MCP auth: none (unauthenticated)` at the end of the run. env_http_headers stays in both modes — identity headers (X-OpenViking-Account / User / Agent) are independent of auth and OV accepts empty values (defaults to "default"). * fix(plugin/codex): support runtime OPENVIKING_CLI_CONFIG_FILE swap Reported: setting OPENVIKING_CLI_CONFIG_FILE=ovcli-local.conf (a config without api_key, for benchmark-memory isolation) and running codex fails with: Environment variable OPENVIKING_API_KEY for MCP server 'openviking-memory' is empty Two issues stacked on top of each other: 1. Codex 0.130 hard-fails MCP startup when bearer_token_env_var resolves to an EMPTY env var (confirmed empirically — not OAuth fallback, just a startup error). 2. The previous codex() wrapper exported `OPENVIKING_API_KEY=""` via the inline-prefix syntax `OPENVIKING_API_KEY="${...:-${...:-}}" codex`, which sets the variable to an empty string when no key is resolvable. So even my prior fix (don't render bearer_token_env_var when no key at install time) didn't help users who install with one conf and run with another via OPENVIKING_CLI_CONFIG_FILE. Fix is two parts: a) Build the env prefix dynamically into a bash array, skipping any OPENVIKING_* whose resolved value is empty. So an empty api_key produces no OPENVIKING_API_KEY at all in codex's env — neither set-to-empty nor set-to-something. b) Have the wrapper re-render the cached .mcp.json's bearer_token_env_var on every codex launch based on the currently-active ovcli.conf. The idempotent fast-path skips writing when the desired state already matches. This makes swapping configs at runtime (typical benchmark isolation workflow) work without re-running the installer. The wrapper now uses `env "${_env_args[@]}" codex "$@"` instead of the inline-prefix form for the same reason — proper handling of conditional env-var presence. Manual setup snippets in README + docs (en/zh) updated to the same empty-aware pattern; the cache-rendering bit is left to the installer- emitted wrapper since it's noisy and only needed when actually swapping configs. Validated with synthetic test: ovcli-local.conf (no api_key) → env passed to codex: URL=..., ACCOUNT=..., USER=..., AGENT_ID=codex (no OPENVIKING_API_KEY at all) → cache .mcp.json rewritten to drop bearer_token_env_var ovcli.conf (with api_key) → env passed to codex: URL=..., API_KEY=..., ACCOUNT=..., USER=..., AGENT_ID=codex → cache .mcp.json rewritten to re-add bearer_token_env_var Idempotent: re-render with same hasKey state does not bump file mtime. * fix(plugin/codex): wrapper also re-renders cache .mcp.json URL Previously the codex() wrapper only re-rendered bearer_token_env_var based on the active ovcli.conf, but the cached .mcp.json URL stayed whatever was baked at install time. Result: swapping OPENVIKING_CLI_CONFIG_FILE to a config that points at a different OV server (e.g. localhost) would still hit the install-time URL — typically the remote production OV — and fail auth. Reported in testing: OPENVIKING_CLI_CONFIG_FILE=ovcli-local.conf codex # ovcli-local.conf: { "url": "http://127.0.0.1:1933" } # cache .mcp.json still says url=https://ov-dev.tosaki.top/mcp # Codex hits remote ov-dev with no bearer → 401 → "Not logged in" OAuth dance Fix: the rewrite block now also patches s.url from the conf-resolved URL (`${_ov_url%/}/mcp`, or `$OPENVIKING_MCP_URL` if explicitly set). Same idempotent fast-path — only writes when something actually changed. Tested both directions: ovcli-local.conf (no key, localhost) → cache .mcp.json: url=http://127.0.0.1:1933/mcp, no bearer field → env passed to codex: no OPENVIKING_API_KEY → /mcp: Auth: None, tools list populated ovcli.conf (with key, remote) → cache .mcp.json: url=https://ov-dev.tosaki.top/mcp, bearer present → /mcp: Auth: Bearer token, tools list populated |
||
|
|
b0076110ae |
refactor(plugin/codex): switch MCP from local stdio to OV /mcp directly (#2022)
* refactor(plugin/codex): switch MCP from local stdio server to OV /mcp (http)
Codex 0.130 supports streamable-HTTP MCP servers with bearer auth via
`bearer_token_env_var` in `.mcp.json` (and per-header env binding via
`env_http_headers`). OpenViking server has exposed `/mcp` natively since
1.27, so the local stdio MCP middleman (`src/memory-server.ts` +
`servers/memory-server.js` + the npm-ci runtime bootstrap) is dead weight:
the model now gets a strictly larger tool set (search, store, read, list,
grep, glob, forget, add_resource, health — vs the previous recall/store/
forget/health) by talking to OV directly, and the plugin loses its only
build/dependency surface.
What changed
- `.mcp.json`: switched to `url` + `bearer_token_env_var: "OPENVIKING_API_KEY"`
+ `env_http_headers` for the multi-tenant identity headers. URL is a
`__OPENVIKING_MCP_URL__` placeholder; installer renders it from ovcli.conf
/ `OPENVIKING_URL` at install time. API key never lands on disk in the
cached .mcp.json — it's pulled from process env at codex launch.
- `setup-helper/install.sh`: resolves the OV /mcp URL (OPENVIKING_MCP_URL >
OPENVIKING_URL/mcp > ovcli.conf.url/mcp > localhost), renders the
.mcp.json placeholder into the cached copy, and appends a `codex()` shell
function wrapper to the user's rc that promotes ovcli.conf fields into
env vars before exec'ing codex (mirrors the claude-code-memory-plugin
pattern; needed because Codex reads OPENVIKING_API_KEY from process env
at MCP launch, not from any file).
- Deleted: `src/memory-server.ts`, `servers/memory-server.js`, `tsconfig.json`,
`package.json`, `package-lock.json`, `scripts/bootstrap-runtime.mjs`,
`scripts/runtime-common.mjs`, `scripts/start-memory-server.mjs`. Net
~2400 lines removed. Hook scripts remain zero-dep .mjs running on
Codex's bundled Node 22.
- README + docs/{en,zh}/agent-integrations/04-codex.md: rewritten to
describe the new architecture. The MCP tools list and protocol details
are now referenced via a link to docs/{en,zh}/guides/06-mcp-integration.md
rather than duplicated in the plugin docs.
- Plugin version: 0.4.1 → 0.5.0.
Validation
Verified end-to-end on Codex 0.130 against `ov-dev.tosaki.top`:
/mcp
🔌 MCP Tools
• openviking-memory
• Auth: Bearer token
• Tools: add_resource, forget, glob, grep, health, list, read, search, store
`openviking-memory.health` returned `OpenViking is healthy ... storage: VikingFS`;
Stop hook reported `appended 2 turn(s) to OpenViking session <id>`.
Notes
- `.mcp.json` headers that don't have a corresponding env var (e.g. user
didn't set `OPENVIKING_USER`) are simply not sent — `env_http_headers`
silently omits missing vars per Codex's MCP runtime.
- Rotating the API key now just needs `codex` restart (env re-reads from
ovcli.conf via the wrapper). URL changes still need a re-install since
the URL is baked into the cached .mcp.json.
- The shell function wrapper has a marker-delimited block so re-running
the installer replaces it in place rather than appending duplicates.
* review(plugin/codex): address copilot feedback on installer + docs
1. Switch the codex() shell-function wrapper from jq to node. The installer
already hard-requires node 22+, while jq is not always present; the old
wrapper would silently fall through to `command codex` with no env
injection when jq was missing, which caused Codex to start with no
Bearer token, OV to return 401, and Codex to drop into its OAuth
fallback. Now there is a single tool dependency for both the installer
and the wrapper it emits.
2. Marker-replacement is now defensive: rewrite-in-place only triggers
when BOTH the BEGIN and END markers exist in the rc. If only BEGIN
is present (manual edit / corruption), warn and append a fresh block
instead of awk-dropping everything from BEGIN to EOF.
3. When no rc is detected, omit the `source $RC` line from the final
"Next:" hint and tell the user to paste the snippet manually instead
of printing `source ` with a trailing space.
4. Docs (README + 04-codex.md zh/en): use the full env var names
(OPENVIKING_API_KEY / OPENVIKING_ACCOUNT / OPENVIKING_USER /
OPENVIKING_AGENT_ID) instead of `_ACCOUNT` / `_USER` shorthand;
update the manual-setup snippets to the node-based wrapper.
The wrapper body is now defined once and reused for both the appended-to-rc
path and the manual-paste path, so the two cannot drift.
|
||
|
|
8034abc158 |
docs(plugin/codex): dedicated agent-integrations page (zh+en) + fix MCP startup (#2019)
* docs(plugin/codex): add dedicated agent-integrations page + fix MCP startup Follow-up to #1957. Lifts Codex out of `04-other-plugins.md` into its own `04-codex.md` (en + zh) with full install steps, configuration, hook behavior, and troubleshooting — mirrors the shape of `02-claude-code.md`. Renumbers `04-other-plugins.md` → `05-` and `05-langchain-langgraph.md` → `06-`. Overview tables in both locales updated; cross-refs fixed. Also fixes two install/runtime bugs surfaced while validating the fresh installer flow against the merged PR: 1. **Stale repo clone**: `setup-helper/install.sh` previously skipped the clone if `~/.openviking/openviking-repo` already existed, so a user who installed before #1957 merged ended up with a pre-PR plugin checkout (no `scripts/`, no `servers/memory-server.js`). The installer now `git fetch + reset --hard` an existing checkout to `$REPO_REF` (default `main`), matching the claude-code installer pattern. 2. **`${CODEX_PLUGIN_ROOT}` not expanded in `.mcp.json`**: Codex 0.130 does not substitute env vars in `.mcp.json` `args`/`env` and does not always inject `CODEX_PLUGIN_ROOT` into MCP child env. The literal string `${CODEX_PLUGIN_ROOT}` was being passed to node, which then tried to resolve `${CODEX_PLUGIN_ROOT}/scripts/start-memory-server.mjs` against codex's cwd and failed with `MODULE_NOT_FOUND`. Fix: - `.mcp.json`: `args: ["scripts/start-memory-server.mjs"]` + `cwd: "."` (matches the syntax 0.1.0 used, which Codex does honor) - `scripts/runtime-common.mjs`: derive plugin root from `import.meta.url` as a fallback so the launcher works regardless of whether `CODEX_PLUGIN_ROOT` is set in the spawn env Bumps plugin to 0.4.1 (package.json + plugin.json + lockfile) since the runtime-common.mjs change invalidates the install-state hash and forces a re-install of node_modules into the per-user runtime data root. * fix(plugin/codex): hooks.json must use relative paths, not ${CODEX_PLUGIN_ROOT} Same root cause as the .mcp.json fix in the previous commit: Codex 0.130 does not expand ${CODEX_PLUGIN_ROOT} in hooks.json `command` strings. The shell that runs the hook sees the literal ${CODEX_PLUGIN_ROOT} and expands it to "" (or leaves it literal), so node tries to load `/scripts/...mjs` and exits 1. Symptom in the chat UI: • SessionStart hook (failed) error: hook exited with code 1 • UserPromptSubmit hook (failed) • Stop hook (failed) Fix: use `./scripts/<name>.mjs` paths, matching the pattern Codex's own bundled plugins (e.g. figma) use. Codex's hook dispatcher resolves these relative to the plugin root (where hooks.json lives). The MCP launcher fix from the prior commit already handles the same class of bug for .mcp.json; this catches the hooks path. * fix(plugin/codex): hooks.json needs absolute paths rendered at install time Previous fix (relative ./scripts/...) was based on the figma example but empirically does not work on Codex 0.130: the hook subprocess runs with cwd = user's cwd (not plugin root) and CODEX_PLUGIN_ROOT is NOT injected into the env. So both ${CODEX_PLUGIN_ROOT}/scripts/foo.mjs and ./scripts/foo.mjs resolve to the wrong absolute path and node exits 1. Verified with a probe shell script wired into hooks.json: argv: /tmp/codex-hook-probe.sh SessionStart cwd: /Users/<user> CODEX_PLUGIN_ROOT: <unset> CODEX_PLUGIN_DATA: <unset> (The "Under-development features are incomplete" banner Codex prints when plugin_hooks is enabled is real - the hook env wiring is unfinished in 0.130.) Fix: keep the source hooks.json as a template (uses __OPENVIKING_PLUGIN_ROOT__ placeholder) and have install.sh sed-render the cache copy with the absolute $CACHE_DIR path on every install. The cached hooks.json is now fully self-contained absolute-path commands; the repo's checked-in copy stays portable. .mcp.json is unaffected: Codex 0.130 does honor the `cwd: "."` field for MCP servers, so relative args resolve against plugin root there. * fix(plugin/codex): bump UserPromptSubmit timeout to 15s Empirically the auto-recall hook can take 0.8s–4s end-to-end (depending on result count and remote OV latency), and Codex 0.130 sometimes adds 4-5s of spawn overhead before our script even starts. The original 8s budget was borderline and produced spurious "hook timed out after 8s" UI errors on slow paths even when the recall would have succeeded. 15s matches the auto-recall internal timeoutMs default (config.mjs:186) and gives enough headroom for spawn-time variance without holding the user's input noticeably longer in the worst case. * fix(plugin/codex): installer accepts OPENVIKING_REPO_BRANCH as alias Per review feedback: the claude-code installer uses OPENVIKING_REPO_BRANCH for the same purpose. Aliasing both names lets users reuse one env var across installers without remembering which plugin uses which name. Precedence: OPENVIKING_REPO_REF > OPENVIKING_REPO_BRANCH > "main". |
||
|
|
e92180a7e1 |
feat(plugin/codex): add lifecycle hooks (recall, capture, pre-compact) to codex-memory-plugin (#1957)
* feat(plugin/codex): add lifecycle hooks (recall, capture, pre-compact)
Brings the codex-memory-plugin to feature parity with the claude-code-memory-plugin
by wiring the four Codex lifecycle hooks via `hooks.json`:
- SessionStart -> bootstrap-runtime.mjs (npm ci into ${CODEX_PLUGIN_DATA}/runtime)
- UserPromptSubmit -> auto-recall.mjs (search OV, inject via hookSpecificOutput.additionalContext)
- Stop -> auto-capture.mjs (incremental transcript capture + last_assistant_message commit)
- PreCompact -> pre-compact-capture.mjs (full transcript -> single OV session -> commit)
Differences from the Claude Code plugin baked into the scripts:
- Codex output schema does not allow `decision: "approve"`; no-op is `{}`
- Stop/PreCompact only support `systemMessage`, not `additionalContext`
- Plugin envs are CODEX_PLUGIN_ROOT / CODEX_PLUGIN_DATA
- Config section is `codex` (was `claude_code`); config file defaults to
`~/.openviking/ovcli.conf`, falling back to legacy `~/.openviking/ov.conf`
Other changes:
- src/memory-server.ts now reads ovcli.conf-style configs (top-level `url`,
`api_key`, `account`, `user`, `agent_id`) so the plugin works against
hosted OpenViking deployments out of the box. Env-var-only operation
(OPENVIKING_URL set, no config file) is also supported.
- .mcp.json points at scripts/start-memory-server.mjs, which boots the same
runtime the hooks use, so the MCP path benefits from npm-ci bootstrap.
- README rewritten with architecture diagram, validation SOP, configuration
reference, and a Codex-vs-Claude-Code differences table.
Validated end-to-end against an OpenViking deployment:
- Auto-recall returns ranked memories with full content and emits
hookSpecificOutput.additionalContext.
- Auto-capture (last_assistant_message path) creates a session, commits, and
the OV pipeline extracts events + preferences within ~60s.
- Pre-compact-capture posts a full 4-turn transcript to one OV session,
commits with archived=true, and produces structured leaf memories
(preferences, events, entities) under viking://user/<user>/memories/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(plugin/codex): drop SessionStart, split Stop=add_message vs PreCompact=commit
Codex's `Stop` hook fires per turn, not at session end, so committing per-Stop
over-fragments memory extraction. And codex re-fires `SessionStart` on short
reconnects, so registering an `npm ci` bootstrap there reinstalls the runtime
unnecessarily.
This change keeps one long-lived OpenViking session per codex `session_id`
across all `Stop` invocations, and only triggers the OV memory extractor on
`PreCompact` (or via an idle-sweep best-effort commit when codex exits without
compacting).
- hooks.json: drop SessionStart entry; keep UserPromptSubmit/Stop/PreCompact
- scripts/session-state.mjs (new): per-codex-session state under
~/.openviking/codex-plugin-state/, tracks ovSessionId + capturedTurnCount
- scripts/auto-capture.mjs (Stop): incremental add_message only, idle-sweep at
the tail to commit stale codex sessions (default IDLE_TTL=30 min, override
with OPENVIKING_CODEX_IDLE_TTL_MS)
- scripts/pre-compact-capture.mjs (PreCompact): catch-up append + commit the
long-lived OV session, then null out ovSessionId so the next Stop opens a
fresh OV session for the post-compact half
- MCP runtime install stays lazy in start-memory-server.mjs (already there);
no SessionStart hook means short reconnects don't re-trigger npm ci
- VERIFICATION.md: end-to-end SOP against a live OV server (~3 min)
- bump plugin to 0.3.0
Verified end-to-end against ov.zaynjarvis.com:
Stop adds turns idempotently and incrementally; PreCompact commits to
history/archive_001/ with extractor producing memories under
viking://user/<user>/memories/profile.md after ~30 s; post-compact Stop
opens a fresh OV session; idle-sweep commits stale state files.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(plugin/codex): replace idle-sweep with SessionStart(source=clear) commit
Per Zayn's followup ("非必要不要加 idle commit"): drop the idle-sweep added
in the previous commit and use codex's actual context-disappearing signal —
SessionStart with source=clear — to commit orphaned sessions.
Codex hook signal map:
- /compact → PreCompact ✅ commit (already)
- /clear → SessionStart(source=clear) for the NEW session_id;
the prior transcript is orphaned. Now committed.
- /new → SessionStart(source=startup); ambiguous with fresh
codex startup, so we don't act on it.
- /resume / short reconnect → SessionStart(source=resume|startup); no-op
to avoid corrupting still-active sessions.
- SIGTERM/Ctrl+C/exit → no hook fires. Documented as a known gap; users
should /compact before /exit if they want commit.
Changes:
- new scripts/session-start-commit.mjs: gates internally on source=clear,
iterates listStates(), and commits any state file whose codexSessionId
!= the new SessionStart session_id, then clears that state file
- hooks/hooks.json: re-register SessionStart pointing at the new script
(timeout 30s)
- scripts/auto-capture.mjs: remove sweepIdleSessions() and
IDLE_TTL_MS env handling; Stop is now strictly add_message
- README/VERIFICATION.md: update arch diagram, replace idle-sweep step
with SessionStart(source=clear) verify (positive + negative paths),
add "Known gap: SIGTERM/exit are silent" section
- bump to 0.3.1
Verified end-to-end against ov.zaynjarvis.com:
Stop add+idempotent ✓
SessionStart source=startup → {} ✓
SessionStart source=resume → {} ✓
SessionStart source=clear → committed prior OV session, history/archive_001/
appeared, profile.md gained "Favorite snack: dark chocolate" within 30 s.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* chore(plugin/codex): SessionStart matcher = "clear" (native dispatcher gate)
Codex's hooks dispatcher matches the SessionStart hook's `matcher` field
against the SessionStart `source` value. Setting matcher to "clear" means
codex won't even spawn our script on `source=startup` or `source=resume`
(short reconnects); we previously gated this in-script. The internal
source check in session-start-commit.mjs is kept as defense-in-depth.
Source: codex-rs/hooks/src/events/session_start.rs `select_handlers(...,
matcher_input: Some(request.source.as_str()))` and
codex-rs/hooks/src/events/common.rs `is_exact_matcher` — "clear" is
all-alphanumeric so it's matched as exact equality, not regex.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(plugin/codex): active-window heuristic + idle-TTL sweep at SessionStart (v0.4.0)
Source of truth: examples/codex-memory-plugin/DESIGN.md (added in this commit).
Behavioral changes:
- SessionStart matcher widens from `clear` to `clear|startup`. Both sources
run the same active-window heuristic; `resume` is a hard no-op (still fires
on short reconnects).
- Heuristic (DESIGN.md §3): count state files (excluding new session_id) within
ACTIVE_WINDOW_MS (default 2 min). 0 → noop, 1 → commit it (just-ended
session), ≥2 → skip and rely on idle TTL. Tunable via
OPENVIKING_CODEX_ACTIVE_WINDOW_MS.
- Idle-TTL sweep returns at the tail of session-start-commit.mjs only (not
every Stop). Default IDLE_TTL_MS = 30 min via OPENVIKING_CODEX_IDLE_TTL_MS.
Catches SIGTERM/Ctrl+C/`/exit` orphans and the ≥2-active skip path.
- Stop hook deliberately does NOT sweep — state-write-on-every-turn already
gives us the freshness signal. Marker comment added.
- Stop hook adds post-compact transcript-shrink defense: if
allTurns.length < state.capturedTurnCount, reset capturedTurnCount = 0.
- Commit-on-failure preserves state everywhere (PreCompact, heuristic,
idle sweep). A non-2xx /commit no longer clears ovSessionId; the next
sweep retries.
- session-state.mjs saveState now uses atomic write (tmpfile + rename) for
crash safety. listStates ignores the brief `<id>.json.tmp` window.
Bump: package.json + .codex-plugin/plugin.json → 0.4.0.
Docs: README "How It Works" gained a DESIGN.md pointer and rewrites the
SessionStart section to reflect heuristic + idle TTL. VERIFICATION.md step 6
now exercises all four heuristic branches (0/1/≥2 active, idle TTL, resume).
Phase-2 resume context inject documented in DESIGN.md but explicitly out of
scope here.
Verified locally with synthetic stdin tests against a fake OV server:
1-active commit, ≥2-active skip, idle TTL sweep, resume noop,
unreachable-server keeps state.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(plugin/codex): align config loading with claude-code plugin
Addresses three review points on PR #1957:
1. Honor OPENVIKING_CLI_CONFIG_FILE for the ovcli.conf override path
(matches the convention used by `ov` CLI and claude-code-memory-plugin).
OPENVIKING_CONFIG_FILE stays as the ov.conf override; for backward
compat it still works when pointed at an ovcli-shaped file.
2. Strict env-first priority for every connection / identity field
(baseUrl, apiKey, account, user, agentId). Env vars now win over
ovcli.conf, which wins over ov.conf's codex.* block / server.*,
which wins over built-in defaults.
3. Unify hook and MCP-server config loading: src/memory-server.ts now
imports loadConfig from scripts/config.mjs (relative path stays
valid post-compile because servers/ and scripts/ are siblings),
eliminating the divergent account/user/agentId fallback chains
the PR-Agent reviewer flagged.
Auth header: emit Authorization: Bearer (primary, required by OpenViking
Cloud) plus the legacy X-API-Key during the transition window. All six
fetch sites updated (4 hook scripts + memory-server.ts + compiled
servers/memory-server.js).
README: document the new resolution chain, OPENVIKING_CLI_CONFIG_FILE,
OPENVIKING_BEARER_TOKEN alias, and the Authorization: Bearer migration.
* docs(plugin/codex): put installation first
* fix(plugin/codex): harden runtime and capture paths
* docs(plugin/codex): align local marketplace name
* docs(plugin/codex): add one-line installer
* fix(plugin/codex): support branch installer testing
* fix(plugin/codex): keep installer env surface stable
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: zhengxiao.wu <zhengxiao.wu@bytedance.com>
|
||
|
|
fa0be9c958 |
feat: make queuefs backend configurable (#2018)
fix: harden request wait tracker against queue races update refactor queuefs mode config docs: document queuefs mode and refactor mount resolver |
||
|
|
6a65776b63 | Add files via upload (#2013) | ||
|
|
15817dcf15 |
Revert "Feat/fs count api (#1989)" (#1997)
This reverts commit
|
||
|
|
4f42c26225 | docs(server): clarify Python 3.14 Ark warning (#1987) | ||
|
|
3222b14d0c |
Feat/fs count api (#1989)
* feat(fs): add count API for directory entry counting Adds a dedicated `count` endpoint that returns the exact number of files and sub-directories under a directory by traversing the filesystem, distinct from `stat`'s vector-index-based estimate. Wired through VikingFS, FSService, HTTP router and sync/async/local SDK clients. * feat(cli): add `ov count` command for directory entry counting Wires the new fs.count HTTP endpoint into the Rust CLI. Adds `-r/--recursive` and `-a/--all` flags. Documentation updated with CLI usage examples. * fix --------- Co-authored-by: dingben.db@bytedance.com <dingben.db@bytedance.com@bytedance.com> |
||
|
|
81d1b5afd7 |
feat(langchain): add LangChain and LangGraph context adapters (#1964)
* feat(langchain-langgraph): add adapter primitives * feat(langchain-langgraph): add context backend lifecycle * fix(langchain-langgraph): harden context backend integration * fix(langchain-langgraph): accept canonical store result URIs * docs(langchain): point users to runnable examples * docs(langchain): add missing integration examples * refactor(langchain): address integration review feedback * fix(langchain): address review-blocking integration bugs * fix(langchain): honor user ids and safe store filters * fix(langchain): reject unsupported store TTL writes |
||
|
|
25e7615c51 |
feat(vlm): support extra request body passthrough (#1973)
Add VLM configuration support for provider-specific JSON body fields and pass them through to OpenAI-compatible and LiteLLM completion calls. Document the option and cover OpenAI, LiteLLM, DashScope merge behavior, and legacy flat config migration. |
||
|
|
c2345b9e6a |
feat: CLI optmization for counting (#1980)
* feat: ov add-resource (spec -L --level), ov stat (return count for dir) * feat: ov add-resource (spec -L --level), ov stat (return count for dir) |
||
|
|
5fdbfd4e49 |
feat(ovpack): support vector snapshots and consistency checks (#1965)
* feat(ovpack): add vector snapshot backup support * feat(ovpack): add vector snapshots and consistency checks * fix(ovpack): validate reserved paths and index expectations * fix(ovpack): limit consistency report output * fix(ovpack): isolate archive content namespace * chore(ovpack): move consistency cli under system |
||
|
|
68b049528d |
docs(observability): note ov tui image preview capability (#1917)
* docs(observability): note ov tui image preview capability Follows volcengine/OpenViking#1916 (feat: ov CLI support) which added image_preview.rs supporting png/jpg/jpeg/gif/bmp/webp/tiff/tif files. * docs(observability): note ov tui image preview capability (zh mirror) |
||
|
|
db3d5db156 |
docs(storage): document task_tracker backend config from #1949 (#1971)
* docs(storage): document task_tracker backend config from #1949 * docs(storage): document task_tracker backend config from #1949 (ZH) |
||
|
|
4237d5018f |
docs(retrieval): align score_propagation_alpha default 0.5→1.0 with #1947 (#1948)
* docs(retrieval): align score_propagation_alpha default 0.5→1.0 with #1947 * docs(retrieval): align score_propagation_alpha default 0.5→1.0 with #1947 * docs(retrieval): align score_propagation_alpha default 0.5→1.0 with #1947 * docs(retrieval): align score_propagation_alpha default 0.5→1.0 with #1947 * docs(retrieval): align score_propagation_alpha default 0.5→1.0 with #1947 |
||
|
|
7c69819684 |
docs(telemetry): list session create/add_message endpoints + SDK methods (#1943) (#1958)
* docs(telemetry): list session create/add_message endpoints + SDK methods (#1943) * docs(telemetry): 镜像 EN — list session create/add_message endpoints + SDK methods (#1943) |
||
|
|
217eaa5448 |
docs(api): document isLocked field in stat() response (#1940) (#1956)
* docs(api): document isLocked field in stat() response (#1940) * docs(api,zh): document isLocked field in stat() response (#1940) |
||
|
|
e648b2679c |
feat(ovpack): add v2 manifest and backup restore (#1927)
* feat(ovpack): add v2 manifest and conflict policy Add a portable OVPack manifest for scalar metadata and make imports validate scope, derived files, and conflicts before writing. * fix(ovpack): remove import vectorize option Make OVPack imports always rebuild vectors in the target environment, keep legacy packages compatible, and reject unsupported manifest versions before writing. * fix(ovpack): remove force import alias Use on_conflict as the single OVPack import conflict policy and reject removed force inputs. * fix(ovpack): regenerate runtime vector metadata Keep type portable but stop exporting or applying created_at, updated_at, and active_count from OVPack manifests. * fix(ovpack): validate manifest contents * fix(ovpack): require manifests for imports * fix(ovpack): close manifest validation gaps * fix(ovpack): defer parent creation until validation passes * fix(ovpack): remove export size guard * fix(ovpack): support session and scope-root restores * docs(ovpack): document full backup migration * feat(ovpack): add backup restore workflow * fix(ovpack): validate import scope compatibility |
||
|
|
b88b9c3baf |
docs(claude-code): describe session-start profile injection (#1914) (#1962)
* docs(claude-code): describe session-start profile injection from #1914 * docs(claude-code): describe session-start profile injection from #1914 (zh) |
||
|
|
c4794319c5 |
docs(ovcli.conf): note interactive setup-cli + multi-server switch from #1916 (#1961)
* docs(ovcli.conf): note interactive setup-cli + multi-server switch from #1916 * docs(ovcli.conf,zh): note interactive setup-cli + multi-server switch from #1916 |
||
|
|
0073ce7e05 |
fix(openclaw): split OpenViking import tools (#1946)
Replace the ambiguous OpenClaw ov_import surface with explicit add_resource and add_skill tools and slash commands. Guide media attachment imports through the existing OpenViking temp-upload and resource import APIs instead of invented upload endpoints. Update docs, plugin manifest, and unit coverage for the split import commands. Co-authored-by: GPT-5.5 <noreply@openai.com> |
||
|
|
0f5a569eec |
fix(mcp): support recursive directory deletion in forget tool (#1935)
The MCP `forget` tool only forwarded `uri` to `fs.rm` and never set `recursive`, so any directory URI hit a `FailedPreconditionError` from `VikingFS.rm`. Docs and the REST DELETE endpoint already advertised directory deletion, leaving MCP clients without an equivalent. Add an optional `recursive` parameter to the MCP tool (default False to preserve fail-closed behavior on accidental tree deletes), update the zh/en MCP integration tables, and cover both directory paths in tests. Fixes #1928 |
||
|
|
66d447187b | fix(storage): canonicalize shorthand namespace URIs on write (#1929) | ||
|
|
6c58cb18b8 |
docs(openclaw-plugin): surface openclaw openviking status in Verify section (#1924)
Source: PR #1904 (LinQiang391, merged 2026-05-08T09:38:05Z by qin-ctx) added two CLI commands to the OpenViking OpenClaw plugin: - `openclaw openviking setup` — interactive + non-interactive (`--base-url ...`) with `--api-key`, `--agent-prefix`, `--account-id`, `--user-id`, `--allow-offline`, `--force-slot`, `--reconfigure`, `--zh`, `--json` - `openclaw openviking status` — `Show current OpenViking plugin status and connectivity`, with `--zh`, `--json` The integration page docs/{en,zh}/agent-integrations/03-openclaw.md was not touched by #1904; its Verify section still only lists indirect manual checks (`openclaw config get plugins.slots.contextEngine`, `openclaw logs --follow`, `cat openviking.log`, `ov-install --current-version`). None of those probe server compatibility or give a single-command health summary. This commit adds a small additive intro to Verify in both en + zh that: - Documents `openclaw openviking status` as the one-shot health check (registration, connectivity, version compatibility — calls `getStatus(configPath)` + `checkServiceHealth` + `formatCompatRange` per examples/openclaw-plugin/commands/setup.ts L590-612). - Calls out `--json` for automation, with the explicit caveat that `setup --json` requires `--base-url` (setup.ts L498 enforces this; without the caveat readers may attempt bare `setup --json` and hit `--json requires --base-url for non-interactive mode`). - Leaves all existing manual-signal verifications in place — the original steps are still useful for debugging individual layers. |
||
|
|
3d35c1ad62 |
docs(config): document temp_upload server config + ovcli.conf upload.mode (#1899) (#1925)
* docs(server,cli): document temp_upload server config + ovcli.conf upload.mode (#1899) * docs(zh): mirror temp_upload + upload.mode docs (#1899) |
||
|
|
7d5fa62398 |
feat(oauth): native OAuth 2.1 authorization for MCP clients (#1870)
* feat(oauth): hand-sewn OAuth 2.1 M1+M2 (config, JWT, storage, /oauth/token, JWT discriminator)
Snapshot before evaluating migration to mcp.server.auth SDK provider. The
hand-rolled HS256 JWT implementation in openviking/server/oauth/jwt.py is
the main candidate for replacement: its surface area is small but it would
require careful crypto review by maintainers, while the official MCP SDK
already ships an OAuth provider wired into FastMCP.
Included so far:
- OAuthConfig + integration into OpenVikingConfig (default disabled)
- openviking/server/oauth/{jwt,storage,otp,router}.py
- POST /oauth/token (authorization_code + refresh_token, PKCE S256, RFC 6749 errors)
- JWT discriminator in resolve_identity (fail-closed; ResolvedIdentity.from_oauth)
- WWW-Authenticate Bearer hint on /mcp 401 (RFC 9728)
- 49 OAuth-specific unit/integration tests (all passing)
Not yet implemented (M3 / MVP gap):
- /oauth/register (DCR), /oauth/authorize (HTML + OTP submit), well-known metadata
- POST /api/v1/auth/otp REST endpoint
* refactor(oauth): switch to mcp.server.auth SDK provider, drop hand-sewn JWT
Replaces the hand-rolled HS256 JWT signer / token endpoint / DCR with
the OAuth 2.1 surface shipped in mcp.server.auth. We supply a Provider
that adapts the existing OAuthStore (SQLite) to the SDK Protocol, plus
two custom routes the SDK doesn't own: an OTP-entry HTML page (the URL
provider.authorize() returns) and POST /api/v1/auth/otp for issuing
OTPs against an existing API key.
Net result: all OAuth crypto is now the SDK's responsibility (PKCE
S256, redirect_uri matching, error formatting). The OpenViking-side code
contains zero cryptography — access tokens are opaque random strings
prefixed with `ovat_` and looked up in SQLite by SHA-256 hash. Refresh
tokens, auth codes, OTPs use the same scheme.
Highlights:
- openviking/server/oauth/provider.py: OpenVikingOAuthProvider implements
the 8-method SDK Protocol, including subclassing AuthorizationCode /
RefreshToken / AccessToken to pin (account_id, user_id, role) per
token. Refresh-token replay triggers per-user chain revocation.
- openviking/server/oauth/storage.py: adds oauth_access_tokens and
oauth_pending_authorizations tables; peek_auth_code / peek_refresh
for non-destructive lookups; revoke_user_tokens cascades all OAuth
state for an (account, user) pair when a key is rotated.
- openviking/server/oauth/router.py: minimal authorize page (inline
HTML with frame-ancestors 'none') + OTP endpoint authenticated via
existing get_request_context dependency.
- openviking/server/auth.py: replaces JWT discriminator with prefix
match + provider.load_access_token; still fail-closed.
- openviking/server/app.py: mounts SDK routes via create_auth_routes
alongside our authorize-page + OTP routes.
- Deletes openviking/server/oauth/jwt.py and tests/server/oauth/test_jwt.py.
Tests: 32 passing, including a full DCR -> OTP -> authorize page ->
token-exchange -> /mcp lookup happy path, refresh rotation, and replay
detection. Existing test_auth.py regression unchanged.
Phase 1 still missing for full Claude.ai connectivity:
- WWW-Authenticate hint already present on /mcp 401 (from M2)
- /.well-known/oauth-protected-resource (RFC 9728) — not currently
emitted by the SDK; small custom route still TODO.
* docs(oauth): rewrite design doc to reflect mcp.server.auth SDK approach
The earlier draft described a hand-sewn HS256 JWT plan; the implementation
took a different route after discovering mcp.server.auth ships a complete
RFC 6749 / 7591 / 8414 server. Updated to reflect:
- SDK owns the protocol surface (DCR, /authorize parsing, /token, metadata,
PKCE, redirect_uri matching, error codes).
- OpenViking only contributes a Provider implementation, the OTP-entry
HTML page, and POST /api/v1/auth/otp.
- Tokens are opaque (ovat_ / ovrt_ / ovac_ prefixes) — no JWT, no crypto
on our side.
- Implementation status: M1/M2/M3 done; only RFC 9728 protected-resource
metadata + reverse-proxy issuer derivation remain for full Claude.ai
end-to-end connectivity.
* feat(oauth): add /.well-known/oauth-protected-resource (RFC 9728)
The /mcp 401 path already advertises this URL via WWW-Authenticate
Bearer resource_metadata="...", but the endpoint itself didn't exist —
clients fetched it and got a 404, which silently broke the discovery
chain even though /.well-known/oauth-authorization-server worked. Wire
up the resource metadata document so the full RFC 9728 → RFC 8414
discovery chain works end-to-end.
Uses mcp.shared.auth.ProtectedResourceMetadata pydantic model. Reads
X-Forwarded-Proto/Host so the published resource URL matches what the
client used (matches our existing WWW-Authenticate behavior).
Cache-Control: max-age=3600 — metadata is stable across requests.
* feat(console): add OTP issuance button in Settings panel
Adds a "Get OTP" button under the Settings panel of the 8020 web
console. Clicking it issues an OAuth OTP via the user's existing API
key (already loaded into sessionStorage) and displays it inline with
a copy-to-clipboard button.
Replaces the previous workflow of users having to:
curl -X POST -H "X-Api-Key: $KEY" http://1933/api/v1/auth/otp
…with a single button-click flow that the user can reach from any
machine with a browser.
Wires:
- console/app.py: new POST /console/api/v1/ov/auth/otp proxy route,
forwarding to upstream /api/v1/auth/otp. Not gated by write_enabled
since OTP issuance is an authentication artifact, not data mutation.
- index.html: new OAuth section in the Settings panel with otpBox
(hidden until OTP is generated) and a Copy button.
- app.js: getOtpBtn click handler calls callConsole, otpCopyBtn copies
to clipboard. Clear failure messages when the user has no API key
loaded yet.
This is the lightweight half of the Console-OAuth integration. The
fuller "same-origin auto-authorize" flow (Phase 2) — where the
authorize page detects sessionStorage and submits the OTP form
automatically — is still TBD and will reuse this proxy route.
* feat(oauth): device-flow style authorize page + console verify form
Pivots the OTP flow direction so the UX matches OAuth 2.0 Device
Authorization Grant (RFC 8628) more closely:
Old (push): user goes to console -> Get OTP -> copy -> paste in
client's authorize page -> submit -> redirect.
New (pull): client's authorize page DISPLAYS a 6-char code -> user
types it into the console verify form -> page polls -> redirect.
This removes one tab switch and aligns with how users mentally model
authorization ("I'm approving the request shown over there from
where I'm already signed in"). The legacy POST /api/v1/auth/otp +
"Get OTP" button are kept under a collapsed details element for any
scripted/CLI flows that still drive the older pattern.
Also wires OPENVIKING_PUBLIC_BASE_URL env var as the highest-priority
public origin override, used consistently by:
- /.well-known/oauth-protected-resource
- WWW-Authenticate header
- authorize page links
- SDK issuer at app start.
Server changes:
- storage.py: oauth_pending_authorizations gains display_code,
verified, verified_account_id/user_id/role columns; new
find_pending_by_display_code + mark_pending_verified.
- provider.authorize() now generates display_code at pending creation
and returns the page URL.
- router.py:
* GET /oauth/authorize/page — renders the code + same-origin quick-
authorize panel (sessionStorage detection, but click still required
so authorization is never silent).
* GET /oauth/authorize/page/status — polled by the page until verified;
response carries the redirect_url with auth_code on approval.
* POST /api/v1/auth/oauth-verify — authenticated; binds caller
identity to a pending row (decision=approve|deny).
Console changes:
- Settings panel: new "Authorize an MCP client" section with code input
and Authorize/Deny buttons. Legacy "Get OTP" still available under
details.
- console proxy gains POST /console/api/v1/ov/auth/oauth-verify.
Tests: 38 OAuth tests passing, including a full device-flow happy path,
deny path, idempotency (one-shot pending), unknown-code rejection,
status-410 on consumed/expired, refresh rotation, OPENVIKING_PUBLIC_BASE_URL
override, and X-Forwarded-* fallback.
* docs(oauth): add 11-oauth guide + Caddy/nginx templates + .env-driven compose
Adds a top-level OAuth 2.1 guide (zh/en) covering the production path
end-to-end. Opens with a 5-step recommended setup so readers don't have
to wade through the rationale before they can deploy. Drops the "MCP"
qualifier from the doc name — OAuth 2.1 here is generic and serves any
OAuth client, not just MCP.
- docs/{en,zh}/guides/11-oauth.md: new. Recommended setup at the top,
then background, full device flow, HTTP-local vs HTTPS-production
deployment, Caddy + nginx templates, docker-compose with the shipped
Caddy service, curl walkthrough, config reference, troubleshooting.
- docker-compose.yml: replace the prior PR's commented-out hint with a
single OPENVIKING_PUBLIC_BASE_URL var (read by both the openviking
service and an optional Caddy reverse-proxy service that's also
shipped commented-out). Same env var drives Caddy via
{$OPENVIKING_PUBLIC_BASE_URL}, so the public domain is configured
once in .env.
- docs/{en,zh}/guides/06-mcp-integration.md: replace the "OAuth Proxy
(planned, use community Cloudflare Worker)" section with a short
pointer to the new 11-oauth guide. The community proxy is still
mentioned as an alternative.
Same env-variable design also matches what the MCP add_resource tool
expects (it already reads OPENVIKING_PUBLIC_BASE_URL), so deployments
get a single source of truth for the public address.
* fix(oauth): read API key from localStorage on authorize page
The same-origin "Quick authorize" panel was reading sessionStorage,
which is per-tab. Since the OAuth authorize page opens in a different
tab from the console, the panel never showed up even when the user was
signed in.
The console persists the API key in localStorage as well (key
"ov_console_api_key" — see static/console_settings.js's
LEGACY_API_KEY_STORAGE_KEY) for cross-tab use, and that copy is what
the authorize page should consult.
Switch the page JS to localStorage first, fall back to sessionStorage
for resilience. No console-side change needed; the localStorage entry
has been written by the console all along.
* docs: add public access guide + default port 1934 aggregated proxy
- Add Caddyfile with :1934 HTTP aggregated proxy (merges 1933+8020)
- Enable Caddy service by default in docker-compose.yml on port 1934
- Add docs/{en,zh}/guides/12-public-access.md with full HTTPS setup guide
- Simplify 11-oauth.md: replace inline reverse proxy config with refs to 12
- Add HTTPS requirement callout to OAuth recommended setup
- Update 03-deployment.md to mention port 1934 as recommended entry point
* fix(oauth): address Copilot review + ruff format
- Update oauth_config.py docstrings to describe opaque tokens, not JWT
(we switched away from JWT during implementation)
- Remove unused authorize_rate_limit_per_min config field — was never
enforced anywhere in router/storage, dead config misled operators
- Wrap all OAuthStore read paths in self._lock (matching writes); the
shared sqlite3.Connection with check_same_thread=False is not safe
for concurrent cursor use across threads
- Clarify provider.exchange_refresh_token comment that replay revokes
the entire (account, user) family, not just the (client, account,
user) chain — broader blast radius is intentional
- ruff format: 8 files reformatted to satisfy CI lint
* perf(docker): add cargo + ccache cache mounts to py-builder stage
The two heavy RUN steps in py-builder (uv sync + maturin build) re-execute
on every Python source change because the upstream COPY layer for openviking/
invalidates the cache. Each rerun was ~510s + ~115s ≈ 10 min of wasted work
even though Rust/C++ source was unchanged.
Add BuildKit cache mounts so cargo and the C++ engine compilation can skip
work whose inputs are unchanged:
- Mount /cargo-target, cargo registry, and cargo git so cargo's incremental
build artifacts persist across layer reruns. Pin CARGO_TARGET_DIR so the
path stays stable when uv builds wheels in ephemeral isolated tempdirs.
- Install ccache and prepend /usr/lib/ccache to PATH so cmake (which calls
shutil.which("gcc")) resolves the ccache wrapper. ccache is path-agnostic,
so it benefits the cmake_build subdir even though setup.py recreates it
in a fresh tempdir each wheel build.
- Mount /root/.ccache so the ccache hash store persists across reruns.
Expected: hot rebuilds on Python-only changes drop step 15 from ~510s to
~60-120s (uv wheel packaging overhead remains; cargo + g++ skip on cache hit).
* perf(docker): drop redundant second maturin build step
The second RUN step in py-builder built ragfs-python a second time and
extracted its .so into the installed openviking package. This was
redundant: setup.py's build_ragfs_python_artifact() already runs maturin
during step 15 (uv sync --no-editable), and because build_meta passes
'bdist_wheel' through PEP 517, _should_require_ragfs_artifact() returns
True and the build fails closed if maturin can't produce ragfs_python.so.
The .so is then bundled into the wheel via package_data and installed
into /app/.venv on wheel install. The second step's only effect was to
overwrite the same file, costing ~115s per build.
Verified after the fact by inspecting the installed venv and importing
ragfs_python in the runtime container.
* feat(oauth): bind OAuth token lifetime to authorizing API key
Previously OAuth tokens lived independently of the API key that authorized
them. Rotating a user's key did not invalidate already-issued OAuth access /
refresh tokens, so a compromised key remained dangerous even after rotation.
Tie every OAuth token to the SHA-256 fingerprint of the API key whose holder
authorized it:
- APIKeyManager grows get_user_key_fingerprint(account_id, user_id) ->
sha256(stored_key_value). The stored value is whatever sits in
user_info["key"] (plaintext key or argon2id hash), written once on
create / regenerate and never mutated in place, so the fp is stable per
key-generation and changes the moment regenerate_key runs.
- OAuth storage gains an authorizing_key_fp column on oauth_codes,
oauth_pending_authorizations (verified_key_fp), oauth_refresh_tokens, and
oauth_access_tokens. ALTER TABLE migration guarded by PRAGMA table_info
for dev DBs that predate the field.
- Provider data classes thread the fp through authorize ->
exchange_authorization_code -> _issue_token_pair, and refresh rotation
preserves it from the consumed token's record.
- Router endpoints capture the caller's current fp at the only two
identity-binding moments: /api/v1/auth/otp (caller) and
/api/v1/auth/oauth-verify (verifier). If the manager returns None
(ROOT key, trusted-mode identity, or removed user), refuse to issue
OAuth state -- there is no key whose lifecycle we could honor.
- auth.py:_try_resolve_oauth_token recomputes the user's current fp on
every OAuth bearer auth and demands strict equality via
hmac.compare_digest. NULL / empty / mismatch all fail closed with a
401 telling the client to re-authorize.
Crypto notes: sha256 over a 256-bit-random API key (or its argon2id hash)
is preimage-safe, so an oauth.db leak does not reveal the API key. No new
secret material introduced; the fp is derived deterministically from data
that already exists.
Tests: 3 new lifecycle tests in test_auth_integration (rotation rejected,
user-removed rejected, missing-fp fail-closed), 3 new router tests
(no-fp caller / verifier rejected, fp recorded on access + refresh), 2 new
APIKeyManager tests (fp changes on rotate / vanishes on remove).
Pre-existing inserts in test_storage updated to pass _FP. 82/82 OAuth +
APIKeyManager tests pass.
* docs(oauth): document OAuth lifetime ≤ authorizing key lifetime
The fingerprint binding landed in the previous commit; users need to know
that key rotation now auto-invalidates derived OAuth tokens (no separate
revoke step) and that ROOT / trusted-mode identities cannot issue OAuth.
Updates both en and zh under docs/guides/11-oauth.md, replacing the
"operator should also revoke ..." paragraph with the new automatic
behavior + brief note on the SHA-256 fingerprint scheme.
* fix(oauth): close 4 review findings on token lifecycle
External security review of #1870 surfaced four real gaps in the OAuth
implementation. All four directly affect the lifecycle / privilege model.
P1: role downgrade did not invalidate OAuth tokens
set_role rewrites user_info["role"] without touching user_info["key"],
so the SHA-256 fingerprint binding stays valid and an ADMIN demoted to
USER continues to resolve as ADMIN. Refresh tokens keep minting fresh
ADMIN access tokens. Fixed in two places:
- auth.py:_try_resolve_oauth_token re-fetches Role.get_user_role and
rejects when the embedded role outranks the current role.
- provider.exchange_refresh_token gets a role_resolver callback (wired
in app.py to api_key_manager.get_user_role) and applies the same
gate before consuming a refresh.
Promotion remains harmless — the embedded lower privilege is still
authorized, only downgrades trigger rejection.
P1: confidential client secrets were never enforced
provider.get_client returned client_secret=None regardless of the
stored hash; the MCP SDK's ClientAuthenticator skips secret validation
when the returned client has a falsy secret, silently allowing
client_secret_basic / client_secret_post clients to authenticate with
only client_id. Real MCP clients all use "none" + PKCE per RFC 8252
§8.4 anyway, so register_client now rejects non-"none" auth methods at
DCR. Native/desktop apps can't keep secrets — PKCE is the actual
proof-of-possession.
P1: OAuth tokens could mint new OAuth grants
/api/v1/auth/otp and /api/v1/auth/oauth-verify accepted any caller
resolved through get_request_context, including identities resolved
from OAuth bearers. A stolen 1h access token could call oauth_verify
with its own pending row and walk away with a 30d refresh-token
chain — privilege time-extension. RequestContext now carries
from_oauth (mirroring ResolvedIdentity.from_oauth) and both endpoints
reject from_oauth=True with 403, forcing primary auth.
P2: GC erased refresh-token replay tombstones
gc_expired deleted "WHERE expires_at < ? OR consumed = 1" every
minute. After GC, is_refresh_known_but_consumed could not distinguish
a replay from an unknown token and exchange_refresh_token never fired
revoke_chain — defeating RFC 9700 §4.14 family revocation for late
replays. GC now keeps consumed refresh rows until their natural
expires_at; storage cost bounded by the 30d max refresh TTL.
Also adds from_oauth field to RequestContext and propagates from
ResolvedIdentity in get_request_context.
Tests: 7 new (role downgrade rejection in bearer auth + refresh path,
role promotion is harmless, confidential DCR rejected, from_oauth
rejected at OTP and oauth-verify, refresh tombstone preserved across
GC). Pre-existing test_oauth_root_can_be_used and
test_dcr_registers_client updated to match the stricter contract.
89/89 OAuth + APIKeyManager tests pass.
* fix(oauth): downgrade confidential DCR to public instead of rejecting
The previous P1.2 fix rejected DCR when token_endpoint_auth_method was
not "none", reasoning that we never enforce client_secret server-side
so accepting confidential auth methods would be a silent security
downgrade. That is the right invariant — but the rejection broke real
clients: the OAuth 2.0 default for token_endpoint_auth_method is
"client_secret_basic", and at least Claude Desktop relies on the SDK to
fill in defaults rather than explicitly setting "none". DCR for those
clients started returning 400 even though they would work fine with PKCE
(which they all use anyway).
Soft-failure design instead: accept any registered auth method, but
overwrite the stored value to "none" and log a warning. The end-state
is identical to the rejection path — every client is treated as
public+PKCE, no secret is ever stored or enforced — but Claude Desktop's
DCR no longer blows up.
Updates the test from asserting 400 to asserting that a confidential
registration is silently downgraded: stored auth_method == "none",
client_secret_hash is None.
* delete(docs): remove error file
* docs(zh): sync 03-deployment.md with English version
|
||
|
|
ac3346422a |
feat(rebuild): add rebuild api scaffold (#1592)
* feat(admin): add rebuild api scaffold
feat: add admin rebuild API
fix: harden admin rebuild execution
feat(cli): add rebuild command support
fix(rebuild): support namespace rebuild routing
refactor(rebuild): unify memory semantic rebuild mode
refactor(rebuild): move http endpoint to content route
fix(rebuild): skip root namespace vectorization
fix(rebuild): harden namespace classification
refactor: rename rebuild api to reindex
refactor: rename reindex executor module
refactor(reindex): remove unused reason field
* fix(reindex): tighten namespace URI handling
Share segment-based Viking URI classification across context inference and reindex execution, add skill namespace support, and require root reindex requests to select an account.
* refactor: reuse indexing pipeline in reindex
* Revert "refactor: reuse indexing pipeline in reindex"
This reverts commit
|
||
|
|
b9a8c4e120 |
feat: path variables and -p for add-resource (#1896)
* feat: 提交1:路径变量系统
feat: Add path variable system with calendar variables
- Implement {calendar:today}, {calendar:ym}, etc.
- Integrate with all URI-handling API endpoints
- Add comprehensive unit tests
- Update documentation
提交2:Rust CLI重构
feat: Rust CLI refactor with progress bar support
- Split HttpClient into BaseClient and FileUploader
- Add dynamic timeout configuration
- Add progress bar for compression/upload
- Configure via --progress flag or config file
* feat: 提交1:路径变量系统
feat: Add path variable system with calendar variables
- Implement {calendar:today}, {calendar:ym}, etc.
- Integrate with all URI-handling API endpoints
- Add comprehensive unit tests
- Update documentation
提交2:Rust CLI重构
feat: Rust CLI refactor with progress bar support
- Split HttpClient into BaseClient and FileUploader
- Add dynamic timeout configuration
- Add progress bar for compression/upload
- Configure via --progress flag or config file
* feat: support --parent, and change default root path for image
* feat: support --parent, and change default root path for image
* docs: fix docs
* fix: review comments
---------
Co-authored-by: openviking <openviking@example.com>
|
||
|
|
4f28f086bd |
feat(server): add shared temp upload mode (#1899)
fix(server): move shared temp uploads to upload namespace refactor(server): flatten shared upload namespace refactor(server): simplify shared upload semantics fix(server): restrict upload scope and add python shared upload mode |
||
|
|
a5649a4d20 |
chore(agent-tools): converge MCP tool names (#1851)
* chore(agent-tools): converge MCP tool names Rename model-visible explicit memory tools without adding a new agent HTTP API or changing existing CLI behavior. Keep OV server /mcp store renamed to remember while preserving its existing session write and commit implementation. Rename Codex MCP openviking_store to remember and keep its original session create/message/commit/cleanup flow; leave OpenClaw memory_store and ov add-memory unchanged. Co-authored-by: GPT-5.5 <noreply@openai.com> * refactor(agent-tools): align MCP and search tool names Apply the search-tool alignment patch across Codex MCP, OpenClaw, OV MCP docs, and focused tests. Co-authored-by: wlff123 <wulf234@163.com> Co-authored-by: GPT-5.5 <noreply@openai.com> --------- Co-authored-by: GPT-5.5 <noreply@openai.com> |
||
|
|
9b3b47a593 |
ci: deploy VitePress docs to TOS (#1897)
* ci: deploy docs to TOS * ci: avoid listing TOS bucket during docs deploy * ci: use virtual-hosted TOS uploads |
||
|
|
acec33bb5f |
fix(server,plugin): readable OV session id + MCP store role_id (#1895)
* refactor(claude-code-plugin): readable OV session id (cc-<uuid>__agent-<id>)
Replace the SHA-256-derived `cc-<hash>` form with a literal embedding of the
CC session_id, so OV/CC ids can be matched by eye instead of via shasum.
Subagent isolation still works by appending `__agent-<agentId>` to the parent
id, preserving lineage in the string itself.
Old `cc-<hash>` sessions are left untouched (no migration); they expire
naturally as users start new CC sessions.
Docs (zh/en) gain a short subsection explaining the format and where to find
the live cc_session_id ↔ ov_session_id pair (~/.openviking/state/last-capture.json).
* fix(server): MCP store now resolves role_id via shared ctx helper
Messages stored through the MCP `store` tool persisted with `role_id=null`
because that path called `Session.add_message` directly, skipping the HTTP
router's `_resolve_message_role_id` fallback (user.user_id for role=user,
user.agent_id for role=assistant).
Lift the resolver onto `RequestContext.resolve_role_id(role, override=None)`
so both call paths share one implementation:
- HTTP `POST /api/v1/sessions/{id}/messages` now calls
`_ctx.resolve_role_id(request.role, request.role_id)`.
- MCP `store` tool calls `ctx.resolve_role_id(msg.role)` per message.
Drop the now-vestigial `http_request: Request` parameter from the HTTP
add_message handler (the local resolver was the only thing using it).
* fix(server): address copilot review on PR #1895
- identity.py: rename `role` → `message_role` in resolve_role_id signature so
it doesn't shadow `RequestContext.role` (the authz role). Also adds blank
line after ToolContext docstring to satisfy ruff format.
- tests/server/test_api_sessions.py: drop now-unused `http_request=...` and
the `auth_mode` / `api_key_manager` plumbing from `_call_add_message_route`
helper and its call sites — these were only needed by the resolver's stale
`http_request` parameter, which the previous commit deleted.
- tests/server/test_mcp_endpoint.py: add regression test asserting MCP `store`
now passes the resolved role_id (user.user_id for user, user.agent_id for
assistant) to Session.add_message. Also fixes a pre-existing import bug
(`list_dir` → `ls`) that was preventing the whole file from being collected.
|
||
|
|
268147d110 |
feat(claude-code-plugin): OpenViking statusline (opt-in) (#1890)
* feat(claude-code-plugin): add OpenViking statusline (opt-in) A one-line OV status renders under the CC input box: server health, last-turn recall stats, pending capture, and queue alerts. Network calls share a 5 s file cache and have a 250 ms hard timeout so the statusline never blocks render. - scripts/statusline.mjs: main entry, ANSI degrade, 80-char cap - scripts/lib/state.mjs: atomic JSON state writer + TTL reader - scripts/lib/server-probe.mjs: cached /health (+ /observer/queue) - auto-recall / auto-capture: write last-recall.json / last-capture.json - setup-helper/install.sh: opt-in prompt; replace-or-skip for existing user statusline; backup + restore-instructions - bump plugin version 0.2.0 -> 0.3.0 * fix(claude-code-plugin): give /observer/queue its own 250ms budget Queue probe was sharing the /health 250 ms budget; on remote servers where /health used 200ms+, the queue probe got ~50ms or was skipped entirely, so queue_healthy flapped between false (when /health was fast) and null (when /health was slow). Result: ⚠ queue badge appeared intermittently even when the queue was consistently unhealthy. Worst-case statusline latency goes from 250ms to 500ms; typical case is unchanged (~150ms) since both endpoints respond in tens of ms when the server is healthy. * fix(claude-code-plugin): loosen statusline timeout to 1s, show archive count - 250ms was too aggressive for remote OV servers; ordinary network jitter (200-400ms /health) was producing spurious "OV ✗ offline" flicker. Bumped to 1s per endpoint. Worst case render is now ~2s but the 5s cache amortises this to once per 5s window per session. - pending_tokens is a sawtooth: it climbs to commit_threshold then snaps to 0 on commit. Showing only "X/20k tok" of a long conversation read as "we only captured X tokens", which hid the work already archived. Now the statusline also shows "N arch" — the running commit_count pulled from the OV session metadata. So a long session now shows e.g. "✎ 573/20k tok · 2 arch" instead of just "✎ 573/20k tok". * fix(claude-code-plugin): drop ⚠ queue badge — false-alarm by design QueueObserver.is_healthy() is derived from QueueManager.has_errors(), which is `any(q._error_count > 0 for q in queues)`. _error_count is a lifetime cumulative counter that never resets, so any server with a single transient embedding failure ever flips is_healthy to false forever — even when the next 1000 jobs all succeed. Real example from a production server: 41 jobs processed, 2 historic errors (95%+ success rate), is_healthy returns false. The badge then appears constantly for users whose OV experience is fine. Removing the badge and the second network round-trip. Connectivity (OV ✓), recall activity (↩ N mem), and capture progress (✎ N/20k arch) already cover whether OV is functioning end-to-end. * feat(claude-code-plugin): four new statusline signals - ↩ N mem (0.92): max recall score appended in parens. Quality hint without an extra segment. auto-recall.mjs now writes top_score in last-recall.json. - ✗ N dropped: turns that auto-capture failed to push this batch. Not sticky — auto-capture overwrites last-capture.json each Stop hook, so transient failures clear themselves on next success. Sustained failures stay visible (which is when the user needs to know). - 🔗 resumed / 🔗 compact: session-start.mjs writes a 1-min TTL event when CC source is resume or compact. Lets the user see that OV did re-hydrate context across restarts instead of having to guess. - +N today: cross-session daily commit_count. auto-capture maintains daily-stats.json (resets on date rollover). Hidden when 0 to keep fresh-day mornings unobtrusive. Distinct from per-session "M arch" which only counts the current CC session. Truncation order verified: server → recall → capture → dropped (alert) → resumed (info) → today (info). 80-char cap drops the lowest-priority tail when the line gets crowded. * fix(claude-code-plugin): drop "tok" unit from statusline size numbers Recall side: `tokens_used` is a chars/4 heuristic (estimateTokens in auto-recall.mjs), not real tokens. For CJK-heavy text the heuristic underestimates by 2-4x, so labelling it "tok" is misleading. Capture side: `pending_tokens` comes from the server, but the server's own counter is also approximate. Mixing the two under the same label invites the wrong mental model. Just drop the unit. The magnitude is meaningful on its own (1.2k = medium injection, 573/20k = 3% of next archive). Configuration field names (recallTokenBudget, commitTokenThreshold) keep "Token" so we don't churn user-facing config. * fix(claude-code-plugin): drop recall size number — heuristic was misleading The "1.2k" between mem count and latency was estimateTokens(text) = ceil(text.length / 4) on the assembled injection block. For CJK-heavy content the heuristic underestimates by 2-4x, which is enough that showing the number does more harm than presenting count + score + latency alone. Capture side keeps "573/20k" because the server reports pending_tokens itself (more accurate, and the ratio against threshold is meaningful even if the absolute count is approximate). * fix(claude-code-plugin): always emit session-event marker on resume/compact Statusline expected `🔗 resumed/compact` to reflect that the event happened, but session-start.mjs only wrote the marker when `formatArchiveContext` had something to inject. Fresh sessions with no prior archive saw a `/compact` silently — statusline showed nothing, leaving the user wondering whether the hook fired at all. Move the writeJsonState call ahead of the no-archive early return and tag the payload with `had_context: false` for the empty case. The badge now fires on every resume/compact event with a 1-minute TTL. `✎` capture pending is unaffected — that segment is gated on `cc_session_id === sessionId` and after `/branch` there's no Stop hook for the new session yet, which is correct (stale capture from a different session would be misleading). * docs(claude-code-plugin): add STATUSLINE.md personalization guide Statusline has more knobs than env vars expose — segment ordering, colors, composing with another statusline, custom segments, state file shapes — and the integration doc is the wrong venue for that level of detail. Add a recipe-style guide aimed at an AI assistant reading it end-to-end, so users can ask Claude Code "personalize my statusline" instead of spelunking source. - examples/claude-code-memory-plugin/docs/STATUSLINE.md: recipes (drop a segment, recolor, compose, reset state, add a custom segment) + state file schemas + pointers to the canonical files. Defers env-var reference back to docs/en/agent-integrations/02-claude-code.md. - install.sh: print a copy-pasteable seed prompt at the end of install. Not intrusive — no auto-launch, just a tip the user can ignore. - docs/{en,zh}/agent-integrations/02-claude-code.md: cross-link the new doc from the Statusline section. * docs(claude-code-plugin): anchor STATUSLINE.md paths to install location Recipes referenced \`scripts/statusline.mjs\` etc. with no anchor, so an agent reading the doc had no way to resolve them — `~/.openviking/openviking-repo/examples/claude-code-memory-plugin/scripts/...` is far enough off the beaten path that "go look in scripts/" doesn't land. Define \`\$REPO\` / \`\$PLUGIN\` / \`\$STATE\` once at the top with how to verify each (jq on settings.json, find as fallback), then propagate the prefixes through every recipe. The install seed prompt already passes the absolute path of STATUSLINE.md, so the chain is now self-contained. * feat(claude-code-plugin): segment glossary + yellow ⚠ slow + dual-purpose install tip Three small refinements after seeing the statusline in the wild: - statusline.mjs: split the unhealthy branch — `OV ⚠ slow` (yellow) when the probe times out, `OV ✗ offline` (red) when it errors. Slow ≠ dead; red was alarmist for transient lag (e.g. remote SaaS GC pauses). - examples/claude-code-memory-plugin/docs/STATUSLINE.md: add "What each segment means" — a full glossary covering every state combination (✓/⚠/✗/⚡, ↩, ✎ in its three forms, dropped, 🔗 resumed/compact, +N today), plus a "missing when?" troubleshooting list. The integration docs only had four example lines, two of which were stale; the canonical reference now lives next to the code. - docs/{en,zh}/agent-integrations/02-claude-code.md: refresh the example block (drop stale `1.2k tok` / `12k/20k tok`, add ⚠ slow + 🔗 resumed + +N today rows), and broaden the cross-link to advertise both explanation and personalization. - install.sh: rewrite the seed prompt as "walk me through what each segment means, then ask if I want to personalize" — covers the more common "what does this badge mean?" path before customization. * docs: link STATUSLINE.md via absolute GitHub URL, not relative path VitePress only ships docs under \`docs/\`, but STATUSLINE.md lives in \`examples/claude-code-memory-plugin/docs/\` (next to the plugin code, where it logically belongs). Relative \`../../examples/...\` resolved on GitHub but 404'd on the published docs site. Use an absolute https://github.com/volcengine/OpenViking/blob/main/... URL — works in both renders, and a parenthetical note tells readers why. * docs: drop the parenthetical about why the link goes to GitHub It was meta — readers don't need to know why the link's absolute. Just click. * docs(claude-code-plugin): move STATUSLINE.md to plugin root A docs/ folder with one file is awkward when README.md and README_CN.md already sit at the plugin root. Moves STATUSLINE.md alongside them and fixes up: - Stale opening line that pointed at the integration doc for the segment glossary — that glossary now lives in STATUSLINE.md itself, so the cross-reference is just for env vars. - Drop the "(path notation defined just below)" parenthetical (meta). - Update the install seed prompt and the en/zh integration cross-links to the new path. * fix(claude-code-plugin): address Copilot review on PR #1890 Code: - state.mjs: derive STATE_DIR from \$OPENVIKING_HOME (with ~ expansion) so the override the docs already advertised actually works. Default unchanged. Was hard-coded to homedir(). - auto-recall.mjs: rename \`session_id\` → \`cc_session_id\` in last-recall.json to match last-capture.json / last-session-event.json. STATUSLINE.md schema already used \`cc_session_id\`. No reader filtered on the recall field, so this is a schema-cleanup, not a behavior change. - install.sh: quote the plugin path inside the JSON \`command\` value, so CC's /bin/sh -c invocation tolerates spaces / metacharacters in \$REPO_DIR (custom OPENVIKING_REPO_DIR locations). - install.sh: mktemp inside ~/.claude/ instead of \$TMPDIR, so the final rename is within one filesystem (atomic). Was crossing tmpfs/$HOME on Linux, where \`mv\` falls back to copy+unlink and isn't crash-safe. Comments / docs (drift from earlier "drop tok / 250→1000ms" passes): - server-probe.mjs: header comment said "Hard 250 ms" while the constant is 1000. Replaced with a forward-reference to the constant block which already explains the choice. - README.md / README_CN.md: refresh the example block (drop \`1.2k tok\` / \`12k/20k tok\`, add \`⚠ slow\` / \`🔗 resumed\` / \`+N today\` rows), correct the hard-timeout sentence (250 ms → 1 s), cross-link STATUSLINE.md. - install.sh: the \`info\` sample at registration time was also stale. * chore(claude-code-plugin): version 0.3.0 → 0.2.1 Statusline is additive and opt-in — no API breaks, no behavior change for existing installs that skip the prompt. A patch bump fits better than a minor. |
||
|
|
43a8a734dc | Delete docs/images/wechat-group-qrcode.jpg (#1864) | ||
|
|
23cfd61570 |
fix(favicon): preserve transparency instead of baking white background (#1881)
The favicons added in #1879 were generated with `-background white -extent` to pad the trimmed logo to a square canvas. The source ov-logo.png is a transparent sRGBA PNG, so this baked a solid white square into the alpha channel. On dark-mode browser tabs the icon showed up as a white block. Regenerate the variants with `-background none` so the padded canvas stays transparent. The visible glyph is unchanged; only the previously white padding is now alpha=0. |
||
|
|
33113eb057 |
feat: serve tight favicon variants for API server and docs site (#1879)
* feat(server): serve favicon and apple-touch-icon at root Browsers and MCP clients (claude.ai, Claude Desktop) auto-fetch /favicon.ico and /apple-touch-icon.png to display a server icon. The 1933 server previously returned JSON 404s for these paths, leaving connectors with a generic placeholder. Add small route handlers that serve OV-branded icons from the existing console/static directory (already shipped as package data). Also wire the console index.html to the new icons. * feat(docs): use tight favicon variants for VitePress site The docs site previously pointed `<link rel="icon">` at the same 1000x1000 ov-logo.png used by the in-page nav, so the tab favicon rendered visibly small inside heavy whitespace. Reuse the trim+square favicon variants generated for the API server (favicon.ico, favicon-32.png, apple-touch-icon.png) under docs/images/ and wire them into the VitePress head config. The in-page nav logo still uses the original PNG (whitespace looks fine in that context). |