diff --git a/packages/provider-muse/src/server/adapter.test.ts b/packages/provider-muse/src/server/adapter.test.ts index 847c2b4551c59..cc84894ac6ef9 100644 --- a/packages/provider-muse/src/server/adapter.test.ts +++ b/packages/provider-muse/src/server/adapter.test.ts @@ -850,16 +850,33 @@ describe("MuseAdapterV2", () => { }).pipe(Effect.provide(testLayer)), ); - it.effect( - "closes a newly opened host when native session registration returns another identity", - () => + it.effect.each([ + { + reason: "another identity", + method: "session/start", + sessionId: "unexpected-session", + initialNativeThreadId: undefined, + history: { items: [] }, + }, + { + reason: "an unavailable projection", + method: "session/resume", + sessionId: "saved-session", + initialNativeThreadId: "saved-session", + history: { items: null, mode: "none", noneReason: "projectionUnavailable" }, + }, + ])( + "closes a newly opened host when native session registration returns $reason", + ({ method, sessionId, initialNativeThreadId, history }) => Effect.gen(function* () { const fake = yield* makeFakeMuse(); - fake.queueResponse("session/start", { - session: { sessionId: "unexpected-session", modelId: MODEL, activeTurnId: null }, - history: { items: [] }, + fake.queueResponse(method, { + session: { sessionId, modelId: MODEL, activeTurnId: null }, + history, }); - const result = yield* makeHarness(fake).pipe(Effect.exit); + const result = yield* makeHarness(fake, INSTANCE_ID, initialNativeThreadId).pipe( + Effect.exit, + ); assert.strictEqual(result._tag, "Failure"); yield* Effect.promise(() => fake.host.exited); assert.strictEqual(fake.closeCount(), 1); @@ -1441,6 +1458,11 @@ describe("MuseAdapterV2", () => { text: "Previous answer", }; fake.setHistory([nativeItem]); + fake.queueResponse("session/resume", { + session: { sessionId: "saved-session", modelId: MODEL, activeTurnId: null }, + viewCursor: "", + history: { items: null, mode: "none", noneReason: "excluded" }, + }); const harness = yield* makeHarness(fake, INSTANCE_ID, "saved-session"); const resumed = yield* fake.takeCall("session/resume"); assert.strictEqual(resumed.params.sessionId, "saved-session"); diff --git a/packages/provider-muse/src/server/adapter.ts b/packages/provider-muse/src/server/adapter.ts index b29f8d51947bf..fd1526a428c6e 100644 --- a/packages/provider-muse/src/server/adapter.ts +++ b/packages/provider-muse/src/server/adapter.ts @@ -1879,6 +1879,10 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( ); if (result.session.sessionId !== nativeSessionId) return yield* protocolError("Muse returned a different session identity"); + if (result.history?.noneReason === "projectionUnavailable") + return yield* protocolError( + "Muse cannot stream this session because its live view is unavailable", + ); if (requestedId) yield* request("session/setApprovalMode", { mode: museApprovalMode(args.runtimePolicy.runtimeMode), diff --git a/packages/provider-muse/src/server/protocol.ts b/packages/provider-muse/src/server/protocol.ts index 9c3e7c40d957b..0c1fdad7db3b2 100644 --- a/packages/provider-muse/src/server/protocol.ts +++ b/packages/provider-muse/src/server/protocol.ts @@ -70,6 +70,7 @@ export const MuseSessionResult = Schema.Struct({ history: Schema.optional( Schema.Struct({ items: Schema.NullOr(Schema.Array(MuseItem)), + noneReason: Schema.optional(Schema.String), snapshot: Schema.optional( Schema.NullOr( Schema.Struct({