feat(core): expose session switching endpoints

This commit is contained in:
Dax Raad
2026-06-21 23:59:01 -04:00
parent 9dadc2455f
commit 35b3fc85d0
7 changed files with 283 additions and 4 deletions
+9 -3
View File
@@ -129,7 +129,7 @@ export interface Interface {
readonly switchAgent: (input: {
sessionID: SessionSchema.ID
agent: string
}) => Effect.Effect<void, OperationUnavailableError>
}) => Effect.Effect<void, NotFoundError>
readonly switchModel: (input: {
sessionID: SessionSchema.ID
model: ModelV2.Ref
@@ -376,8 +376,14 @@ export const layer = Layer.effect(
skill: Effect.fn("V2Session.skill")(function* () {
return yield* new OperationUnavailableError({ operation: "skill" })
}),
switchAgent: Effect.fn("V2Session.switchAgent")(function* () {
return yield* new OperationUnavailableError({ operation: "switchAgent" })
switchAgent: Effect.fn("V2Session.switchAgent")(function* (input) {
yield* result.get(input.sessionID)
yield* events.publish(SessionEvent.AgentSwitched, {
sessionID: input.sessionID,
messageID: SessionMessage.ID.create(),
timestamp: yield* DateTime.now,
agent: input.agent,
})
}),
switchModel: Effect.fn("V2Session.switchModel")(function* (input) {
yield* result.get(input.sessionID)
+28 -1
View File
@@ -336,7 +336,34 @@ describe("SessionV2.create", () => {
expect(yield* unavailable(session.shell({ sessionID: created.id, command: "pwd" }))).toBe("shell")
expect(yield* unavailable(session.skill({ sessionID: created.id, skill: "review" }))).toBe("skill")
expect(yield* unavailable(session.switchAgent({ sessionID: created.id, agent: "build" }))).toBe("switchAgent")
}),
)
it.effect("switches the selected agent through the durable Session event", () =>
Effect.gen(function* () {
const session = yield* SessionV2.Service
const created = yield* session.create({ location })
yield* session.switchAgent({ sessionID: created.id, agent: "plan" })
expect(yield* session.get(created.id)).toMatchObject({ agent: "plan" })
expect(
Array.from(yield* session.events({ sessionID: created.id }).pipe(Stream.take(1), Stream.runCollect)),
).toMatchObject([{ type: "session.next.agent.switched", data: { agent: "plan" } }])
}),
)
it.effect("rejects an agent switch for a missing Session", () =>
Effect.gen(function* () {
const session = yield* SessionV2.Service
const missing = SessionV2.ID.make("ses_missing_agent_switch")
expect(
yield* session.switchAgent({ sessionID: missing, agent: "plan" }).pipe(
Effect.flip,
Effect.map((error) => error._tag),
),
).toBe("Session.NotFoundError")
}),
)