* Render ACP sub-agent tool-call events in chat (#132)
Port of upstream OpenHands frontend PRs:
- OpenHands#13994 — initial ACPToolCallEvent rendering
- OpenHands#14246 — drop the 'ACP · ' prefix from titles
- OpenHands#14247 — suppress in_progress events to avoid empty-args flash
Adds end-to-end support for the new V1 ACPToolCallEvent surfaced by ACP
sub-agents (Claude Code, Codex, Gemini CLI, …). Each tool call is rendered
through the same GenericEventMessage wrapper used for observation events,
so the resulting card shape, success indicator, and markdown formatting
match the rest of the OpenHands chat.
Highlights:
- New ACPToolCallEvent type + isACPToolCallEvent type guard, wired into
the OpenHandsEvent union.
- getACPToolCallContent + getACPToolCallTitleKey helpers — execute calls
render as Command:/Output: blocks just like getTerminalObservationContent;
non-execute calls render their raw_input as a JSON Input: block. Errors
use **Error:**, missing output falls back to OBSERVATION$COMMAND_NO_OUTPUT,
and very long output is truncated to MAX_CONTENT_LENGTH.
- getACPToolCallResult — maps status + is_error to success | error |
undefined, mirroring getObservationResult so in_progress calls render
without a check mark.
- shouldRenderEvent — hides in_progress ACP events to avoid an empty-args
card flashing before the first populated event arrives (matches #14247).
- handleEventForUI — dedupes ACP events by tool_call_id so streaming
in_progress → completed/failed transitions update the card in place
instead of stacking duplicate cards.
- EventMessage dispatch — routes ACP events to GenericEventMessageWrapper
after hook execution events, before generic action handling.
- Five new ACTION_MESSAGE$ACP_* i18n keys (RUN / EDIT / READ / FETCH /
TOOL) localised across all bundled languages, with the title rendered
inline via the existing <cmd> Trans component (no 'ACP · ' prefix per
#14246).
Tests: 39 new/modified vitest cases across get-acp-tool-call-content,
should-render-event, handle-event-for-ui, and event-message-acp-tool-call.
Visual verification + demo GIF: .pr/issue-132/
This commit was created by an AI agent (OpenHands) on behalf of the user.
Co-authored-by: openhands <openhands@all-hands.dev>
* Capture real ACP card rendering for #142 demo
Addresses @neubig's review feedback on PR #142 ('I did not see any
examples of ACP events being rendered in the interface'):
- Updates demo.gif to actually show three ACPToolCallEvent cards
rendering in the live conversation UI (collapsed view + expanded
view with Command/Output/Input blocks).
- Adds 04-acp-cards-collapsed.png + 05-acp-cards-expanded.png as the
individual frames, plus 04-acp-rendering-notes.md explaining what
each frame shows and why a synthetic event injection was needed
(no Claude Code / Codex / Gemini CLI binary in the sandbox).
- Adds capture.mjs (the Playwright script used to record the demo) so
future re-records are reproducible.
- In src/stores/use-event-store.ts, exposes the existing Zustand
store on window.__OH_EVENT_STORE__ when import.meta.env.DEV is
true. Tree-shaken from production builds; no behaviour change for
end users. Useful as a general dev affordance for fixture/preview
tooling beyond this PR.
Functional verification (unchanged from the original PR):
npx vitest run __tests__/components/v1/chat/event-content-helpers/get-acp-tool-call-content.test.ts __tests__/components/v1/chat/event-message-acp-tool-call.test.tsx
Test Files 2 passed (2)
Tests 20 passed (20)
This commit was created by an AI agent (OpenHands) on behalf of the user.
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: seed automation API key into agent-server secrets
- Add seedAutomationSecret() that calls PUT /api/settings/secrets after
agent-server is ready, storing the automation API key as
OPENHANDS_AUTOMATION_API_KEY
- This makes the key available to agents during conversations so they can
authenticate with the automation backend
- Add sessionApiKey to config for optional auth header
- Update help text and documentation
Co-authored-by: openhands <openhands@all-hands.dev>
* test: add tests for seed automation secret and fix CI failure
- Add tests for localApiKey and sessionApiKey config in buildConfig
- Add tests for secrets documentation in help output
- Fix root-layout-refetch.test.tsx unhandled rejection from framer-motion
by adding async cleanup with microtask flush
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: detect SESSION_API_KEY when seeding automation secret
The seedAutomationSecret() function was failing with 401 Unauthorized
because it wasn't detecting the SESSION_API_KEY environment variable
that the agent-server uses by default (V0 config).
The agent-server checks these env vars for session API keys:
- SESSION_API_KEY (V0 config, picked up by default factory)
- OH_SESSION_API_KEYS_0 (V1 config)
The original code only checked OH_SESSION_API_KEY and VITE_SESSION_API_KEY,
missing the actual env vars the server reads. In OpenHands Cloud
environments, SESSION_API_KEY is set automatically, causing the 401.
This fix adds SESSION_API_KEY and OH_SESSION_API_KEYS_0 to the
fallback chain, with SESSION_API_KEY taking highest precedence
since it matches the agent-server's default behavior.
Adds tests verifying:
- SESSION_API_KEY detection
- OH_SESSION_API_KEYS_0 detection
- Precedence order (SESSION_API_KEY > OH_SESSION_API_KEYS_0 > others)
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: add retry logic and longer timeout for secret seeding
On slower systems, the agent-server may take longer to start up,
causing the secret seeding to fail with 'fetch failed' errors.
This fix adds:
1. Increased initial wait timeout from 30s to 60s for agent-server startup
2. Retry logic in seedAutomationSecret (5 retries with 2s delay)
3. Better error logging showing elapsed time and last error
4. AbortSignal.timeout on fetch requests to avoid hanging
5. Skip seeding if server fails to start (with warning message)
The retry logic handles transient failures during server warmup
but immediately fails on 401/403 auth errors (no point retrying).
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
This repo typically has large PRs spanning multiple files, so enabling
sub-agent delegation lets the review bot fan out file-level reviews to
dedicated sub-agents for better coverage.
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: add automations frontend on /automations subpath
Port automations frontend from automation-repo to agent-canvas.
## Changes
### New Routes
- /automations - List view of all automations
- /automations/:automationId - Automation detail view
### New Components
- Automation list components: card, card-skeleton, group, empty-state, error-state
- Automation detail components: header, sections (config, prompt, plugins, activity)
- Shared UI components: toggle-switch, metadata-chip, status-badge, kebab-menu, search-input
### API Integration
- automation-service.api.ts - API client for automation CRUD operations
- Uses existing openHands axios client (shared base URL with agent server)
### MSW Mock Server Handlers
- automation-handlers.ts - Mock handlers for testing
- automations.mock.ts - Sample automation data
- automation-runs.mock.ts - Sample automation run data
### Tests
- API tests: automation-service.test.ts, automation-handlers.test.ts
- Component tests: toggle-switch, metadata-chip, search-input, error-state
- Detail component tests: section-card, run-status-badge, not-found-state
### Hooks
- use-automations.ts - React Query hook for fetching automations list
- use-automation-detail.ts - React Query hook for fetching single automation
- use-has-permission.ts - Permission checking utility hook
### Types
- automation.ts - TypeScript types for automation entities
### Icons
- Added SVG icons: activity, bell, calendar, check-circle, chevron-down,
chevron-left, clock, cog, database, exclamation-circle, git-branch,
kebab-vertical, power, puzzle, search, sparkle, target, trash, x-circle, x-mark
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: add local API key auth for automation backend
- Use VITE_AUTOMATION_API_KEY env var for frontend to authenticate
- Pass AUTOMATION_LOCAL_API_KEY to automation backend in dev mode
- Use dedicated axios instance with Bearer auth interceptor
- Add --refresh to uvx to ensure latest git commits are fetched
- URL-encode automation IDs in API paths
The default local API key is 'openhands-local-api-key' which matches
between the frontend and backend for local development.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: automation service tests and ingress port conflicts
- Fix automation-service.test.ts to mock axios instance correctly
(was mocking openHands but service uses automationAxios)
- Use vi.hoisted() for mock functions available during vi.mock hoisting
- Change ingress test ports from 19000-19003 to 29000-29003 to avoid
conflict with VS Code server on port 19000
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: add CORS origins for automation backend in dev mode
The automation backend defaults CORS origins to app.all-hands.dev,
which blocks localhost requests. Add localhost origins for the
ingress port and Vite dev server port.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: update create-instructions styling and add missing i18n keys
- Use semantic color tokens (text-content, text-basic, bg-base-secondary,
bg-base, border-default) instead of hardcoded neutral-* colors
- Add all AUTOMATIONS$ i18n keys for the automations frontend
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
* feat: track install immediately without consent, add proxy support
BREAKING CHANGE: Install event (canvas_install) is now sent immediately
on first use, regardless of consent status. Users can still opt out via
VITE_DO_NOT_TRACK=1 or browser's Do Not Track setting.
Changes:
- trackInstall() sends the install event immediately without waiting for consent
- trackFirstUse() is now deprecated, calls trackInstall() for backward compat
- Session/custom events still require user consent
- Add VITE_POSTHOG_UI_HOST env var for reverse proxy support
- PostHog initialization now includes ui_host configuration
This change allows tracking library adoption even if users haven't made
a consent choice yet, while still respecting hard opt-outs.
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: default PostHog host to z.openhands.dev proxy
Use OpenHands' managed reverse proxy by default to bypass ad blockers.
The proxy at z.openhands.dev routes telemetry to PostHog's US region.
Library consumers can still override with VITE_POSTHOG_HOST if needed.
Co-authored-by: openhands <openhands@all-hands.dev>
* chore: remove deprecated trackFirstUse function
Only trackInstall() is now exported. No backwards compatibility shim needed.
Co-authored-by: openhands <openhands@all-hands.dev>
* docs: add privacy/GDPR compliance notes to install tracking
Address review feedback by documenting the privacy implications and
GDPR legal basis (legitimate interest under Article 6(1)(f)) for
sending the anonymous install event before consent.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: wait for translations before showing consent modal
- Use useTranslation's 'ready' state to wait for translations to load
- Add small delay (50ms) to ensure DOM is fully hydrated
- Prevents translation keys from flashing on first appearance
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: multi-backend support with cloud SaaS proxy routing
* feat: route conversation export through cloud proxy on cloud backends
* fix: route conversation delete through cloud proxy on cloud backends
* fix: forward settings diffs verbatim through cloud proxy save
* fix: surface cloud-aware settings sub-pages and gate local-only routes
* fix: route secrets settings through cloud proxy on cloud backends
* fix: route conversation stop runtime through cloud proxy on cloud backends
* fix: re-expose planning agent UI for cloud backends and route plan file reads through cloud proxy
* fix: route Display Cost runtime fetch through cloud proxy and ungate local metrics without session API key
* fix: handle WAITING_FOR_SANDBOX task status from cloud backends to prevent UI crash
* fix: re-expose Public Share in conversation menu for cloud backends
* fix: redirect to home when switching backends from a conversation page
* fix: hide cloud orgs the API key can't access in backend selector
* feat: support running multiple local agent-servers with shared persistence
* fix: lint
* fix: failing tests
Switch the markdown code block component from `Prism` (which pulls in
all ~300 refractor grammars, including curiosities like brainfuck) to
`PrismLight` with an explicit allowlist of ~58 commonly-used languages.
Each grammar's built-in `aliases` array (e.g. `js`, `ts`, `py`,
`html`) is auto-wired, plus a small `EXTRA_ALIASES` map covers
aliases the prism modules don't declare themselves (`terraform`/`tf`
for hcl, `c++`/`cxx` for cpp, `rs` for rust, `ml` for ocaml,
`ps1` for powershell, etc.).
Unrecognized languages still render correctly: react-syntax-highlighter
silently falls back to plain text when a grammar isn't registered.
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
* feat: add first-use telemetry tracking with consent
- Add telemetry service with consent management (src/services/telemetry.ts)
- Add useTelemetry React hook for easy integration (src/hooks/use-telemetry.ts)
- Add TelemetryConsentBanner component with i18n support
- Add local development server for testing (scripts/telemetry-dev-server.mjs)
- Add comprehensive tests for telemetry service and hook
- Export telemetry utilities from library index
- Respect DO_NOT_TRACK environment variable for privacy
- Uses localhost:8080 endpoint for development
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: address PR review feedback
- Make TELEMETRY_ENDPOINT configurable via VITE_TELEMETRY_ENDPOINT env var
- Make POSTHOG_API_KEY configurable via VITE_POSTHOG_API_KEY env var
- Add validation to skip telemetry if API key not configured (except localhost)
- Fix DO_NOT_TRACK to work in browser environments using VITE_DO_NOT_TRACK
- Also respect browser's navigator.doNotTrack standard
- Update consent banner hint text to reference correct env var
- Add documentation comments for all configuration options
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: hardcode PostHog credentials for centralized telemetry
- Use OpenHands PostHog project API key for all library users
- Use PostHog US Cloud endpoint (https://us.i.posthog.com/capture)
- Remove environment variable configuration for endpoint/API key
- Telemetry now automatically sends to centralized project when consent granted
- Users can still opt out via UI, VITE_DO_NOT_TRACK, or browser DNT setting
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: use separate PostHog API keys for dev and production
- Dev environment: phc_kBtz5nKmxVRRQ7HtPwr2QX9eMC5j65zE86QKocVNwb4U
- Production: phc_BgzfxKdgsYMLFTmJqt424ZoyVHvKFfrwttLimzdYTKFK
- Automatically selects key based on import.meta.env.DEV
Co-authored-by: openhands <openhands@all-hands.dev>
* chore: use single production PostHog API key everywhere
Simplify by using the same API key for all environments.
Co-authored-by: openhands <openhands@all-hands.dev>
* chore: rename telemetry events
- library_first_use → canvas_install
- library_session_start → canvas_new_session
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: migrate telemetry to PostHog SDK
Replace raw HTTP requests with PostHog SDK for:
- Automatic event batching
- Built-in retry logic with exponential backoff
- Offline support (queues events, sends when back online)
- Automatic session tracking
- Better device/browser info enrichment
Benefits:
- More reliable event delivery
- Reduced network requests
- Cleaner code with less manual state management
- Future-proof for feature flags, session replay, etc.
Co-authored-by: openhands <openhands@all-hands.dev>
* refactor: remove redundant hasTrackedFirstUse state in hook
The trackFirstUse() function already has built-in deduplication via
localStorage, so the local React state was unnecessary. Simplified
the hook and added a comment explaining the deduplication mechanism.
Co-authored-by: openhands <openhands@all-hands.dev>
* chore: remove obsolete telemetry dev server
The local dev server was used when telemetry used raw HTTP requests
to a configurable endpoint. Now that we use the PostHog SDK with
the real PostHog endpoint, this is no longer needed.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: remove trailing comma in package.json
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: address PR review feedback
- Make POSTHOG_API_KEY configurable via VITE_POSTHOG_API_KEY env var
- Make POSTHOG_HOST configurable via VITE_POSTHOG_HOST env var
- Add session deduplication using sessionStorage to prevent duplicate
canvas_new_session events from multiple hook instances
- Clear sessionStorage in clearTelemetryData()
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: use dynamic imports for PostHog SSR compatibility
- Convert top-level posthog-js import to dynamic import for SSR safety
- Add getPostHog() lazy loader that only imports in browser context
- Make setTelemetryConsent, clearTelemetryData, getPostHogInstance async
- Update documentation to clarify default telemetry destination
- Update tests for async function signatures
This ensures the library works correctly in SSR frameworks (Next.js, Remix,
etc.) that might import this module server-side.
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: add telemetry consent banner to app layout
The consent banner now appears on all pages until the user explicitly
accepts or declines telemetry. This ensures users are always prompted
for consent on their first visit regardless of which page they land on.
Co-authored-by: openhands <openhands@all-hands.dev>
* refactor: update telemetry consent banner to modal style
- Changed from bottom banner to centered modal overlay (matching OpenHands)
- Uses ModalBackdrop, ModalBody, BaseModalTitle, BaseModalDescription
- Single checkbox with 'Confirm preferences' button pattern
- Full-screen overlay blocks interaction until user makes a choice
- Added i18n keys: TELEMETRY$SEND_ANONYMOUS_DATA, TELEMETRY$CONFIRM_PREFERENCES
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: ensure PostHog is initialized before tracking events
- Made grantConsent/denyConsent in useTelemetry hook async to ensure
PostHog initialization completes before state update triggers tracking
- Updated tests for async consent functions
- This fixes a race condition where trackFirstUse() could be called before
PostHog's opt_in_capturing() had been executed
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
Ports the i18n half of upstream OpenHands frontend PR #14286
('fix(i18n): translate hardcoded settings strings'):
- app-settings.tsx: replaced the hardcoded
placeholder='Username for git commits' and
placeholder='Email for git commits' strings on the Git Settings
inputs with t(I18nKey.SETTINGS$GIT_USERNAME_PLACEHOLDER) and
t(I18nKey.SETTINGS$GIT_EMAIL_PLACEHOLDER).
- mcp-server-form.tsx: replaced the hardcoded label='Timeout (seconds)'
on the SHTTP timeout input with t(I18nKey.SETTINGS$MCP_TIMEOUT_LABEL).
All three new keys ship with translations for every locale agent-canvas
bundles (en/ja/zh-CN/zh-TW/ko-KR/no/it/pt/es/ar/fr/tr/de/uk/ca), and
make-i18n regenerates declaration.ts with matching enum members.
The companion upstream PR #14291 — which drops the
ENABLE_SANDBOX_GROUPING() feature-flag gate around the
sandbox-grouping-strategy dropdown — is a no-op here: the
sandbox-grouping field, the dropdown, and the feature flag were all
already stripped from agent-canvas during the original OSS port, and
sandbox_grouping_strategy is not exposed by openhands-agent-server's
settings schemas (verified against 1.20.1). Per the issue's own
acceptance criteria, this PR therefore does not re-introduce the
control; see .pr/issue-134/visual-verification.md for details.
Visual verification + demo GIF: .pr/issue-134/
This commit was created by an AI agent (OpenHands) on behalf of the user.
Co-authored-by: openhands <openhands@all-hands.dev>
Group npm packages by feature area so related libraries land in a
single PR rather than as separate, racing updates. This avoids the
package-lock.json conflicts we saw when sequential PRs all touched
the same lockfile entries (e.g. tailwindcss + @tailwindcss/vite),
and reduces churn for users that watch the dependencies label.
Groups:
- tailwind, tanstack, i18next, react, react-router, testing,
eslint, monaco, xterm, types
High-impact packages that are not assigned to a group (vite,
framer-motion, axios, posthog-js, lucide-react, etc.) still get
their own PR so each can be reviewed and tested independently.
GitHub Actions updates are now also grouped into a single weekly PR.
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: add automation backend integration with standalone ingress proxy
- Add scripts/ingress.mjs: standalone HTTP reverse proxy for routing traffic
to multiple backends based on URL path prefix
- Add scripts/dev-with-automation.mjs: orchestrates full stack with
agent-server, automation backend (both via uvx), Vite, and ingress
- Make 'npm run dev' run full stack by default (was dev:safe, now dev:automation)
- Rename 'npm run dev:safe' to 'npm run dev:minimal' for agent-server + Vite only
- Update README with new quickstart showing full stack as default
- Update AGENTS.md with architecture documentation
Architecture:
http://localhost:8000 (Ingress)
├── /api/automation/* → Automation Backend (:18001)
├── /api/*, /sockets → Agent Server (:18000)
└── /* (default) → Vite Dev Server (:3001)
* test: add tests for ingress and dev-with-automation scripts
- Add __tests__/scripts/ingress.test.ts with 14 tests covering:
- CLI argument parsing (--help, --port, --route, --default)
- Route matching (exact, prefix, longest-match-first)
- Proxy functionality (forwarding, query params, error handling)
- 502 response when backend unavailable
- 503 response for unmatched routes with no default
- Add __tests__/scripts/dev-with-automation.test.ts with 19 tests covering:
- buildAutomationCommand() with various git refs/repos
- buildConfig() port and path configuration
- CLI --help output
- Graceful exit when uvx is missing
- Export testable functions from dev-with-automation.mjs
* fix: prevent dev-with-automation from auto-executing when imported
The script was calling main() unconditionally, which caused test failures
when vitest imported the module. Now check if the module is the main entry
point before executing.
---------
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
Port of OpenHands/OpenHands#14284. The LLM model badge in the conversation
header was constrained to max-w-[150px] with an inner `truncate`, which
cut off long model identifiers such as `litellm_proxy/claude-sonnet-4-5-20250929`
to `litellm_proxy/cl…`. Drop the width cap and inner truncate, and apply
`whitespace-nowrap` to the outer span so the full name renders inline.
Also adds scripts/record-demo.mjs - a small playwright recorder used to
capture the verification GIF under .pr/issue-135/ - and updates the
existing test to assert the un-truncated structure.
Closes#135.
Co-authored-by: openhands <openhands@all-hands.dev>
Mirrors OpenHands/OpenHands#14011: introduces a shared
SETTINGS_QUERY_KEYS helper as the source of truth for settings query
keys, migrates existing call sites to use it, and adds an ESLint rule
that rejects raw queryKey: ["settings", ...] arrays outside the helper
module. Keeps the same shape of guardrail to prevent settings cache
invalidation bugs from drifting back in.
Co-authored-by: openhands <openhands@all-hands.dev>
The openhands-agent-server package exposes an executable named
'agent-server', not 'openhands-agent-server'. When using PyPI versions
(either specific or latest), we need to use the --from syntax:
uvx --from openhands-agent-server agent-server
This fixes the error:
An executable named 'openhands-agent-server' is not provided by
package 'openhands-agent-server'.
Use 'uvx --from openhands-agent-server agent-server' instead.
Fixes#117
Co-authored-by: openhands <openhands@all-hands.dev>
Add a new exported function that builds the environment variables object
for spawning the agent-server process. This allows downstream consumers
(e.g., the automation service) to use the same env vars without
duplicating the mapping logic.
When new env vars are added or existing ones are renamed, downstream
consumers will automatically inherit the changes by using this helper.
Refactored main() to use the new helper internally.
Closes#118
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: use agent server APIs for settings persistence
- Replace localStorage with HTTP API for settings storage
- Use `X-Expose-Secrets: encrypted` header for GET /api/settings
to receive encrypted secrets (not exposing raw values)
- Use `secrets_encrypted: true` in start conversation payload
- Add `getSettingsForConversation()` to build encrypted settings
payload for conversation start endpoint
- Update secrets service to use /api/settings/secrets endpoints
- Add mock handlers for settings and secrets API endpoints
- Update tests for new API-based settings flow
This integrates with software-agent-sdk PR #3060
(feat/encrypted-secrets-in-transit) which adds server-side
encryption support for secrets in transit.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: update test mocks for encrypted settings API and add OH_SECRET_KEY support
- Update use-create-conversation-metadata.test.ts to mock getSettingsForConversation()
which is now called by buildStartConversationRequestWithEncryptedSettings
- Skip flaky onOpen websocket test that times out intermittently in CI
- Add OH_SECRET_KEY environment variable support in dev-safe.mjs:
- Uses default key for local development
- Can be overridden via OH_SECRET_KEY environment variable
- Logs secret key source at startup
Co-authored-by: openhands <openhands@all-hands.dev>
* docs: update AGENTS.md for settings API and OH_SECRET_KEY
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: update secrets service to use agent-server API routes
Changes:
- Update SecretsService to use /api/settings/secrets endpoints instead of /api/v1/secrets
- Simplify secrets-service.types.ts to remove unused pagination types
- Update use-get-secrets hook to do client-side filtering (agent-server doesn't support pagination)
- Update mock handlers to only use agent-server API routes
- Update secrets-settings test to mock getSecrets instead of searchSecrets
- Remove pageSize option from useSearchSecrets since agent-server doesn't paginate
The agent-server API routes (per SDK PR #3060):
- GET /api/settings/secrets - List secrets (names/descriptions only)
- GET /api/settings/secrets/{name} - Get secret value
- PUT /api/settings/secrets - Upsert secret
- DELETE /api/settings/secrets/{name} - Delete secret
Co-authored-by: openhands <openhands@all-hands.dev>
* docs: update AGENTS.md for secrets API routes
- Document the agent-server secrets CRUD routes in MSW handlers list
- Update git provider token persistence note to reflect server-side storage
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: update secret name validation to match agent-server requirements
- Change pattern from '^\S*$' (no whitespace) to '^[a-zA-Z][a-zA-Z0-9_]{0,63}$'
- Add title prop to SettingsInput component for validation error messages
- Secret names must: start with letter, contain only letters/numbers/underscores, be 1-64 chars
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: include custom secrets in conversation requests via LookupSecret
Custom secrets configured in Settings > Secrets are now automatically
included in conversation start requests. Instead of exposing secret values
to the frontend, we use LookupSecret entries that point to the agent-server
endpoint /api/settings/secrets/{name}. The agent-server fetches the actual
values at runtime.
Changes:
- Add LookupSecret interface to agent-server-adapter.ts
- Add customSecrets option to StartConversationOptions
- Build LookupSecret entries for each custom secret in buildStartConversationRequest
- Update buildStartConversationRequestWithEncryptedSettings to fetch and include
custom secrets list from SecretsService.getSecrets()
- Include X-Session-API-Key header in LookupSecret when configured
This ensures secrets never touch the frontend in plaintext while still
making them available to conversations.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: address review comments - no localStorage fallback, retry logic, SDK docs
Review feedback addressed:
1. secrets-service.ts: Server storage MUST succeed before updating localStorage
- addGitProvider now stores to server FIRST, only updates localStorage on success
- createSecret/updateSecret/deleteSecret now throw on failure (no silent returns)
- Added retry logic with exponential backoff for all API calls
2. settings-service.api.ts: No silent fallback for encrypted settings
- getSettingsForConversation now throws if encrypted fetch fails
- Conversations should not start with broken/redacted credentials
- Added retry logic with exponential backoff
3. AGENTS.md: Document SDK dependency
- Settings persistence APIs require SDK PR #3060
- Until released, npm run dev defaults to main branch
- Documented git provider storage design (server + localStorage)
4. dev-safe.mjs: Default to SDK main branch
- Added DEFAULT_GIT_REF='main' constant
- npm run dev now uses main until settings APIs are released
- TODO comment to update once released
Note: Git provider tokens still use localStorage for frontend git API calls
(repo search, branches), but MUST succeed on server first.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: update server secret when only host changes
When updating just the host (empty token), the server secret's description
must also be updated to keep metadata in sync. Previously, only localStorage
was updated, violating the 'server storage must succeed first' principle.
Now the host-only update path also calls createSecret() to update the
server secret's description before updating localStorage.
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
* feat: use uvx for temporary agent-server installation in dev mode
- Replace direct agent-server CLI invocation with uvx temporary install
- Add OH_AGENT_SERVER_VERSION env var for specific PyPI versions
- Add OH_AGENT_SERVER_GIT_REF env var for git commits/branches
- Auto-install uv in .openhands/setup.sh if not present
- Update documentation (README, DEVELOPMENT.md, AGENTS.md)
- Add comprehensive tests for buildAgentServerCommand()
This removes the requirement to permanently install agent-server via
'uv tool install'. Users only need uv installed, and npm run dev will
automatically download and run the appropriate agent-server version.
Co-authored-by: openhands <openhands@all-hands.dev>
* fix: use subdirectory syntax for git ref in uvx monorepo
The software-agent-sdk is a uv workspace monorepo with packages in
subdirectories (openhands-agent-server/, openhands-tools/, etc.).
When installing from git, uvx requires the #subdirectory= fragment to
specify which package to install from the workspace.
Tested with: OH_AGENT_SERVER_GIT_REF=main npm run dev
Co-authored-by: openhands <openhands@all-hands.dev>
---------
Co-authored-by: openhands <openhands@all-hands.dev>
Restore the default foreground color on the themed AgentServerUIRoot wrapper so inherited text and currentColor icons match the pre-scoping dark theme again.
Co-authored-by: openhands <openhands@all-hands.dev>