Files
paseo-bot[bot]and见安 114389383d Keep files starting with two dots usable (#6604)
* fix(file-explorer): distinguish parent traversal from dot-prefixed names

* test(file-explorer): cover dot-prefixed paths through daemon RPC

* test(plugins): allow slower session readiness in provider fixtures

The event helper exhausted 200 iterations before a valid delayed session
became ready. Use an elapsed-time deadline while preserving real timers
for watchdog tests, and retain a delayed-start regression with launch
flag assertions.

Co-authored-by: 见安 <73627805+Mar-garet@users.noreply.github.com>

---------

Co-authored-by: 见安 <73627805+Mar-garet@users.noreply.github.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-11 16:51:00 +02:00

1746 lines
66 KiB
TypeScript

import { execFile } from "node:child_process";
import { UsageSourceRegistry } from "../../../packages/server/src/server/plugins/usage-sources/index.js";
import { Usage as MuseUsage } from "../server/usage.js";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { setTimeout as delay } from "node:timers/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, expect, test, vi } from "vitest";
import {
PROVIDER_CAPABILITIES,
ProviderEventSchema,
type ProviderConnection,
type ProviderEvent,
type ProviderRegistration,
type ProviderLaunch,
type ProviderInput,
} from "@getpaseo/plugin/server/provider";
import type { UsageSourceRegistration } from "@getpaseo/plugin/server";
import { execCommand } from "@getpaseo/plugin/server";
import contribute from "../index.server.js";
const connections: ProviderConnection[] = [];
const roots: string[] = [];
afterEach(async () => {
vi.useRealTimers();
await Promise.all(connections.splice(0).map((connection) => connection.close()));
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
function registration(
registerUsageSource = (_source: UsageSourceRegistration) => {},
): ProviderRegistration {
let provider: ProviderRegistration | undefined;
const server = {
registerUsageSource,
registerProvider(value: ProviderRegistration) {
provider = value;
},
};
contribute(server);
if (!provider) throw new Error("Provider was not registered");
return provider;
}
async function harness(
scenario = "text-reasoning",
env: Record<string, string> = {},
providerOptions?: Record<string, unknown>,
) {
const root = await mkdtemp(path.join(os.tmpdir(), "muse-provider-test-"));
roots.push(root);
const requests = path.join(root, "requests.ndjson");
const launch: ProviderLaunch = {
command: process.execPath,
args: [fileURLToPath(new URL("./fake-muse.mjs", import.meta.url))],
env: { MUSE_TEST_SCENARIO: scenario, MUSE_TEST_REQUESTS: requests, ...env },
};
await writeFile(requests, "");
let usageSource!: UsageSourceRegistration;
const provider = registration((source) => {
usageSource = source;
});
const connection = await provider.connect({
launch,
versions: [1],
capabilities: PROVIDER_CAPABILITIES,
});
connections.push(connection);
const events: ProviderEvent[] = [];
connection.onEvent((event) => {
events.push(ProviderEventSchema.parse(event));
});
async function wait(
predicate: (event: ProviderEvent) => boolean,
from = 0,
): Promise<ProviderEvent> {
const deadline = Date.now() + 5000;
while (Date.now() < deadline) {
const event = events.slice(from).find(predicate);
if (event) return event;
await delay(10);
}
throw new Error(`Missing event; received ${JSON.stringify(events)}`);
}
async function send(input: ProviderInput) {
const from = events.length;
await connection.send(input);
return from;
}
async function open(
persistence?: Extract<ProviderInput, { type: "session.open" }>["persistence"],
mode = "onRequest",
model: string | null = "meta/muse-spark-1.3",
) {
await send({
type: "session.open",
requestId: "open",
sessionId: "paseo-session",
config: {
cwd: root,
providerOptions,
env: {},
settings: {},
mcpServers: {},
persist: true,
mode,
model: model ?? undefined,
thinkingOption: "high",
systemPrompt: "Test instructions",
},
persistence,
history: "replay",
});
return wait((event) => event.type === "session.ready" || event.type === "request.failed");
}
async function prompt(delivery: "auto" | "steer" = "auto", image = false) {
return send({
type: "session.prompt",
sessionId: "paseo-session",
prompt: {
clientMessageId: `client-${events.length}`,
delivery,
input: {
type: "message",
content: image
? [
{ type: "text", text: "hello" },
{ type: "image", mimeType: "image/png", data: "image-bytes" },
]
: [{ type: "text", text: "hello" }],
},
},
});
}
async function recorded() {
return (await readFile(requests, "utf8"))
.trim()
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line));
}
return {
provider,
launch,
connection,
events,
wait,
send,
open,
prompt,
recorded,
root,
usageSource,
};
}
test("registration exposes Muse with a daemon-resolved command", () => {
expect(registration()).toMatchObject({
id: "muse",
label: "Muse Code",
command: ["muse"],
icon: "icon.svg",
});
});
test("catalog uses model effort variants, modes, and bounded cached discovery", async () => {
const h = await harness("catalog-controls");
await h.send({ type: "catalog", requestId: "catalog-1" });
const catalogEvent = await h.wait((candidate) => candidate.type === "catalog");
expect(catalogEvent).toMatchObject({
catalog: {
models: [
{
id: "meta/muse-spark-1.3",
label: "meta/muse-spark-1.3",
defaultThinkingOptionId: "high",
thinkingOptions: [
{ id: "minimal" },
{ id: "low" },
{ id: "medium" },
{ id: "high" },
{ id: "xhigh" },
],
},
],
defaultMode: "onRequest",
modes: [
{ id: "onRequest" },
{ id: "promptUnmatched" },
{ id: "denyUnmatched" },
{ id: "allowAll", isUnattended: true },
],
},
});
await h.send({ type: "catalog", requestId: "catalog-2" });
await h.wait((event) => event.type === "catalog" && event.requestId === "catalog-2");
expect((await h.recorded()).filter((frame) => frame.method === "model/list")).toHaveLength(1);
});
test("text and reasoning stream, completion overwrites deltas, user echo retains client identity", async () => {
const h = await harness();
expect(await h.open()).toMatchObject({ type: "session.ready" });
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(h.events).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({ type: "reasoning" }),
}),
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
type: "user_message",
clientMessageId: expect.stringMatching(/^client-/),
}),
}),
]),
);
const frames = await h.recorded();
expect(frames.find((frame) => frame.method === "turn/start").params).toMatchObject({
ifBusy: "queue",
reasoningEffort: "high",
displayText: "hello",
input: [
{ type: "text", text: "<system_instructions>\nTest instructions\n</system_instructions>" },
{ type: "text", text: "hello" },
],
});
expect(h.events.findLast((event) => event.type === "session.persistence")).toMatchObject({
persistence: { version: 1, data: { model: "meta/muse-spark-1.3", thinkingOption: "high" } },
});
});
test("tool calls classify read, shell, and edit with fetched JSON patch converted to unified diff", async () => {
const h = await harness("tools-edit");
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(h.events).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
detail: expect.objectContaining({ type: "read", filePath: "f" }),
}),
}),
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
detail: expect.objectContaining({ type: "shell", command: "wc -l f", output: "2 f\n" }),
}),
}),
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
detail: expect.objectContaining({
type: "edit",
unifiedDiff: expect.stringContaining("@@ -1,2 +1,2 @@\n alpha\n-beta\n+gamma\n"),
}),
}),
}),
]),
);
});
test("multi-stage approvals surface Stage 2 then Stage 3 and send the current requirement guard", async () => {
const h = await harness("multi-stage-approval");
await h.open();
await h.prompt();
const first = await h.wait((event) => event.type === "session.permission");
expect(first).toMatchObject({
request: {
title: "Stage 2 of 3",
detail: { type: "shell", command: "rm -rf f" },
actions: [
{ id: "allow_once", behavior: "allow" },
{ id: "allow_local_prefix", behavior: "allow" },
{ id: "abort", behavior: "deny" },
],
},
});
if (first.type !== "session.permission") throw new Error("Expected approval");
const from = await h.send({
type: "session.permission",
sessionId: "paseo-session",
permissionId: first.request.id,
response: { behavior: "allow", selectedActionId: first.request.actions![0]!.id },
});
const second = await h.wait((event) => event.type === "session.permission", from);
expect(second).toMatchObject({
request: { title: "Stage 3 of 3", detail: { command: "rm -rf g" } },
});
await h.send({
type: "session.permission",
sessionId: "paseo-session",
permissionId: first.request.id,
response: { behavior: "allow", selectedActionId: first.request.actions![0]!.id },
});
await h.wait((event) => event.type === "session.permission_resolved");
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(
(await h.recorded())
.filter((frame) => frame.method === "approval/decide")
.map((frame) => frame.params.requirementId.sourceIndex),
).toEqual([1, 2]);
});
test("Full access automatically answers every escalated stage with the published allow-once choice", async () => {
const h = await harness("multi-stage-approval");
await h.open(undefined, "allowAll");
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(h.events.filter((event) => event.type === "session.permission")).toEqual([]);
expect(
(await h.recorded())
.filter((frame) => frame.method === "approval/decide")
.map((frame) => frame.params.choiceId),
).toEqual(["allow_once", "allow_once"]);
});
test("interrupt awaits cancelled terminal and reconciles the tool", async () => {
const h = await harness("interrupt");
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.prompt_result");
await h.send({ type: "session.interrupt", sessionId: "paseo-session", requestId: "interrupt" });
await h.wait((event) => event.type === "request.completed" && event.requestId === "interrupt");
const terminalIndex = h.events.findIndex(
(event) => event.type === "session.turn" && event.state === "canceled",
);
expect(terminalIndex).toBeGreaterThan(-1);
expect(
h.events.findIndex(
(event) => event.type === "request.completed" && event.requestId === "interrupt",
),
).toBeGreaterThan(terminalIndex);
});
test("a fresh connection restores earlier messages from persisted session state", async () => {
const original = await harness();
const historyPath = path.join(original.root, "native-history.ndjson");
original.launch.env.MUSE_TEST_HISTORY = historyPath;
await original.open();
await original.prompt();
await original.wait((event) => event.type === "session.turn" && event.state === "completed");
const messages = [
...new Map(
original.events
.filter((event) => event.type === "timeline.item")
.map((event) => event.item)
.filter((item) => item.type === "user_message" || item.type === "assistant_message")
.map((item) => [item.id, item] as const),
).values(),
];
expect(messages.map((item) => item.type)).toEqual(["user_message", "assistant_message"]);
const saved = original.events.findLast((event) => event.type === "session.persistence");
if (!saved) throw new Error("Expected session persistence");
const persistencePath = path.join(original.root, "persistence.json");
// Older daemon records carry a view cursor even though their timeline is not durable.
await writeFile(
persistencePath,
JSON.stringify({
...saved.persistence,
data: { ...saved.persistence.data, cursor: saved.persistence.data.cursor ?? "v:old:7" },
}),
);
await original.connection.close();
const reopened = await harness("text-reasoning", { MUSE_TEST_HISTORY: historyPath });
expect(await reopened.open(JSON.parse(await readFile(persistencePath, "utf8")))).toMatchObject({
type: "session.ready",
});
const restored = reopened.events
.filter((event) => event.type === "timeline.item")
.map((event) => event.item)
.filter((item) => item.type === "user_message" || item.type === "assistant_message");
expect(restored.map((item) => ({ type: item.type, text: item.text }))).toEqual(
messages.map((item) => ({ type: item.type, text: item.text })),
);
const reopenedState = reopened.events.findLast((event) => event.type === "session.persistence");
expect(reopenedState?.persistence.data).not.toHaveProperty("cursor");
expect(reopened.events.filter((event) => event.type === "session.turn")).toEqual([]);
await reopened.prompt();
await reopened.wait((event) => event.type === "session.turn" && event.state === "completed");
});
for (const cursor of [undefined, "v:old:7"]) {
test(`resume ${cursor ? "with a legacy persistence cursor" : "without a persistence cursor"} replays inline history`, async () => {
const h = await harness("resume-without-cursor");
await h.open({
version: 1,
data: { sessionId: "saved-session", ...(cursor ? { cursor } : {}) },
});
expect(h.events.filter((event) => event.type === "session.turn")).toEqual([]);
const history = h.events.filter((event) => event.type === "timeline.item");
expect(history).toEqual(
expect.arrayContaining([
expect.objectContaining({
item: expect.objectContaining({ type: "assistant_message", text: "RESUME_MARKER_OK" }),
}),
]),
);
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(
h.events.filter((event) => event.type === "session.turn" && event.state === "completed"),
).toHaveLength(1);
expect(
(await h.recorded()).find((frame) => frame.method === "session/resume").params,
).toMatchObject({ sessionId: "saved-session" });
expect(
(await h.recorded()).find((frame) => frame.method === "session/resume").params,
).not.toHaveProperty("cursor");
});
}
test("steer sends ifBusy steer and joins the running turn", async () => {
const h = await harness("steer");
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.prompt_result");
const from = await h.prompt("steer");
expect(await h.wait((event) => event.type === "session.prompt_result", from)).toMatchObject({
result: { type: "steer" },
});
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(
(await h.recorded()).filter((frame) => frame.method === "turn/start")[1].params.ifBusy,
).toBe("steer");
});
test("image turn sends a native image and the subsequent text turn succeeds", async () => {
const h = await harness("image-followup");
await h.open();
await h.prompt("auto", true);
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
const from = await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed", from);
const turns = (await h.recorded()).filter((frame) => frame.method === "turn/start");
expect(turns[0].params.input).toContainEqual({
type: "image",
mediaType: "image/png",
base64Data: "image-bytes",
});
expect(turns[1].params.input).toEqual([{ type: "text", text: "hello" }]);
});
for (const source of ["live", "history", "backfill"]) {
test(`reminder housekeeping emits no timeline items or child sessions from ${source}`, async () => {
const scenario = source === "history" ? "resume-without-cursor" : "text-reasoning";
const frames = (
await readFile(new URL(`./fixtures/${scenario}.ndjson`, import.meta.url), "utf8")
)
.trim()
.split("\n")
.map((line) => JSON.parse(line));
const reminderIds = new Set<string>(
frames
.flatMap(({ msg }) =>
msg.params?.item ? [msg.params.item] : (msg.result?.history?.items ?? []),
)
.filter((item) => item?.kind === "reminderChild")
.map((item) => item.itemId),
);
expect(reminderIds.size).toBeGreaterThan(0);
const h = await harness(scenario, source === "backfill" ? { MUSE_TEST_GAP: "1" } : {});
await h.open(
source === "history" ? { version: 1, data: { sessionId: "saved-session" } } : undefined,
);
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(
h.events.filter((event) => event.type === "timeline.item" && reminderIds.has(event.item.id)),
).toEqual([]);
expect(
h.events.filter((event) => event.type === "session.opened").map((event) => event.sessionId),
).toEqual(["paseo-session"]);
expect((await h.recorded()).filter((frame) => frame.method === "session/read")).toEqual([]);
});
}
for (const [scenario, code, guidance] of [
["auto-review-unavailable", "defaultProfileUnavailable", "permissions.default_profile"],
["unsafe-path", "unsafePath", path.join("/test-data", "muse")],
]) {
test(`${scenario} is a typed actionable construction error`, async () => {
const h = await harness(scenario, { XDG_DATA_HOME: "/test-data" });
expect(await h.open()).toMatchObject({
type: "request.failed",
error: { code, message: expect.stringContaining(guidance!) },
});
});
}
for (const [code, kind] of [
[0, "cleanExit"],
[2, "usage"],
[3, "credentials"],
[4, "leaseHeld"],
[5, "surfaceOff"],
[17, "crash"],
]) {
test(`exit ${code} is classified ${kind} with stderr diagnostics`, async () => {
const h = await harness("text-reasoning", { MUSE_TEST_EXIT: String(code) });
expect(await h.open()).toMatchObject({
type: "request.failed",
error: { code: kind, message: expect.stringContaining("diagnostic tail, not protocol") },
});
});
}
for (const [env, expected] of [
[
{ MUSE_TEST_VERSION: "1.2.9" },
{ available: false, diagnostic: "Update Muse Code: found 1.2.9, need ≥1.3.0" },
],
[
{ MUSE_TEST_ACCOUNT: "loggedOut" },
{ available: false, diagnostic: "Run `muse login` or set META_API_KEY" },
],
[{ MUSE_TEST_ACCOUNT: "envKey" }, { available: true }],
[{ MUSE_TEST_ACCOUNT: "apiKey" }, { available: true }],
[{ MUSE_TEST_ACCOUNT: "accountLogin" }, { available: true }],
] as const) {
test(`status checks version and credential state ${JSON.stringify(env)}`, async () => {
const h = await harness("catalog-controls", env);
expect(await h.provider.status!({ launch: h.launch })).toEqual(expected);
});
}
test("status preserves the version-probe command and stderr diagnostic", async () => {
const h = await harness("catalog-controls", {
MUSE_TEST_VERSION_ERROR: "Muse version probe could not read its installation metadata",
});
// Compare against the original execFile callback: wrapping in MuseError only changed its kind.
const previousDiagnostic = await new Promise<string>((resolve, reject) => {
execFile(h.launch.command, [...h.launch.args, "--version"], { env: h.launch.env }, (error) => {
if (error) resolve(error.message);
else reject(new Error("Expected the version probe to fail"));
});
});
const status = await h.provider.status!({ launch: h.launch });
expect(status).toEqual({ available: false, diagnostic: previousDiagnostic });
expect(status.diagnostic).toContain("--version");
expect(status.diagnostic).toContain(
"Muse version probe could not read its installation metadata",
);
expect(await h.recorded()).toEqual([]);
});
test("status reports a missing executable", async () => {
const h = await harness();
expect(
await h.provider.status!({ launch: { ...h.launch, command: "/missing/muse" } }),
).toMatchObject({ available: false, diagnostic: expect.stringContaining("ENOENT") });
});
test("overloaded retry reuses UUIDv7 commandId", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_RETRY: "turn/start" });
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
const turns = (await h.recorded()).filter((frame) => frame.method === "turn/start");
expect(turns).toHaveLength(2);
expect(turns[0].params.commandId).toBe(turns[1].params.commandId);
});
test("missing saved session refuses to start fresh", async () => {
const h = await harness("resume-without-cursor", { MUSE_TEST_GONE: "1" });
expect(await h.open({ version: 1, data: { sessionId: "gone" } })).toMatchObject({
type: "request.failed",
error: {
code: "sessionNotFound",
message: "This Muse session no longer exists. Create a new agent.",
},
});
expect((await h.recorded()).filter((frame) => frame.method === "session/start")).toEqual([]);
});
test("empty resume viewCursor is a typed error", async () => {
const h = await harness("resume-without-cursor", { MUSE_TEST_EMPTY_CURSOR: "1" });
expect(await h.open({ version: 1, data: { sessionId: "saved" } })).toMatchObject({
type: "request.failed",
error: { code: "emptyViewCursor" },
});
});
test("catalogue identity includes command and routing credentials without exposing them", async () => {
const h = await harness();
const first = await h.provider.getCatalogCacheKey!({ scope: "global", launch: h.launch });
const second = await h.provider.getCatalogCacheKey!({
scope: "global",
launch: { ...h.launch, env: { ...h.launch.env, META_API_KEY: "secret" } },
});
expect(first).not.toBe(second);
expect(second).not.toContain("secret");
});
test("configure reports the effective model, approval mode, and per-turn effort", async () => {
const h = await harness("catalog-controls");
await h.open();
const from = await h.send({
type: "session.configure",
requestId: "configure",
sessionId: "paseo-session",
changes: { model: "meta/muse-spark-1.3", mode: "promptUnmatched", thinkingOption: "low" },
});
await h.wait((event) => event.type === "request.completed" && event.requestId === "configure");
expect(h.events.slice(from)).toContainEqual(
expect.objectContaining({
type: "session.config",
config: expect.objectContaining({
model: "meta/muse-spark-1.3",
mode: "promptUnmatched",
thinkingOption: "low",
models: [expect.objectContaining({ id: "meta/muse-spark-1.3" })],
}),
}),
);
});
test("CLI-style allow response selects a published allow action without a supplied id", async () => {
const h = await harness("staged-approval");
await h.open();
await h.prompt();
const permissionEvent = await h.wait((candidate) => candidate.type === "session.permission");
if (permissionEvent.type !== "session.permission") throw new Error("Expected approval");
await h.send({
type: "session.permission",
sessionId: "paseo-session",
permissionId: permissionEvent.request.id,
response: { behavior: "allow" },
});
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(
(await h.recorded()).find((frame) => frame.method === "approval/decide").params.choiceId,
).toBe("allow_once");
});
test("closing cancels a catalogue host instead of waiting for a stalled request", async () => {
const h = await harness("catalog-controls", { MUSE_TEST_HANG: "model/list" });
await h.send({ type: "catalog", requestId: "catalog" });
await expect
.poll(async () => (await h.recorded()).some((frame) => frame.method === "model/list"))
.toBe(true);
await h.connection.close();
expect(h.events.filter((event) => event.type === "catalog")).toEqual([]);
expect(await h.recorded()).toContainEqual({ event: "hostClosed" });
});
for (const retryKind of ["overloaded", "backpressured"]) {
test(`${retryKind} session construction retries with the same UUIDv7`, async () => {
const h = await harness("text-reasoning", {
MUSE_TEST_RETRY: "session/start",
MUSE_TEST_RETRY_KIND: retryKind,
});
expect(await h.open()).toMatchObject({ type: "session.ready" });
const attempts = (await h.recorded()).filter((frame) => frame.method === "session/start");
expect(attempts).toHaveLength(2);
expect(attempts[0].params.commandId).toBe(attempts[1].params.commandId);
});
}
test("higher-revision item completion replaces streamed text and ignores stale snapshots", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_STALE: "1" });
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
const assistant = h.events.filter(
(event) => event.type === "timeline.item" && event.item.type === "assistant_message",
);
expect(assistant.length).toBeGreaterThan(2);
expect(assistant.at(-1)).toMatchObject({
item: { text: expect.stringContaining("`147/60 = 49/20`") },
});
expect(assistant).not.toContainEqual(
expect.objectContaining({ item: expect.objectContaining({ text: "STALE_REVISION" }) }),
);
expect(h.events).toContainEqual(
expect.objectContaining({
type: "timeline.item",
item: {
type: "reasoning",
id: "f3c7d3fc-1644-46c3-b63c-74e0c7dee6c3",
text: "Computing the rational sum by finding a common denominator and reducing the fraction to lowest terms via gcd.",
},
}),
);
});
test("ordinary subagent tool calls render sub_agent details without inventing child identity", async () => {
const h = await harness("subagent");
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(h.events).toContainEqual(
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
name: "subagent_read_result",
status: "completed",
detail: expect.objectContaining({
type: "sub_agent",
log: expect.stringContaining("SUBAGENT_PHASE0_OK"),
}),
}),
}),
);
});
for (const [variants, expected] of [
[
["low", "high"],
["low", "high"],
],
["unknown", ["minimal", "low", "medium", "high", "xhigh", "max", "ultra"]],
[[], ["minimal", "low", "medium", "high", "xhigh", "max", "ultra"]],
] as const) {
test(`catalogue effort discovery with variants ${JSON.stringify(variants)}`, async () => {
const model = {
modelId: "test-model",
providerId: "meta",
displayLabel: "Test model",
contextLimit: 123456,
isDefault: true,
variants,
defaultReasoningEffort: "high",
};
const h = await harness("catalog-controls", { MUSE_TEST_MODELS: JSON.stringify([model]) });
await h.send({ type: "catalog", requestId: "models" });
const result = await h.wait((event) => event.type === "catalog");
expect(result).toMatchObject({
catalog: {
defaultModel: "test-model",
models: [
{ contextWindowMaxTokens: 123456, thinkingOptions: expected.map((id) => ({ id })) },
],
},
});
});
}
test("catalogue loads models whose effort tiers carry descriptions", async () => {
const deepest = "Use this for deepest analysis and complex fixes.";
const row = (modelId: string, isDefault: boolean) => ({
modelId,
providerId: "meta",
displayLabel: modelId,
contextLimit: 1000000,
isDefault,
defaultReasoningEffort: "max",
variants: ["minimal", "low", "medium", "high", "xhigh", "max"],
reasoningEffortVariants: isDefault ? [] : [{ tier: "xhigh", description: deepest }],
});
const models = [
row("muse-spark-1.3", true),
row("muse-spark-1.3-contributor", false),
row("muse-spark-1.2", false),
row("muse-spark-1.2-contributor", false),
];
const h = await harness("catalog-controls", { MUSE_TEST_MODELS: JSON.stringify(models) });
await h.send({ type: "catalog", requestId: "models" });
const result = await h.wait(
(event) => event.type === "catalog" || event.type === "request.failed",
);
const offered = [
{ id: "minimal" },
{ id: "low" },
{ id: "medium" },
{ id: "high" },
{ id: "xhigh" },
{ id: "max", isDefault: true },
];
expect(result).toMatchObject({
type: "catalog",
catalog: {
defaultModel: "muse-spark-1.3",
models: [
{ id: "muse-spark-1.3", defaultThinkingOptionId: "max" },
{ id: "muse-spark-1.3-contributor", thinkingOptions: offered },
{ id: "muse-spark-1.2", thinkingOptions: offered },
{ id: "muse-spark-1.2-contributor", thinkingOptions: offered },
],
},
});
});
for (const [configuredModel, catalogModels, expectedModel] of [
["configured-model", [], "configured-model"],
[
"",
[
{
modelId: "catalog-model",
providerId: "meta",
displayLabel: "Catalog model",
contextLimit: null,
isDefault: true,
variants: ["high"],
},
],
"catalog-model",
],
["", [], undefined],
] as const) {
test(`null session model preserves ${expectedModel ?? "unset model"}`, async () => {
const h = await harness("text-reasoning", {
MUSE_TEST_NULL_MODEL: "1",
MUSE_TEST_MODELS: JSON.stringify(catalogModels),
});
expect(await h.open(undefined, "onRequest", configuredModel || null)).toEqual({
type: "session.ready",
requestId: "open",
sessionId: "paseo-session",
});
const config = await h.wait((event) => event.type === "session.config");
if (config.type !== "session.config") throw new Error("Expected session config");
expect(config.config.model).toBe(expectedModel);
const saved = h.events.findLast((event) => event.type === "session.persistence");
if (!saved) throw new Error("Expected persistence");
expect(saved.persistence.data.model).toBe(expectedModel);
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
});
}
test("session startup accepts described catalog efforts and publishes the complete list", async () => {
const model = {
modelId: "meta/muse-spark-1.3",
providerId: "meta",
displayLabel: "Muse Spark",
contextLimit: null,
isDefault: true,
defaultReasoningEffort: "max",
variants: ["minimal", "low", "medium", "high", "xhigh", "max"],
reasoningEffortVariants: [{ tier: "xhigh", description: "Deepest analysis" }],
};
const h = await harness("text-reasoning", { MUSE_TEST_MODELS: JSON.stringify([model]) });
expect(await h.open()).toEqual({
type: "session.ready",
requestId: "open",
sessionId: "paseo-session",
});
expect(await h.wait((event) => event.type === "session.config")).toMatchObject({
config: {
models: [{ id: model.modelId, defaultThinkingOptionId: "max" }],
thinkingOptions: model.variants.map((id) => ({ id, isDefault: id === "max" })),
},
});
await h.send({
type: "session.configure",
requestId: "choose-max",
sessionId: "paseo-session",
changes: { thinkingOption: "max" },
});
await h.wait((event) => event.type === "request.completed" && event.requestId === "choose-max");
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(
(await h.recorded()).find((frame) => frame.method === "turn/start").params.reasoningEffort,
).toBe("max");
});
for (const metadata of [
"unknown",
[{ tier: "future-tier", description: { text: "Future metadata" } }],
]) {
test(`catalog ignores unused effort descriptions ${JSON.stringify(metadata)}`, async () => {
const model = {
modelId: "test-model",
providerId: "meta",
displayLabel: "Muse Spark",
contextLimit: null,
isDefault: true,
defaultReasoningEffort: "high",
variants: ["low", "high"],
reasoningEffortVariants: metadata,
};
const h = await harness("catalog-controls", { MUSE_TEST_MODELS: JSON.stringify([model]) });
await h.send({ type: "catalog", requestId: "models" });
expect(
await h.wait((event) => event.type === "catalog" || event.type === "request.failed"),
).toMatchObject({
type: "catalog",
catalog: {
models: [
{ id: model.modelId, thinkingOptions: [{ id: "low" }, { id: "high", isDefault: true }] },
],
},
});
});
}
test("status accepts future credential states and drains its maintenance host", async () => {
const h = await harness("catalog-controls", { MUSE_TEST_ACCOUNT: "futureCredential" });
expect(await h.provider.status!({ launch: h.launch })).toEqual({ available: true });
expect(await h.recorded()).toContainEqual({ event: "hostClosed" });
});
for (const [tool, detail] of [
["grep", "search"],
["glob", "search"],
["ls", "search"],
["web_search", "search"],
["web_fetch", "fetch"],
["custom_tool", "unknown"],
]) {
test(`${tool} tool calls use ${detail} detail`, async () => {
const h = await harness("tools-edit", { MUSE_TEST_TOOL: tool! });
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(h.events).toContainEqual(
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
name: tool,
status: "completed",
detail: expect.objectContaining({ type: detail }),
}),
}),
);
});
}
test("a future item kind uses fallbackText without breaking turn completion", async () => {
const h = await harness("tools-edit", { MUSE_TEST_UNKNOWN_KIND: "1" });
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.turn" && event.state === "completed");
expect(h.events).toContainEqual(
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
name: "futureItem",
detail: { type: "plain_text", label: "futureItem", text: "New MSP item" },
}),
}),
);
});
test("fingerprint mismatch is logged but does not prevent opening", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_FINGERPRINT: "sha256:unverified" });
expect(await h.open()).toMatchObject({ type: "session.ready" });
});
for (const restoring of [false, true]) {
test(`MCP servers map to MSP stdio and streamableHttp on ${restoring ? "resume" : "start"}`, async () => {
const h = await harness(restoring ? "resume-without-cursor" : "text-reasoning");
await h.send({
type: "session.open",
sessionId: "paseo-session",
requestId: "mcp",
persistence: restoring
? { version: 1, data: { sessionId: "saved-session", cursor: "v:old:7" } }
: undefined,
history: "replay",
config: {
cwd: h.root,
env: {},
persist: true,
settings: {},
mcpServers: {
stdio: {
type: "stdio",
command: "test-server",
args: ["--serve"],
env: { TEST_ENV: "yes" },
},
paseo: {
type: "http",
url: "http://localhost/mcp/agents",
headers: { Authorization: "Bearer test-token" },
},
},
},
});
await h.wait((event) => event.type === "session.ready");
expect(
(await h.recorded()).find(
(frame) => frame.method === (restoring ? "session/resume" : "session/start"),
).params.config.mcpServers,
).toEqual({
stdio: {
transport: "stdio",
command: "test-server",
args: ["--serve"],
env: { TEST_ENV: "yes" },
framing: "lineDelimitedJson",
},
paseo: {
transport: "streamableHttp",
url: "http://localhost/mcp/agents",
headers: { Authorization: "Bearer test-token" },
},
});
});
}
test("authRequired turn errors retain their kind and explain login recovery", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_AUTH_REQUIRED: "1" });
await h.open();
await h.prompt();
expect(
await h.wait((event) => event.type === "session.turn" && event.state === "failed"),
).toMatchObject({
error: { code: "authRequired", message: "Run `muse login` or set META_API_KEY." },
});
});
test("status bounds a stalled account read and closes the host", async () => {
const h = await harness("catalog-controls", { MUSE_TEST_HANG: "account/read" });
expect(await h.provider.status!({ launch: h.launch })).toEqual({
available: false,
diagnostic: "Muse account/read timed out",
});
expect(await h.recorded()).toContainEqual({ event: "hostClosed" });
});
test("interrupt targets the running turn when another prompt is queued", async () => {
const h = await harness("interrupt", { MUSE_TEST_QUEUE: "1" });
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.prompt_result");
const from = await h.prompt();
await h.wait((event) => event.type === "session.prompt_result", from);
await h.send({
type: "session.interrupt",
sessionId: "paseo-session",
requestId: "interrupt-queued",
});
await h.wait(
(event) => event.type === "request.completed" && event.requestId === "interrupt-queued",
);
const requests = await h.recorded();
const active = requests.find((frame) => frame.method === "turn/start");
expect(requests.find((frame) => frame.method === "turn/interrupt").params.turnId).toBe(
active.params.commandId,
);
});
for (const terminal of ["completed", "failed"]) {
test(`interrupt resolves when its targeted turn independently ends ${terminal}`, async () => {
const h = await harness("interrupt", { MUSE_TEST_INTERRUPT_TERMINAL: terminal });
await h.open();
await h.prompt();
await h.wait((event) => event.type === "session.prompt_result");
await h.send({
type: "session.interrupt",
sessionId: "paseo-session",
requestId: "racing-interrupt",
});
await h.wait((event) => event.type === "session.turn" && event.state === terminal);
await h.wait(
(event) => event.type === "request.completed" && event.requestId === "racing-interrupt",
);
expect(h.events.filter((event) => event.type === "request.failed")).toEqual([]);
});
}
for (const [behavior, choiceId] of [
["allow", "allow_once"],
["deny", "abort"],
] as const) {
test(`CLI ${behavior} prefers the plain once choice over a broader fixture grant`, async () => {
const h = await harness("staged-approval", { MUSE_TEST_CHOICES: "reverse" });
await h.open();
await h.prompt();
const permission = await h.wait((event) => event.type === "session.permission");
if (permission.type !== "session.permission") throw new Error("Expected permission");
await h.send({
type: "session.permission",
sessionId: "paseo-session",
permissionId: permission.request.id,
response: { behavior },
});
await expect
.poll(
async () =>
(await h.recorded()).find((frame) => frame.method === "approval/decide")?.params.choiceId,
)
.toBe(choiceId);
});
}
test("CLI deny rejects a tool without aborting when denied/once is offered", async () => {
const h = await harness("staged-approval", { MUSE_TEST_DENIED: "1" });
await h.open();
await h.prompt();
const permission = await h.wait((event) => event.type === "session.permission");
if (permission.type !== "session.permission") throw new Error("Expected permission");
await h.send({
type: "session.permission",
sessionId: "paseo-session",
permissionId: permission.request.id,
response: { behavior: "deny" },
});
await expect
.poll(
async () =>
(await h.recorded()).find((frame) => frame.method === "approval/decide")?.params.choiceId,
)
.toBe("reject_once");
});
test("skills publish commands and command prompts submit structured skill parts", async () => {
const h = await harness("skill");
await h.open();
expect(await h.wait((e) => e.type === "session.commands")).toMatchObject({
commands: expect.arrayContaining([
expect.objectContaining({ name: "phase0", description: expect.any(String) }),
expect.objectContaining({ name: "compact", description: expect.any(String) }),
]),
});
await h.send({
type: "session.prompt",
sessionId: "paseo-session",
prompt: {
clientMessageId: "skill-client",
delivery: "auto",
input: { type: "command", name: "phase0", arguments: "argument text" },
},
});
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
expect((await h.recorded()).find((f) => f.method === "turn/start").params).toMatchObject({
input: expect.arrayContaining([
{ type: "skill", selector: "phase0", arguments: "argument text" },
]),
displayText: "/phase0 argument text",
});
});
test("skill changes refresh the session command catalog", async () => {
const h = await harness("skill", { MUSE_TEST_SKILLS_CHANGED: "1" });
await h.open();
const from = await h.prompt();
expect(await h.wait((e) => e.type === "session.commands", from)).toMatchObject({
commands: [{ name: "compact", description: "Compact conversation context" }],
});
expect((await h.recorded()).filter((f) => f.method === "skill/list")).toHaveLength(2);
});
for (const mode of ["answer", "cancel"] as const) {
test(`todo and question ${mode} replay through the provider interface`, async () => {
const h = await harness(mode === "answer" ? "phase3-controls" : "phase3-cancel");
await h.open();
await h.prompt();
if (mode === "answer")
expect(
await h.wait((e) => e.type === "timeline.item" && e.item.type === "todo"),
).toMatchObject({
item: {
items: [
{ text: "inspect f", completed: false, status: "in_progress" },
{ text: "report", completed: false, status: "pending" },
],
},
});
const permission = await h.wait(
(e) => e.type === "session.permission" && e.request.kind === "question",
);
if (permission.type !== "session.permission") throw new Error("Expected question");
expect(permission.request.input).toMatchObject({
questions: [
{ header: "Color", options: [{ label: "Blue" }, { label: "Green" }], multiSelect: false },
],
});
await h.send({
type: "session.permission",
sessionId: "paseo-session",
permissionId: permission.request.id,
response:
mode === "answer"
? { behavior: "allow", updatedInput: { answers: { Color: "Blue" } } }
: { behavior: "deny", message: "QA cancellation" },
});
await h.wait(
(e) => e.type === "session.permission_resolved" && e.permissionId === permission.request.id,
);
const frame = (await h.recorded()).find(
(f) => f.method === (mode === "answer" ? "userInput/answer" : "userInput/cancel"),
);
expect(frame.params.userInputId).toBe(permission.request.id);
if (mode === "answer")
expect(frame.params.answers).toEqual([{ questionId: "color_choice", selectedLabel: "Blue" }]);
expect((await h.recorded()).some((f) => f.result && Object.keys(f.result).length === 0)).toBe(
true,
);
});
}
for (const variant of ["tool", "dedicated", "workflow"]) {
test(`${variant} subagent opens only the supplied child and pages its real transcript`, async () => {
const h = await harness("subagent", { MUSE_TEST_CHILD: variant });
await h.open();
await h.prompt();
expect(
await h.wait((e) => e.type === "session.opened" && e.sessionId === "fixture-child"),
).toMatchObject({
parentSessionId: "paseo-session",
restoration: "parent",
toolCallId: expect.any(String),
});
expect(
await h.wait(
(e) =>
e.type === "timeline.item" &&
e.sessionId === "fixture-child" &&
e.item.type === "assistant_message",
),
).toMatchObject({ item: { text: "SUBAGENT_PHASE0_OK" } });
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
expect(
h.events.filter((e) => e.type === "session.opened" && e.sessionId === "fixture-child"),
).toHaveLength(1);
expect(
(await h.recorded())
.filter((f) => f.method === "session/read")
.map((f) => f.params.sessionId),
).toEqual(["fixture-child"]);
expect(
h.events.filter(
(e) =>
e.type === "timeline.item" &&
e.sessionId === "fixture-child" &&
e.item.type === "assistant_message",
),
).toHaveLength(1);
});
}
test("gap backfill repairs a missing completion without duplicating item revisions", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_GAP: "1" });
await h.open();
await h.prompt();
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
const frames = (await h.recorded()).filter((f) => f.method === "view/page");
expect(frames.map((f) => f.params.cursor)).toEqual(["fixture-last-cursor"]);
const messages = h.events.filter(
(e) => e.type === "timeline.item" && e.item.type === "assistant_message",
);
expect(messages.at(-1)).toMatchObject({ item: { text: expect.stringContaining("49/20") } });
expect(h.events.filter((e) => e.type === "session.turn" && e.state === "completed")).toHaveLength(
1,
);
});
test("silent active turns page after two minutes and stop polling on recovered terminal", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_SILENT: "1" });
await h.open();
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
await h.prompt();
await h.wait((e) => e.type === "session.prompt_result");
await vi.advanceTimersByTimeAsync(119999);
expect((await h.recorded()).filter((f) => f.method === "view/page")).toEqual([]);
await vi.advanceTimersByTimeAsync(1);
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
await vi.advanceTimersByTimeAsync(240000);
expect((await h.recorded()).filter((f) => f.method === "view/page")).toHaveLength(1);
});
test("session readiness accepts initialization beyond the old event polling window", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_INITIALIZE_DELAY_MS: "3500" });
expect(await h.open()).toMatchObject({ type: "session.ready" });
expect((await h.recorded()).find((frame) => frame.event === "hostStarted").args).toEqual([
"serve",
"--sandbox-network",
"proxy-only",
"--disable-sandbox",
"--trust-workspace",
]);
}, 10000);
test("default launch disables sandbox and trusts workspace", async () => {
const h = await harness();
await h.open();
expect((await h.recorded()).find((f) => f.event === "hostStarted").args).toEqual([
"serve",
"--sandbox-network",
"proxy-only",
"--disable-sandbox",
"--trust-workspace",
]);
});
test.each([
[{ sandbox: { enabled: true } }, ["--sandbox-network", "proxy-only", "--trust-workspace"]],
[{ trustWorkspace: false }, ["--sandbox-network", "proxy-only", "--disable-sandbox"]],
[
{ sandbox: { network: "restricted" } },
["--sandbox-network", "restricted", "--disable-sandbox", "--trust-workspace"],
],
[
{ sandbox: { network: "enabled" } },
["--sandbox-network", "enabled", "--disable-sandbox", "--trust-workspace"],
],
[
{ sandbox: { enabled: true, network: "proxy-only" }, trustWorkspace: false },
["--sandbox-network", "proxy-only"],
],
])("provider options %j control launch flags", async (params, args) => {
const h = await harness("text-reasoning", {}, params);
await h.open();
expect((await h.recorded()).find((f) => f.event === "hostStarted").args).toEqual([
"serve",
...args,
]);
});
test.each([
[{ sandbox: { enabled: "true" } }, "sandbox.enabled"],
[{ sandbox: { network: "invalid" } }, "sandbox.network"],
[{ trustWorkspace: 1 }, "trustWorkspace"],
[{ unexpected: true }, "unexpected"],
])("invalid provider options %j fail session creation clearly", async (options, field) => {
const h = await harness("text-reasoning", {}, options);
const event = await h.open();
expect(event).toMatchObject({
type: "request.failed",
error: { code: "invalidProviderOptions" },
});
if (event.type !== "request.failed") throw new Error("Expected failure");
expect(event.error.message).toContain("Invalid Muse providerOptions");
expect(event.error.message).toContain(field);
expect(await h.recorded()).toEqual([]);
});
test("resuming a session reapplies its provider options to the new host", async () => {
const h = await harness(
"resume-without-cursor",
{},
{
sandbox: { enabled: true, network: "restricted" },
trustWorkspace: false,
},
);
await h.open({ version: 1, data: { sessionId: "saved-session" } });
const saved = h.events.find((e) => e.type === "session.opened");
if (saved?.type !== "session.opened" || !saved.persistence)
throw new Error("Expected persistence");
await h.send({ type: "session.close", sessionId: "paseo-session", requestId: "close" });
await h.wait((e) => e.type === "session.closed");
const from = h.events.length;
await h.open(saved.persistence);
await h.wait((e) => e.type === "session.ready", from);
const frames = await h.recorded();
expect(frames.filter((f) => f.event === "hostStarted").map((f) => f.args)).toEqual([
["serve", "--sandbox-network", "restricted"],
["serve", "--sandbox-network", "restricted"],
]);
expect(frames.some((f) => f.method === "session/resume")).toBe(true);
});
test("an uncorrelated MSP parse error keeps the provider diagnostic", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_NULL_ERROR_ID: "1" });
expect(await h.open()).toMatchObject({
type: "request.failed",
error: { code: "parseError", message: "Cannot recover request id" },
});
});
test("text deltas with an omitted field stream as text", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_IMPLICIT_TEXT_DELTA: "1" });
await h.open();
await h.wait((e) => e.type === "session.ready");
await h.prompt();
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
expect(
h.events.some(
(e) =>
e.type === "timeline.item" &&
e.item.type === "assistant_message" &&
e.item.text === "Sum with",
),
).toBe(true);
});
test("sessions with a null model import and resume without a null persistence model", async () => {
const h = await harness("resume-without-cursor", { MUSE_TEST_NULL_MODEL: "1" });
await h.send({
type: "sessions",
requestId: "null-list",
cwd: "/tmp/muse-phase0/repo",
limit: 2,
});
const listed = await h.wait((e) => e.type === "sessions");
if (listed.type !== "sessions") throw new Error("Expected sessions");
expect(listed.sessions).toHaveLength(2);
expect(listed.sessions[0]!.persistence.data.model).toBeUndefined();
await h.open(listed.sessions[0]!.persistence);
await h.wait((e) => e.type === "session.ready");
expect(
h.events.some((e) => e.type === "session.config" && e.config.model === "meta/muse-spark-1.3"),
).toBe(true);
});
test("sessions list filters workspace and imported persistence opens via resume", async () => {
const h = await harness("resume-without-cursor");
await h.send({
type: "sessions",
requestId: "import-list",
cwd: "/tmp/muse-phase0/repo",
limit: 2,
});
const listed = await h.wait((e) => e.type === "sessions");
if (listed.type !== "sessions") throw new Error("Expected sessions");
expect(listed.sessions).toHaveLength(2);
expect((await h.recorded()).find((f) => f.method === "session/list").params.workspaceRoot).toBe(
"/tmp/muse-phase0/repo",
);
await h.open(listed.sessions[0]!.persistence);
expect((await h.recorded()).filter((f) => f.method === "session/start")).toEqual([]);
expect((await h.recorded()).find((f) => f.method === "session/resume").params.sessionId).toBe(
listed.sessions[0]!.persistence.data.sessionId,
);
});
for (const fixture of ["phase3-compact", "phase3-compact-history", "phase3-compact-limit"]) {
test(`${fixture} routes compact and exposes the recorded outcome`, async () => {
const h = await harness(fixture);
const rows = (
await readFile(
fileURLToPath(new URL(`./fixtures/${fixture}.ndjson`, import.meta.url)),
"utf8",
)
)
.trim()
.split("\n")
.map((line) => JSON.parse(line));
const nativeId = rows.find((row) => row.msg.method === "session/resume").msg.params.sessionId;
await h.open({ version: 1, data: { sessionId: nativeId } });
await h.send({
type: "session.prompt",
sessionId: "paseo-session",
prompt: {
clientMessageId: "compact",
delivery: "auto",
input: { type: "command", name: "compact", arguments: "" },
},
});
if (fixture === "phase3-compact-limit") {
expect(await h.wait((e) => e.type === "session.prompt_result")).toMatchObject({
result: { type: "completed" },
});
expect(
await h.wait(
(e) =>
e.type === "timeline.item" &&
e.item.type === "compaction" &&
e.item.status === "completed",
),
).toMatchObject({ item: { preTokens: 22192 } });
} else
expect(await h.wait((e) => e.type === "session.prompt_result")).toMatchObject({
result: {
type: "failed",
error: {
code: "commandRejected",
message: expect.stringContaining("context_compaction.provider_context_limit_tokens"),
},
},
});
expect((await h.recorded()).filter((f) => f.method === "turn/start")).toEqual([]);
});
}
for (const usage of [
{},
{
usage: {
observedAtMs: 1700000000000,
tier: "Pro",
window: { usedPercent: 75, resetsAtMs: 1700018000000, windowDurationMins: 300 },
weekly: { usedPercent: 105, resetsAtMs: 1700604800000 },
},
},
]) {
test(`usage source presents ${"usage" in usage ? "subscription windows" : "real route absence"} from discovered accounts`, async () => {
const h = await harness("catalog-controls", { MUSE_TEST_USAGE: JSON.stringify(usage) });
await h.open();
const inputs = await h.usageSource.discover({ kind: "global" });
expect(inputs).toHaveLength(1);
const account = inputs[0]!;
expect(account).toEqual({
key: expect.stringMatching(/^[a-f0-9]{64}$/),
label: "Muse Code",
input: { account: account.key },
});
const report = await h.usageSource.fetch(account.input);
if ("usage" in usage)
expect(report).toMatchObject({
status: "available",
planLabel: "Pro",
windows: [
{
id: "five_hour",
label: "5-hour",
usedPct: 75,
remainingPct: 25,
tone: "warning",
},
{ id: "weekly", usedPct: 105, remainingPct: 0, tone: "danger" },
],
});
else
expect(report).toEqual({
status: "unavailable",
problem: { kind: "no_quota", detail: "No usage quota reported" },
});
expect((await h.recorded()).filter((f) => f.method === "usage/read")).toHaveLength(1);
});
}
test("a terminal before turn admission does not start an idle watchdog", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_ACK_LAST: "1" });
await h.open();
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
await h.prompt();
await h.wait((e) => e.type === "session.prompt_result");
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
await vi.advanceTimersByTimeAsync(120000);
await delay(30);
expect((await h.recorded()).filter((f) => f.method === "view/page")).toEqual([]);
});
test("admitted compact publishes compaction progress and completes the command without a turn", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_COMPACT: "1" });
await h.open();
await h.send({
type: "session.prompt",
sessionId: "paseo-session",
prompt: {
clientMessageId: "compact-admitted",
delivery: "auto",
input: { type: "command", name: "compact", arguments: "" },
},
});
expect(await h.wait((e) => e.type === "session.prompt_result")).toMatchObject({
result: { type: "completed" },
});
await h.wait(
(e) =>
e.type === "timeline.item" && e.item.type === "compaction" && e.item.status === "completed",
);
expect(
h.events
.filter((e) => e.type === "timeline.item" && e.item.type === "compaction")
.map((e) => e.type === "timeline.item" && e.item),
).toEqual([
{ type: "compaction", id: "fixture-compaction", status: "loading" },
{ type: "compaction", id: "fixture-compaction", status: "completed" },
]);
expect((await h.recorded()).filter((f) => f.method === "turn/start")).toEqual([]);
});
test("default workflow children render structured tool details from the real no-flag run", async () => {
const h = await harness("text-reasoning", { MUSE_TEST_WORKFLOW: "1" });
await h.open();
await h.prompt();
const event = await h.wait(
(e) =>
e.type === "timeline.item" &&
e.item.type === "tool_call" &&
e.item.name === "workflow" &&
e.item.detail.type === "sub_agent" &&
e.item.detail.log?.includes("MUSE_PARITY_CHILD_OK") === true,
);
expect(event).toMatchObject({ item: { status: "completed", detail: { type: "sub_agent" } } });
expect(h.events.filter((e) => e.type === "session.opened")).toHaveLength(1);
const workflows = h.events.filter(
(e) => e.type === "timeline.item" && e.item.type === "tool_call" && e.item.name === "workflow",
);
expect(workflows.length).toBeGreaterThan(1);
for (const workflow of workflows)
expect(workflow).toMatchObject({ item: { detail: { type: "sub_agent" } } });
});
for (const source of ["user", "project"] as const) {
test(`real ${source} skill catalog and command replay with explicit workspace trust`, async () => {
const h = await harness(`phase3-${source}-skill`, {}, { trustWorkspace: source === "project" });
await h.open();
expect(await h.wait((e) => e.type === "session.commands")).toMatchObject({
commands: expect.arrayContaining([
expect.objectContaining({ name: `muse-parity-${source}` }),
]),
});
await h.send({
type: "session.prompt",
sessionId: "paseo-session",
prompt: {
clientMessageId: "real-skill",
delivery: "auto",
input: { type: "command", name: `muse-parity-${source}`, arguments: "QA" },
},
});
await h.wait((e) => e.type === "session.turn" && e.state === "completed");
expect(h.events).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "timeline.item",
item: expect.objectContaining({
type: "assistant_message",
text: `MUSE_PARITY_${source.toUpperCase()}_OK`,
}),
}),
]),
);
const recorded = await h.recorded();
expect(recorded.find((f) => f.method === "turn/start").params.input).toContainEqual({
type: "skill",
selector: `muse-parity-${source}`,
arguments: "QA",
});
expect(recorded.find((f) => f.event === "hostStarted").args.includes("--trust-workspace")).toBe(
source === "project",
);
});
}
test("usage identity follows the resolved config directory across credential and route changes", async () => {
const identities: string[] = [];
for (const [config, token, route] of [
["/usage/config", "token-a", "route-a"],
["/usage/config", "token-b", "route-b"],
["/usage/other", "token-b", "route-b"],
]) {
const h = await harness("catalog-controls", {
XDG_CONFIG_HOME: config!,
META_API_KEY: token!,
META_BASE_URL: route!,
});
await h.open();
const inputs = await h.usageSource.discover({ kind: "global" });
expect(inputs).toHaveLength(1);
identities.push(inputs[0]!.key);
}
expect(identities[0]).toBe(identities[1]);
expect(identities[2]).not.toBe(identities[0]);
});
test.each(["no sessions", "unrelated environment"])("Muse discovery is empty with %s", async () => {
const { Usage } = await import("../server/usage.js");
expect(await new Usage().registration().discover({ kind: "global" })).toEqual([]);
});
test("Muse window identity follows the reported duration instead of assuming five hours", async () => {
const h = await harness("catalog-controls", {
MUSE_TEST_USAGE: JSON.stringify({
usage: {
observedAtMs: 1700000000000,
tier: "Pro",
window: { usedPercent: 11, resetsAtMs: 1700018000000, windowDurationMins: 120 },
weekly: { usedPercent: 22, resetsAtMs: 1700604800000 },
},
}),
});
await h.open();
const [account] = await h.usageSource.discover({ kind: "global" });
expect(await h.usageSource.fetch(account!.input)).toMatchObject({
status: "available",
windows: [
{ id: "7200s", label: "2-hour", shortLabel: "2h" },
{ id: "weekly", label: "Weekly", shortLabel: "wk" },
],
});
});
test("status accepts initialization slower than the account deadline without isolating history", async () => {
const h = await harness("catalog-controls", { MUSE_TEST_INITIALIZE_DELAY_MS: "3500" });
expect(await h.provider.status!({ launch: h.launch })).toEqual({ available: true });
expect(await h.recorded()).toContainEqual({ event: "hostClosed" });
}, 10000);
for (const extension of ["cmd", "bat"]) {
test.runIf(process.platform === "win32")(
`launches the discovered Muse .${extension} for status, catalog and sessions`,
async () => {
const h = await harness("catalog-controls");
const shim = path.join(h.root, `muse launcher.${extension}`);
await writeFile(shim, `@echo off\r\n"${process.execPath}" "${h.launch.args[0]}" %*\r\n`);
h.launch.command = shim;
h.launch.args = [];
expect(await h.provider.status!({ launch: h.launch })).toEqual({ available: true });
await h.send({ type: "catalog", requestId: "shim-catalog" });
expect(await h.wait((event) => event.type === "catalog")).toMatchObject({
requestId: "shim-catalog",
});
await h.open();
expect(await h.wait((event) => event.type === "session.opened")).toMatchObject({
sessionId: "paseo-session",
});
},
20000,
);
}
test("force-stops a Muse host that ignores stdin EOF", async () => {
const h = await stubbornHostHarness();
try {
const result = await Promise.race([
h.provider.status!({ launch: h.launch }),
delay(10000).then(() => {
throw new Error("Muse status did not finish closing its host");
}),
]);
expect(result).toEqual({ available: true });
const hosts = (await h.recorded()).filter((row) => row.event === "stubbornHost");
expect(hosts).toHaveLength(1);
expect(() => process.kill(hosts[0].pid, 0)).toThrow();
} finally {
await cleanupStubbornHosts(h);
}
}, 15000);
test("closes the whole Muse host after an uncorrelated MSP error", async () => {
const h = await stubbornHostHarness({ MUSE_TEST_NULL_ERROR_ID: "1" });
try {
const result = await h.provider.status!({ launch: h.launch });
expect(result).toEqual({ available: false, diagnostic: "Cannot recover request id" });
const recorded = await h.recorded();
expect(recorded.some((row) => row.event === "hostClosed")).toBe(true);
const hosts = recorded.filter((row) => row.event === "stubbornHost");
expect(hosts).toHaveLength(1);
expect(() => process.kill(hosts[0].pid, 0)).toThrow();
} finally {
await cleanupStubbornHosts(h);
}
}, 15000);
async function stubbornHostHarness(env: Record<string, string> = {}) {
const h = await harness("catalog-controls", { MUSE_TEST_STUBBORN: "1", ...env });
if (process.platform === "win32") {
const shim = path.join(h.root, "stubborn muse.cmd");
await writeFile(shim, `@echo off\r\n"${process.execPath}" "${h.launch.args[0]}" %*\r\n`);
h.launch.command = shim;
h.launch.args = [];
}
return h;
}
async function cleanupStubbornHosts(h: Awaited<ReturnType<typeof harness>>) {
// Keep a failing Windows regression from leaving its deliberately stubborn child alive.
for (const host of (await h.recorded()).filter((row) => row.event === "stubbornHost")) {
if (process.platform === "win32") {
await execCommand("taskkill.exe", ["/PID", String(host.pid), "/T", "/F"], {
shell: false,
}).catch(() => {});
} else {
try {
process.kill(host.pid, "SIGKILL");
} catch {}
}
}
}
test("Muse reports belong only to the session's account and remain available globally", async () => {
const usage = new MuseUsage();
const homeEnv = { HOME: "/usage/home" };
const configEnv = { HOME: "/usage/home", XDG_CONFIG_HOME: "/usage/work" };
const accountIds = [homeEnv, configEnv].map((env, index) => {
const launch = { command: "muse", args: [], env };
usage.attach(`session-${index}`, launch, async () => ({
usage: {
observedAtMs: 1700000000000,
tier: "Pro",
window: { usedPercent: 11, resetsAtMs: 1700018000000, windowDurationMins: 120 },
weekly: { usedPercent: 22, resetsAtMs: 1700604800000 },
},
}));
return `muse:${usage.remember(launch).account}`;
});
const sessions = new Map([
["claude", { provider: "claude", env: homeEnv, sessionKey: "claude" }],
["codex", { provider: "codex", env: configEnv, sessionKey: "codex" }],
["muse-home", { provider: "muse", env: homeEnv, sessionKey: "muse-home" }],
["muse-work", { provider: "muse", env: configEnv, sessionKey: "muse-work" }],
["muse-unknown", { provider: "muse", env: { HOME: "/usage/unknown" }, sessionKey: "unknown" }],
]);
const registry = new UsageSourceRegistry(Date.now, 300_000, undefined, {
hasAgent: (id) => sessions.has(id),
usageSession: (id) => sessions.get(id) ?? null,
});
registry.register(usage.registration());
expect((await registry.listReports()).map((report) => report.id)).toEqual(accountIds);
expect(await registry.listReports({ agentId: "claude" })).toEqual([]);
expect(await registry.listReports({ agentId: "codex" })).toEqual([]);
for (const [index, agentId] of ["muse-home", "muse-work"].entries()) {
const reports = await registry.listReports({ agentId });
expect(reports.map((report) => report.id)).toEqual([accountIds[index]]);
expect(reports[0]!.report.status).toBe("available");
}
expect(await registry.listReports({ agentId: "muse-unknown" })).toEqual([]);
expect((await registry.listReports()).map((report) => report.id)).toEqual(accountIds);
});