refactor(core): rename guidance modules (#37207)
This commit is contained in:
parent
d01dfa57b7
commit
a5b28c2af2
9 changed files with 103 additions and 97 deletions
|
|
@ -27,13 +27,13 @@ import { Pty } from "./pty"
|
||||||
import { QuestionV2 } from "./question"
|
import { QuestionV2 } from "./question"
|
||||||
import { Shell } from "./shell"
|
import { Shell } from "./shell"
|
||||||
import { Reference } from "./reference"
|
import { Reference } from "./reference"
|
||||||
import { ReferenceGuidance } from "./reference/guidance"
|
import { ReferenceInstructions } from "./reference/instructions"
|
||||||
import { SessionRunnerLLM } from "./session/runner/llm"
|
import { SessionRunnerLLM } from "./session/runner/llm"
|
||||||
import { SessionRunnerModel } from "./session/runner/model"
|
import { SessionRunnerModel } from "./session/runner/model"
|
||||||
import { SessionCompaction } from "./session/compaction"
|
import { SessionCompaction } from "./session/compaction"
|
||||||
import { SessionTitle } from "./session/title"
|
import { SessionTitle } from "./session/title"
|
||||||
import { SkillV2 } from "./skill"
|
import { SkillV2 } from "./skill"
|
||||||
import { SkillGuidance } from "./skill/guidance"
|
import { SkillInstructions } from "./skill/instructions"
|
||||||
import { Snapshot } from "./snapshot"
|
import { Snapshot } from "./snapshot"
|
||||||
import { InstructionDiscovery } from "./instruction-discovery"
|
import { InstructionDiscovery } from "./instruction-discovery"
|
||||||
import { InstructionBuiltIns } from "./instructions/builtins"
|
import { InstructionBuiltIns } from "./instructions/builtins"
|
||||||
|
|
@ -75,8 +75,8 @@ const locationServiceNodes = [
|
||||||
ToolRegistry.node,
|
ToolRegistry.node,
|
||||||
ToolRegistry.toolsNode,
|
ToolRegistry.toolsNode,
|
||||||
Image.node,
|
Image.node,
|
||||||
SkillGuidance.node,
|
SkillInstructions.node,
|
||||||
ReferenceGuidance.node,
|
ReferenceInstructions.node,
|
||||||
InstructionEntry.node,
|
InstructionEntry.node,
|
||||||
Form.node,
|
Form.node,
|
||||||
QuestionV2.node,
|
QuestionV2.node,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export * as McpGuidance from "./guidance"
|
export * as McpInstructions from "./instructions"
|
||||||
|
|
||||||
import { makeLocationNode } from "../effect/app-node"
|
import { makeLocationNode } from "../effect/app-node"
|
||||||
import { Context, Effect, Layer, Schema } from "effect"
|
import { Context, Effect, Layer, Schema } from "effect"
|
||||||
|
|
@ -54,7 +54,7 @@ export interface Interface {
|
||||||
readonly load: (agent: AgentV2.Selection) => Effect.Effect<Instructions.Instructions>
|
readonly load: (agent: AgentV2.Selection) => Effect.Effect<Instructions.Instructions>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/McpGuidance") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/McpInstructions") {}
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
|
|
@ -62,7 +62,7 @@ export const layer = Layer.effect(
|
||||||
const mcp = yield* MCP.Service
|
const mcp = yield* MCP.Service
|
||||||
|
|
||||||
return Service.of({
|
return Service.of({
|
||||||
load: Effect.fn("McpGuidance.load")(function* (selection) {
|
load: Effect.fn("McpInstructions.load")(function* (selection) {
|
||||||
const agent = selection.info
|
const agent = selection.info
|
||||||
if (!agent) return Instructions.empty
|
if (!agent) return Instructions.empty
|
||||||
const source = (value: ReadonlyArray<Summary> | Instructions.Removed) =>
|
const source = (value: ReadonlyArray<Summary> | Instructions.Removed) =>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export * as ReferenceGuidance from "./guidance"
|
export * as ReferenceInstructions from "./instructions"
|
||||||
|
|
||||||
import { makeLocationNode } from "../effect/app-node"
|
import { makeLocationNode } from "../effect/app-node"
|
||||||
import { Context, Effect, Layer, Schema } from "effect"
|
import { Context, Effect, Layer, Schema } from "effect"
|
||||||
|
|
@ -57,7 +57,7 @@ export interface Interface {
|
||||||
readonly load: () => Effect.Effect<Instructions.Instructions>
|
readonly load: () => Effect.Effect<Instructions.Instructions>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/ReferenceGuidance") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/ReferenceInstructions") {}
|
||||||
|
|
||||||
const layer = Layer.effect(
|
const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
|
|
@ -65,7 +65,7 @@ const layer = Layer.effect(
|
||||||
const references = yield* Reference.Service
|
const references = yield* Reference.Service
|
||||||
|
|
||||||
return Service.of({
|
return Service.of({
|
||||||
load: Effect.fn("ReferenceGuidance.load")(function* () {
|
load: Effect.fn("ReferenceInstructions.load")(function* () {
|
||||||
const available = (yield* references.list())
|
const available = (yield* references.list())
|
||||||
.filter((reference) => reference.description !== undefined)
|
.filter((reference) => reference.description !== undefined)
|
||||||
.map((reference) => ({
|
.map((reference) => ({
|
||||||
|
|
@ -8,10 +8,10 @@ import { InstructionDiscovery } from "../instruction-discovery"
|
||||||
import { Instructions } from "../instructions/index"
|
import { Instructions } from "../instructions/index"
|
||||||
import { InstructionBuiltIns } from "../instructions/builtins"
|
import { InstructionBuiltIns } from "../instructions/builtins"
|
||||||
import { Location } from "../location"
|
import { Location } from "../location"
|
||||||
import { McpGuidance } from "../mcp/guidance"
|
import { McpInstructions } from "../mcp/instructions"
|
||||||
import { PluginSupervisor } from "../plugin/supervisor"
|
import { PluginSupervisor } from "../plugin/supervisor"
|
||||||
import { ReferenceGuidance } from "../reference/guidance"
|
import { ReferenceInstructions } from "../reference/instructions"
|
||||||
import { SkillGuidance } from "../skill/guidance"
|
import { SkillInstructions } from "../skill/instructions"
|
||||||
import { AgentNotFoundError } from "./error"
|
import { AgentNotFoundError } from "./error"
|
||||||
import { SessionHistory } from "./history"
|
import { SessionHistory } from "./history"
|
||||||
import { InstructionEntry } from "./instruction-entry"
|
import { InstructionEntry } from "./instruction-entry"
|
||||||
|
|
@ -52,11 +52,11 @@ const layer = Layer.effect(
|
||||||
const discovery = yield* InstructionDiscovery.Service
|
const discovery = yield* InstructionDiscovery.Service
|
||||||
const entries = yield* InstructionEntry.Service
|
const entries = yield* InstructionEntry.Service
|
||||||
const location = yield* Location.Service
|
const location = yield* Location.Service
|
||||||
const mcpGuidance = yield* McpGuidance.Service
|
const mcpInstructions = yield* McpInstructions.Service
|
||||||
const models = yield* SessionRunnerModel.Service
|
const models = yield* SessionRunnerModel.Service
|
||||||
const plugins = yield* PluginSupervisor.Service
|
const plugins = yield* PluginSupervisor.Service
|
||||||
const referenceGuidance = yield* ReferenceGuidance.Service
|
const referenceInstructions = yield* ReferenceInstructions.Service
|
||||||
const skillGuidance = yield* SkillGuidance.Service
|
const skillInstructions = yield* SkillInstructions.Service
|
||||||
const store = yield* SessionStore.Service
|
const store = yield* SessionStore.Service
|
||||||
|
|
||||||
const select = Effect.fn("SessionContext.select")(function* (sessionID: SessionSchema.ID) {
|
const select = Effect.fn("SessionContext.select")(function* (sessionID: SessionSchema.ID) {
|
||||||
|
|
@ -72,9 +72,9 @@ const layer = Layer.effect(
|
||||||
[
|
[
|
||||||
builtins.load(sessionID),
|
builtins.load(sessionID),
|
||||||
discovery.load(),
|
discovery.load(),
|
||||||
skillGuidance.load(agent),
|
skillInstructions.load(agent),
|
||||||
referenceGuidance.load(),
|
referenceInstructions.load(),
|
||||||
mcpGuidance.load(agent),
|
mcpInstructions.load(agent),
|
||||||
entries.load(sessionID),
|
entries.load(sessionID),
|
||||||
],
|
],
|
||||||
{ concurrency: "unbounded" },
|
{ concurrency: "unbounded" },
|
||||||
|
|
@ -108,11 +108,11 @@ export const node = makeLocationNode({
|
||||||
InstructionDiscovery.node,
|
InstructionDiscovery.node,
|
||||||
InstructionEntry.node,
|
InstructionEntry.node,
|
||||||
Location.node,
|
Location.node,
|
||||||
McpGuidance.node,
|
McpInstructions.node,
|
||||||
PluginSupervisor.node,
|
PluginSupervisor.node,
|
||||||
ReferenceGuidance.node,
|
ReferenceInstructions.node,
|
||||||
SessionRunnerModel.node,
|
SessionRunnerModel.node,
|
||||||
SessionStore.node,
|
SessionStore.node,
|
||||||
SkillGuidance.node,
|
SkillInstructions.node,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export * as SkillGuidance from "./guidance"
|
export * as SkillInstructions from "./instructions"
|
||||||
|
|
||||||
import { makeLocationNode } from "../effect/app-node"
|
import { makeLocationNode } from "../effect/app-node"
|
||||||
import { Context, Effect, Layer, Schema } from "effect"
|
import { Context, Effect, Layer, Schema } from "effect"
|
||||||
|
|
@ -60,7 +60,7 @@ export interface Interface {
|
||||||
readonly load: (agent: AgentV2.Selection) => Effect.Effect<Instructions.Instructions>
|
readonly load: (agent: AgentV2.Selection) => Effect.Effect<Instructions.Instructions>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SkillGuidance") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SkillInstructions") {}
|
||||||
|
|
||||||
const layer = Layer.effect(
|
const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
|
|
@ -68,7 +68,7 @@ const layer = Layer.effect(
|
||||||
const skills = yield* SkillV2.Service
|
const skills = yield* SkillV2.Service
|
||||||
|
|
||||||
return Service.of({
|
return Service.of({
|
||||||
load: Effect.fn("SkillGuidance.load")(function* (selection) {
|
load: Effect.fn("SkillInstructions.load")(function* (selection) {
|
||||||
const agent = selection.info
|
const agent = selection.info
|
||||||
if (!agent) return Instructions.empty
|
if (!agent) return Instructions.empty
|
||||||
const permitted = SkillV2.available(yield* skills.list(), agent)
|
const permitted = SkillV2.available(yield* skills.list(), agent)
|
||||||
|
|
@ -3,18 +3,18 @@ import { Effect, Layer } from "effect"
|
||||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||||
import { Reference } from "@opencode-ai/core/reference"
|
import { Reference } from "@opencode-ai/core/reference"
|
||||||
import { ReferenceGuidance } from "@opencode-ai/core/reference/guidance"
|
import { ReferenceInstructions } from "@opencode-ai/core/reference/instructions"
|
||||||
import { it } from "./lib/effect"
|
import { it } from "./lib/effect"
|
||||||
import { readInitial, readUpdate } from "./lib/instructions"
|
import { readInitial, readUpdate } from "./lib/instructions"
|
||||||
|
|
||||||
const guidanceLayer = (referenceLayer: Layer.Layer<Reference.Service>) =>
|
const instructionsLayer = (referenceLayer: Layer.Layer<Reference.Service>) =>
|
||||||
AppNodeBuilder.build(ReferenceGuidance.node, [[Reference.node, referenceLayer]])
|
AppNodeBuilder.build(ReferenceInstructions.node, [[Reference.node, referenceLayer]])
|
||||||
|
|
||||||
describe("ReferenceGuidance", () => {
|
describe("ReferenceInstructions", () => {
|
||||||
it.effect("lists available references in the instructions", () =>
|
it.effect("lists available references in the instructions", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const guidance = yield* ReferenceGuidance.Service
|
const instructions = yield* ReferenceInstructions.Service
|
||||||
const generation = yield* readInitial(yield* guidance.load())
|
const generation = yield* readInitial(yield* instructions.load())
|
||||||
|
|
||||||
expect(generation.text).toContain("<available_references>")
|
expect(generation.text).toContain("<available_references>")
|
||||||
expect(generation.text).toContain("<name>docs</name>")
|
expect(generation.text).toContain("<name>docs</name>")
|
||||||
|
|
@ -22,7 +22,7 @@ describe("ReferenceGuidance", () => {
|
||||||
expect(generation.text).toContain("<description>Use for product documentation</description>")
|
expect(generation.text).toContain("<description>Use for product documentation</description>")
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Effect.provide(
|
Effect.provide(
|
||||||
guidanceLayer(
|
instructionsLayer(
|
||||||
Layer.mock(Reference.Service, {
|
Layer.mock(Reference.Service, {
|
||||||
list: () =>
|
list: () =>
|
||||||
Effect.succeed([
|
Effect.succeed([
|
||||||
|
|
@ -43,22 +43,22 @@ describe("ReferenceGuidance", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.effect("omits guidance when no references are available", () =>
|
it.effect("omits instructions when no references are available", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const guidance = yield* ReferenceGuidance.Service
|
const instructions = yield* ReferenceInstructions.Service
|
||||||
const generation = yield* readInitial(yield* guidance.load())
|
const generation = yield* readInitial(yield* instructions.load())
|
||||||
expect(generation.text).toBe("")
|
expect(generation.text).toBe("")
|
||||||
}).pipe(Effect.provide(guidanceLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed([]) })))),
|
}).pipe(Effect.provide(instructionsLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed([]) })))),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.effect("omits references without descriptions", () =>
|
it.effect("omits references without descriptions", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const guidance = yield* ReferenceGuidance.Service
|
const instructions = yield* ReferenceInstructions.Service
|
||||||
const generation = yield* readInitial(yield* guidance.load())
|
const generation = yield* readInitial(yield* instructions.load())
|
||||||
expect(generation.text).toBe("")
|
expect(generation.text).toBe("")
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Effect.provide(
|
Effect.provide(
|
||||||
guidanceLayer(
|
instructionsLayer(
|
||||||
Layer.mock(Reference.Service, {
|
Layer.mock(Reference.Service, {
|
||||||
list: () =>
|
list: () =>
|
||||||
Effect.succeed([
|
Effect.succeed([
|
||||||
|
|
@ -84,11 +84,11 @@ describe("ReferenceGuidance", () => {
|
||||||
})
|
})
|
||||||
let references = [reference("docs", "Use for product documentation")]
|
let references = [reference("docs", "Use for product documentation")]
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* ReferenceGuidance.Service
|
const instructions = yield* ReferenceInstructions.Service
|
||||||
const initialized = yield* readInitial(yield* guidance.load())
|
const initialized = yield* readInitial(yield* instructions.load())
|
||||||
|
|
||||||
references = [reference("docs", "Use for product documentation"), reference("examples", "Use for examples")]
|
references = [reference("docs", "Use for product documentation"), reference("examples", "Use for examples")]
|
||||||
const added = yield* readUpdate(yield* guidance.load(), initialized)
|
const added = yield* readUpdate(yield* instructions.load(), initialized)
|
||||||
expect(added.text).toBe(
|
expect(added.text).toBe(
|
||||||
[
|
[
|
||||||
"New project references are available in addition to those previously listed:",
|
"New project references are available in addition to those previously listed:",
|
||||||
|
|
@ -101,9 +101,11 @@ describe("ReferenceGuidance", () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
references = [reference("examples", "Use for examples")]
|
references = [reference("examples", "Use for examples")]
|
||||||
expect((yield* readUpdate(yield* guidance.load(), added)).text).toBe(
|
expect((yield* readUpdate(yield* instructions.load(), added)).text).toBe(
|
||||||
"The following project references are no longer available and must not be used: docs.",
|
"The following project references are no longer available and must not be used: docs.",
|
||||||
)
|
)
|
||||||
}).pipe(Effect.provide(guidanceLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed(references) }))))
|
}).pipe(
|
||||||
|
Effect.provide(instructionsLayer(Layer.mock(Reference.Service, { list: () => Effect.succeed(references) }))),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -33,9 +33,9 @@ import { Location } from "@opencode-ai/core/location"
|
||||||
import { InstructionBuiltIns } from "@opencode-ai/core/instructions/builtins"
|
import { InstructionBuiltIns } from "@opencode-ai/core/instructions/builtins"
|
||||||
import { InstructionDiscovery } from "@opencode-ai/core/instruction-discovery"
|
import { InstructionDiscovery } from "@opencode-ai/core/instruction-discovery"
|
||||||
import { Instructions } from "@opencode-ai/core/instructions"
|
import { Instructions } from "@opencode-ai/core/instructions"
|
||||||
import { SkillGuidance } from "@opencode-ai/core/skill/guidance"
|
import { SkillInstructions } from "@opencode-ai/core/skill/instructions"
|
||||||
import { ReferenceGuidance } from "@opencode-ai/core/reference/guidance"
|
import { ReferenceInstructions } from "@opencode-ai/core/reference/instructions"
|
||||||
import { McpGuidance } from "@opencode-ai/core/mcp/guidance"
|
import { McpInstructions } from "@opencode-ai/core/mcp/instructions"
|
||||||
import { PluginSupervisor } from "@opencode-ai/core/plugin/supervisor"
|
import { PluginSupervisor } from "@opencode-ai/core/plugin/supervisor"
|
||||||
import { PluginHooks } from "@opencode-ai/core/plugin/hooks"
|
import { PluginHooks } from "@opencode-ai/core/plugin/hooks"
|
||||||
import { SystemPromptPlugin } from "@opencode-ai/core/plugin/system-prompt"
|
import { SystemPromptPlugin } from "@opencode-ai/core/plugin/system-prompt"
|
||||||
|
|
@ -76,9 +76,11 @@ const model = OpenAIChat.route
|
||||||
const models = SessionRunnerModel.layerWith(() => Effect.succeed(SessionRunnerModel.resolved(model)))
|
const models = SessionRunnerModel.layerWith(() => Effect.succeed(SessionRunnerModel.resolved(model)))
|
||||||
const systemContext = Layer.mock(InstructionBuiltIns.Service, { load: () => Effect.succeed(Instructions.empty) })
|
const systemContext = Layer.mock(InstructionBuiltIns.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||||
const instructionContext = Layer.mock(InstructionDiscovery.Service, { load: () => Effect.succeed(Instructions.empty) })
|
const instructionContext = Layer.mock(InstructionDiscovery.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||||
const skillGuidance = Layer.mock(SkillGuidance.Service, { load: () => Effect.succeed(Instructions.empty) })
|
const skillInstructions = Layer.mock(SkillInstructions.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||||
const referenceGuidance = Layer.mock(ReferenceGuidance.Service, { load: () => Effect.succeed(Instructions.empty) })
|
const referenceInstructions = Layer.mock(ReferenceInstructions.Service, {
|
||||||
const mcpGuidance = Layer.mock(McpGuidance.Service, { load: () => Effect.succeed(Instructions.empty) })
|
load: () => Effect.succeed(Instructions.empty),
|
||||||
|
})
|
||||||
|
const mcpInstructions = Layer.mock(McpInstructions.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||||
const config = Layer.succeed(Config.Service, Config.Service.of({ entries: () => Effect.succeed([]) }))
|
const config = Layer.succeed(Config.Service, Config.Service.of({ entries: () => Effect.succeed([]) }))
|
||||||
const pluginSupervisor = Layer.succeed(PluginSupervisor.Service, PluginSupervisor.Service.of({ flush: Effect.void }))
|
const pluginSupervisor = Layer.succeed(PluginSupervisor.Service, PluginSupervisor.Service.of({ flush: Effect.void }))
|
||||||
const promptCatalog = Layer.mock(Catalog.Service, {
|
const promptCatalog = Layer.mock(Catalog.Service, {
|
||||||
|
|
@ -102,9 +104,9 @@ const runnerLayer = AppNodeBuilder.build(SessionRunnerLLM.node, [
|
||||||
[InstructionBuiltIns.node, systemContext],
|
[InstructionBuiltIns.node, systemContext],
|
||||||
[InstructionDiscovery.node, instructionContext],
|
[InstructionDiscovery.node, instructionContext],
|
||||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||||
[SkillGuidance.node, skillGuidance],
|
[SkillInstructions.node, skillInstructions],
|
||||||
[ReferenceGuidance.node, referenceGuidance],
|
[ReferenceInstructions.node, referenceInstructions],
|
||||||
[McpGuidance.node, mcpGuidance],
|
[McpInstructions.node, mcpInstructions],
|
||||||
[Config.node, config],
|
[Config.node, config],
|
||||||
[PermissionV2.node, permission],
|
[PermissionV2.node, permission],
|
||||||
[ToolOutputStore.node, ToolOutputStore.nodeWithoutConfig],
|
[ToolOutputStore.node, ToolOutputStore.nodeWithoutConfig],
|
||||||
|
|
@ -140,8 +142,8 @@ const it = testEffect(
|
||||||
SessionRunnerModel.node,
|
SessionRunnerModel.node,
|
||||||
InstructionBuiltIns.node,
|
InstructionBuiltIns.node,
|
||||||
InstructionDiscovery.node,
|
InstructionDiscovery.node,
|
||||||
SkillGuidance.node,
|
SkillInstructions.node,
|
||||||
ReferenceGuidance.node,
|
ReferenceInstructions.node,
|
||||||
Config.node,
|
Config.node,
|
||||||
Snapshot.node,
|
Snapshot.node,
|
||||||
SessionRunnerLLM.node,
|
SessionRunnerLLM.node,
|
||||||
|
|
@ -156,8 +158,8 @@ const it = testEffect(
|
||||||
[InstructionBuiltIns.node, systemContext],
|
[InstructionBuiltIns.node, systemContext],
|
||||||
[InstructionDiscovery.node, instructionContext],
|
[InstructionDiscovery.node, instructionContext],
|
||||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||||
[SkillGuidance.node, skillGuidance],
|
[SkillInstructions.node, skillInstructions],
|
||||||
[ReferenceGuidance.node, referenceGuidance],
|
[ReferenceInstructions.node, referenceInstructions],
|
||||||
[Config.node, config],
|
[Config.node, config],
|
||||||
[Snapshot.node, Snapshot.noopLayer],
|
[Snapshot.node, Snapshot.noopLayer],
|
||||||
[PluginSupervisor.node, pluginSupervisor],
|
[PluginSupervisor.node, pluginSupervisor],
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,9 @@ import { SessionStore } from "@opencode-ai/core/session/store"
|
||||||
import { Instructions } from "@opencode-ai/core/instructions"
|
import { Instructions } from "@opencode-ai/core/instructions"
|
||||||
import { InstructionBuiltIns } from "@opencode-ai/core/instructions/builtins"
|
import { InstructionBuiltIns } from "@opencode-ai/core/instructions/builtins"
|
||||||
import { InstructionDiscovery } from "@opencode-ai/core/instruction-discovery"
|
import { InstructionDiscovery } from "@opencode-ai/core/instruction-discovery"
|
||||||
import { SkillGuidance } from "@opencode-ai/core/skill/guidance"
|
import { SkillInstructions } from "@opencode-ai/core/skill/instructions"
|
||||||
import { ReferenceGuidance } from "@opencode-ai/core/reference/guidance"
|
import { ReferenceInstructions } from "@opencode-ai/core/reference/instructions"
|
||||||
import { McpGuidance } from "@opencode-ai/core/mcp/guidance"
|
import { McpInstructions } from "@opencode-ai/core/mcp/instructions"
|
||||||
import { ModelV2 } from "@opencode-ai/core/model"
|
import { ModelV2 } from "@opencode-ai/core/model"
|
||||||
import { Location } from "@opencode-ai/core/location"
|
import { Location } from "@opencode-ai/core/location"
|
||||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||||
|
|
@ -318,7 +318,7 @@ const systemContext = Layer.mock(InstructionBuiltIns.Service, {
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
const instructionContext = Layer.mock(InstructionDiscovery.Service, { load: () => Effect.succeed(Instructions.empty) })
|
const instructionContext = Layer.mock(InstructionDiscovery.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||||
const skillGuidance = Layer.mock(SkillGuidance.Service, {
|
const skillInstructions = Layer.mock(SkillInstructions.Service, {
|
||||||
load: (agent) =>
|
load: (agent) =>
|
||||||
Effect.succeed(
|
Effect.succeed(
|
||||||
skillBaselines.has(agent.id)
|
skillBaselines.has(agent.id)
|
||||||
|
|
@ -335,8 +335,10 @@ const skillGuidance = Layer.mock(SkillGuidance.Service, {
|
||||||
: Instructions.empty,
|
: Instructions.empty,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
const referenceGuidance = Layer.mock(ReferenceGuidance.Service, { load: () => Effect.succeed(Instructions.empty) })
|
const referenceInstructions = Layer.mock(ReferenceInstructions.Service, {
|
||||||
const mcpGuidance = Layer.mock(McpGuidance.Service, { load: () => Effect.succeed(Instructions.empty) })
|
load: () => Effect.succeed(Instructions.empty),
|
||||||
|
})
|
||||||
|
const mcpInstructions = Layer.mock(McpInstructions.Service, { load: () => Effect.succeed(Instructions.empty) })
|
||||||
const config = Layer.succeed(
|
const config = Layer.succeed(
|
||||||
Config.Service,
|
Config.Service,
|
||||||
Config.Service.of({
|
Config.Service.of({
|
||||||
|
|
@ -382,11 +384,11 @@ const runnerLayer = AppNodeBuilder.build(SessionRunnerLLM.node, [
|
||||||
[InstructionBuiltIns.node, systemContext],
|
[InstructionBuiltIns.node, systemContext],
|
||||||
[InstructionDiscovery.node, instructionContext],
|
[InstructionDiscovery.node, instructionContext],
|
||||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||||
[SkillGuidance.node, skillGuidance],
|
[SkillInstructions.node, skillInstructions],
|
||||||
[ReferenceGuidance.node, referenceGuidance],
|
[ReferenceInstructions.node, referenceInstructions],
|
||||||
[PermissionV2.node, permission],
|
[PermissionV2.node, permission],
|
||||||
[Config.node, config],
|
[Config.node, config],
|
||||||
[McpGuidance.node, mcpGuidance],
|
[McpInstructions.node, mcpInstructions],
|
||||||
[ToolOutputStore.node, ToolOutputStore.nodeWithoutConfig],
|
[ToolOutputStore.node, ToolOutputStore.nodeWithoutConfig],
|
||||||
[PluginSupervisor.node, pluginSupervisor],
|
[PluginSupervisor.node, pluginSupervisor],
|
||||||
])
|
])
|
||||||
|
|
@ -424,8 +426,8 @@ const it = testEffect(
|
||||||
InstructionBuiltIns.node,
|
InstructionBuiltIns.node,
|
||||||
InstructionDiscovery.node,
|
InstructionDiscovery.node,
|
||||||
InstructionEntry.node,
|
InstructionEntry.node,
|
||||||
SkillGuidance.node,
|
SkillInstructions.node,
|
||||||
ReferenceGuidance.node,
|
ReferenceInstructions.node,
|
||||||
Config.node,
|
Config.node,
|
||||||
Snapshot.node,
|
Snapshot.node,
|
||||||
SessionRunnerLLM.node,
|
SessionRunnerLLM.node,
|
||||||
|
|
@ -440,8 +442,8 @@ const it = testEffect(
|
||||||
[InstructionBuiltIns.node, systemContext],
|
[InstructionBuiltIns.node, systemContext],
|
||||||
[InstructionDiscovery.node, instructionContext],
|
[InstructionDiscovery.node, instructionContext],
|
||||||
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
[Location.node, Location.boundNode({ directory: AbsolutePath.make("/project") })],
|
||||||
[SkillGuidance.node, skillGuidance],
|
[SkillInstructions.node, skillInstructions],
|
||||||
[ReferenceGuidance.node, referenceGuidance],
|
[ReferenceInstructions.node, referenceInstructions],
|
||||||
[Snapshot.node, Snapshot.noopLayer],
|
[Snapshot.node, Snapshot.noopLayer],
|
||||||
[SessionExecution.node, execution],
|
[SessionExecution.node, execution],
|
||||||
[Config.node, config],
|
[Config.node, config],
|
||||||
|
|
@ -1424,7 +1426,7 @@ describe("SessionRunnerLLM", () => {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.effect("updates selected-agent skill guidance after an agent switch", () =>
|
it.effect("updates selected-agent skill instructions after an agent switch", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* setup
|
const session = yield* setup
|
||||||
const events = yield* EventV2.Service
|
const events = yield* EventV2.Service
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { AgentV2 } from "@opencode-ai/core/agent"
|
||||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||||
import { SkillV2 } from "@opencode-ai/core/skill"
|
import { SkillV2 } from "@opencode-ai/core/skill"
|
||||||
import { SkillGuidance } from "@opencode-ai/core/skill/guidance"
|
import { SkillInstructions } from "@opencode-ai/core/skill/instructions"
|
||||||
import { it } from "../lib/effect"
|
import { it } from "../lib/effect"
|
||||||
import { readInitial, readUpdate } from "../lib/instructions"
|
import { readInitial, readUpdate } from "../lib/instructions"
|
||||||
|
|
||||||
|
|
@ -40,11 +40,11 @@ const manual = SkillV2.Info.make({
|
||||||
})
|
})
|
||||||
|
|
||||||
const layer = (list: () => SkillV2.Info[]) =>
|
const layer = (list: () => SkillV2.Info[]) =>
|
||||||
AppNodeBuilder.build(SkillGuidance.node, [
|
AppNodeBuilder.build(SkillInstructions.node, [
|
||||||
[SkillV2.node, Layer.mock(SkillV2.Service, { list: () => Effect.succeed(list()) })],
|
[SkillV2.node, Layer.mock(SkillV2.Service, { list: () => Effect.succeed(list()) })],
|
||||||
])
|
])
|
||||||
|
|
||||||
describe("SkillGuidance", () => {
|
describe("SkillInstructions", () => {
|
||||||
it.effect("renders described agent skills and updates the complete available list", () => {
|
it.effect("renders described agent skills and updates the complete available list", () => {
|
||||||
const agent = AgentV2.Info.make({
|
const agent = AgentV2.Info.make({
|
||||||
...AgentV2.Info.empty(build),
|
...AgentV2.Info.empty(build),
|
||||||
|
|
@ -52,8 +52,8 @@ describe("SkillGuidance", () => {
|
||||||
})
|
})
|
||||||
let skills = [hidden, denied, manual, effect]
|
let skills = [hidden, denied, manual, effect]
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
const initialized = yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))
|
const initialized = yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))
|
||||||
|
|
||||||
expect(initialized.text).toBe(
|
expect(initialized.text).toBe(
|
||||||
[
|
[
|
||||||
|
|
@ -72,7 +72,7 @@ describe("SkillGuidance", () => {
|
||||||
|
|
||||||
skills = []
|
skills = []
|
||||||
expect(
|
expect(
|
||||||
yield* guidance
|
yield* instructions
|
||||||
.load({ id: agent.id, info: agent })
|
.load({ id: agent.id, info: agent })
|
||||||
.pipe(Effect.flatMap((context) => readUpdate(context, initialized))),
|
.pipe(Effect.flatMap((context) => readUpdate(context, initialized))),
|
||||||
).toMatchObject({ text: "Skill guidance is no longer available. Do not use any previously listed skill." })
|
).toMatchObject({ text: "Skill guidance is no longer available. Do not use any previously listed skill." })
|
||||||
|
|
@ -90,11 +90,11 @@ describe("SkillGuidance", () => {
|
||||||
})
|
})
|
||||||
let skills = [effect]
|
let skills = [effect]
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
const initialized = yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))
|
const initialized = yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))
|
||||||
|
|
||||||
skills = [effect, debugging]
|
skills = [effect, debugging]
|
||||||
const added = yield* guidance
|
const added = yield* instructions
|
||||||
.load({ id: agent.id, info: agent })
|
.load({ id: agent.id, info: agent })
|
||||||
.pipe(Effect.flatMap((context) => readUpdate(context, initialized)))
|
.pipe(Effect.flatMap((context) => readUpdate(context, initialized)))
|
||||||
expect(added.text).toBe(
|
expect(added.text).toBe(
|
||||||
|
|
@ -109,7 +109,7 @@ describe("SkillGuidance", () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
skills = [debugging]
|
skills = [debugging]
|
||||||
const removed = yield* guidance
|
const removed = yield* instructions
|
||||||
.load({ id: agent.id, info: agent })
|
.load({ id: agent.id, info: agent })
|
||||||
.pipe(Effect.flatMap((context) => readUpdate(context, added)))
|
.pipe(Effect.flatMap((context) => readUpdate(context, added)))
|
||||||
expect(removed.text).toBe("The following skill IDs are no longer available and must not be used: effect.")
|
expect(removed.text).toBe("The following skill IDs are no longer available and must not be used: effect.")
|
||||||
|
|
@ -120,12 +120,12 @@ describe("SkillGuidance", () => {
|
||||||
const agent = AgentV2.Info.make(AgentV2.Info.empty(build))
|
const agent = AgentV2.Info.make(AgentV2.Info.empty(build))
|
||||||
let skills = [effect]
|
let skills = [effect]
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
const initialized = yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))
|
const initialized = yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))
|
||||||
|
|
||||||
skills = [SkillV2.Info.make({ ...effect, description: "Build applications with Effect v4" })]
|
skills = [SkillV2.Info.make({ ...effect, description: "Build applications with Effect v4" })]
|
||||||
expect(
|
expect(
|
||||||
yield* guidance
|
yield* instructions
|
||||||
.load({ id: agent.id, info: agent })
|
.load({ id: agent.id, info: agent })
|
||||||
.pipe(Effect.flatMap((context) => readUpdate(context, initialized))),
|
.pipe(Effect.flatMap((context) => readUpdate(context, initialized))),
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
|
|
@ -136,18 +136,18 @@ describe("SkillGuidance", () => {
|
||||||
}).pipe(Effect.provide(layer(() => skills)))
|
}).pipe(Effect.provide(layer(() => skills)))
|
||||||
})
|
})
|
||||||
|
|
||||||
it.effect("omits guidance when the selected agent denies all skills", () => {
|
it.effect("omits instructions when the selected agent denies all skills", () => {
|
||||||
const agent = AgentV2.Info.make({
|
const agent = AgentV2.Info.make({
|
||||||
...AgentV2.Info.empty(build),
|
...AgentV2.Info.empty(build),
|
||||||
permissions: [{ action: "skill", resource: "*", effect: "deny" }],
|
permissions: [{ action: "skill", resource: "*", effect: "deny" }],
|
||||||
})
|
})
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
expect((yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toBe("")
|
expect((yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toBe("")
|
||||||
}).pipe(Effect.provide(layer(() => [effect])))
|
}).pipe(Effect.provide(layer(() => [effect])))
|
||||||
})
|
})
|
||||||
|
|
||||||
it.effect("omits guidance when a resource-specific denial follows the global denial", () => {
|
it.effect("omits instructions when a resource-specific denial follows the global denial", () => {
|
||||||
const agent = AgentV2.Info.make({
|
const agent = AgentV2.Info.make({
|
||||||
...AgentV2.Info.empty(build),
|
...AgentV2.Info.empty(build),
|
||||||
permissions: [
|
permissions: [
|
||||||
|
|
@ -156,8 +156,8 @@ describe("SkillGuidance", () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
expect((yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toBe("")
|
expect((yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toBe("")
|
||||||
}).pipe(Effect.provide(layer(() => [effect])))
|
}).pipe(Effect.provide(layer(() => [effect])))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -170,14 +170,14 @@ describe("SkillGuidance", () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
expect((yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toContain(
|
expect(
|
||||||
"<name>Effect</name>",
|
(yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text,
|
||||||
)
|
).toContain("<name>Effect</name>")
|
||||||
}).pipe(Effect.provide(layer(() => [effect])))
|
}).pipe(Effect.provide(layer(() => [effect])))
|
||||||
})
|
})
|
||||||
|
|
||||||
it.effect("omits guidance when a specifically allowed skill is denied again", () => {
|
it.effect("omits instructions when a specifically allowed skill is denied again", () => {
|
||||||
const agent = AgentV2.Info.make({
|
const agent = AgentV2.Info.make({
|
||||||
...AgentV2.Info.empty(build),
|
...AgentV2.Info.empty(build),
|
||||||
permissions: [
|
permissions: [
|
||||||
|
|
@ -187,8 +187,8 @@ describe("SkillGuidance", () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
const guidance = yield* SkillGuidance.Service
|
const instructions = yield* SkillInstructions.Service
|
||||||
expect((yield* guidance.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toBe("")
|
expect((yield* instructions.load({ id: agent.id, info: agent }).pipe(Effect.flatMap(readInitial))).text).toBe("")
|
||||||
}).pipe(Effect.provide(layer(() => [effect])))
|
}).pipe(Effect.provide(layer(() => [effect])))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
Loading…
Add table
Add a link
Reference in a new issue