docs: document auto reply routing helpers

This commit is contained in:
Peter Steinberger
2026-06-04 02:11:29 -04:00
parent 20d7c7ae02
commit a70e618b20
16 changed files with 41 additions and 0 deletions
@@ -1,3 +1,4 @@
/** Formats and appends token/cost usage lines to reply payloads. */
import {
estimateUsageCost,
formatTokenCount,
+3
View File
@@ -1,3 +1,4 @@
/** Resolves channel and account context for command handlers. */
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
@@ -30,6 +31,7 @@ type ChannelAccountParams = {
};
};
/** Resolves the command surface channel from inbound context and command state. */
export function resolveCommandSurfaceChannel(params: CommandSurfaceParams): string {
const channel =
params.ctx.OriginatingChannel ??
@@ -39,6 +41,7 @@ export function resolveCommandSurfaceChannel(params: CommandSurfaceParams): stri
return normalizeOptionalLowercaseString(channel) ?? "";
}
/** Resolves command account id, falling back to plugin default account config. */
export function resolveChannelAccountId(params: ChannelAccountParams): string {
const accountId = normalizeOptionalString(params.ctx.AccountId) ?? "";
if (accountId) {
@@ -1,3 +1,4 @@
/** Handles diagnostics commands and private owner routing for sensitive diagnostics output. */
import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { resolveSessionAgentId } from "../../agents/agent-scope.js";
import { createExecTool } from "../../agents/bash-tools.js";
@@ -58,6 +59,7 @@ const defaultDiagnosticsCommandDeps: DiagnosticsCommandDeps = {
deliverPrivateDiagnosticsReply,
};
/** Creates a diagnostics command handler with injectable private-route dependencies. */
export function createDiagnosticsCommandHandler(
deps: Partial<DiagnosticsCommandDeps> = {},
): CommandHandler {
@@ -69,6 +71,7 @@ export function createDiagnosticsCommandHandler(
await handleDiagnosticsCommandWithDeps(resolvedDeps, params, allowTextCommands);
}
/** Default diagnostics command handler. */
export const handleDiagnosticsCommand: CommandHandler = createDiagnosticsCommandHandler();
async function handleDiagnosticsCommandWithDeps(
@@ -1,3 +1,4 @@
/** Private command reply routing for sensitive owner-only command output. */
import { resolveExpiresAtMsFromDurationMs } from "@openclaw/normalization-core/number-coercion";
import {
normalizeLowercaseStringOrEmpty,
@@ -14,6 +15,7 @@ import type { ReplyPayload } from "../types.js";
import type { HandleCommandsParams } from "./commands-types.js";
import { routeReply } from "./route-reply.js";
/** Resolved private delivery target for command replies and approvals. */
export type PrivateCommandRouteTarget = {
channel: string;
to: string;
@@ -24,6 +26,7 @@ export type PrivateCommandRouteTarget = {
const PRIVATE_COMMAND_APPROVAL_ROUTE_TTL_MS = 5 * 60_000;
const EXPIRED_PRIVATE_COMMAND_APPROVAL_ROUTE_EXPIRES_AT_MS = 0;
/** Resolves expiry timestamp for temporary private approval routes. */
export function resolvePrivateCommandApprovalRouteExpiresAtMs(nowMs = Date.now()): number {
return (
resolveExpiresAtMsFromDurationMs(PRIVATE_COMMAND_APPROVAL_ROUTE_TTL_MS, { nowMs }) ??
@@ -31,6 +34,7 @@ export function resolvePrivateCommandApprovalRouteExpiresAtMs(nowMs = Date.now()
);
}
/** Finds private owner DM routes that can receive sensitive command replies. */
export async function resolvePrivateCommandRouteTargets(params: {
commandParams: HandleCommandsParams;
request: ExecApprovalRequest;
@@ -80,6 +84,7 @@ export async function resolvePrivateCommandRouteTargets(params: {
});
}
/** Delivers a sensitive command reply to the resolved private targets. */
export async function deliverPrivateCommandReply(params: {
commandParams: HandleCommandsParams;
targets: PrivateCommandRouteTarget[];
@@ -105,6 +110,7 @@ export async function deliverPrivateCommandReply(params: {
return results.some((result) => result.status === "fulfilled" && result.value.ok);
}
/** Reads the command message thread id from command context. */
export function readCommandMessageThreadId(params: HandleCommandsParams): string | undefined {
return typeof params.ctx.MessageThreadId === "string" ||
typeof params.ctx.MessageThreadId === "number"
@@ -112,6 +118,7 @@ export function readCommandMessageThreadId(params: HandleCommandsParams): string
: undefined;
}
/** Reads the best delivery target for command route resolution. */
export function readCommandDeliveryTarget(params: HandleCommandsParams): string | undefined {
return (
normalizeOptionalString(params.ctx.OriginatingTo) ??
+2
View File
@@ -1,3 +1,4 @@
/** Builds /status replies using the command's authorized channel context. */
import { logVerbose } from "../../globals.js";
import { buildStatusText } from "../../status/status-text.js";
import type { BuildStatusTextParams } from "../../status/status-text.types.js";
@@ -9,6 +10,7 @@ type BuildStatusReplyParams = Omit<BuildStatusTextParams, "statusChannel"> & {
command: CommandContext;
};
/** Builds a status reply or suppresses unauthorized status requests. */
export async function buildStatusReply(
params: BuildStatusReplyParams,
): Promise<ReplyPayload | undefined> {
@@ -1,3 +1,4 @@
/** Text extraction helpers for subagent command output. */
import { sanitizeTextContent } from "../../agents/tools/chat-history-text.js";
import { extractTextFromChatContent } from "../../shared/chat-content.js";
@@ -1,3 +1,4 @@
/** Resolves /model directive selections and auth profile overrides. */
import { ensureAuthProfileStore } from "../../agents/auth-profiles.js";
import { isModelKeyAllowedBySet } from "../../agents/model-selection-shared.js";
import {
@@ -44,6 +45,7 @@ function resolveStoredNumericProfileModelDirective(params: { raw: string; agentD
return { modelRaw, profileId, profileProvider: profile.provider };
}
/** Resolves the requested model/profile override from parsed inline directives. */
export function resolveModelSelectionFromDirective(params: {
directives: InlineDirectives;
cfg: OpenClawConfig;
@@ -1,3 +1,4 @@
/** Validation and status handling for /queue directives. */
import type { SessionEntry } from "../../config/sessions.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import type { ReplyPayload } from "../types.js";
@@ -1,19 +1,23 @@
/** Resolves the effective reply route from current context and persisted session route. */
import type { SessionEntry } from "../../config/sessions/types.js";
import { stringifyRouteThreadId } from "../../plugin-sdk/channel-route.js";
import type { InputProvenance } from "../../sessions/input-provenance.js";
import { INTERNAL_MESSAGE_CHANNEL, normalizeMessageChannel } from "../../utils/message-channel.js";
import type { FinalizedMsgContext } from "../templating.js";
/** Current finalized context fields used for reply route resolution. */
export type EffectiveReplyRouteContext = Pick<
FinalizedMsgContext,
"Provider" | "Surface" | "OriginatingChannel" | "OriginatingTo" | "AccountId" | "InputProvenance"
>;
/** Persisted session fields used as route fallback/inheritance. */
export type EffectiveReplyRouteEntry = Pick<
SessionEntry,
"deliveryContext" | "lastChannel" | "lastTo" | "lastAccountId" | "route"
>;
/** Effective channel target selected for source reply delivery. */
export type EffectiveReplyRoute = {
channel?: string;
to?: string;
@@ -22,6 +26,7 @@ export type EffectiveReplyRoute = {
inheritedExternalRoute?: boolean;
};
/** Returns true for synthetic providers that should not define a user channel route. */
export function isSystemEventProvider(provider?: string): boolean {
return provider === "heartbeat" || provider === "cron-event" || provider === "exec-event";
}
@@ -53,6 +58,7 @@ function resolveTrustedInheritedThreadId(
return undefined;
}
/** Resolves current, inherited, or persisted reply route for a session turn. */
export function resolveEffectiveReplyRoute(params: {
ctx: EffectiveReplyRouteContext;
entry?: EffectiveReplyRouteEntry;
@@ -1,3 +1,4 @@
/** Prepares queued follow-up payloads for source-channel delivery. */
import type { MessagingToolSend } from "../../agents/embedded-agent-messaging.types.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { stripHeartbeatToken } from "../heartbeat.js";
@@ -23,6 +24,7 @@ function hasReplyPayloadMedia(payload: ReplyPayload): boolean {
return Array.isArray(payload.mediaUrls) && payload.mediaUrls.some((url) => url.trim().length > 0);
}
/** Strips heartbeat tokens, applies threading, and dedupes message-tool sends. */
export function resolveFollowupDeliveryPayloads(params: {
cfg: OpenClawConfig;
payloads: ReplyPayload[];
@@ -1,3 +1,4 @@
/** Handles inline slash commands, skill invocations, and abort actions before model runs. */
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
@@ -120,6 +121,7 @@ function isMentionOnlyResidualText(text: string, wasMentioned: boolean | undefin
return /^(?:<@[!&]?[A-Za-z0-9._:-]+>|<!(?:here|channel|everyone)>|[:,.!?-]|\s)+$/u.test(trimmed);
}
/** Result of attempting to handle an inbound message as an inline action. */
export type InlineActionResult =
| { kind: "reply"; reply: ReplyPayload | ReplyPayload[] | undefined }
| {
@@ -177,6 +179,7 @@ function extractBlockedToolReason(result: unknown): string | null {
return typeof reason === "string" && reason.trim() ? reason.trim() : null;
}
/** Handles inline actions or returns continue when the message should become a model turn. */
export async function handleInlineActions(params: {
ctx: MsgContext;
sessionCtx: TemplateContext;
@@ -1,3 +1,4 @@
/** Lightweight reply-stage profiler for slow-turn diagnostics. */
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { isDiagnosticFlagEnabled } from "../../infra/diagnostic-flags.js";
@@ -1,3 +1,4 @@
/** Resolves runtime policy session keys distinct from transcript session keys. */
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
@@ -84,6 +85,7 @@ function isMainSessionAlias(params: {
}
/** Resolves the session key used for runtime policy checks and direct-message scoping. */
/** Resolves the session key used for sandbox/tool/runtime policy lookups. */
export function resolveRuntimePolicySessionKey(params: {
cfg?: OpenClawConfig;
ctx?: RuntimePolicyContext;
+4
View File
@@ -1,3 +1,4 @@
/** Public session-fork facade with parent-size admission checks. */
import type { SessionEntry } from "../../config/sessions/types.js";
import { createLazyImportLoader } from "../../shared/lazy-promise.js";
@@ -9,6 +10,7 @@ import { createLazyImportLoader } from "../../shared/lazy-promise.js";
const DEFAULT_PARENT_FORK_MAX_TOKENS = 100_000;
const sessionForkRuntimeLoader = createLazyImportLoader(() => import("./session-fork.runtime.js"));
/** Decision for whether a child session should fork parent context or start isolated. */
export type ParentForkDecision =
| {
status: "fork";
@@ -37,6 +39,7 @@ function formatParentForkTooLargeMessage(params: {
);
}
/** Decides whether parent context is small enough to fork into a child session. */
export async function resolveParentForkDecision(params: {
parentEntry: SessionEntry;
storePath: string;
@@ -62,6 +65,7 @@ export async function resolveParentForkDecision(params: {
};
}
/** Forks a new session transcript from a parent session. */
export async function forkSessionFromParent(params: {
parentEntry: SessionEntry;
agentId: string;
@@ -1,3 +1,4 @@
/** Clears reset-related queues and system events for session keys. */
import { drainSystemEventEntries } from "../../infra/system-events.js";
import { clearSessionQueues, type ClearSessionQueueResult } from "./queue/cleanup.js";
+2
View File
@@ -1,3 +1,4 @@
/** Persists usage, cost, model, and CLI session metadata after reply runs. */
import {
clearCliSession,
setCliSessionBinding,
@@ -92,6 +93,7 @@ function estimateSessionRunCostUsd(params: {
return resolveNonNegativeNumber(estimateUsageCost({ usage: params.usage, cost }));
}
/** Persists usage accounting and selected runtime metadata to the session store. */
export async function persistSessionUsageUpdate(params: {
storePath?: string;
sessionKey?: string;