Skip to content

Claude/venice ai acode plugin cpwzba#2413

Closed
DUptain1993 wants to merge 2 commits into
Acode-Foundation:mainfrom
DUptain1993:claude/venice-ai-acode-plugin-cpwzba
Closed

Claude/venice ai acode plugin cpwzba#2413
DUptain1993 wants to merge 2 commits into
Acode-Foundation:mainfrom
DUptain1993:claude/venice-ai-acode-plugin-cpwzba

Conversation

@DUptain1993

Copy link
Copy Markdown

No description provided.

claude added 2 commits June 27, 2026 07:27
Adds a full-featured Venice AI coding assistant plugin for Acode with:

- Sidebar chat panel with streaming responses and conversation history
- Full codebase context: active file, selection, all open tabs
- Per-code-block actions: insert at cursor, replace file, new file, copy
- Inline system-prompt editor with live preview and reset-to-default
- Model picker: Gemma 4 Uncensored, Llama 3.3 70B, DeepSeek R1,
  Qwen 2.5 Coder, Mistral 3.1, Venice Uncensored, Llama 3.2 3B
- Acode settings integration (API key, model, temperature, max tokens)
- Context toggles: current file / selection / all open files
- Auto file-creation banner when AI mentions a filename
- Lightweight markdown renderer with fenced code highlighting
- localStorage persistence for all settings across sessions

Plugin files live in acode-plugins/venice-ai-wizard/ (source) and
acode-plugins/venice-ai-wizard.zip (installable package).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzaGGLg7cz4jMb85KMAXrb
Replace Element.after() DOM calls (unsupported on some Android WebViews)
with strict appendChild ordering. Wrap init() in try/catch so any failure
logs to console instead of marking the plugin broken. Add inline system
prompt editor in the sidebar panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzaGGLg7cz4jMb85KMAXrb
@greptile-apps

greptile-apps Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds the Venice AI Coding Wizard — a new community plugin that integrates Venice AI's OpenAI-compatible API into Acode as a sidebar chat panel, allowing users to generate, edit, and create code files with full editor context.

  • Core chat flow: Sends the active file content, selection, or open tabs as context to Venice AI's /chat/completions endpoint with streaming support and a streaming→non-streaming fallback; response code blocks each get Insert/Replace/New-file/Copy action buttons.
  • API credential storage: The Venice AI API key and all settings are persisted to localStorage as plaintext JSON, making the key readable by any other plugin running in the same WebView.
  • Streaming fallback bug: Any non-OK HTTP response (401, 429, 500) from the first streaming call is silently swallowed and triggers an identical second API call, doubling quota consumption on every API error before the user sees a message.

Confidence Score: 2/5

The plugin has two functional defects on the primary user-facing code path that should be fixed before merging.

API errors silently fire two network requests wasting user quota, and the API key is stored in shared localStorage with no isolation — any other installed plugin can extract it.

acode-plugins/venice-ai-wizard/main.js — the sendMessage try/catch nesting and the loadSettings/saveSettings pair need attention before shipping.

Security Review

  • API key exposure via localStorage (main.js loadSettings/saveSettings): The Venice AI API key is serialized into localStorage["venice_ai_wizard_settings"] as plaintext JSON. Any other Acode plugin executing in the same WebView origin can read this value directly, enabling API key theft and unauthorized usage billed to the victim.
  • No other injection, RCE, or credential-forwarding issues identified — user input is rendered via textContent and createTextNode, not innerHTML, so XSS is not a concern in the chat display.

Important Files Changed

Filename Overview
acode-plugins/venice-ai-wizard/main.js Core plugin implementation: API key stored in plaintext localStorage, double API call on streaming failure, O(n²) streaming render, unbounded conversation history
acode-plugins/venice-ai-wizard/plugin.json Plugin metadata — well-formed; missing a price field (common in Acode plugin registry entries) but not structurally blocking
acode-plugins/venice-ai-wizard/readme.md Documentation — covers setup, features, models, and file operations; no issues
acode-plugins/venice-ai-wizard.zip Pre-built zip of the plugin; risks diverging from source files if either is updated independently

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant Panel as Venice AI Wizard Panel
    participant LS as localStorage
    participant Editor as Acode Editor
    participant Venice as Venice AI API

    User->>Panel: Types message, presses Send
    Panel->>LS: loadSettings() — reads apiKey, model, etc.
    Panel->>Editor: getActiveFileContext() / getSelection()
    Panel->>Panel: buildContextualHistory() — prepend file context to last user msg
    Panel->>Venice: POST /chat/completions (stream:true)
    alt stream succeeds
        Venice-->>Panel: SSE delta chunks
        Panel->>Panel: "streamDiv.textContent = full (each chunk, O(n²))"
        Panel->>Panel: renderMarkdown(full) on completion
    else stream throws (including API errors like 401/429)
        Panel->>Panel: remove streamDiv (swallow error)
        Panel->>Venice: POST /chat/completions (stream:false) — SECOND call
        Venice-->>Panel: full response or error
    end
    Panel->>LS: saveSettings() if needed
    Panel->>User: Display response + code action buttons
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant Panel as Venice AI Wizard Panel
    participant LS as localStorage
    participant Editor as Acode Editor
    participant Venice as Venice AI API

    User->>Panel: Types message, presses Send
    Panel->>LS: loadSettings() — reads apiKey, model, etc.
    Panel->>Editor: getActiveFileContext() / getSelection()
    Panel->>Panel: buildContextualHistory() — prepend file context to last user msg
    Panel->>Venice: POST /chat/completions (stream:true)
    alt stream succeeds
        Venice-->>Panel: SSE delta chunks
        Panel->>Panel: "streamDiv.textContent = full (each chunk, O(n²))"
        Panel->>Panel: renderMarkdown(full) on completion
    else stream throws (including API errors like 401/429)
        Panel->>Panel: remove streamDiv (swallow error)
        Panel->>Venice: POST /chat/completions (stream:false) — SECOND call
        Venice-->>Panel: full response or error
    end
    Panel->>LS: saveSettings() if needed
    Panel->>User: Display response + code action buttons
