Official

sidebar

Built-in sidebar UI (JavaScript UI plugin).

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/sidebar@0.2.0

Permissions in 0.2.0

  • Control chats chats.controlHighCreates chats, sends prompts and cancels runs.Add and remove workspaces, and create, rename, check off and archive chats
  • Read chats chats.readMediumReads your transcripts and chat lists.List workspaces and chats, and follow them as agents work
  • Show panels ui.slotsLowShows views in the listed parts of the window.Draw the sidebarSlots: left

Files

preview.test.ts3.7 KB
import assert from "node:assert/strict";
import test from "node:test";
import type { TranscriptItem } from "convergence";
import { createPreview, transcriptPreview, updatePreview } from "./preview.ts";

test("preview follows streamed lines, tools, and the final answer", () => {
  const preview = createPreview();
  updatePreview(preview, { event: "text_delta", itemId: "a", text: "Checking the\nconfiguration", mode: "append" });
  assert.equal(preview.line, "configuration");
  updatePreview(preview, { event: "text_delta", itemId: "a", text: " now.\n", mode: "append" });
  assert.equal(preview.line, "configuration now.");
  updatePreview(preview, {
    event: "tool_call_started",
    id: "tool",
    name: "read",
    kind: "read",
    title: "Read settings.json",
    status: "running",
  });
  assert.equal(preview.line, "Read settings.json");
  updatePreview(preview, { event: "tool_call_updated", id: "tool", title: "Read config/settings.json" });
  assert.equal(preview.line, "Read config/settings.json");
  updatePreview(preview, { event: "text_delta", itemId: "b", text: "Fixed the configuration.", mode: "append" });
  assert.equal(preview.line, "Fixed the configuration.");
  assert.equal(
    updatePreview(preview, { event: "tool_call_updated", id: "tool", status: "completed", title: "Read complete" }),
    false,
  );
  assert.equal(preview.line, "Fixed the configuration.");
  assert.equal(updatePreview(preview, { event: "run_finished", outcome: { status: "completed" } }), false);
});

test("replacement snapshots and reasoning retain the latest non-empty line", () => {
  const preview = createPreview();
  updatePreview(preview, { event: "reasoning_delta", itemId: "r", text: "Considering options", mode: "append" });
  assert.equal(preview.line, "Considering options");
  updatePreview(preview, {
    event: "reasoning_delta",
    itemId: "r",
    text: "Revised\nA simpler approach\n",
    mode: "replace",
  });
  assert.equal(preview.line, "A simpler approach");
  updatePreview(preview, { event: "text_delta", itemId: "a", text: "", mode: "append" });
  assert.equal(preview.line, "A simpler approach");
  updatePreview(preview, { event: "text_delta", itemId: "a", text: "x".repeat(2000), mode: "append" });
  assert.equal(preview.text.length, 512);
});

test("history restores activity without letting user messages or notices replace it", () => {
  const items: TranscriptItem[] = [
    { id: "a", role: "assistant", text: "First line\nFinal line\n" },
    {
      id: "tool",
      role: "tool",
      call: { id: "tool", name: "edit", kind: "edit", title: "Edit app.rs", status: "failed" },
    },
    { id: "notice", role: "notice", text: "Error" },
    { id: "user", role: "user", blocks: [] },
  ];
  assert.equal(transcriptPreview(items).line, "Edit app.rs");
  assert.equal(
    transcriptPreview([...items, { id: "answer", role: "assistant", text: "Done\nTests pass" }]).line,
    "Tests pass",
  );
  assert.equal(transcriptPreview([]).line, "");
  const other = createPreview();
  assert.equal(other.line, "");
});

test("a message's line is shown without its Markdown marks", () => {
  const line = (text: string) => {
    const preview = createPreview();
    updatePreview(preview, { event: "text_delta", itemId: "a", text, mode: "replace" });
    return preview.line;
  };
  assert.equal(line("**There are 8 files.**"), "There are 8 files.");
  assert.equal(line("Files: `index.html`, `style.css`, `pool.js`."), "Files: index.html, style.css, pool.js.");
  assert.equal(line("Done.\n- See [the guide](https://example.com) for **more**"), "See the guide for more");
  assert.equal(line("## Summary"), "Summary");
  // Mid-stream, an unclosed mark goes too.
  assert.equal(line("**Checking the con"), "Checking the con");
});

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <320.2 KB3 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.