initial
Release / release (push) Failing after 9s

This commit is contained in:
2026-09-09 09:56:21 +02:00
commit 04e80929f0
37 changed files with 3463 additions and 0 deletions
+192
View File
@@ -0,0 +1,192 @@
import type { PluginModule } from "@opencode-ai/plugin";
import type {
AgentConfig,
Config,
PermissionConfig,
PermissionObjectConfig,
} from "@opencode-ai/sdk/v2";
import { gptAgents, settings, workerNames } from "./config.js";
import { discover } from "./provider.js";
import { descriptions, routingGuide } from "./routing.js";
// OpenCode permission patterns are wildcards, not filesystem globs.
function matches(value: string, pattern: string) {
const escaped = pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
return new RegExp(`^${escaped}$`, process.platform === "win32" ? "si" : "s").test(value);
}
function rulesFor(action: string, defaults: PermissionObjectConfig, ...layers: PermissionConfig[]) {
const result = { ...defaults };
for (const layer of layers) {
for (const [key, rule] of Object.entries(
layer instanceof Object ? layer : { "*": layer },
)) {
if (!rule || !matches(action, key)) continue;
for (const [pattern, effect] of Object.entries(
rule instanceof Object ? rule : { "*": rule },
)) {
// Last matching rule wins. A new wildcard supersedes every earlier rule.
if (pattern === "*") {
for (const previous of Object.keys(result)) delete result[previous];
}
delete result[pattern];
result[pattern] = effect;
}
}
}
return result;
}
function flatPermission(action: string, ...layers: PermissionConfig[]) {
return (
Object.entries(rulesFor(action, { "*": "allow" }, ...layers)).findLast(([pattern]) =>
matches("*", pattern),
)?.[1] ?? "allow"
);
}
export default {
id: "stanp-opencode",
async server(context, options) {
const selected = settings.parse({ mode: "gpt", ...options });
const activeWorkers = new Set<string>();
return {
async config(input) {
// SAFETY: OpenCode 1.18.30 passes its validated live Config here. The plugin's
// legacy SDK type is stale; SDK/v2 describes the actual permission schema.
const config = input as Config;
const provider = await discover(config, selected.provider);
if (!provider) return;
const assignments = selected.mode === "gpt" ? gptAgents : selected.agents;
const deep = selected.mode === "custom" || provider.models.has("gpt-6-astra");
const workers = workerNames.filter((name) => deep || !name.startsWith("deep-"));
const coordinate =
selected.mode === "gpt" && !deep
? { model: "gpt-5.6-sol", effort: "medium" }
: assignments.coordinate;
if (
![coordinate, ...workers.map((name) => assignments[name])].every(({ model }) =>
provider.models.has(model),
)
)
return;
const agents = (config.agent ??= {});
const global = config.permission ?? {};
const build = agents.build?.permission ?? {};
// Custom agents already inherit OpenCode's defaults and global permissions.
// Supply only build's differing defaults, explicit todo access (task otherwise
// disables it), and build's original overrides. Do not flatten global rules.
const inherited: Exclude<PermissionConfig, string> = {
question: flatPermission("question", global),
plan_enter: flatPermission("plan_enter", global),
todowrite: flatPermission("todowrite", global),
};
for (const [key, rule] of Object.entries(
build instanceof Object ? build : { "*": build },
)) {
delete inherited[key];
inherited[key] = rule;
}
delete inherited.task;
inherited.task = "deny";
for (const name of workers) {
const assignment = assignments[name];
const options: AgentConfig["options"] = {
reasoningEffort: assignment.effort,
};
const tier = provider.models.get(assignment.model)?.serviceTier;
if (selected.lunaPriority && tier) {
options.serviceTier = tier;
}
agents[name] = {
mode: "subagent",
description: descriptions[name],
model: `${selected.provider}/${assignment.model}`,
options,
permission: structuredClone(inherited),
};
activeWorkers.add(name);
}
const reading: Exclude<PermissionConfig, string> = {};
for (const name of ["glob", "grep", "list", "todoread"]) {
reading[name] = rulesFor(name, { "*": "allow" }, global, build);
}
agents.coordinate = {
mode: "primary",
description:
"Read, plan, and coordinate specialist workers; never implement directly.",
model: `${selected.provider}/${coordinate.model}`,
options: { reasoningEffort: coordinate.effort },
prompt: routingGuide(deep),
permission: {
"*": "deny",
...reading,
read: rulesFor(
"read",
{
"*": "allow",
"*.env": "ask",
"*.env.*": "ask",
"*.env.example": "allow",
},
global,
build,
),
external_directory: rulesFor(
"external_directory",
{ "*": "ask" },
global,
build,
),
doom_loop: "ask",
webfetch: flatPermission("webfetch", global, build),
websearch: flatPermission("websearch", global, build),
todowrite: flatPermission("todowrite", global, build),
question: flatPermission("question", global, build),
task: {
"*": "deny",
...Object.fromEntries(workers.map((name) => [name, "allow" as const])),
},
},
};
},
async "tool.execute.before"(input, output) {
if (activeWorkers.size === 0 || input.tool !== "task") return;
const target = output.args?.subagent_type;
// Primary mode only hides coordinate from suggestions; @mentions and subtask
// commands can bypass task permissions, so enforce the policy here as well.
if (target === "coordinate") {
throw new Error("coordinate is a primary agent and cannot be delegated to");
}
const recent = await context.client.session.messages({
path: { id: input.sessionID },
query: { limit: 1 },
throwOnError: true,
});
const message = recent.data?.at(-1)?.info;
const parent =
message?.role === "assistant"
? (
await context.client.session.message({
path: { id: input.sessionID, messageID: message.parentID },
throwOnError: true,
})
).data?.info
: message;
// Subtask commands label their synthetic assistant with the TARGET agent;
// its parent user message identifies the caller, including resumed sessions.
if (parent?.role !== "user")
throw new Error("Cannot determine task caller; refusing delegation");
if (activeWorkers.has(parent.agent))
throw new Error("Workers cannot delegate tasks");
if (parent.agent === "coordinate" && !activeWorkers.has(target)) {
throw new Error("coordinate may delegate only to its available workers");
}
},
};
},
} satisfies PluginModule;