Official

chat

Built-in chat 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/chat@0.2.0

Permissions in 0.2.0

  • Control chats chats.controlHighCreates chats, sends prompts and cancels runs.Send prompts, answer agent questions, and create, change, fork, revert, stop and group chats
  • Run other plugins' commands commands.runHighRuns commands of other plugins.Run the sidebar's New chat command from the title bar
  • Read chats chats.readMediumReads your transcripts and chat lists.Show workspaces, tasks, chats and transcripts, and follow them as agents work
  • Read files fs.readMediumReads files in the listed places.Suggest workspace files for @ mentionsPlaces: the open workspace
  • Show panels ui.slotsLowShows views in the listed parts of the window.Draw the chat grid and the title barSlots: centertitle

Files

state.ts4.1 KB
// Chat state belongs to the plugin scope; each pane reads only its chat's transcript.
// Drafts and queues retain their baseline storage and shared-state names.
import type {
  AgentInfo,
  ApprovalRequest,
  AttachedImage,
  Chat,
  ComboMark,
  ConfigOption,
  Element,
  Plan,
  QuestionRequest,
  SecurityRequest,
  Skill,
  SlashCommand,
  TranscriptItem,
  Usage,
  ViewHandle,
  Workspace,
} from "convergence";
import type { Cycle } from "./switcher.ts";

export type UserItem = Extract<TranscriptItem, { role: "user" }>;
export type TextItem = Extract<TranscriptItem, { role: "assistant" | "reasoning" }>;
export type ToolItem = Extract<TranscriptItem, { role: "tool" }>;
export type Question = QuestionRequest & { submitting?: boolean };
export type Prompt = { text: string; attachments: readonly { id: string; name?: string }[]; blocks?: readonly import("convergence").ContentBlock[]; id?: string };
/// A draft waiting to start. Its prompt stays in its composer, editable,
/// until no chat runs; then the queue sends it.
export type QueuedChat = { chatId: string };
export type Composer = {
  text: string;
  /// The agent the options, commands and skills below are from.
  agentId: string | null;
  options: readonly ConfigOption[];
  commands: readonly SlashCommand[];
  skills: readonly Skill[];
  loading: boolean;
  picker: { kind: "commands" | "skills" | "files"; query: string; index?: number } | null;
  files: readonly string[];
  queue: Prompt[];
  attachments: AttachedImage[];
  submitting?: boolean;
  /// The input this composer is handing to the agent: it is already
  /// acknowledged, so it shows as the user's message, not as held input.
  dispatching?: string;
  error?: string;
  submission?: { id: string; signature: string };
  pendingEdit?: { id: string; revision: number; text: string };
};
export type Transcript = {
  items: TranscriptItem[];
  approvals: ApprovalRequest[];
  questions: Question[];
  requests: readonly SecurityRequest[];
  plan: Plan | null;
  running: boolean;
  checkpoints: Set<string>;
  expanded: Set<string>;
  folds: Map<string, boolean>;
  answers: Record<string, Record<string, unknown>>;
  editing: string | null;
  editText: string;
  loaded: boolean;
  usage?: Usage;
  finished?: Set<string>;
};
export type Pane = { chatId: string; view: ViewHandle };
export type ActivityProps = {
  icon?: string | Element;
  label?: string;
  detail?: string;
  extra?: Element | null;
  status?: Element | null;
  open?: boolean;
  onClick?: () => void;
  tone?: string;
  compact?: boolean;
};
/** A workspace picker's trigger: the workspace's name and mark, and whether it can change. The kernel's combobox opens the list. */
export type AnchorProps = { name: string; mark: ComboMark; can: boolean };
export class ChatState {
  chats: Record<string, Chat> = {};
  agents: Record<string, AgentInfo> = {};
  agentList: readonly AgentInfo[] = [];
  composers: Record<string, Composer> = {};
  transcripts: Record<string, Transcript> = {};
  workspaces: Record<string, Workspace> = {};
  // Pane views by chat id, the most recently shown first in recentPanes.
  panes: Record<string, Pane> = {};
  recentPanes: string[] = [];
  paneViews: Record<string, { chatId: string; view: ViewHandle }> = {};
  chatQueue: QueuedChat[] = [];
  // The queued draft just sent: it holds the queue until the host reports
  // it running, so a second chat cannot start in the gap.
  releasing: string | null = null;
  wsPicker: Record<string, boolean> = {};
  recent: string[] = [];
  itemImages: Record<string, string[] | "pending"> = {};
  switcher: Cycle | null = null;
  promptedChat: string | null = null;
  historyRead: Record<string, number> = {};
  historyChat: string | null = null;
  restored: Record<string, string> | null = null;
  saving = false;
  unsaved = false;
  draftTitles: Record<string, string> = {};
  ticking = false;
}
export function initialTranscript(): Transcript {
  return {
    items: [],
    approvals: [],
    questions: [],
    requests: [],
    plan: null,
    running: false,
    checkpoints: new Set(),
    expanded: new Set(),
    folds: new Map(),
    answers: {},
    editing: null,
    editText: "",
    loaded: false,
  };
}

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <3105.1 KB5 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.