mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 09:42:53 +00:00
* test(muse): record real host protocol fixtures * feat(muse): add built-in MSP provider Co-authored-by: millerben95 <millerben95@users.noreply.github.com> * fix(muse): expose provider settings in the app The daemon registered host settings without a client contribution, leaving sandbox, network, and workspace trust unreachable. Share the definition with a settings screen and expose it through Command Center even when installed plugins are disabled. * docs(muse): publish setup and provider support * test: correct runtime fixtures and restart readiness * fix(muse): hide reminder housekeeping from the timeline * fix(muse): use the official Muse Code mark Source: https://muse.ai/images/favicon/app-squiggle.svg Preserve the official silhouette path verbatim. Remove its gradient definition and use currentColor, scaling the original 100-unit coordinates into the 24-unit viewBox. Black silhouette renders match pixel-for-pixel. * fix(muse): adapt to current plugin APIs Use the screen API introduced with sidebar plugin items. Usage reports now come from source account discovery; remove the retired agent usage-reference request and headline marker, and exercise discovery through the existing provider fixtures. --------- Co-authored-by: millerben95 <millerben95@users.noreply.github.com>
95 lines
2.9 KiB
TypeScript
95 lines
2.9 KiB
TypeScript
import type { ProviderEvent } from "@getpaseo/plugin/server/provider";
|
|
import { MspConnection, type Notification } from "./connection.js";
|
|
import { Timeline } from "./timeline.js";
|
|
import {
|
|
childSessionSchema,
|
|
pageSchema,
|
|
itemNotificationSchema,
|
|
notificationSchema,
|
|
deltaSchema,
|
|
type WireItem,
|
|
} from "./wire.js";
|
|
|
|
interface Child {
|
|
timeline: Timeline;
|
|
cursor: string | undefined;
|
|
}
|
|
export class Children {
|
|
private readonly children = new Map<string, Child>();
|
|
constructor(
|
|
private readonly host: MspConnection,
|
|
private readonly parentId: string,
|
|
private readonly cwd: string,
|
|
private readonly emit: (event: ProviderEvent) => void,
|
|
) {}
|
|
|
|
async update(item: WireItem): Promise<void> {
|
|
if (
|
|
!item.childSessionId ||
|
|
!(
|
|
item.kind === "subagent" ||
|
|
item.kind === "workflow" ||
|
|
item.tool === "workflow" ||
|
|
item.tool.startsWith("subagent_")
|
|
)
|
|
)
|
|
return;
|
|
const id = item.childSessionId;
|
|
if (!this.children.has(id)) {
|
|
const { session } = await this.host.request(
|
|
"session/read",
|
|
{ sessionId: id },
|
|
childSessionSchema,
|
|
);
|
|
this.emit({
|
|
type: "session.opened",
|
|
sessionId: id,
|
|
parentSessionId: this.parentId,
|
|
toolCallId: item.callId ?? item.itemId,
|
|
capabilities: [],
|
|
restoration: "parent",
|
|
cwd: session.workspaceRoot ?? this.cwd,
|
|
title: session.title,
|
|
});
|
|
this.children.set(id, {
|
|
timeline: new Timeline(this.host, id, id, this.emit),
|
|
cursor: undefined,
|
|
});
|
|
}
|
|
await this.page(id);
|
|
}
|
|
async notify(id: string, event: Notification): Promise<void> {
|
|
const child = this.children.get(id);
|
|
if (!child) return;
|
|
if (["item/started", "item/updated", "item/completed"].includes(event.method)) {
|
|
const { item } = itemNotificationSchema.parse(event.params);
|
|
await child.timeline.fold(item);
|
|
} else if (event.method === "item/delta") child.timeline.delta(deltaSchema.parse(event.params));
|
|
}
|
|
async refresh(): Promise<void> {
|
|
for (const id of this.children.keys()) await this.page(id);
|
|
}
|
|
private async page(id: string): Promise<void> {
|
|
const child = this.children.get(id)!;
|
|
let next: string | null;
|
|
do {
|
|
const page = await this.host.request(
|
|
"view/page",
|
|
{ sessionId: id, cursor: child.cursor, limit: 1000, direction: "forward" },
|
|
pageSchema,
|
|
);
|
|
for (const event of page.events) {
|
|
await this.notify(id, event);
|
|
const envelope = notificationSchema.parse(event.params);
|
|
if (envelope.viewCursor) child.cursor = envelope.viewCursor;
|
|
}
|
|
next = page.nextCursor;
|
|
if (next !== null) child.cursor = next;
|
|
} while (next !== null);
|
|
}
|
|
close(): void {
|
|
for (const sessionId of this.children.keys()) this.emit({ type: "session.closed", sessionId });
|
|
this.children.clear();
|
|
}
|
|
}
|