Plugins
Modellimplementationer, verktyg, runtime-skills, granskningsmål, kopplingar och bakgrundstjänster använder ett RuntimePlugin-protokoll och en PluginHost per Harness. Det här är plugins i kodarkitekturen, inte MCP-servrar. En plugin kan bidra med flera förmågor; systemet delar inte in plugins i ömsesidigt uteslutande kategorier.
Agentloopen, Session, behörigheter och register stannar i motorn. Befintliga deklarativa oharness.plugin.json-paket stöds fortfarande och importeras aldrig automatiskt som körbara moduler.
Snabbstart
Kör demonstrationen med fyra plugins utan inloggningsuppgifter eller nätverksanrop:
npm run demo:plugins
npm run test:plugins
Exemplen i script/fixtures/runtime-plugins.ts implementerar en skriptad modell, ett lokalt verktyg, en granskningsobservatör och en mockad koppling. De är demonstrationer. Den hostade molnkopplingen registreras som standard som connector-cloud utanför tillfälliga instanser. Den registrerar två verktyg utan nätverksåtkomst vid uppstart; saknad inloggning eller serverkonfiguration blir ett verktygsfel, inte ett uppstartsfel. Stäng av den med { "id": "connector-cloud", "enabled": false } i runtimePlugins i plugins.json. Den som bäddar in motorn kan sätta cloudConnectors: false. Den direkta Composio-adaptern är fortfarande ett alternativ för utvecklare som måste aktiveras uttryckligen; aktivera den inte i hostade klienter. Pipedream är inte implementerat.
Den inbyggda minnespluginen, med id memory, äger nu minnesverktyget, promptindexet per tur, webbsidan och livscykeln för bakgrundssynkroniseringen. Den är aktiv även i tillfälliga Harness-instanser (samma som tidigare beteende för lokalt minne). Stäng av den i plugins.json för att ta bort alla dessa bidrag. Det är något annat än att stänga av molnsynkronisering och behålla det lokala minnet.
Skriva en plugin
import type { RuntimePlugin } from "@oharness/engine";
export default {
id: "hello-tools",
version: "1.0.0",
apiVersion: 1,
parseConfig(raw) {
if (raw !== undefined) throw new Error("This plugin takes no options");
return {};
},
setup(ctx) {
ctx.tools.register({
name: "hello_greet",
description: "Return a greeting",
inputSchema: { type: "object", properties: {} },
readOnly: true,
async execute() {
return { content: [{ type: "text", text: "Hello" }] };
},
});
},
} satisfies RuntimePlugin;
setup körs en gång per Harness-instans. Håll klienter, timers och föränderligt tillstånd inuti den, inte på modulnivå. Plugin-id:n använder små bokstäver, siffror, punkter, understreck eller bindestreck. API-version 1 kontrolleras oberoende av pluginens egen version.
Ladda och konfigurera
En app som bäddar in motorn kan uttryckligen ange betrodda plugindefinitioner:
import { Harness } from "@oharness/engine";
import greeting from "./hello-plugin.js";
const harness = await Harness.create({
runtimePlugins: [{ plugin: greeting, required: true }],
});
try {
console.log(harness.runtimePluginStatus());
// Create sessions / agents as usual.
} finally {
await harness.close();
}
Alternativt kan du lägga till runtimePlugins i användarens pluginfil (plugins.json i OHARNESS_HOME, normalt ~/.oharness). En absolut modulsökväg eller ett installerat paket som går att lösa upp från den katalogen accepteras; inget laddas ner. Moduler måste ha en plugindefinition som default-export. Använd helst byggd JavaScript för distribution; TypeScript-källkod kräver den delmängd av type-stripping som Node stöder.
{
"runtimePlugins": [
{
"id": "hello-tools",
"module": "/absolute/path/to/hello-plugin.js",
"enabled": true,
"required": true
}
]
}
config skickas till parseConfig. En konfigurerad post med samma id kan konfigurera eller stänga av en inline-plugin, men kan inte ersätta dess implementation med en annan modul. Dubblerade id:n inom någon av listorna avvisas. enabled: false hoppar helt över modulimport, tolkning av konfigurationen och setup. required är som standard true; valfria fel syns i statusen utan att Harness-skapandet avbryts. Konfigurationslistan ersätter en tidigare lista i stället för att djupsammanfoga pluginalternativ. Ändringar gäller nästa gång en Harness skapas, inte aktiva sessioner.
Projektkonfiguration kan inte införa eller konfigurera om runtime-plugins. Att ange HarnessOptions.config är ett uttryckligt förtroendebeslut av appen som bäddar in motorn. Lägg inga hemligheter i projektkonfiguration eller pluginmetadata. Läs in dem via din godkända integration för inloggningsuppgifter eller miljön, och logga dem aldrig.
Förmågegränssnitt
| Gränssnitt | Bidrag |
|---|---|
ctx.tools.register(tool) | Verktygsdefinition och implementation; den befintliga behörighetskedjan gäller fortfarande |
ctx.skills.register(skill) | Skill i minnet som finns under pluginens livstid; dubblerade namn avvisas |
ctx.models.register(provider, models) | Modellimplementation plus dess modelldefinitioner, validerade atomiskt |
ctx.auth.register(provider) | Autentiseringsimplementation enligt det befintliga AuthProvider-kontraktet |
ctx.hooks.on(event, handler) | Befintlig typad livscykel-hook, med automatisk avregistrering |
ctx.audit.registerSink(sink) | Observatör av behörighets- och körningshändelser, best-effort |
ctx.prompts.register(source) | Asynkront avsnitt i systemprompten som uppdateras för varje agenttur |
ctx.pages.register(page) | Sidbeskrivning som värden renderar, plus hanterare för läsning och åtgärder |
ctx.onStart(fn) | Bakgrundsarbete, startat efter att alla pluginers setup är klara |
ctx.onDispose(fn) | Resurser att stänga, i omvänd ordning mot hur de skaffades |
Alla bidrag måste registreras under setup. Värden håller reda på dem och återställer dem automatiskt. Standardregistreringen avvisar konflikter; plugins kan inte i tysthet ersätta inbyggda delar eller varandras registreringar. Befintliga flytande register-API:er är fortfarande kompatibla. Att ta bort en autentiseringsimplementation raderar inte inloggningsuppgifter. Modelleverantörer ska bidra med egna id:n och inte försöka skriva över befintliga katalogposter. Välj en pluginmodell med den vanliga model-konfigurationen eller API:et för modellval.
En runtime-skill anger name, description och body, plus en valfri absolut directory när dess innehåll refererar till skript eller andra filer. Den använder samma skill-verktyg med stegvis visning som sparade skills, visas med källan runtime och tas bort automatiskt när dess plugin stoppas. Runtime-skills kan inte ersätta inbyggda, användar-, projekt-, konfigurerade eller andra runtime-skills med samma namn.
ctx.signal meddelar att pluginen stängs ner. Verktyg och modellströmmar får också den kombinerade avbrytsignalen från värd och anrop. Håll bakgrundsarbete kooperativt; registrera städning av en resurs direkt efter att den skaffats, före senare awaits som kan misslyckas. onStart måste returnera efter att arbetet startats, inte vänta på en oändlig arbetsloop. Spara dess slutförandelöfte och vänta på det i onDispose i stället.
Granskningsplugin
Motorn levereras med jsonlAuditPlugin (id: "audit-jsonl") som använder samma kontrakt. Skicka { path: "/absolute/path/to/events.jsonl" } som dess konfiguration, antingen via inbäddnings-API:et eller en modulpost som pekar på packages/engine/dist/runtime-plugins/builtin/jsonl-audit.js. Den skapar föräldrakataloger, öppnar i append-läge (0600 för en nyskapad fil) och tömmer sin kö innan den stängs. Den roterar inte filer och ändrar inte behörigheter på befintliga filer.
Ett granskningsmål tar emot RuntimeAuditEvent:
permission: befintliga beslut och svar från PermissionEngine, inklusive subagentbeslut som värden vidarebefordrar.tool_execution: slutförda anrop, inklusive kastade verktygsfel som gjorts om till felresultat; innehåller tidtagning, verktygets identitet och vilken runtime-plugin som äger det.
Körningshändelser utelämnar in- och utdata samt inloggningsuppgifter. Behörighetsposter behåller de befintliga fälten för regel och principal, som kan innehålla sökvägar eller kommandoargument: målen måste hantera deras känslighet. Det här är ingen fullständig händelseuppspelning eller efterlevnadsjournal; anrop som blockeras före PermissionEngine får inga extra beslutsposter i den här implementationen.
Varje mål får sin egen ögonblicksbild och ordnade kö. Ett mål som kastar fel blockerar inte ett annat mål och ändrar inte ett verktygsbeslut. Köerna rymmer högst 1 000 väntande händelser per mål och varnar och släpper överskjutande händelser. Vid nedstängning töms köerna innan målens resurser stängs, inom tidsgränsen för städning. audit: false stänger av runtime-granskningshändelser liksom den befintliga behörighetsloggen.
Exempel på uppsättning av ett filmål (inuti en plugin):
const file = await open(config.path, "a", 0o600); // node:fs/promises
ctx.onDispose(() => file.close());
ctx.audit.registerSink(async (event) => {
await file.appendFile(JSON.stringify(event) + "\n");
});
Det här bevarar motorns befintliga best-effort-semantik för granskning. Att göra en granskningsplugin obligatorisk garanterar uppstart, inte fail-closed- skrivningar. Obligatorisk, beständig granskning skulle kräva en separat utformad körningspolicy som väntar in skrivningen.
Kopplingsplugin
Använd createConnectorPlugin som en bekväm fabrik, eller implementera RuntimePlugin direkt. Fabriken returnerar en vanlig RuntimePlugin, inte en annan mekanism:
const plugin = createConnectorPlugin({
id: "my-connector",
version: "1.0.0",
parseConfig: validateConnectorConfig,
async connect(config, { signal, logger }) {
// Wrap your selected SDK. Bind a trusted account here, not from model input.
return createMyConnectorClient(config, { signal, logger });
},
});
ConnectorClient exponerar tools(): Promise<readonly Tool[]> och close(). Om upptäckten av verktyg misslyckas stängs den anslutna klienten ändå. Hjälpfunktionen tvingar av readOnly och sessionLocal för externa verktyg, så att leverantörens metadata inte i tysthet kan kringgå godkännande eller begränsningarna i plan-läget. Exponera bara godkända verktyg, använd namnrymder, validera argument och skicka vidare varje anrops signal till SDK:t. Skicka inte en leverantörs hela katalog in i varje modellprompt.
Värden implementerar inte leverantörs-OAuth, kontolänkning, webhooks eller lagring per tenant. Molnkopplingen står för skapandet av hostade länkar, interaktionen i webben och verifierad bindning; den ändrar inte värdprotokollet. Kontoidentitet för flera användare måste komma från betrodd appkontext, aldrig från ett konto-id som modellen valt. Inga ändringar i molninfrastrukturen krävs för själva pluginvärden.
Livscykel och kompatibilitet
- Lös upp aktiverade poster och validera kontraktet och konfigurationen.
- Kör setup i konfigurerad ordning; registrera registreringar och resursägande.
- Kör start när alla setup är klara; exponera aktiv status.
- Vid fel, rulla tillbaka den misslyckade pluginen; avbryt och rulla tillbaka allt om den är obligatorisk.
- När Harness stängs: förhindra nya värdkörningar, avbryt eller töm befintliga körningar, avbryt pluginanrop, avregistrera bidrag och stäng sedan resurser i omvänd ordning.
Städningen är idempotent och fortsätter efter enskilda fel. Tömning av agenter och varje plugins tömning och städning har en gräns på fem sekunder. Timeouter rapporteras; de kan inte tvinga ickekooperativ JavaScript att stoppa, och arbete kan finnas kvar efter en timeout. Ett inaktuellt klonat pluginverktyg avvisar anrop efter att dess plugin stoppats.
CLI:ts /plugins och webbens /context visar runtime-tillståndet bredvid deklarativa paket. Felmeddelanden utelämnar undantagsmeddelanden från plugins för att inte läcka hemligheter i konfigurationen. Lägg vid behov till rensad diagnostik med den pluginspecifika loggern.
V1 har medvetet ingen urladdning under drift, inget beroende- eller tjänstediagram mellan plugins, ingen installation av fjärrpaket, ingen sandlåda, ingen godtycklig modulladdare för gränssnittet och ingen utbytbar Agent/Session-kärna. Installerade plugins i samma process har processens befogenheter: kontext-API:et är en gräns för underhållbarhet, inte en säkerhetssandlåda.
Se konfigurationsgränser för att migrera äldre inline-konfiguration. Separata pluginfiler är betrodd användarkonfiguration och laddas aldrig från en godtycklig projektkatalog.