feat(opencode): cut plugin system and remote behavior feeds; add SessionContext assembly and Runner

- delete plugin machinery (loader, install, meta, hooks) and all trigger sites
- fold first-party provider auth into static registry (provider/hooks.ts)
- remove remote instruction URL fetch, skills remote puller, TUI plugin host
- add session/context.ts: single provenance-tagged context assembly point
- add session/runner.ts: admit/context/stream/tools loop with compaction policy
- relocate audit artifacts to audit/ (FORK-AUDIT, instruction docs as evidence)
This commit is contained in:
2026-08-22 16:13:02 -05:00
parent 5645a12361
commit cfca4cec19
77 changed files with 929 additions and 12054 deletions
@@ -10,7 +10,6 @@ import { disposeAllInstances, TestInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { TestConfig } from "../fixture/config"
import { Config } from "@/config/config"
import { Plugin } from "@/plugin"
import { Agent } from "@/agent/agent"
import { InstanceState } from "@/effect/instance-state"
@@ -26,30 +25,6 @@ const configLayer = TestConfig.layer({
directories: () => InstanceState.directory.pipe(Effect.map((dir) => [path.join(dir, ".opencode")])),
})
// Fake Plugin.Service that returns a single plugin whose `tool` map contains
// one definition with `args: undefined`. Used to exercise the plugin entry
// point of `fromPlugin` for the #27451 / #27630 regression.
const brokenPluginLayer = Layer.succeed(
Plugin.Service,
Plugin.Service.of({
init: () => Effect.void,
trigger: ((_name: unknown, _input: unknown, output: unknown) =>
Effect.succeed(output)) as Plugin.Interface["trigger"],
list: () =>
Effect.succeed([
{
tool: {
broken_plugin_tool: {
description: "plugin tool with missing args",
args: undefined as unknown as Record<string, never>,
execute: async () => "ok",
},
},
},
]),
}),
)
const root = LayerNode.group([ToolRegistry.node, Agent.node])
const replacements = [
[Config.node, configLayer],
@@ -93,8 +68,6 @@ const withEmptyCodeMode = testEffect(
],
]),
)
const withBrokenPlugin = testEffect(LayerNode.compile(root, [...replacements, [Plugin.node, brokenPluginLayer]]))
afterEach(async () => {
await disposeAllInstances()
})
@@ -257,21 +230,6 @@ describe("tool.registry", () => {
}),
)
// Same regression, plugin entry point. The original reports (#27451, #27630)
// came in through `plugin.list()` — `oh-my-opencode` was registering a tool
// with `args: undefined` and crashing every message submit. The file-scan
// and plugin-list loops both funnel through `fromPlugin`, but covering both
// entry points means a future refactor that splits them won't silently lose
// protection.
withBrokenPlugin.instance("tolerates a plugin tool registered with null/undefined args", () =>
Effect.gen(function* () {
const registry = yield* ToolRegistry.Service
const ids = yield* registry.ids()
expect(ids).toContain("read")
expect(ids).toContain("broken_plugin_tool")
}),
)
it.instance("loads tools from .opencode/tools (plural)", () =>
Effect.gen(function* () {
const test = yield* TestInstance