mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 19:40:21 +00:00
feat: generalize plugin provider capabilities and hooks
This commit is contained in:
@@ -451,6 +451,13 @@ describe("setupSearch", () => {
|
||||
},
|
||||
],
|
||||
typedHooks: [
|
||||
{
|
||||
pluginId: "tavily-search",
|
||||
hookName: "before_provider_configure",
|
||||
priority: 10,
|
||||
source: "/tmp/tavily-search",
|
||||
handler: () => ({ note: "Generic provider guidance." }),
|
||||
},
|
||||
{
|
||||
pluginId: "tavily-search",
|
||||
hookName: "before_search_provider_configure",
|
||||
@@ -473,7 +480,7 @@ describe("setupSearch", () => {
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Provider setup",
|
||||
message: "Read the provider docs before entering your key.",
|
||||
message: "Generic provider guidance.\n\nRead the provider docs before entering your key.",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
@@ -481,6 +488,7 @@ describe("setupSearch", () => {
|
||||
|
||||
it("fires after_search_provider_activate only when the active provider changes", async () => {
|
||||
const afterActivate = vi.fn();
|
||||
const afterProviderActivate = vi.fn();
|
||||
loadOpenClawPlugins.mockReturnValue({
|
||||
searchProviders: [
|
||||
{
|
||||
@@ -506,6 +514,13 @@ describe("setupSearch", () => {
|
||||
},
|
||||
],
|
||||
typedHooks: [
|
||||
{
|
||||
pluginId: "tavily-search",
|
||||
hookName: "after_provider_activate",
|
||||
priority: 0,
|
||||
source: "/tmp/tavily-search",
|
||||
handler: afterProviderActivate,
|
||||
},
|
||||
{
|
||||
pluginId: "tavily-search",
|
||||
hookName: "after_search_provider_activate",
|
||||
@@ -547,6 +562,19 @@ describe("setupSearch", () => {
|
||||
|
||||
expect(result.tools?.web?.search?.provider).toBe("tavily");
|
||||
expect(afterActivate).toHaveBeenCalledTimes(1);
|
||||
expect(afterProviderActivate).toHaveBeenCalledTimes(1);
|
||||
expect(afterProviderActivate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
providerKind: "search",
|
||||
slot: "providers.search",
|
||||
providerId: "tavily",
|
||||
previousProviderId: "brave",
|
||||
intent: "switch-active",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
workspaceDir: undefined,
|
||||
}),
|
||||
);
|
||||
expect(afterActivate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
providerId: "tavily",
|
||||
|
||||
@@ -330,25 +330,48 @@ async function maybeNoteBeforeSearchProviderConfigure(params: {
|
||||
prompter: WizardPrompter;
|
||||
workspaceDir?: string;
|
||||
}): Promise<void> {
|
||||
if (!params.hookRunner?.hasHooks("before_search_provider_configure")) {
|
||||
if (
|
||||
!params.hookRunner?.hasHooks("before_provider_configure") &&
|
||||
!params.hookRunner?.hasHooks("before_search_provider_configure")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const result = await params.hookRunner.runBeforeSearchProviderConfigure(
|
||||
{
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
intent: params.intent,
|
||||
activeProviderId: resolveCapabilitySlotSelection(params.config, "providers.search") ?? null,
|
||||
configured: params.provider.configured,
|
||||
},
|
||||
{
|
||||
workspaceDir: params.workspaceDir,
|
||||
},
|
||||
);
|
||||
if (result?.note?.trim()) {
|
||||
await params.prompter.note(result.note, "Provider setup");
|
||||
const activeProviderId =
|
||||
resolveCapabilitySlotSelection(params.config, "providers.search") ?? null;
|
||||
const ctx = { workspaceDir: params.workspaceDir };
|
||||
const genericResult = params.hookRunner.hasHooks("before_provider_configure")
|
||||
? await params.hookRunner.runBeforeProviderConfigure(
|
||||
{
|
||||
providerKind: "search",
|
||||
slot: "providers.search",
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
intent: params.intent,
|
||||
activeProviderId,
|
||||
configured: params.provider.configured,
|
||||
},
|
||||
ctx,
|
||||
)
|
||||
: undefined;
|
||||
const searchResult = params.hookRunner.hasHooks("before_search_provider_configure")
|
||||
? await params.hookRunner.runBeforeSearchProviderConfigure(
|
||||
{
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
intent: params.intent,
|
||||
activeProviderId,
|
||||
configured: params.provider.configured,
|
||||
},
|
||||
ctx,
|
||||
)
|
||||
: undefined;
|
||||
const note = [genericResult?.note, searchResult?.note].filter(hasNonEmptyString).join("\n\n");
|
||||
if (note.trim()) {
|
||||
await params.prompter.note(note, "Provider setup");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,41 +391,65 @@ async function runAfterSearchProviderHooks(params: {
|
||||
const activeProviderAfter =
|
||||
resolveCapabilitySlotSelection(params.resultConfig, "providers.search") ?? null;
|
||||
|
||||
const ctx = { workspaceDir: params.workspaceDir };
|
||||
const genericConfigureEvent = {
|
||||
providerKind: "search" as const,
|
||||
slot: "providers.search",
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
intent: params.intent,
|
||||
activeProviderId: activeProviderAfter,
|
||||
configured: params.provider.configured,
|
||||
};
|
||||
const searchConfigureEvent = {
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
intent: params.intent,
|
||||
activeProviderId: activeProviderAfter,
|
||||
configured: params.provider.configured,
|
||||
};
|
||||
|
||||
if (params.hookRunner.hasHooks("after_provider_configure")) {
|
||||
await params.hookRunner.runAfterProviderConfigure(genericConfigureEvent, ctx);
|
||||
}
|
||||
if (params.hookRunner.hasHooks("after_search_provider_configure")) {
|
||||
await params.hookRunner.runAfterSearchProviderConfigure(
|
||||
{
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
intent: params.intent,
|
||||
activeProviderId: activeProviderAfter,
|
||||
configured: params.provider.configured,
|
||||
},
|
||||
{
|
||||
workspaceDir: params.workspaceDir,
|
||||
},
|
||||
);
|
||||
await params.hookRunner.runAfterSearchProviderConfigure(searchConfigureEvent, ctx);
|
||||
}
|
||||
|
||||
if (
|
||||
activeProviderAfter === params.provider.providerId &&
|
||||
activeProviderBefore !== activeProviderAfter &&
|
||||
params.hookRunner.hasHooks("after_search_provider_activate")
|
||||
(params.hookRunner.hasHooks("after_provider_activate") ||
|
||||
params.hookRunner.hasHooks("after_search_provider_activate"))
|
||||
) {
|
||||
await params.hookRunner.runAfterSearchProviderActivate(
|
||||
{
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
previousProviderId: activeProviderBefore,
|
||||
intent: params.intent,
|
||||
},
|
||||
{
|
||||
workspaceDir: params.workspaceDir,
|
||||
},
|
||||
);
|
||||
const genericActivateEvent = {
|
||||
providerKind: "search" as const,
|
||||
slot: "providers.search",
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
previousProviderId: activeProviderBefore,
|
||||
intent: params.intent,
|
||||
};
|
||||
const searchActivateEvent = {
|
||||
providerId: params.provider.providerId,
|
||||
providerLabel: params.provider.providerLabel,
|
||||
providerSource: params.provider.providerSource,
|
||||
pluginId: params.provider.pluginId,
|
||||
previousProviderId: activeProviderBefore,
|
||||
intent: params.intent,
|
||||
};
|
||||
if (params.hookRunner.hasHooks("after_provider_activate")) {
|
||||
await params.hookRunner.runAfterProviderActivate(genericActivateEvent, ctx);
|
||||
}
|
||||
if (params.hookRunner.hasHooks("after_search_provider_activate")) {
|
||||
await params.hookRunner.runAfterSearchProviderActivate(searchActivateEvent, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -334,6 +334,29 @@ describe("config plugin validation", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("routes missing slot selection diagnostics to the slot config path", async () => {
|
||||
const res = validateInSuite({
|
||||
agents: { list: [{ id: "pi" }] },
|
||||
plugins: {
|
||||
enabled: true,
|
||||
slots: { memory: "missing-memory-backend" },
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.ok).toBe(false);
|
||||
if (!res.ok) {
|
||||
expect(res.issues).toContainEqual({
|
||||
path: "plugins.slots.memory",
|
||||
message: "plugin not found: missing-memory-backend",
|
||||
});
|
||||
expect(res.warnings).toContainEqual({
|
||||
path: "plugins.slots.memory",
|
||||
message:
|
||||
"plugin: memory slot plugin not found or not marked as memory: missing-memory-backend",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("surfaces allowed enum values for plugin config diagnostics", async () => {
|
||||
const res = validateInSuite({
|
||||
agents: { list: [{ id: "pi" }] },
|
||||
|
||||
@@ -49,7 +49,7 @@ function resolvePluginDiagnosticPath(diag: {
|
||||
if (diag.message.includes("plugin path not found")) {
|
||||
return "plugins.load.paths";
|
||||
}
|
||||
if (diag.code === "capability_slot_conflict" && diag.slot) {
|
||||
if (diag.slot) {
|
||||
return resolveCapabilitySlotConfigPath(diag.slot as CapabilitySlotId);
|
||||
}
|
||||
if (diag.pluginId) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import type { PluginRegistry } from "./registry.js";
|
||||
import type {
|
||||
PluginHookAfterSearchProviderActivateEvent,
|
||||
PluginHookAfterSearchProviderConfigureEvent,
|
||||
PluginHookAfterProviderActivateEvent,
|
||||
PluginHookAfterProviderConfigureEvent,
|
||||
PluginHookAfterCompactionEvent,
|
||||
PluginHookAfterToolCallEvent,
|
||||
PluginHookAgentContext,
|
||||
@@ -18,6 +20,8 @@ import type {
|
||||
PluginHookBeforeAgentStartResult,
|
||||
PluginHookBeforeSearchProviderConfigureEvent,
|
||||
PluginHookBeforeSearchProviderConfigureResult,
|
||||
PluginHookBeforeProviderConfigureEvent,
|
||||
PluginHookBeforeProviderConfigureResult,
|
||||
PluginHookBeforeModelResolveEvent,
|
||||
PluginHookBeforeModelResolveResult,
|
||||
PluginHookBeforePromptBuildEvent,
|
||||
@@ -64,6 +68,8 @@ export type {
|
||||
PluginHookBeforeAgentStartResult,
|
||||
PluginHookBeforeSearchProviderConfigureEvent,
|
||||
PluginHookBeforeSearchProviderConfigureResult,
|
||||
PluginHookBeforeProviderConfigureEvent,
|
||||
PluginHookBeforeProviderConfigureResult,
|
||||
PluginHookBeforeModelResolveEvent,
|
||||
PluginHookBeforeModelResolveResult,
|
||||
PluginHookBeforePromptBuildEvent,
|
||||
@@ -104,6 +110,8 @@ export type {
|
||||
PluginHookGatewayStopEvent,
|
||||
PluginHookAfterSearchProviderConfigureEvent,
|
||||
PluginHookAfterSearchProviderActivateEvent,
|
||||
PluginHookAfterProviderConfigureEvent,
|
||||
PluginHookAfterProviderActivateEvent,
|
||||
};
|
||||
|
||||
export type HookRunnerLogger = {
|
||||
@@ -201,6 +209,16 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
}),
|
||||
});
|
||||
|
||||
const mergeBeforeProviderConfigure = (
|
||||
acc: PluginHookBeforeProviderConfigureResult | undefined,
|
||||
next: PluginHookBeforeProviderConfigureResult,
|
||||
): PluginHookBeforeProviderConfigureResult => ({
|
||||
note: concatOptionalTextSegments({
|
||||
left: acc?.note,
|
||||
right: next.note,
|
||||
}),
|
||||
});
|
||||
|
||||
const handleHookError = (params: {
|
||||
hookName: PluginHookName;
|
||||
pluginId: string;
|
||||
@@ -348,6 +366,32 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
>("before_search_provider_configure", event, ctx, mergeBeforeSearchProviderConfigure);
|
||||
}
|
||||
|
||||
async function runBeforeProviderConfigure(
|
||||
event: PluginHookBeforeProviderConfigureEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
): Promise<PluginHookBeforeProviderConfigureResult | undefined> {
|
||||
return runModifyingHook<"before_provider_configure", PluginHookBeforeProviderConfigureResult>(
|
||||
"before_provider_configure",
|
||||
event,
|
||||
ctx,
|
||||
mergeBeforeProviderConfigure,
|
||||
);
|
||||
}
|
||||
|
||||
async function runAfterProviderConfigure(
|
||||
event: PluginHookAfterProviderConfigureEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
): Promise<void> {
|
||||
return runVoidHook("after_provider_configure", event, ctx);
|
||||
}
|
||||
|
||||
async function runAfterProviderActivate(
|
||||
event: PluginHookAfterProviderActivateEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
): Promise<void> {
|
||||
return runVoidHook("after_provider_activate", event, ctx);
|
||||
}
|
||||
|
||||
async function runAfterSearchProviderConfigure(
|
||||
event: PluginHookAfterSearchProviderConfigureEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
@@ -771,8 +815,11 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
|
||||
runBeforeModelResolve,
|
||||
runBeforePromptBuild,
|
||||
runBeforeAgentStart,
|
||||
runBeforeProviderConfigure,
|
||||
runBeforeSearchProviderConfigure,
|
||||
runAfterProviderConfigure,
|
||||
runAfterSearchProviderConfigure,
|
||||
runAfterProviderActivate,
|
||||
runAfterSearchProviderActivate,
|
||||
runLlmInput,
|
||||
runLlmOutput,
|
||||
|
||||
@@ -2256,4 +2256,84 @@ describe("loadOpenClawPlugins", () => {
|
||||
expect.arrayContaining([expect.objectContaining({ id: "memory-b", status: "error" })]),
|
||||
);
|
||||
});
|
||||
|
||||
it("errors when a declared capability is not registered at runtime", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id: "search-manifest-mismatch",
|
||||
body: `module.exports = { id: "search-manifest-mismatch", register(api) { api.registerSearchProvider({ id: "alpha", name: "Alpha", search: async () => ({ content: "alpha" }) }); } };`,
|
||||
manifest: {
|
||||
id: "search-manifest-mismatch",
|
||||
configSchema: EMPTY_PLUGIN_SCHEMA,
|
||||
provides: ["providers.search.beta"],
|
||||
},
|
||||
});
|
||||
|
||||
const registry = loadRegistryFromSinglePlugin({ plugin });
|
||||
|
||||
expect(registry.diagnostics).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
level: "error",
|
||||
pluginId: "search-manifest-mismatch",
|
||||
code: "capability_declared_not_registered",
|
||||
capability: "providers.search.beta",
|
||||
message: "declared capability was not registered at runtime: providers.search.beta",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("warns when a runtime capability is not declared in the manifest", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id: "search-runtime-undeclared",
|
||||
body: `module.exports = { id: "search-runtime-undeclared", register(api) { api.registerSearchProvider({ id: "alpha", name: "Alpha", search: async () => ({ content: "alpha" }) }); } };`,
|
||||
manifest: {
|
||||
id: "search-runtime-undeclared",
|
||||
configSchema: EMPTY_PLUGIN_SCHEMA,
|
||||
},
|
||||
});
|
||||
|
||||
const registry = loadRegistryFromSinglePlugin({ plugin });
|
||||
|
||||
expect(registry.diagnostics).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
level: "warn",
|
||||
pluginId: "search-runtime-undeclared",
|
||||
code: "capability_registered_not_declared",
|
||||
capability: "providers.search.alpha",
|
||||
message: "runtime capability was not declared in manifest: providers.search.alpha",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("emits a structured diagnostic when the configured memory slot is missing", () => {
|
||||
useNoBundledPlugins();
|
||||
|
||||
const registry = loadOpenClawPlugins({
|
||||
cache: false,
|
||||
config: {
|
||||
plugins: {
|
||||
slots: {
|
||||
memory: "missing-memory-backend",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(registry.diagnostics).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
level: "warn",
|
||||
code: "capability_slot_selection_missing",
|
||||
slot: "memory.backend",
|
||||
capability: "missing-memory-backend",
|
||||
message: "memory slot plugin not found or not marked as memory: missing-memory-backend",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -340,6 +340,10 @@ function collectDeclaredCapabilities(plugin: PluginRecord): Set<string> {
|
||||
return new Set([...plugin.declaredCapabilities, ...plugin.capabilityIds]);
|
||||
}
|
||||
|
||||
function collectCapabilityIds(plugin: PluginRecord): Set<string> {
|
||||
return new Set(plugin.capabilityIds);
|
||||
}
|
||||
|
||||
function evaluateCapabilityRelationships(params: {
|
||||
activePlugins: PluginRecord[];
|
||||
candidatePlugin?: PluginRecord;
|
||||
@@ -417,6 +421,44 @@ function evaluateCapabilityRelationships(params: {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
function evaluateCapabilityDeclarationAlignment(plugin: PluginRecord): PluginDiagnostic[] {
|
||||
const diagnostics: PluginDiagnostic[] = [];
|
||||
const declaredCapabilities = new Set(plugin.declaredCapabilities);
|
||||
const runtimeCapabilities = collectCapabilityIds(plugin);
|
||||
|
||||
for (const capability of declaredCapabilities) {
|
||||
if (runtimeCapabilities.has(capability)) {
|
||||
continue;
|
||||
}
|
||||
diagnostics.push({
|
||||
level: "error",
|
||||
pluginId: plugin.id,
|
||||
source: plugin.source,
|
||||
code: "capability_declared_not_registered",
|
||||
capability,
|
||||
slot: capability.includes(".") ? capability.split(".").slice(0, -1).join(".") : undefined,
|
||||
message: `declared capability was not registered at runtime: ${capability}`,
|
||||
});
|
||||
}
|
||||
|
||||
for (const capability of runtimeCapabilities) {
|
||||
if (declaredCapabilities.has(capability)) {
|
||||
continue;
|
||||
}
|
||||
diagnostics.push({
|
||||
level: "warn",
|
||||
pluginId: plugin.id,
|
||||
source: plugin.source,
|
||||
code: "capability_registered_not_declared",
|
||||
capability,
|
||||
slot: capability.includes(".") ? capability.split(".").slice(0, -1).join(".") : undefined,
|
||||
message: `runtime capability was not declared in manifest: ${capability}`,
|
||||
});
|
||||
}
|
||||
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
function recordPluginError(params: {
|
||||
logger: PluginLogger;
|
||||
registry: PluginRegistry;
|
||||
@@ -987,6 +1029,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
message: "plugin register returned a promise; async registration is ignored",
|
||||
});
|
||||
}
|
||||
pushDiagnostics(registry.diagnostics, evaluateCapabilityDeclarationAlignment(record));
|
||||
registry.plugins.push(record);
|
||||
seenIds.set(pluginId, candidate.origin);
|
||||
} catch (err) {
|
||||
@@ -1007,6 +1050,9 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
if (typeof memorySlot === "string" && !memorySlotMatched) {
|
||||
registry.diagnostics.push({
|
||||
level: "warn",
|
||||
code: "capability_slot_selection_missing",
|
||||
slot: "memory.backend",
|
||||
capability: memorySlot,
|
||||
message: `memory slot plugin not found or not marked as memory: ${memorySlot}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -487,9 +487,12 @@ export type PluginDiagnostic = {
|
||||
source?: string;
|
||||
code?:
|
||||
| "capability_declared_duplicate"
|
||||
| "capability_declared_not_registered"
|
||||
| "capability_missing_requirement"
|
||||
| "capability_conflict_present"
|
||||
| "capability_slot_conflict";
|
||||
| "capability_slot_conflict"
|
||||
| "capability_registered_not_declared"
|
||||
| "capability_slot_selection_missing";
|
||||
capability?: string;
|
||||
slot?: string;
|
||||
};
|
||||
@@ -502,8 +505,11 @@ export type PluginHookName =
|
||||
| "before_model_resolve"
|
||||
| "before_prompt_build"
|
||||
| "before_agent_start"
|
||||
| "before_provider_configure"
|
||||
| "before_search_provider_configure"
|
||||
| "after_provider_configure"
|
||||
| "after_search_provider_configure"
|
||||
| "after_provider_activate"
|
||||
| "after_search_provider_activate"
|
||||
| "llm_input"
|
||||
| "llm_output"
|
||||
@@ -531,8 +537,11 @@ export const PLUGIN_HOOK_NAMES = [
|
||||
"before_model_resolve",
|
||||
"before_prompt_build",
|
||||
"before_agent_start",
|
||||
"before_provider_configure",
|
||||
"before_search_provider_configure",
|
||||
"after_provider_configure",
|
||||
"after_search_provider_configure",
|
||||
"after_provider_activate",
|
||||
"after_search_provider_activate",
|
||||
"llm_input",
|
||||
"llm_output",
|
||||
@@ -678,8 +687,30 @@ export type PluginHookSearchProviderContext = {
|
||||
workspaceDir?: string;
|
||||
};
|
||||
|
||||
export type PluginHookProviderLifecycleContext = {
|
||||
workspaceDir?: string;
|
||||
};
|
||||
|
||||
export type PluginHookSearchProviderSource = "builtin" | "plugin";
|
||||
|
||||
export type PluginHookProviderKind = "search";
|
||||
|
||||
export type PluginHookBeforeProviderConfigureEvent = {
|
||||
providerKind: PluginHookProviderKind;
|
||||
slot: string;
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
providerSource: PluginHookSearchProviderSource;
|
||||
pluginId?: string;
|
||||
intent: "switch-active" | "configure-provider";
|
||||
activeProviderId?: string | null;
|
||||
configured: boolean;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeProviderConfigureResult = {
|
||||
note?: string;
|
||||
};
|
||||
|
||||
export type PluginHookBeforeSearchProviderConfigureEvent = {
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
@@ -694,6 +725,18 @@ export type PluginHookBeforeSearchProviderConfigureResult = {
|
||||
note?: string;
|
||||
};
|
||||
|
||||
export type PluginHookAfterProviderConfigureEvent = {
|
||||
providerKind: PluginHookProviderKind;
|
||||
slot: string;
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
providerSource: PluginHookSearchProviderSource;
|
||||
pluginId?: string;
|
||||
intent: "switch-active" | "configure-provider";
|
||||
activeProviderId?: string | null;
|
||||
configured: boolean;
|
||||
};
|
||||
|
||||
export type PluginHookAfterSearchProviderConfigureEvent = {
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
@@ -704,6 +747,17 @@ export type PluginHookAfterSearchProviderConfigureEvent = {
|
||||
configured: boolean;
|
||||
};
|
||||
|
||||
export type PluginHookAfterProviderActivateEvent = {
|
||||
providerKind: PluginHookProviderKind;
|
||||
slot: string;
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
providerSource: PluginHookSearchProviderSource;
|
||||
pluginId?: string;
|
||||
previousProviderId?: string | null;
|
||||
intent: "switch-active" | "configure-provider";
|
||||
};
|
||||
|
||||
export type PluginHookAfterSearchProviderActivateEvent = {
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
@@ -1026,6 +1080,13 @@ export type PluginHookHandlerMap = {
|
||||
event: PluginHookBeforeAgentStartEvent,
|
||||
ctx: PluginHookAgentContext,
|
||||
) => Promise<PluginHookBeforeAgentStartResult | void> | PluginHookBeforeAgentStartResult | void;
|
||||
before_provider_configure: (
|
||||
event: PluginHookBeforeProviderConfigureEvent,
|
||||
ctx: PluginHookProviderLifecycleContext,
|
||||
) =>
|
||||
| Promise<PluginHookBeforeProviderConfigureResult | void>
|
||||
| PluginHookBeforeProviderConfigureResult
|
||||
| void;
|
||||
before_search_provider_configure: (
|
||||
event: PluginHookBeforeSearchProviderConfigureEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
@@ -1033,10 +1094,18 @@ export type PluginHookHandlerMap = {
|
||||
| Promise<PluginHookBeforeSearchProviderConfigureResult | void>
|
||||
| PluginHookBeforeSearchProviderConfigureResult
|
||||
| void;
|
||||
after_provider_configure: (
|
||||
event: PluginHookAfterProviderConfigureEvent,
|
||||
ctx: PluginHookProviderLifecycleContext,
|
||||
) => Promise<void> | void;
|
||||
after_search_provider_configure: (
|
||||
event: PluginHookAfterSearchProviderConfigureEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
) => Promise<void> | void;
|
||||
after_provider_activate: (
|
||||
event: PluginHookAfterProviderActivateEvent,
|
||||
ctx: PluginHookProviderLifecycleContext,
|
||||
) => Promise<void> | void;
|
||||
after_search_provider_activate: (
|
||||
event: PluginHookAfterSearchProviderActivateEvent,
|
||||
ctx: PluginHookSearchProviderContext,
|
||||
|
||||
Reference in New Issue
Block a user