Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions packages/provider-muse/src/server/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,59 @@ describe("MuseAdapterV2", () => {
}).pipe(Effect.scoped, Effect.provide(testLayer)),
);

it.effect("lists a running subagent under its turn item's native id as Muse reveals it", () =>
Effect.gen(function* () {
const fake = yield* makeFakeMuse();
const harness = yield* makeHarness(fake);
const { nativeId } = yield* startConversation(harness, fake);
const revisions = [
{ revision: 1, status: "inProgress" },
{ revision: 2, status: "inProgress", subagentId: "" },
{ revision: 3, status: "inProgress", subagentId: "subagent-1" },
{
revision: 4,
status: "inProgress",
subagentId: "subagent-1",
childSessionId: "child-session-1",
},
{
revision: 5,
status: "completed",
subagentId: "subagent-1",
childSessionId: "child-session-1",
},
];
const listed: Array<{ nativeId: string | null | undefined; roster: Array<string> }> = [];
for (const fields of revisions) {
yield* fake.emit(fields.revision === 1 ? "item/started" : "item/updated", {
turnId: nativeId,
item: { itemId: "sub-1", kind: "subagent", turnId: nativeId, ...fields },
});
const item = yield* harness.takeEvent(
"turn_item.updated",
(event) => event.turnItem.type === "subagent",
);
const roster = harness.allEvents.findLast(
(event) => event.type === "provider_thread.updated",
);
listed.push({
nativeId: item.turnItem.nativeItemRef?.nativeId,
roster:
roster?.type === "provider_thread.updated"
? (roster.providerThread.pendingBackgroundTasks ?? []).map((task) => task.taskId)
: [],
});
}
assert.deepStrictEqual(listed, [
{ nativeId: "sub-1", roster: ["sub-1"] },
{ nativeId: "sub-1", roster: ["sub-1"] },
{ nativeId: "subagent-1", roster: ["subagent-1"] },
{ nativeId: "child-session-1", roster: ["child-session-1"] },
{ nativeId: "child-session-1", roster: [] },
]);
}).pipe(Effect.scoped, Effect.provide(testLayer)),
);

it.effect("keeps a workflow child's first label after later revisions drop it", () =>
Effect.gen(function* () {
const fake = yield* makeFakeMuse();
Expand Down
17 changes: 12 additions & 5 deletions packages/provider-muse/src/server/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ const nativeRef = (nativeId: string) => ({
nativeId,
strength: "strong" as const,
});
/** The roster lists a child under its turn item's native id, so readers can pair the two. */
const backgroundTaskId = (item: MuseItem) =>
item.kind === "subagent" ? item.childSessionId || item.subagentId || item.itemId : item.itemId;
const recordSchema = Schema.Record(Schema.String, Schema.Unknown);
const INFORMATIONAL_NOTIFICATIONS = new Set([
"session/contextUsage",
Expand Down Expand Up @@ -595,9 +598,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* (
item.result?.summary ??
(status === "failed" ? item.failureReason : undefined);
const result = resultText ?? null;
const nativeTaskRef = item.childSessionId
? nativeRef(item.childSessionId)
: nativeRef(item.subagentId ?? item.itemId);
const nativeTaskRef = nativeRef(backgroundTaskId(item));
const subagent: OrchestrationV2Subagent = {
id: base.nodeId,
threadId: base.threadId,
Expand Down Expand Up @@ -675,7 +676,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* (
const description = item?.objective ?? item?.entryId ?? item?.fallbackText;
return [
{
taskId: itemId,
taskId: item ? backgroundTaskId(item) : itemId,
kind:
item?.kind === "subagent" ? ("subagent" as const) : ("background_task" as const),
...(description ? { description } : {}),
Expand Down Expand Up @@ -714,7 +715,13 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* (
? "cancelled"
: "failed",
});
if (wasRunning !== observedChildren.has(item.itemId)) yield* syncBackground();
const running = observedChildren.has(item.itemId);
// Muse reveals a subagent's ids over several revisions, so a running child is
// relisted whenever the id its turn item carries moves.
const listed = thread?.pendingBackgroundTasks?.some(
(task) => task.taskId === backgroundTaskId(item),
);
if (wasRunning !== running || (running && !listed)) yield* syncBackground();
}
const streaming = status === "running";
const base = {
Expand Down
Loading