Skip to content

refactor(plugin):Merge OpenCode plugins#1859

Merged
yangxinxin-7 merged 5 commits into
volcengine:mainfrom
tanyouqing:refactor/merge_opencode_plugins
May 12, 2026
Merged

refactor(plugin):Merge OpenCode plugins#1859
yangxinxin-7 merged 5 commits into
volcengine:mainfrom
tanyouqing:refactor/merge_opencode_plugins

Conversation

@tanyouqing
Copy link
Copy Markdown
Contributor

Description

Adds a unified OpenCode plugin package for OpenViking. The plugin merges repository retrieval and long-term memory capabilities into a package-style OpenCode plugin that can be enabled through OpenCode's plugin configuration.

Related Issue

N/A

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test update

Changes Made

  • Added examples/opencode-plugin as a unified OpenCode plugin package.
  • Added OpenViking memory and repository tools including search, read, browse, grep, glob, add, remove, queue status, and session commit.
  • Added package metadata so the plugin can be published to npm and enabled via "plugin": ["openviking-opencode-plugin"].
  • Added English and Chinese installation documentation for package-based and source-based setup.
  • Added an OpenCode wrapper entry for local plugin installation.

Testing

  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have tested this on the following platforms:
    • Linux
    • macOS
    • Windows

Checklist

  • My code follows the project's coding style
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 5, 2026

CLA assistant check
All committers have signed the CLA.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🏅 Score: 80
🧪 No relevant tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

Incorrect file extension for ESM wrapper

The wrapper file uses ESM syntax (export) but has a .js extension. When placed in the OpenCode plugins directory (which lacks a package.json with "type": "module"), Node.js will interpret it as CommonJS and throw a syntax error. The file should be renamed to .mjs.

export { OpenVikingPlugin, default } from "./openviking/index.mjs"
Documentation consistent with wrapper file extension

INSTALL.md correctly references openviking.mjs, but the actual file is named openviking.js. After renaming the wrapper to .mjs, verify all docs are consistent.

cp examples/opencode-plugin/wrappers/openviking.mjs ~/.config/opencode/plugins/openviking.mjs
Documentation uses wrong wrapper extension

INSTALL-ZH.md references openviking.js, but the wrapper should use .mjs for ESM compatibility. Update the documentation to use .mjs after renaming the wrapper file.

mkdir -p ~/.config/opencode/plugins/openviking
cp examples/opencode-plugin/wrappers/openviking.js ~/.config/opencode/plugins/openviking.js

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

PR Code Suggestions ✨

No code suggestions found for the PR.

@tanyouqing tanyouqing changed the title Merge OpenCode plugins into main branch refactor(plugin):Merge OpenCode plugins May 5, 2026
@qin-ctx qin-ctx requested review from Mijamind719 and ZaynJarvis May 6, 2026 03:12
@t0saki
Copy link
Copy Markdown
Collaborator

t0saki commented May 6, 2026

整体方向 +1,分层清晰,工具语义也设计得到位。merge 前几个 must-fix,后面是建议。

🔴 必修

  1. wrappers/openviking.js 必须改成 .mjs,INSTALL.md 同步更新。
    ~/.config/opencode/plugins/ 默认没有 package.json,那里的 .js 走 CJS 解析,export { ... } from 直接 SyntaxError。package.json"check": "node --check wrappers/openviking.js" 也要跟着改。

  2. INSTALL.md / INSTALL-ZH.md L60-62 那段开发期残留要清理。

    \\If it cannot be loaded automatically, try running it manually:
    cd D:\OV-intern\ov-plugins-merge\.opencode\plugins
    

    \\If 是错别字,D:\OV-intern\... 是作者本机路径,不能进发布文档。

  3. README.md / INSTALL.md 声称"替换了 examples/opencode/examples/opencode-memory-plugin/",但 PR 没删旧目录。
    要么这个 PR 一起删旧目录,要么把"replaces"文案改成"新增统一插件,旧示例后续会下线"。文档和代码必须一致,否则用户不知道该装哪个。

  4. package.jsonfiles 列了不存在的 README_ZH.md
    仓库里只有 INSTALL-ZH.md,没有 README_ZH.mdnpm pack 会丢中文 README。要么改名,要么从 files 删掉。

  5. @opencode-ai/plugin: "latest" 锁到具体版本。
    latest 影响重现性、有供应链风险。改成 ^x.y.z