Loading

Reviews (1): Last reviewed commit: "fix: rewrite plugin v1.1.0 to fix crash ..." | Re-trigger Greptile

Comment on lines +770 to +798
try {
var messages = [{ role: "system", content: cfg.systemPrompt }].concat(buildContextualHistory());
var assistantContent = "";
var streamDiv = null;
var rawBuf = "";

try {
removeTypingEl();
streamDiv = el("div", "vai-message assistant");
if ($messagesEl) $messagesEl.appendChild(streamDiv);

await callVenice(messages, function (delta, full) {
rawBuf = full;
if (streamDiv) streamDiv.textContent = full;
if ($messagesEl) $messagesEl.scrollTop = $messagesEl.scrollHeight;
});

assistantContent = rawBuf;
if (streamDiv) {
streamDiv.textContent = "";
streamDiv.appendChild(renderMarkdown(assistantContent));
maybeAddFileCreation(streamDiv, assistantContent);
}
} catch (streamErr) {
if (streamDiv && streamDiv.parentNode) streamDiv.parentNode.removeChild(streamDiv);
removeTypingEl();
assistantContent = await callVenice(messages);
addAssistantMsg(assistantContent);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Double API call on any streaming-layer error

When callVenice throws because of a non-OK HTTP status (401 invalid key, 429 rate-limit, 500 server error, etc.), the error is thrown before the reader is opened. The inner catch (streamErr) silently swallows that error and immediately fires a second identical callVenice(messages) call at line 796, which will fail for the same reason. The user sees only the error from the second attempt, but two API requests have been charged against the account. The inner catch should re-throw errors that originate from a non-OK HTTP response rather than treating all errors as recoverable streaming failures.

Comment on lines +53 to +67
function loadSettings() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) return Object.assign(defaults(), JSON.parse(raw));
} catch (_) {}
return defaults();
}

const cfg = loadSettings();

function saveSettings() {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg));
} catch (_) {}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security Venice AI API key stored in plaintext localStorage

cfg.apiKey (including the raw Venice AI API key) is serialized into localStorage["venice_ai_wizard_settings"] via JSON.stringify. In the Acode WebView environment, any other plugin that runs in the same WebView context can read localStorage directly and extract the key without any user interaction. An API key leak allows an attacker to make authenticated requests to Venice AI billed to the victim's account. Ideally the key should be stored in a more isolated store (Acode's secure storage API if available), or at minimum users should be warned that the key is stored in accessible local storage.

Comment on lines +781 to +784
await callVenice(messages, function (delta, full) {
rawBuf = full;
if (streamDiv) streamDiv.textContent = full;
if ($messagesEl) $messagesEl.scrollTop = $messagesEl.scrollHeight;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 O(n²) streaming rendering on mobile

streamDiv.textContent = full is called on every streamed chunk, replacing the entire accumulated response string each time. For a max-4096-token response arriving in small chunks, this results in roughly O(n²) total character writes. On low-end Android devices (the primary Acode target), this can cause visible jank as responses grow longer. An incremental approach — appending only delta to a text node, or periodically flushing — would keep rendering O(n).

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +197 to +234
function buildContextualHistory() {
if (!conversationHistory.length) return [];
var history = conversationHistory.map(function (m) { return { role: m.role, content: m.content }; });

for (var i = history.length - 1; i >= 0; i--) {
if (history[i].role === "user") {
var original = history[i].content;
var parts = [];

if (cfg.includeCurrentFile) {
var ctx = getActiveFileContext();
if (ctx && ctx.content) {
parts.push("## Current file: " + ctx.filename + "\n```" + ctx.language + "\n" + ctx.content + "\n```");
}
}
if (cfg.includeSelection) {
var sel = getSelection();
if (sel) parts.push("## Selected text\n```\n" + sel + "\n```");
}
if (cfg.includeOpenFiles) {
var others = getOpenFilesContext();
if (others.length) {
var txt = others.map(function (f) {
return "### " + f.filename + "\n```" + f.language + "\n" + f.content + "\n```";
}).join("\n\n");
parts.push("## Other open files\n" + txt);
}
}

history[i] = {
role: "user",
content: parts.length ? parts.join("\n\n") + "\n\n---\n\n" + original : original,
};
break;
}
}
return history;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Unbounded conversation history grows request payload without limit

conversationHistory is never pruned. buildContextualHistory() also injects the full active file content into the last user message on every send. For a long session with a large file open (includeCurrentFile: true), the HTTP request body grows indefinitely — duplicate file content is prepended on every round-trip until the request exceeds Venice AI's context window or the API returns a 413/400. Consider capping history length (e.g. last N pairs) and only injecting file context into the very latest message rather than the full history.

@bajrangCoder

Copy link
Copy Markdown
Member

This is not the right place to publish your plugin, you should publish the plugin zip on https://acode.app

@github-project-automation github-project-automation Bot moved this from Backlog to Done in The Code Board - Acode Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants