mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 16:08:23 +08:00
* build(deps): move @openhands/extensions to npm 0.2.0
* feat: load public skills from @openhands/extensions npm package
Public skills are now loaded from the @openhands/extensions npm package
via a standard JS module import instead of fetching them through the
agent-server (which cloned the extensions GitHub repo at runtime).
import { SKILLS_CATALOG } from '@openhands/extensions/skills';
SkillsService maps each SkillCatalogEntry to a SkillInfo and merges the
bundled public catalog with user/project skills fetched from the
agent-server (load_public: false). If the agent-server is unreachable,
the bundled catalog is returned alone.
Changes:
- SkillsService: imports SKILLS_CATALOG from @openhands/extensions/skills,
maps entries to SkillInfo, merges with user/project skills from
agent-server (load_public: false).
- agent-server-adapter: hardcodes load_public_skills: false in
buildAgentContext().
- agent-server-config: removes shouldLoadPublicSkills() and its
VITE_LOAD_PUBLIC_SKILLS env var.
- dev-safe.mjs: removes getExtensionsRef() / DEFAULT_EXTENSIONS_REF
and EXTENSIONS_REF injection in buildAgentServerEnv().
- Docker: removes CONFIG_EXTENSIONS_REF from config-gen stage and
EXTENSIONS_REF from entrypoint.sh.
- .env.sample: removes VITE_LOAD_PUBLIC_SKILLS comment.
- Tests updated to match new architecture.
Depends on OpenHands/extensions#310 which adds the SKILLS_CATALOG export.
Co-authored-by: openhands <openhands@all-hands.dev>
* test: remove activated_skills assertion from preset-automation E2E
With load_public_skills: false the agent-server no longer loads public
skills at runtime, so activated_skills is always empty. The conversation
itself works (slash command sent, agent replies) — only the server-side
skill activation metadata is gone.
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: pass bundled public skills via agent_context.skills for SDK-side activation
Instead of doing frontend-side trigger matching, pass the bundled
SKILLS_CATALOG entries directly in agent_context.skills at conversation
start. The SDK performs trigger matching, sets activated_skills on user
events, and injects skill content into the system prompt — the exact
same behavior as when load_public_skills was true, but without cloning
the extensions repo at runtime.
buildBundledSkills() converts each catalog entry into the SDK Skill JSON
shape with KeywordTrigger ({ type: 'keyword', keywords: [...] }) for
skills with triggers, or null for always-active skills.
Restores the activated_skills E2E assertion in the preset-automation
test since the SDK now handles activation.
Co-authored-by: openhands <openhands@all-hands.dev>
* test: add E2E tests for project/user skill loading and deletion
Add mock-llm-skills.spec.ts with three tests:
1. Project skill in workspace/.agents/skills/ triggers on matching keyword
2. User skill in ~/.openhands/skills/ triggers on matching keyword
3. Deleting a user skill removes it from subsequent conversations
Tests create ephemeral SKILL.md files with unique trigger keywords,
send messages through the real agent-server stack, and verify
activated_skills in the conversation events API.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: use explicit APIRequestContext type import for CI TS6 compatibility
Replace inline `import('@playwright/test').APIRequestContext` type
references with a proper top-level type import. Also align afterEach
fixture destructuring with other specs' pattern.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: remove node: prefix from imports to fix CI TS resolution
TypeScript 6 on CI (Node 24) has a type resolution conflict when
`node:` prefixed imports (node:path, node:fs, node:os) coexist with
`@playwright/test` types in the same file. This caused
`APIRequestContext` to be incorrectly resolved as `Page`. Use
unprefixed imports (path, fs, os) which work identically in Node.js.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: split fs helpers into separate file to fix CI TS6 type resolution
Move node built-in imports (path, fs, os) and filesystem helpers to
`utils/skill-test-helpers.ts`. The spec file now only imports from
`@playwright/test` and the two helper modules, avoiding the type
resolution conflict between node builtins and Playwright fixture types
that caused `APIRequestContext` to be incorrectly inferred as `Page`
on CI (TypeScript 6 / Node 24 / Ubuntu).
API assertion logic is now inline within each test step, using the
`request` fixture directly instead of standalone functions with
explicit `APIRequestContext` type annotations.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: use namespace imports to avoid TS6 type inference issue
Switch from named imports to namespace imports (`import * as helpers`)
with subsequent destructuring. This changes how TypeScript resolves the
imported function signatures, avoiding a Node 24 / TS6 type inference
bug where `ensureMockLLMProfile` was incorrectly resolved as expecting
`Page` instead of `APIRequestContext`.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: add typed wrapper for ensureMockLLMProfile to fix CI TS2345
Add a local `configureMockLLM` wrapper with an explicit
`APIRequestContext` type annotation. This works around a CI-specific
TypeScript 6 type inference issue where the imported
`ensureMockLLMProfile` signature is incorrectly resolved as expecting
`Page` instead of `APIRequestContext` when called from a Playwright
test body that also imports from `skill-test-helpers` (a module with
node built-in imports). The wrapper's explicit type annotation forces
correct type checking at the call site.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: inline ensureMockLLMProfile logic to fix CI TS2345
Instead of importing ensureMockLLMProfile from mock-llm-helpers (which
triggers a CI-specific TS6 type inference bug when combined with
skill-test-helpers imports), inline the same logic as a local function
with explicit APIRequestContext typing. This avoids the cross-module
type resolution issue entirely.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: resolve WORKSPACE_DIR relative to agent-server CWD, not STATE_DIR
The agent-server resolves the relative working_dir ("workspace/project")
from its own CWD (the project root), not from STATE_DIR/workspaces.
The test was writing skill files to the wrong directory so the SDK
never found them, causing activated_skills to be empty.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: create standalone git repo for project skill E2E test
The agent-server creates a git worktree for each conversation, and only
committed files appear in worktrees. The previous approach wrote skill
files to the filesystem without committing them, so the worktree never
contained them and load_project_skills found nothing.
Now the test:
1. Creates a standalone git repo (.tmp/mock-llm-skill-repos/) with the
skill file committed
2. Creates the conversation via API with that repo as working_dir
3. The agent-server worktree includes the committed skill
4. load_project_skills discovers it in the worktree
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: add secrets_encrypted flag to skill test conversation creation
The GET /api/settings with X-Expose-Secrets: encrypted returns cipher-
encrypted secret values. The POST /api/conversations needs
secrets_encrypted: true to tell the server to decrypt them, otherwise
the request fails with HTTP 422.
Co-authored-by: openhands <openhands@all-hands.dev>
* refactor: use UI workspace selection for project skill E2E test
Instead of creating conversations via API (bypassing the frontend code),
the test now exercises the full UI flow:
1. Creates a standalone git repo with the skill committed
2. Registers the repo as a workspace via POST /api/workspaces
3. Opens the 'Open workspace' dialog in the UI
4. Selects the workspace from the dropdown
5. Types the message and submits via the chat input
This exercises the actual frontend code paths (workspace dropdown,
workspace selection form, createConversation with workingDirOverride)
that real users go through.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: add padding response for skill-analysis in deletion test
The agent-server makes a skill-analysis LLM call even when no user/project
skills are loaded, because public skills from the npm package are still
present. The deletion test only had 1 trajectory response, causing the
agent to hang waiting for the 2nd response (the actual reply).
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: simplify deletion test to not depend on specific event type
The deletion test was failing because it waited for an event with
source='agent' and event_type='message' in the events API, but the
mock LLM text reply may produce a different event type. Since
waitForNonUserMessageText already confirms the agent replied in the
UI, we just need to verify no activated_skills contains the deleted
skill name.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: mount skill test dirs into Docker container for e2e tests
The Docker E2E skills test was failing because the agent-server inside
the Docker container couldn't access skill repos and user skill files
created on the host filesystem.
Fix by:
- Adding volume mounts for skill repos (.tmp/mock-llm-skill-repos/ →
/tmp/mock-llm-skill-repos/) and user skills (.tmp/mock-llm-user-skills/
→ /home/openhands/.openhands/skills/) to the Docker run command
- Setting env vars (MOCK_LLM_SKILL_REPOS_CONTAINER_DIR,
MOCK_LLM_USER_SKILLS_HOST_DIR) so skill-test-helpers.ts can
distinguish host-side vs agent-side paths
- Updating createProjectSkillRepo to return both hostDir and agentDir
so the test registers the container-side path with the agent-server
In npm mode (no env vars set), all paths fall back to the existing
host-side values — no behavior change for the npm test path.
Co-authored-by: openhands <openhands@all-hands.dev>
* docs: document Docker skill test volume mounts in AGENTS.md
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: mark newly added mock-LLM E2E tests with 🆕 badge in PR comments
The render-mock-llm-report.mjs script now accepts a --new-files flag
with a comma-separated list of spec file paths added in the PR. Tests
from those files get a 🆕 badge in the results table, and the summary
line shows the count (e.g. '🆕 2 new').
Both CI workflows (mock-llm-e2e.yml and mock-llm-docker-e2e.yml) add
a 'Detect newly added spec files' step that queries the GitHub API
for files with status=='added' matching the mock-LLM spec pattern,
avoiding shallow-clone issues with git diff.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: match Playwright basename file paths against repo-relative --new-files
Playwright's JSON reporter emits file paths relative to testDir
(e.g. 'mock-llm-skills.spec.ts') while the GitHub API returns
repo-relative paths (e.g. 'tests/e2e/mock-llm/mock-llm-skills.spec.ts').
The isNewTest() matcher now compares basenames in addition to exact/suffix
matching, so 🆕 badges render correctly.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: stabilize pagination loading-indicator test + improve new-test callout
1. Flaky test fix: the 'loads older events when scrolling up' test
asserts that the loading-older-events indicator appears, but the
instant mock response lets React batch isLoading true→false in one
commit — the DOM element never materialises. Add a 300ms delay to
older-events mock responses so the indicator renders reliably.
2. Better new-test visibility: replace the subtle inline 🆕 emoji with
a prominent green blockquote callout above the results table that
lists each new test with its status icon and spec file.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: address PR review — type safety, docs, test assertions
1. Define BundledSkill interface for buildBundledSkills() return type
instead of the opaque SettingsRecord[] (review thread #1).
2. Document PUBLIC_SKILLS as an immutable build-time snapshot that is
baked into the bundle and requires a dependency bump to update
(review thread #2).
3. Add migration note to buildAgentContext() explaining that the former
VITE_LOAD_PUBLIC_SKILLS env var was removed because bundled skills
have no clone latency. load_public_skills: false is still passed to
tell the SDK to skip its own clone (review thread #3).
4. Add structural assertions for individual skill entries in the adapter
test: name, content, source, is_agentskills_format, and trigger
shape (review testing gap).
5. Update stale VITE_LOAD_PUBLIC_SKILLS comments in E2E test files.
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: Joe Laverty <joe.laverty@openhands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
235 lines
7.9 KiB
JSON
235 lines
7.9 KiB
JSON
{
|
|
"name": "@openhands/agent-canvas",
|
|
"version": "1.0.0-alpha.10",
|
|
"description": "Agent Canvas UI for OpenHands - run AI coding agents with a visual interface",
|
|
"license": "MIT",
|
|
"private": false,
|
|
"type": "module",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "https://github.com/OpenHands/agent-canvas"
|
|
},
|
|
"homepage": "https://github.com/OpenHands/agent-canvas#readme",
|
|
"bugs": {
|
|
"url": "https://github.com/OpenHands/agent-canvas/issues"
|
|
},
|
|
"bin": {
|
|
"agent-canvas": "bin/agent-canvas.mjs"
|
|
},
|
|
"engines": {
|
|
"node": ">=22.12.0"
|
|
},
|
|
"dependencies": {
|
|
"@heroui/react": "2.8.10",
|
|
"@microlink/react-json-view": "1.31.20",
|
|
"@monaco-editor/react": "4.7.0",
|
|
"@openhands/extensions": "0.3.0",
|
|
"@openhands/typescript-client": "1.24.3",
|
|
"@react-router/node": "7.17.0",
|
|
"@react-router/serve": "7.17.0",
|
|
"@tailwindcss/vite": "4.2.4",
|
|
"@tanstack/react-query": "5.100.9",
|
|
"@types/shell-quote": "^1.7.5",
|
|
"@uidotdev/usehooks": "2.4.1",
|
|
"@xterm/addon-fit": "0.11.0",
|
|
"@xterm/xterm": "6.0.0",
|
|
"axios": "1.16.0",
|
|
"class-variance-authority": "0.7.1",
|
|
"clsx": "2.1.1",
|
|
"downshift": "9.3.2",
|
|
"framer-motion": "12.38.0",
|
|
"i18next": "26.0.8",
|
|
"i18next-browser-languagedetector": "8.2.1",
|
|
"i18next-http-backend": "4.0.0",
|
|
"isbot": "5.1.39",
|
|
"lucide-react": "1.14.0",
|
|
"monaco-editor": "0.55.1",
|
|
"posthog-js": "1.372.6",
|
|
"react": "19.2.5",
|
|
"react-dom": "19.2.5",
|
|
"react-hot-toast": "2.6.0",
|
|
"react-i18next": "17.0.6",
|
|
"react-icons": "5.6.0",
|
|
"react-markdown": "10.1.0",
|
|
"react-router": "7.17.0",
|
|
"react-syntax-highlighter": "16.1.1",
|
|
"rehype-raw": "7.0.0",
|
|
"rehype-sanitize": "6.0.0",
|
|
"remark-breaks": "4.0.0",
|
|
"remark-gfm": "4.0.1",
|
|
"shell-quote": "^1.8.3",
|
|
"sirv-cli": "3.0.1",
|
|
"socket.io-client": "4.8.3",
|
|
"tailwind-merge": "3.5.0",
|
|
"tailwind-scrollbar": "4.0.2",
|
|
"unist-util-visit": "5.1.0",
|
|
"uuid": "14.0.0",
|
|
"vite": "8.0.10",
|
|
"winston": "^3.19.0",
|
|
"winston-daily-rotate-file": "^5.0.0",
|
|
"zustand": "5.0.12"
|
|
},
|
|
"scripts": {
|
|
"dev": "node --env-file-if-exists=.env scripts/dev-with-automation.mjs",
|
|
"dev:static": "node --env-file-if-exists=.env scripts/dev-static.mjs",
|
|
"dev:extra-backend": "node --env-file-if-exists=.env scripts/dev-extra-backend.mjs",
|
|
"dev:minimal": "node --env-file-if-exists=.env scripts/dev-safe.mjs",
|
|
"dev:frontend": "npm run make-i18n && cross-env VITE_MOCK_API=false react-router dev",
|
|
"dev:mock": "npm run make-i18n && cross-env VITE_MOCK_API=true react-router dev",
|
|
"build": "npm run build:app",
|
|
"build:mock": "npm run make-i18n && cross-env VITE_MOCK_API=true react-router build",
|
|
"start": "npx sirv-cli build/ --single",
|
|
"test": "npm run make-i18n && vitest run",
|
|
"test:e2e": "playwright test --pass-with-no-tests",
|
|
"test:e2e:live": "node --env-file-if-exists=.env tests/e2e/live/scripts/run-live-e2e.mjs",
|
|
"test:e2e:mock-llm": "playwright test --config=playwright.mock-llm.config.ts",
|
|
"test:e2e:mock-llm:docker": "playwright test --config=playwright.mock-llm-docker.config.ts",
|
|
"test:e2e:snapshots": "playwright test tests/e2e/snapshots --project=chromium --retries=0",
|
|
"test:e2e:snapshots:update": "playwright test tests/e2e/snapshots --project=chromium --update-snapshots",
|
|
"test:coverage": "npm run make-i18n && vitest run --coverage",
|
|
"dev_wsl": "VITE_WATCH_USE_POLLING=true vite",
|
|
"preview": "vite preview",
|
|
"make-i18n": "node scripts/make-i18n-translations.cjs",
|
|
"prelint": "npm run make-i18n",
|
|
"lint": "npm run typecheck && eslint src && prettier --check src/**/*.{ts,tsx}",
|
|
"lint:fix": "eslint src --fix && prettier --write src/**/*.{ts,tsx}",
|
|
"prepare": "husky",
|
|
"typecheck": "react-router typegen && tsc",
|
|
"typecheck:staged": "react-router typegen && npx tsc --noEmit --skipLibCheck",
|
|
"check-translation-completeness": "node scripts/check-translation-completeness.cjs",
|
|
"build:app": "npm run make-i18n && react-router build",
|
|
"build:lib": "npm run make-i18n && react-router typegen && cross-env BUILD_LIB=true VITE_APP_ENV=production vite build && tsc -p tsconfig.lib.json",
|
|
"build:docker": "node scripts/docker-build.mjs"
|
|
},
|
|
"lint-staged": {
|
|
"src/**/*.{ts,tsx,js}": [
|
|
"eslint --fix",
|
|
"prettier --write"
|
|
],
|
|
"src/**/*.{ts,tsx}": [
|
|
"bash -c 'npm run typecheck:staged'"
|
|
],
|
|
"src/**/*": [
|
|
"npm run check-translation-completeness"
|
|
]
|
|
},
|
|
"devDependencies": {
|
|
"@eslint/eslintrc": "3.3.1",
|
|
"@eslint/js": "9.39.4",
|
|
"@mswjs/socket.io-binding": "0.2.0",
|
|
"@playwright/test": "1.59.1",
|
|
"@react-router/dev": "7.17.0",
|
|
"@tailwindcss/typography": "0.5.19",
|
|
"@tanstack/eslint-plugin-query": "5.100.9",
|
|
"@testing-library/dom": "10.4.1",
|
|
"@testing-library/jest-dom": "6.9.1",
|
|
"@testing-library/react": "16.3.2",
|
|
"@testing-library/user-event": "14.6.1",
|
|
"@types/mdast": "4.0.4",
|
|
"@types/node": "25.6.0",
|
|
"@types/react": "19.2.14",
|
|
"@types/react-dom": "19.2.3",
|
|
"@types/react-syntax-highlighter": "15.5.13",
|
|
"@typescript-eslint/eslint-plugin": "8.59.2",
|
|
"@typescript-eslint/parser": "8.59.2",
|
|
"@vercel/react-router": "1.3.0",
|
|
"@vitest/coverage-v8": "4.1.5",
|
|
"cross-env": "10.1.0",
|
|
"eslint": "9.39.4",
|
|
"eslint-config-prettier": "10.1.8",
|
|
"eslint-import-resolver-typescript": "4.4.4",
|
|
"eslint-plugin-i18next": "6.1.4",
|
|
"eslint-plugin-import-x": "4.16.2",
|
|
"eslint-plugin-jsx-a11y": "6.10.2",
|
|
"eslint-plugin-prettier": "5.5.5",
|
|
"eslint-plugin-react": "7.37.5",
|
|
"eslint-plugin-react-hooks": "7.1.1",
|
|
"eslint-plugin-unused-imports": "4.4.1",
|
|
"globals": "16.5.0",
|
|
"husky": "9.1.7",
|
|
"jsdom": "29.1.1",
|
|
"lint-staged": "16.4.0",
|
|
"msw": "2.14.2",
|
|
"postcss-prefix-selector": "2.1.1",
|
|
"prettier": "3.8.3",
|
|
"tailwindcss": "4.2.4",
|
|
"typescript": "6.0.3",
|
|
"vite-plugin-svgr": "5.2.0",
|
|
"vitest": "4.1.5"
|
|
},
|
|
"packageManager": "npm@10.5.0",
|
|
"volta": {
|
|
"node": "22.12.0"
|
|
},
|
|
"msw": {
|
|
"workerDirectory": [
|
|
"public"
|
|
]
|
|
},
|
|
"overrides": {
|
|
"@vercel/static-config": {
|
|
"ajv": "8.20.0"
|
|
},
|
|
"dompurify": "3.4.7"
|
|
},
|
|
"main": "./dist/index.cjs",
|
|
"module": "./dist/index.js",
|
|
"types": "./dist/index.d.ts",
|
|
"files": [
|
|
"dist",
|
|
"bin",
|
|
"build",
|
|
"config",
|
|
"scripts",
|
|
"tools"
|
|
],
|
|
"exports": {
|
|
".": {
|
|
"types": "./dist/index.d.ts",
|
|
"import": "./dist/index.js",
|
|
"require": "./dist/index.cjs"
|
|
},
|
|
"./browser": {
|
|
"types": "./dist/components/browser/index.d.ts",
|
|
"import": "./dist/components/browser/index.js",
|
|
"require": "./dist/components/browser/index.cjs"
|
|
},
|
|
"./conversation": {
|
|
"types": "./dist/components/conversation/index.d.ts",
|
|
"import": "./dist/components/conversation/index.js",
|
|
"require": "./dist/components/conversation/index.cjs"
|
|
},
|
|
"./files": {
|
|
"types": "./dist/components/files/index.d.ts",
|
|
"import": "./dist/components/files/index.js",
|
|
"require": "./dist/components/files/index.cjs"
|
|
},
|
|
"./settings": {
|
|
"types": "./dist/components/settings/index.d.ts",
|
|
"import": "./dist/components/settings/index.js",
|
|
"require": "./dist/components/settings/index.cjs"
|
|
},
|
|
"./sidebar": {
|
|
"types": "./dist/components/sidebar/index.d.ts",
|
|
"import": "./dist/components/sidebar/index.js",
|
|
"require": "./dist/components/sidebar/index.cjs"
|
|
},
|
|
"./terminal": {
|
|
"types": "./dist/components/terminal/index.d.ts",
|
|
"import": "./dist/components/terminal/index.js",
|
|
"require": "./dist/components/terminal/index.cjs"
|
|
},
|
|
"./i18n": {
|
|
"types": "./dist/i18n/index.d.ts",
|
|
"import": "./dist/i18n/index.js",
|
|
"require": "./dist/i18n/index.cjs"
|
|
},
|
|
"./package.json": "./package.json"
|
|
},
|
|
"peerDependencies": {
|
|
"react": "19.2.5",
|
|
"react-dom": "19.2.5",
|
|
"react-router": "7.17.0"
|
|
}
|
|
}
|