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
workflows.test.ts5.3 KB
// Regression coverage for workflows using the real chat modules.
// Fixtures exercise the same behavior and assertions as the JavaScript tests.
import { item as fixtureItem, fixtureTask } from "./ui-for-tests.test.ts";
import assert from "node:assert/strict";
import test from "node:test";
import {
isWorkflow,
agentsOf,
phasesOf,
currentPhase,
displayStatus,
stripOf,
progressLabel,
phaseDetail,
phaseOpen,
resultView,
modelName,
keywordsIn,
} from "./workflows.ts";
const agent = (
index: number,
phase: string,
status: import("convergence").TaskStatus,
extra: Parameters<typeof fixtureTask>[1] = {},
) =>
fixtureItem({
id: `task-wf/${index}`,
role: "task",
task: { id: `wf/${index}`, title: `agent ${index}`, status, phase, parentTaskId: "wf", ...extra },
items: [],
});
const run = (
status: import("convergence").TaskStatus,
agents: import("./grouping.ts").TaskItem[],
phases = ["Find", "Verify"],
) =>
fixtureItem({
id: "task-wf",
role: "task",
task: {
id: "wf",
title: "Review the branch",
status,
workflow: { logs: [], phases: phases.map((title) => ({ title })) },
},
items: [fixtureItem({ id: "a", role: "assistant", text: "not an agent" }), ...agents],
});
test("a workflow's agents sit in the phases the script declared, in order", () => {
const item = run("running", [
agent(1, "Find", "completed"),
agent(2, "Find", "running"),
agent(3, "Extra", "queued"),
]);
assert.ok(isWorkflow(item.task));
assert.equal(agentsOf(item).length, 3);
const phases = phasesOf(item);
assert.deepEqual(
phases.map((phase) => phase.title),
["Find", "Verify", "Extra"],
"an undeclared phase goes last",
);
assert.deepEqual(
phases.map((phase) => phase.state),
["running", "pending", "queued"],
);
assert.equal(phases[0].tally.done, 1);
assert.equal(currentPhase(phases)?.title, "Find");
});
test("the phase a run has reached is the last one an agent started in", () => {
const item = run("running", [agent(1, "Find", "completed"), agent(2, "Verify", "running")]);
const phases = phasesOf(item);
assert.equal(currentPhase(phases)?.title, "Verify");
assert.equal(progressLabel(item, phases), "Phase 2 of 2 · Verify · 1/2 agents");
const done = run("completed", [agent(1, "Find", "completed"), agent(2, "Verify", "failed")]);
assert.equal(
progressLabel(done, phasesOf(done)),
"Phase 2 of 2 · 1/2 agents · 1 failed",
"no phase name once it ended",
);
});
test("an agent that needs the user makes the whole run need the user", () => {
assert.equal(displayStatus(run("running", [agent(1, "Find", "waiting")])), "waiting");
assert.equal(displayStatus(run("running", [agent(1, "Find", "running")])), "running");
assert.equal(
displayStatus(run("cancelled", [agent(1, "Find", "waiting")])),
"cancelled",
"an ended run says how it ended",
);
});
test("the strip has a segment per agent and a placeholder per phase not reached", () => {
const strip = stripOf(phasesOf(run("running", [agent(1, "Find", "completed"), agent(2, "Find", "running")])));
assert.equal(strip.length, 2);
assert.deepEqual(
strip[0].segments.map((segment) => segment.tone),
["success", "primary"],
);
assert.equal(strip[1].segments.length, 1);
assert.equal(strip[1].segments[0].surface, "muted");
});
test("phase rows say what they wait for and open while something happens", () => {
const phases = phasesOf(
run("running", [
agent(1, "Find", "completed", { usage: { usedTokens: 12_000 } }),
agent(2, "Find", "waiting"),
agent(3, "Find", "queued"),
]),
);
assert.equal(phaseDetail(phases[0]), "1 needs you · 1 queued · 1/3 · 12k tok");
assert.equal(phaseDetail(phases[1]), "Not started");
assert.equal(phaseOpen(phases[0]), true);
assert.equal(phaseOpen(phases[1]), false);
});
test("a result is text when the script returned text, and JSON otherwise", () => {
assert.deepEqual(resultView("All good."), { kind: "text", text: "All good." });
assert.deepEqual(resultView({ a: 1 }), { kind: "json", text: '{\n "a": 1\n}' });
assert.equal(resultView(null), null);
assert.equal(resultView(" "), null);
});
test("a model id reads as the agent's name for it", () => {
const option = {
choices: [
{ value: "claude-haiku-4-5", name: "Haiku 4.5", aliases: ["haiku"] },
{ value: "claude-opus-5-5[1m]", name: "Opus 5.5" },
],
};
assert.equal(modelName(option, "claude-haiku-4-5-20251001"), "Haiku 4.5");
assert.equal(modelName(option, "haiku"), "Haiku 4.5");
assert.equal(modelName(option, "claude-opus-5-5"), "Opus 5.5");
assert.equal(modelName(option, "gpt-9"), "gpt-9");
assert.equal(modelName(undefined, null), null);
});
test("a keyword counts only as a word of its own", () => {
const keywords = [{ word: "ultracode", label: "Ultracode" }];
assert.equal(keywordsIn("ultracode: review the branch", keywords).length, 1);
assert.equal(keywordsIn("Please ULTRACODE this", keywords).length, 1);
assert.equal(keywordsIn("(ultracode)", keywords).length, 1);
for (const text of ["/ultracode", "--ultracode", "ultracode.ts", "ultracoder", "src/ultracode/x", "my-ultracode"]) {
assert.equal(keywordsIn(text, keywords).length, 0, text);
}
assert.deepEqual(keywordsIn("", keywords), []);
assert.deepEqual(keywordsIn("ultracode", []), []);
});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.