sync
parent
a1c20e3e00
commit
167709c48e
|
|
@ -13,7 +13,7 @@ export namespace Agent {
|
|||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
mode: z.enum(["subagent", "primary", "all"]),
|
||||
builtIn: z.boolean(),
|
||||
internal: z.boolean(),
|
||||
topP: z.number().optional(),
|
||||
temperature: z.number().optional(),
|
||||
color: z.string().optional(),
|
||||
|
|
@ -112,7 +112,7 @@ export namespace Agent {
|
|||
options: {},
|
||||
permission: agentPermission,
|
||||
mode: "subagent",
|
||||
builtIn: true,
|
||||
internal: true,
|
||||
},
|
||||
explore: {
|
||||
name: "explore",
|
||||
|
|
@ -147,7 +147,7 @@ export namespace Agent {
|
|||
options: {},
|
||||
permission: agentPermission,
|
||||
mode: "subagent",
|
||||
builtIn: true,
|
||||
internal: true,
|
||||
},
|
||||
build: {
|
||||
name: "build",
|
||||
|
|
@ -155,13 +155,13 @@ export namespace Agent {
|
|||
options: {},
|
||||
permission: agentPermission,
|
||||
mode: "primary",
|
||||
builtIn: true,
|
||||
internal: true,
|
||||
},
|
||||
summary: {
|
||||
name: "summary",
|
||||
mode: "subagent",
|
||||
options: {},
|
||||
builtIn: true,
|
||||
internal: true,
|
||||
permission: agentPermission,
|
||||
prompt: `You are a title generator. You output ONLY a thread title. Nothing else.
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ Your output must be:
|
|||
...defaultTools,
|
||||
},
|
||||
mode: "primary",
|
||||
builtIn: true,
|
||||
internal: true,
|
||||
},
|
||||
}
|
||||
for (const [key, value] of Object.entries(cfg.agent ?? {})) {
|
||||
|
|
@ -226,7 +226,7 @@ Your output must be:
|
|||
permission: agentPermission,
|
||||
options: {},
|
||||
tools: {},
|
||||
builtIn: false,
|
||||
internal: false,
|
||||
}
|
||||
const {
|
||||
name,
|
||||
|
|
|
|||
|
|
@ -91,10 +91,10 @@ export namespace SessionCompaction {
|
|||
providerID: string
|
||||
modelID: string
|
||||
}
|
||||
agent: string
|
||||
abort: AbortSignal
|
||||
auto: boolean
|
||||
}) {
|
||||
const agent = await Agent.get("compaction")
|
||||
const model = await Provider.getModel(input.model.providerID, input.model.modelID)
|
||||
const system = [...SystemPrompt.compaction(model.providerID)]
|
||||
const msg = (await Session.updateMessage({
|
||||
|
|
@ -102,7 +102,8 @@ export namespace SessionCompaction {
|
|||
role: "assistant",
|
||||
parentID: input.parentID,
|
||||
sessionID: input.sessionID,
|
||||
mode: input.agent,
|
||||
mode: "compaction",
|
||||
agent: "compaction",
|
||||
summary: true,
|
||||
path: {
|
||||
cwd: Instance.directory,
|
||||
|
|
@ -127,7 +128,6 @@ export namespace SessionCompaction {
|
|||
model: model,
|
||||
abort: input.abort,
|
||||
})
|
||||
const agent = await Agent.get(input.agent)
|
||||
const result = await processor.process({
|
||||
user: input.messages.findLast((m) => m.info.id === input.parentID)!.info as MessageV2.User,
|
||||
agent,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import type { Agent } from "@/agent/agent"
|
|||
import type { MessageV2 } from "./message-v2"
|
||||
import { Plugin } from "@/plugin"
|
||||
import { SystemPrompt } from "./system"
|
||||
import { ToolRegistry } from "@/tool/registry"
|
||||
|
||||
export namespace LLM {
|
||||
const log = Log.create({ service: "llm" })
|
||||
|
|
@ -43,7 +44,12 @@ export namespace LLM {
|
|||
})
|
||||
const [language, cfg] = await Promise.all([Provider.getLanguage(input.model), Config.get()])
|
||||
|
||||
const [first, ...rest] = [...SystemPrompt.header(input.model.providerID), ...input.system]
|
||||
const [first, ...rest] = [
|
||||
...SystemPrompt.header(input.model.providerID),
|
||||
...(input.agent.prompt ?? SystemPrompt.provider(input.model)),
|
||||
...input.system,
|
||||
...(input.user.system ? [input.user.system] : []),
|
||||
]
|
||||
const system = [first, rest.join("\n")]
|
||||
|
||||
const params = await Plugin.trigger(
|
||||
|
|
@ -80,6 +86,8 @@ export namespace LLM {
|
|||
OUTPUT_TOKEN_MAX,
|
||||
)
|
||||
|
||||
const tools = await resolveTools(input)
|
||||
|
||||
return streamText({
|
||||
onError(error) {
|
||||
l.error("stream error", {
|
||||
|
|
@ -88,7 +96,7 @@ export namespace LLM {
|
|||
},
|
||||
async experimental_repairToolCall(failed) {
|
||||
const lower = failed.toolCall.toolName.toLowerCase()
|
||||
if (lower !== failed.toolCall.toolName && input.tools[lower]) {
|
||||
if (lower !== failed.toolCall.toolName && tools[lower]) {
|
||||
l.info("repairing tool call", {
|
||||
tool: failed.toolCall.toolName,
|
||||
repaired: lower,
|
||||
|
|
@ -110,8 +118,8 @@ export namespace LLM {
|
|||
temperature: params.temperature,
|
||||
topP: params.topP,
|
||||
providerOptions: ProviderTransform.providerOptions(input.model, params.options),
|
||||
activeTools: Object.keys(input.tools).filter((x) => x !== "invalid"),
|
||||
tools: input.tools,
|
||||
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
|
||||
tools,
|
||||
maxOutputTokens,
|
||||
abortSignal: input.abort,
|
||||
headers: {
|
||||
|
|
@ -151,4 +159,16 @@ export namespace LLM {
|
|||
experimental_telemetry: { isEnabled: cfg.experimental?.openTelemetry },
|
||||
})
|
||||
}
|
||||
|
||||
async function resolveTools(input: Pick<StreamInput, "tools" | "agent" | "user">) {
|
||||
const enabled = pipe(
|
||||
input.agent.tools,
|
||||
mergeDeep(await ToolRegistry.enabled(input.agent)),
|
||||
mergeDeep(input.user.tools ?? {}),
|
||||
)
|
||||
for (const [key, value] of Object.entries(enabled)) {
|
||||
if (value === false) delete input.tools[key]
|
||||
}
|
||||
return input.tools
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -348,7 +348,11 @@ export namespace MessageV2 {
|
|||
parentID: z.string(),
|
||||
modelID: z.string(),
|
||||
providerID: z.string(),
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
mode: z.string(),
|
||||
agent: z.string(),
|
||||
path: z.object({
|
||||
cwd: z.string(),
|
||||
root: z.string(),
|
||||
|
|
|
|||
|
|
@ -5,24 +5,22 @@ import z from "zod"
|
|||
import { Identifier } from "../id/id"
|
||||
import { MessageV2 } from "./message-v2"
|
||||
import { Log } from "../util/log"
|
||||
import { Flag } from "../flag/flag"
|
||||
import { SessionRevert } from "./revert"
|
||||
import { Session } from "."
|
||||
import { Agent } from "../agent/agent"
|
||||
import { Provider } from "../provider/provider"
|
||||
import { generateText, type ModelMessage, type Tool as AITool, tool, jsonSchema } from "ai"
|
||||
import { type Tool as AITool, tool, jsonSchema } from "ai"
|
||||
import { SessionCompaction } from "./compaction"
|
||||
import { Instance } from "../project/instance"
|
||||
import { Bus } from "../bus"
|
||||
import { ProviderTransform } from "../provider/transform"
|
||||
import { SystemPrompt } from "./system"
|
||||
import { Plugin } from "../plugin"
|
||||
|
||||
import PROMPT_PLAN from "../session/prompt/plan.txt"
|
||||
import BUILD_SWITCH from "../session/prompt/build-switch.txt"
|
||||
import MAX_STEPS from "../session/prompt/max-steps.txt"
|
||||
import { defer } from "../util/defer"
|
||||
import { clone, mergeDeep, pipe } from "remeda"
|
||||
import { mergeDeep, pipe } from "remeda"
|
||||
import { ToolRegistry } from "../tool/registry"
|
||||
import { Wildcard } from "../util/wildcard"
|
||||
import { MCP } from "../mcp"
|
||||
|
|
@ -36,7 +34,6 @@ import { Command } from "../command"
|
|||
import { $, fileURLToPath } from "bun"
|
||||
import { ConfigMarkdown } from "../config/markdown"
|
||||
import { SessionSummary } from "./summary"
|
||||
import { Config } from "../config/config"
|
||||
import { NamedError } from "@opencode-ai/util/error"
|
||||
import { fn } from "@/util/fn"
|
||||
import { SessionProcessor } from "./processor"
|
||||
|
|
@ -89,8 +86,8 @@ export namespace SessionPrompt {
|
|||
.optional(),
|
||||
agent: z.string().optional(),
|
||||
noReply: z.boolean().optional(),
|
||||
system: z.string().optional(),
|
||||
tools: z.record(z.string(), z.boolean()).optional(),
|
||||
system: z.string().optional(),
|
||||
parts: z.array(
|
||||
z.discriminatedUnion("type", [
|
||||
MessageV2.TextPart.omit({
|
||||
|
|
@ -138,6 +135,20 @@ export namespace SessionPrompt {
|
|||
})
|
||||
export type PromptInput = z.infer<typeof PromptInput>
|
||||
|
||||
export const prompt = fn(PromptInput, async (input) => {
|
||||
const session = await Session.get(input.sessionID)
|
||||
await SessionRevert.cleanup(session)
|
||||
|
||||
const message = await createUserMessage(input)
|
||||
await Session.touch(input.sessionID)
|
||||
|
||||
if (input.noReply === true) {
|
||||
return message
|
||||
}
|
||||
|
||||
return loop(input.sessionID)
|
||||
})
|
||||
|
||||
export async function resolvePromptParts(template: string): Promise<PromptInput["parts"]> {
|
||||
const parts: PromptInput["parts"] = [
|
||||
{
|
||||
|
|
@ -189,20 +200,6 @@ export namespace SessionPrompt {
|
|||
return parts
|
||||
}
|
||||
|
||||
export const prompt = fn(PromptInput, async (input) => {
|
||||
const session = await Session.get(input.sessionID)
|
||||
await SessionRevert.cleanup(session)
|
||||
|
||||
const message = await createUserMessage(input)
|
||||
await Session.touch(input.sessionID)
|
||||
|
||||
if (input.noReply === true) {
|
||||
return message
|
||||
}
|
||||
|
||||
return loop(input.sessionID)
|
||||
})
|
||||
|
||||
function start(sessionID: string) {
|
||||
const s = state()
|
||||
if (s[sessionID]) return
|
||||
|
|
@ -296,6 +293,7 @@ export namespace SessionPrompt {
|
|||
parentID: lastUser.id,
|
||||
sessionID,
|
||||
mode: task.agent,
|
||||
agent: task.agent,
|
||||
path: {
|
||||
cwd: Instance.directory,
|
||||
root: Instance.worktree,
|
||||
|
|
@ -406,7 +404,6 @@ export namespace SessionPrompt {
|
|||
messages: msgs,
|
||||
parentID: lastUser.id,
|
||||
abort,
|
||||
agent: lastUser.agent,
|
||||
model: {
|
||||
providerID: model.providerID,
|
||||
modelID: model.id,
|
||||
|
|
@ -448,6 +445,7 @@ export namespace SessionPrompt {
|
|||
parentID: lastUser.id,
|
||||
role: "assistant",
|
||||
mode: agent.name,
|
||||
agent: agent.name,
|
||||
path: {
|
||||
cwd: Instance.directory,
|
||||
root: Instance.worktree,
|
||||
|
|
@ -470,11 +468,6 @@ export namespace SessionPrompt {
|
|||
model,
|
||||
abort,
|
||||
})
|
||||
const system = await resolveSystemPrompt({
|
||||
model,
|
||||
agent,
|
||||
system: lastUser.system,
|
||||
})
|
||||
const tools = await resolveTools({
|
||||
agent,
|
||||
sessionID,
|
||||
|
|
@ -495,7 +488,7 @@ export namespace SessionPrompt {
|
|||
agent,
|
||||
abort,
|
||||
sessionID,
|
||||
system,
|
||||
system: [...(await SystemPrompt.environment()), ...(await SystemPrompt.custom())],
|
||||
messages: [
|
||||
...MessageV2.toModelMessage(msgs),
|
||||
...(isLastStep
|
||||
|
|
@ -532,21 +525,6 @@ export namespace SessionPrompt {
|
|||
return Provider.defaultModel()
|
||||
}
|
||||
|
||||
async function resolveSystemPrompt(input: { system?: string; agent: Agent.Info; model: Provider.Model }) {
|
||||
using _ = log.time("system")
|
||||
let system = []
|
||||
system.push(
|
||||
...(() => {
|
||||
if (input.system) return [input.system]
|
||||
if (input.agent.prompt) return [input.agent.prompt]
|
||||
return SystemPrompt.provider(input.model)
|
||||
})(),
|
||||
)
|
||||
system.push(...(await SystemPrompt.environment()))
|
||||
system.push(...(await SystemPrompt.custom()))
|
||||
return system
|
||||
}
|
||||
|
||||
async function resolveTools(input: {
|
||||
agent: Agent.Info
|
||||
model: Provider.Model
|
||||
|
|
@ -561,7 +539,6 @@ export namespace SessionPrompt {
|
|||
mergeDeep(await ToolRegistry.enabled(input.agent)),
|
||||
mergeDeep(input.tools ?? {}),
|
||||
)
|
||||
|
||||
for (const item of await ToolRegistry.tools(input.model.providerID)) {
|
||||
if (Wildcard.all(item.id, enabledTools) === false) continue
|
||||
const schema = ProviderTransform.schema(input.model, z.toJSONSchema(item.parameters))
|
||||
|
|
@ -625,7 +602,6 @@ export namespace SessionPrompt {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
for (const [key, item] of Object.entries(await MCP.tools())) {
|
||||
if (Wildcard.all(key, enabledTools) === false) continue
|
||||
const execute = item.execute
|
||||
|
|
@ -704,7 +680,6 @@ export namespace SessionPrompt {
|
|||
created: Date.now(),
|
||||
},
|
||||
tools: input.tools,
|
||||
system: input.system,
|
||||
agent: agent.name,
|
||||
model: input.model ?? agent.model ?? (await lastModel(input.sessionID)),
|
||||
}
|
||||
|
|
@ -995,7 +970,7 @@ export namespace SessionPrompt {
|
|||
synthetic: true,
|
||||
})
|
||||
}
|
||||
const wasPlan = input.messages.some((msg) => msg.info.role === "assistant" && msg.info.mode === "plan")
|
||||
const wasPlan = input.messages.some((msg) => msg.info.role === "assistant" && msg.info.agent === "plan")
|
||||
if (wasPlan && input.agent.name === "build") {
|
||||
userMessage.parts.push({
|
||||
id: Identifier.ascending("part"),
|
||||
|
|
@ -1057,6 +1032,7 @@ export namespace SessionPrompt {
|
|||
sessionID: input.sessionID,
|
||||
parentID: userMsg.id,
|
||||
mode: input.agent,
|
||||
agent: input.agent,
|
||||
cost: 0,
|
||||
path: {
|
||||
cwd: Instance.directory,
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ export namespace SystemPrompt {
|
|||
export function compaction(providerID: string) {
|
||||
switch (providerID) {
|
||||
case "anthropic":
|
||||
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_COMPACTION]
|
||||
return [PROMPT_COMPACTION]
|
||||
default:
|
||||
return [PROMPT_COMPACTION]
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ export namespace SystemPrompt {
|
|||
export function summarize(providerID: string) {
|
||||
switch (providerID) {
|
||||
case "anthropic":
|
||||
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_SUMMARIZE]
|
||||
return [PROMPT_SUMMARIZE]
|
||||
default:
|
||||
return [PROMPT_SUMMARIZE]
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ export namespace SystemPrompt {
|
|||
export function title(providerID: string) {
|
||||
switch (providerID) {
|
||||
case "anthropic":
|
||||
return [PROMPT_ANTHROPIC_SPOOF.trim(), PROMPT_TITLE]
|
||||
return [PROMPT_TITLE]
|
||||
default:
|
||||
return [PROMPT_TITLE]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue