perf(inbound): trim cold startup import graph#52082
Conversation
8908ebe to
ab5783c
Compare
ab5783c to
b434dd5
Compare
Greptile SummaryThis PR is a well-measured checkpoint for cold-start import graph trimming on the inbound reply path. It systematically splits monolithic modules into lightweight primitive/base files and heavier runtime files, then converts the rare hot-path consumers to lazy dynamic Key changes:
Notable observations:
Confidence Score: 4/5
|
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 IDOR / missing authorization: chat
|
| Property | Value |
|---|---|
| Severity | Medium |
| CWE | CWE-639 |
| Location | src/auto-reply/reply/directive-handling.model-selection.ts:25-156 |
Description
The new /model directive handling infers an auth profile override when the model directive ends with an 8-digit numeric suffix (e.g. /model openai/gpt-4o@20251001).
- Input:
params.directives.rawModelDirectivecomes from chat user text (/model ...). - Auth-profile lookup:
resolveStoredNumericProfileModelDirective()loads the localauth-profiles.jsonstore and checksstore.profiles[profileId]for an 8-digit ID. - No authorization: if the profile exists (and provider matches), the code sets
profileOverridewithout verifying the sender is allowed to use that auth profile (e.g., owner-only), enabling credential/profile switching by ID. - Impact: any chat user who is allowed to run
/modeldirectives (which may include non-owners depending on channel/group config) can switch the agent to a different stored credential profile, causing potential billing abuse or access to more privileged provider credentials.
Vulnerable code (inferred profile override by numeric ID):
const profileId = trimmed.slice(profileDelimiter + 1).trim();
if (!/^\d{8}$/.test(profileId)) return null;
const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false });
const profile = store.profiles[profileId];
if (!profile) return null;
return { modelRaw, profileId, profileProvider: profile.provider };And later applying that inferred ID as profileOverride:
const rawProfile = params.directives.rawModelProfile ??
(useStoredNumericProfile ? storedNumericProfile?.profileId : undefined);
...
profileOverride = profileResolved.profileId;Note: This also unintentionally allows bypassing the “numeric model selection not supported in chat” guard by using a non-digit string plus @<8digits> (e.g. 2@20251001), though the larger risk is profile selection.
Recommendation
Require explicit authorization for auth-profile overrides, and/or remove the implicit numeric-profile inference.
Recommended hardening options:
-
Owner-only profile overrides: Only allow
rawModelProfile/ inferred profile usage when the sender is an owner/admin (or when a dedicated config flag is enabled). -
Disable numeric inference by default: Treat
@YYYYMMDDas part of the model unless the user explicitly provides a separate profile override token.
Example (conceptual) fix: pass sender auth context into model selection and block profile overrides for non-owners:
export function resolveModelSelectionFromDirective(params: {
directives: InlineDirectives;
...
senderIsOwner: boolean;
}): ... {
...
const requestedProfile = params.directives.rawModelProfile ??
(params.senderIsOwner && useStoredNumericProfile ? storedNumericProfile?.profileId : undefined);
if (requestedProfile && !params.senderIsOwner) {
return { errorText: "Auth profile overrides are restricted to owners." };
}
...
}Additionally, if numeric IDs must be supported, consider using non-guessable profile identifiers (e.g., provider:<uuid>), and avoid allowing selection of profiles not explicitly allowlisted for chat override.
Analyzed PR: #52082 at commit 9148c41
Last updated on: 2026-03-22T06:19:41Z
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f175b6bf70
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts (cherry picked from commit 041f0b8)
* refactor: add channel-pairing plugin-sdk module — prerequisite for upstream discord adapter batch * refactor: tighten plugin sdk channel surfaces (cherry picked from commit 002cc07) * refactor: split remaining monitor runtime helpers (cherry picked from commit 005b25e) * perf(inbound): trim cold startup import graph (openclaw#52082) * perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts (cherry picked from commit 041f0b8) * Discord: consolidate message tool discovery (cherry picked from commit 0a0ca80) * Discord: move group policy behind plugin boundary (cherry picked from commit 0bfaa36) * Plugins: move config-backed directories behind channel plugins (cherry picked from commit 2b5fa09) * perf: reduce plugin runtime startup overhead (cherry picked from commit 3382ef2) * Channels: centralize outbound payload contracts (cherry picked from commit 4aae0d4) * Discord: own message tool components schema (cherry picked from commit 05634ee) * Plugins: remove first-party legacy message discovery shims (cherry picked from commit 1c6676c) * refactor: move Discord channel implementation to extensions/ (openclaw#45660) * refactor: move Discord channel implementation to extensions/discord/src/ Move all Discord source files from src/discord/ to extensions/discord/src/, following the extension migration pattern. Source files in src/discord/ are replaced with re-export shims. Channel-plugin files from src/channels/plugins/*/discord* are similarly moved and shimmed. - Copy all .ts source files preserving subdirectory structure (monitor/, voice/) - Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues) - Fix all relative imports to use correct paths from new location - Create re-export shims at original locations for backward compatibility - Delete test files from shim locations (tests live in extension now) - Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate extension files outside src/ - Update write-plugin-sdk-entry-dts.ts to match new declaration output paths * fix: add importOriginal to thread-bindings session-meta mock for extensions test * style: fix formatting in thread-bindings lifecycle test (cherry picked from commit 5682ec3) * refactor: route extension seams through public apis (cherry picked from commit 58cf9b8) * fix: gate setup-only plugin side effects (cherry picked from commit 59bcac4) * refactor: simplify tlon and discord setup accounts (cherry picked from commit 6fa0027) * refactor: make OutboundSendDeps dynamic with channel-ID keys (openclaw#45517) * refactor: make OutboundSendDeps dynamic with channel-ID keys Replace hardcoded per-channel send fields (sendTelegram, sendDiscord, etc.) with a dynamic index-signature type keyed by channel ID. This unblocks moving channel implementations to extensions without breaking the outbound dispatch contract. - OutboundSendDeps and CliDeps are now { [channelId: string]: unknown } - Each outbound adapter resolves its send fn via bracket access with cast - Lazy-loading preserved via createLazySender with module cache - Delete 6 deps-send-*.runtime.ts one-liner re-export files - Harden guardrail scan against deleted-but-tracked files * fix: preserve outbound send-deps compatibility * style: fix formatting issues (import order, extra bracket, trailing whitespace) * fix: resolve type errors from dynamic OutboundSendDeps in tests and extension * fix: remove unused OutboundSendDeps import from deliver.test-helpers (cherry picked from commit 7764f71) * refactor: finish plugin-owned channel runtime seams (cherry picked from commit 7964563) * refactor: reuse shared account config lookups (cherry picked from commit 7ae0941) * Channels: move onboarding adapters into extensions (cherry picked from commit 8b001d6) * perf: narrow discord timeout import seam (cherry picked from commit 8e6a4c2) * Plugin SDK: restore scoped imports for bundled channels (cherry picked from commit 9270094) * Channels: add message action capabilities (cherry picked from commit 92bea97) * refactor(discord): share plugin base config (cherry picked from commit a0e7e3c) * refactor: split monitor runtime helpers (cherry picked from commit a2518a1) * test: strengthen regression coverage and trim low-value checks (cherry picked from commit b4656f1) * fix: reduce plugin and discord warning noise (cherry picked from commit c156f7c) * refactor: move channel delivery and ACP seams into plugins (cherry picked from commit d163278) * Discord: lazy-load setup wizard surface (cherry picked from commit d663df7) * refactor(hook-tests): share subagent hook helpers (cherry picked from commit e56e492) * refactor(test): dedupe startup and nostr test fixtures (cherry picked from commit f1b2c56) * Plugins: honor native command aliases at dispatch (cherry picked from commit f90d432) * refactor: move Discord channel implementation to extensions/ (openclaw#45660) * refactor: move Discord channel implementation to extensions/discord/src/ Move all Discord source files from src/discord/ to extensions/discord/src/, following the extension migration pattern. Source files in src/discord/ are replaced with re-export shims. Channel-plugin files from src/channels/plugins/*/discord* are similarly moved and shimmed. - Copy all .ts source files preserving subdirectory structure (monitor/, voice/) - Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues) - Fix all relative imports to use correct paths from new location - Create re-export shims at original locations for backward compatibility - Delete test files from shim locations (tests live in extension now) - Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate extension files outside src/ - Update write-plugin-sdk-entry-dts.ts to match new declaration output paths * fix: add importOriginal to thread-bindings session-meta mock for extensions test * style: fix formatting in thread-bindings lifecycle test (cherry picked from commit 5682ec3) * fix: gate setup-only plugin side effects (cherry picked from commit 59bcac4) * refactor: simplify tlon and discord setup accounts (cherry picked from commit 6fa0027) * refactor: reuse shared account config lookups (cherry picked from commit 7ae0941) * refactor: remove dock shim and move session routing into plugins (cherry picked from commit 85b7bc7) * Channels: move onboarding adapters into extensions (cherry picked from commit 8b001d6) * test: strengthen regression coverage and trim low-value checks (cherry picked from commit b4656f1) * fix: reduce plugin and discord warning noise (cherry picked from commit c156f7c) * test: share runtime group policy fallback cases (cherry picked from commit 208fb1a) * fix: unblock discord startup on deploy rate limits (cherry picked from commit 2659fc6) * refactor: share discord auto thread params (cherry picked from commit 301594b) * test: dedupe discord forwarded media assertions (cherry picked from commit 3eb039c) * refactor: share discord preflight shared fields (cherry picked from commit 58a51e2) * test(discord): cover additional utility surfaces (cherry picked from commit 59be2c8) * Discord: add shared interactive renderer (cherry picked from commit 59d355b) * chore: remove session-fork.runtime.ts — references gutted pi-coding-agent layer * fix: resolve remaining conflict markers in discord extension files * fix: remove remaining diff3 base markers from discord extension files * fix: remove duplicate extension files and broken runtime bridges — fork structure differs from upstream * fix: remove all new files with type errors — incompatible with fork module structure * fix: restore all remaining files with type errors to fork-sync-base versions * fix: second pass — restore remaining files with type errors * fix: pass 5 — restore/remove files with type errors * fix: restore files inadvertently deleted by upstream restructuring * fix: restore inadvertently modified files and remove orphan runtime extractions * fix: restore dock property in PluginChannelRegistration type * fix: restore dock property in PluginChannelRegistration and zalo exports * fix: remove extension discord src files and restore src/discord imports — fork keeps discord in src/ * fix: round 1 type error cleanup * fix: resolve lint error and restore test files modified by upstream refactoring * fix: format suites.ts and restore channels-misc.test.ts --------- Co-authored-by: Peter Steinberger <[email protected]> Co-authored-by: Vincent Koc <[email protected]> Co-authored-by: Gustavo Madeira Santana <[email protected]> Co-authored-by: scoootscooob <[email protected]>
* perf(inbound): trim cold startup import graph * chore(reply): drop redundant inline action type import * fix(inbound): restore warning and maintenance seams * fix(reply): restore type seam and secure forked transcripts
Summary
This is a checkpoint PR for the current inbound cold-start reduction work.
It trims several heavy static imports off the inbound reply path by splitting hot read paths from heavier runtime/write/discovery paths.
What changed
infra/outbound/targets.tssession-fork.runtime.ts/modelinfo UI pathcommands-core.runtime.tscommands.runtime.tsopenclaw-tools.runtime.tsabort-cutoff.runtime.tsMeasured impact
Representative cold import numbers from local
node --expose-gc --import tsxprobes:src/infra/session-maintenance-warning.ts9.9s / 169MB-> about122ms / 1.9MBextensions/discord/session-key-api.ts5.5s / 169MB-> about313ms / ~0MBsrc/config/sessions/store.ts514ms / 55MBsrc/auto-reply/reply/session.ts563ms / 56MBsrc/auto-reply/reply/get-reply-inline-actions.ts335ms / 2.7MBsrc/auto-reply/reply/abort-cutoff.ts302ms / ~0MBsrc/auto-reply/reply/directive-handling.persist.ts~7-13s / ~160MBclass to about2.16s / 59MBValidation
pnpm tsgoKnown remaining hotspot
The next measured long pole is the actual agent execution stack:
src/auto-reply/reply/agent-runner.ts: about4.1s / 161.5MBsrc/agents/pi-embedded.ts: about4.35s / 161.6MBThat stack is pulled in by
src/auto-reply/reply/get-reply-run.ts, so the next follow-up should moverunReplyAgentbehind a runtime boundary and continue re-measuring from there.