+16




![opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)


![opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)
![opencode-agent[bot]](/assets/img/avatar_default.png)
opencode-agent[bot]
opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
usrnk1
James Long
opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Aiden Cline
Brendan Allan
Jack
opencode
Frank
Jay
Luke Parker
Aarav Sareen
Julian Coy
Brendan Allan
Vladimir Glafirov
Adam
Dustin Deus
Kit Langton
Simon Klee
Jay
David Hill
Aiden Cline
James Long
冯基魁
Aiden Cline
a7746379d5
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Julian Coy <julian@ex-machina.co> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Jay <air@live.ca> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: James Long <jlongster@users.noreply.github.com> Co-authored-by: 冯基魁 <56265583+fengjikui@users.noreply.github.com> Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
150 lines
4.4 KiB
TypeScript
150 lines
4.4 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
|
import { Effect, Layer } from "effect"
|
|
import type { Agent } from "../../src/agent/agent"
|
|
import { NamedError } from "@opencode-ai/core/util/error"
|
|
import { Skill } from "../../src/skill"
|
|
import { Permission } from "../../src/permission"
|
|
import type { Provider } from "../../src/provider/provider"
|
|
import { SystemPrompt } from "../../src/session/system"
|
|
import { MCP } from "../../src/mcp"
|
|
import { testEffect } from "../lib/effect"
|
|
|
|
const skills: Skill.Info[] = [
|
|
{
|
|
name: "zeta-skill",
|
|
description: "Zeta skill.",
|
|
location: "/tmp/zeta-skill/SKILL.md",
|
|
content: "# zeta-skill",
|
|
},
|
|
{
|
|
name: "alpha-skill",
|
|
description: "Alpha skill.",
|
|
location: "/tmp/alpha-skill/SKILL.md",
|
|
content: "# alpha-skill",
|
|
},
|
|
{
|
|
name: "middle-skill",
|
|
description: "Middle skill.",
|
|
location: "/tmp/middle-skill/SKILL.md",
|
|
content: "# middle-skill",
|
|
},
|
|
{
|
|
name: "manual-skill",
|
|
location: "/tmp/manual-skill/SKILL.md",
|
|
content: "# manual-skill",
|
|
},
|
|
]
|
|
|
|
const build: Agent.Info = {
|
|
name: "build",
|
|
mode: "primary",
|
|
permission: Permission.fromConfig({ "*": "allow" }),
|
|
options: {},
|
|
}
|
|
|
|
const it = testEffect(
|
|
LayerNode.compile(SystemPrompt.node, [
|
|
[
|
|
MCP.node,
|
|
Layer.mock(MCP.Service, {
|
|
instructions: () =>
|
|
Effect.succeed([
|
|
{
|
|
name: "guide-server",
|
|
instructions: "Use lookup before mutate.",
|
|
tools: [],
|
|
},
|
|
{
|
|
name: "tool-server",
|
|
instructions: "Prefer search before update.",
|
|
tools: ["tool-server_search", "tool-server_update"],
|
|
},
|
|
]),
|
|
}),
|
|
],
|
|
[
|
|
Skill.node,
|
|
Layer.succeed(
|
|
Skill.Service,
|
|
Skill.Service.of({
|
|
get: (name) => Effect.succeed(skills.find((skill) => skill.name === name)),
|
|
require: (name) => {
|
|
const info = skills.find((skill) => skill.name === name)
|
|
if (info) return Effect.succeed(info)
|
|
return Effect.fail(new Skill.NotFoundError({ name, available: skills.map((skill) => skill.name) }))
|
|
},
|
|
all: () => Effect.succeed(skills),
|
|
dirs: () => Effect.succeed([]),
|
|
available: () => Effect.succeed(skills),
|
|
}),
|
|
),
|
|
],
|
|
]),
|
|
)
|
|
|
|
describe("session.system", () => {
|
|
test("selects the Meta prompt for Muse Spark model IDs", () => {
|
|
expect(SystemPrompt.provider({ api: { id: "meta/muse-spark-preview" } } as Provider.Model)[0]).toContain(
|
|
"Meta Muse Spark",
|
|
)
|
|
})
|
|
|
|
it.effect("skills output is sorted by name and stable across calls", () =>
|
|
Effect.gen(function* () {
|
|
const prompt = yield* SystemPrompt.Service
|
|
const first = yield* prompt.skills(build)
|
|
const second = yield* prompt.skills(build)
|
|
const output = first ?? (yield* Effect.fail(new NamedError.Unknown({ message: "missing skills output" })))
|
|
|
|
expect(first).toBe(second)
|
|
|
|
const alpha = output.indexOf("<name>alpha-skill</name>")
|
|
const middle = output.indexOf("<name>middle-skill</name>")
|
|
const zeta = output.indexOf("<name>zeta-skill</name>")
|
|
|
|
expect(alpha).toBeGreaterThan(-1)
|
|
expect(middle).toBeGreaterThan(alpha)
|
|
expect(zeta).toBeGreaterThan(middle)
|
|
expect(output).not.toContain("manual-skill")
|
|
}),
|
|
)
|
|
|
|
it.effect("MCP output includes connected server instructions", () =>
|
|
Effect.gen(function* () {
|
|
const prompt = yield* SystemPrompt.Service
|
|
const output = yield* prompt.mcp(build)
|
|
|
|
expect(output).toBe(
|
|
[
|
|
"<mcp_instructions>",
|
|
' <server name="guide-server">',
|
|
" Use lookup before mutate.",
|
|
" </server>",
|
|
' <server name="tool-server">',
|
|
" Prefer search before update.",
|
|
" </server>",
|
|
"</mcp_instructions>",
|
|
].join("\n"),
|
|
)
|
|
}),
|
|
)
|
|
|
|
it.effect("MCP output omits servers when all advertised tools are denied", () =>
|
|
Effect.gen(function* () {
|
|
const prompt = yield* SystemPrompt.Service
|
|
const output = yield* prompt.mcp(build, Permission.fromConfig({ "tool-server_*": "deny" }))
|
|
|
|
expect(output).toBe(
|
|
[
|
|
"<mcp_instructions>",
|
|
' <server name="guide-server">',
|
|
" Use lookup before mutate.",
|
|
" </server>",
|
|
"</mcp_instructions>",
|
|
].join("\n"),
|
|
)
|
|
}),
|
|
)
|
|
})
|