mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 14:17:58 +08:00
* feat: default EXTENSIONS_REF from pinned @openhands/extensions SHA in package.json
The agent-server polls the OpenHands extensions repo using EXTENSIONS_REF
(defaulting to 'main'), while the frontend bundles the MCP catalog and
automations data from a pinned commit SHA in package.json. This caused the
two to silently diverge — skills loaded at runtime were from latest main
while the UI showed catalog data from an older commit.
Fix: derive a DEFAULT_EXTENSIONS_REF from the '#<sha>' fragment in the
@openhands/extensions git URL in package.json and inject it as the agent-
server's EXTENSIONS_REF when the caller hasn't set it explicitly.
- scripts/dev-safe.mjs: getExtensionsRef() helper reads package.json at
module init; buildAgentServerEnv() spreads EXTENSIONS_REF into the
child-process env unless the caller already set it.
- docker/Dockerfile (config-gen stage): reads package.json alongside
config/defaults.json and emits CONFIG_EXTENSIONS_REF=<sha> into
defaults.env when the dependency uses a pinned git SHA.
- docker/entrypoint.sh: applies CONFIG_EXTENSIONS_REF as the default via
EXTENSIONS_REF="${EXTENSIONS_REF:-${CONFIG_EXTENSIONS_REF:-}}".
User-supplied EXTENSIONS_REF always takes precedence in both paths.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: address review bot comments on EXTENSIONS_REF sync
- entrypoint.sh: guard EXTENSIONS_REF export so an absent CONFIG_EXTENSIONS_REF
never sets the variable to an empty string (which would defeat the
agent-server's own 'main' default in os.environ.get('EXTENSIONS_REF','main'))
- scripts/dev-safe.mjs: getExtensionsRef() now checks devDependencies as
fallback when @openhands/extensions is not in dependencies
- docker/Dockerfile config-gen stage: same devDependencies fallback
- AGENTS.md: replace internal constant name DEFAULT_EXTENSIONS_REF with the
actual env var EXTENSIONS_REF; also note the empty-string guard
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: pre-seed extensions cache when EXTENSIONS_REF is a commit SHA
The SDK's git clone --depth 1 --branch <sha> fails because --branch only
accepts branch/tag names, not raw 40-char commit SHAs (GitHub returns
'fatal: Remote branch <sha> not found').
When EXTENSIONS_REF is a commit SHA, pre-seed the public-skills cache with
a plain full git clone + checkout before starting the agent-server:
- docker/entrypoint.sh: pre-seeds after EXTENSIONS_REF is exported; is a
no-op when the cache already exists or EXTENSIONS_REF is a branch/tag name
- scripts/dev-safe.mjs: exports preseedExtensionsCache() and
DEFAULT_EXTENSIONS_REF; calls pre-seed in main() before spawning the
agent-server; guards with the same SHA regex as entrypoint.sh
- scripts/dev-with-automation.mjs: imports preseedExtensionsCache and
DEFAULT_EXTENSIONS_REF from dev-safe.mjs; calls pre-seed in
startAgentServer() before spawnService()
The SDK's update path (fetch + git checkout) can handle SHAs once the
repo exists, so this is sufficient without any SDK changes.
Co-authored-by: openhands <openhands@all-hands.dev>
* refactor(docker): bake extensions clone into image, cp at runtime
Move the git clone for the pinned extensions SHA from the entrypoint
into a dedicated Dockerfile build stage (extensions-cache).
Why: the clone is deterministic (same SHA for a given image), so doing
it once at build time is cheaper than doing it on first container start.
It also avoids network access at runtime entirely for Docker users.
How:
- New extensions-cache stage (node:24-slim + git): reads package.json,
clones OpenHands/extensions, checks out the pinned SHA into
/tmp/public-skills. When the dependency is not SHA-pinned the stage
exits early with an empty directory (graceful no-op).
- Final stage: COPY --from=extensions-cache stores the result at
/opt/agent-canvas/extensions-cache/ — outside the
/home/openhands/.openhands VOLUME so it is not hidden by bind-mounts.
chown passes ownership to openhands.
- entrypoint.sh: replaces git clone + checkout with cp -r from the
pre-baked path. The guard checks [ -d "${_ext_baked}/.git" ] so
the block is a no-op when the stage produced an empty directory.
The npm-dev preseedExtensionsCache() path in dev-safThe npm-dev preseedExtensionsCache() path in d — that still clones over the
network on first run (build-time baking does not apply to npm dev).
Co-authored-by: openhands <openhands@all-hands.dev>
* fix(extensions-preseed): handle existing shallow clone missing the pinned SHA
When `npm run dev` has been run previously without the PR, the agent-server
SDK creates a shallow clone of the extensions repo via:
git clone --depth 1 --branch main
After PR #1060 is applied, `EXTENSIONS_REF` is set to the 40-char pinned
SHA. The SDK then tries `git checkout <sha>` against that shallow clone,
which fails because the specific commit is not in its shallow history.
`preseedExtensionsCache` had a blind early-return when `.git` already
existed, trusting the "SDK update path" to handle it. But the SDK's update
path also does a plain `git checkout <sha>` and cannot succeed on a shallow
clone that lacks the commit.
Fix: before returning early, verify the SHA is accessible with
git cat-file -t <sha>
If it returns anything other than "commit", the cache is a shallow clone
that pre-dates the pinned commit. Unshallow the existing clone with
git fetch --unshallow
so the full history is available, then checkout the SHA.
Co-authored-by: openhands <openhands@all-hands.dev>
* refactor(extensions): copy from node_modules on npm, always overwrite cache
Implement the intended architecture for extensions cache seeding:
npm path (dev-safe.mjs / dev-with-automation.mjs):
- Replace preseedExtensionsCache (git clone approach) with
copyExtensionsToSkillsCache, which copies the already-installed
node_modules/@openhands/extensions directly into the skills cache.
No network call required — npm already installed the package at
the pinned SHA.
- Always overwrite the cache (rm -rf + cpSync) so stale files from a
prior run that used a different version are never left behind.
- Set EXTENSIONS_REF unconditionally in buildAgentServerEnv so it
always matches the pre-seeded cache content.
Docker path (docker/entrypoint.sh):
- Always copy /opt/agent-canvas/extensions-cache into the skills cache
(rm -rf + cp -r), removing the prior guard that skipped the copy when
.git already existed.
- Set EXTENSIONS_REF unconditionally from CONFIG_EXTENSIONS_REF.
Both paths accept that the SDK will warn 'Using cached version' for a
raw commit SHA — that warning is the expected fallback until SDK polling
is disabled in a follow-up PR.
Note: the npm package only publishes integrations/ and automations/.
Docker's baked full clone also includes marketplaces/, plugins/, skills/.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix(extensions): delete stale cache on npm startup instead of copying files
The copy-from-node_modules approach was still broken: the npm package has no
.git directory, so the SDK tried a fresh 'git clone --depth 1 --branch <sha>'
on top of the already-populated directory and got confused.
Simpler fix: just delete ~/.openhands/cache/skills/public-skills on startup.
The SDK starts with a clean slate and attempts its normal clone; for a raw SHA
ref it will warn 'Using cached version' which is the accepted fallback until
SDK polling is disabled in a follow-up PR.
- Replace copyExtensionsToSkillsCache (copy from node_modules) with
clearExtensionsCache (delete the directory)
- Remove now-unused cpSync import
- Docker path unchanged (baked clone always copied by entrypoint.sh)
Co-authored-by: openhands <openhands@all-hands.dev>
* fix(extensions): always delete cache; only set EXTENSIONS_REF if not in env
Three simple rules:
1. Always delete ~/.openhands/cache/skills/public-skills on startup so the
SDK clones fresh — no conditional on DEFAULT_EXTENSIONS_REF.
2. Only inject EXTENSIONS_REF into the agent-server env when the caller has
not already set it (restore the !process.env.EXTENSIONS_REF guard).
3. Let the agent-server SDK do its own cloning from there.
Co-authored-by: openhands <openhands@all-hands.dev>
* feat(extensions): use OH_PUBLIC_SKILLS_PATH to bypass git polling
Switch from EXTENSIONS_REF (which still went through the SDK's broken
git clone --branch <sha> path) to OH_PUBLIC_SKILLS_PATH, introduced in
software-agent-sdk PR #3513. When set, the SDK skips all git operations
and loads public skills directly from the given directory.
npm path (dev-safe.mjs / dev-with-automation.mjs):
- Add cloneExtensionsForSkillsCache(sha, cacheDir): does a targeted
git fetch --depth=1 origin <sha> into ~/.openhands/cache/skills/public-skills.
Reuses the existing directory when it already contains the right commit.
- buildAgentServerEnv now sets OH_PUBLIC_SKILLS_PATH (not EXTENSIONS_REF)
pointing at that directory when DEFAULT_EXTENSIONS_REF is known and
OH_PUBLIC_SKILLS_PATH is not already in the environment.
Docker path (docker/entrypoint.sh):
- Removes EXTENSIONS_REF export entirely.
- After copying the baked clone to the skills cache, exports
OH_PUBLIC_SKILLS_PATH pointing at that directory.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix(dev): include openhands-sdk in git-ref uvx args
When OH_AGENT_SERVER_GIT_REF is set, openhands-sdk was missing from the
--with args, so uv fell back to the released PyPI version. That version
lacks the public_skills_path parameter added to update_skills_repository
in SDK PR #3513, so the agent-server's skills_service called the old SDK
path and did a plain git clone of 'main' — overwriting the SHA-pinned
clone our cloneExtensionsForSkillsCache had just created.
Fix: add --with git+<repo>@<ref>#subdirectory=openhands-sdk alongside the
existing openhands-tools and openhands-workspace entries, matching what
the PyPI and local paths already do for openhands-sdk.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix(dev): add --reinstall to git-ref uvx command
When OH_AGENT_SERVER_GIT_REF points at a branch whose version string
matches the current PyPI release (e.g. both are 1.25.0), uv silently
reuses the cached PyPI wheels and the git ref is never used.
--reinstall forces uv to build a fresh environment from the specified
git sources regardless of what is already cached. The git clone itself
is still cached in ~/.cache/uv/git-v0/, so only the first run after a
ref change pays the full network cost.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: rename OH_PUBLIC_SKILLS_PATH to PUBLIC_SKILLS_PATH
Aligns with the env var name used by the SDK PR.
Co-authored-by: openhands <openhands@all-hands.dev>
* test: update dev-safe buildAgentServerCommand tests for --reinstall and openhands-sdk
The git-ref path now adds --reinstall (so uv doesn't silently reuse cached
PyPI wheels) and includes openhands-sdk as a --with package so inter-package
APIs stay in sync. Update the two affected toEqual assertions to match.
Co-authored-by: openhands <openhands@all-hands.dev>
* refactor: use EXTENSIONS_REF instead of PUBLIC_SKILLS_PATH for extensions pinning
The PUBLIC_SKILLS_PATH / cloneExtensionsForSkillsCache approach was considered
but not chosen. EXTENSIONS_REF is the variable the agent-server SDK uses, and
it now skips network polling when the requested SHA is already in its cache.
- scripts/dev-safe.mjs: replace PUBLIC_SKILLS_PATH spread in buildAgentServerEnv
with a simple EXTENSIONS_REF injection; remove cloneExtensionsForSkillsCache
function, EXTENSIONS_REPO const, and the main() clone call; remove the
spawnSync and rmSync imports that were only needed for cloning
- scripts/dev-with-automation.mjs: remove cloneExtensionsForSkillsCache import
and the clone call from startAgentServer()
- docker/Dockerfile: remove the extensions-cache build stage entirely; keep the
CONFIG_EXTENSIONS_REF line in config-gen (still needed for entrypoint.sh)
- docker/entrypoint.sh: replace the cp-based seeding block with a single
export EXTENSIONS_REF="${EXTENSIONS_REF:-${CONFIG_EXTENSIONS_REF:-}}"
- AGENTS.md: replace inaccurate OH_PUBLIC_SKILLS_PATH bullet with an accurate
description of the EXTENSIONS_REF flow
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>