🟡 强烈建议

  1. makeAuthHeaders 改用 Authorization: Bearer
    Server 两种都吃,但 Bearer 是更通用的标准头,也方便后面统一鉴权链路。

  2. mergeConfigOPENVIKING_URL / OPENVIKING_ENDPOINT env 覆盖。
    现在只 cover 了 apiKey/account/user/agentId,endpoint 是漏的。

  3. memory-recall.mjs 的 token 抽取支持 CJK。
    RECALL_TOKEN_RE = /[a-z0-9]{2,}/gi 不匹配中文,中文 query 进 lexicalOverlapBoost 永远返回 0,这条路径对中文用户是死代码。
    建议:/[\p{Script=Han}]+|[a-z0-9]{2,}/giu + 加几个中文停用词。
    PREFERENCE_QUERY_RE / TEMPORAL_QUERY_RE 已经支持中文,这部分 OK。)

  4. session.createdrepoContext.refreshRepos({ force: true }) 改成 force: false
    现在每开一个新 session 都强制绕过 TTL 重拉一次,没必要,TTL 自然兜就行。

  5. memsearch 返回结果别全 JSON.stringify
    formatSearchResults 当前把 memories + resources + skills 完整序列化,包含 content 字段,20 条结果容易爆 LLM context。设计意图是让 LLM 用 memread 取详情,所以 search 返回里只保留 uri / title / abstract 就够了。

  6. flushPendingMessages "内容更新"分支会导致重复入库(memory-session.mjs ~L285-292)。
    send 期间 stream chunk 进来时,旧版本已经成功 POST 了但因为 latest !== content 命中 continue、跳过 capture 标记,下一轮 flush 会用新内容再发一次,同 messageId 不同 content 在 OV 那边形成重复消息。要么加注释明确这是预期行为,要么改成"接受 latest 作为下一轮种子"。

  7. makeRequest / makeMultipartRequest 的 401/403 分支把 server 真实错误抹掉了。
    trusted mode 下"Missing X-OpenViking-Account"这种具体信息直接看不到,debug 困难。建议拼上 errorMessage:Authentication failed (${errorMessage}). Check apiKey/account/user...

  8. 加几个单测。
    mergeMessageContent 的合并语义、memory-recall 的排序 / 去重 / token boost 都是会出 corner case 的地方,2400 行代码 0 测试有点慌。

💡 架构层面(不阻塞合并,可开 follow-up)

读侧工具(memsearch / memread / membrowse / memgrep / memglob)长期建议复用 OV 自带 MCP,plugin 只保留 OpenCode event 必须做的事——session 映射、消息流捕获、auto-commit、auto-recall。这样跨客户端(Claude Code / OpenCode / Cursor / Codex)工具表一致,server 升级新工具 plugin 不用跟版本。memadd 的本地上传可以接上 #1847 的 progressive upload 流程。

Rename wrapper to .mjs, clean install docs, align README/INSTALL wording with the current directory layout, remove invalid README_ZH.md from package files, and pin @opencode-ai/plugin version.
@tanyouqing tanyouqing force-pushed the refactor/merge_opencode_plugins branch from 79a6170 to 33f4ee7 Compare May 6, 2026 10:59
Copy link
Copy Markdown
Collaborator

@ZaynJarvis ZaynJarvis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work merging the split plugins into one unified package. Two suggestions:

  1. Remove autoStartServer — the plugin should only take an endpoint URL + API key. Decoupling from server lifecycle is cleaner and avoids the detached-process management problem (no PID tracking / no shutdown).

  2. Wrapper path assumptionwrappers/openviking.mjs exports from "./openviking/index.mjs", which assumes the source-install directory layout. This will break when installed as an npm package. Consider adjusting or documenting the constraint.

Otherwise the architecture is solid — native OpenCode plugin API (event/tool/system.transform/messages.transform) is the right call over the shell-hook approach.

@ZaynJarvis
Copy link
Copy Markdown
Collaborator

