Skip to content

fix: opencode-plugin shares one agentId across all projects, merging unrelated memories#2230

Open
mvanhorn wants to merge 1 commit into
volcengine:mainfrom
mvanhorn:fix/1942-openviking-opencode-plugin-per-project-namespace
Open

fix: opencode-plugin shares one agentId across all projects, merging unrelated memories#2230
mvanhorn wants to merge 1 commit into
volcengine:mainfrom
mvanhorn:fix/1942-openviking-opencode-plugin-per-project-namespace

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

Description

The opencode memory plugin (examples/opencode-memory-plugin/) uses a single hardcoded agentId regardless of which project the agent is operating on. Memories from unrelated projects accumulate under the same identity and get surfaced into each other's prompts, polluting context.

Related Issue

Closes #1942. qin-ctx acknowledged the bug in-thread.

Type of Change

  • Bug fix

Changes Made

Derive an effective agentId per project: base-{slug}-{sha1(directory)[:8]}. Wired up via a new projectIsolation flag in openviking-config.example.json (default on, opt-out for backward compat). Updated the example README so users see the new behavior and how to disable it.

Testing

examples/opencode-memory-plugin/tests/test-project-isolation.mjs exercises the namespace derivation, opt-out, and verifies two projects no longer collide on memory keys.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • Tests pass locally
  • I have added tests that prove my fix is effective

AI was used for assistance.

@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ✅

1942 - Fully compliant

Compliant requirements:

  • Added per-project agentId derivation using directory path
  • Added projectIsolation config flag (default true for backward compatibility)
  • Added tests for the new functionality
  • Updated documentation
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🏅 Score: 88
🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ Recommended focus areas for review

Potential Empty Agent ID

OPENVIKING_AGENT_ID_OVERRIDE can result in an empty string agent ID if set to an empty value, which would send an empty X-OpenViking-Agent header.

if (Object.prototype.hasOwnProperty.call(process.env, "OPENVIKING_AGENT_ID_OVERRIDE")) {
  return process.env.OPENVIKING_AGENT_ID_OVERRIDE ?? ""
}
Test Code Duplication

Tests duplicate implementation logic from the plugin source, risking drift if the plugin code changes.

function sanitizeProjectBasename(value) {
  const sanitized = value.replace(/[^A-Za-z0-9_-]+/g, "").slice(0, 64)
  return sanitized || "project"
}

function resolveBaseAgentId(value) {
  if (typeof value === "string" && value.trim()) {
    return value.trim()
  }
  return DEFAULT_CONFIG.agentId
}

function resolveEffectiveAgentId(baseAgentId, projectIsolation, directory, env = process.env) {
  if (!projectIsolation) {
    return baseAgentId
  }

  if (Object.prototype.hasOwnProperty.call(env, "OPENVIKING_AGENT_ID_OVERRIDE")) {
    return env.OPENVIKING_AGENT_ID_OVERRIDE ?? ""
  }

  if (!directory) {
    return baseAgentId
  }

  const basename = sanitizeProjectBasename(path.basename(directory))
  const hash = createHash("sha1").update(directory).digest("hex").slice(0, 8)
  return `${baseAgentId}-${basename}-${hash}`
}

function loadConfig({ fileConfig = {}, directory, env = {} } = {}) {
  const config = {
    ...DEFAULT_CONFIG,
    ...fileConfig,
  }
  const baseAgentId = env.OPENVIKING_AGENT_ID
    ? resolveBaseAgentId(env.OPENVIKING_AGENT_ID)
    : resolveBaseAgentId(config.agentId)
  const projectIsolation = typeof config.projectIsolation === "boolean"
    ? config.projectIsolation
    : DEFAULT_CONFIG.projectIsolation

  return {
    ...config,
    agentId: resolveEffectiveAgentId(baseAgentId, projectIsolation, directory, env),
    projectIsolation,
  }
}

@github-actions
Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Validate agent ID override value

Validate the OPENVIKING_AGENT_ID_OVERRIDE value before returning it, falling back to
the base agent ID if the override is empty or whitespace-only to avoid invalid agent
IDs.

examples/opencode-memory-plugin/openviking-memory.ts [407-409]

 if (Object.prototype.hasOwnProperty.call(process.env, "OPENVIKING_AGENT_ID_OVERRIDE")) {
-  return process.env.OPENVIKING_AGENT_ID_OVERRIDE ?? ""
+  const override = process.env.OPENVIKING_AGENT_ID_OVERRIDE;
+  if (typeof override === "string" && override.trim()) {
+    return override.trim();
+  }
 }
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly validates the OPENVIKING_AGENT_ID_OVERRIDE value to avoid using an empty or whitespace-only agent ID, which improves robustness by preventing invalid configurations.

Medium
Avoid mutating input config object

Create a deep copy of the config object before modifying it to prevent unintended
side effects on the original input config (e.g., DEFAULT_CONFIG or parsed file
config).

examples/opencode-memory-plugin/openviking-memory.ts [420-439]

 function finalizeConfig(config: OpenVikingConfig, directory?: string): OpenVikingConfig {
+  // Create a deep copy to avoid mutating the input config
+  const finalConfig: OpenVikingConfig = {
+    ...config,
+    autoCommit: config.autoCommit ? { ...config.autoCommit } : undefined,
+  };
+
   if (process.env.OPENVIKING_API_KEY) {
-    config.apiKey = process.env.OPENVIKING_API_KEY
+    finalConfig.apiKey = process.env.OPENVIKING_API_KEY;
   }
 
   if (process.env.OPENVIKING_AGENT_ID) {
-    config.agentId = resolveBaseAgentId(process.env.OPENVIKING_AGENT_ID)
+    finalConfig.agentId = resolveBaseAgentId(process.env.OPENVIKING_AGENT_ID);
   } else {
-    config.agentId = resolveBaseAgentId(config.agentId)
+    finalConfig.agentId = resolveBaseAgentId(finalConfig.agentId);
   }
 
-  config.projectIsolation = resolveBoolean(config.projectIsolation, DEFAULT_CONFIG.projectIsolation)
-  config.agentId = resolveEffectiveAgentId(config.agentId, config.projectIsolation, directory)
+  finalConfig.projectIsolation = resolveBoolean(finalConfig.projectIsolation, DEFAULT_CONFIG.projectIsolation);
+  finalConfig.agentId = resolveEffectiveAgentId(finalConfig.agentId, finalConfig.projectIsolation, directory);
 
-  if (config.autoCommit) {
-    config.autoCommit.intervalMinutes = getAutoCommitIntervalMinutes(config)
+  if (finalConfig.autoCommit) {
+    finalConfig.autoCommit.intervalMinutes = getAutoCommitIntervalMinutes(finalConfig);
   }
 
-  return config
+  return finalConfig;
 }
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that mutating the input config object could lead to unintended side effects, and the improved code creates a copy to prevent this. This is a quality improvement that helps maintain code correctness.

Low

@mvanhorn mvanhorn force-pushed the fix/1942-openviking-opencode-plugin-per-project-namespace branch from 48c5632 to b71fce7 Compare May 25, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

[Bug]: opencode plugin 没有办法能根据项目进行隔离。都会存到同一个记忆里

1 participant