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

InterfejsWkł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ść

  1. Rozwiąż włączone wpisy i zwaliduj kontrakt oraz konfigurację.
  2. Uruchom setupy w skonfigurowanej kolejności; zapisz rejestracje i własność zasobów.
  3. Po zakończeniu wszystkich setupów uruchom starty; udostępnij status aktywny.
  4. W razie błędu wycofaj wtyczkę, która zawiodła; jeśli była wymagana, przerwij i wycofaj wszystkie.
  5. 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.