// Lightweight runtime surface for plugin-owned agent harnesses. // Keep heavyweight tool construction out of this module so harness imports can // register quickly inside gateway startup and Docker e2e runs. import { shouldLoadRequesterScopedMcpHarnessRuntime } from "../agents/agent-bundle-mcp-runtime-shared.js"; import { mergeAgentRunAttemptTerminal, normalizeAgentRunAttemptTerminal, projectAgentRunAttemptTerminal, setAgentRunAttemptTerminalFailure, } from "../agents/agent-run-terminal-outcome.js"; import type { CodexBundleMcpThreadConfig, LoadCodexBundleMcpThreadConfigParams, } from "../agents/codex-mcp-config.types.js"; import { resolveActiveEmbeddedRunSessionId } from "../agents/embedded-agent-runner/active-run-projections.js"; import type { EmbeddedRunAttemptParams as CoreEmbeddedRunAttemptParams, EmbeddedRunAttemptResult, } from "../agents/embedded-agent-runner/run/types.js"; import { abortAndDrainEmbeddedAgentRun, abortEmbeddedAgentRun, clearActiveEmbeddedRun, queueEmbeddedAgentMessageWithOutcome, setActiveEmbeddedRun, type EmbeddedAgentQueueMessageOptions, } from "../agents/embedded-agent-runner/runs.js"; import { runStructuredInput } from "../agents/harness/structured-input-execution.js"; import { compileStructuredInputForm, compileStructuredInputQuestions, compileStructuredInputUrl, isStructuredInputRecord, snapshotStructuredInput, } from "../agents/harness/structured-input.js"; import type { SandboxFsBridge } from "../agents/sandbox/fs-bridge.js"; import { inferToolMetaFromArgsCore } from "../agents/tool-display.js"; import { createToolPolicyMatcher } from "../agents/tool-policy-match.js"; import { expandToolGroups } from "../agents/tool-policy-shared.js"; import { buildWatchedSessionsPromptLines, prepareWatchedSessionsPrompt, } from "../agents/watched-sessions-prompt.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveExecModePolicy } from "../infra/exec-approvals-core.js"; import { maxAsk, minSecurity } from "../infra/exec-approvals-policy.js"; import type { ImageContent } from "../llm/types.js"; import { redactToolDetail } from "../logging/redact.js"; import type { PromptImageOrderEntry } from "../media/prompt-image-order.js"; import { truncateUtf16Safe } from "../utils.js"; /** Default truncation limit for user-facing tool progress output. */ export const TOOL_PROGRESS_OUTPUT_MAX_CHARS = 8_000; /** Core exec mode algebra for plugin-owned policy adapters. */ export const execPolicy = Object.freeze({ resolveExecModePolicy, minSecurity, maxAsk }); /** * Renders the Watched Sessions prompt block for plugin-owned harness prompts. * Harness runtimes that assemble their own instruction layers (e.g. Codex) * must surface the same watched-session facts as the embedded prompt, or the * model keeps refusing cross-session questions on those runtimes (openclaw#114797). */ export function buildWatchedSessionsHarnessContext(params: { config?: OpenClawConfig; sessionKey?: string; sandboxed?: boolean; toolNames: Iterable; capabilityToolNames?: Iterable; }): string | undefined { const lines = buildWatchedSessionsPromptLines( prepareWatchedSessionsPrompt({ enabled: true, ...params }), ); return lines.length > 0 ? lines.join("\n").trimEnd() : undefined; } export { FAST_MODE_AUTO_PROGRESS_KIND } from "../auto-reply/reply-payload.js"; export { buildTemporalContextText } from "../agents/date-time.js"; export { projectProgressCardChannelUpdate } from "../session-cards/progress-card-channel-summary.js"; export { isDeliveredMessageToolOnlySourceReplyResult, isDeliveredMessagingToolResult, } from "../agents/embedded-agent-message-tool-source-reply.js"; export { formatFastModeAutoProgressText, resolveFastModeForElapsed } from "../shared/fast-mode.js"; export type { AgentMessage } from "../agents/runtime/index.js"; export type { FastModeAutoProgressState } from "../shared/fast-mode.js"; export type { AgentHarness, AgentHarnessV2, AgentHarnessAuthBindingFingerprintParams, AgentHarnessAttemptParams, AgentHarnessAttemptParamsV2, AgentHarnessAttemptResult, AgentHarnessCompactParams, AgentHarnessCompactResult, AgentHarnessNativeCompaction, AgentHarnessNativeCompactionParams, AgentHarnessNativeCompactionRequest, AgentHarnessModelCatalogParams, AgentHarnessRegistrationOptions, AgentHarnessDeliveryDefaults, AgentHarnessResultClassification, AgentHarnessRuntimeArtifactBinding, AgentHarnessSideQuestionParams, AgentHarnessSideQuestionParamsV2, AgentHarnessSideQuestionResult, AgentHarnessSettledTurnFinalizationResult, AgentHarnessResetParams, AgentHarnessSessionDeletionParams, AgentHarnessSessionDeletionMutation, AgentHarnessSessionForkFailureCode, AgentHarnessSessionForkParams, AgentHarnessSessionForkResult, AgentHarnessSupport, AgentHarnessSupportContext, } from "../agents/harness/types.js"; export { AgentHarnessPreflightError, AgentHarnessSessionSupersededError, } from "../agents/harness/errors.js"; export { projectSettledTurnFinalizationAttemptResult } from "../agents/harness/settled-turn-finalization-result.js"; export const agentHarnessAttemptTerminal = { merge: mergeAgentRunAttemptTerminal, normalize: normalizeAgentRunAttemptTerminal, project: projectAgentRunAttemptTerminal, setFailure: setAgentRunAttemptTerminalFailure, }; export { projectAgentHarnessTranscriptMessageForDisplay } from "../agents/harness/transcript-visibility.js"; export { isOpenClawRuntimeContextCustomMessage } from "../agents/internal-runtime-context.js"; export { restorePreparedUserTurnOperationalMetaForRuntime } from "../sessions/user-turn-transcript.metadata.js"; export { fingerprintResolvedAuthProfileCredential } from "../agents/execution-auth-binding.js"; export type { AgentHarnessUserInputAnswers, AgentHarnessUserInputOption, AgentHarnessUserInputPromptOptions, AgentHarnessUserInputQuestion, } from "../agents/harness/user-input-bridge.js"; export type { AgentHarnessQuestionGatewayCall } from "../agents/harness/gateway-question-dispatch.js"; type EmbeddedRunAttemptParamsBase = Omit< CoreEmbeddedRunAttemptParams, | "admittedRunContext" | "authoredContextTokenCap" | "contextEngineLogicalTurnLease" | "onContextEngineTurnCandidate" | "pluginHarnessToolPolicySafeDeniedTools" | "trajectoryRecorder" > & { /** Per-model context cap authored by the operator and forwarded to harness runtimes. */ authoredContextTokenCap?: number; /** Audited exact denies that the plugin harness must enforce against native equivalents. */ pluginHarnessToolPolicySafeDeniedTools?: readonly string[]; }; /** * @deprecated Use EmbeddedRunAttemptParamsV2. The optional capability keeps * existing harness source compatible through 2026-10-12. */ export type EmbeddedRunAttemptParams = EmbeddedRunAttemptParamsBase & { hostCapabilities?: import("../agents/harness/host-capability-types.js").AgentHarnessHostCapabilities; }; /** Current host-prepared attempt contract for agent harnesses. */ export type EmbeddedRunAttemptParamsV2 = EmbeddedRunAttemptParamsBase & { hostCapabilities: import("../agents/harness/host-capability-types.js").AgentHarnessHostCapabilities; }; export type { EmbeddedRunAttemptResult }; export type { ContextEngine as HarnessContextEngine, ContextEngineHostCapability, ContextEngineOperation, ContextEngineProjection, } from "../context-engine/types.js"; export type { CompactEmbeddedAgentSessionParams } from "../agents/embedded-agent-runner/compact.js"; export type { EmbeddedAgentCompactResult } from "../agents/embedded-agent-runner/types.js"; export type { AnyAgentTool } from "../agents/tools/common.js"; export type { MessagingToolSend, MessagingToolSourceReplyPayload, } from "../agents/embedded-agent-messaging.types.js"; export type { HeartbeatToolResponse } from "../auto-reply/heartbeat-tool-response.js"; export type { AgentApprovalEventData, AgentEventPayload } from "../infra/agent-events.js"; export type { ExecApprovalDecision } from "../infra/exec-approvals.js"; /** * Plugin exec-review contract: low/medium-risk `allow-once` permits one run; * `ask` routes to human approval. Consumers must handle `deny` explicitly: * do not run or escalate, and return the rationale and rejection guidance to the * agent. The configured exec reviewer maps provider failures, timeouts, and * invalid responses to `ask`, and detected reviewer-directed injection to `deny`. */ export type { ExecAutoReviewDecision, ExecAutoReviewInput, ExecAutoReviewer, } from "../infra/exec-auto-review.js"; export type { NormalizedUsage } from "../agents/usage.js"; export type { AgentToolResultMiddleware, AgentToolResultMiddlewareContext, AgentToolResultMiddlewareEvent, AgentToolResultMiddlewareOptions, AgentToolResultMiddlewareResult, AgentToolResultMiddlewareRuntime, OpenClawAgentToolResult, } from "../plugins/agent-tool-result-middleware-types.js"; export type { CodexAppServerExtensionContext, CodexAppServerExtensionFactory, CodexAppServerExtensionRuntime, CodexAppServerToolResultEvent, CodexAppServerToolResultHandlerResult, } from "../plugins/codex-app-server-extension-types.js"; export type { NativeHookRelayEvent, NativeHookRelayProcessResponse, NativeHookRelayProvider, NativeHookRelayRegistrationHandle, } from "../agents/harness/native-hook-relay.js"; export { VERSION as OPENCLAW_VERSION } from "../version.js"; export { formatErrorMessage } from "../infra/errors.js"; export { formatApprovalDisplayPath } from "../infra/approval-display-paths.js"; export { buildAgentHookContextChannelFields } from "../plugins/hook-agent-context.js"; export { emitAgentEvent, onAgentEvent, resetAgentEventsForTest } from "../infra/agent-events.js"; export { runAgentCleanupStep } from "../agents/run-cleanup-timeout.js"; export { resolveAgentRunAbortLifecycleFields } from "../agents/run-termination.js"; export { isHostScopedAgentToolActive } from "../agents/agent-tools.ring-zero-context.js"; export { log as embeddedAgentLog } from "../agents/embedded-agent-runner/logger.js"; export { buildAgentRuntimePlan } from "../agents/runtime-plan/build.js"; export { prepareAgentRuntimeAuth } from "../agents/runtime-plan/prepare-auth.js"; export { classifyEmbeddedAgentRunResultForModelFallback } from "../agents/embedded-agent-runner/result-fallback-classifier.js"; export { resolveUserPath } from "../utils.js"; export { callGatewayTool } from "../agents/tools/gateway.js"; export { hasGatewayToolRoutingContext } from "../agents/tools/in-process-gateway.js"; export type { NodeListNode } from "../agents/tools/nodes-utils.js"; export { listNodes, resolveNodeIdFromList, selectDefaultNodeFromList, } from "../agents/tools/nodes-utils.js"; export { formatToolAggregate } from "../auto-reply/tool-meta.js"; export { HEARTBEAT_RESPONSE_TOOL_NAME, normalizeHeartbeatToolResponse, } from "../auto-reply/heartbeat-tool-response.js"; export { isMessagingTool, isMessagingToolSendAction } from "../agents/embedded-agent-messaging.js"; export { extractMessagingToolSend, extractMessagingToolSendResult, } from "../agents/embedded-agent-messaging-extraction.js"; export { extractToolResultMediaArtifact, filterToolResultMediaUrls, } from "../agents/embedded-agent-tool-media.js"; export { extractToolErrorMessage, sanitizeToolResult, } from "../agents/embedded-agent-tool-results.js"; export { formatToolExecutionErrorMessage, isToolResultError, resolveToolExecutionErrorKind, resolveToolResultFailureKind, type ToolResultFailureKind, } from "../agents/tool-result-error.js"; export { normalizeUsage } from "../agents/usage.js"; export { resolveAgentDir, resolveDefaultAgentDir } from "../agents/agent-scope.js"; export { resolveSessionAgentIds } from "./agent-scope-runtime.js"; export { resolveModelAuthMode } from "../agents/model-auth.js"; export { supportsModelTools } from "../agents/model-tool-support.js"; export { isAgentToolReplaySafe } from "../agents/tool-replay-safety.js"; export { getChannelAgentToolMeta } from "../agents/channel-tool-metadata.js"; export { buildAgentHarnessUserInputAnswers, deliverAgentHarnessUserInputPrompt, emptyAgentHarnessUserInputAnswers, formatAgentHarnessUserInputPrompt, normalizeAgentHarnessUserInputAnswer, } from "../agents/harness/user-input-bridge.js"; export { cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, runAgentHarnessGatewayQuestion, } from "../agents/harness/gateway-question.js"; /** Bounded structured-input compilation and execution for native agent harnesses. */ export const agentHarnessStructuredInput = Object.freeze({ compileForm: compileStructuredInputForm, compileQuestions: compileStructuredInputQuestions, compileUrl: compileStructuredInputUrl, isRecord: isStructuredInputRecord, run: runStructuredInput, snapshot: snapshotStructuredInput, }); export { buildSkillWorkshopPromptSection, SKILL_WORKSHOP_TOOL_NAME, } from "../agents/skill-workshop-prompt.js"; export { buildDelegationGuidanceSection, resolveMainSessionDelegationMode, } from "../agents/delegation-guidance.js"; export { buildHarnessVisibleReplyGuidance } from "../auto-reply/source-reply-delivery-mode.js"; export { buildCredentialSafetyPrompt } from "../agents/credential-safety-prompt.js"; export { buildUiPresentationPrompt } from "../agents/ui-presentation-prompt.js"; export { normalizeQuestionTimeoutSeconds } from "../agents/tools/ask-user-tool-normalization.js"; export { resolveAttemptFsWorkspaceOnly } from "../agents/embedded-agent-runner/run/attempt-prompt-helpers.js"; export { resolveAttemptSpawnWorkspaceDir } from "../agents/embedded-agent-runner/run/attempt-thread-helpers.js"; export { buildEmbeddedAttemptToolRunContext } from "../agents/embedded-agent-runner/run/attempt-tool-run-context.js"; export { applyEmbeddedAttemptToolsAllow, resolveEmbeddedAttemptToolConstructionPlan, } from "../agents/embedded-agent-runner/run/attempt-tool-construction-plan.js"; export { getPluginToolMeta, getPluginToolSideEffectOwnerKey } from "../plugins/tool-metadata.js"; export { attachModelProviderRequestTransport, getModelProviderRequestTransport, } from "../agents/provider-request-config.js"; export { abortAndDrainEmbeddedAgentRun as abortAndDrainAgentHarnessRun, abortEmbeddedAgentRun as abortAgentHarnessRun, clearActiveEmbeddedRun, resolveActiveEmbeddedRunSessionId, setActiveEmbeddedRun, }; /** * @deprecated Active-run queueing is an internal runtime concern. This legacy * boolean API only reports immediate queue eligibility and cannot observe async * runtime rejection; runtime-owned delivery paths should use acceptance-aware * steering instead of public SDK queueing. */ export function queueAgentHarnessMessage( sessionId: string, text: string, options?: EmbeddedAgentQueueMessageOptions, ): boolean { return queueEmbeddedAgentMessageWithOutcome(sessionId, text, options).queued; } export { finalizeAgentToolAvailability } from "../agents/agent-tool-availability.js"; export { disposeRegisteredAgentHarnesses } from "../agents/harness/registry.js"; export { logAgentRuntimeToolDiagnostics, normalizeAgentRuntimeTools, } from "../agents/runtime-plan/tools.js"; export { filterProviderNormalizableTools, inspectRuntimeToolInputSchemas, projectRuntimeToolInputSchema, type RuntimeToolInputSchemaJson, type RuntimeToolInputSchemaProjection, type RuntimeToolSchemaDiagnostic, } from "../agents/tool-schema-projection.js"; export type { CodexBundleMcpThreadConfig, LoadCodexBundleMcpThreadConfigParams, } from "../agents/codex-mcp-config.types.js"; export { normalizeProviderToolSchemas } from "../agents/embedded-agent-runner/tool-schema-runtime.js"; /** Detect prompt image references and load them through the same limits used by embedded runs. */ export async function detectAndLoadAgentHarnessPromptImages(params: { prompt: string; userTurnTranscriptRecorder?: EmbeddedAgentQueueMessageOptions["userTurnTranscriptRecorder"]; workspaceDir: string; model: { input?: string[] }; existingImages?: ImageContent[]; imageOrder?: PromptImageOrderEntry[]; media?: import("../media/media-facts.js").MediaFact[]; config?: import("../config/types.openclaw.js").OpenClawConfig; workspaceOnly?: boolean; localRoots?: readonly string[]; sandbox?: { root: string; bridge: SandboxFsBridge }; }): Promise<{ images: ImageContent[]; imageFactIndexes: Array; detectedRefs: Array<{ raw: string; resolved: string; type: "path" | "media-uri" }>; loadedCount: number; skippedCount: number; failedMediaCount: number; }> { const [{ resolveImageSanitizationLimits }, { detectAndLoadPromptImages }, { MAX_IMAGE_BYTES }] = await Promise.all([ import("../agents/image-sanitization.js"), import("../agents/embedded-agent-runner/run/images.js"), import("@openclaw/media-core/constants"), ]); return detectAndLoadPromptImages({ prompt: params.prompt, workspaceDir: params.workspaceDir, model: params.model, existingImages: params.existingImages, imageOrder: params.imageOrder, media: params.media, userTurnTranscriptRecorder: params.userTurnTranscriptRecorder, maxBytes: MAX_IMAGE_BYTES, maxDimensionPx: resolveImageSanitizationLimits(params.config).maxDimensionPx, workspaceOnly: params.workspaceOnly, localRoots: params.localRoots, sandbox: params.sandbox, }); } /** Load Codex bundle MCP thread config without forcing the heavy config module into SDK imports. */ export async function loadCodexBundleMcpThreadConfig( params: LoadCodexBundleMcpThreadConfigParams, ): Promise { const { loadCodexBundleMcpThreadConfigCore: load } = await import("../agents/codex-mcp-config.js"); return load(params); } /** Lazily load the strict MCP proxy client with core-owned framing, startup, and shutdown. */ export const mcpStdioRuntime = Object.freeze({ async load() { const { createMcpStdioClient } = await import("../agents/mcp-stdio-client.js"); return { createMcpStdioClient }; }, }); export type { McpToolCatalog, SessionMcpRuntime } from "../agents/agent-bundle-mcp-types.js"; export { assignSafeServerNames as assignMcpCatalogSafeServerNames } from "../agents/agent-bundle-mcp-names.js"; /** * Materialize an MCP App view for a tool executed by a harness-native MCP client. * The harness supplies a runtime adapter so the view keeps using that exact connection. */ export async function prepareHarnessNativeMcpAppPreview(params: { runtime: import("../agents/agent-bundle-mcp-types.js").SessionMcpRuntime; agentId?: string; serverName: string; toolName: string; uiResourceUri: string; toolCallId: string; toolInput: unknown; toolResult: import("@modelcontextprotocol/sdk/types.js").CallToolResult; allowedAppToolNames: ReadonlySet; resultMetaState?: "unavailable"; }): Promise<{ mcpAppPreview: unknown } | undefined> { if (params.runtime.mcpAppsEnabled !== true) { return undefined; } const { buildMcpAppCanvasPayload, fetchMcpAppView } = await import("../agents/mcp-ui-resource.js"); const view = await fetchMcpAppView({ runtime: params.runtime, agentId: params.agentId, serverName: params.serverName, toolName: params.toolName, uiResourceUri: params.uiResourceUri, toolCallId: params.toolCallId, toolInput: params.toolInput, toolResult: params.toolResult, allowedAppToolNames: params.allowedAppToolNames, }); if (!view) { return undefined; } return { mcpAppPreview: buildMcpAppCanvasPayload({ ...view, ...(params.runtime.sessionKey ? { originSessionKey: params.runtime.sessionKey } : {}), ...(params.resultMetaState ? { resultMetaState: params.resultMetaState } : {}), }), }; } /** * Materialize requester-scoped MCP tools for a harness run (dynamic tools, not * harness-native MCP config). Lazy-loaded so harness plugins avoid the MCP manager graph. */ export async function materializeRequesterScopedMcpToolsForHarnessRun( params: Parameters< typeof import("../agents/agent-bundle-mcp-harness.js").materializeRequesterScopedMcpToolsForHarnessRunCore >[0], ): Promise< Awaited< ReturnType< typeof import("../agents/agent-bundle-mcp-harness.js").materializeRequesterScopedMcpToolsForHarnessRunCore > > > { const shouldLoad = shouldLoadRequesterScopedMcpHarnessRuntime(params); if (!shouldLoad) { return undefined; } const { materializeRequesterScopedMcpToolsForHarnessRunCore: materialize } = await import("../agents/agent-bundle-mcp-harness.js"); return materialize(params); } export { resolveSandboxContext } from "../agents/sandbox.js"; export type { SandboxContext, SandboxWorkspaceAccess } from "../agents/sandbox.js"; export { splitSandboxBindSpec } from "../agents/sandbox/bind-spec.js"; export { hasSandboxBindContainerPathAliases, hasSandboxBindReadonlyHostShadows, resolveWritableSandboxBindHostRoots, } from "../agents/sandbox/fs-paths.js"; export { buildBootstrapContextForFiles, resolveBootstrapContextForRun, resolveBootstrapFilesForRun, } from "../agents/bootstrap-files.js"; export type { EmbeddedContextFile } from "../agents/embedded-agent-helpers/context-file.js"; export { isSubagentSessionKey } from "../routing/session-key.js"; export { acquireSessionWriteLock, resolveSessionWriteLockAcquireTimeoutMs, resolveSessionWriteLockOptions, type SessionWriteLockAcquireTimeoutConfig, } from "./session-write-lock-runtime.js"; export { consumeAdjustedParamsForToolCall, consumePreExecutionBlockedToolCall, finalizeToolTerminalPresentation, getBeforeToolCallFailureDisposition, getBeforeToolCallPolicyDiagnosticState, hasBeforeToolCallPolicy, isToolWrappedWithBeforeToolCallHook, requestDeferredPluginToolApproval, runBeforeToolCallHook, setBeforeToolCallDiagnosticsEnabled, wrapToolWithBeforeToolCallHook, type BeforeToolCallPolicyDiagnosticState, type BeforeToolCallFailureDisposition, type DeferredPluginToolApproval, } from "../agents/agent-tools.before-tool-call.js"; export { isReplaySafeToolCall } from "../agents/tool-mutation.js"; export { resolveAgentHarnessBeforePromptBuildResult, runAgentHarnessAfterCompactionHook, runAgentHarnessBeforeCompactionHook, } from "../agents/harness/prompt-compaction-hook-helpers.js"; export { createCodexAppServerToolResultExtensionRunner } from "../agents/harness/codex-app-server-extensions.js"; export { createAgentToolResultMiddlewareRunner } from "../agents/harness/tool-result-middleware.js"; export { assertContextEngineHostSupport, CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, } from "../context-engine/host-compat.js"; export { assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, finalizeHarnessContextEngineTurn, isActiveHarnessContextEngine, runHarnessContextEngineMaintenance, } from "../agents/harness/context-engine-lifecycle.js"; // Plugin-owned (`ownsCompaction`) compaction safety timeout. Exposed on the // agent-harness-runtime surface so plugin harnesses such as Codex bound their // own `ContextEngine.compact()` calls with the exact same finite, host-resolved // timeout the built-in embedded-agent runner uses — one shared implementation, no // copy-pasted watchdog. export { compactWithSafetyTimeout, compactContextEngineWithSafetyTimeout, resolveCompactionTimeoutMs, } from "../agents/embedded-agent-runner/compaction-safety-timeout.js"; export { estimateRenderedLlmBoundaryTokenPressure, formatPrePromptPrecheckLog, PREEMPTIVE_OVERFLOW_ERROR_TEXT, shouldPreemptivelyCompactBeforePrompt, type LlmBoundaryTokenPressure, type PreemptiveCompactionDecision, } from "../agents/embedded-agent-runner/run/preemptive-compaction.js"; export { resolveContextEngineOwnerPluginId } from "../context-engine/registry.js"; export { runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeMessageWriteHook, } from "../agents/harness/hook-helpers.js"; export { awaitAgentEndSideEffects, runAgentEndSideEffects, } from "../agents/harness/agent-end-side-effects.js"; export { buildEmbeddedForegroundPromptContext } from "../agents/embedded-agent-runner/run/agent-end-context.js"; export type { EmbeddedForegroundPromptContext } from "../agents/embedded-agent-runner/run/params.js"; export { awaitAgentHarnessAgentEndHook, getAgentHarnessHookRunner, runAgentHarnessBeforeAgentFinalizeHook, runAgentHarnessAgentEndHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, } from "../agents/harness/lifecycle-hook-helpers.js"; export { buildNativeHookRelayCommand, hasNativeHookRelayInvocation, invokeNativeHookRelay, resolveNativeHookRelayDeferredToolApproval, testing as nativeHookRelayTesting, registerNativeHookRelay, } from "../agents/harness/native-hook-relay.js"; /** * Derive the same compact user-facing tool detail that embedded OpenClaw uses for progress logs. */ export type ToolProgressDetailMode = "explain" | "raw"; /** Infer compact display metadata for one tool invocation from its name and arguments. */ export function inferToolMetaFromArgs( toolName: string, args: unknown, options?: { detailMode?: ToolProgressDetailMode }, ): string | undefined { return inferToolMetaFromArgsCore(toolName, args, options); } /** * Prepare verbose tool output for user-facing progress messages. */ export function formatToolProgressOutput( output: string, options?: { maxChars?: number }, ): string | undefined { const trimmed = output.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim(); if (!trimmed) { return undefined; } const redacted = redactToolDetail(trimmed); const maxChars = options?.maxChars ?? TOOL_PROGRESS_OUTPUT_MAX_CHARS; if (redacted.length <= maxChars) { return redacted; } return `${truncateUtf16Safe(redacted, maxChars)}\n...(truncated)...`; } /** Inputs used to classify a finished harness turn with little or no visible assistant output. */ export type AgentHarnessTerminalOutcomeInput = { assistantTexts: readonly string[]; reasoningText?: string | null; planText?: string | null; promptError?: unknown; turnCompleted: boolean; }; /** Terminal fallback classification emitted by agent harness adapters. */ export type AgentHarnessTerminalOutcomeClassification = NonNullable< EmbeddedRunAttemptResult["agentHarnessResultClassification"] >; /** * Classify terminal harness turns that completed without assistant output that * should advance fallback. Deliberate silent replies such as NO_REPLY count as * intentional output, while whitespace-only text remains fallback-eligible. * This is intentionally SDK-level so plugin harness adapters such as Codex * preserve the same OpenClaw-owned fallback signals as the built-in OpenClaw path * without re-implementing terminal-result policy. */ export function classifyAgentHarnessTerminalOutcome( params: AgentHarnessTerminalOutcomeInput, ): AgentHarnessTerminalOutcomeClassification | undefined { if ( !params.turnCompleted || (params.promptError !== undefined && params.promptError !== null) || hasVisibleAssistantText(params.assistantTexts) ) { return undefined; } if (params.planText?.trim()) { return "planning-only"; } if (params.reasoningText?.trim()) { return "reasoning-only"; } return "empty"; } function hasVisibleAssistantText(assistantTexts: readonly string[]): boolean { return assistantTexts.some((text) => text.trim().length > 0); } export const toolPolicy = Object.freeze({ createToolPolicyMatcher, expandToolGroups });