openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
616 lines • 27 kB
TypeScript
import { Dt as ToolResultMessage, G as Model, S as AssistantMessageEvent, Tt as Tool, W as Message, gt as StreamFn$1, k as ImageContent, mt as SimpleStreamOptions, x as AssistantMessage, yt as TextContent } from "./types.openclaw-yUy4bDSr.js";
import "./index-BOg-bzEX.js";
import { Static, TSchema } from "typebox";
//#region packages/agent-core/src/types.d.ts
/**
* Stream function used by the agent loop.
*
* Contract:
* - Must not throw or return a rejected promise for request/model/runtime failures.
* - Must return an AssistantMessageEventStream.
* - Failures must be encoded in the returned stream via protocol events and a
* final AssistantMessage with stopReason "error" or "aborted" and errorMessage.
*/
type StreamFn = StreamFn$1;
/**
* Configuration for how tool calls from a single assistant message are executed.
*
* - "sequential": prepare, execute, and finalize each call before the next; steering can skip the tail after one call starts.
* - "parallel": prepare calls sequentially, then execute allowed tools concurrently without steering skips.
* `tool_execution_end` is emitted in tool completion order after each tool is finalized,
* while tool-result message artifacts are emitted later in assistant source order.
*/
type ToolExecutionMode = "sequential" | "parallel";
/**
* Controls how many queued user messages are injected when the agent loop reaches a queue drain point.
*
* - "all": drain and inject every queued message at that point.
* - "one-at-a-time": drain and inject only the oldest queued message, leaving the rest queued for later drain points.
*/
type QueueMode = "all" | "one-at-a-time";
type AgentToolCall = Extract<AssistantMessage["content"][number], {
type: "toolCall";
}>;
/**
* Result returned from `beforeToolCall`.
*
* Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.
* `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.
*/
interface BeforeToolCallResult {
block?: boolean;
reason?: string;
}
/** A call participating in an internal whole-batch admission check. */
interface InternalToolBatchCall {
toolCall: AgentToolCall;
/** Validated arguments, or the raw arguments when validation rejected the call. */
args: unknown;
/** Resolved tool identity for OpenClaw-owned argument canonicalization. */
tool?: AgentTool;
/**
* Error result for a call rejected by argument validation. It never executes;
* the batch lifecycle commits it at its assistant-order launch position.
*/
validationFailure?: AgentToolResult<unknown>;
}
/** Typed core signal used to recover once from a critical tool loop. */
interface ToolLoopIntervention {
kind: "critical-tool-loop";
toolCallId: string;
toolName: string;
actionKey: string;
detector: string;
count: number;
reason: string;
}
/** Bucketed feedback for an admitted call, not a veto or recovery attempt. */
interface ToolLoopWarning {
kind: "tool-loop-warning";
toolCallId: string;
count: number;
}
interface InternalBeforeToolBatchContext {
assistantMessage: AssistantMessage;
calls: InternalToolBatchCall[];
context: AgentContext;
}
type InternalBeforeToolBatchResult = {
intervention: ToolLoopIntervention;
warnings?: never;
} | {
intervention?: never;
warnings?: ToolLoopWarning[];
};
interface DeferredToolCallContext {
assistantMessage: AssistantMessage;
/** The raw tool call block from `assistantMessage.content`. */
toolCall: AgentToolCall;
/** Current agent context when the hook runs. */
context: AgentContext;
}
/**
* Partial override returned from `afterToolCall`.
*
* Merge semantics are field-by-field:
* - `content`: if provided, replaces the tool result content array in full
* - `details`: if provided, replaces the tool result details value in full
* - `isError`: if provided, replaces the tool result error flag
* - `terminate`: if provided, replaces the early-termination hint
*
* Omitted fields keep the original executed tool result values.
* There is no deep merge for `content` or `details`.
*/
interface AfterToolCallResult {
content?: (TextContent | ImageContent)[];
details?: unknown;
isError?: boolean;
/**
* Hint that the agent should stop after the current tool batch.
* Early termination only happens when every finalized tool result in the batch sets this to true.
*/
terminate?: boolean;
}
interface BeforeToolCallContext extends DeferredToolCallContext {
/** Validated tool arguments for the target tool schema. */
args: unknown;
}
interface AfterToolCallContext extends BeforeToolCallContext {
/** The executed tool result before unknown `afterToolCall` overrides are applied. */
result: AgentToolResult<unknown>;
/** Whether the executed tool result is currently treated as an error. */
isError: boolean;
}
/**
* Context passed to `afterToolOutcome` after every finalized tool outcome.
*
* Unlike `afterToolCall`, this hook also observes failures that prevented
* execution. `args` contains validated arguments when execution reached the
* prepared state, otherwise the raw model arguments.
*/
interface AfterToolOutcomeContext extends AfterToolCallContext {
/** Whether the tool implementation started executing. */
executionStarted: boolean;
/** Typed pre-execution failure provenance when available. */
errorKind?: "argument-validation";
}
interface ShouldStopAfterTurnContext {
/** The assistant message that completed the turn. */
message: AssistantMessage;
/** Tool result messages passed to the preceding `turn_end` event. */
toolResults: ToolResultMessage[];
/** Current agent context after the turn's assistant message and tool results have been appended. */
context: AgentContext;
/** Messages that this loop invocation will return if it exits at this point. Prompt runs include the initial prompt messages; continuation runs do not include pre-existing context messages. */
newMessages: AgentMessage[];
}
/** Replacement runtime state used by the agent loop before starting another provider request. */
type AgentLoopContinuationUpdate = Pick<AgentContext, "systemPrompt" | "tools">;
interface AgentLoopTurnUpdate {
/** Commit accepted steering and settle this invocation without another model request. */
stop?: boolean;
/** Prepare only an admitted continuation, after its queued input has been emitted. */
prepareContinuation?: (context: AgentContext) => AgentLoopContinuationUpdate | Promise<AgentLoopContinuationUpdate>;
/** Context for the next provider request. */
context?: AgentContext;
/** Model for the next provider request. */
model?: Model;
/** Thinking level for the next provider request. */
thinkingLevel?: ThinkingLevel;
}
interface PrepareNextTurnContext extends ShouldStopAfterTurnContext {}
/** @internal Mutable loop safety evidence shared by prompt retries in one Agent run. */
type ToolLoopRecoveryState = {
criticalToolLoopSeen: boolean;
repeatedToolError?: {
signature: string;
count: number;
};
};
interface AgentLoopConfig extends SimpleStreamOptions {
model: Model;
/** Logical thinking level retained across model changes before provider mapping. */
thinkingLevel?: ThinkingLevel;
/**
* Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.
*
* Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage
* that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,
* status messages) should be filtered out.
*
* Contract: must not throw or reject. Return a safe fallback value instead.
* Throwing interrupts the low-level agent loop without producing a normal event sequence.
*/
convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
/**
* Optional transform applied to the context before `convertToLlm`.
*
* Use this for operations that work at the AgentMessage level:
* - Context window management (pruning old messages)
* - Injecting context from external sources
*
* Contract: must not throw or reject. Return the original messages or another
* safe fallback value instead.
*/
transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;
/**
* Resolves an API key dynamically for each LLM call.
*
* Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire
* during long-running tool execution phases.
*
* Contract: must not throw or reject. Return undefined when no key is available.
*/
getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
/**
* Called after each turn fully completes and `turn_end` has been emitted.
*
* If it returns true, the loop emits `agent_end` and exits before polling steering or follow-up queues,
* without starting another LLM call. Steering already drained at a tool checkpoint takes precedence,
* so this hook is deferred until that steering turn completes.
*
* Use this to request a graceful stop after the current turn, e.g. before context gets too full.
*
* Contract: must not throw or reject. Throwing interrupts the low-level agent loop without producing a normal event sequence.
*/
shouldStopAfterTurn?: (context: ShouldStopAfterTurnContext) => boolean | Promise<boolean>;
/**
* Called after `turn_end` and before the loop decides whether another provider request should start.
* Return replacement context/model/thinking state to affect the next turn in this run.
* Return undefined to keep using the current context/config.
*/
prepareNextTurn?: (context: PrepareNextTurnContext) => AgentLoopTurnUpdate | undefined | Promise<AgentLoopTurnUpdate | undefined>;
/**
* Returns steering messages to inject into the conversation mid-run.
*
* After a call from the assistant message actually starts, sequential execution
* checks before each later call, including again after asynchronous preparation.
* Streamed batches share that started state. Parallel batches never steering-skip.
* Both modes check after a batch settles, before stop hooks. Drained messages
* follow tool results before the next LLM call; already-running calls continue.
*
* Once a check returns messages, the loop carries that exact result to the
* next turn without polling again. This preserves queue drain ordering.
*
* Contract: must not throw or reject. Resolve to [] when no steering messages are available.
*/
getSteeringMessages?: () => Promise<AgentMessage[]>;
/**
* Returns follow-up messages to process after the agent would otherwise stop.
*
* Called when the agent has no more tool calls and no steering messages.
* If messages are returned, they're added to the context and the agent
* continues with another turn.
*
* Contract: must not throw or reject. Return [] when no follow-up messages are available.
*/
getFollowUpMessages?: () => Promise<AgentMessage[]>;
/** Consumes the cancellation fact for a previously drained queue message. */
consumeQueuedMessageCancellation?: (message: AgentMessage) => boolean;
/** Default: "parallel" */
toolExecution?: ToolExecutionMode;
/**
* Called before a tool is executed, after arguments have been validated.
*
* Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.
* The hook receives the agent abort signal and is responsible for honoring it.
*/
beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
/** @internal OpenClaw-owned batch admission. Not a plugin or session SDK hook. */
beforeToolBatch?: (context: InternalBeforeToolBatchContext, signal?: AbortSignal) => Promise<InternalBeforeToolBatchResult | undefined>;
/**
* @internal OpenClaw-owned turn completion. Runs once after every tool call from one
* assistant message has settled; returning true ends the turn even when not every
* result asked to terminate. Not a plugin or session SDK hook.
*/
completesToolTurn?: (context: {
message: AssistantMessage;
toolResults: ToolResultMessage[];
}) => boolean;
/** @internal Preserves loop safety evidence across Agent.continue() retries. */
toolLoopRecoveryState?: ToolLoopRecoveryState;
/**
* Hydrates an already-authorized tool that was deferred out of the current
* provider-visible tool set. Return undefined for every other unknown name so
* the loop keeps the normal "Tool <name> not found" result. Thrown or rejected
* failures become error tool results for the requested call.
*/
resolveDeferredTool?: (context: DeferredToolCallContext, signal?: AbortSignal) => Promise<AgentTool | undefined> | AgentTool | undefined;
/**
* Called after a tool finishes executing, before `tool_execution_end` and tool-result message events are emitted.
*
* Return an `AfterToolCallResult` to override parts of the executed tool result.
* The hook receives the agent abort signal and is responsible for honoring it.
*/
afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
/**
* Called after every tool outcome is finalized, including failures that
* prevented execution. It runs after `afterToolCall` for executed tools.
*/
afterToolOutcome?: (context: AfterToolOutcomeContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
}
/**
* Thinking/reasoning level for models that support it.
* Note: "xhigh" is only supported by selected model families. Use model thinking-level metadata
* from openclaw/plugin-sdk/llm to detect support for a concrete model.
*/
type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
interface BashExecutionMessage {
role: "bashExecution";
command: string;
/** Captured command output, usually already truncated for context. */
output: string;
/** Process exit code when the command reached process exit. */
exitCode: number | undefined;
cancelled: boolean;
/** True when output was shortened for transcript/context storage. */
truncated: boolean;
/** Optional path containing the complete output when truncation occurred. */
fullOutputPath?: string;
/** Millisecond timestamp for transcript ordering. */
timestamp: number;
/** Exclude this command transcript from model context while keeping it in session history. */
excludeFromContext?: boolean;
}
interface CustomMessage<T = unknown> {
role: "custom";
/** Application-defined discriminator for rendering or handling this message. */
customType: string;
/** Content replayed into model context when this message is included. */
content: string | (TextContent | ImageContent)[];
/** Whether UI surfaces should display this message. */
display: boolean;
/** Keep display-only application activity out of future model context. */
excludeFromContext?: boolean;
details?: T;
/** Millisecond timestamp for transcript ordering. */
timestamp: number;
}
interface BranchSummaryMessage {
role: "branchSummary";
/** Summary text inserted back into model context. */
summary: string;
/** Entry id of the branch root or source leaf being summarized. */
fromId: string;
/** Millisecond timestamp for transcript ordering. */
timestamp: number;
}
interface CompactionSummaryMessage {
role: "compactionSummary";
/** Summary text inserted back into model context. */
summary: string;
tokensBefore: number;
/** Timestamp may be numeric in memory or string when loaded from older persisted rows. */
timestamp: number | string;
tokensAfter?: number;
/** Optional first retained entry id from the compaction range. */
firstKeptEntryId?: string;
details?: unknown;
}
/**
* Extensible interface for custom app and harness messages.
* Apps can extend via declaration merging.
*/
interface CustomAgentMessages {
bashExecution: BashExecutionMessage;
custom: CustomMessage;
branchSummary: BranchSummaryMessage;
compactionSummary: CompactionSummaryMessage;
}
type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];
/**
* Public agent state.
*
* `tools` and `messages` use accessor properties so implementations can copy
* assigned arrays before storing them.
*/
interface AgentState {
/** System prompt sent with each model request. */
systemPrompt: string;
/** Active model used for future turns. */
model: Model;
/** Requested reasoning level for future turns. */
thinkingLevel: ThinkingLevel;
/** Available tools. Assigning a new array copies the top-level array. */
set tools(tools: AgentTool[]);
get tools(): AgentTool[];
/** Conversation transcript. Assigning a new array copies the top-level array. */
set messages(messages: AgentMessage[]);
get messages(): AgentMessage[];
/**
* True while the agent is processing a prompt or continuation.
*
* This remains true until awaited `agent_end` listeners settle.
*/
readonly isStreaming: boolean;
/** Partial assistant message for the current streamed response, if any. */
readonly streamingMessage?: AgentMessage;
/** Tool call ids currently executing. */
readonly pendingToolCalls: ReadonlySet<string>;
/** Error message from the most recent failed or aborted assistant turn, if any. */
readonly errorMessage?: string;
}
/** Channel-safe progress text emitted by a running tool. */
interface AgentToolProgress {
/** Public text suitable for user-facing progress surfaces. */
text: string;
/** Tool progress is rendered by channel progress UIs. */
visibility: "channel";
/** Progress text must not contain secrets, private args, or fetched content. */
privacy: "public";
/** Optional stable id for progress line replacement. */
id?: string;
}
interface AgentToolResult<T> {
/** Text or image content returned to the model. */
content: (TextContent | ImageContent)[];
/** Arbitrary structured details for logs or UI rendering. */
details: T;
/** Optional public progress hint for partial tool updates; never model content. */
progress?: AgentToolProgress;
/**
* Hint that the agent should stop after the current tool batch.
* Early termination only happens when every finalized tool result in the batch sets this to true.
*/
terminate?: boolean;
}
/** Callback used by tools to stream partial execution updates. */
type AgentToolUpdateCallback<T = unknown> = (partialResult: AgentToolResult<T>) => void;
/** Origin class for tool output that can taint later model-authored content in the same turn. */
type ToolResultContentSource = "network";
interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unknown> extends Tool<TParameters> {
/** Human-readable label for UI display. */
label: string;
/** Optional schema for the structured `AgentToolResult.details` value. */
outputSchema?: TSchema;
/** Preserve lifecycle telemetry without rendering transient channel progress. */
hideFromChannelProgress?: boolean;
/** Tool results contain externally controlled network content. */
resultContentSource?: ToolResultContentSource;
/**
* Optional compatibility shim for raw tool-call arguments before schema validation.
* Must return an object that matches `TParameters`.
*/
prepareArguments?: (args: unknown) => Static<TParameters>;
/** Execute the tool call. Throw on failure instead of encoding errors in `content`. */
execute: (toolCallId: string, params: Static<TParameters>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<TDetails>) => Promise<AgentToolResult<TDetails>>;
/**
* Per-tool execution mode override.
* - "sequential": this tool must execute one at a time with other tool calls.
* - "parallel": this tool can execute concurrently with other tool calls.
*
* If omitted, the default execution mode applies.
*/
executionMode?: ToolExecutionMode;
}
/** Context snapshot passed into the low-level agent loop. */
interface AgentContext {
/** System prompt included with the request. */
systemPrompt: string;
/** Transcript visible to the model. */
messages: AgentMessage[];
/** Tools available for this run. */
tools?: AgentTool[];
}
/**
* Events emitted by the Agent for UI updates.
*
* `agent_end` is the last event emitted for a run, but awaited `Agent.subscribe()`
* listeners for that event are still part of run settlement. The agent becomes
* idle only after those listeners finish.
*/
type AgentEvent = {
type: "agent_start";
} | {
type: "agent_end";
messages: AgentMessage[];
} | {
type: "turn_start";
} | {
type: "turn_end";
message: AgentMessage;
toolResults: ToolResultMessage[];
} | {
type: "message_start";
message: AgentMessage;
} | {
type: "message_update";
message: AgentMessage;
assistantMessageEvent: AssistantMessageEvent;
} | {
type: "message_end";
message: AgentMessage;
} | {
type: "tool_execution_start";
toolCallId: string;
toolName: string;
args: unknown;
hideFromChannelProgress?: boolean;
} | {
type: "tool_execution_update";
toolCallId: string;
toolName: string;
args: unknown;
partialResult: unknown;
hideFromChannelProgress?: boolean;
} | {
type: "tool_execution_end";
toolCallId: string;
/** Issuing assistant response identity; provider call ids are only unique within one response. */
assistantTurnId?: string;
toolName: string;
result: unknown;
isError: boolean;
/** False when resolution, preparation, validation, policy, or queued steering prevented execution. */
executionStarted?: boolean;
/** Typed pre-execution failure provenance for safe downstream diagnostics. */
errorKind?: "argument-validation";
hideFromChannelProgress?: boolean;
};
//#endregion
//#region src/agents/image-sanitization.d.ts
type ImageSanitizationLimits = {
maxDimensionPx?: number;
maxBytes?: number;
};
//#endregion
//#region src/agents/tools/tool-results.d.ts
declare function textResult<TDetails>(text: string, details: TDetails): AgentToolResult<TDetails>;
declare function jsonResult<TDetails>(payload: TDetails): AgentToolResult<TDetails>;
//#endregion
//#region src/agents/tools/common.d.ts
type AgentToolWithMeta<TParameters extends TSchema, TResult> = AgentTool<TParameters, TResult> & {
displaySummary?: string;
/** Keep this tool model-visible; hidden catalog bridges cannot preserve its result contract. */
catalogMode?: "direct-only";
/** Gateway client capabilities required before this tool can be assembled. */
requiredClientCaps?: string[];
/**
* Allow a result's `details.sourceReply` to be delivered to the current source as the
* user-visible reply, without another model turn. Only the tool author can declare this;
* tool results alone never grant it.
*/
canDeliverSourceReply?: boolean;
/** Tool-owned execution and transport wait budget, before any harness completion grace. */
getExecutionTimeoutMs?: (args: unknown) => number | undefined;
prepareBeforeToolCallParams?: (params: unknown, ctx: {
toolCallId?: string;
hookContext?: unknown;
signal?: AbortSignal;
}) => unknown;
finalizeBeforeToolCallParams?: (params: unknown, preparedParams: unknown) => unknown;
};
type ErasedAgentToolExecute = {
execute(this: void, toolCallId: string, params: unknown, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback): Promise<AgentToolResult<unknown>>;
};
type AnyAgentTool = Omit<AgentToolWithMeta<TSchema, unknown>, "execute"> & ErasedAgentToolExecute;
type StringParamOptions = {
required?: boolean;
trim?: boolean;
label?: string;
allowEmpty?: boolean;
};
type ActionGate<T extends Record<string, boolean | undefined>> = (key: keyof T, defaultValue?: boolean) => boolean;
declare function createActionGate<T extends Record<string, boolean | undefined>>(actions: T | undefined): ActionGate<T>;
declare function readToolStringParam(params: Record<string, unknown>, key: string, options: StringParamOptions & {
required: true;
}): string;
declare function readToolStringParam(params: Record<string, unknown>, key: string, options?: StringParamOptions): string | undefined;
declare function readStringOrNumberParam(params: Record<string, unknown>, key: string, options?: {
required?: boolean;
label?: string;
}): string | undefined;
declare function readNumberParam(params: Record<string, unknown>, key: string, options?: {
required?: boolean;
label?: string;
integer?: boolean;
strict?: boolean;
positiveInteger?: boolean;
nonNegativeInteger?: boolean;
}): number | undefined;
declare function readPositiveIntegerParam(params: Record<string, unknown>, key: string, options?: {
message?: string;
max?: number;
}): number | undefined;
declare function readNonNegativeIntegerParam(params: Record<string, unknown>, key: string, options?: {
message?: string;
max?: number;
}): number | undefined;
declare function readFiniteNumberParam(params: Record<string, unknown>, key: string, options?: {
message?: string;
min?: number;
max?: number;
minExclusive?: boolean;
maxExclusive?: boolean;
}): number | undefined;
declare function readStringArrayParam(params: Record<string, unknown>, key: string, options: StringParamOptions & {
required: true;
}): string[];
declare function readStringArrayParam(params: Record<string, unknown>, key: string, options?: StringParamOptions): string[] | undefined;
type ReactionParams = {
emoji: string;
remove: boolean;
isEmpty: boolean;
};
declare function readReactionParams(params: Record<string, unknown>, options: {
emojiKey?: string;
removeKey?: string;
removeErrorMessage: string;
}): ReactionParams;
declare function imageResultFromFile(params: {
label: string;
path: string;
extraText?: string;
details?: Record<string, unknown>;
imageSanitization?: ImageSanitizationLimits;
}): Promise<AgentToolResult<unknown>>;
type AvailableTag = {
id?: string;
name: string;
moderated?: boolean;
emoji_id?: string | null;
emoji_name?: string | null;
};
declare function parseAvailableTags(raw: unknown): AvailableTag[] | undefined;
//#endregion
export { StreamFn as A, AgentTool as C, CustomMessage as D, BashExecutionMessage as E, ToolExecutionMode as M, ToolLoopWarning as N, PrepareNextTurnContext as O, AgentState as S, AgentToolUpdateCallback as T, AfterToolCallResult as _, parseAvailableTags as a, AgentLoopTurnUpdate as b, readNumberParam as c, readStringArrayParam as d, readStringOrNumberParam as f, ImageSanitizationLimits as g, textResult as h, imageResultFromFile as i, ThinkingLevel as j, QueueMode as k, readPositiveIntegerParam as l, jsonResult as m, AnyAgentTool as n, readFiniteNumberParam as o, readToolStringParam as p, createActionGate as r, readNonNegativeIntegerParam as s, ActionGate as t, readReactionParams as u, AgentEvent as v, AgentToolResult as w, AgentMessage as x, AgentLoopConfig as y };