refactor: break SessionPrompt/TaskTool cycle via ctx injection (#21948)

This commit is contained in:
Kit Langton
2026-04-10 19:36:13 -04:00
committed by GitHub
parent d72ddd71fa
commit d9d5a0615e
3 changed files with 47 additions and 67 deletions

View File

@@ -46,7 +46,7 @@ import { Process } from "@/util/process"
import { Cause, Effect, Exit, Layer, Option, Scope, ServiceMap } from "effect"
import { InstanceState } from "@/effect/instance-state"
import { makeRuntime } from "@/effect/run-service"
import { TaskTool } from "@/tool/task"
import { TaskTool, type TaskPromptOps } from "@/tool/task"
import { SessionRunState } from "./run-state"
// @ts-ignore
@@ -356,7 +356,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
abort: options.abortSignal!,
messageID: input.processor.message.id,
callID: options.toolCallId,
extra: { model: input.model, bypassAgentCheck: input.bypassAgentCheck },
extra: { model: input.model, bypassAgentCheck: input.bypassAgentCheck, promptOps },
agent: input.agent.name,
messages: input.messages,
metadata: (val) =>
@@ -586,7 +586,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
sessionID,
abort: signal,
callID: part.callID,
extra: { bypassAgentCheck: true },
extra: { bypassAgentCheck: true, promptOps },
messages: msgs,
metadata(val: { title?: string; metadata?: Record<string, any> }) {
return Effect.runPromise(
@@ -1655,6 +1655,12 @@ NOTE: At any point in time through this workflow you should feel free to ask the
return result
})
const promptOps: TaskPromptOps = {
cancel: (sessionID) => Effect.runFork(cancel(sessionID)),
resolvePromptParts: (template) => Effect.runPromise(resolvePromptParts(template)),
prompt: (input) => Effect.runPromise(prompt(input)),
}
return Service.of({
cancel,
prompt,

View File

@@ -5,11 +5,17 @@ import { Session } from "../session"
import { SessionID, MessageID } from "../session/schema"
import { MessageV2 } from "../session/message-v2"
import { Agent } from "../agent/agent"
import { SessionPrompt } from "../session/prompt"
import type { SessionPrompt } from "../session/prompt"
import { Config } from "../config/config"
import { Effect } from "effect"
import { Log } from "@/util/log"
export interface TaskPromptOps {
cancel(sessionID: SessionID): void
resolvePromptParts(template: string): Promise<SessionPrompt.PromptInput["parts"]>
prompt(input: SessionPrompt.PromptInput): Promise<MessageV2.WithParts>
}
const id = "task"
const parameters = z.object({
@@ -113,10 +119,13 @@ export const TaskTool = Tool.defineEffect(
},
})
const ops = ctx.extra?.promptOps as TaskPromptOps
if (!ops) return yield* Effect.fail(new Error("TaskTool requires promptOps in ctx.extra"))
const messageID = MessageID.ascending()
function cancel() {
SessionPrompt.cancel(nextSession.id)
ops.cancel(nextSession.id)
}
return yield* Effect.acquireUseRelease(
@@ -125,9 +134,9 @@ export const TaskTool = Tool.defineEffect(
}),
() =>
Effect.gen(function* () {
const parts = yield* Effect.promise(() => SessionPrompt.resolvePromptParts(params.prompt))
const parts = yield* Effect.promise(() => ops.resolvePromptParts(params.prompt))
const result = yield* Effect.promise(() =>
SessionPrompt.prompt({
ops.prompt({
messageID,
sessionID: nextSession.id,
model: {