mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-24 12:59:33 +00:00
164 lines
5.4 KiB
TypeScript
164 lines
5.4 KiB
TypeScript
/**
|
|
* Tags Code Mode exec/wait control tools and normalizes hook params for the
|
|
* exec-compatible before-tool-call surface.
|
|
*/
|
|
import { isPlainObject } from "../utils.js";
|
|
import { normalizeToolName } from "./tool-policy.js";
|
|
import type { AnyAgentTool } from "./tools/common.js";
|
|
|
|
/** Model-visible Code Mode exec tool name. */
|
|
export const CODE_MODE_EXEC_TOOL_NAME = "exec";
|
|
/** Model-visible Code Mode wait tool name. */
|
|
export const CODE_MODE_WAIT_TOOL_NAME = "wait";
|
|
/** Hook metadata kind for Code Mode exec tools. */
|
|
export const CODE_MODE_EXEC_TOOL_KIND = "code_mode_exec";
|
|
|
|
/** Hook metadata kind type for Code Mode exec tools. */
|
|
export type CodeModeExecToolKind = typeof CODE_MODE_EXEC_TOOL_KIND;
|
|
/** Source language accepted by the Code Mode exec tool. */
|
|
export type CodeModeExecToolInputKind = "javascript" | "typescript";
|
|
/** Metadata attached to before-tool-call events for Code Mode exec. */
|
|
export type CodeModeExecHookMetadata = {
|
|
toolKind: CodeModeExecToolKind;
|
|
toolInputKind?: CodeModeExecToolInputKind;
|
|
};
|
|
|
|
const codeModeControlTools = new WeakSet<AnyAgentTool>();
|
|
|
|
/** Mark a tool as owned by code mode control flow. */
|
|
export function markCodeModeControlTool<T extends AnyAgentTool>(tool: T): T {
|
|
codeModeControlTools.add(tool);
|
|
return tool;
|
|
}
|
|
|
|
/** Return whether a tool was marked as code-mode owned. */
|
|
export function isCodeModeControlTool(tool: AnyAgentTool): boolean {
|
|
return codeModeControlTools.has(tool);
|
|
}
|
|
|
|
function isCodeModeExecTool(tool: AnyAgentTool): boolean {
|
|
return isCodeModeControlTool(tool) && normalizeToolName(tool.name) === CODE_MODE_EXEC_TOOL_NAME;
|
|
}
|
|
|
|
function resolveCodeModeExecToolInputKind(params: unknown): CodeModeExecToolInputKind | undefined {
|
|
if (!isPlainObject(params)) {
|
|
return undefined;
|
|
}
|
|
const language = params.language;
|
|
if (language === undefined || language === "javascript") {
|
|
return "javascript";
|
|
}
|
|
if (language === "typescript") {
|
|
return "typescript";
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function normalizeCodeModeExecParams(params: unknown): unknown {
|
|
if (!isPlainObject(params)) {
|
|
return params;
|
|
}
|
|
const code = params.code;
|
|
const command = params.command;
|
|
if (typeof code === "string" && typeof command !== "string") {
|
|
// Code-mode accepts both `code` and generic exec `command`; keep them paired
|
|
// so downstream hooks can read either shape.
|
|
return { ...params, command: params.code };
|
|
}
|
|
if (typeof command === "string" && typeof code !== "string") {
|
|
return { ...params, code: params.command };
|
|
}
|
|
return params;
|
|
}
|
|
|
|
/** Build before-tool-call metadata for a marked code-mode exec tool. */
|
|
export function getCodeModeExecBeforeHookMetadata(params: {
|
|
tool: AnyAgentTool;
|
|
params: unknown;
|
|
}): CodeModeExecHookMetadata | undefined {
|
|
if (!isCodeModeExecTool(params.tool)) {
|
|
return undefined;
|
|
}
|
|
const toolInputKind = resolveCodeModeExecToolInputKind(params.params);
|
|
return {
|
|
toolKind: CODE_MODE_EXEC_TOOL_KIND,
|
|
...(toolInputKind && { toolInputKind }),
|
|
};
|
|
}
|
|
|
|
/** Build before-tool-call metadata when only the tool kind is available. */
|
|
export function getCodeModeExecBeforeHookMetadataForToolKind(params: {
|
|
toolKind: unknown;
|
|
params: unknown;
|
|
}): CodeModeExecHookMetadata | undefined {
|
|
if (params.toolKind !== CODE_MODE_EXEC_TOOL_KIND) {
|
|
return undefined;
|
|
}
|
|
const toolInputKind = resolveCodeModeExecToolInputKind(params.params);
|
|
return {
|
|
toolKind: CODE_MODE_EXEC_TOOL_KIND,
|
|
...(toolInputKind && { toolInputKind }),
|
|
};
|
|
}
|
|
|
|
/** Normalize before-hook params for a marked code-mode exec tool. */
|
|
export function normalizeCodeModeExecBeforeHookParams(params: {
|
|
tool: AnyAgentTool;
|
|
params: unknown;
|
|
}): unknown {
|
|
if (!isCodeModeExecTool(params.tool)) {
|
|
return params.params;
|
|
}
|
|
return normalizeCodeModeExecParams(params.params);
|
|
}
|
|
|
|
/** Normalize before-hook params when only the code-mode tool kind is available. */
|
|
export function normalizeCodeModeExecBeforeHookParamsForToolKind(params: {
|
|
toolKind: unknown;
|
|
params: unknown;
|
|
}): unknown {
|
|
if (params.toolKind !== CODE_MODE_EXEC_TOOL_KIND) {
|
|
return params.params;
|
|
}
|
|
return normalizeCodeModeExecParams(params.params);
|
|
}
|
|
|
|
/** Reconcile hook-adjusted `code` and `command` fields after code-mode normalization. */
|
|
export function reconcileCodeModeExecBeforeHookParams(params: {
|
|
tool: AnyAgentTool;
|
|
originalParams: unknown;
|
|
hookParams: unknown;
|
|
adjustedParams: unknown;
|
|
}): unknown {
|
|
if (
|
|
!isCodeModeExecTool(params.tool) ||
|
|
!isPlainObject(params.originalParams) ||
|
|
!isPlainObject(params.hookParams) ||
|
|
!isPlainObject(params.adjustedParams)
|
|
) {
|
|
return params.adjustedParams;
|
|
}
|
|
const hookCode = params.hookParams.code;
|
|
const hookCommand = params.hookParams.command;
|
|
if (typeof hookCode !== "string" || hookCode !== hookCommand) {
|
|
return params.adjustedParams;
|
|
}
|
|
|
|
const adjustedCode = params.adjustedParams.code;
|
|
const adjustedCommand = params.adjustedParams.command;
|
|
const adjustedCodeChanged = typeof adjustedCode === "string" && adjustedCode !== hookCode;
|
|
const adjustedCommandChanged =
|
|
typeof adjustedCommand === "string" && adjustedCommand !== hookCode;
|
|
if (adjustedCodeChanged === adjustedCommandChanged) {
|
|
return params.adjustedParams;
|
|
}
|
|
|
|
if (adjustedCodeChanged) {
|
|
return { ...params.adjustedParams, command: adjustedCode };
|
|
}
|
|
if (adjustedCommandChanged) {
|
|
return { ...params.adjustedParams, code: adjustedCommand };
|
|
}
|
|
return params.adjustedParams;
|
|
}
|