* feat(uri): add viking://~ home alias for the caller's user root
Accept `viking://~` (and `viking://~/<suffix>`) as a server-side alias
for the authenticated caller's user namespace root. The alias is
expanded at the request boundary by resolve_current_user_uri for
USER/ADMIN identities, so every control plane that already funnels
through validate_request_viking_uri (REST, MCP, and therefore CLI/SDK
clients) gets it with no client changes.
Design points:
- `~` is a reserved token that can never collide with a real user id
(validate_user_id's charset excludes it), so segment-0 aliasing does
not weaken canonical-first parsing.
- The canonical parser (resolve_uri) rejects the alias outright,
mirroring the legacy-session handling: root-role requests, internal
callers, and storage paths fail closed instead of materializing a
literal '~' directory.
- The alias is accepted but never advertised: scope error copy
("Must be one of: ...") filters it at both the parser and the public
validator, and VikingURI.build refuses to mint it, so responses and
persisted data stay canonical.
- MCP search now resolves exclude_uris with the same strictness as the
REST search router (closes the one entry point that skipped it).
* refactor(mcp): drop tilde mention from tool docstrings
Agents pass viking://~ through verbatim and responses echo the
canonical form, so the alias is self-explanatory on contact; carrying
the sentence in 12 tool descriptions costs every MCP session tokens.
Docs keep the alias documented for humans.
* feat(dsh): serve tools over the shared stdio MCP proxy
Replace the dsh bundle's seven hand-registered `viking_*` tools with the
OpenViking MCP surface, reached through the same stdio proxy every other
memory integration starts, and collapse the four duplicated proxy
entrypoints onto a shared config builder.
The bundle now mounts `@deepseek-ai/dsh-mcp-client` (which ships with dsh
itself) on `servers/mcp-proxy.mjs`. Pointing an MCP SDK client straight at
the server's `/mcp` endpoint does not work: with `stateless_http=True` the
server still answers `GET /mcp` with an idle 200 SSE stream, and once the
SDK client opens that standalone stream it stops resolving POST responses,
so `tools/list` never returns. The stdio proxy owns the transport itself
and is unaffected.
`trimSlash`, `normalizePath`, `uniq`, the watched-credential-path list and
the cfg -> proxyConfig mapping existed in four near-identical copies
(claude-code, codex, opencode, agent-plugins; the last one carried a
"keep in sync with claude-code" comment). They move to
`memory-plugin-shared/lib/mcp-proxy-config.mjs` and all five entrypoints —
including the new dsh one — now shape their config through
`buildMcpProxyConfig`. Behavior is preserved per field, including codex's
explicit `mcpUrl` override, claude-code's `ovcli.conf` credential-source
probe, and opencode's extra watched config file.
The bridge is mounted last in `apply()` so a proxy that fails to start
cannot hold up profile injection, recall, capture, commit, or the URI
guard registrations above it.
* feat(dsh): add to the unified installer and ship the shared skill
The bundle now registers its own isolated `ctx.skills` provider serving the
shared `openviking-memory` skill, so DSH gets the same guidance the Claude
Code, Codex, and Cursor integrations ship. `sync.mjs` distributes the skill
to the bundle, and the provider uses `includeDefaultRoots: false` so it
never shadows DSH's own project/user skill catalog.
`install.sh` grows a `dsh` harness id, auto-detected like the others, plus a
profile prompt that defaults to `web` (`--dsh-profile` / `OPENVIKING_DSH_PROFILE`
answer it up front). The installer always installs the published package:
`dsh plugin` forwards to pnpm, and a linked source tree cannot resolve the
dsh peers the bundle imports because Node resolves them from the checkout's
realpath rather than from the profile.
Documentation is restructured around installing rather than internals. The
integration page now leads with the one-line installer and keeps behavior at
the level the other harness pages use, with configuration in a details block;
design rationale moves to the bundle README, which itself leads with Install
and groups the rationale under "Design notes". Capability-reference claims
that dsh is outside the unified installer are corrected.
* chore(dsh): release 0.2.0
The MCP tool surface, the stdio proxy transport, and the bundled skill all
change what the bundle does for an existing user, so this is a minor bump
rather than a patch. 0.1.0 remains the native-`viking_*` tool surface.
* docs(dsh): note pnpm's 24h minimum release age
pnpm 11 refuses releases younger than minimumReleaseAge (24 hours by
default), and surfaces it as a registry 404, so installing a freshly
published version reads as "the package does not exist".
* fix(dsh): honour dev source mode in the installer
install_dsh ignored SOURCE_MODE and always fetched the published package,
so selecting "current checkout" installed npm's build instead of the
working tree and validation still reported success.
npm is the bundle's only distribution channel, so the github/tos choice
does not apply to it: every mode except dev now installs the published
package, and dev packs the checkout with npm pack first. It has to arrive
as a real package rather than a link, because a linked source tree
resolves its dsh peers from its own realpath and misses the profile's
hoisted node_modules. The install line reports which source was used.
* fix(dsh): make repeated installs actually overwrite
Two ways a re-run silently kept stale code:
pnpm treats an already-satisfied version as a no-op regardless of which
tarball the file: dependency points at, so a dev re-install after editing
the checkout left the previous build in place. Local installs now drop the
package before adding it back; that is confined to local sources, since
doing it for the registry path would leave nothing installed when add
fails.
A bare package name has the same effect in reverse: a profile holding a
dev build satisfies it, so switching back to the published package was a
no-op. The registry path now asks for @latest.
The packed tarball is named after a fingerprint of the checkout's shipped
files, so an unchanged checkout skips the pack and keeps a stable path in
the profile lockfile.
* fix(feishu): support legacy doc imports
Keep legacy Feishu doc resources on the doc API path instead of routing doccn tokens through docx blocks.
* test(feishu): trim legacy doc coverage
Keep legacy doc import coverage focused on public Feishu accessor behavior and remove extra mock-level assertions.
viking://user is a protected namespace root in _ensure_supported_delete_namespace,
so viking_fs.rm("viking://user/", recursive=True) was silently rejected with
PermissionDeniedError. The exception was swallowed by try/except, leaving
account metadata deleted from accounts.json but all filesystem data intact.
Use _async_agfs.rm("/local/{account_id}", recursive=True) directly to bypass
the namespace guard. This also now correctly deletes _system/users.json which
was previously missed (not under viking://user/ or viking://resources/).
The public MountableFS constructors do not initialize a pathlock manager, but
multi-write mount and raw copy used expect() on the missing manager, so calling
either fast path on such an instance panicked.
Return Error::Config with a clear message instead; tests that need the success
path already build the manager via with_test_pathlock_manager().
* fix(sdk): expose tree level limit
Why:
- Align all HTTP SDKs with the documented filesystem tree contract and make the bundled Python example valid.
- Let callers control traversal depth without bypassing the SDK clients.
What:
- Expose language-idiomatic tree depth options in Python, TypeScript, and Go.
- Forward level_limit while preserving the default depth of 3 and an explicit depth of 0.
- Add request-contract tests for Python async/sync clients, TypeScript, and Go.
Risk:
- Adding a field to exported Go TreeOptions can affect external unkeyed composite literals; keyed literals, nil options, and zero-value options remain compatible.
- Server behavior is unchanged.
Tests:
- Python: 111 passed, 1 known baseline test deselected; focused tree tests, Ruff check, and Ruff format check passed.
- TypeScript: npm test -- --run; npm run typecheck; npm run build; npm run test:node-types; npm pack --dry-run.
- Go: go vet ./...; go test ./... -count=1; go test -race ./... -count=1.
Live Docs:
- Not applicable; the existing English and Chinese filesystem API references already define level_limit.
* test(sdk): fold tree level limit cases into existing fs option tests
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
Co-authored-by: Claude <noreply@anthropic.com>
- VolcEngine selection now opens an access-tier submenu: Agent Plan
(api/plan/v3), Coding Plan (api/coding/v3), or pay-as-you-go API, with
doubao-seed-2.0-lite / doubao-embedding-vision defaults for the plans
- BytePlus gains the same submenu with ModelArk Coding Plan
(api/coding/v3, dola-seed-2.0-lite / skylark-embedding-vision) and
pay-as-you-go
- Embedding 'Other (manual)' becomes a Custom/manual submenu offering
interactive OpenAI-compatible prompts (URL, key, model, dimension)
alongside the editor hand-off
- Custom VLM/embedding prompts accept !back to return to the provider
menu; plan submenus support back navigation and seed defaults from the
existing config endpoint (unknown volces.com/bytepluses.com endpoints
are offered as a keep-current option)
- Two-step flow seeds the VLM plan menu from the embedding tier choice;
summaries now show the api_base so tiers are distinguishable
- Fix provider re-detection for BytePlus plan endpoints (domain-based,
since VolcEngine and BytePlus share the volcengine provider string)
- Document the plan endpoints in ov.conf.example
* perf(resource): make wait=false ingestion durable
Move source preparation and parser work behind the durable AddResource task
boundary, while keeping task-owned credentials private and short-lived.
* fix(feishu): preflight add-resource roots
* test(fs): align tree rel_path fakes with binding
* docs: fix integration docs and comments that contradict the code
- codex: credential resolution in the default `auto` mode is env-first — `credentials.mjs`
only falls back to `ovcli.conf` when no credential env var is set, while the docs and the
`config.mjs` header comment claimed `ovcli.conf` wins by default. Also document
`OPENVIKING_CREDENTIAL_SOURCE=cli`, which was undocumented.
- codex: the four hook scripts send the key as `X-API-Key` in addition to
`Authorization: Bearer`; the README documented Bearer only.
- claude-code: the OV session id is `cc-<cc_session_id>` verbatim (`deriveHarnessSessionId`
does no hashing), not `cc-<sha256(cc_session_id)>`.
- claude-code: `hooks.json` registers 9 hooks, not 7 — the responsibilities table was
missing the `PreToolUse` `viking://` guard and the `PostToolUse` skill-experience hook.
- claude-code: archival is triggered client-side (the `Stop` hook commits once
server-reported pending tokens cross `commitTokenThreshold`, default 20000, plus
unconditional commits from `PreCompact` / `SessionEnd` / `SubagentStop`). The README
attributed it to a server-side `auto_commit_threshold`, but
`memory.session_auto_commit.default_enabled` is false and no plugin sends a policy.
- trae / opencode: the MCP proxy transparently exposes the full server tool set (16 tools);
the docs listed a 4-item sample or 11-13 tools and omitted `tree` / `write` / `edit`.
- trae-cli: the installer registers the MCP server as `openviking-memory`, but the verify
step told users to look for `openviking`.
- pi: the manual install block omitted the `pi install <dest>` registration step that the
one-click installer runs, so a hand-copied extension is never registered.
- install.sh: `--uninstall` handles cursor, trae, trae-cn, trae-cli and zcode; the `--help`
text still said Cursor/TRAE only.
- mcp_endpoint.py: the module docstring enumerated 13 tools and omitted `recall`,
`list_watches` and `cancel_watch`; replaced the stale enumeration with a pointer to the
`@mcp.tool` registrations.
* docs: add a cross-integration capability reference page
The agent-integrations section had per-integration install guides but no place
to compare integrations against each other. This adds one bilingual page that
does that, and wires it into the existing pages in both directions.
- New page `docs/{en,zh}/agent-integrations/16-capability-reference.md`: a
dimension-first comparison of every OpenViking integration — active tool
surface, automatic hook surface, install/credential/config layering, recall
and injection, session and commit lifecycle (including a shutdown-path x
harness end-state matrix), compaction takeover, write/delete boundaries,
degradation, and a per-harness profile card for each integration.
- Sidebar: `StructuredSidebarCopy` gains an optional `topItems` field so a
section can list flat entries next to its overview; agent-integrations uses
it to place the new page beside the overview. Other sections are unaffected.
- Links both ways: the overview and all 14 per-integration pages link to the
reference, and the reference links back to each integration page from its
profile card, from the non-coding integration table, and from the custom
agent integration paths. Section cross-references (§x.x) are real in-page
anchor links, generated from the built heading ids.
- trae-cli is documented as TraeCode CLI 2.0 only, installed through a codex
plugin alias; 1.0 and its standalone plugin are called out as unsupported.
- The MCP tool surface is described as 15 tools throughout, matching the
removal of the `recall` tool in favour of `search` with `mode="context"`.
Pages outside this change that still mention an MCP `recall` tool
(04-codex, 12-cursor, 15-agent-plugins, guides/06-mcp-integration) need a
follow-up sweep once that removal lands.
* docs: 更新服务端 MCP 工具面描述,简化信息并明确更新方式
* docs(hermes): recommend memory setup openviking
Point Hermes docs at `hermes memory setup openviking` and describe the
real wizard. Shorten Volcengine console agent guides to TOS + cloud API
key, and mark docs/images as console-only.
* docs(hermes): drop setup filler
Keep the command and the two connection paths. Remove picker
explanations and wizard narration.
* docs: keep images AGENTS.md.local local-only
Ignore AGENTS.md.local like AGENTS.md. Drop the unused
docs/images/README.md.
* docs(console): keep harness setup to command plus API key
Drop installer narration, idempotency notes, and copied site
guides. Console pages only need the TOS command and cloud key.
* docs(console): restore Install / Verify / Troubleshoot
Keep the short cloud setup, put it back under the three section
headings the console pages use.
* docs(console): add Reference links
Point each harness page at docs.openviking.net, the coding-agent
blog where it exists, and the example source.
* docs(console): label Reference as manual settings and blog
Use Docs on Manual Settings for the full site page. Use Blog
about how it works where a how-it-works writeup exists.
The official npm bin (ov.mjs) starts with #!/usr/bin/env node. The Python
entry-point fallback skipped every shebang file on PATH, so the Node wrapper
was never exec'd and users got 'ov binary not found'.
Only compare against and skip the current Python entry point itself; other
PATH executables (including the Node shebang wrapper) are exec'd normally, and
exec failures are no longer swallowed.
* chore: remove dead git tuning knobs and duplicate release/frontend files
- GitTuningConfig: drop upload_concurrency, restore_concurrency,
ref_cas_max_retry, and ref_cas_backoff_ms, which were parsed but never read
anywhere (verified no source readers). Keep commit_index_enabled and
blob_exists_precheck_enabled, the two knobs that actually take effect.
- Design doc: mark the removed knobs as roadmap items to be added back when
the behavior lands, and correct stale 'not implemented' claims
(validate_account_id is enforced at the Git service entry; blob reads are
limited via show_with_limit).
- Remove .github/workflows/release-vikingbot-first.yml: a historical one-off
PyPI release workflow whose bot/ package lacks build metadata.
- web-studio: delete pnpm-lock.yaml and the pnpm-only package.json block;
the Makefile and CI already use npm + package-lock.json as the single
install chain. Net -9,695 lines.
* docs: align cleanup notes with current behavior
---------
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
- Root pytest config excludes the self-contained api_test/oc2ov_test E2E
subprojects so the root collection no longer descends into them.
- Gemini E2E module skips via importorskip when google.genai is absent,
instead of failing at import time before the skip check.
- Session lifecycle tests reference the existing partial-based client
fixture instead of the removed AsyncOpenViking API.
- The remote-server ensure_resources_dir fixture is no longer session-wide
autouse; it is injected only into cli_remote-marked tests, and the
cli_remote marker is registered in the root config.
* chore(web-studio): remove dead code (unreachable files and unused exports)
Static sweep of web-studio/src for code no module reaches from the
main.tsx / routeTree.gen.ts entry graph, plus exported symbols nothing
references anywhere (including their own file and tests).
Deleted files:
- src/router.tsx — superseded by the inline createRouter in main.tsx
- src/lib/sessions/generate-title.ts — never called
- src/lib/sessions/types/session.ts — re-export barrel nobody imports
- src/components/ui/{breadcrumb,combobox,context-menu,input-group,progress}.tsx
— shadcn primitives never added to any screen; re-addable via `shadcn add`
Removed unused exports (and the imports/constants they were the last
consumer of):
- lib/admin-options.ts: sortedAccounts
- lib/sessions/types/chat.ts: ChatState
- lib/sessions/types/message.ts: getTextContent, getToolParts, getContextParts
- routes/home/-lib/format.ts: formatDateKey, formatTimestamp
- routes/playground/-lib/types.ts: VikingEntryHandler
- routes/playground/-lib/utils.ts: buildBreadcrumbs
- routes/resources/-hooks/viking-fm.ts: usePrefetchVikingFsList, useVikingFind
- routes/resources/-lib/normalize.ts: sameUri, normalizeUriForDisplay
- routes/resources/-lib/upload.ts: getExtensionFromName
No behaviour change. `vite build` succeeds, `tsc --noEmit` output is
byte-identical to main, and `vitest run` shows the same pre-existing
19 failed files / 56 failed tests as main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(web-studio): drop the unused pnpm lockfile
Nothing in the repo installs web-studio with pnpm: `make build-studio`
runs `npm ci && npm run build`, and both setup-node steps in
`.github/workflows/_build.yml` cache on `web-studio/package-lock.json`.
No workflow reads `pnpm-lock.yaml`.
The file had also drifted out of sync with package.json — 24 specifiers
missing — so `pnpm install --frozen-lockfile` failed outright, which only
ever hurt someone reaching for pnpm locally.
Also drops the `pnpm.onlyBuiltDependencies` field: pnpm 11 no longer
reads it and warns when it is present.
package-lock.json stays as the single source of truth.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
- Add rehype-raw and rehype-sanitize so raw HTML in Markdown renders while stripping scripts and other dangerous tags
- Give table cells full borders, center content both axes, and pass through colSpan / rowSpan
- Add file-preview-html tests covering raw HTML rendering, table attribute passthrough, and script stripping
* fix(plugin): report server recall top score
Derive the status snapshot's top score from server-assembled context so /ov no longer reports 0.00 for scored recalls.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
* test(plugin): remove redundant recall state tests
* fix(plugin): remove misleading recall top score
---------
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>