Files
openclaw/src/agents/embedded-agent-runner/run/params.ts
Peter Steinberger 32c84b0f41 feat(skills): capture reusable techniques from successful work (#105674)
* feat(skills): capture reusable experience safely

* feat(skills): review completed work for reusable learning

* docs(skills): explain self-learning

* docs: clarify self-learning runtime scope

* fix(skills): harden autonomous workshop reviews

* test(skills): align review prompt fixture
2026-07-13 00:22:06 -07:00

340 lines
16 KiB
TypeScript

/**
* Shared parameter types for embedded-agent run orchestration.
*/
import type { FastMode } from "@openclaw/normalization-core/string-coerce";
import type {
BlockReplyContext,
PartialReplyPayload,
SourceReplyDeliveryMode,
TaskSuggestionDeliveryMode,
} from "../../../auto-reply/get-reply-options.types.js";
import type { ReplyPayload } from "../../../auto-reply/reply-payload.js";
import type { ReplyOperation } from "../../../auto-reply/reply/reply-run-registry.js";
import type { ReasoningLevel, ThinkLevel, VerboseLevel } from "../../../auto-reply/thinking.js";
import type { ChatType } from "../../../channels/chat-type.js";
import type { InboundEventKind } from "../../../channels/inbound-event/kind.js";
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
import type { ImageContent } from "../../../llm/types.js";
import type { PromptImageOrderEntry } from "../../../media/prompt-image-order.js";
import type { PluginHookChannelContext } from "../../../plugins/hook-types.js";
import type { CommandQueueEnqueueFn } from "../../../process/command-queue.types.js";
import type { InputProvenance } from "../../../sessions/input-provenance.js";
import type { UserTurnTranscriptRecorder } from "../../../sessions/user-turn-transcript.types.js";
import type { SkillSnapshot } from "../../../skills/types.js";
import type {
SkillProposalOrigin,
SkillWorkshopProposalMutationBudget,
} from "../../../skills/workshop/types.js";
import type { ExecElevatedDefaults, ExecToolDefaults } from "../../bash-tools.exec-types.js";
import type { BootstrapContextRunKind } from "../../bootstrap-mode.js";
import type { AgentStreamParams, ClientToolDefinition } from "../../command/shared-types.js";
import type { BlockReplyPayload } from "../../embedded-agent-payloads.js";
import type {
BlockReplyChunking,
ToolProgressDetailMode,
ToolResultFormat,
} from "../../embedded-agent-subscribe.shared-types.js";
import type { FastModeAutoProgressState } from "../../fast-mode.js";
import type { ExpectedAgentHarnessRuntimeArtifact } from "../../harness/runtime-artifact.types.js";
import type { AgentInternalEvent } from "../../internal-events.js";
import type { AgentRunSessionTarget } from "../../run-session-target.js";
import type { AgentMessage } from "../../runtime/index.js";
import type { SilentReplyPromptMode } from "../../system-prompt.types.js";
import type { PromptMode } from "../../system-prompt.types.js";
import type { EmbeddedAgentExecutionPhase } from "../execution-phase.js";
import type { BlockReplyFlushContext } from "../types.js";
import type { AuthProfileFailurePolicy } from "./auth-profile-failure-policy.types.js";
export type { ClientToolDefinition } from "../../command/shared-types.js";
export type EmbeddedRunTrigger = "cron" | "heartbeat" | "manual" | "memory" | "overflow" | "user";
type ReasoningStreamPayload = Pick<
ReplyPayload,
"text" | "mediaUrls" | "isReasoning" | "isReasoningSnapshot"
> & {
requiresReasoningProgressOptIn?: boolean;
};
export type CurrentInboundPromptContext = {
text: string;
resumableText?: string;
promptJoiner?: "\n\n" | "\n" | " ";
/** Generated goal blocks owned by inbound-context assembly, never user text. */
injectedGoalContexts?: string[];
};
export type RunEmbeddedAgentParams = {
sessionId: string;
sessionKey?: string;
/** Storage-neutral transcript/session target. Defaults to sessionId/sessionKey/agentId. */
sessionTarget?: AgentRunSessionTarget;
/** Immutable gateway lifecycle ownership captured when this execution was admitted. */
lifecycleGeneration?: string;
/** Provider prompt-cache affinity key; distinct from transcript/session identity. */
promptCacheKey?: string;
/** Session-like key for sandbox and tool-policy resolution. Defaults to sessionKey. */
sandboxSessionKey?: string;
agentId?: string;
messageChannel?: string;
messageProvider?: string;
/** Capabilities declared by the gateway client that originated this run. */
clientCaps?: string[];
chatType?: ChatType;
agentAccountId?: string;
/** What initiated this agent run: "user", "heartbeat", "cron", "memory", "overflow", or "manual". */
trigger?: EmbeddedRunTrigger;
/** Stable cron job identifier populated for cron-triggered runs. */
jobId?: string;
/** Relative workspace path that memory-triggered writes are allowed to append to. */
memoryFlushWritePath?: string;
/** Delivery target for topic/thread routing. */
messageTo?: string;
/** Thread/topic identifier for routing replies to the originating thread. */
messageThreadId?: string | number;
/** Group id for channel-level tool policy resolution. */
groupId?: string | null;
/** Group channel label (e.g. #general) for channel-level tool policy resolution. */
groupChannel?: string | null;
/** Group space label (e.g. guild/team id) for channel-level tool policy resolution. */
groupSpace?: string | null;
/** Trusted provider role ids for the requester in this group turn. */
memberRoleIds?: string[];
/** Opaque host-issued capability for current-turn channel message actions. */
messageActionTurnCapability?: string;
/** Parent session key for subagent policy inheritance. */
spawnedBy?: string | null;
/** Whether workspaceDir points at the canonical agent workspace for bootstrap purposes. */
isCanonicalWorkspace?: boolean;
senderId?: string | null;
senderName?: string | null;
senderUsername?: string | null;
senderE164?: string | null;
/** Trusted sender identity bit for command/channel-action auth. */
senderIsOwner?: boolean;
/** Device-scoped operator session allowed to review approvals initiated by this run. */
approvalReviewerDeviceId?: string;
/** Current channel ID for auto-threading (Slack). */
currentChannelId?: string;
/** Transport-native chat/conversation ID for hook identity context. */
chatId?: string;
/** Channel-specific identity metadata surfaced to plugin hooks. */
channelContext?: PluginHookChannelContext;
/** Routable target for the current conversation when it differs from the native channel ID. */
currentMessagingTarget?: string;
/** Current thread timestamp for auto-threading (Slack). */
currentThreadTs?: string;
/** Current inbound message id for action fallbacks (e.g. Telegram react). */
currentMessageId?: string | number;
/** True when the current inbound turn carried audio media. */
currentInboundAudio?: boolean;
/** Reply-to mode for Slack auto-threading. */
replyToMode?: "off" | "first" | "all" | "batched";
/** Mutable ref to track if a reply was sent (for "first" mode). */
hasRepliedRef?: { value: boolean };
/** Require explicit message tool targets (no implicit last-route sends). */
requireExplicitMessageTarget?: boolean;
/** If true, omit the message tool from the tool list. */
disableMessageTool?: boolean;
/** Restrict this reconstructed run to restart-safe tools. */
forceRestartSafeTools?: boolean;
/** Internal one-shot model probe mode: no tools, no workspace/chat prompt policy. */
modelRun?: boolean;
/** Disable trajectory persistence for auxiliary runs with no durable session owner. */
disableTrajectory?: boolean;
/** Restrict Skill Workshop to one pending proposal mutation for an internal review run. */
skillWorkshopProposalOnly?: boolean;
/** Preserve the foreground run as proposal provenance for an internal review run. */
skillWorkshopOrigin?: SkillProposalOrigin;
/** Run-scoped mutation budget shared across internal runner attempts. */
skillWorkshopProposalMutationBudget?: SkillWorkshopProposalMutationBudget;
/** Explicit system prompt mode override for trusted callers. */
promptMode?: PromptMode;
/** Keep the message tool available even when a narrow profile would omit it. */
forceMessageTool?: boolean;
/** Include the heartbeat response tool for structured heartbeat outcomes. */
enableHeartbeatTool?: boolean;
/** Keep the heartbeat response tool available even when a narrow profile would omit it. */
forceHeartbeatTool?: boolean;
/** Allow runtime plugins for this run to late-bind the gateway subagent. */
allowGatewaySubagentBinding?: boolean;
/** @deprecated Use sessionTarget plus sessionId/sessionKey/agentId for runtime identity. */
sessionFile?: string;
workspaceDir: string;
/** Task working directory for tool/runtime execution. Defaults to workspaceDir. */
cwd?: string;
agentDir?: string;
/**
* Run config consumed by core paths (model selection, tools, plugin
* activation). Plugin harnesses resolve `plugins.entries.<id>.config` from
* the live global config, NOT from this object — per-run plugin-config
* overrides are unsupported; use an explicit run param instead.
*/
config?: OpenClawConfig;
skillsSnapshot?: SkillSnapshot;
prompt: string;
/** User-visible prompt body to submit and persist; runtime context travels separately. */
transcriptPrompt?: string;
currentInboundEventKind?: InboundEventKind;
currentInboundContext?: CurrentInboundPromptContext;
images?: ImageContent[];
imageOrder?: PromptImageOrderEntry[];
/** Optional client-provided tools (OpenResponses hosted tools). */
clientTools?: ClientToolDefinition[];
/** Disable built-in tools for this run (LLM-only mode). */
disableTools?: boolean;
provider?: string;
model?: string;
/** Effective model fallback chain for this session attempt. Undefined uses config defaults. */
modelFallbacksOverride?: string[];
/** Session-pinned embedded harness id. Prevents runtime hot-switching. */
agentHarnessId?: string;
/** True when the pinned non-default harness owns model selection for this session. */
modelSelectionLocked?: boolean;
/** Explicit runtime override selected for this turn. Unlike agentHarnessId, this may force OpenClaw. */
agentHarnessRuntimeOverride?: string;
/** Verified setup continuation: pin both the harness and its local implementation. */
expectedAgentHarnessRuntimeArtifact?: ExpectedAgentHarnessRuntimeArtifact;
authProfileId?: string;
authProfileIdSource?: "auto" | "user";
thinkLevel?: ThinkLevel;
fastMode?: FastMode;
/** Stable outer-run start time for auto fast-mode cutoff across retries/fallbacks. */
fastModeStartedAtMs?: number;
/** Effective auto fast-mode cutoff for this run, in seconds. */
fastModeAutoOnSeconds?: number;
/** Shared notification state for nested harnesses that can observe the same tool boundary. */
fastModeAutoProgressState?: FastModeAutoProgressState;
/** True when the outer model fallback loop has reached its final candidate. */
isFinalFallbackAttempt?: boolean;
verboseLevel?: VerboseLevel;
reasoningLevel?: ReasoningLevel;
toolResultFormat?: ToolResultFormat;
toolProgressDetail?: ToolProgressDetailMode;
/** If true, suppress tool error warning payloads for this run (including mutating tools). */
suppressToolErrorWarnings?: boolean | (() => boolean | undefined);
/** Bootstrap context mode for workspace file injection. */
bootstrapContextMode?: "full" | "lightweight";
/** Run kind hint for context mode behavior. */
bootstrapContextRunKind?: BootstrapContextRunKind;
/** Optional tool allow-list; when set, only these tools are sent to the model. */
toolsAllow?: string[];
/** Seen bootstrap truncation warning signatures for this session (once mode dedupe). */
bootstrapPromptWarningSignaturesSeen?: string[];
/** Last shown bootstrap truncation warning signature for this session. */
bootstrapPromptWarningSignature?: string;
execOverrides?: Pick<
ExecToolDefaults,
"host" | "security" | "ask" | "node" | "nodeCwd" | "notifyOnExit" | "notifyOnExitEmptySuccess"
>;
bashElevated?: ExecElevatedDefaults;
timeoutMs: number;
/**
* Explicit per-run timeout override, in milliseconds, when the caller knows
* the run was launched with a deliberate per-run value (e.g. a cron payload's
* `timeoutSeconds`) rather than inheriting `agents.defaults.timeoutSeconds`.
* When set, the LLM idle watchdog honors this value directly instead of
* inferring "explicitness" from `timeoutMs !== agents.defaults.timeoutSeconds`,
* which fails when the explicit value happens to numerically equal the agent
* default.
*/
runTimeoutOverrideMs?: number;
runId: string;
abortSignal?: AbortSignal;
onExecutionStarted?: (info?: { lifecycleGeneration?: string }) => void;
onExecutionPhase?: (info: {
phase: EmbeddedAgentExecutionPhase;
provider?: string;
model?: string;
backend?: string;
source?: string;
tool?: string;
toolCallId?: string;
itemId?: string;
firstModelCallStarted?: boolean;
}) => void;
onLaneWait?: (info: { waitMs: number; queuedAhead: number; waiting?: boolean }) => void;
onRunProgress?: (info: {
reason: string;
provider?: string;
model?: string;
backend?: string;
}) => void;
onSessionIdChanged?: (sessionId: string) => void;
replyOperation?: ReplyOperation;
shouldEmitToolResult?: () => boolean;
shouldEmitToolOutput?: () => boolean;
onPartialReply?: (payload: PartialReplyPayload) => void | Promise<void>;
onAssistantMessageStart?: () => void | Promise<void>;
onBlockReply?: (payload: BlockReplyPayload, context?: BlockReplyContext) => void | Promise<void>;
onBlockReplyFlush?: (context: BlockReplyFlushContext) => void | Promise<void>;
blockReplyBreak?: "text_end" | "message_end";
blockReplyChunking?: BlockReplyChunking;
onReasoningStream?: (payload: ReasoningStreamPayload) => void | Promise<void>;
streamReasoningInNonStreamModes?: boolean;
onReasoningEnd?: () => void | Promise<void>;
onToolResult?: (payload: ReplyPayload) => void | Promise<void>;
/** Synchronous private observer for the sanitized per-tool result. */
onAgentToolResult?: (event: { toolName: string; result: unknown; isError: boolean }) => void;
onAgentEvent?: (evt: {
stream: string;
data: Record<string, unknown>;
sessionKey?: string;
}) => void | Promise<void>;
onToolStreamBoundary?: () => void | Promise<void>;
/**
* Emit lifecycle "finishing" when the attempt ends; the caller owns the
* final lifecycle "end" or "error" after fallback and post-turn work settle.
*/
deferTerminalLifecycle?: boolean;
/** @deprecated Use deferTerminalLifecycle. */
deferTerminalLifecycleEnd?: boolean;
lane?: string;
enqueue?: CommandQueueEnqueueFn;
extraSystemPrompt?: string;
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
taskSuggestionDeliveryMode?: TaskSuggestionDeliveryMode;
silentReplyPromptMode?: SilentReplyPromptMode;
internalEvents?: AgentInternalEvent[];
inputProvenance?: InputProvenance;
streamParams?: AgentStreamParams;
ownerNumbers?: string[];
enforceFinalTag?: boolean;
silentExpected?: boolean;
/** Skip per-chunk live visible-text parsing when no live stream consumer exists (e.g. subagents). */
suppressLiveStreamOutput?: boolean;
/**
* Treat a clean empty assistant stop as an intentional silent reply.
* Only set when the caller's prompt policy already allows an exact NO_REPLY
* final answer for silence.
*/
allowEmptyAssistantReplyAsSilent?: boolean;
authProfileFailurePolicy?: AuthProfileFailurePolicy;
/**
* Allow a single run attempt even when all auth profiles are in cooldown,
* but only for inferred transient cooldowns like `rate_limit` or `overloaded`.
*
* This is used by model fallback when trying sibling models on providers
* where transient service pressure is often model-scoped.
*/
allowTransientCooldownProbe?: boolean;
suppressNextUserMessagePersistence?: boolean;
suppressTranscriptOnlyAssistantPersistence?: boolean;
suppressAssistantErrorPersistence?: boolean;
userTurnTranscriptRecorder?: UserTurnTranscriptRecorder;
/** Keep an internal continuation prompt from being replaced by the original prepared turn. */
skipPreparedUserTurnMessage?: boolean;
onUserMessagePersisted?: (message: Extract<AgentMessage, { role: "user" }>) => void;
onUserMessagePersistenceInvalidated?: () => void;
onAssistantErrorMessagePersisted?: (
message: Extract<AgentMessage, { role: "assistant" }>,
) => void;
/**
* Dispose bundled MCP runtimes when the overall run ends instead of preserving
* the session-scoped cache. Intended for one-shot local CLI runs that must
* exit promptly after emitting the final JSON result.
*/
cleanupBundleMcpOnRunEnd?: boolean;
/** Mark explicit one-shot local CLI runs so plugin tools can release resources promptly. */
oneShotCliRun?: boolean;
};