diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e99cdd97fa..aeb574ef6d 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added system prompt note listing pre-installed Python packages (requests, httpx, pyyaml, tomli, python-dotenv, pandas, numpy, scipy, beautifulsoup4, lxml, pydantic). +- Added `DEFAULT_RLM_EXTRA_UV_ARGS` constant and kernel bootstrap installation of those packages; updated prompt to reference the constant instead of a hardcoded list. ## [0.0.2] - 2026-05-20 diff --git a/packages/coding-agent/src/core/kernel/bootstrap.ts b/packages/coding-agent/src/core/kernel/bootstrap.ts index 2d3284e8bc..733bec0923 100644 --- a/packages/coding-agent/src/core/kernel/bootstrap.ts +++ b/packages/coding-agent/src/core/kernel/bootstrap.ts @@ -8,10 +8,23 @@ import { createInterface } from "node:readline/promises"; import { setTimeout as sleep } from "node:timers/promises"; import { fileURLToPath } from "node:url"; -const BOOTSTRAP_SCHEMA = 2; +const BOOTSTRAP_SCHEMA = 3; const PYTHON_VERSION = "3.11"; const IPYKERNEL_REQUIREMENT = "ipykernel"; const RUNTIME_REQUIREMENT = "prime-agent-runtime"; +export const DEFAULT_RLM_EXTRA_UV_ARGS = [ + "requests", + "httpx", + "pyyaml", + "tomli", + "python-dotenv", + "pandas", + "numpy", + "scipy", + "beautifulsoup4", + "lxml", + "pydantic", +]; const UV_INSTALL_COMMAND = "curl -LsSf https://astral.sh/uv/install.sh | sh"; const RUNTIME_READY_CHECK = "import rlm; assert hasattr(rlm, 'run'); assert callable(rlm); assert hasattr(rlm, 'rlm'); assert callable(rlm.rlm); assert not hasattr(rlm, 'background'); assert not hasattr(rlm.rlm, 'background')"; @@ -26,6 +39,7 @@ interface BootstrapVersion { schema: number; ipykernel?: string; runtime?: string; + extraUvArgs?: string[]; } function errorMessage(error: unknown): string { @@ -266,21 +280,35 @@ async function readBootstrapVersion(venv: string): Promise typeof v === "string") + ? (parsed.extraUvArgs as string[]) + : undefined; return { schema: parsed.schema, ipykernel: typeof parsed.ipykernel === "string" ? parsed.ipykernel : undefined, runtime: typeof parsed.runtime === "string" ? parsed.runtime : undefined, + extraUvArgs, }; } catch { return null; } } +function extraUvArgsMatch(a: string[] | undefined, b: string[] | undefined): boolean { + if (a === b) return true; + if (!a || !b) return false; + if (a.length !== b.length) return false; + return a.every((v, i) => v === b[i]); +} + function bootstrapVersionCurrent(version: BootstrapVersion | null): boolean { return ( version?.schema === BOOTSTRAP_SCHEMA && version.ipykernel === IPYKERNEL_REQUIREMENT && - version.runtime === RUNTIME_REQUIREMENT + version.runtime === RUNTIME_REQUIREMENT && + extraUvArgsMatch(version.extraUvArgs, DEFAULT_RLM_EXTRA_UV_ARGS) ); } @@ -289,6 +317,7 @@ async function writeBootstrapVersion(venv: string): Promise { schema: BOOTSTRAP_SCHEMA, ipykernel: IPYKERNEL_REQUIREMENT, runtime: RUNTIME_REQUIREMENT, + extraUvArgs: DEFAULT_RLM_EXTRA_UV_ARGS, }; await writeFile(path.join(venv, BOOTSTRAP_VERSION_FILE), `${JSON.stringify(version)}\n`, "utf8"); } @@ -318,7 +347,15 @@ async function bootstrapVenv(venv: string): Promise { await run(uv, ["python", "install", PYTHON_VERSION]); await run(uv, ["venv", venv, "--python", PYTHON_VERSION, "--seed"]); - await run(uv, ["pip", "install", "--python", python, IPYKERNEL_REQUIREMENT, runtimeRequirement]); + await run(uv, [ + "pip", + "install", + "--python", + python, + IPYKERNEL_REQUIREMENT, + runtimeRequirement, + ...DEFAULT_RLM_EXTRA_UV_ARGS, + ]); await writeBootstrapVersion(venv); } diff --git a/packages/coding-agent/src/core/prompts/rlm.ts b/packages/coding-agent/src/core/prompts/rlm.ts index c86cce6388..4a1c3f735f 100644 --- a/packages/coding-agent/src/core/prompts/rlm.ts +++ b/packages/coding-agent/src/core/prompts/rlm.ts @@ -1,3 +1,5 @@ +import { DEFAULT_RLM_EXTRA_UV_ARGS } from "../kernel/bootstrap.js"; + export interface RlmPromptOptions { cwd: string; skillsDir?: string; @@ -53,7 +55,7 @@ export function buildRlmPrompt(options: RlmPromptOptions): string { "", "Use `ipython` for both Python and shell work. For repository shell commands, prefer IPython shell syntax: `!rg ...`, `!npm run check`, or `%%bash` for multi-line scripts. Do not wrap ordinary shell commands in Python subprocesses unless you need Python-level processing.", "", - "The kernel has requests, httpx, pyyaml, tomli, python-dotenv, pandas, numpy, scipy, beautifulsoup4, lxml, and pydantic pre-installed. Import them directly; no pip install needed.", + `The kernel has these packages pre-installed: ${DEFAULT_RLM_EXTRA_UV_ARGS.join(", ")}. Import them directly; no pip install needed.`, ); }