From 5b11985439e4221f25d3d4173767b50119314462 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 13 Apr 2026 18:34:04 +0100 Subject: [PATCH] perf(cron): lazy-load external content runtime --- .../run-external-content.runtime.ts | 4 ++ src/cron/isolated-agent/run.runtime.ts | 4 +- src/cron/isolated-agent/run.test-harness.ts | 7 +++- src/cron/isolated-agent/run.ts | 12 +++++- src/security/external-content-source.ts | 26 ++++++++++++ src/security/external-content.ts | 41 +++++-------------- 6 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 src/cron/isolated-agent/run-external-content.runtime.ts create mode 100644 src/security/external-content-source.ts diff --git a/src/cron/isolated-agent/run-external-content.runtime.ts b/src/cron/isolated-agent/run-external-content.runtime.ts new file mode 100644 index 00000000000..f2382495b51 --- /dev/null +++ b/src/cron/isolated-agent/run-external-content.runtime.ts @@ -0,0 +1,4 @@ +export { + buildSafeExternalPrompt, + detectSuspiciousPatterns, +} from "../../security/external-content.js"; diff --git a/src/cron/isolated-agent/run.runtime.ts b/src/cron/isolated-agent/run.runtime.ts index a413efbe11f..c873a114224 100644 --- a/src/cron/isolated-agent/run.runtime.ts +++ b/src/cron/isolated-agent/run.runtime.ts @@ -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"; diff --git a/src/cron/isolated-agent/run.test-harness.ts b/src/cron/isolated-agent/run.test-harness.ts index a043af16173..c3c51ff3a67 100644 --- a/src/cron/isolated-agent/run.test-harness.ts +++ b/src/cron/isolated-agent/run.test-harness.ts @@ -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, })); diff --git a/src/cron/isolated-agent/run.ts b/src/cron/isolated-agent/run.ts index e87d6182ede..d2c0fd876cd 100644 --- a/src/cron/isolated-agent/run.ts +++ b/src/cron/isolated-agent/run.ts @@ -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 | undefined; let cronExecutorRuntimePromise: Promise | undefined; +let cronExternalContentRuntimePromise: + | Promise + | undefined; let cronAuthProfileRuntimePromise: | Promise | 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, diff --git a/src/security/external-content-source.ts b/src/security/external-content-source.ts new file mode 100644 index 00000000000..7f893b1ad2c --- /dev/null +++ b/src/security/external-content-source.ts @@ -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; +} diff --git a/src/security/external-content.ts b/src/security/external-content.ts index 30575d135a5..d4380f4273a 100644 --- a/src/security/external-content.ts +++ b/src/security/external-content.ts @@ -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 = { email: "Email", webhook: "Webhook", @@ -107,25 +112,6 @@ const EXTERNAL_SOURCE_LABELS: Record = { 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 { - 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. */