refactor(opencode): reuse deferred MCP resolution
This commit is contained in:
parent
0bff5bf389
commit
c2d619741b
4 changed files with 30 additions and 46 deletions
|
|
@ -61,6 +61,11 @@ interface DeferredToolDescriptor {
|
|||
searchText: string
|
||||
}
|
||||
|
||||
interface Output {
|
||||
tools: Record<string, Tool>
|
||||
deferredSystemPrompt?: string
|
||||
}
|
||||
|
||||
const SearchDeferredToolsParameters = Schema.Struct({
|
||||
query: Schema.String.annotate({
|
||||
description:
|
||||
|
|
@ -229,9 +234,8 @@ export const resolve = Effect.fn("SessionMcpTools.resolve")(function* (input: In
|
|||
return output
|
||||
})
|
||||
|
||||
const hasMcpResourceServer = Object.values(yield* mcp.clients()).some(
|
||||
(client) => !!client.getServerCapabilities()?.resources,
|
||||
)
|
||||
const clients = yield* mcp.clients()
|
||||
const hasMcpResourceServer = Object.values(clients).some((client) => !!client.getServerCapabilities()?.resources)
|
||||
if (hasMcpResourceServer) addResourceTools(tools, { input, run, mcp, plugin, truncate, context })
|
||||
|
||||
const mcpTools = yield* mcp.tools()
|
||||
|
|
@ -261,7 +265,10 @@ export const resolve = Effect.fn("SessionMcpTools.resolve")(function* (input: In
|
|||
allowedMcpTools,
|
||||
executeMcpTool,
|
||||
})
|
||||
return tools
|
||||
return {
|
||||
tools,
|
||||
deferredSystemPrompt: deferredSystemPrompt(allowedMcpTools, clients),
|
||||
} satisfies Output
|
||||
}
|
||||
|
||||
for (const [key, item] of Object.entries(mcpTools)) {
|
||||
|
|
@ -275,35 +282,11 @@ export const resolve = Effect.fn("SessionMcpTools.resolve")(function* (input: In
|
|||
tools[key] = item
|
||||
}
|
||||
|
||||
return tools
|
||||
return { tools } satisfies Output
|
||||
})
|
||||
|
||||
export const systemPrompt = Effect.fn("SessionMcpTools.systemPrompt")(function* (input: {
|
||||
agent: Agent.Info
|
||||
session: Session.Info
|
||||
messages: SessionV1.WithParts[]
|
||||
}) {
|
||||
const mcp = yield* MCP.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
if (!flags.experimentalToolSearch) return undefined
|
||||
|
||||
const mcpTools = yield* mcp.tools()
|
||||
const mcpDisabled = Permission.disabled(
|
||||
Object.keys(mcpTools),
|
||||
Permission.merge(input.agent.permission, input.session.permission ?? []),
|
||||
)
|
||||
const userTools = currentUserToolOverrides(input.messages)
|
||||
const allowedTools = Object.fromEntries(
|
||||
Object.entries(mcpTools).filter(([key]) => userTools?.[key] !== false && !mcpDisabled.has(key)),
|
||||
)
|
||||
if (Object.keys(allowedTools).length === 0) return undefined
|
||||
|
||||
const deferredDescriptors = yield* deferredToolDescriptors(allowedTools)
|
||||
if (deferredToolSchemaTokens(deferredDescriptors) < MIN_DEFERRED_MCP_SCHEMA_TOKENS) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const servers = deferredServerSummaries(Object.keys(allowedTools), Object.keys(yield* mcp.clients()))
|
||||
function deferredSystemPrompt(allowedMcpTools: Record<string, Tool>, clients: Record<string, unknown>) {
|
||||
const servers = deferredServerSummaries(Object.keys(allowedMcpTools), Object.keys(clients))
|
||||
return [
|
||||
DEFERRED_TOOL_SYSTEM_PROMPT,
|
||||
servers.length === 0
|
||||
|
|
@ -315,7 +298,7 @@ export const systemPrompt = Effect.fn("SessionMcpTools.systemPrompt")(function*
|
|||
]
|
||||
.filter((part): part is string => part !== undefined)
|
||||
.join("\n\n")
|
||||
})
|
||||
}
|
||||
|
||||
function addResourceTools(
|
||||
tools: Record<string, Tool>,
|
||||
|
|
|
|||
|
|
@ -1223,7 +1223,7 @@ export const layer = Layer.effect(
|
|||
const bypassAgentCheck = lastUserMsg?.parts.some((p) => p.type === "agent") ?? false
|
||||
const promptOps = yield* ops()
|
||||
|
||||
const tools = yield* SessionTools.resolve({
|
||||
const resolvedTools = yield* SessionTools.resolve({
|
||||
agent,
|
||||
session,
|
||||
model,
|
||||
|
|
@ -1240,6 +1240,7 @@ export const layer = Layer.effect(
|
|||
Effect.provideService(Truncate.Service, truncate),
|
||||
Effect.provideService(RuntimeFlags.Service, flags),
|
||||
)
|
||||
const tools = resolvedTools.tools
|
||||
|
||||
if (lastUser.format?.type === "json_schema") {
|
||||
tools["StructuredOutput"] = createStructuredOutputTool({
|
||||
|
|
@ -1260,12 +1261,7 @@ export const layer = Layer.effect(
|
|||
sys.environment(model),
|
||||
instruction.system().pipe(Effect.orDie),
|
||||
sys.mcp(agent, session.permission),
|
||||
tools.search_deferred_tools && tools.call_deferred_tool
|
||||
? SessionTools.deferredSystemPrompt({ agent, session, messages: msgs }).pipe(
|
||||
Effect.provideService(MCP.Service, mcp),
|
||||
Effect.provideService(RuntimeFlags.Service, flags),
|
||||
)
|
||||
: Effect.succeed(undefined),
|
||||
Effect.succeed(resolvedTools.deferredSystemPrompt),
|
||||
MessageV2.toModelMessagesEffect(msgs, model),
|
||||
])
|
||||
const system = [
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ import { SessionProcessor } from "./processor"
|
|||
import { Session } from "./session"
|
||||
import { PartID } from "./schema"
|
||||
|
||||
export const DEFERRED_TOOL_SYSTEM_PROMPT = SessionMcpTools.DEFERRED_TOOL_SYSTEM_PROMPT
|
||||
export const deferredSystemPrompt = SessionMcpTools.systemPrompt
|
||||
|
||||
export const resolve = Effect.fn("SessionTools.resolve")(function* (input: {
|
||||
agent: Agent.Info
|
||||
model: Provider.Model
|
||||
|
|
@ -112,8 +109,12 @@ export const resolve = Effect.fn("SessionTools.resolve")(function* (input: {
|
|||
})
|
||||
}
|
||||
|
||||
Object.assign(tools, yield* SessionMcpTools.resolve(input))
|
||||
return tools
|
||||
const mcp = yield* SessionMcpTools.resolve(input)
|
||||
Object.assign(tools, mcp.tools)
|
||||
return {
|
||||
tools,
|
||||
deferredSystemPrompt: mcp.deferredSystemPrompt,
|
||||
}
|
||||
})
|
||||
|
||||
export * as SessionTools from "./tools"
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ const belowThresholdIt = makeIt({
|
|||
queryDescription: "Natural language analytics query",
|
||||
})
|
||||
|
||||
function resolveTools(input: { messages?: SessionV1.WithParts[]; canDeferMcpTools?: boolean } = {}) {
|
||||
function resolveToolResult(input: { messages?: SessionV1.WithParts[]; canDeferMcpTools?: boolean } = {}) {
|
||||
return SessionTools.resolve({
|
||||
agent,
|
||||
model,
|
||||
|
|
@ -145,6 +145,10 @@ function resolveTools(input: { messages?: SessionV1.WithParts[]; canDeferMcpTool
|
|||
})
|
||||
}
|
||||
|
||||
function resolveTools(input: { messages?: SessionV1.WithParts[]; canDeferMcpTools?: boolean } = {}) {
|
||||
return resolveToolResult(input).pipe(Effect.map((result) => result.tools))
|
||||
}
|
||||
|
||||
describe("session.tools", () => {
|
||||
deferredIt.instance("defers MCP tools behind fixed search and call tools", () =>
|
||||
Effect.gen(function* () {
|
||||
|
|
@ -181,7 +185,7 @@ describe("session.tools", () => {
|
|||
|
||||
deferredIt.instance("lists deferred MCP servers in the system prompt", () =>
|
||||
Effect.gen(function* () {
|
||||
const prompt = yield* SessionTools.deferredSystemPrompt({ agent, session, messages: [] })
|
||||
const prompt = (yield* resolveToolResult()).deferredSystemPrompt
|
||||
|
||||
expect(prompt).toContain("Deferred MCP servers available through `search_deferred_tools`:")
|
||||
expect(prompt).toContain("- posthog: 2 tools")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue