mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
docs: document embedded runner state helpers
This commit is contained in:
@@ -9,6 +9,12 @@ import {
|
||||
import { resolveProviderCacheTtlEligibility } from "../../plugins/provider-runtime.js";
|
||||
import { isGooglePromptCacheEligible } from "./prompt-cache-retention.js";
|
||||
|
||||
/**
|
||||
* Cache-TTL eligibility and session markers for provider prompt-cache retention.
|
||||
*
|
||||
* Providers can override eligibility through plugin runtime hooks; built-in fallback logic covers
|
||||
* Anthropic-family and Google prompt-cache semantics.
|
||||
*/
|
||||
type CustomEntryLike = { type?: unknown; customType?: unknown; data?: unknown };
|
||||
|
||||
const CACHE_TTL_CUSTOM_TYPE = "openclaw.cache-ttl";
|
||||
@@ -24,6 +30,7 @@ type CacheTtlContext = {
|
||||
modelId?: string;
|
||||
};
|
||||
|
||||
/** Returns whether this provider/model pair supports cache-TTL session markers. */
|
||||
export function isCacheTtlEligibleProvider(
|
||||
provider: string,
|
||||
modelId: string,
|
||||
@@ -75,6 +82,7 @@ function matchesCacheTtlContext(
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Reads the most recent cache-TTL marker that matches the optional provider/model context. */
|
||||
export function readLastCacheTtlTimestamp(
|
||||
sessionManager: unknown,
|
||||
context?: CacheTtlContext,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { hasAcceptedSessionSpawn } from "../accepted-session-spawn.js";
|
||||
|
||||
/**
|
||||
* Helpers for deciding whether an embedded run produced user-visible or outbound effects.
|
||||
*
|
||||
* Fallback and retry code uses these checks to avoid rerunning a model after messages, media,
|
||||
* cron entries, or spawned sessions have already been delivered.
|
||||
*/
|
||||
type AgentPayloadLike = {
|
||||
text?: unknown;
|
||||
mediaUrl?: unknown;
|
||||
@@ -73,6 +79,7 @@ function collectMediaUrlsFromRecord(record: Record<string, unknown>, output: Set
|
||||
}
|
||||
}
|
||||
|
||||
/** Collects media URLs from agent payloads and committed messaging-tool delivery metadata. */
|
||||
export function collectDeliveredMediaUrls(result: AgentDeliveryEvidence): string[] {
|
||||
const urls = new Set<string>();
|
||||
if (Array.isArray(result.payloads)) {
|
||||
@@ -88,6 +95,7 @@ export function collectDeliveredMediaUrls(result: AgentDeliveryEvidence): string
|
||||
return Array.from(urls);
|
||||
}
|
||||
|
||||
/** Collects media URLs recorded by messaging-tool sends and their target attachments. */
|
||||
export function collectMessagingToolDeliveredMediaUrls(
|
||||
result: Pick<AgentDeliveryEvidence, "messagingToolSentMediaUrls" | "messagingToolSentTargets">,
|
||||
): string[] {
|
||||
@@ -107,6 +115,7 @@ function hasPositiveNumber(value: unknown): boolean {
|
||||
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
||||
}
|
||||
|
||||
/** Extracts a gateway result payload when the response carries delivery evidence fields. */
|
||||
export function getGatewayAgentResult(response: unknown): AgentDeliveryEvidence | null {
|
||||
if (!response || typeof response !== "object") {
|
||||
return null;
|
||||
@@ -134,6 +143,7 @@ function hasAgentDeliveryEvidenceShape(value: object): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/** Returns whether payload metadata contains visible text, media, presentation, or channel data. */
|
||||
export function hasVisibleAgentPayload(
|
||||
result: Pick<AgentDeliveryEvidence, "payloads">,
|
||||
options: { includeErrorPayloads?: boolean; includeReasoningPayloads?: boolean } = {},
|
||||
@@ -164,12 +174,14 @@ export function hasVisibleAgentPayload(
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns whether the messaging tool attempted or committed an outbound delivery. */
|
||||
export function hasMessagingToolDeliveryEvidence(result: AgentDeliveryEvidence): boolean {
|
||||
return (
|
||||
result.didSendViaMessagingTool === true || hasCommittedMessagingToolDeliveryEvidence(result)
|
||||
);
|
||||
}
|
||||
|
||||
/** Returns whether messaging-tool metadata proves committed text, media, or target delivery. */
|
||||
export function hasCommittedMessagingToolDeliveryEvidence(
|
||||
result: Pick<
|
||||
AgentDeliveryEvidence,
|
||||
@@ -183,6 +195,7 @@ export function hasCommittedMessagingToolDeliveryEvidence(
|
||||
);
|
||||
}
|
||||
|
||||
/** Returns whether any outbound side effect makes a retry unsafe. */
|
||||
export function hasOutboundDeliveryEvidence(result: AgentDeliveryEvidence): boolean {
|
||||
return (
|
||||
hasMessagingToolDeliveryEvidence(result) ||
|
||||
@@ -193,6 +206,7 @@ export function hasOutboundDeliveryEvidence(result: AgentDeliveryEvidence): bool
|
||||
);
|
||||
}
|
||||
|
||||
/** Formats an agent-command delivery failure message from delivery status metadata. */
|
||||
export function getAgentCommandDeliveryFailure(result: AgentDeliveryEvidence): string | undefined {
|
||||
const status = result.deliveryStatus?.status;
|
||||
if (status !== "failed" && status !== "partial_failed") {
|
||||
|
||||
@@ -2,6 +2,12 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coe
|
||||
import { isExecLikeToolName, type ToolErrorSummary } from "../tool-error-summary.js";
|
||||
import type { EmbeddedRunFailureSignal } from "./types.js";
|
||||
|
||||
/**
|
||||
* Converts terminal tool errors from unattended embedded runs into failure signals.
|
||||
*
|
||||
* Cron runs need fatal execution-denied signals so schedulers do not treat blocked shell access as
|
||||
* a normal silent completion.
|
||||
*/
|
||||
const FAILURE_SIGNAL_CODES = ["SYSTEM_RUN_DENIED", "INVALID_REQUEST"] as const;
|
||||
|
||||
function resolveFailureSignalCode(
|
||||
@@ -15,6 +21,7 @@ function resolveFailureSignalCode(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Resolves fatal cron failure metadata from the last exec-like tool error, if applicable. */
|
||||
export function resolveEmbeddedRunFailureSignal(params: {
|
||||
trigger?: string | undefined;
|
||||
lastToolError?: ToolErrorSummary | undefined;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { Model } from "../../llm/types.js";
|
||||
|
||||
/**
|
||||
* Reads optional context-token metadata from discovered models without widening the core model type.
|
||||
*/
|
||||
type AgentModelWithOptionalContextTokens = Model & {
|
||||
contextTokens?: number;
|
||||
};
|
||||
|
||||
/** Returns finite context-token metadata when a model discovery source provided it. */
|
||||
export function readAgentModelContextTokens(model: Model | null | undefined): number | undefined {
|
||||
const value = (model as AgentModelWithOptionalContextTokens | null | undefined)?.contextTokens;
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
||||
|
||||
@@ -6,6 +6,12 @@ import {
|
||||
} from "../../auto-reply/reply/reply-run-registry.js";
|
||||
import { resolveGlobalSingleton } from "../../shared/global-singleton.js";
|
||||
|
||||
/**
|
||||
* Shared process state for embedded-agent runs, queues, snapshots, and model-switch requests.
|
||||
*
|
||||
* The maps are global-singleton backed so reloads and lazy imports inside the same gateway process
|
||||
* do not split active-run bookkeeping.
|
||||
*/
|
||||
export type EmbeddedAgentQueueHandle = {
|
||||
kind?: "embedded";
|
||||
queueMessage: (text: string, options?: EmbeddedAgentQueueMessageOptions) => Promise<void>;
|
||||
@@ -93,6 +99,7 @@ export const EMBEDDED_RUN_MODEL_SWITCH_REQUESTS =
|
||||
embeddedRunState.modelSwitchRequests ??
|
||||
(embeddedRunState.modelSwitchRequests = new Map<string, EmbeddedRunModelSwitchRequest>());
|
||||
|
||||
/** Counts active embedded runs while including auto-reply registry runs for shared sessions. */
|
||||
export function getActiveEmbeddedRunCount(): number {
|
||||
let activeCount = ACTIVE_EMBEDDED_RUNS.size;
|
||||
for (const sessionId of listActiveReplyRunSessionIds()) {
|
||||
@@ -103,6 +110,7 @@ export function getActiveEmbeddedRunCount(): number {
|
||||
return Math.max(activeCount, getActiveReplyRunCount());
|
||||
}
|
||||
|
||||
/** Lists active embedded-run session keys from both embedded and auto-reply registries. */
|
||||
export function listActiveEmbeddedRunSessionKeys(): string[] {
|
||||
return [
|
||||
...new Set([
|
||||
@@ -112,6 +120,7 @@ export function listActiveEmbeddedRunSessionKeys(): string[] {
|
||||
].toSorted((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
/** Lists active embedded-run session ids from all embedded-run lookup maps. */
|
||||
export function listActiveEmbeddedRunSessionIds(): string[] {
|
||||
return [
|
||||
...new Set([
|
||||
|
||||
@@ -3,6 +3,12 @@ import { visitObjectContentBlocks } from "../../shared/message-content-blocks.js
|
||||
import type { StreamFn } from "../runtime/index.js";
|
||||
import type { MutableAssistantMessageEventStream } from "../stream-compat.js";
|
||||
|
||||
/**
|
||||
* Decodes HTML entities inside streamed tool-call arguments before downstream execution.
|
||||
*
|
||||
* Some providers HTML-escape JSON-ish argument strings in tool-call content blocks; this wrapper
|
||||
* repairs only arguments, preserving user-facing assistant text exactly as emitted.
|
||||
*/
|
||||
const HTML_ENTITY_RE = /&(?:amp|lt|gt|quot|apos|#39|#x[0-9a-f]+|#\d+);/i;
|
||||
|
||||
function decodeHtmlEntities(value: string): string {
|
||||
@@ -24,6 +30,7 @@ function decodeHtmlEntities(value: string): string {
|
||||
.replace(/&#(\d+);/gi, (_, dec: string) => decodeNumericEntity(dec, 10));
|
||||
}
|
||||
|
||||
/** Recursively decodes common HTML entities in string leaves of an object graph. */
|
||||
export function decodeHtmlEntitiesInObject(value: unknown): unknown {
|
||||
if (typeof value === "string") {
|
||||
return HTML_ENTITY_RE.test(value) ? decodeHtmlEntities(value) : value;
|
||||
@@ -65,6 +72,8 @@ function wrapStreamMessageObjects(
|
||||
};
|
||||
|
||||
const originalAsyncIterator = stream[Symbol.asyncIterator].bind(stream);
|
||||
// Patch both final result and streamed partial/message events. Tool execution can consume either
|
||||
// path depending on provider wrapper shape, so one-sided decoding would leave escaped args live.
|
||||
(stream as { [Symbol.asyncIterator]: typeof originalAsyncIterator })[Symbol.asyncIterator] =
|
||||
function () {
|
||||
const iterator = originalAsyncIterator();
|
||||
@@ -89,6 +98,7 @@ function wrapStreamMessageObjects(
|
||||
return stream;
|
||||
}
|
||||
|
||||
/** Wraps a stream function so tool-call arguments are decoded before consumers inspect them. */
|
||||
export function createHtmlEntityToolCallArgumentDecodingWrapper(
|
||||
baseStreamFn: StreamFn | undefined,
|
||||
): StreamFn {
|
||||
|
||||
Reference in New Issue
Block a user