mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-19 14:04:51 +00:00
333 lines
8.8 KiB
TypeScript
333 lines
8.8 KiB
TypeScript
import type { OperatorScope } from "../gateway/operator-scopes.js";
|
|
import type { AgentEventPayload, AgentEventStream } from "../infra/agent-events.js";
|
|
import type {
|
|
PluginHookAgentContext,
|
|
PluginHookBeforeToolCallEvent,
|
|
PluginHookBeforeToolCallResult,
|
|
PluginHookToolContext,
|
|
} from "./hook-types.js";
|
|
import type { PluginJsonValue } from "./host-hook-json.js";
|
|
import type {
|
|
PluginAgentTurnPrepareResult,
|
|
PluginNextTurnInjectionPlacement,
|
|
PluginNextTurnInjectionRecord,
|
|
} from "./host-hook-turn-types.js";
|
|
|
|
export { isPluginJsonValue } from "./host-hook-json.js";
|
|
export type { PluginJsonPrimitive, PluginJsonValue } from "./host-hook-json.js";
|
|
export type {
|
|
PluginAgentTurnPrepareEvent,
|
|
PluginAgentTurnPrepareResult,
|
|
PluginHeartbeatPromptContributionEvent,
|
|
PluginHeartbeatPromptContributionResult,
|
|
PluginNextTurnInjection,
|
|
PluginNextTurnInjectionEnqueueResult,
|
|
PluginNextTurnInjectionPlacement,
|
|
PluginNextTurnInjectionRecord,
|
|
} from "./host-hook-turn-types.js";
|
|
|
|
export type PluginHostCleanupReason = "disable" | "reset" | "delete" | "restart";
|
|
|
|
export type PluginSessionExtensionProjectionContext = {
|
|
sessionKey: string;
|
|
sessionId?: string;
|
|
state: PluginJsonValue | undefined;
|
|
};
|
|
|
|
export type PluginSessionExtensionRegistration = {
|
|
namespace: string;
|
|
description: string;
|
|
project?: (ctx: PluginSessionExtensionProjectionContext) => PluginJsonValue | undefined;
|
|
cleanup?: (ctx: { reason: PluginHostCleanupReason; sessionKey?: string }) => void | Promise<void>;
|
|
/**
|
|
* When set, after every successful `patchSessionExtension` the projected
|
|
* value is mirrored to `SessionEntry[<slotKey>]` so non-plugin readers
|
|
* can consume the typed slot without reaching into
|
|
* `pluginExtensions[pluginId][namespace]`.
|
|
*
|
|
* The slot is a read-only mirror: writes always go through
|
|
* `patchSessionExtension`; the host overwrites the slot value on every
|
|
* subsequent patch.
|
|
*/
|
|
sessionEntrySlotKey?: string;
|
|
/**
|
|
* Optional JSON-compatible schema describing the projected slot value.
|
|
* Purely informational at this layer; clients may use it to validate the
|
|
* mirrored slot against a contract.
|
|
*/
|
|
sessionEntrySlotSchema?: PluginJsonValue;
|
|
};
|
|
|
|
export type PluginSessionExtensionProjection = {
|
|
pluginId: string;
|
|
namespace: string;
|
|
value: PluginJsonValue;
|
|
};
|
|
|
|
export type PluginSessionExtensionPatchParams = {
|
|
key: string;
|
|
pluginId: string;
|
|
namespace: string;
|
|
value?: PluginJsonValue;
|
|
unset?: boolean;
|
|
};
|
|
|
|
export type PluginToolPolicyDecision =
|
|
| PluginHookBeforeToolCallResult
|
|
| {
|
|
allow?: boolean;
|
|
reason?: string;
|
|
};
|
|
|
|
export type PluginTrustedToolPolicyRegistration = {
|
|
id: string;
|
|
description: string;
|
|
evaluate: (
|
|
event: PluginHookBeforeToolCallEvent,
|
|
ctx: PluginHookToolContext,
|
|
) => PluginToolPolicyDecision | void | Promise<PluginToolPolicyDecision | void>;
|
|
};
|
|
|
|
export type PluginToolMetadataRegistration = {
|
|
toolName: string;
|
|
displayName?: string;
|
|
description?: string;
|
|
risk?: "low" | "medium" | "high";
|
|
tags?: string[];
|
|
};
|
|
|
|
export type PluginCommandContinuation = {
|
|
continueAgent?: boolean;
|
|
};
|
|
|
|
export type PluginControlUiDescriptor = {
|
|
id: string;
|
|
surface: "session" | "tool" | "run" | "settings";
|
|
label: string;
|
|
description?: string;
|
|
placement?: string;
|
|
schema?: PluginJsonValue;
|
|
requiredScopes?: OperatorScope[];
|
|
};
|
|
|
|
export type PluginSessionActionContext = {
|
|
pluginId: string;
|
|
actionId: string;
|
|
sessionKey?: string;
|
|
payload?: PluginJsonValue;
|
|
client?: {
|
|
connId?: string;
|
|
scopes: string[];
|
|
};
|
|
};
|
|
|
|
export type PluginSessionActionResult =
|
|
| {
|
|
ok?: true;
|
|
result?: PluginJsonValue;
|
|
reply?: PluginJsonValue;
|
|
continueAgent?: boolean;
|
|
}
|
|
| {
|
|
ok: false;
|
|
error: string;
|
|
code?: string;
|
|
details?: PluginJsonValue;
|
|
};
|
|
|
|
export type PluginSessionActionRegistration = {
|
|
id: string;
|
|
description?: string;
|
|
schema?: PluginJsonValue;
|
|
requiredScopes?: OperatorScope[];
|
|
handler: (
|
|
ctx: PluginSessionActionContext,
|
|
) => PluginSessionActionResult | void | Promise<PluginSessionActionResult | void>;
|
|
};
|
|
|
|
export type PluginRuntimeLifecycleRegistration = {
|
|
id: string;
|
|
description?: string;
|
|
cleanup?: (ctx: {
|
|
reason: PluginHostCleanupReason;
|
|
sessionKey?: string;
|
|
runId?: string;
|
|
}) => void | Promise<void>;
|
|
};
|
|
|
|
export type PluginAgentEventSubscriptionRegistration = {
|
|
id: string;
|
|
description?: string;
|
|
streams?: AgentEventStream[];
|
|
handle: (
|
|
event: AgentEventPayload,
|
|
ctx: {
|
|
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Run-context JSON reads are caller-typed by namespace.
|
|
getRunContext: <T extends PluginJsonValue = PluginJsonValue>(
|
|
namespace: string,
|
|
) => T | undefined;
|
|
setRunContext: (namespace: string, value: PluginJsonValue) => void;
|
|
clearRunContext: (namespace?: string) => void;
|
|
},
|
|
) => void | Promise<void>;
|
|
};
|
|
|
|
export type PluginAgentEventEmitParams = {
|
|
runId: string;
|
|
stream: AgentEventStream;
|
|
data: PluginJsonValue;
|
|
sessionKey?: string;
|
|
};
|
|
|
|
export type PluginAgentEventEmitResult =
|
|
| { emitted: true; stream: AgentEventStream }
|
|
| { emitted: false; reason: string };
|
|
|
|
export type PluginRunContextPatch = {
|
|
runId: string;
|
|
namespace: string;
|
|
value?: PluginJsonValue;
|
|
unset?: boolean;
|
|
};
|
|
|
|
export type PluginRunContextGetParams = {
|
|
runId: string;
|
|
namespace: string;
|
|
};
|
|
|
|
export type PluginSessionSchedulerJobRegistration = {
|
|
id: string;
|
|
sessionKey: string;
|
|
kind: string;
|
|
description?: string;
|
|
cleanup?: (ctx: {
|
|
reason: PluginHostCleanupReason;
|
|
sessionKey: string;
|
|
jobId: string;
|
|
}) => void | Promise<void>;
|
|
};
|
|
|
|
export type PluginSessionSchedulerJobHandle = {
|
|
id: string;
|
|
pluginId: string;
|
|
sessionKey: string;
|
|
kind: string;
|
|
};
|
|
|
|
export type PluginSessionAttachmentFile = {
|
|
path: string;
|
|
};
|
|
|
|
export type PluginAttachmentChannelHints = {
|
|
telegram?: {
|
|
parseMode?: "HTML";
|
|
disableNotification?: boolean;
|
|
/**
|
|
* Require host-side detection to match this MIME before forcing document delivery.
|
|
* Mismatched files are rejected before the outbound adapter is called.
|
|
*/
|
|
forceDocumentMime?: string;
|
|
};
|
|
slack?: {
|
|
threadTs?: string;
|
|
};
|
|
};
|
|
|
|
export type PluginSessionAttachmentCaptionFormat = "plain" | "html" | "markdown";
|
|
|
|
export type PluginSessionAttachmentParams = {
|
|
sessionKey: string;
|
|
files: PluginSessionAttachmentFile[];
|
|
text?: string;
|
|
threadId?: string | number;
|
|
forceDocument?: boolean;
|
|
maxBytes?: number;
|
|
captionFormat?: PluginSessionAttachmentCaptionFormat;
|
|
channelHints?: PluginAttachmentChannelHints;
|
|
};
|
|
|
|
export type PluginSessionAttachmentResult =
|
|
| {
|
|
ok: true;
|
|
channel: string;
|
|
deliveredTo: string;
|
|
count: number;
|
|
}
|
|
| { ok: false; error: string };
|
|
|
|
export type PluginSessionTurnSchedule =
|
|
| { at: string | number | Date }
|
|
| { delayMs: number }
|
|
| { cron: string; tz?: string };
|
|
|
|
type PluginSessionTurnScheduleCommonParams = {
|
|
sessionKey: string;
|
|
message: string;
|
|
agentId?: string;
|
|
deliveryMode?: "none" | "announce";
|
|
name?: string;
|
|
/** Optional cleanup tag. Reserved cron-name delimiters like `:` are rejected. */
|
|
tag?: string;
|
|
};
|
|
|
|
export type PluginSessionTurnScheduleParams =
|
|
| ({
|
|
at: string | number | Date;
|
|
deleteAfterRun?: boolean;
|
|
} & PluginSessionTurnScheduleCommonParams)
|
|
| ({
|
|
delayMs: number;
|
|
deleteAfterRun?: boolean;
|
|
} & PluginSessionTurnScheduleCommonParams)
|
|
| ({
|
|
cron: string;
|
|
tz?: string;
|
|
deleteAfterRun?: false;
|
|
} & PluginSessionTurnScheduleCommonParams);
|
|
|
|
export type PluginSessionTurnUnscheduleByTagParams = {
|
|
sessionKey: string;
|
|
tag: string;
|
|
};
|
|
|
|
export type PluginSessionTurnUnscheduleByTagResult = {
|
|
removed: number;
|
|
failed: number;
|
|
};
|
|
|
|
export function normalizePluginHostHookId(value: string | undefined): string {
|
|
return (value ?? "").trim();
|
|
}
|
|
|
|
function normalizeQueuedInjectionText(
|
|
entry: PluginNextTurnInjectionRecord,
|
|
placement: PluginNextTurnInjectionPlacement,
|
|
): string | undefined {
|
|
const candidate = entry as {
|
|
placement?: unknown;
|
|
text?: unknown;
|
|
};
|
|
if (candidate.placement !== placement || typeof candidate.text !== "string") {
|
|
return undefined;
|
|
}
|
|
const text = candidate.text.trim();
|
|
return text || undefined;
|
|
}
|
|
|
|
export function buildPluginAgentTurnPrepareContext(params: {
|
|
queuedInjections: PluginNextTurnInjectionRecord[];
|
|
}): PluginAgentTurnPrepareResult {
|
|
const prepend = params.queuedInjections
|
|
.map((entry) => normalizeQueuedInjectionText(entry, "prepend_context"))
|
|
.filter(Boolean);
|
|
const append = params.queuedInjections
|
|
.map((entry) => normalizeQueuedInjectionText(entry, "append_context"))
|
|
.filter(Boolean);
|
|
return {
|
|
...(prepend.length > 0 ? { prependContext: prepend.join("\n\n") } : {}),
|
|
...(append.length > 0 ? { appendContext: append.join("\n\n") } : {}),
|
|
};
|
|
}
|
|
|
|
export type PluginHostHookRunContext = PluginHookAgentContext;
|