Wtyczki
Implementacje modeli, narzędzia, skille środowiska uruchomieniowego, odbiorniki audytu, konektory i usługi w tle korzystają z jednego protokołu RuntimePlugin i jednego PluginHost na każdy Harness. To wtyczki architektury kodu, a nie serwery MCP. Wtyczka może wnosić kilka możliwości; system nie przypisuje wzajemnie wykluczających się kategorii wtyczek.
Pętla agenta, Session, uprawnienia i rejestry pozostają w silniku. Istniejące deklaratywne pakiety oharness.plugin.json są nadal obsługiwane i nigdy nie są automatycznie importowane jako moduły wykonywalne.
Szybki start
Uruchom demonstrację czterech wtyczek bez poświadczeń i wywołań sieciowych:
npm run demo:plugins
npm run test:plugins
Przykłady w script/fixtures/runtime-plugins.ts implementują model ze scenariuszem, lokalne narzędzie, obserwatora audytu i zasymulowany konektor. To demonstracje. Hostowany konektor w chmurze jest domyślnie rejestrowany jako connector-cloud poza instancjami tymczasowymi. Przy starcie rejestruje dwa narzędzia bez dostępu do sieci; brak logowania lub konfiguracji serwera staje się błędem narzędzia, a nie błędem startu. Wyłącz go przez { "id": "connector-cloud", "enabled": false } w runtimePlugins w pliku plugins.json. Aplikacje osadzające mogą ustawić cloudConnectors: false. Bezpośredni adapter Composio pozostaje opcjonalną alternatywą dla deweloperów; nie włączaj go w klientach hostowanych. Pipedream nie jest zaimplementowany.
Wbudowana wtyczka pamięci, ID memory, odpowiada teraz za narzędzie pamięci, indeks w prompcie dla każdej tury, stronę Web i cykl życia synchronizacji w tle. Jest aktywna nawet w tymczasowych instancjach Harness (zgodnie z wcześniejszym zachowaniem pamięci lokalnej). Wyłącz ją w plugins.json, aby usunąć wszystkie te elementy. To coś innego niż wyłączenie synchronizacji z chmurą przy zachowaniu pamięci lokalnej.
Napisz wtyczkę
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 uruchamia się raz na każdą instancję Harness. Trzymaj klientów, timery i zmienny stan w jego wnętrzu, a nie na poziomie modułu. Identyfikatory wtyczek składają się z małych liter, cyfr, kropek, podkreśleń lub łączników. Wersja API 1 jest sprawdzana niezależnie od własnej wersji wtyczki.
Ładowanie i konfiguracja
Aplikacja osadzająca może jawnie przekazać zaufane definicje wtyczek:
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();
}
Możesz też dodać runtimePlugins do pliku wtyczek użytkownika (plugins.json w OHARNESS_HOME, zwykle ~/.oharness). Akceptowana jest bezwzględna ścieżka modułu lub zainstalowany pakiet rozwiązywalny z tego katalogu; nic nie jest pobierane. Moduły muszą eksportować definicję wtyczki jako domyślny eksport. Do dystrybucji preferuj zbudowany JavaScript; źródłowy TypeScript wymaga podzbioru obsługiwanego przez mechanizm usuwania typów w Node.
{
"runtimePlugins": [
{
"id": "hello-tools",
"module": "/absolute/path/to/hello-plugin.js",
"enabled": true,
"required": true
}
]
}
config jest przekazywany do parseConfig. Skonfigurowany wpis o tym samym ID może skonfigurować lub wyłączyć wtyczkę przekazaną bezpośrednio, ale nie może podmienić jej implementacji na inny moduł. Zduplikowane ID w obrębie którejkolwiek listy są odrzucane. enabled: false całkowicie pomija import modułu, parsowanie konfiguracji i setup. required ma domyślnie wartość true; błędy opcjonalnych wtyczek są widoczne w statusie i nie przerywają tworzenia Harness. Lista z konfiguracji zastępuje wcześniejszą listę zamiast głęboko scalać opcje wtyczek. Zmiany obowiązują przy następnym utworzeniu Harness, a nie w aktywnych sesjach.
Konfiguracja projektu nie może wprowadzać ani przekonfigurowywać wtyczek środowiska uruchomieniowego. Przekazanie HarnessOptions.config to jawna decyzja o zaufaniu podjęta przez aplikację osadzającą. Nie umieszczaj sekretów w konfiguracji projektu ani w metadanych wtyczek. Wczytuj je przez zatwierdzoną integrację poświadczeń lub środowisko i nigdy ich nie loguj.
Interfejsy możliwości
| Interfejs | Wkład |
|---|---|
ctx.tools.register(tool) | Definicja i implementacja narzędzia; istniejący potok uprawnień nadal obowiązuje |
ctx.skills.register(skill) | Skill w pamięci, dostępny przez czas życia tej wtyczki; zduplikowane nazwy są odrzucane |
ctx.models.register(provider, models) | Implementacja modelu wraz z definicjami modeli, walidowana atomowo |
ctx.auth.register(provider) | Implementacja uwierzytelniania zgodna z istniejącym kontraktem AuthProvider |
ctx.hooks.on(event, handler) | Istniejący typowany hook cyklu życia, z automatycznym wypisaniem |
ctx.audit.registerSink(sink) | Obserwator zdarzeń uprawnień i wykonania działający w trybie best-effort |
ctx.prompts.register(source) | Asynchroniczna sekcja promptu systemowego odświeżana przy każdej turze agenta |
ctx.pages.register(page) | Deskryptor strony renderowanej przez hosta oraz handlery odczytu i akcji |
ctx.onStart(fn) | Praca w tle, uruchamiana po zakończeniu setupu wszystkich wtyczek |
ctx.onDispose(fn) | Zasoby do zamknięcia, w kolejności odwrotnej do pozyskania |
Wszystkie elementy muszą zostać zarejestrowane podczas setup. Host je śledzi i automatycznie wycofuje. Domyślna rejestracja odrzuca konflikty; wtyczki nie mogą po cichu podmieniać elementów wbudowanych ani swoich nawzajem. Istniejące płynne API rejestrów pozostają zgodne. Usunięcie implementacji uwierzytelniania nie kasuje poświadczeń. Dostawcy modeli powinni wnosić własne ID, a nie próbować nadpisywać istniejących wpisów katalogu. Model z wtyczki wybierasz przez zwykłą konfigurację model lub API wyboru modelu.
Skill środowiska uruchomieniowego dostarcza name, description i body, a także opcjonalny bezwzględny directory, gdy jego treść odwołuje się do skryptów lub innych plików. Korzysta z tego samego narzędzia skill ze stopniowym ujawnianiem co skille zapisane na dysku, pojawia się ze źródłem runtime i jest automatycznie usuwany, gdy jego wtyczka się zatrzymuje. Skille środowiska uruchomieniowego nie mogą zastępować skilli wbudowanych, użytkownika, projektu, skonfigurowanych ani innych skilli środowiska uruchomieniowego o tej samej nazwie.
ctx.signal sygnalizuje zamykanie wtyczki. Narzędzia i strumienie modeli otrzymują też połączony sygnał anulowania hosta i wywołania. Praca w tle powinna współpracować; rejestruj sprzątanie zasobu zaraz po jego pozyskaniu, przed kolejnymi await, które mogą się nie powieść. onStart musi się rozwiązać po uruchomieniu pracy, a nie czekać na nieskończoną pętlę workera. Zamiast tego zachowaj obietnicę jej zakończenia i poczekaj na nią w onDispose.
Wtyczka audytu
Silnik dostarcza jsonlAuditPlugin (id: "audit-jsonl"), zbudowaną na tym samym kontrakcie. Przekaż jej jako konfigurację { path: "/absolute/path/to/events.jsonl" }, przez API osadzania albo wpis modułu wskazujący na packages/engine/dist/runtime-plugins/builtin/jsonl-audit.js. Tworzy katalogi nadrzędne, otwiera plik w trybie dopisywania (0600 dla nowo utworzonego pliku) i opróżnia kolejkę przed zamknięciem. Nie rotuje plików ani nie zmienia uprawnień istniejących wcześniej plików.
Odbiornik audytu otrzymuje RuntimeAuditEvent:
permission: istniejące rekordy decyzji i odpowiedzi PermissionEngine, w tym decyzje subagentów przekazywane przez hosta.tool_execution: zakończone wywołania, w tym rzucone błędy narzędzi zamienione na wyniki z błędem; zawiera czas trwania, tożsamość narzędzia i wtyczkę środowiska uruchomieniowego, do której ono należy.
Zdarzenia wykonania pomijają treść wejścia i wyjścia oraz poświadczenia. Rekordy uprawnień zachowują istniejące pola reguły i podmiotu, które mogą zawierać ścieżki lub argumenty poleceń: odbiorniki muszą uwzględniać ich wrażliwość. To nie jest pełne odtworzenie zdarzeń ani dziennik zgodności; wywołania zablokowane przed PermissionEngine nie zyskują w tej implementacji dodatkowych rekordów decyzji.
Każdy odbiornik dostaje własną migawkę i uporządkowaną kolejkę. Odbiornik, który rzuca wyjątek, nie blokuje innego odbiornika ani nie zmienia decyzji o narzędziu. Kolejki mieszczą do 1000 oczekujących zdarzeń na odbiornik, a nadmiarowe zdarzenia są odrzucane z ostrzeżeniem. Zamykanie opróżnia kolejki przed zamknięciem zasobów odbiorników, w granicach limitu czasu sprzątania. audit: false wyłącza zdarzenia audytu środowiska uruchomieniowego oraz istniejący zapis uprawnień.
Przykładowa konfiguracja odbiornika plikowego (wewnątrz wtyczki):
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");
});
To zachowuje istniejącą semantykę audytu silnika typu best-effort. Oznaczenie wtyczki audytu jako wymaganej gwarantuje start, a nie zapisy typu fail-closed. Obowiązkowy, trwały audyt wymagałby osobno zaprojektowanej polityki wykonania z oczekiwaniem na zapis.
Wtyczka konektora
Użyj createConnectorPlugin jako wygodnej fabryki albo zaimplementuj RuntimePlugin bezpośrednio. Fabryka zwraca zwykły RuntimePlugin, a nie inny mechanizm:
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 udostępnia tools(): Promise<readonly Tool[]> i close(). Jeśli poznawanie narzędzi się nie powiedzie, połączony klient i tak zostaje zamknięty. Pomocnik wymusza wyłączenie readOnly i sessionLocal dla narzędzi zewnętrznych, więc metadane dostawcy nie mogą po cichu ominąć zatwierdzania ani ograniczeń trybu plan. Udostępniaj tylko zatwierdzone narzędzia, używaj przestrzeni nazw, waliduj argumenty i przekazuj sygnał każdego wywołania do SDK. Nie wysyłaj całego katalogu dostawcy w każdym prompcie modelu.
Host nie implementuje OAuth dostawców, łączenia kont, webhooków ani przechowywania danych tenantów. Konektor w chmurze zapewnia tworzenie hostowanych linków, interakcję w Web i zweryfikowane powiązanie; nie zmienia protokołu hosta. Tożsamość kont wielu użytkowników musi pochodzić z zaufanego kontekstu aplikacji, nigdy z ID konta wybranego przez model. Sam host wtyczek nie wymaga żadnych zmian w infrastrukturze chmurowej.
Cykl życia i zgodność
- Rozwiąż włączone wpisy i zwaliduj kontrakt oraz konfigurację.
- Uruchom setupy w skonfigurowanej kolejności; zapisz rejestracje i własność zasobów.
- Po zakończeniu wszystkich setupów uruchom starty; udostępnij status aktywny.
- W razie błędu wycofaj wtyczkę, która zawiodła; jeśli była wymagana, przerwij i wycofaj wszystkie.
- Przy zamykaniu Harness zablokuj nowe uruchomienia hosta, anuluj i opróżnij istniejące, przerwij wywołania wtyczek, wyrejestruj ich elementy, a następnie zamknij zasoby w odwrotnej kolejności.
Sprzątanie jest idempotentne i trwa dalej mimo pojedynczych błędów. Opróżnianie agentów oraz opróżnianie i sprzątanie każdej wtyczki mają limit pięciu sekund. Przekroczenia czasu są zgłaszane; nie mogą siłą zatrzymać niewspółpracującego JavaScriptu i po przekroczeniu czasu praca może trwać dalej. Nieaktualne, sklonowane narzędzie wtyczki odrzuca wywołania po zatrzymaniu jego wtyczki.
/plugins w CLI i /context w Web pokazują stan środowiska uruchomieniowego obok pakietów deklaratywnych. Błędy pomijają komunikaty wyjątków dostarczone przez wtyczki, aby nie ujawnić sekretów z konfiguracji. W razie potrzeby dodaj oczyszczoną diagnostykę przez logger przypisany do wtyczki.
V1 celowo nie ma odładowywania w locie, grafu zależności i usług między wtyczkami, zdalnego instalatora pakietów, piaskownicy, dowolnego ładowania modułów interfejsu ani wymienialnego rdzenia Agent/Session. Zainstalowane wtyczki działające w procesie mają uprawnienia procesu: API kontekstu to granica ułatwiająca utrzymanie kodu, a nie piaskownica bezpieczeństwa.
Zobacz granice konfiguracji, aby zmigrować starszą konfigurację wbudowaną. Osobne pliki wtyczek to zaufana konfiguracja użytkownika, nigdy nie są ładowane z dowolnego katalogu projektu.