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
Files
composition.ts2.3 KB
// Capture a complete structured composition before acknowledging or clearing it.
// Reading one attachment fails the submission, never silently sends a subset.
import * as Effect from "effect/Effect";
import type { Chat, ContentBlock } from "convergence";
import type { Controller } from "./controller.ts";
import type { Prompt } from "./state.ts";
import { mentionInstructions, tagTokens } from "./mentions.ts";
let sequence = 0;
export const inputId = () => `chat:${Date.now()}:${++sequence}:${Math.random().toString(36).slice(2)}`;
export function steeringReason(c: Controller, chat: Chat): string | undefined {
if (!c.isRunning(chat)) return;
if (c.scheduler.available === false) return "Active steering needs an updated app; Queue retains the prompt";
const capabilities = c.state.agents[chat.agentId]?.capabilities;
if (!capabilities?.steer && !capabilities?.simulatedSteer) return "This agent does not support active steering; use Queue or Stop";
}
export const blocksFor = Effect.fn("Chat.blocksFor")(function* (c: Controller, chatId: string, text: string, attachments: Prompt["attachments"]) {
const composer = c.composer(chatId);
const blocks: ContentBlock[] = [];
const [name = "", ...rest] = text.slice(1).split(/\s+/);
if (text.startsWith("$") && composer.skills.some((skill) => skill.name === name))
blocks.push({ type: "skill", name, input: rest.join(" ") });
else if (text) blocks.push({ type: "text", text });
const mentions = c.api.mentions.list(), tags = tagTokens(text, mentions);
for (const token of text.split(/\s+/)) {
if (tags.has(token)) continue;
if (token.startsWith("@") && token.length > 1 && /[./]/.test(token))
blocks.push({ type: "file_ref", path: token.slice(1).replace(/[,.;:]+$/, "") });
}
const instructions = mentionInstructions(text, mentions);
if (instructions) blocks.push({ type: "text", text: instructions });
for (const image of attachments) {
const { mimeType, data } = yield* c.kernel.call("image_data", { id: image.id });
blocks.push({ type: "image", mimeType, data });
}
return blocks;
});
export function amendedInput(snapshot: import("convergence").InputSnapshot, id: string) {
return snapshot.inputs.find((input) => input.id === id) ?? snapshot.inputs.filter((input) => input.intent === "steer" && input.state === "held").at(-1);
}Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 105.1 KB | 5 permissions | Listed |
No comments yet.