mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:20:44 +00:00
perf(cron): lazy-load external content runtime
This commit is contained in:
4
src/cron/isolated-agent/run-external-content.runtime.ts
Normal file
4
src/cron/isolated-agent/run-external-content.runtime.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
buildSafeExternalPrompt,
|
||||
detectSuspiciousPatterns,
|
||||
} from "../../security/external-content.js";
|
||||
@@ -17,9 +17,7 @@ export { setSessionRuntimeModel } from "../../config/sessions/types.js";
|
||||
export { logWarn } from "../../logger.js";
|
||||
export { normalizeAgentId } from "../../routing/session-key.js";
|
||||
export {
|
||||
buildSafeExternalPrompt,
|
||||
detectSuspiciousPatterns,
|
||||
isExternalHookSession,
|
||||
mapHookExternalContentSource,
|
||||
resolveHookExternalContentSource,
|
||||
} from "../../security/external-content.js";
|
||||
} from "../../security/external-content-source.js";
|
||||
|
||||
@@ -116,14 +116,17 @@ vi.mock("./run.runtime.js", () => ({
|
||||
setCliSessionId: vi.fn(),
|
||||
logWarn: (...args: unknown[]) => logWarnMock(...args),
|
||||
normalizeAgentId: vi.fn((id: string) => id),
|
||||
buildSafeExternalPrompt: buildSafeExternalPromptMock,
|
||||
detectSuspiciousPatterns: detectSuspiciousPatternsMock,
|
||||
mapHookExternalContentSource: mapHookExternalContentSourceMock,
|
||||
isExternalHookSession: isExternalHookSessionMock,
|
||||
resolveHookExternalContentSource: resolveHookExternalContentSourceMock,
|
||||
getRemoteSkillEligibility: getRemoteSkillEligibilityMock,
|
||||
}));
|
||||
|
||||
vi.mock("./run-external-content.runtime.js", () => ({
|
||||
buildSafeExternalPrompt: buildSafeExternalPromptMock,
|
||||
detectSuspiciousPatterns: detectSuspiciousPatternsMock,
|
||||
}));
|
||||
|
||||
vi.mock("./run-context.runtime.js", () => ({
|
||||
lookupContextTokens: lookupContextTokensMock,
|
||||
}));
|
||||
|
||||
@@ -29,9 +29,7 @@ import {
|
||||
} from "./run-session-state.js";
|
||||
import {
|
||||
DEFAULT_CONTEXT_TOKENS,
|
||||
buildSafeExternalPrompt,
|
||||
deriveSessionTotalTokens,
|
||||
detectSuspiciousPatterns,
|
||||
ensureAgentWorkspace,
|
||||
hasNonzeroUsage,
|
||||
isCliProvider,
|
||||
@@ -61,6 +59,9 @@ let sessionStoreRuntimePromise:
|
||||
| Promise<typeof import("../../config/sessions/store.runtime.js")>
|
||||
| undefined;
|
||||
let cronExecutorRuntimePromise: Promise<typeof import("./run-executor.runtime.js")> | undefined;
|
||||
let cronExternalContentRuntimePromise:
|
||||
| Promise<typeof import("./run-external-content.runtime.js")>
|
||||
| undefined;
|
||||
let cronAuthProfileRuntimePromise:
|
||||
| Promise<typeof import("./run-auth-profile.runtime.js")>
|
||||
| undefined;
|
||||
@@ -79,6 +80,11 @@ async function loadCronExecutorRuntime() {
|
||||
return await cronExecutorRuntimePromise;
|
||||
}
|
||||
|
||||
async function loadCronExternalContentRuntime() {
|
||||
cronExternalContentRuntimePromise ??= import("./run-external-content.runtime.js");
|
||||
return await cronExternalContentRuntimePromise;
|
||||
}
|
||||
|
||||
async function loadCronAuthProfileRuntime() {
|
||||
cronAuthProfileRuntimePromise ??= import("./run-auth-profile.runtime.js");
|
||||
return await cronAuthProfileRuntimePromise;
|
||||
@@ -407,6 +413,7 @@ async function prepareCronRunContext(params: {
|
||||
let commandBody: string;
|
||||
|
||||
if (isExternalHook) {
|
||||
const { detectSuspiciousPatterns } = await loadCronExternalContentRuntime();
|
||||
const suspiciousPatterns = detectSuspiciousPatterns(input.message);
|
||||
if (suspiciousPatterns.length > 0) {
|
||||
logWarn(
|
||||
@@ -417,6 +424,7 @@ async function prepareCronRunContext(params: {
|
||||
}
|
||||
|
||||
if (shouldWrapExternal) {
|
||||
const { buildSafeExternalPrompt } = await loadCronExternalContentRuntime();
|
||||
const hookType = mapHookExternalContentSource(hookExternalContentSource ?? "webhook");
|
||||
const safeContent = buildSafeExternalPrompt({
|
||||
content: input.message,
|
||||
|
||||
26
src/security/external-content-source.ts
Normal file
26
src/security/external-content-source.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
|
||||
export type HookExternalContentSource = "gmail" | "webhook";
|
||||
|
||||
export function resolveHookExternalContentSource(
|
||||
sessionKey: string,
|
||||
): HookExternalContentSource | undefined {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(sessionKey);
|
||||
if (normalized.startsWith("hook:gmail:")) {
|
||||
return "gmail";
|
||||
}
|
||||
if (normalized.startsWith("hook:webhook:") || normalized.startsWith("hook:")) {
|
||||
return "webhook";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function mapHookExternalContentSource(
|
||||
source: HookExternalContentSource,
|
||||
): "email" | "webhook" {
|
||||
return source === "gmail" ? "email" : "webhook";
|
||||
}
|
||||
|
||||
export function isExternalHookSession(sessionKey: string): boolean {
|
||||
return resolveHookExternalContentSource(sessionKey) !== undefined;
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
export {
|
||||
isExternalHookSession,
|
||||
mapHookExternalContentSource,
|
||||
resolveHookExternalContentSource,
|
||||
type HookExternalContentSource,
|
||||
} from "./external-content-source.js";
|
||||
import {
|
||||
mapHookExternalContentSource,
|
||||
resolveHookExternalContentSource,
|
||||
} from "./external-content-source.js";
|
||||
|
||||
/**
|
||||
* Security utilities for handling untrusted external content.
|
||||
@@ -92,10 +101,6 @@ export type ExternalContentSource =
|
||||
| "web_fetch"
|
||||
| "unknown";
|
||||
|
||||
// Hook-origin async runs need immutable ingress provenance because routed
|
||||
// session keys can be normalized outside the hook:* namespace.
|
||||
export type HookExternalContentSource = "gmail" | "webhook";
|
||||
|
||||
const EXTERNAL_SOURCE_LABELS: Record<ExternalContentSource, string> = {
|
||||
email: "Email",
|
||||
webhook: "Webhook",
|
||||
@@ -107,25 +112,6 @@ const EXTERNAL_SOURCE_LABELS: Record<ExternalContentSource, string> = {
|
||||
unknown: "External",
|
||||
};
|
||||
|
||||
export function resolveHookExternalContentSource(
|
||||
sessionKey: string,
|
||||
): HookExternalContentSource | undefined {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(sessionKey);
|
||||
if (normalized.startsWith("hook:gmail:")) {
|
||||
return "gmail";
|
||||
}
|
||||
if (normalized.startsWith("hook:webhook:") || normalized.startsWith("hook:")) {
|
||||
return "webhook";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function mapHookExternalContentSource(
|
||||
source: HookExternalContentSource,
|
||||
): Extract<ExternalContentSource, "email" | "webhook"> {
|
||||
return source === "gmail" ? "email" : "webhook";
|
||||
}
|
||||
|
||||
const FULLWIDTH_ASCII_OFFSET = 0xfee0;
|
||||
|
||||
// Map of Unicode angle bracket homoglyphs to their ASCII equivalents.
|
||||
@@ -363,13 +349,6 @@ export function buildSafeExternalPrompt(params: {
|
||||
return `${context}${wrappedContent}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a session key indicates an external hook source.
|
||||
*/
|
||||
export function isExternalHookSession(sessionKey: string): boolean {
|
||||
return resolveHookExternalContentSource(sessionKey) !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the hook type from a session key.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user