import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { Context, Effect, Layer } from "effect" import { InstanceState } from "@/effect/instance-state" import PROMPT_ANTHROPIC from "./prompt/anthropic.txt" import PROMPT_DEFAULT from "./prompt/default.txt" import PROMPT_BEAST from "./prompt/beast.txt" import PROMPT_GEMINI from "./prompt/gemini.txt" import PROMPT_GPT from "./prompt/gpt.txt" import PROMPT_KIMI from "./prompt/kimi.txt" import PROMPT_CODEX from "./prompt/codex.txt" import PROMPT_TRINITY from "./prompt/trinity.txt" import type { Provider } from "@/provider/provider" import type { Agent } from "@/agent/agent" import { Permission } from "@/permission" import { Skill } from "@/skill" import { AbsolutePath } from "@opencode-ai/core/schema" import { Location } from "@opencode-ai/core/location" import { LocationServiceMap, locationServiceMapLayer } from "@opencode-ai/core/location-services" import { Reference } from "@opencode-ai/core/reference" import { MCP } from "@/mcp" import { PermissionV1 } from "@opencode-ai/core/v1/permission" export function provider(model: Provider.Model) { if (model.api.id.includes("gpt-4") || model.api.id.includes("o1") || model.api.id.includes("o3")) return [PROMPT_BEAST] if (model.api.id.includes("gpt")) { if (model.api.id.includes("codex")) { return [PROMPT_CODEX] } return [PROMPT_GPT] } if (model.api.id.includes("gemini-")) return [PROMPT_GEMINI] if (model.api.id.includes("claude")) return [PROMPT_ANTHROPIC] if (model.api.id.toLowerCase().includes("trinity")) return [PROMPT_TRINITY] if (model.api.id.toLowerCase().includes("kimi")) return [PROMPT_KIMI] return [PROMPT_DEFAULT] } export interface Interface { readonly environment: (model: Provider.Model) => Effect.Effect readonly skills: (agent: Agent.Info) => Effect.Effect readonly mcp: (agent: Agent.Info, permission?: PermissionV1.Ruleset) => Effect.Effect } export class Service extends Context.Service()("@opencode/SystemPrompt") {} const layer = Layer.effect( Service, Effect.gen(function* () { const skill = yield* Skill.Service const mcp = yield* MCP.Service const locations = yield* LocationServiceMap.Service return Service.of({ environment: Effect.fn("SystemPrompt.environment")(function* (model: Provider.Model) { const ctx = yield* InstanceState.context const references = yield* Effect.gen(function* () { return (yield* (yield* Reference.Service).list()).filter((reference) => reference.description !== undefined) }).pipe(Effect.provide(locations.get(Location.Ref.make({ directory: AbsolutePath.make(ctx.directory) })))) return [ [ `You are powered by the model named ${model.api.id}. The exact model ID is ${model.providerID}/${model.api.id}`, `Here is some useful information about the environment you are running in:`, ``, ` Working directory: ${ctx.directory}`, ` Workspace root folder: ${ctx.worktree}`, ` Is directory a git repo: ${ctx.project.vcs === "git" ? "yes" : "no"}`, ` Platform: ${process.platform}`, ` Today's date: ${new Date().toDateString()}`, ``, ].join("\n"), references.length === 0 ? undefined : [ "Project references provide additional directories that can be accessed when relevant.", "", ...references .toSorted((a, b) => a.name.localeCompare(b.name)) .flatMap((reference) => [ " ", ` ${reference.name}`, ` ${reference.path}`, ...(reference.description === undefined ? [] : [` ${reference.description}`]), " ", ]), "", ].join("\n"), ].filter((part): part is string => part !== undefined) }), skills: Effect.fn("SystemPrompt.skills")(function* (agent: Agent.Info) { if (Permission.disabled(["skill"], agent.permission).has("skill")) return const list = yield* skill.available(agent) return [ "Skills provide specialized instructions and workflows for specific tasks.", "Use the skill tool to load a skill when a task matches its description.", // the agents seem to ingest the information about skills a bit better if we present a more verbose // version of them here and a less verbose version in tool description, rather than vice versa. Skill.fmt(list, { verbose: true }), ].join("\n") }), mcp: Effect.fn("SystemPrompt.mcp")(function* (agent: Agent.Info, permission?: PermissionV1.Ruleset) { const ruleset = Permission.merge(agent.permission, permission ?? []) const instructions = (yield* mcp.instructions()).filter( (item) => item.tools.length === 0 || Permission.disabled(item.tools, ruleset).size < item.tools.length, ) if (instructions.length === 0) return return [ "", ...instructions.flatMap((item) => [ ` `, ...item.instructions.split("\n").map((line) => ` ${line}`), " ", ]), "", ].join("\n") }), }) }), ) const locationServiceMapNode = LayerNode.make({ service: LocationServiceMap.Service, layer: locationServiceMapLayer, deps: [], }) export const node = LayerNode.make({ service: Service, layer: layer, deps: [Skill.node, MCP.node, locationServiceMapNode], }) export * as SystemPrompt from "./system"