Repository navigation
Conversation
* docs(cli): clarify snapshot commit path requirements * docs(cli): scope snapshot commit help examples --------- Co-authored-by: r266-tech <r266-tech@users.noreply.github.com>
* feat(reindex): adopt RFV durable planning Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(reindex): transfer root lease ownership Co-authored-by: TRAE CLI <traecli@bytedance.com> * feat(reindex): configure root queue concurrency Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(reindex): preserve request context on replay Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(reindex): close RFV planner review gaps Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(reindex): restore telemetry in durable worker Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(reindex): reuse overview summaries for missing vectors Co-authored-by: TRAE CLI <traecli@bytedance.com> * perf(reindex): cache overview summary parsing Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(reindex): reuse parent overview for file rebuilds Co-authored-by: TRAE CLI <traecli@bytedance.com> * test(sdk): update reindex client contract Co-authored-by: TRAE CLI <traecli@bytedance.com> --------- Co-authored-by: TRAE CLI <traecli@bytedance.com>
…volcengine#5407) Co-authored-by: r266-tech <r266-tech@users.noreply.github.com>
* docs(hermes): align setup and capability guidance * docs(hermes-plugin): prepare README for catalog publication * docs(hermes-plugin): keep release compatibility detail in review notes * docs: keep Hermes plugin README focused on users
…engine#5157) * feat(agent-plugins): add ov-tasks skill for structured task handoff in OpenViking One task = one markdown file under viking://agent/tasks/<scope>/ (falls back to viking://resources/tasks on servers that reject agent writes): frontmatter header + Goal/Context/Decisions/Progress/Next/Questions/Log. Any agent with the ov CLI can claim, run one slice, verify, write back, and hand off; references/loop.sh drives unattended ticks. Design notes, dropped PowerContext/LoopX concepts, and the herdr rehearsal are in docs/design/ov-tasks.md. * docs(ov-tasks): write the task body in the task's main language
* refactor(retrieval): 改为全局召回并统一候选精排 移除逐层检索、递归 rerank 和父级分数传播。 仅在 THINKING 且有可用 rerank 客户端时召回 3 × limit 候选并精排一次。 同步现有测试、中英文文档和配置示例,保留原文件名与类名。 * refactor(retrieval): 将精排候选池缩小至两倍 limit
* fix(dsh): forward MCP diagnostic settings Signed-off-by: Hao Zhe <haozhe4547@gmail.com> * chore(dsh): bump memory plugin version Signed-off-by: Hao Zhe <haozhe4547@gmail.com> * docs(dsh): clarify MCP proxy diagnostic settings Signed-off-by: Hao Zhe <haozhe4547@gmail.com> --------- Signed-off-by: Hao Zhe <haozhe4547@gmail.com>
…ne#5449) * fix(openviking): keep a .env file's own line endings when writing one variable _write_env_vars round-tripped through text mode, so on Windows "\n".join came back out as CRLF and an LF .env had every untouched line rewritten -- the exact corruption the surrogateescape read was added to prevent. The universal-newline read hid it too: CRLF was folded to LF before anything could observe the file's real ending. Read and write with newline="" and adopt the ending the file already uses, so an LF file stays LF and a CRLF file stays CRLF on every platform. test_non_utf8_env_preserves_unrelated_bytes failed on Windows for this reason; it passes now, with LF and CRLF cases pinned explicitly. (cherry picked from commit ea977a9a757b4bf8daa170cba2be2d2587cdfd70) * test(hermes-plugin): cover credential and setup line endings * test(hermes-plugin): limit Unix permission checks to POSIX --------- Co-authored-by: anhtahaylove <everest.kill1@gmail.com>
直接使用向量或 rerank 分数,删除热度混合和无用配置传参。 同步配置示例、现有测试及父目录继承分的遗留文档。
Co-authored-by: r266-tech <r266-tech@users.noreply.github.com>
…ine#5444) DSH 0.1.7 removed `agent/session-start`; per-agent startup now runs from the serial `agent/created` event with `{ agent, source, signal? }`. The plugin only listened on the old name, so on 0.1.7+ startup initialize, early profile injection and the per-session disposal commit never ran. Start the agent from `agent/created` when the payload carries `source` (0.1.7+), and keep `agent/session-start` for 0.1.0-rc.6 / 0.1.5, whose `agent/created` is composition-only and has no `source`. Startup runs once per agent, resolves to undefined so it never bails the serial dispatch, returns on abort, and swallows failures instead of failing agent creation. Bump to 0.5.9. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ls): 支持返回目录摘要与概览 为 ls 补充 L0/L1 内容控制参数,并同步 CLI、Go/TypeScript SDK、文档与测试。 * feat(go-sdk): 支持获取列表分页状态 新增 ListPage 和 TreePage 接口以返回 has_more,并保持原有 List 和 Tree 接口兼容。 * feat(sdk): 对齐各语言 ls 和 tree 能力 补齐 Python 和 TypeScript 的分页状态支持,并统一 L0/L1、目录过滤及扩展字段参数。 * test: 精简 ls/tree 与 SDK 分页测试 --------- Co-authored-by: zhanghe.1216 <@bytedance.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…nto archive (volcengine#5451) * feat(agent-plugins): rename ov-tasks to ov-kanban, fold task detail into archive Follow-up to volcengine#5157. - Rename the skill to ov-kanban. A board holds many tasks; the old name collided with `ov task` (server processing tasks). Root moves to viking://agent/kanban (fallback viking://resources/kanban), `<scope>` becomes `<board>`, env vars become OV_KANBAN_*. - Fold instead of compress: text leaves a task file only by moving to archive/<id>.md verbatim (old Log lines, replaced Next/Decisions, long evidence). Agents read in layers: boards, board view, task, archive. - Board view: one `ov grep` returns title/status/owner/updated per task. - Add the exit rule for `cancelled`; pin the `updated` format. - loop.sh: run under bash (3.2+) and zsh, quote cwd, remove the prompt temp file after each tick. - Add the skill README with the idea and roadmap (task DAG, scoped kanban, recall on session start, claim-aware loop, atomic claim, ov compile). * docs(ov-kanban): link the skill README by GitHub URL The relative link left the docs site, so check-built-anchors.py could not resolve its #roadmap fragment.
…olcengine#5457) Record constraints and workarounds found while deploying v0.3.0 on EKS from the package and these guides alone: one delivery per cluster and the cluster-wide license webhook, online license activation, init images missing from vikinglist, crane import of gzip archives, vdb.yaml fields init config leaves out or gets wrong (hdfs ConfigMap, oneAgent, tbase tolerations, sizing), the HDFS root write permission behind indexes stuck in INIT, and the root_api_key field needed by recent ov CLI.
Co-authored-by: r266-tech <r266-tech@users.noreply.github.com>
* fix(cli): accept oidc_token and trusted auth_mode in ovcli.conf OVCLIConfig says it accepts every field the Rust CLI can write, but it rejected two ovcli.conf settings that the Rust CLI and the Python SDK both accept: the `oidc_token` field and `auth_mode: "trusted"` (the trusted-mode client example in the authentication guide). Either one made load_ovcli_config raise, so `ov doctor`'s VikingBot check reported an ovcli.conf api_key as not configured. * fix(cli): limit shared config fix to OIDC token Remove the trusted auth mode extension and its test case so this PR only accepts the existing OIDC token field. * test(cli): remove added OIDC config test --------- Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…olcengine#5427) When a Markdown document is large enough to be split, _build_structure took the pre-heading virtual section from content before the first heading of any level, but only added sections for headings at the minimum level. Text under lower-level headings that appear before the first top-level heading (e.g. a "## Preface" ahead of "# Chapter 1") belonged to no section and was silently dropped from the imported resource. Start the leading virtual section at the first top-level heading instead, so that text is kept in the document-named section.
Co-authored-by: r266-tech <r266-tech@users.noreply.github.com>
…ine#5426) When a Jina dense config omits `dimension`, EmbeddingModelConfig.get_effective_dimension() fell through to the generic 2048 fallback, while JinaDenseEmbedder returns the model's native size (1024 for jina-embeddings-v5-text-small, 768 for -nano). The vector collection was therefore created with a dimension that does not match the vectors the embedder writes. Resolve the Jina default through the same model table the embedder uses, in both get_effective_dimension() and the credential cross-check.
…olcengine#5435) format_table_to_markdown, used for Feishu docx tables, embedded sheets, spreadsheets and bitables, wrote cell text into the table verbatim. A cell containing "|" added a column, and a multi-line cell ended the row early, so the rest of the table was misaligned or not a table at all. A column that is empty in every row also got an empty delimiter cell, which Markdown renderers do not accept, so the whole table fell back to text. Escape "|" as "\|", join cell lines with "<br>" (as the AnyDoc table renderer does), and give every column at least a "---" delimiter.
* feat(retrieval): add event time-decay ranking Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): preserve time-decay compatibility Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): simplify event time-decay ranking Co-authored-by: TRAE CLI <traecli@bytedance.com> * chore: keep main formatting after merge Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): keep time decay out of reindex Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): expose time decay score details Co-authored-by: TRAE CLI <traecli@bytedance.com> * refactor(retrieval): simplify event time decay Co-authored-by: TRAE CLI <traecli@bytedance.com> * feat(retrieval): apply event time decay in cloud and find Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): simplify cloud time-decay routing Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): recall event memories by indexed type tags * fix(retrieval): use nullable protection for multiplicative event decay * test(memory): cover extracted peer event updates * fix(search): align event time decay behavior Co-authored-by: TRAE CLI <traecli@bytedance.com> * docs(retrieval): document find decay protection Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): finalize event time decay contract Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): align event decay with tag routing contract Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(retrieval): align extracted tags and account decay routing Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix: rank bounded time-decay candidates inside vector engines Co-authored-by: TRAE CLI <traecli@bytedance.com> * refactor(retrieval): drop unused event URI classifiers * fix(retrieval): remove stale hotness test and align decay contracts * fix(retrieval): run deferred decay fusion in native engine * fix(retrieval): apply event decay during vector recall * refactor(retrieval): remove redundant decay plumbing and tests * test(retrieval): consolidate event time-decay coverage * refactor(storage): remove redundant search result processing * refactor(retrieval): leave openGauss decay options unprocessed --------- Signed-off-by: lusendong.6789 <lusendong.6789@bytedance.com> Co-authored-by: TRAE CLI <traecli@bytedance.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
`/viking commit` only showed "OpenViking: commit failed". Takeover reports failure for several different outcomes -- a server `skipped` result, an HTTP error, an undelivered queue, a capture gap, an archive that failed without a Working Memory summary -- and every reason stayed in the debug log. On 0.4.22 a user hit the most common one: the server answered 200 `skipped: all_within_keep_window` because it had already archived everything older than pi's last three user turns (e.g. an earlier archive whose summary failed and was dropped, a native-compaction archive with keep 0, or a 0.3.x commit that kept 3 messages). Nothing was wrong, but pi said "failed". TakeoverCore now records `lastFailure` on every path that returns false, the sync layer keeps the HTTP status and server message of a failed commit, and `/viking commit` shows the reason (a warning, not an error, when there is simply nothing new to archive). Without takeover, a `skipped` result is no longer reported as "committed successfully". Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…anging, add the bootstrap (volcengine#5464) * ci(release): publish a Claude Code URL marketplace to TOS Each v* release now uploads the Claude Code plugin alone as an immutable releases/<tag>/openviking-memory-claude.zip, plus a marketplace.json whose only entry is an archive source (url + sha256) pointing at that zip, with the entry version taken from the plugin manifest. The manifest is kept under releases/<tag>/ and copied, no-cache, to plugins/claude/marketplace.json. Claude Code 2.1.224+ adds such a URL marketplace and installs and updates the plugin over plain HTTPS, with no git clone. The existing memory-plugin-marketplace.zip keeps publishing for the other harnesses. * feat(installer): auto-updating TOS channel for Claude Code 2.1.224+ With --dist tos, a native claude at 2.1.224 or later (the first release with the archive plugin source) now registers the TOS URL marketplace (plugins/claude/marketplace.json) instead of the unpacked archive directory, and sets extraKnownMarketplaces.openviking.autoUpdate in ~/.claude/settings.json, next to the declaration `marketplace add` writes. Claude Code leaves auto-update off for third-party marketplaces; an explicit false set by the user is kept on re-runs. An existing registration with another source (local directory or the GitHub git-subdir manifest) is re-registered through the existing claude_marketplace_sync. If the URL marketplace cannot be added, and on older or custom Claude-format CLIs, the installer keeps the local directory path it used before. * fix(claude-plugin): recognize URL marketplaces in the memory doctor Claude Code caches a url-sourced marketplace as a bare manifest file, so the doctor's installLocation directory check reported the TOS URL marketplace as a missing directory. Report it as registered instead, with its effective auto-update setting (the user-settings declaration first, since known_marketplaces.json only syncs on the next startup), and update the doctor skill's notes on TOS updates. Bump the plugin to 0.6.6. * chore(installer): add a version stamp for release builds * feat(installer): install every harness from the release bundle The installer used to clone the repository for anything that needed plugin files on disk (hook hosts, pi, the OpenCode MCP proxy, the statusline, legacy Claude Code). The clone ran inside command substitutions, so its state was lost and every later caller fetched again. Every file-based harness now reads the plugin bundle (memory-plugin-marketplace.zip), downloaded at most once per run by ensure_bundle at top level and only when a step needs it; dev mode uses the checkout's examples/ directory. The clone, the source-zip fallback (OPENVIKING_REPO_ARCHIVE_URL) and OPENVIKING_REPO_DIR are gone. The installer's own helpers now prefer the bundle over the copy an earlier install left under agent-integrations, which may be older. OpenCode always gets the file plugin from the bundle. The npm registration and the MCP proxy snapshot are removed, and jsonc-edit drops an existing @openviking/opencode-plugin entry so the plugin does not load twice. The statusline command now looks up the installed plugin's path in installed_plugins.json on every run, so it follows Claude Code plugin updates and needs no bundle. A statusline an earlier installer registered is repointed without a prompt; a foreign one still asks. The memory doctor and STATUSLINE.md describe the new command. Bump the plugins the shared library change reaches: codex 0.10.7, agent-hook 0.5.5, opencode 0.5.3, dsh 0.5.9, pi 0.4.7. * feat(installer): install from the TOS release channel only The installer no longer offers a GitHub channel. Claude Code 2.1.224+ uses the TOS URL marketplace, Codex-format CLIs the TOS git marketplace, and the rest (and the fallbacks) the release bundle; a checkout still installs itself in dev mode. The channel menu, the generated git-subdir manifest for Claude Code and the GitHub git marketplace for Codex are gone. --dist github, --source remote and OPENVIKING_REPO_URL / _REF / _BRANCH are still accepted: the installer says once that they are ignored and carries on. --dist tos is accepted as a no-op. A re-run moves Claude and Codex registrations that point at GitHub to TOS through the existing sync helpers, and never deletes the old directory marketplace, which other Claude-format CLIs may still use. When ~/.openviking/openviking-repo is no longer referenced from ~/.claude/settings.json, the run ends by saying it can be deleted. A copy of install.sh that is neither a release build nor in a checkout (for example one fetched from raw.githubusercontent.com) now downloads the release installer, checks its marker line and runs it with --dist tos and the original arguments, so the script and the bundle come from one release. If that fails it continues with itself. OPENVIKING_INSTALLER_REEXEC=0 turns this off. Codex's TOS path is folded into codex_marketplace_sync with the directory fallback, and codex_unregister replaces the repeated remove/uninstall/ marketplace-remove triple. * feat(installer): detect the UI language instead of asking The language menu is gone. The language comes from --lang, then OPENVIKING_LANG, then LC_ALL / LANG. When those are unset or C/POSIX on macOS, which is common in terminals started from the GUI, the system language is read from `defaults read -g AppleLocale`. Anything else is English. With no menu to wait for, the language is settled before the release re-exec, so its one message no longer needs its own lookup. * feat(installer): check the server and confirm before changing anything The credentials step used to write ovcli.conf as soon as it had an answer, so a run abandoned at any later prompt had already changed the machine. Now the run gathers the connection, checks it, shows what it will do and asks, and only then writes ovcli.conf and installs. - A first-time setup offers the local server first; the keep/reconfigure menu for an existing config is unchanged. - The check asks the server for /health, then /api/v1/system/status with the key and the account/user headers, and tells an unreachable server, a rejected or missing key and any other HTTP status apart. Headers go to curl on stdin, so the key never appears in the process list. On a problem an interactive run offers to re-enter the URL and key or to continue; an unreachable server defaults to continue, since a local server is often not started yet. Without curl the check is skipped. - The review lists the harnesses and commands, the server, the masked key and, per harness, the files and commands the install will touch. Cancelling exits 0 with nothing written. --yes and runs without a tty print it and go on. - A run without a tty that got no --url, no --api-key and found no config says after the review that it configured http://127.0.0.1:1933 without a key, with a command to re-run against another server. * feat(installer): number the steps and end with what to do next Headings read [n/N], with N counted from this run: credentials, review, one step per install (each Claude- or Codex-format CLI counts once) and validation. TRAE and TRAE CN get a heading of their own. The environment check prints only errors and warnings. The closing section lists, per installed harness, what to do next (restart or /reload), how updates arrive (automatic for Claude Code on the URL marketplace and for Codex-format CLIs on the TOS git marketplace, a re-run of the installer otherwise), a verify command where one exists, and how to uninstall. * feat(installer): pin the release through the install-site resolver After the confirmation step, a default-channel install asks $OPENVIKING_INSTALL_SITE/install/v1/<harness>.json which release to install, one parallel request per selected harness under its public name (a TraeCode CLI alias counts as trae-cli). An answer is used only when it is schema 1 and every URL points into the TOS bucket: the bundle comes from its bundle_url and must match its sha256, and Codex registers its git_url unless OPENVIKING_CODEX_TOS_GIT_URL is set. Anything else, including a timeout, keeps the built-in latest-release URLs without output. DO_NOT_TRACK, dev mode and --uninstall skip the request, and OPENVIKING_MARKETPLACE_ARCHIVE_URL replaces the bundle URL and its check. The final summary shows the resolved plugin version. Tests run the installer against a local resolver; every other installer test sets DO_NOT_TRACK so none reaches the live site. * feat(installer): skip Claude Code without the plugin command Claude Code older than 2.0 has no 'claude plugin'. The installer used to wire such builds up with 'claude mcp add' plus a hooks merge into settings.json, which needed the release bundle and never updated. It now skips them with a warning to upgrade Claude Code; the plugin README keeps the manual steps. * refactor(installer): validate the hook hosts through one function Cursor, TRAE, TRAE CN and ZCode run the same hook runtime and were validated by four near-identical blocks that differed only in paths and the session-start event name. One function now takes the host and keeps every check: the hooks and MCP entries, the installed files, node --check and the hook smoke test. The TraeCode CLI block is gone: trae-cli is installed as a Codex-format plugin, so the harness never reaches validation. * docs(installer): describe what the installer does, contacts and writes The header of install.sh now states, for people and agents reading it before a run: what it installs, the hosts it contacts, the files it writes per harness, that it needs no sudo and stays inside $HOME, the non-interactive form, how to uninstall, how to compare a release copy with the repository, and what the release lookup sends and how DO_NOT_TRACK skips it. usage() lists the environment variables and the retired --dist values; the shared README gains an Installer section and the capability reference drops the GitHub channel. * feat(installer): add the bootstrap served at openviking.net/install The bootstrap downloads the installer from the TOS bucket, refuses anything that is not the installer script, and runs it with --dist tos, the optional per-harness --harness value and the caller's arguments, reading input from /dev/tty when there is one so `curl ... | bash` can still prompt. * ci(release): stamp the release version into the published installer The uploaded copies of memory-plugin-shared/install.sh now carry INSTALLER_VERSION="<tag without v>"; the repository file keeps "dev". The job fails before uploading anything when the dev line is missing or duplicated. * ci(release): publish the installer bootstrap next to the installer bootstrap.sh is uploaded as an immutable per-release copy and, with the stable paths, as memory-plugin-shared/bootstrap.sh. * ci(release): publish the Codex git marketplace in a safe order A single `s3 sync --delete` uploaded info/refs, the pack list and the new pack in no set order and deleted the old pack in the same pass, so a Codex client that checks the marketplace on startup could see refs naming a pack that was not there yet. Upload the new packs first, then objects/info/packs, packed-refs, info/refs and HEAD, and only then delete what the new repository no longer contains. Stop uploading releases/<tag>/memory-plugins.git; nothing references it. * ci(release): keep stable paths on manual TOS runs unless asked, document hotfix and rollback A manual run of release-tos on an older tag used to overwrite the Claude Code marketplace and the Codex git repository by default, which moves every auto-updating user to that release. update_latest now defaults to false. RELEASE.md and RELEASE_CN.md describe how to publish a memory plugin hotfix without a product release, how to roll back, and when each host picks the change up. * fix(plugins): run entrypoint CLIs started through a symlinked path Scripts that are also imported as modules ran their CLI only when fileURLToPath(import.meta.url) equalled path.resolve(process.argv[1]). Node resolves the entrypoint's module URL through symlinks, argv[1] keeps the path as typed, so with a symlinked ~/.openviking or HOME the check failed and the script exited 0 without doing anything. The installer's host-json-config helper was one of them: an uninstall that read it from $OPENVIKING_HOME left every hook and MCP entry in place. Compare the real paths of both sides, the check ov-memory-doctor and the URI guard already use, in the installer helpers, every plugin's MCP proxy, sync.mjs, the pi experiment's credentials CLI and the release scripts. * fix(installer): finish validation when the Codex plugin cache is missing Validation looked for the cached stdio proxy with a find pipeline under pipefail, so a missing cache directory made the whole run exit 1 after a successful install. Add tests for the native Codex path: moving a GitHub marketplace to the TOS git repository, the bundle fallback and the updates line. * fix(installer): honor CLAUDE_CONFIG_DIR and CODEX_HOME Claude Code and Codex read their settings, marketplaces, plugin registry and cache from CLAUDE_CONFIG_DIR and CODEX_HOME when set, but the installer always used ~/.claude and ~/.codex. With CLAUDE_CONFIG_DIR set it enabled marketplace auto-update and registered the statusline in a file Claude Code does not read, leaving a stray declaration that the printed uninstall commands never clean. With CODEX_HOME set it wrote config.toml where Codex does not look. CODEX_CONFIG_FILE still takes precedence for config.toml. * fix(installer): fall back to the bundle when the URL marketplace install fails Registering the TOS URL marketplace first removes an earlier GitHub or directory registration together with its plugin. When the plugin then failed to install from the URL marketplace, the installer aborted and left Claude Code with no plugin, and the remaining harnesses were not installed. It now falls back to the bundle's directory marketplace and retries once, as it already did when the URL marketplace could not be added. * test(installer): keep test installs off the default server port Every installer run checks the server it is given, and the tests passed http://127.0.0.1:1933, the port a developer's own OpenViking server listens on. Runs whose assertions do not depend on the check now use a closed port, and the run that relies on the default URL gets a curl that cannot connect. * fix(installer): keep handled CLI failures from tripping the error report claude_cmd and codex_cmd ran the CLI through `command`. When a program started that way fails, bash 3.2 (macOS /bin/bash) runs the ERR trap even inside `if`, `!` or `||`. So every handled failure, such as a TOS marketplace that cannot be added or `claude plugin enable` exiting 1 right after an install, printed "OpenViking installer stopped unexpectedly" and disarmed the trap, and a real failure later in the run exited silently. The CLIs are now called directly. A Codex git marketplace that cannot be added is followed by the fallback to the bundle, so its error output is silenced again, as before the TOS channel moved into codex_marketplace_sync. * fix(installer): run the downloaded release installer from its own directory The re-exec saved the release installer as a file directly in $TMPDIR and ran it there. The installer takes a lib/install found beside the running script as its own helpers, so a stray or planted $TMPDIR/lib/install became the installer runtime. The copy now runs from a private mktemp -d directory, which is removed afterwards. * fix(installer): run when bash reads the script from stdin With the script piped into bash (`curl ... | bash`), BASH_SOURCE is unset, and resolve_self_checkout aborted the run under set -u. It now treats such a run as one outside any checkout. * fix(installer): keep the API key out of node's arguments Writing ovcli.conf passed the URL, API key, account and user to node as process arguments, which any user can read in the process list while it runs. They now reach that one node process through its environment. * fix(installer): print an uninstall command Codex accepts The next steps told Codex users to run `codex plugin uninstall`, which Codex rejects (it only has `plugin remove`), and the `&&` then skipped the marketplace removal. Codex-format CLIs now get `plugin remove`, and TraeCode CLI, which only has `plugin uninstall`, keeps it. * fix(installer): ask about the statusline before the review The review listed the statusline under ~/.claude/settings.json for every native Claude Code install, but a run without a tty skips it unless --statusline is given, and an interactive run only asked about it after the install had been confirmed. The question now comes before the review, and the review names the statusline only when the install will write it. Without a tty nothing changes: only --statusline, or a statusline an earlier installer registered, gets written. * fix(installer): list the TRAE CLI cleanup and the shared hook runtime in the review The review left out two things the install changes: the deprecated TRAE CLI Hooks integration a TraeCode CLI install removes from ~/.trae/cli/hooks.json, ~/.trae/traecli.toml and ~/.openviking/agent-integrations/trae-cli, and the shared hook runtime in ~/.openviking/agent-integrations/memory-plugin-shared that Cursor, TRAE, TRAE CN and ZCode run from. * fix(installer): translate the statusline lines and name the install source A Chinese run still printed "Sample:", "statusline registered" and the OPENVIKING_STATUSLINE hint in English, and the review showed the internal source mode ("archive"). Those lines go through t now, and the review names the source as the OpenViking release or this checkout. * ci(release): build the plugin zips reproducibly zip stores each file's mtime, so every run of the workflow built memory-plugin-marketplace.zip and openviking-memory-claude.zip with new bytes and overwrote the releases/<tag>/ keys. A manual re-run keeps the stable paths by default, so plugins/claude/marketplace.json and releases/latest/channels.json went on pinning the old sha256, and every Claude Code URL-marketplace install or update and every checksum-verified bundle download then failed. Both zips now come from one script that stamps every entry with the tagged commit's time, sorts the entries and leaves out extra attributes, so a rebuild of the same tag gives the same bytes.
volcengine#5477) * feat(installer): download from the docs site, from the host that answers first The installer, the plugin bundle and the Claude Code and Codex marketplaces are fetched from the documentation site, which serves them under /dl from docs.openviking.net and docs.openviking.ai. The bootstrap downloads the installer from both at once and runs the copy that arrives first; the installer run on its own asks both for one small file. The host that answered serves the rest of the install and is the one Claude Code and Codex keep for their own updates. OPENVIKING_DOWNLOAD_BASE names other locations, separated by spaces. OPENVIKING_TOS_BASE is still read. The commands the installer prints use the https://openviking.net/install entry. * ci(docs): publish the plugin downloads with the docs site Both docs deployments build the installer, the plugin bundle, the Claude Code URL marketplace and the Codex git marketplace from the commit they deploy and publish them under /dl, laid out like the TOS release bucket. They now also run when a plugin, the installer or a release script changes on main, so a plugin change is published when it is merged. The zips are stamped with a fixed time there, so a zip changes only when its content does and the Claude Code zip keeps its bytes for as long as the plugin version names it. * docs(plugins): install through the site entry in plugin guides and doctor hints * chore(plugins): bump the versions past the ones main released Main released the previous versions with the installer rework; the plugin READMEs, doctor hints and skills change here, and main released pi 0.4.7 in the meantime. * docs(plugins): say what the installer's header holds in the note for AI agents * feat(installer): publish the version check's channel data with the docs downloads The docs download tree carries releases/latest/channels.json for the install site's resolver. It names the published version and no checksum: the two hosts deploy one after the other and keep no earlier build, so a pinned bundle would fail to verify while they differ. The resolver answers with one host's addresses, so the installer reads the same paths from the download location it is using.
…ine#5487) The /install/v1 route that answers the version check can only go live on openviking.ai for now, so the installer asks there by default. The bootstrap no longer sets OPENVIKING_INSTALL_SITE: the site serves a copy of it that changes rarely, while the installer is republished with the docs on every merge, so the installer alone decides where the check goes and moving it back needs no site release. The /install entry itself works on both hosts and the commands that name it are unchanged.
* feat(search): add the keywords search type Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix: import cycle and fulltext capability caching Co-authored-by: TRAE CLI <traecli@bytedance.com> --------- Co-authored-by: TRAE CLI <traecli@bytedance.com>
…tovers, and plainer prompts (volcengine#5490) * feat(installer): ask for the language before anything else again An interactive run opens with a Language / 语言 menu, preselecting the language the locale suggests, so a wrong guess costs one keypress instead of a whole run in the other language. --lang, OPENVIKING_LANG, --yes and a run without a terminal skip the menu and use the locale as before. The menu comes after the hand-over to the release installer, so the bootstrap path asks once. * fix(installer): clear what earlier installers left in Claude Code and Codex Installers before the unified `openviking` marketplace registered `openviking-plugins-local`, and their fallback for older Claude Code merged hooks into settings.json and added a user-scope `openviking` MCP server running the plugin's proxy. volcengine#4773 dropped the cleanup on the assumption that every install had moved on, but machines that skipped the releases in between still carry them. In Codex, once that marketplace's directory is gone every `codex plugin marketplace list` fails. The installer then cannot see the `openviking` registration a GitHub install made, skips removing it, and both the git and the directory `marketplace add` fail with "already added from a different source". While the directory is still there, the old plugin runs next to the new one. In Claude Code, the old plugin, the merged hooks and the MCP server all run next to the plugin, so every hook and tool runs twice. The Claude Code and Codex steps now remove those leftovers first, and the review lists them. Only hooks that run the plugin's scripts and an `openviking` server that runs its proxy are touched. Codex also retries `marketplace add` after unregistering the name when it reports a different source, since a registration `marketplace list` cannot report still holds it. The other harnesses already reclaim what their earlier installs wrote. * fix(installer): plainer prompts and key hints for first-time users - An interactive install opens with two lines on what it does and that every change is confirmed first. - The key hints sit beside each question, in bold, instead of in a line under the options that people missed; the language menu's hint is bilingual since the language is not known yet. - The tool menu says "AI tools" instead of "harness", "(not installed)" instead of "(not found in PATH)", and marks adding another command as advanced. - The DeepSeek Harness profile is no longer asked for; --dsh-profile still picks one. - Choosing the cloud service shows where to get an API key; the key prompt says that typing is not shown and only mentions unauthenticated servers when one can be; "keep current" is offered only when there is a current URL. - The statusline question says it is for Claude Code. - The review lists removing the deprecated TRAE CLI Hooks integration only when there is one.
* feat(debug): expose vector metric and dense score semantics Co-authored-by: TRAE CLI <traecli@bytedance.com> * refactor(observer): reuse vikingdb status for score semantics Co-authored-by: TRAE CLI <traecli@bytedance.com> * fix(observer): preserve status contract and docs links Co-authored-by: TRAE CLI <traecli@bytedance.com> --------- Co-authored-by: TRAE CLI <traecli@bytedance.com>
…engine#5483) Co-authored-by: r266-tech <r266-tech@users.noreply.github.com>
…state (volcengine#5936) * fix(opencode-plugin): prune stale fully-captured sessions from local state * fix(opencode-plugin): persist pruned session state right after load
…onger has the session (volcengine#5935) * fix(opencode-plugin): drop local session mapping when the server no longer has the session * fix(opencode-plugin): tighten stale-mapping comment; assert commitSession returns not_found
…5981) * fix(gateway): log the capture status a request ends with The request log row carried the capture status read when the request was prepared. A long request that started while delivery was retrying finished after the worker's delivery_recovered row, so its stale "retrying" became the newest status for the session and /admin/overview kept counting the capture in capture_issues until another request arrived. Re-read the capture document just before the row is written. * fix(gateway): keep the request log row when the capture refresh fails
…nt (volcengine#6102) Ark/OpenAI SDK read timeouts say "Request timed out." and bare timeout exceptions carry no message, so both were classified as unknown and never retried. Minimal fix extracted from volcengine#5927. Fixes volcengine#5513
* refactor(session): 清理无生产调用的旧实现 移除仅测试使用的图展示和提示词生成器,以及未使用的文本补丁解析路径。 保留结构化补丁和描述渲染契约,删除对应的过期测试。 Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com> * refactor(session): 保留记忆图展示实现 按维护者要求保留 graph_view.py,仍删除对应的专属单测。 Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com> --------- Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
… and outputs (volcengine#5937) * fix(plugins): apply capture filter substitutions to tool parts and warn on invalid rules filterCaptureParts carried tool call/result parts through untouched, so a captureFilters redaction rule never reached a secret that appeared in a tool input or output, and those payloads were stored verbatim. Substitutions now run over tool_input values and tool_output too; only text parts still decide the drop/keep verdict. A rule that failed to compile was skipped without any signal at capture time. It is now reported on stderr once per process. * fix(plugins): keep capture filter change scoped to tool-part redaction Drop the capture-time invalid-rule warning so it does not overlap the separate config-load warning work, and document that s rules now rewrite tool call inputs and tool result outputs while d/k rules only read text.
Session 记忆提取统一走归档和后台任务流程,删除 Web Studio 的重复入口、客户端声明及过期测试,并更新中英文迁移说明。
BREAKING CHANGE: 移除 POST /api/v1/sessions/{session_id}/extract,调用方需改用 commit 并查询返回的任务 ID。
Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com>
Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
Remove the estimator left without production callers after the truncation rewrite, along with its test-only assertions. Keep truncation coverage. Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…ngine#5760) (volcengine#5771) * fix(embedding): truncate plain list inputs to max_input_tokens (volcengine#5760) The OpenAI-compatible embedder's _prepare_embedding_input passed plain list[str] inputs straight through to the provider, skipping the max_input_tokens guard entirely, while the base prepare_embedding_input collapsed the same input to an empty string for text-only embedders and left items unbounded for multimodal embedders. The embedding queue contract explicitly allows list inputs, so any producer could submit oversized items that fail with provider errors (e.g. bigmodel 1210). Both guard layers now keep the list shape and bound every item individually via truncate_embedding_input; max_input_tokens=None keeps the historical passthrough. Single-string and dict-part multimodal behavior is unchanged. * fix(embedding): narrow batch truncation to OpenAI input guard --------- Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…#6137) Avoid invalid Chat Completions requests by selecting none for affected models when tools are present. Preserve explicit settings and Codex defaults. Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
A short skill that teaches agents how to search and read resources under viking://resources/: pick find/grep/glob/list, then drill from a directory's .abstract.md -> .overview.md -> the file. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…l task errors (volcengine#5622) (volcengine#5772) on_dequeue only guarded _parse_message; any exception raised inside _process — e.g. the json.JSONDecodeError from Session.load() reading a torn line of the live messages.jsonl during a compact commit — escaped the handler. The queue layer then swallowed it: the delivery was never acked, the queue row stayed in processing, and the task record stayed pending until the client gave up with "Phase 2 timed out". A worker restart replays the same payload into the same race, so recovery never completes on its own. Wrap the _process call and mirror the existing session-missing branch: reuse the Phase-1 task record (tracker.create is idempotent for an explicit task_id owned by the same user), record the real error via tracker.fail, and settle the delivery as ProcessResult.failed so clients see the underlying crash instead of a timeout. Out of scope (noted in the PR): locking Session.load() against the Phase-1 drain-rewrite, switching Phase 2 to the immutable archive, and client timeout semantics. Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…ine#6148) * fix(studio): preserve setup across upgrades and respect asset base * fix(studio): remove code badge from open-source connections * fix(studio): remove redundant setup back button * fix(studio): use green for active connection status
Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com> Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com>
…s queued (volcengine#6077) Fixes volcengine#6076 Co-authored-by: mayunxiang.26 <mayunxiang.26@bytedance.com>
) * fix(storage): avoid blocking executor on pathlock waits Run contended PathLock acquisition on the native Tokio runtime so unrelated storage work can keep using Python's shared executor. * refactor(storage): simplify async pathlock bridge * test(storage): 对齐账户测试后端的异步锁接口 替换已有测试接口,保留原契约和用例;测试总行数不增加。 Co-authored-by: qin-ctx <qinhaojie.exe@bytedance.com> --------- Co-authored-by: qin-ptr <222410531+qin-ptr@users.noreply.github.com>
…key docs (volcengine#6107) * docs(gateway): explain how to find or reset a lost management key Add a short "Lost the management key" section to the operations guide and link it from the troubleshooting entry and the management_key_invalid error. In Studio, the unlock dialog now lists the server configuration page and how to reset the key, and the enable-gateway reveal no longer implies the key is shown only once, since it is saved in ov.conf. * feat(gateway): add a quick mode card to the Studio overview Before the first key exists, the Get started checklist offers a quick mode card that does the first three steps in one click: it imports OpenViking's VLM settings when no upstream is usable, reuses or creates the "default" context profile, and issues a key for the current user. Root outside dev mode gets the issue dialog prefilled to pick the key's user. When the management key is locked and something is missing, the card offers unlocking instead.
…ts (volcengine#6101) * feat(gateway): support queries, {api_key} and key_header on upstreams Builds on append_endpoint_path: base_url may carry a query (stored parameters win over client ones), {api_key} fills the key into the URL with no auth header, and key_header picks Bearer, api-key or x-api-key. Exact endpoints serve their protocol's path and its sub-paths only, so count_tokens and stored responses continue after the endpoint while embeddings and the connection test no longer reach them. * feat(gateway): import Azure OpenAI VLM credentials as exact-endpoint upstreams * feat(studio): choose the key header and accept queries and {api_key} in upstream URLs * docs(gateway): document upstream queries, {api_key}, the key header and Azure
…teway (volcengine#6119) * feat(mcp): add download tool that returns a single-use download link The MCP server cannot write onto the agent's disk, so `download(uri)` mirrors the add_resource local-file flow in reverse: it stats the file under the caller, mints a single-use token bound to the caller identity and the resolved URI, and returns a curl command for GET /api/v1/content/download?token=. A token request serves only the bound URI and re-checks ACL at fetch; API-key requests (ov get) are unchanged. Download tokens live in a separate store so they can never be redeemed on temp_upload, or the reverse. Upload and download tokens are now 10 characters instead of 6. * feat(gateway): proxy MCP download links and add mcp_file_links option The gateway calls OpenViking's /mcp on its internal URL, so links OpenViking mints point at an address clients cannot reach. Upload links were already rewritten to the gateway's /uploads; download links were passed through unchanged. New gateway setting mcp_file_links governs both kinds of link: - proxy (default): rewrite upload and download links to public_url and forward them through /gateway/uploads and the new /gateway/downloads, dropping OpenViking's "auto-detected URL" note that no longer applies. - direct: pass OpenViking's links through unchanged; the server's OPENVIKING_PUBLIC_BASE_URL must be reachable by the client. download requires a client shell tool, like local-path uploads. A missing public_url in proxy mode is now reported to the model instead of being masked by the generic tool error.
…y and reply lookup (volcengine#6026) * perf(gateway): look up reply owners through replay_kinds Without ANALYZE statistics SQLite plans replies() as a scan of the covering primary key over every record of the scope, which costs about 20 ms per request at 1k stored sessions. Force the (scope,kind,anchor) index: 0.2 ms at 1k sessions, and cheaper on small stores too. * perf(gateway): search auxiliary patterns case-sensitively on lowercased text classify() ran six re.I searches over str(system) on every request; re.I disables re's literal search, so each scanned every offset (~3.8 ms at 80k chars). Lowercase the probe once and search lowercased plain patterns instead, falling back to re.I when the text has one of the three letters where lower() and re.I disagree. Also skip the deferred-tool regex in client_openviking_tools for texts without '__'. * perf(gateway): cut per-event cost of the streamed reply relay - SSEDecoder.feed scans a chunk once with a precompiled newline-first pattern and copies the remainder once, instead of an uncompiled `\r?\n\r?\n` search that tried a match at every byte and re-sliced the buffer per frame. - The hidden tool loop writes all events decoded from one upstream chunk as one client write (events before a failure included), so a loaded worker makes one ASGI send per chunk instead of per event. - The relayed-reply capture reads the assembled reply once per chunk. - Streamed text, thinking, signatures and tool arguments grow in place, so a long reply no longer costs time quadratic in its length. A delta that cannot be appended leaves the text streamed so far intact. Client bytes and replay records are unchanged. * perf(gateway): hash only the reply when anchoring it after the request chain relayed, record_reasoning, the tool loop's persist and capture staging each recomputed hidden_chain over [*request.messages, *reply], re-normalizing and re-hashing the whole history (about 1 ms at 140k tokens per call, three or four times per request, mostly through a thread handoff). The request's capture_chain already is that history's chain, so prefix_chain/hidden_chain now take the chain to continue (`after`) and reply_chain hashes only the reply. Anchors are bit-identical: the continued chain seeds from the last non-empty digest, exactly the state the full pass carries. * perf(gateway): skip rereading the captured reply for chunks that complete no event A chunk that ends inside an event, common when the upstream splits large frames, made the relayed-reply capture rebuild the whole reply so far for nothing. With 64-byte chunks a 395-event reply cost 4.0 ms of capture CPU instead of 2.9 ms.
…6157) Since volcengine#6077 on_resource_added returns once the reason commit is queued, but memory_linking.status still said "success", which reads as a finished link. It now says "accepted" (the word connector submissions already use), and the API docs say to poll commit_task_id for the result. Follow-up to the volcengine#6077 review. Co-authored-by: mayunxiang.26 <mayunxiang.26@bytedance.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
本 PR 修复 OpenViking 本地开发模式下的跨源请求安全问题。
当用户未配置
root_api_key且未显式指定auth_mode时,服务会自动进入dev模式。该模式不要求 API Key,并以 ROOT 身份处理请求。同时,服务默认将 CORS 配置为[*]。在默认监听本机地址的情况下,任意恶意网页仍可借助浏览器请求127.0.0.1:1933,从而访问本地 OpenViking 实例。Human Involvement
Related Issue
本 PR 暂无关联 Issue。
Type of Change
Changes Made
localhost、127.0.0.1和[::1],允许任意本地开发端口。Origin的 HTTP 请求在进入路由前直接返回403。Origin的 CLI、SDK 和 curl 请求行为不变。cors_origins配置行为不变。原始问题与修复后的行为
修复前,攻击链如下:
https://evil.example等恶意网页。http://127.0.0.1:1933。修复后:
https://evil.example等非 loopback 来源无法通过 CORS preflight。multipart/form-data,也会在服务端 Origin guard 处被拒绝,不会进入业务路由。http://localhost:5173、http://127.0.0.1:3000等本地开发页面仍然可以访问 dev server。Origin时不受影响。Testing
验证命令:
HOME=$PWD/.tmp/ovtest-home uv run --all-extras python -m pytest -q tests/server/test_cors.py uv run ruff check openviking/server/app.py tests/server/test_cors.py验证结果:4 个测试通过,Ruff 检查通过。
本问题先通过代码路径分析确认,再通过新增测试验证修复行为;未进行真实恶意网页浏览器端到端测试。
Checklist
Screenshots (if applicable)
不适用。
Additional Notes
本修复只针对 dev 模式下“免鉴权 + 任意网页跨源访问”的问题。非 dev 模式的 CORS 配置语义未改变。