One more — the 10-minute auto-commit interval may not be the best trigger. Memories are already in the session context while the conversation is alive. Committing on a timer wastes cycles when context is intact. Consider committing only at context-loss boundaries (session end, compaction, context window pressure) instead of on a fixed interval.

…to-start logic from the plugin runtime - Replace fixed-interval auto commits with lifecycle-boundary commits - Commit sessions on compaction, deletion, error, and plugin shutdown - Exclude source-install wrapper files from the npm package - Update docs to clarify wrapper usage and remove deprecated config fields
@chenjw chenjw requested a review from yangxinxin-7 May 7, 2026 04:20
@A0nameless0man
Copy link
Copy Markdown
Contributor

A0nameless0man commented May 12, 2026

记忆召回应使用 chat.message 钩子替代 experimental.chat.messages.transform

memory-recall.mjs 和 index.mjs 中的召回实现使用了 experimental.chat.messages.transform 注入 。这个钩子有一个问题:它没有状态——对消息的修改不会持久化到会话存储。每次 LLM API 调用(含工具调用续接)都会重新触发,导致每次注入的记忆集合可能不同。

问题示意:

用户输入"修个bug"
  → API 调用 #1:搜索 OpenViking → 注入记忆 A、B、C
  → LLM 调用工具
  → API 调用 #2:重新搜索 → 注入记忆 A、D、E  ← B、C 丢了!
  → API 调用 #3:重新搜索 → 注入记忆 D、F、G  ← A 又丢了

后果:上下文不一致、前序记忆丢失、KV-cache 命中率极差、OpenViking API 重复调用。

解决方案:使用 chat.message + synthetic: true

chat.message 钩子在用户消息创建时触发一次,修改 output.parts 会持久化到会话历史。synthetic: true 在 TUI 中不可见但对 LLM 作为 context 发送。

需改动 index.mjs:

// 将
"experimental.chat.messages.transform": async (_input, output) => {
  await recall.injectRelevantMemories(output)
},
// 替换为
"chat.message": async (input, output) => {
  try {
    if (!config.autoRecall?.enabled) return
    await recall.injectRelevantMemories(input, output)
  } catch (error) {
    log("WARN", "recall", "Auto recall failed", { error: error?.message })
  }
},

需改动 memory-recall.mjs:

injectRelevantMemories 签名从 (output) 改为 (input, output)
appendToLastTextPart(lastUser, block) 替换为插入 synthetic part:
output.parts.unshift({
  id: `prt-ov-recall-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
  type: "text",
  text: block,
  synthetic: true,
  sessionID: input.sessionID,
  messageID: input.messageID,
})

另外:

OpenCode schema 要求 Part ID 必须以 prt 开头(packages/opencode/src/session/schema.ts:24Schema.isStartsWith("prt")
幂等性:保留 标记检测,防止重加载会话后重复注入

参见 #1977

…c parts

Replace experimental.chat.messages.transform recall injection with chat.message.

Inject recalled memories as hidden synthetic text parts persisted in session history.

Preserve idempotency with relevant-memories marker detection and update README wording.
Copy link
Copy Markdown
Collaborator

@yangxinxin-7 yangxinxin-7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, good addition.

@yangxinxin-7 yangxinxin-7 merged commit 31a31ff into volcengine:main May 12, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in OpenViking project May 12, 2026
ZaynJarvis pushed a commit that referenced this pull request May 13, 2026
* Merge opencode plugins

* update:readme&install docs

* Fix Opencode plugin packaging issues

Rename wrapper to .mjs, clean install docs, align README/INSTALL wording with the current directory layout, remove invalid README_ZH.md from package files, and pin @opencode-ai/plugin version.

* Address OpenCode plugin review feedback - Remove OpenViking server auto-start logic from the plugin runtime - Replace fixed-interval auto commits with lifecycle-boundary commits - Commit sessions on compaction, deletion, error, and plugin shutdown - Exclude source-install wrapper files from the npm package - Update docs to clarify wrapper usage and remove deprecated config fields

* fix(opencode-plugin): persist memory recall via chat.message synthetic parts

Replace experimental.chat.messages.transform recall injection with chat.message.

Inject recalled memories as hidden synthetic text parts persisted in session history.

Preserve idempotency with relevant-memories marker detection and update README wording.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants