10 Commits
Author SHA1 Message Date
Hiep Le 89dc8bd446 feat: land the Canvas Extensions frontend (load pages, sidebar, customize) (#16895) 2026-08-27 17:53:40 +00:00
d104ffdc33 feat: fix default and free models (#16922)
Co-authored-by: neubig <neubig@users.noreply.github.com>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Juan Pedro Michelini Jorge <juan@juan.com.uy>
Co-authored-by: allhands-bot <allhands-bot@users.noreply.github.com>
2026-08-27 15:58:35 +00:00
Juan Pedro Michelini Jorgeandopenhands 8989bf3bb5 feat: set Canvas default model to Kimi K3 and tag it free (#16657)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-17 16:12:28 -03:00
bf2e37dcad fix: preserve MCP credentials during Canvas mutations (#16144)
Co-authored-by: neubig <neubig@users.noreply.github.com>
Co-authored-by: Rohit Malhotra <rohitvinodmalhotra@gmail.com>
Co-authored-by: openhands <openhands@all-hands.dev>
2026-08-04 22:48:42 -04:00
246dbd48c3 feat: set Canvas default model to GLM 5.2 (#16146)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Graham Neubig <neubig@gmail.com>
2026-08-03 18:04:10 -03:00
Hiep Le 7f42eb9857 chore: remove dead upload-path remnants (#1236) 2026-06-10 13:46:11 +00:00
chuckbutkusandopenhands 2ed2c571e6 fix(upload): resolve relative working dirs against /api/file/home (#1106)
* fix(upload): resolve relative working dirs against /api/file/home

The agent-server's /api/file/upload endpoint requires an absolute path
and `mkdir -p`'s the parent. The frontend's `toAbsoluteWorkspacePath`
was naively prepending `/` to the default relative `workspace/project`
working dir, producing `/workspace/project/<hex>/...`. On macOS and
fresh Docker images that path lives under a read-only filesystem root,
so uploads failed with `OSError: [Errno 30] Read-only file system:
'/workspace'`.

Conversations themselves kept working because the agent-server resolves
relative `workspace.working_dir` against its own process CWD (which is
writable in dev), so the worktree landed elsewhere and only uploads
mistargeted the read-only root.

Fix: introduce `getAgentServerHomeDir` (cached per backend, backed by
`FileClient.getHome` → `GET /api/file/home`) and a
`resolveAbsoluteAgentServerPath` helper. Both the conversation-start
payload and the file-upload destination now go through this resolver,
so they always agree on a single absolute path anchored at the
agent-server's home directory (e.g. `~/workspace/project/<hex>`).
Absolute paths pass through unchanged, so explicit workspace selections
and `VITE_WORKING_DIR` overrides are unaffected.

Spec: WUP-001 in specs/workspace-upload-path.md.

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

* docs: clarify resolveConversationUploadWorkingDir returns raw (possibly-relative) working dir

Add JSDoc explaining that callers must funnel the result through
buildWorkspaceUploadPath (which calls resolveAbsoluteWorkspacePath) to
get an absolute path for the upload endpoint.  Also document that the
UNC path case is already covered by the existing isAbsolutePath regex.

Addresses review comment on PR #1106.

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

* test(e2e): add mock-llm image-upload test

Adds an end-to-end mock-LLM test that exercises the full image-attachment
pipeline:

1. Attaches a minimal 1×1 PNG to the home-page chat via the hidden file
   input (data-testid="upload-image-input") using Playwright's
   setInputFiles.
2. Submits "What is in this image?" — creating a conversation and sending
   the message via sendMessageWithAttachments.
3. Verifies the agent replies with IMAGE_REPLY_TOKEN in the chat UI.
4. Verifies the user MessageEvent in the conversation events API has
   image_urls populated (base64 data: URL).
5. Verifies at least one /v1/chat/completions call to the mock server
   contained an image_url content block — confirming the image was
   forwarded to the LLM as expected.

Supporting changes:
- mock-llm-server.py: add GET /admin/requests endpoint that exposes all
  captured completion request bodies since the last reset; reset also
  clears the history.
- mock-llm-helpers.ts: add getMockLLMRequests(), IMAGE_REPLY_TOKEN,
  and MINIMAL_PNG_BASE64 exports.
- AGENTS.md: document the new endpoint and test spec.

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

* test(e2e): add padding response to image-upload trajectory

The agent-server makes one internal LLM call for skill-analysis before
the main agent loop starts. The original 1-response trajectory was
consumed by that internal call, leaving the agent with a 500 error and
retry storm.

Add 1 padding response (turn 0: empty text) + 1 safety buffer (turn 2)
following the same pattern as mock-llm-automation.spec.ts.

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

* test(e2e): use gpt-4o model name for vision-capable LLM requests

litellm strips image_url content blocks for unknown model names like
'openai/mock-test-model'. Switch to 'openai/gpt-4o' so litellm knows
the model accepts vision content and includes base64 image_url blocks
in the completion request body.

The base_url still points at the local mock server; the model name is
only a hint to litellm's request formatter.

Also improve assertion diagnostics to print all captured LLM requests
(not just the first) when the assertion fails.

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-06-03 17:47:45 -04:00
f87167deb6 fix: always send frontend's chosen default model to agent-server (#898)
* fix: always send frontend's chosen default model to agent-server (#807)

When the agent-server returns an empty model string (e.g. because no
settings have been saved yet), the adapter's type guard accepted it as
a valid string and forwarded it verbatim. The agent-server would then
fall back to its SDK default ('gpt-5.5') rather than using the
frontend's chosen default ('openhands/minimax-m2.7').

Fix: strengthen the guard in buildConfiguredOpenHandsAgentSettings to
also reject empty/whitespace-only strings, so the frontend's own
default is always sent explicitly:

  llm.model =
    typeof llm.model === 'string' && llm.model.trim().length > 0
      ? llm.model
      : DEFAULT_SETTINGS.llm_model;

While here: align the legacy V0 /api/options/models mock endpoint's
default_model with DEFAULT_SETTINGS (was incorrectly set to claude-opus;
should be minimax-m2.7 to match the rest of the defaults), and document
the canonical default model and the two-location update rule in AGENTS.md.

Closes #807

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

* refactor(tests): parameterize llm.model fallback tests + add spec + AGENTS.md

- Extract getModelFrom() helper to eliminate repeated as unknown as ModelPayload casts
- Consolidate 7 individual fallback it() blocks into two it.each() groups:
  (1) agent_settings variants (undefined/empty/whitespace/no-llm-block/empty-settings)
  (2) encryptedAgentSettings variants (empty model / empty object)
- Rename @spec annotation from BM-807 to LLD-001 (not a backend-management spec)
- Add specs/llm-defaults.md with LLD-001 checklist
- Add AGENTS.md note documenting canonical default model, its location, and
  the two-location update rule

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-06-01 15:29:30 +00:00
Rohit Malhotraandopenhands 60bc93f0e9 Add backend management specs and @spec annotations (#727)
* Add backend management specs and @spec annotations

- Curate specs/backend-management.md with 4 behavioral specs (BM-001–BM-004)
- Add @spec comments to source and test files for traceability
- Add spec file convention note to AGENTS.md

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

* Remove duplicate BM-002 test (non-conversation redirect)

The 'stays on settings' case was covered by two tests; keep the one
with the clearer assertion and drop the redundant copy.

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

* Parameterize BM-002 tests with it.each

Collapse 3 identical-structure redirect tests into a single
parameterized it.each covering conversation detail, automation detail,
and non-ID routes.

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

* Document @spec tagging convention in AGENTS.md

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-21 18:08:53 -04:00
Rohit Malhotraandopenhands e6d12e3fff fix(BM-001): auto-switch active backend on addBackend (#714)
* spec(BM-001): add spec + failing tests for auto-switch on connect

Adding a backend should automatically switch the active selection to it.
Currently addBackend registers the entry but leaves the user on the
previous backend, forcing a manual switch.

- specs/backend-management.md: BM-001 definition
- Two new test cases (cloud + local) that assert the active backend
  changes after addBackend — both fail against the current implementation.

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

* fix(BM-001): auto-switch active backend on addBackend

addBackend now calls setActiveSelection after registering the new entry,
so the user lands on the backend they just connected — whether via the
manual host+key form or the cloud OAuth device-flow login.

- src/contexts/active-backend-context.tsx: one-line fix in addBackend
- Updated add-backend-modal test to assert the new active selection
- Updated backend-selector tests that used addBackend purely as setup
  to reset active to the default local backend afterward

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

* chore: remove noisy @spec markers from test setup comments

Keep @spec BM-001 only where it marks the implementation or directly
asserts the spec behavior. Setup-only adjustments (resetting active
backend after addBackend) are incidental — plain comments suffice.

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

* chore: consolidate @spec BM-001 to one test + one implementation site

The spec marker belongs in exactly two places: the line that implements
the behavior and the single test that verifies it. Removed the redundant
local-backend variant (same code path as cloud) and dropped @spec labels
from the modal test (its assertion stays, just without the tag).

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

* refactor: remove setActive workarounds from backend-selector tests

Instead of manually resetting active state after addBackend, tests now
work with the auto-switch naturally:

- Local backend tests: click the seeded default 'Local' (which is no
  longer active after auto-switch) to trigger the switch.
- Cloud org tests: add a local backend after the cloud one so the last
  auto-switch lands on local, leaving cloud backends unselected and
  their org rows visible in the dropdown.

No ctx.setActive(DEFAULT_LOCAL_BACKEND_ID) calls remain.

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

* refactor: extract shared seed constants in backend-selector tests

SEED_LOCAL_1 and SEED_CLOUD_PRODUCTION replace 12+4 identical inline
config objects. The two remaining inline blocks have different apiKey
values and correctly stay as-is.

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-21 18:49:20 +00:00