fix(core): derive previous agent in projection (#41685)
This commit is contained in:
@@ -339,11 +339,7 @@ export type Endpoint5_31Output =
|
||||
readonly type: "session.agent.selected"
|
||||
readonly durable: { readonly aggregateID: string; readonly seq: Event.Seq; readonly version: Event.Version }
|
||||
readonly location?: Location.Ref | undefined
|
||||
readonly data: {
|
||||
readonly sessionID: Session.ID
|
||||
readonly agent: Agent.ID
|
||||
readonly previous?: Agent.ID | undefined
|
||||
}
|
||||
readonly data: { readonly sessionID: Session.ID; readonly agent: Agent.ID }
|
||||
}
|
||||
| {
|
||||
readonly id: Event.ID
|
||||
|
||||
@@ -436,7 +436,7 @@ export type SessionAgentSelected = {
|
||||
type: "session.agent.selected"
|
||||
durable: { aggregateID: string; seq: number; version: 1 }
|
||||
location?: LocationRef
|
||||
data: { sessionID: string; agent: string; previous?: string }
|
||||
data: { sessionID: string; agent: string }
|
||||
}
|
||||
|
||||
export type SessionModelSelected = {
|
||||
|
||||
@@ -349,6 +349,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: import("../p
|
||||
input?.location ?? Location.Ref.make({ directory: location.directory, workspaceID: location.workspaceID }),
|
||||
}),
|
||||
get: (input) => runtime.session.get(input.sessionID),
|
||||
message: runtime.session.message,
|
||||
prompt: runtime.session.prompt,
|
||||
generate: (input) => runtime.session.generate(input).pipe(Effect.map((text) => ({ text }))),
|
||||
command: runtime.session.command,
|
||||
|
||||
@@ -2,6 +2,7 @@ export * as PlanPlugin from "./plan"
|
||||
|
||||
import { ToolFailure } from "@opencode-ai/ai"
|
||||
import { define } from "@opencode-ai/plugin/effect/plugin"
|
||||
import { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
import { Effect, Stream } from "effect"
|
||||
import { Agent } from "../agent"
|
||||
|
||||
@@ -37,18 +38,18 @@ export const Plugin = define({
|
||||
|
||||
yield* ctx.event.subscribe().pipe(
|
||||
Stream.filter((event) => event.type === "session.agent.selected"),
|
||||
Stream.runForEach((event) => {
|
||||
if (event.data.agent === event.data.previous) return Effect.void
|
||||
const text = event.data.agent === plan ? enter : event.data.previous === plan ? leave : undefined
|
||||
if (!text) return Effect.void
|
||||
return ctx.session
|
||||
.synthetic({
|
||||
Stream.runForEach((event) =>
|
||||
Effect.gen(function* () {
|
||||
const message = yield* ctx.session.message({
|
||||
sessionID: event.data.sessionID,
|
||||
text,
|
||||
resume: false,
|
||||
messageID: SessionMessage.ID.fromEvent(event.id),
|
||||
})
|
||||
.pipe(Effect.catch(() => Effect.void))
|
||||
}),
|
||||
if (message?.type !== "agent-switched" || message.agent === message.previous) return
|
||||
const text = message.agent === plan ? enter : message.previous === plan ? leave : undefined
|
||||
if (!text) return
|
||||
yield* ctx.session.synthetic({ sessionID: event.data.sessionID, text, resume: false })
|
||||
}).pipe(Effect.catch(() => Effect.void)),
|
||||
),
|
||||
Effect.forkScoped({ startImmediately: true }),
|
||||
)
|
||||
}),
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface Interface {
|
||||
| "get"
|
||||
| "create"
|
||||
| "messages"
|
||||
| "message"
|
||||
| "prompt"
|
||||
| "generate"
|
||||
| "command"
|
||||
@@ -59,6 +60,7 @@ export const layerWithCell = (cell: Cell) =>
|
||||
get: (sessionID) => require(cell, (runtime) => runtime.session.get(sessionID)),
|
||||
create: (input) => require(cell, (runtime) => runtime.session.create(input)),
|
||||
messages: (input) => require(cell, (runtime) => runtime.session.messages(input)),
|
||||
message: (input) => require(cell, (runtime) => runtime.session.message(input)),
|
||||
prompt: (input) => require(cell, (runtime) => runtime.session.prompt(input)),
|
||||
generate: (input) => require(cell, (runtime) => runtime.session.generate(input)),
|
||||
command: (input) => require(cell, (runtime) => runtime.session.command(input)),
|
||||
|
||||
@@ -716,11 +716,10 @@ const layer = Layer.effect(
|
||||
.pipe(Effect.ignore, Effect.forkIn(scope, { startImmediately: true }), Effect.asVoid)
|
||||
}),
|
||||
switchAgent: Effect.fn("Session.switchAgent")(function* (input) {
|
||||
const session = yield* result.get(input.sessionID)
|
||||
yield* result.get(input.sessionID)
|
||||
yield* bus.publish(SessionEvent.AgentSelected, {
|
||||
sessionID: input.sessionID,
|
||||
agent: input.agent,
|
||||
previous: session.agent,
|
||||
})
|
||||
}),
|
||||
switchModel: Effect.fn("Session.switchModel")(function* (input) {
|
||||
|
||||
@@ -61,7 +61,7 @@ export function update(adapter: Adapter, event: SessionEvent.DurableEvent) {
|
||||
"session.usage.recorded": () => Effect.void,
|
||||
"session.agent.selected": (event) => {
|
||||
return Effect.gen(function* () {
|
||||
const previous = event.data.previous ?? (yield* adapter.getAgent())
|
||||
const previous = yield* adapter.getAgent()
|
||||
yield* adapter.appendMessage(
|
||||
SessionMessage.AgentSelected.make({
|
||||
id: SessionMessage.ID.fromEvent(event.id),
|
||||
|
||||
@@ -102,6 +102,7 @@ export function host(overrides: Overrides = {}): Plugin.Context {
|
||||
hook: overrides.session?.hook ?? (() => Effect.die("unused session.hook")),
|
||||
create: overrides.session?.create ?? (() => Effect.die("unused session.create")),
|
||||
get: overrides.session?.get ?? (() => Effect.die("unused session.get")),
|
||||
message: overrides.session?.message ?? (() => Effect.die("unused session.message")),
|
||||
prompt: overrides.session?.prompt ?? (() => Effect.die("unused session.prompt")),
|
||||
generate: overrides.session?.generate ?? (() => Effect.die("unused session.generate")),
|
||||
command: overrides.session?.command ?? (() => Effect.die("unused session.command")),
|
||||
|
||||
@@ -654,7 +654,7 @@ describe("Session.create", () => {
|
||||
expect(yield* session.get(created.id)).toMatchObject({ agent: "plan" })
|
||||
expect(
|
||||
Array.from(yield* logEvents(session, created.id, true).pipe(Stream.drop(1), Stream.take(1), Stream.runCollect)),
|
||||
).toMatchObject([{ type: "session.agent.selected", data: { agent: "plan", previous: "build" } }])
|
||||
).toMatchObject([{ type: "session.agent.selected", data: { agent: "plan" } }])
|
||||
expect(yield* session.messages({ sessionID: created.id, order: "asc" })).toMatchObject([
|
||||
{ type: "agent-switched", agent: "plan", previous: "build" },
|
||||
])
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { Message, SystemPart } from "@opencode-ai/ai"
|
||||
import type { Agent } from "@opencode-ai/schema/agent"
|
||||
import type { Model } from "@opencode-ai/schema/model"
|
||||
import type { Session } from "@opencode-ai/schema/session"
|
||||
import type { JsonSchema } from "effect"
|
||||
import type { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
import type { Effect, JsonSchema } from "effect"
|
||||
import type { Hooks } from "./registration.js"
|
||||
|
||||
export interface SessionContext {
|
||||
@@ -40,5 +41,9 @@ export type SessionDomain = Pick<
|
||||
SessionApi<unknown>,
|
||||
"create" | "get" | "prompt" | "generate" | "command" | "synthetic" | "interrupt" | "rename" | "wait"
|
||||
> & {
|
||||
readonly message: (input: {
|
||||
readonly sessionID: Session.ID
|
||||
readonly messageID: SessionMessage.ID
|
||||
}) => Effect.Effect<SessionMessage.Info | undefined, unknown>
|
||||
readonly hook: Hooks<SessionHooks>
|
||||
}
|
||||
|
||||
@@ -14373,9 +14373,6 @@
|
||||
},
|
||||
"agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"previous": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["sessionID", "agent"],
|
||||
|
||||
@@ -69,7 +69,6 @@ export const AgentSelected = Event.durable({
|
||||
schema: {
|
||||
...Base,
|
||||
agent: Agent.ID,
|
||||
previous: Agent.ID.pipe(optional),
|
||||
},
|
||||
})
|
||||
export type AgentSelected = typeof AgentSelected.Type
|
||||
|
||||
@@ -14373,9 +14373,6 @@
|
||||
},
|
||||
"agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"previous": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["sessionID", "agent"],
|
||||
|
||||
@@ -14373,9 +14373,6 @@
|
||||
},
|
||||
"agent": {
|
||||
"type": "string"
|
||||
},
|
||||
"previous": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["sessionID", "agent"],
|
||||
|
||||
Reference in New Issue
Block a user