21 Commits
Author SHA1 Message Date
f26d734a84 fix: Add --disable-telemetry runtime flag for self-hosted canvas (#16908)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-08-28 13:13:59 -07:00
Shashwat Pratap Singh 150e76046d feat: link chat file paths to the Files drawer (#16181) 2026-08-24 17:27:14 -04:00
Vasco Schiavo f6097bda53 chore: remove unreachable frontend modules and their tests (#16606) 2026-08-14 15:46:35 +00:00
Mayank Joshi 32e359c29b refactor: replace positional createConversation params with an options object (#1587) (#16500) 2026-08-10 19:32:37 -04:00
Hiep Le e882651e29 feat: add a typed agent action for launching local or Cloud child conversations (#16380) 2026-08-07 18:58:12 +07:00
bd248915a7 fix: identify PostHog users with Cloud identity (#16087)
Co-authored-by: neubig <neubig@users.noreply.github.com>
Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-30 12:27:10 -04:00
Rohit Malhotraandopenhands 7fa7b16968 fix: enable Agent Canvas pageview telemetry (#16169)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-29 12:26:04 -04:00
Rohit Malhotraandopenhands 22fe594133 feat: forward automation telemetry context (#1917)
* feat: forward telemetry context to automations

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: sync automation telemetry consent

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: default automation telemetry key in launchers

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: bake production telemetry defaults into npm package

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: dedupe automation consent sync

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump automation version to 1.3.0

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-24 05:47:26 +00:00
Rohit Malhotraandopenhands e45376f98e Add cloud org id to telemetry context (#1924)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-24 01:22:53 -04:00
Rohit Malhotraandopenhands f3f75d994a fix: keep PostHog identity stable for Cloud context (#1892)
* fix: keep PostHog identity stable for Cloud context

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: guard legacy PostHog identity cleanup

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: address PostHog cloud context review

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: dedupe telemetry email properties

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-23 14:10:47 +00:00
Rohit Malhotraandopenhands 4d0fc5c942 fix: add version context to telemetry (#1872)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-21 23:09:16 -04:00
Rohit Malhotraandopenhands efd20f7d56 fix: suppress telemetry consent prompt in Cloud Canvas (#1848)
* Suppress telemetry consent modal in Cloud Canvas

Treat same-origin locked Cloud cookie deployments as already consented for Canvas library telemetry so the modal does not flicker before the main app login flow.

Co-authored-by: openhands <openhands@all-hands.dev>

* Stabilize mock LLM settings tests after ACP

Reset the default agent profile back to OpenHands through the agent-profile API before LLM-profile setup paths that need /settings/llm. This prevents an ACP profile left by the previous serial spec from redirecting later settings tests to /settings/agents.

Co-authored-by: openhands <openhands@all-hands.dev>

* Stabilize files tab mock E2E git setup

Ensure the attached-workspace conversation has both an origin remote and a real HEAD commit before asserting that the Files tab defaults to diff view. The diff default now intentionally depends on both attached source metadata and an available commit base.

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix desktop right panel toggle visibility

Update the desktop right-panel toggle to set both the user-toggled flag and the visible state. The missing visibility update left the panel visually closed in mock E2E while off-screen tab controls remained mounted.

Co-authored-by: openhands <openhands@all-hands.dev>

* Make files tab mock E2E open the panel explicitly

Wait for the desktop panel toggle to report an open state before interacting with Files tab controls, then verify the Diff segment can be selected for the attached-workspace conversation.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-20 15:33:24 +00:00
Graham Neubigandneubig 3598bb14e3 fix: preserve Canvas analytics identity (#1839)
* fix: unify Canvas PostHog identity

* fix: preserve funnel events across backend transitions

* fix: preserve telemetry consent through cloud login

* fix: isolate Canvas telemetry from host PostHog

* fix: preserve telemetry during client startup

* fix: centralize Canvas telemetry ownership

* fix: make telemetry lifecycle atomic

---------

Co-authored-by: neubig <neubig@users.noreply.github.com>
2026-07-19 20:16:20 +01:00
Graham Neubig e9d54fb29f feat: instrument the Canvas Cloud funnel (#1828)
Instrument the consented Canvas-to-Cloud funnel and identify Canvas Cloud requests with coarse client metadata.
2026-07-18 00:22:23 +01:00
2b7ceea667 refactor: define canvas UI as an SDK client tool (#1797)
* refactor: define canvas UI as an SDK client tool

Send a JSON-defined canvas_ui_client tool on new, profile-based, and resumed conversation requests while retaining the legacy Python registration for persisted conversations. Normalize the new SDK event kinds to the existing Canvas UI rendering.

Co-authored-by: smolpaws <engel@enyst.org>

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: omit canvas client tool from ACP launches

* refactor: rename canvas client tool

Use the semantic canvas_ui_control name and contain the SDK-generated action discriminator behind exported constants.

Co-authored-by: Engel Nyst <engel.nyst@gmail.com>

---------

Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Debug Agent <157206163+simonrosenberg@users.noreply.github.com>
2026-07-15 09:40:57 +00:00
nanookclawandNanook 746a8b4a4f fix: scope files selection to conversation (#1354)
Signed-off-by: Nanook <nanookclaw@users.noreply.github.com>
Co-authored-by: Nanook <nanookclaw@users.noreply.github.com>
2026-06-15 07:36:25 -06:00
dcf469855a UI polish: drawer tabs, empty states, and browser chrome (#1288)
* chore: bump version to 1.0.0-beta.1

* chore: publish beta and rc versions as 'latest' dist-tag

* chore: bump version to 1.0.0-beta.2

* fix: use X-Session-API-Key for local automation auth in prompts and RUNTIME_SERVICES (#999)

Fixes #980

The agent prompt in recommended-automations-launcher and the
RUNTIME_SERVICES block in agent-server-adapter both advertised
X-API-Key as the auth header for the local automation backend.
The automation service (openhands-automation) does not accept
X-API-Key — it accepts Authorization: Bearer and X-Session-API-Key.

X-Session-API-Key is the established local convention: the agent
server uses it, the frontend automation API client uses it (with an
explicit comment that both backends share the same header), and
auth.py describes it as matching that convention. Update both call
sites and the corresponding test assertion to use X-Session-API-Key.

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: reuse mock-LLM E2E tests for Docker image validation (#992)

* feat: reuse mock-LLM E2E tests for Docker image validation

Add a Docker-specific Playwright config (playwright.mock-llm-docker.config.ts)
that runs the exact same test specs and helpers against the agent-canvas Docker
image instead of the npm build path (bin/agent-canvas.mjs + uvx).

Key changes:

- Split MOCK_LLM_BASE_URL into two constants in mock-llm-helpers.ts:
  - MOCK_LLM_BASE_URL: always host-local, used by tests for admin API
  - MOCK_LLM_AGENT_URL: env-overridable, used when configuring the LLM
    profile (the URL the agent-server uses for inference). Defaults to
    MOCK_LLM_BASE_URL for backward compatibility with the npm path.

- New playwright.mock-llm-docker.config.ts:
  - Starts the mock LLM server on the host (same as npm path)
  - Runs the Docker container with --network host (Linux CI)
  - Points to the same testDir (tests/e2e/mock-llm/) and specs
  - Separate output dirs to avoid collision with npm path results

- New CI workflow (.github/workflows/mock-llm-docker-e2e.yml):
  - Builds the Docker image from current code (or uses a pre-built image)
  - Runs the same specs against the container
  - Posts PR comment with differentiated report title

- render-mock-llm-report.mjs: accept --title flag for Docker vs npm reports
- npm run test:e2e:mock-llm:docker script added
- .gitignore updated for docker test output dirs

The npm path (test:e2e:mock-llm) is fully backward-compatible — no env var
override needed since MOCK_LLM_AGENT_URL defaults to MOCK_LLM_BASE_URL.

Co-authored-by: openhands <openhands@all-hands.dev>

* refactor: chain Docker E2E off existing Docker CI via workflow_run

Instead of rebuilding the Docker image in the E2E workflow (duplicating
~10-15 min of Docker build time), use workflow_run to trigger automatically
after the existing 'Docker' workflow completes successfully.

The workflow now:
- Triggers on: workflow_run (Docker completed) + workflow_dispatch (manual)
- Derives the image tag from the Docker build's commit SHA
  (ghcr.io/openhands/agent-canvas:sha-<short>-amd64)
- Pulls the already-built image from GHCR — no rebuild needed
- Checks out code at the same SHA as the Docker build
- Extracts PR number from workflow_run.pull_requests[] for comments

Removed: Docker build steps, Buildx setup, build-arg resolution.
All image building stays in docker.yml where it belongs.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: replace flaky 1s timeout with polling for Active badge assertion

The 'Active badge' check in step 2 used a hardcoded 1-second
waitForTimeout before reloading. On a loaded CI runner the profile
activation mutation may not persist in time, causing the reload to
show stale state. This is a pre-existing flake (identical test code
passed on the first push and failed on the second).

Replace with expect.poll() that retries the reload+check cycle with
increasing intervals (1s, 2s, 3s) up to 15 seconds total.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: add pull_request trigger for Docker E2E (workflow_run bootstrap)

workflow_run only fires when the workflow file exists on the default
branch (main). Since mock-llm-docker-e2e.yml is new and only on the
PR branch, GitHub doesn't recognize it as a workflow_run listener yet.

Add pull_request trigger (gated by 'e2e-tests' label, skip forks) that
polls the Docker workflow via gh API until it completes for the PR's
head SHA, then pulls the already-built image from GHCR and runs tests.

After merge, workflow_run takes over as the primary automatic trigger.
The pull_request path remains as a fallback for label-gated runs.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: add FILE_STORE, AUTOMATION_BASE_URL, AUTOMATION_WORKSPACE_BASE to Docker entrypoint

The Docker entrypoint was missing several environment variables that the npm
path (dev-with-automation.mjs) sets for the automation backend:

- FILE_STORE=local — without this, the automation backend may fall back to
  cloud storage (S3/GCS) which fails without credentials, causing tarball-
  based presets (preset/prompt, preset/plugin) to silently error
- LOCAL_STORAGE_PATH — where to store files on the local filesystem
- AUTOMATION_BASE_URL — publicly-reachable base URL for callback URLs
- AUTOMATION_WORKSPACE_BASE — where automation runs unpack tarballs

This explains the Docker E2E failure: the agent's curl to create an automation
via /api/automation/v1/preset/prompt returned an error (likely 500 from missing
storage config), but the mock LLM doesn't care about terminal output and
proceeded to return the scripted final reply. The test then found 0 automations.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: exclude auth-modes spec from Docker E2E tests

The mock-llm-auth-modes.spec.ts tests npm-binary-specific --auth-required
behaviour (a second static-server instance on port 18301). The Docker image
doesn't provide this second server — it has its own auth handling. Exclude
the spec from the Docker test run via testIgnore.

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: run auth-modes tests inside Docker via PUBLIC_MODE_PORT

Instead of excluding the auth-modes spec from the Docker E2E run or
spinning up a host-side static server with a duplicate build/ directory,
the Docker entrypoint now supports an optional PUBLIC_MODE_PORT env var.

When set, entrypoint.sh starts a second static-server instance from the
same baked-in frontend assets with --auth-required (no session key
injected). This tests the actual Docker image's auth gate behaviour —
not a host-side approximation.

The Playwright Docker config passes -e PUBLIC_MODE_PORT=18301 to the
container and exports MOCK_LLM_PUBLIC_MODE_URL so the auth-modes spec
can reach it. With --network host the port is accessible from the host.

Co-authored-by: openhands <openhands@all-hands.dev>

* address review feedback: drop unlabeled trigger, improve error messages, document env vars

- Drop 'unlabeled' from pull_request trigger types to avoid wasted
  workflow runs when any label is removed (the job-level if: condition
  would skip immediately anyway)
- Distinguish 'no Docker run found' vs 'didn't complete in time' in
  the polling loop's final error message
- Add comment explaining /api/automation/v1 probe returns 200 without
  auth so the readiness check won't spin for 180s
- Document FILE_STORE, LOCAL_STORAGE_PATH, AUTOMATION_BASE_URL, and
  AUTOMATION_WORKSPACE_BASE in the entrypoint header — these affect
  production deployments, not just E2E tests

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump version to 1.0.0-beta.3

* ci: trigger CI on rel-* branch pushes for tag protection rule (#1004)

The Release Tag ruleset requires test-and-build (ubuntu) to pass
before v* tags can be pushed, but CI previously only ran on main and
pull_request events. This caused rel-* version bump commits to fail
the tag protection check unless a workaround PR was opened.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump version to 1.0.0-beta.4

* chore: auto-graduate npm dist-tag from latest to per-tier once first stable release ships (#1028)

* chore: always publish to npm with --tag latest until first stable release

All alpha/beta/rc versions now get the 'latest' dist-tag so plain
'npm install @openhands/agent-canvas' always resolves to the newest
published release. The per-tier dist-tags (alpha/beta/rc) can be
re-introduced once the first full stable version is ready to ship.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: auto-graduate npm dist-tag when first stable release ships

At publish time, query npm for any published version without a pre-release
suffix. If none exists, all releases (alpha/beta/rc/stable) use --tag latest
so plain 'npm install' always resolves to the newest build. Once a stable
version has been published, pre-release versions revert to their own
dist-tags (alpha/beta/rc) automatically — no workflow change required.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump version to 1.0.0-beta.5

* feat(mcp): render markdown links in helperText; update Slack catalog pin (#1012)

* feat(mcp): render markdown links in helperText; bump extensions to slack field-order PR commit

- Add renderHelperText() to install-server-modal.tsx that converts
  [text](url) patterns into <a> elements with target=_blank, so the
  Slack workspace-ID helper text (and any future catalog entries) can
  embed clickable docs links inline.
- Bump @openhands/extensions to commit 2d43e9c (branch
  slack-catalog-field-order-and-helper-links, PR #285) which:
    • moves SLACK_TEAM_ID before SLACK_BOT_TOKEN in the install modal
    • replaces the plain SLACK_TEAM_ID helper text with linked copy:
      'First visit [here](...#find-your-url) to get your Slack URL
       and then visit [here](...#find-your-workspace-or-org-id) to
       get your workspace ID.'
- Removes stale integrity hash from package-lock.json for the
  @openhands/extensions entry; npm install will recompute it.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to d186872 (SLACK_BOT_TOKEN helperText)

Add inline linked helperText for SLACK_BOT_TOKEN in slack.json (PR #285,
commit d186872): 'You'll need to create or update a Slack App as shown
[here](https://github.com/zencoderai/slack-mcp-server#slack-bot-setup).'
Drops the now-redundant helperLink field.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to b45d3a1 (SLACK_TEAM_ID helperText rewrite)

Update SLACK_TEAM_ID helperText to named links:
'First get your [Slack URL](...). Then use that to get your [Workspace ID](...).'

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to 84a0a6e (SLACK_BOT_TOKEN named link)

Update SLACK_BOT_TOKEN helperText to:
"You'll need to create or update a [Slack App](...#slack-bot-setup)."

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to e07f427 (SLACK_BOT_TOKEN helperText)

Update SLACK_BOT_TOKEN helperText to:
"You'll need to create or update a [Slack App](...) to get a Bot token"

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to 5efd1b8

Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285).

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to 952c759

Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285).

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to f30dbfb

Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285).

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to 02715f4

Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285).

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump @openhands/extensions to cb092c8

Sync to latest commit on slack-catalog-field-order-and-helper-links (PR #285).

Co-authored-by: openhands <openhands@all-hands.dev>

* fix(mcp): validate URL scheme in renderHelperText; use matchAll

- Guard href against javascript:/data: XSS via /^https?:\/\//i test
- Replace exec-in-while with matchAll to drop the eslint-disable comment

Addresses review bot feedback on PR #1012.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix(mcp): use double quotes for fallback href to satisfy Prettier

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: update @openhands/extensions to latest main (62594156)

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: bump version to 1.0.0-beta.6

* chore: bump version to 1.0.0-beta.7

* fix(mcp): drop duplicate renderHelperText after main merge

* chore: bump version to 1.0.0-beta.8

* docs: update README version to 1.0.0-beta.8

* fix: default LLM setup to Anthropic Claude Opus 4.8 (#1089)

* chore: bump version to 1.0.0-beta.9

* docs: update README version to 1.0.0-beta.9

* docs: update README.windows.md version to 1.0.0-beta.9

* fix(dev): align Vite dev origin with ingress and add chat footer padding

Route modules loaded from :3001 while the app opened on :8000, causing blank
screens on npm run dev. Point Vite server.origin/HMR at the ingress URL and add
bottom spacing under the archived conversation banner footer.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): polish spinners, settings empty states, and archived conversation UX

Remove grey track rings from all loading spinners so only the animated arc
remains visible. Wrap bare settings empty/error messages (SDK schema
unavailable, profile load failures, empty profiles/skills/secrets/MCP) in
the shared bordered empty-state container for visual consistency.

Canonicalize 127.0.0.1 backend URLs to localhost so health probes reach the
ingress proxy instead of Vite HMR on macOS dual-stack dev stacks, and sync
stored default-local backend host alongside the session key.

Disable conversation controls for archived sandboxes (MISSING/ERROR) with
tooltips explaining unavailability, using shared archive-status helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): restore light foreground on conversation tab loading state

Use the semantic text-foreground token for the spinner and label so loading copy stays readable on the dark surface background.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): polish conversation tab loading and automations empty state

Conversation tab loading:
- Use TextShimmer on the loading label (same treatment as message sending)
  with block w-full text-center so the sweep flows across the word, not per
  character
- Keep the spinner on text-tertiary-light for readable secondary grey
- Add ConversationTabContentCrossfade to cross-fade between loading and loaded
  content (agent init and lazy tab chunks); content preloads underneath at
  opacity 0 while the overlay fades out over 350ms; reduced-motion falls back
  to an instant swap

Automations empty state:
- Add a top border above the create-instructions section to separate it from
  the hint copy

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): unify drawer empty/loading states and polish browser/files tabs

Align Changes, VS Code, and runtime waiting states with shared drawer patterns, add browser chrome bar with inactive nav when empty, and improve Files tab empty state and tree toggle icon.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): remove browser screenshot rounding and improve panel fill

Drop rounded corners on the screenshot viewer and use min-h-0 flex layout so the browser tab fills the drawer edge-to-edge and collapses correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): polish protip banner, browser chrome, and tab crossfade

Hide non-functional browser nav controls, restyle the changes-tab protip with icon and muted subtext, drop Customize label colons, and fix Suspense fallback setState during render.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): move VS Code to files toolbar and refresh drawer icons

Relocate editor access from the drawer Code tab into a bordered Files toolbar button, swap tab icons to Lucide, add a terminal empty state, and update the VS Code logo asset.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): animate drawer tab label reveal and icon shifts

Use Framer Motion layout transitions so the active tab label expands in and sibling icons slide smoothly when switching drawer tabs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): pin VS Code in drawer tab row and fix tab drag animation

Move VS Code to the drawer header, portal the overflow menu so it is not clipped, and disable tab layout animations while resizing the panel so icons only animate on click.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): shrink drawer tab icons to match standard chrome size

Use h-4 w-4 for drawer tab icons so they align with the ellipsis and other inline controls in the top row.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(home): allow changing repo, branch, or workspace before launch

Replace static git-control-bar link chips on the home screen with the same
dropdowns used in the open-workspace and open-repository dialogs so users
can revise their selection until they send the first message.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Revert "feat(home): allow changing repo, branch, or workspace before launch"

This reverts commit 569bf18bd18dbbe2bd2eaec5747737a162079e0e.

* refactor: remove unrelated files

* refactor: remove unrelated files

* refactor: remove unrelated files

* refactor: remove unrelated files

* refactor: remove unrelated files

* refactor: vscode tab

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Tim O'Farrell <tofarr@gmail.com>
Co-authored-by: Rohit Malhotra <rohitvinodmalhotra@gmail.com>
Co-authored-by: chuckbutkus <chuck@openhands.dev>
Co-authored-by: Hiep Le <69354317+hieptl@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-06-10 12:35:34 +07:00
Hiep Le 5c33c10b18 feat(frontend): add canvas_ui tool so the agent can drive the UI (#420)
* feat: add canvas_ui tool so the agent can drive the UI

* refactor: update the code based on feedback

* fix: failing tests

* refactor: update the code based on feedback
2026-05-16 16:09:43 +07:00
Rohit Malhotraandopenhands 48b275a94f feat: track install immediately and use reverse proxy for ad blocker bypass (#155)
* 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>
2026-05-07 14:29:32 -04:00
Rohit Malhotraandopenhands e2615344df feat: add first-use telemetry tracking with consent (#138)
* 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>
2026-05-07 13:18:01 -04:00
openhands 1c239d73a9 Port OpenHands frontend to direct agent_server integration
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-24 02:46:34 +00:00