mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
1 Commits
pre-org-ch
...
1.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c97d66131d |
@@ -161,7 +161,7 @@ poetry run pytest ./tests/unit/test_*.py
|
||||
To reduce build time (e.g., if no changes were made to the client-runtime component), you can use an existing Docker
|
||||
container image by setting the SANDBOX_RUNTIME_CONTAINER_IMAGE environment variable to the desired Docker image.
|
||||
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:1.1-nikolaik`
|
||||
Example: `export SANDBOX_RUNTIME_CONTAINER_IMAGE=ghcr.io/openhands/runtime:1.2-nikolaik`
|
||||
|
||||
## Develop inside Docker container
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- SANDBOX_API_HOSTNAME=host.docker.internal
|
||||
- DOCKER_HOST_ADDR=host.docker.internal
|
||||
#
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/openhands/runtime:1.1-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-ghcr.io/openhands/runtime:1.2-nikolaik}
|
||||
- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234}
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -7,7 +7,7 @@ services:
|
||||
image: openhands:latest
|
||||
container_name: openhands-app-${DATE:-}
|
||||
environment:
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.openhands.dev/openhands/runtime:1.1-nikolaik}
|
||||
- SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE:-docker.openhands.dev/openhands/runtime:1.2-nikolaik}
|
||||
#- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of ~/.openhands for this user
|
||||
- WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace}
|
||||
ports:
|
||||
|
||||
@@ -29,9 +29,7 @@ class ResolverUserContext(UserContext):
|
||||
|
||||
return UserInfo(id=user_id)
|
||||
|
||||
async def get_authenticated_git_url(
|
||||
self, repository: str, is_optional: bool = False
|
||||
) -> str:
|
||||
async def get_authenticated_git_url(self, repository: str) -> str:
|
||||
# This would need to be implemented based on the git provider tokens
|
||||
# For now, return a basic HTTPS URL
|
||||
return f'https://github.com/{repository}.git'
|
||||
|
||||
125
enterprise/poetry.lock
generated
125
enterprise/poetry.lock
generated
@@ -1178,7 +1178,7 @@ files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\" or os_name == \"nt\"", dev = "os_name == \"nt\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""}
|
||||
markers = {main = "platform_system == \"Windows\" or os_name == \"nt\" or sys_platform == \"win32\"", dev = "os_name == \"nt\"", test = "platform_system == \"Windows\" or sys_platform == \"win32\""}
|
||||
|
||||
[[package]]
|
||||
name = "comm"
|
||||
@@ -2264,14 +2264,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.20.3"
|
||||
version = "3.19.1"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
files = [
|
||||
{file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"},
|
||||
{file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"},
|
||||
{file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"},
|
||||
{file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5858,14 +5858,14 @@ llama = ["llama-index (>=0.12.29,<0.13.0)", "llama-index-core (>=0.12.29,<0.13.0
|
||||
|
||||
[[package]]
|
||||
name = "openhands-agent-server"
|
||||
version = "1.8.2"
|
||||
version = "1.8.1"
|
||||
description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent"
|
||||
optional = false
|
||||
python-versions = ">=3.12"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "openhands_agent_server-1.8.2-py3-none-any.whl", hash = "sha256:e9abb2e0fe970715537d0e0fc1aea3dd64bb9e8b531f70cb72b3d4e486aaa46a"},
|
||||
{file = "openhands_agent_server-1.8.2.tar.gz", hash = "sha256:43db2371ee84b100ac921396338dee74359fceeb5c9400c90530bcc5730144c3"},
|
||||
{file = "openhands_agent_server-1.8.1-py3-none-any.whl", hash = "sha256:c0dfe620184633a173094ffaa77b0d13124ea7bf84e7b534b1641e5fc5fd0256"},
|
||||
{file = "openhands_agent_server-1.8.1.tar.gz", hash = "sha256:08adfe26d867ff0cb0c1e87bb0ad6e058c9a97374964ba6a9860ea35d32764a0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -5891,89 +5891,89 @@ files = []
|
||||
develop = true
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = ">=3.9,<3.11.13 || >3.11.13"
|
||||
aiohttp = ">=3.9.0,!=3.11.13"
|
||||
anthropic = {version = "*", extras = ["vertex"]}
|
||||
anyio = "4.9"
|
||||
asyncpg = ">=0.30"
|
||||
bashlex = ">=0.18"
|
||||
anyio = "4.9.0"
|
||||
asyncpg = "^0.30.0"
|
||||
bashlex = "^0.18"
|
||||
boto3 = "*"
|
||||
browsergym-core = "0.13.3"
|
||||
deprecated = "*"
|
||||
deprecation = ">=2.1"
|
||||
deprecation = "^2.1.0"
|
||||
dirhash = "*"
|
||||
docker = "*"
|
||||
fastapi = "*"
|
||||
fastmcp = ">=2.12.4"
|
||||
google-api-python-client = ">=2.164"
|
||||
fastmcp = "^2.12.4"
|
||||
google-api-python-client = "^2.164.0"
|
||||
google-auth-httplib2 = "*"
|
||||
google-auth-oauthlib = "*"
|
||||
google-cloud-aiplatform = "*"
|
||||
google-genai = "*"
|
||||
html2text = "*"
|
||||
httpx-aiohttp = ">=0.1.8"
|
||||
ipywidgets = ">=8.1.5"
|
||||
jinja2 = ">=3.1.6"
|
||||
httpx-aiohttp = "^0.1.8"
|
||||
ipywidgets = "^8.1.5"
|
||||
jinja2 = "^3.1.6"
|
||||
joblib = "*"
|
||||
json-repair = "*"
|
||||
jupyter-kernel-gateway = "*"
|
||||
kubernetes = ">=33.1"
|
||||
jupyter_kernel_gateway = "*"
|
||||
kubernetes = "^33.1.0"
|
||||
libtmux = ">=0.46.2"
|
||||
litellm = ">=1.74.3"
|
||||
lmnr = ">=0.7.20"
|
||||
memory-profiler = ">=0.61"
|
||||
litellm = ">=1.74.3, !=1.64.4, !=1.67.*"
|
||||
lmnr = "^0.7.20"
|
||||
memory-profiler = "^0.61.0"
|
||||
numpy = "*"
|
||||
openai = "2.8"
|
||||
openai = "2.8.0"
|
||||
openhands-aci = "0.3.2"
|
||||
openhands-agent-server = "1.8.2"
|
||||
openhands-sdk = "1.8.2"
|
||||
openhands-tools = "1.8.2"
|
||||
opentelemetry-api = ">=1.33.1"
|
||||
opentelemetry-exporter-otlp-proto-grpc = ">=1.33.1"
|
||||
pathspec = ">=0.12.1"
|
||||
openhands-agent-server = "1.8.1"
|
||||
openhands-sdk = "1.8.1"
|
||||
openhands-tools = "1.8.1"
|
||||
opentelemetry-api = "^1.33.1"
|
||||
opentelemetry-exporter-otlp-proto-grpc = "^1.33.1"
|
||||
pathspec = "^0.12.1"
|
||||
pexpect = "*"
|
||||
pg8000 = ">=1.31.5"
|
||||
pillow = ">=11.3"
|
||||
playwright = ">=1.55"
|
||||
poetry = ">=2.1.2"
|
||||
prompt-toolkit = ">=3.0.50"
|
||||
protobuf = ">=5,<6"
|
||||
pg8000 = "^1.31.5"
|
||||
pillow = "^11.3.0"
|
||||
playwright = "^1.55.0"
|
||||
poetry = "^2.1.2"
|
||||
prompt-toolkit = "^3.0.50"
|
||||
protobuf = "^5.0.0,<6.0.0"
|
||||
psutil = "*"
|
||||
pybase62 = ">=1"
|
||||
pygithub = ">=2.5"
|
||||
pyjwt = ">=2.9"
|
||||
pybase62 = "^1.0.0"
|
||||
pygithub = "^2.5.0"
|
||||
pyjwt = "^2.9.0"
|
||||
pylatexenc = "*"
|
||||
pypdf = ">=6"
|
||||
pypdf = "^6.0.0"
|
||||
python-docx = "*"
|
||||
python-dotenv = "*"
|
||||
python-frontmatter = ">=1.1"
|
||||
python-frontmatter = "^1.1.0"
|
||||
python-jose = {version = ">=3.3", extras = ["cryptography"]}
|
||||
python-json-logger = ">=3.2.1"
|
||||
python-json-logger = "^3.2.1"
|
||||
python-multipart = "*"
|
||||
python-pptx = "*"
|
||||
python-socketio = ">=5.11.4"
|
||||
python-socketio = "^5.11.4"
|
||||
pythonnet = "*"
|
||||
pyyaml = ">=6.0.2"
|
||||
qtconsole = ">=5.6.1"
|
||||
rapidfuzz = ">=3.9"
|
||||
redis = ">=5.2,<7"
|
||||
requests = ">=2.32.5"
|
||||
pyyaml = "^6.0.2"
|
||||
qtconsole = "^5.6.1"
|
||||
rapidfuzz = "^3.9.0"
|
||||
redis = ">=5.2,<7.0"
|
||||
requests = "^2.32.5"
|
||||
setuptools = ">=78.1.1"
|
||||
shellingham = ">=1.5.4"
|
||||
sqlalchemy = {version = ">=2.0.40", extras = ["asyncio"]}
|
||||
sse-starlette = ">=3.0.2"
|
||||
starlette = ">=0.48"
|
||||
tenacity = ">=8.5,<10"
|
||||
shellingham = "^1.5.4"
|
||||
sqlalchemy = {version = "^2.0.40", extras = ["asyncio"]}
|
||||
sse-starlette = "^3.0.2"
|
||||
starlette = "^0.48.0"
|
||||
tenacity = ">=8.5,<10.0"
|
||||
termcolor = "*"
|
||||
toml = "*"
|
||||
tornado = ">=6.5"
|
||||
types-toml = "*"
|
||||
urllib3 = ">=2.6.3"
|
||||
urllib3 = "^2.6.3"
|
||||
uvicorn = "*"
|
||||
whatthepatch = ">=1.0.6"
|
||||
whatthepatch = "^1.0.6"
|
||||
zope-interface = "7.2"
|
||||
|
||||
[package.extras]
|
||||
third-party-runtimes = ["daytona (==0.24.2)", "e2b-code-interpreter (>=2)", "modal (>=0.66.26,<1.2)", "runloop-api-client (==0.50)"]
|
||||
third-party-runtimes = ["daytona (==0.24.2)", "e2b-code-interpreter (>=2.0.0,<3.0.0)", "modal (>=0.66.26,<1.2.0)", "runloop-api-client (==0.50.0)"]
|
||||
|
||||
[package.source]
|
||||
type = "directory"
|
||||
@@ -5981,20 +5981,19 @@ url = ".."
|
||||
|
||||
[[package]]
|
||||
name = "openhands-sdk"
|
||||
version = "1.8.2"
|
||||
version = "1.8.1"
|
||||
description = "OpenHands SDK - Core functionality for building AI agents"
|
||||
optional = false
|
||||
python-versions = ">=3.12"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "openhands_sdk-1.8.2-py3-none-any.whl", hash = "sha256:b4fad9581865ce222a3e6722384e4df56113db01bd34c2d2d408dfd9695365c0"},
|
||||
{file = "openhands_sdk-1.8.2.tar.gz", hash = "sha256:5bfb17c8b9515210d121249deb1f3d0dc407c3737edc55b5e73330b4571d61e3"},
|
||||
{file = "openhands_sdk-1.8.1-py3-none-any.whl", hash = "sha256:133275f56321585c016b4718d56c8fc7bb834f4ef7cab1ef66b0c71c49d47d1d"},
|
||||
{file = "openhands_sdk-1.8.1.tar.gz", hash = "sha256:9e2baa6c512ac4c2bc1c2c0bf8b1dbdb0267d794a8b86b7306a4656fc0cb8b0b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
deprecation = ">=2.1.0"
|
||||
fastmcp = ">=2.11.3"
|
||||
filelock = ">=3.20.1"
|
||||
httpx = ">=0.27.0"
|
||||
litellm = ">=1.80.10"
|
||||
lmnr = ">=0.7.24"
|
||||
@@ -6009,14 +6008,14 @@ boto3 = ["boto3 (>=1.35.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "openhands-tools"
|
||||
version = "1.8.2"
|
||||
version = "1.8.1"
|
||||
description = "OpenHands Tools - Runtime tools for AI agents"
|
||||
optional = false
|
||||
python-versions = ">=3.12"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "openhands_tools-1.8.2-py3-none-any.whl", hash = "sha256:283f0c1fdd316914559cd16ade792383715478a8f5a73f7166daffc34bf9e5af"},
|
||||
{file = "openhands_tools-1.8.2.tar.gz", hash = "sha256:eae416e3867f7cb595129a33a4b9237886c4b8a075d2bc7618da55963f2747d5"},
|
||||
{file = "openhands_tools-1.8.1-py3-none-any.whl", hash = "sha256:9404b17edb8960d4af3a4439e6f68e37c92c59d0705f13096e4a8ff9b6ffc472"},
|
||||
{file = "openhands_tools-1.8.1.tar.gz", hash = "sha256:e59fcd9ca3baa6266e92020646c4c5f5266f57761f434770cf0cd458b1a33cb0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
||||
@@ -39,8 +39,6 @@ ROLE_CHECK_ENABLED = os.getenv('ROLE_CHECK_ENABLED', 'false').lower() in (
|
||||
'on',
|
||||
)
|
||||
|
||||
DUPLICATE_EMAIL_CHECK = os.getenv('DUPLICATE_EMAIL_CHECK', 'true') in ('1', 'true')
|
||||
|
||||
# reCAPTCHA Enterprise
|
||||
RECAPTCHA_PROJECT_ID = os.getenv('RECAPTCHA_PROJECT_ID', '').strip()
|
||||
RECAPTCHA_SITE_KEY = os.getenv('RECAPTCHA_SITE_KEY', '').strip()
|
||||
|
||||
@@ -19,7 +19,6 @@ from keycloak.exceptions import (
|
||||
from server.auth.constants import (
|
||||
BITBUCKET_APP_CLIENT_ID,
|
||||
BITBUCKET_APP_CLIENT_SECRET,
|
||||
DUPLICATE_EMAIL_CHECK,
|
||||
GITHUB_APP_CLIENT_ID,
|
||||
GITHUB_APP_CLIENT_SECRET,
|
||||
GITLAB_APP_CLIENT_ID,
|
||||
@@ -647,10 +646,6 @@ class TokenManager:
|
||||
if not email:
|
||||
return False
|
||||
|
||||
# We have the option to skip the duplicate email check in test environments
|
||||
if not DUPLICATE_EMAIL_CHECK:
|
||||
return False
|
||||
|
||||
base_email = extract_base_email(email)
|
||||
if not base_email:
|
||||
logger.warning(f'Could not extract base email from: {email}')
|
||||
|
||||
@@ -20,7 +20,7 @@ This is the frontend of the OpenHands project. It is a React application that pr
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 22.12.x or later
|
||||
- Node.js 20.x or later
|
||||
- `npm`, `bun`, or any other package manager that supports the `package.json` file
|
||||
|
||||
### Installation
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ErrorMessageBanner } from "#/components/features/chat/error-message-banner";
|
||||
|
||||
describe("ErrorMessageBanner", () => {
|
||||
it("calls onDismiss when the close button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onDismiss = vi.fn();
|
||||
|
||||
render(
|
||||
<ErrorMessageBanner
|
||||
message="Something went wrong"
|
||||
onDismiss={onDismiss}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByLabelText("BUTTON$CLOSE"));
|
||||
expect(onDismiss).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("shows a View More / View Less toggle for long messages", async () => {
|
||||
const user = userEvent.setup();
|
||||
const longMessage = "a".repeat(400);
|
||||
|
||||
render(<ErrorMessageBanner message={longMessage} />);
|
||||
|
||||
const toggle = screen.getByTestId("error-message-banner-toggle");
|
||||
expect(toggle).toHaveTextContent("COMMON$VIEW_MORE");
|
||||
|
||||
await user.click(toggle);
|
||||
expect(toggle).toHaveTextContent("COMMON$VIEW_LESS");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import { describe, it, expect, afterEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { MemoryRouter, Route, Routes } from "react-router";
|
||||
import { ConversationTabsContextMenu } from "#/components/features/conversation/conversation-tabs/conversation-tabs-context-menu";
|
||||
|
||||
function renderWithRouter(conversationId: string, onClose: () => void) {
|
||||
return render(
|
||||
<MemoryRouter initialEntries={[`/conversations/${conversationId}`]}>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/conversations/:conversationId"
|
||||
element={<ConversationTabsContextMenu isOpen onClose={onClose} />}
|
||||
/>
|
||||
</Routes>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("ConversationTabsContextMenu", () => {
|
||||
afterEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it("should use per-conversation localStorage key for unpinned tabs", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onClose = () => {};
|
||||
|
||||
// Render for conversation-1
|
||||
const { unmount } = renderWithRouter("conversation-1", onClose);
|
||||
|
||||
// Unpin the terminal tab in conversation-1
|
||||
const terminalItem = screen.getByText("COMMON$TERMINAL");
|
||||
await user.click(terminalItem);
|
||||
|
||||
// Verify localStorage key is per-conversation
|
||||
const stored1 = JSON.parse(
|
||||
localStorage.getItem("conversation-unpinned-tabs-conversation-1") || "[]",
|
||||
);
|
||||
expect(stored1).toContain("terminal");
|
||||
|
||||
unmount();
|
||||
|
||||
// Switch to conversation-2
|
||||
renderWithRouter("conversation-2", onClose);
|
||||
|
||||
// conversation-2 should have its own empty state
|
||||
const stored2 = JSON.parse(
|
||||
localStorage.getItem("conversation-unpinned-tabs-conversation-2") || "[]",
|
||||
);
|
||||
expect(stored2).toEqual([]);
|
||||
|
||||
// conversation-1 state should still have terminal unpinned
|
||||
const stored1Again = JSON.parse(
|
||||
localStorage.getItem("conversation-unpinned-tabs-conversation-1") || "[]",
|
||||
);
|
||||
expect(stored1Again).toContain("terminal");
|
||||
});
|
||||
|
||||
it("should toggle tab pin state when clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onClose = () => {};
|
||||
|
||||
renderWithRouter("conversation-1", onClose);
|
||||
|
||||
const terminalItem = screen.getByText("COMMON$TERMINAL");
|
||||
|
||||
// Click to unpin
|
||||
await user.click(terminalItem);
|
||||
let stored = JSON.parse(
|
||||
localStorage.getItem("conversation-unpinned-tabs-conversation-1") || "[]",
|
||||
);
|
||||
expect(stored).toContain("terminal");
|
||||
|
||||
// Click again to pin
|
||||
await user.click(terminalItem);
|
||||
stored = JSON.parse(
|
||||
localStorage.getItem("conversation-unpinned-tabs-conversation-1") || "[]",
|
||||
);
|
||||
expect(stored).not.toContain("terminal");
|
||||
});
|
||||
});
|
||||
@@ -1,90 +0,0 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { MemoryRouter } from "react-router";
|
||||
import { ConversationTabs } from "#/components/features/conversation/conversation-tabs/conversation-tabs";
|
||||
import { ConversationTabsContextMenu } from "#/components/features/conversation/conversation-tabs/conversation-tabs-context-menu";
|
||||
|
||||
const TASK_CONVERSATION_ID = "task-ec03fb2ab8604517b24af632b058c2fd";
|
||||
const REAL_CONVERSATION_ID = "conv-abc123";
|
||||
|
||||
vi.mock("#/utils/feature-flags", () => ({
|
||||
USE_PLANNING_AGENT: () => false,
|
||||
}));
|
||||
|
||||
let mockConversationId = TASK_CONVERSATION_ID;
|
||||
|
||||
vi.mock("#/hooks/use-conversation-id", () => ({
|
||||
useConversationId: () => ({ conversationId: mockConversationId }),
|
||||
}));
|
||||
|
||||
const createWrapper = (conversationId: string) => {
|
||||
return ({ children }: { children: React.ReactNode }) => (
|
||||
<MemoryRouter initialEntries={[`/conversations/${conversationId}`]}>
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
describe("ConversationTabs localStorage behavior", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
vi.resetAllMocks();
|
||||
mockConversationId = TASK_CONVERSATION_ID;
|
||||
});
|
||||
|
||||
describe("task-prefixed conversation IDs", () => {
|
||||
it("should not create localStorage entries for task-prefixed conversation IDs", () => {
|
||||
render(<ConversationTabs />, {
|
||||
wrapper: createWrapper(TASK_CONVERSATION_ID),
|
||||
});
|
||||
|
||||
expect(
|
||||
localStorage.getItem(`conversation-state-${TASK_CONVERSATION_ID}`),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("consolidated localStorage key", () => {
|
||||
it("should use a single consolidated key for tab state", async () => {
|
||||
mockConversationId = REAL_CONVERSATION_ID;
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ConversationTabs />, {
|
||||
wrapper: createWrapper(REAL_CONVERSATION_ID),
|
||||
});
|
||||
|
||||
const changesTab = screen.getByText("COMMON$CHANGES");
|
||||
await user.click(changesTab);
|
||||
|
||||
const consolidatedKey = `conversation-state-${REAL_CONVERSATION_ID}`;
|
||||
const storedState = localStorage.getItem(consolidatedKey);
|
||||
expect(storedState).not.toBeNull();
|
||||
|
||||
const parsed = JSON.parse(storedState!);
|
||||
expect(parsed).toHaveProperty("selectedTab");
|
||||
expect(parsed).toHaveProperty("rightPanelShown");
|
||||
expect(parsed).toHaveProperty("unpinnedTabs");
|
||||
});
|
||||
|
||||
it("should store unpinned tabs in consolidated key via context menu", async () => {
|
||||
mockConversationId = REAL_CONVERSATION_ID;
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ConversationTabsContextMenu isOpen={true} onClose={vi.fn()} />);
|
||||
|
||||
const terminalItem = screen.getByText("COMMON$TERMINAL");
|
||||
await user.click(terminalItem);
|
||||
|
||||
const consolidatedKey = `conversation-state-${REAL_CONVERSATION_ID}`;
|
||||
const storedState = localStorage.getItem(consolidatedKey);
|
||||
expect(storedState).not.toBeNull();
|
||||
|
||||
const parsed = JSON.parse(storedState!);
|
||||
expect(parsed.unpinnedTabs).toContain("terminal");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import {
|
||||
clearConversationLocalStorage,
|
||||
LOCAL_STORAGE_KEYS,
|
||||
} from "#/utils/conversation-local-storage";
|
||||
|
||||
describe("conversation localStorage utilities", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
describe("clearConversationLocalStorage", () => {
|
||||
it("removes the consolidated conversation-state localStorage entry", () => {
|
||||
const conversationId = "conv-123";
|
||||
|
||||
// Set up the consolidated key
|
||||
const consolidatedKey = `${LOCAL_STORAGE_KEYS.CONVERSATION_STATE}-${conversationId}`;
|
||||
localStorage.setItem(
|
||||
consolidatedKey,
|
||||
JSON.stringify({
|
||||
selectedTab: "editor",
|
||||
rightPanelShown: true,
|
||||
unpinnedTabs: [],
|
||||
}),
|
||||
);
|
||||
|
||||
clearConversationLocalStorage(conversationId);
|
||||
|
||||
expect(localStorage.getItem(consolidatedKey)).toBeNull();
|
||||
});
|
||||
|
||||
it("does not throw if conversation keys do not exist", () => {
|
||||
expect(() => {
|
||||
clearConversationLocalStorage("non-existent-id");
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,11 +15,10 @@ import { MemoryRouter, Route, Routes } from "react-router";
|
||||
import { useOptimisticUserMessageStore } from "#/stores/optimistic-user-message-store";
|
||||
import { useBrowserStore } from "#/stores/browser-store";
|
||||
import { useCommandStore } from "#/stores/command-store";
|
||||
import { useErrorMessageStore } from "#/stores/error-message-store";
|
||||
import {
|
||||
createMockMessageEvent,
|
||||
createMockUserMessageEvent,
|
||||
createMockConversationErrorEvent,
|
||||
createMockAgentErrorEvent,
|
||||
createMockBrowserObservationEvent,
|
||||
createMockBrowserNavigateActionEvent,
|
||||
createMockExecuteBashActionEvent,
|
||||
@@ -52,9 +51,6 @@ afterEach(() => {
|
||||
mswServer.resetHandlers();
|
||||
// Clean up any React components
|
||||
cleanup();
|
||||
// Reset stores to prevent state leakage between tests
|
||||
useErrorMessageStore.getState().removeErrorMessage();
|
||||
useEventStore.getState().clearEvents();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -281,23 +277,16 @@ describe("Conversation WebSocket Handler", () => {
|
||||
|
||||
// 5. Error Handling Tests
|
||||
describe("Error Handling & Recovery", () => {
|
||||
beforeEach(() => {
|
||||
// Clear stores before each error handling test to prevent state leakage
|
||||
useErrorMessageStore.getState().removeErrorMessage();
|
||||
useEventStore.getState().clearEvents();
|
||||
});
|
||||
|
||||
it("should update error message store on ConversationErrorEvent", async () => {
|
||||
// ConversationErrorEvent represents infrastructure/authentication errors
|
||||
// that should be shown as a banner to the user.
|
||||
const mockConversationErrorEvent = createMockConversationErrorEvent();
|
||||
it("should update error message store on AgentErrorEvent", async () => {
|
||||
// Create a mock AgentErrorEvent to send through WebSocket
|
||||
const mockAgentErrorEvent = createMockAgentErrorEvent();
|
||||
|
||||
// Set up MSW to send the error event when connection is established
|
||||
mswServer.use(
|
||||
wsLink.addEventListener("connection", ({ client, server }) => {
|
||||
server.connect();
|
||||
// Send the mock error event after connection
|
||||
client.send(JSON.stringify(mockConversationErrorEvent));
|
||||
client.send(JSON.stringify(mockAgentErrorEvent));
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -310,7 +299,7 @@ describe("Conversation WebSocket Handler", () => {
|
||||
// Wait for connection and error event processing
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("error-message")).toHaveTextContent(
|
||||
"Your session has expired. Please log in again.",
|
||||
"Failed to execute command: Permission denied",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -449,60 +438,6 @@ describe("Conversation WebSocket Handler", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should clear error message when a successful event is received after a ConversationErrorEvent", async () => {
|
||||
// This test verifies that error banners disappear when follow-up messages
|
||||
// are sent and received. Only ConversationErrorEvent sets the error banner,
|
||||
// and any non-error event should clear it.
|
||||
const conversationId = "test-conversation-error-clear";
|
||||
|
||||
// Set up MSW to mock event count API and send events
|
||||
mswServer.use(
|
||||
http.get(
|
||||
`http://localhost:3000/api/conversations/${conversationId}/events/count`,
|
||||
() => HttpResponse.json(2),
|
||||
),
|
||||
wsLink.addEventListener("connection", ({ client, server }) => {
|
||||
server.connect();
|
||||
|
||||
// Send a ConversationErrorEvent first (this sets the error banner)
|
||||
const mockConversationErrorEvent = createMockConversationErrorEvent();
|
||||
client.send(JSON.stringify(mockConversationErrorEvent));
|
||||
|
||||
// Send a successful (non-error) event immediately after
|
||||
// This simulates the user sending a follow-up message and receiving a response
|
||||
const mockSuccessEvent = createMockMessageEvent({
|
||||
id: "success-event-after-error",
|
||||
});
|
||||
client.send(JSON.stringify(mockSuccessEvent));
|
||||
}),
|
||||
);
|
||||
|
||||
// Verify error message store is initially empty
|
||||
expect(useErrorMessageStore.getState().errorMessage).toBeNull();
|
||||
|
||||
// Render with WebSocket context (minimal component just to trigger connection)
|
||||
renderWithWebSocketContext(
|
||||
<ConnectionStatusComponent />,
|
||||
conversationId,
|
||||
`http://localhost:3000/api/conversations/${conversationId}`,
|
||||
);
|
||||
|
||||
// Wait for connection
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("connection-state")).toHaveTextContent(
|
||||
"OPEN",
|
||||
);
|
||||
});
|
||||
|
||||
// Wait for both events to be received and error to be cleared
|
||||
// The error was set by the first event (ConversationErrorEvent),
|
||||
// then cleared by the second successful event (MessageEvent).
|
||||
await waitFor(() => {
|
||||
expect(useEventStore.getState().events.length).toBe(2);
|
||||
expect(useErrorMessageStore.getState().errorMessage).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not create duplicate events when WebSocket reconnects with resend_all=true", async () => {
|
||||
const conversationId = "test-conversation-reconnect";
|
||||
let connectionCount = 0;
|
||||
|
||||
97
frontend/package-lock.json
generated
97
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "openhands-frontend",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"dependencies": {
|
||||
"@heroui/react": "2.8.7",
|
||||
"@microlink/react-json-view": "^1.27.1",
|
||||
@@ -89,7 +89,7 @@
|
||||
"vitest": "^4.0.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
"node": ">=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@acemir/cssom": {
|
||||
@@ -192,7 +192,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -732,7 +731,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -779,7 +777,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -2348,7 +2345,6 @@
|
||||
"version": "2.4.25",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/system/-/system-2.4.25.tgz",
|
||||
"integrity": "sha512-F6UUoGTQ+Qas5wYkCzLjXE7u74Z9ygO0u0+dkTW7zCaY7ds65CcmvZ/ahKz2ES3Tk6TNks1MJSyaQ9rFLs8AqA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@heroui/react-utils": "2.1.14",
|
||||
"@heroui/system-rsc": "2.3.21",
|
||||
@@ -2428,7 +2424,6 @@
|
||||
"version": "2.4.25",
|
||||
"resolved": "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.25.tgz",
|
||||
"integrity": "sha512-nTptYhO1V9rMoh9SJDnMfaSmFuoXvbem1UuwgHcraRtqy/TIVBPqv26JEGzSoUCL194TDGOJpqrpMuab/PdXcw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@heroui/shared-utils": "2.1.12",
|
||||
"color": "^4.2.3",
|
||||
@@ -5431,7 +5426,6 @@
|
||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
@@ -5890,7 +5884,6 @@
|
||||
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
@@ -6071,14 +6064,6 @@
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/prismjs": {
|
||||
"version": "1.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz",
|
||||
@@ -6099,7 +6084,6 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -6140,7 +6124,6 @@
|
||||
"integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "7.18.0",
|
||||
@@ -6198,7 +6181,6 @@
|
||||
"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.18.0",
|
||||
"@typescript-eslint/types": "7.18.0",
|
||||
@@ -6737,7 +6719,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -7132,52 +7113,6 @@
|
||||
"@babel/types": "^7.23.6"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros/node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros/node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
@@ -7318,7 +7253,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -8007,8 +7941,7 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
@@ -8726,7 +8659,6 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@@ -8850,7 +8782,6 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -8931,7 +8862,6 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@@ -9023,7 +8953,6 @@
|
||||
"integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"aria-query": "^5.3.2",
|
||||
"array-includes": "^3.1.8",
|
||||
@@ -9118,7 +9047,6 @@
|
||||
"integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.8",
|
||||
"array.prototype.findlast": "^1.2.5",
|
||||
@@ -9152,7 +9080,6 @@
|
||||
"integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -9420,7 +9347,6 @@
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -10415,7 +10341,6 @@
|
||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||
}
|
||||
],
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
},
|
||||
@@ -11177,7 +11102,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz",
|
||||
"integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@acemir/cssom": "^0.9.28",
|
||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||
@@ -12884,7 +12808,6 @@
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
||||
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"dompurify": "3.2.7",
|
||||
"marked": "14.0.0"
|
||||
@@ -12977,7 +12900,6 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@inquirer/confirm": "^5.0.0",
|
||||
"@mswjs/interceptors": "^0.40.0",
|
||||
@@ -13702,7 +13624,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -13780,7 +13701,6 @@
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -14004,7 +13924,6 @@
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -14053,7 +13972,6 @@
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -14166,7 +14084,6 @@
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz",
|
||||
"integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
"set-cookie-parser": "^2.6.0"
|
||||
@@ -14528,7 +14445,6 @@
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@@ -15504,7 +15420,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
|
||||
"integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/dcastil"
|
||||
@@ -15631,7 +15546,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -15933,7 +15847,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16238,7 +16151,6 @@
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -16409,7 +16321,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "openhands-frontend",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
"node": ">=22.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/react": "2.8.7",
|
||||
@@ -121,7 +121,7 @@
|
||||
},
|
||||
"packageManager": "npm@10.5.0",
|
||||
"volta": {
|
||||
"node": "22.12.0"
|
||||
"node": "22.0.0"
|
||||
},
|
||||
"msw": {
|
||||
"workerDirectory": [
|
||||
|
||||
@@ -66,7 +66,7 @@ export function ChatInterface() {
|
||||
const posthog = usePostHog();
|
||||
const { setMessageToSend } = useConversationStore();
|
||||
const { data: conversation } = useActiveConversation();
|
||||
const { errorMessage, removeErrorMessage } = useErrorMessageStore();
|
||||
const { errorMessage } = useErrorMessageStore();
|
||||
const { isLoadingMessages } = useWsClient();
|
||||
const { isTask, taskStatus, taskDetail } = useTaskPolling();
|
||||
const conversationWebSocket = useConversationWebSocket();
|
||||
@@ -342,12 +342,7 @@ export function ChatInterface() {
|
||||
{!hitBottom && <ScrollToBottomButton onClick={scrollDomToBottom} />}
|
||||
</div>
|
||||
|
||||
{errorMessage && (
|
||||
<ErrorMessageBanner
|
||||
message={errorMessage}
|
||||
onDismiss={removeErrorMessage}
|
||||
/>
|
||||
)}
|
||||
{errorMessage && <ErrorMessageBanner message={errorMessage} />}
|
||||
|
||||
<InteractiveChatBox onSubmit={handleSendMessage} />
|
||||
</div>
|
||||
|
||||
@@ -1,87 +1,30 @@
|
||||
import React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Trans } from "react-i18next";
|
||||
import { Link } from "react-router";
|
||||
import { X } from "lucide-react";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { cn } from "#/utils/utils";
|
||||
import i18n from "#/i18n";
|
||||
|
||||
interface ErrorMessageBannerProps {
|
||||
message: string;
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
const DEFAULT_MAX_COLLAPSED_CHARS = 220;
|
||||
|
||||
export function ErrorMessageBanner({
|
||||
message,
|
||||
onDismiss,
|
||||
}: ErrorMessageBannerProps) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
|
||||
const isI18nKey = i18n.exists(message);
|
||||
const displayTextForLength = isI18nKey ? String(t(message)) : message;
|
||||
const shouldShowToggle =
|
||||
displayTextForLength.length > DEFAULT_MAX_COLLAPSED_CHARS;
|
||||
|
||||
const isCollapsed = shouldShowToggle && !isExpanded;
|
||||
|
||||
export function ErrorMessageBanner({ message }: ErrorMessageBannerProps) {
|
||||
return (
|
||||
<div
|
||||
className="w-full rounded-lg p-2 border border-[#FF0006] bg-[#4A0709] flex gap-2 items-start text-white"
|
||||
data-testid="error-message-banner"
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div
|
||||
className={cn(
|
||||
"whitespace-pre-wrap wrap-break-words",
|
||||
isCollapsed && "line-clamp-3",
|
||||
)}
|
||||
data-testid="error-message-banner-content"
|
||||
>
|
||||
{isI18nKey ? (
|
||||
<Trans
|
||||
i18nKey={message}
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="underline font-bold cursor-pointer"
|
||||
to="/settings/billing"
|
||||
>
|
||||
link
|
||||
</Link>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
message
|
||||
)}
|
||||
</div>
|
||||
|
||||
{shouldShowToggle && (
|
||||
<button
|
||||
type="button"
|
||||
className="mt-1 text-xs underline font-semibold cursor-pointer"
|
||||
onClick={() => setIsExpanded((prev) => !prev)}
|
||||
data-testid="error-message-banner-toggle"
|
||||
>
|
||||
{isExpanded
|
||||
? t(I18nKey.COMMON$VIEW_LESS)
|
||||
: t(I18nKey.COMMON$VIEW_MORE)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{onDismiss && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDismiss}
|
||||
className="shrink-0 rounded-md p-1 hover:bg-black/10 cursor-pointer"
|
||||
aria-label={t(I18nKey.BUTTON$CLOSE)}
|
||||
data-testid="error-message-banner-dismiss"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
<div className="w-full rounded-lg p-2 text-black border border-red-800 bg-red-500">
|
||||
{i18n.exists(message) ? (
|
||||
<Trans
|
||||
i18nKey={message}
|
||||
components={{
|
||||
a: (
|
||||
<Link
|
||||
className="underline font-bold cursor-pointer"
|
||||
to="/settings/billing"
|
||||
>
|
||||
link
|
||||
</Link>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
message
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocalStorage } from "@uidotdev/usehooks";
|
||||
import { ContextMenu } from "#/ui/context-menu";
|
||||
import { ContextMenuListItem } from "../../context-menu/context-menu-list-item";
|
||||
import { useClickOutsideElement } from "#/hooks/use-click-outside-element";
|
||||
import { useConversationId } from "#/hooks/use-conversation-id";
|
||||
import { useConversationLocalStorageState } from "#/utils/conversation-local-storage";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import TerminalIcon from "#/icons/terminal.svg?react";
|
||||
import GlobeIcon from "#/icons/globe.svg?react";
|
||||
@@ -14,6 +13,7 @@ import PillIcon from "#/icons/pill.svg?react";
|
||||
import PillFillIcon from "#/icons/pill-fill.svg?react";
|
||||
import { USE_PLANNING_AGENT } from "#/utils/feature-flags";
|
||||
import LessonPlanIcon from "#/icons/lesson-plan.svg?react";
|
||||
import { useConversationId } from "#/hooks/use-conversation-id";
|
||||
|
||||
interface ConversationTabsContextMenuProps {
|
||||
isOpen: boolean;
|
||||
@@ -27,8 +27,11 @@ export function ConversationTabsContextMenu({
|
||||
const ref = useClickOutsideElement<HTMLUListElement>(onClose);
|
||||
const { t } = useTranslation();
|
||||
const { conversationId } = useConversationId();
|
||||
const { state, setUnpinnedTabs } =
|
||||
useConversationLocalStorageState(conversationId);
|
||||
|
||||
const [unpinnedTabs, setUnpinnedTabs] = useLocalStorage<string[]>(
|
||||
`conversation-unpinned-tabs-${conversationId}`,
|
||||
[],
|
||||
);
|
||||
|
||||
const shouldUsePlanningAgent = USE_PLANNING_AGENT();
|
||||
|
||||
@@ -51,11 +54,11 @@ export function ConversationTabsContextMenu({
|
||||
if (!isOpen) return null;
|
||||
|
||||
const handleTabClick = (tab: string) => {
|
||||
if (state.unpinnedTabs.includes(tab)) {
|
||||
setUnpinnedTabs(state.unpinnedTabs.filter((item) => item !== tab));
|
||||
} else {
|
||||
setUnpinnedTabs([...state.unpinnedTabs, tab]);
|
||||
}
|
||||
setUnpinnedTabs((prev) =>
|
||||
prev.includes(tab)
|
||||
? prev.filter((tabItem) => tabItem !== tab)
|
||||
: [...prev, tab],
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -66,7 +69,7 @@ export function ConversationTabsContextMenu({
|
||||
className="mt-2 w-fit z-[9999]"
|
||||
>
|
||||
{tabConfig.map(({ tab, icon: Icon, i18nKey }) => {
|
||||
const pinned = !state.unpinnedTabs.includes(tab);
|
||||
const pinned = !unpinnedTabs.includes(tab);
|
||||
return (
|
||||
<ContextMenuListItem
|
||||
key={tab}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocalStorage } from "@uidotdev/usehooks";
|
||||
import TerminalIcon from "#/icons/terminal.svg?react";
|
||||
import GlobeIcon from "#/icons/globe.svg?react";
|
||||
import ServerIcon from "#/icons/server.svg?react";
|
||||
@@ -8,7 +9,6 @@ import VSCodeIcon from "#/icons/vscode.svg?react";
|
||||
import ThreeDotsVerticalIcon from "#/icons/three-dots-vertical.svg?react";
|
||||
import LessonPlanIcon from "#/icons/lesson-plan.svg?react";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { useConversationLocalStorageState } from "#/utils/conversation-local-storage";
|
||||
import { ConversationTabNav } from "./conversation-tab-nav";
|
||||
import { ChatActionTooltip } from "../../chat/chat-action-tooltip";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
@@ -32,11 +32,23 @@ export function ConversationTabs() {
|
||||
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const {
|
||||
state: persistedState,
|
||||
setSelectedTab: setPersistedSelectedTab,
|
||||
setRightPanelShown: setPersistedRightPanelShown,
|
||||
} = useConversationLocalStorageState(conversationId);
|
||||
// Persist selectedTab and isRightPanelShown in localStorage per conversation
|
||||
const [persistedSelectedTab, setPersistedSelectedTab] =
|
||||
useLocalStorage<ConversationTab | null>(
|
||||
`conversation-selected-tab-${conversationId}`,
|
||||
"editor",
|
||||
);
|
||||
|
||||
const [persistedIsRightPanelShown, setPersistedIsRightPanelShown] =
|
||||
useLocalStorage<boolean>(
|
||||
`conversation-right-panel-shown-${conversationId}`,
|
||||
true,
|
||||
);
|
||||
|
||||
const [persistedUnpinnedTabs] = useLocalStorage<string[]>(
|
||||
`conversation-unpinned-tabs-${conversationId}`,
|
||||
[],
|
||||
);
|
||||
|
||||
const shouldUsePlanningAgent = USE_PLANNING_AGENT();
|
||||
|
||||
@@ -49,13 +61,13 @@ export function ConversationTabs() {
|
||||
// Initialize Zustand state from localStorage on component mount
|
||||
useEffect(() => {
|
||||
// Initialize selectedTab from localStorage if available
|
||||
setSelectedTab(persistedState.selectedTab);
|
||||
setHasRightPanelToggled(persistedState.rightPanelShown);
|
||||
setSelectedTab(persistedSelectedTab);
|
||||
setHasRightPanelToggled(persistedIsRightPanelShown);
|
||||
}, [
|
||||
setSelectedTab,
|
||||
setHasRightPanelToggled,
|
||||
persistedState.selectedTab,
|
||||
persistedState.rightPanelShown,
|
||||
persistedSelectedTab,
|
||||
persistedIsRightPanelShown,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -77,13 +89,13 @@ export function ConversationTabs() {
|
||||
if (selectedTab === tab && isRightPanelShown) {
|
||||
// If clicking the same active tab, close the drawer
|
||||
setHasRightPanelToggled(false);
|
||||
setPersistedRightPanelShown(false);
|
||||
setPersistedIsRightPanelShown(false);
|
||||
} else {
|
||||
// If clicking a different tab or drawer is closed, open drawer and select tab
|
||||
onTabChange(tab);
|
||||
if (!isRightPanelShown) {
|
||||
setHasRightPanelToggled(true);
|
||||
setPersistedRightPanelShown(true);
|
||||
setPersistedIsRightPanelShown(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -154,7 +166,7 @@ export function ConversationTabs() {
|
||||
|
||||
// Filter out unpinned tabs
|
||||
const visibleTabs = tabs.filter(
|
||||
(tab) => !persistedState.unpinnedTabs.includes(tab.tabValue),
|
||||
(tab) => !persistedUnpinnedTabs.includes(tab.tabValue),
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -329,17 +329,16 @@ export function ConversationWebSocketProvider({
|
||||
if (isV1Event(event)) {
|
||||
addEvent(event);
|
||||
|
||||
// Handle ConversationErrorEvent specifically - show error banner
|
||||
// AgentErrorEvent errors are displayed inline in the chat, not as banners
|
||||
// Handle ConversationErrorEvent specifically
|
||||
if (isConversationErrorEvent(event)) {
|
||||
setErrorMessage(event.detail);
|
||||
} else {
|
||||
// Clear error message on any non-ConversationErrorEvent
|
||||
removeErrorMessage();
|
||||
}
|
||||
|
||||
// Track credit limit reached if AgentErrorEvent has budget-related error
|
||||
// Handle AgentErrorEvent specifically
|
||||
if (isAgentErrorEvent(event)) {
|
||||
setErrorMessage(event.error);
|
||||
|
||||
// Track credit limit reached if the error is budget-related
|
||||
if (isBudgetOrCreditError(event.error)) {
|
||||
trackCreditLimitReached({
|
||||
conversationId: conversationId || "unknown",
|
||||
@@ -418,7 +417,6 @@ export function ConversationWebSocketProvider({
|
||||
isLoadingHistoryMain,
|
||||
expectedEventCountMain,
|
||||
setErrorMessage,
|
||||
removeErrorMessage,
|
||||
removeOptimisticUserMessage,
|
||||
queryClient,
|
||||
conversationId,
|
||||
@@ -426,7 +424,6 @@ export function ConversationWebSocketProvider({
|
||||
appendInput,
|
||||
appendOutput,
|
||||
updateMetricsFromStats,
|
||||
trackCreditLimitReached,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import ConversationService from "#/api/conversation-service/conversation-service.api";
|
||||
import { clearConversationLocalStorage } from "#/utils/conversation-local-storage";
|
||||
|
||||
export const useDeleteConversation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -25,11 +24,6 @@ export const useDeleteConversation = () => {
|
||||
|
||||
return { previousConversations };
|
||||
},
|
||||
|
||||
onSuccess: (_, variables) => {
|
||||
clearConversationLocalStorage(variables.conversationId);
|
||||
},
|
||||
|
||||
onError: (err, variables, context) => {
|
||||
if (context?.previousConversations) {
|
||||
queryClient.setQueryData(
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { AgentStateChangeObservation } from "#/types/core/observations";
|
||||
import { MessageEvent } from "#/types/v1/core";
|
||||
import { AgentErrorEvent } from "#/types/v1/core/events/observation-event";
|
||||
import { ConversationErrorEvent } from "#/types/v1/core/events/conversation-state-event";
|
||||
import { MockSessionMessaage } from "./session-history.mock";
|
||||
|
||||
export const generateAgentStateChangeObservation = (
|
||||
@@ -237,19 +236,3 @@ export const createMockBrowserNavigateActionEvent = (
|
||||
llm_response_id: "llm-response-789",
|
||||
security_risk: { level: "low" },
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a mock ConversationErrorEvent for testing conversation-level error handling
|
||||
* These are infrastructure/authentication errors that should show error banners
|
||||
*/
|
||||
export const createMockConversationErrorEvent = (
|
||||
overrides: Partial<ConversationErrorEvent> = {},
|
||||
): ConversationErrorEvent => ({
|
||||
id: "conversation-error-123",
|
||||
timestamp: new Date().toISOString(),
|
||||
source: "environment",
|
||||
kind: "ConversationErrorEvent",
|
||||
code: "AuthenticationError",
|
||||
detail: "Your session has expired. Please log in again.",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
@@ -62,11 +62,6 @@ export interface ConversationState {
|
||||
}
|
||||
|
||||
interface ConversationStateUpdateEventBase extends BaseEvent {
|
||||
/**
|
||||
* Discriminator field for type guards
|
||||
*/
|
||||
kind: "ConversationStateUpdateEvent";
|
||||
|
||||
/**
|
||||
* The source is always "environment" for conversation state update events
|
||||
*/
|
||||
@@ -110,11 +105,6 @@ export type ConversationStateUpdateEvent =
|
||||
|
||||
// Conversation error event - contains error information
|
||||
export interface ConversationErrorEvent extends BaseEvent {
|
||||
/**
|
||||
* Discriminator field for type guards
|
||||
*/
|
||||
kind: "ConversationErrorEvent";
|
||||
|
||||
/**
|
||||
* The source is always "environment" for conversation error events
|
||||
*/
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import type { ConversationTab } from "#/stores/conversation-store";
|
||||
|
||||
export const LOCAL_STORAGE_KEYS = {
|
||||
CONVERSATION_STATE: "conversation-state",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Consolidated conversation state stored in a single localStorage key.
|
||||
*/
|
||||
export interface ConversationState {
|
||||
selectedTab: ConversationTab | null;
|
||||
rightPanelShown: boolean;
|
||||
unpinnedTabs: string[];
|
||||
}
|
||||
|
||||
const DEFAULT_CONVERSATION_STATE: ConversationState = {
|
||||
selectedTab: "editor",
|
||||
rightPanelShown: true,
|
||||
unpinnedTabs: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a conversation ID is a temporary task ID that should not be persisted.
|
||||
* Task IDs have the format "task-{uuid}" and are used during V1 conversation initialization.
|
||||
*/
|
||||
export function isTaskConversationId(conversationId: string): boolean {
|
||||
return conversationId.startsWith("task-");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full conversation state from localStorage.
|
||||
*/
|
||||
export function getConversationState(
|
||||
conversationId: string,
|
||||
): ConversationState {
|
||||
if (isTaskConversationId(conversationId)) {
|
||||
return DEFAULT_CONVERSATION_STATE;
|
||||
}
|
||||
try {
|
||||
const key = `${LOCAL_STORAGE_KEYS.CONVERSATION_STATE}-${conversationId}`;
|
||||
const item = localStorage.getItem(key);
|
||||
if (item !== null) {
|
||||
return { ...DEFAULT_CONVERSATION_STATE, ...JSON.parse(item) };
|
||||
}
|
||||
return DEFAULT_CONVERSATION_STATE;
|
||||
} catch {
|
||||
return DEFAULT_CONVERSATION_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the conversation state in localStorage, merging with existing state.
|
||||
*/
|
||||
export function setConversationState(
|
||||
conversationId: string,
|
||||
updates: Partial<ConversationState>,
|
||||
): void {
|
||||
if (isTaskConversationId(conversationId)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const key = `${LOCAL_STORAGE_KEYS.CONVERSATION_STATE}-${conversationId}`;
|
||||
const currentState = getConversationState(conversationId);
|
||||
const newState = { ...currentState, ...updates };
|
||||
localStorage.setItem(key, JSON.stringify(newState));
|
||||
} catch (err) {
|
||||
console.warn("Failed to set conversation localStorage", err);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearConversationLocalStorage(conversationId: string) {
|
||||
try {
|
||||
const key = `${LOCAL_STORAGE_KEYS.CONVERSATION_STATE}-${conversationId}`;
|
||||
localStorage.removeItem(key);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"Failed to clear conversation localStorage",
|
||||
conversationId,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook for conversation-scoped localStorage state.
|
||||
* Returns the full state and individual setters for each property.
|
||||
*/
|
||||
export function useConversationLocalStorageState(conversationId: string): {
|
||||
state: ConversationState;
|
||||
setSelectedTab: (tab: ConversationTab | null) => void;
|
||||
setRightPanelShown: (shown: boolean) => void;
|
||||
setUnpinnedTabs: (tabs: string[]) => void;
|
||||
} {
|
||||
const [state, setState] = useState<ConversationState>(() =>
|
||||
getConversationState(conversationId),
|
||||
);
|
||||
|
||||
const updateState = (updates: Partial<ConversationState>) => {
|
||||
setState((prev) => ({ ...prev, ...updates }));
|
||||
setConversationState(conversationId, updates);
|
||||
};
|
||||
|
||||
return {
|
||||
state,
|
||||
setSelectedTab: (tab) => updateState({ selectedTab: tab }),
|
||||
setRightPanelShown: (shown) => updateState({ rightPanelShown: shown }),
|
||||
setUnpinnedTabs: (tabs) => updateState({ unpinnedTabs: tabs }),
|
||||
};
|
||||
}
|
||||
@@ -43,6 +43,7 @@ export default defineConfig(({ mode }) => {
|
||||
"i18next-browser-languagedetector",
|
||||
"react-i18next",
|
||||
"axios",
|
||||
"date-fns",
|
||||
"@uidotdev/usehooks",
|
||||
"react-icons/fa6",
|
||||
"react-icons/fa",
|
||||
@@ -50,6 +51,8 @@ export default defineConfig(({ mode }) => {
|
||||
"tailwind-merge",
|
||||
"@heroui/react",
|
||||
"lucide-react",
|
||||
"react-select",
|
||||
"react-select/async",
|
||||
"@microlink/react-json-view",
|
||||
"socket.io-client",
|
||||
// These are discovered when launching conversations:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -465,9 +465,7 @@ async def _get_org_repository_url(
|
||||
Authenticated Git URL if successful, None otherwise
|
||||
"""
|
||||
try:
|
||||
remote_url = await user_context.get_authenticated_git_url(
|
||||
org_openhands_repo, is_optional=True
|
||||
)
|
||||
remote_url = await user_context.get_authenticated_git_url(org_openhands_repo)
|
||||
return remote_url
|
||||
except AuthenticationError as e:
|
||||
_logger.debug(
|
||||
|
||||
@@ -13,7 +13,7 @@ from openhands.sdk.utils.models import DiscriminatedUnionMixin
|
||||
|
||||
# The version of the agent server to use for deployments.
|
||||
# Typically this will be the same as the values from the pyproject.toml
|
||||
AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:10fff69-python'
|
||||
AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:7c91cbe-python'
|
||||
|
||||
|
||||
class SandboxSpecService(ABC):
|
||||
|
||||
@@ -63,13 +63,9 @@ class AuthUserContext(UserContext):
|
||||
self._provider_handler = provider_handler
|
||||
return provider_handler
|
||||
|
||||
async def get_authenticated_git_url(
|
||||
self, repository: str, is_optional: bool = False
|
||||
) -> str:
|
||||
async def get_authenticated_git_url(self, repository: str) -> str:
|
||||
provider_handler = await self.get_provider_handler()
|
||||
url = await provider_handler.get_authenticated_git_url(
|
||||
repository, is_optional=is_optional
|
||||
)
|
||||
url = await provider_handler.get_authenticated_git_url(repository)
|
||||
return url
|
||||
|
||||
async def get_latest_token(self, provider_type: ProviderType) -> str | None:
|
||||
|
||||
@@ -21,9 +21,7 @@ class SpecifyUserContext(UserContext):
|
||||
async def get_user_info(self) -> UserInfo:
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_authenticated_git_url(
|
||||
self, repository: str, is_optional: bool = False
|
||||
) -> str:
|
||||
async def get_authenticated_git_url(self, repository: str) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
|
||||
|
||||
@@ -23,16 +23,8 @@ class UserContext(ABC):
|
||||
"""Get the user info."""
|
||||
|
||||
@abstractmethod
|
||||
async def get_authenticated_git_url(
|
||||
self, repository: str, is_optional: bool = False
|
||||
) -> str:
|
||||
"""Get an authenticated git URL for a repository.
|
||||
|
||||
Args:
|
||||
repository: Repository name (owner/repo)
|
||||
is_optional: If True, logs at debug level instead of error level
|
||||
when repository is not found. Use for optional repositories.
|
||||
"""
|
||||
async def get_authenticated_git_url(self, repository: str) -> str:
|
||||
"""Get the provider tokens for the user"""
|
||||
|
||||
@abstractmethod
|
||||
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -57,9 +57,7 @@ class ForgejoMixinBase(BaseGitService, HTTPClient):
|
||||
self.base_url = self.BASE_URL # Backwards compatibility for existing usage
|
||||
parsed = urlparse(self.BASE_URL)
|
||||
self.base_domain = parsed.netloc or self.DEFAULT_DOMAIN
|
||||
# Preserve the protocol from BASE_URL (http or https)
|
||||
protocol = parsed.scheme or 'https'
|
||||
self.web_base_url = f'{protocol}://{self.base_domain}'.rstrip('/')
|
||||
self.web_base_url = f'https://{self.base_domain}'.rstrip('/')
|
||||
|
||||
@property
|
||||
def provider(self) -> str:
|
||||
|
||||
@@ -675,22 +675,6 @@ class ProviderHandler:
|
||||
if provider != ProviderType.AZURE_DEVOPS:
|
||||
domain = self.provider_tokens[provider].host or domain
|
||||
|
||||
# Detect protocol before normalizing domain
|
||||
# Default to https, but preserve http if explicitly specified
|
||||
protocol = 'https'
|
||||
if domain and domain.strip().startswith('http://'):
|
||||
# Check if insecure HTTP access is allowed
|
||||
allow_insecure = os.environ.get(
|
||||
'ALLOW_INSECURE_GIT_ACCESS', 'false'
|
||||
).lower() in ('true', '1', 'yes')
|
||||
if not allow_insecure:
|
||||
raise ValueError(
|
||||
'Attempting to connect to an insecure git repository over HTTP. '
|
||||
"If you'd like to allow this nonetheless, set "
|
||||
'ALLOW_INSECURE_GIT_ACCESS=true as an environment variable.'
|
||||
)
|
||||
protocol = 'http'
|
||||
|
||||
# Normalize domain to prevent double protocols or path segments
|
||||
if domain:
|
||||
domain = domain.strip()
|
||||
@@ -706,18 +690,16 @@ class ProviderHandler:
|
||||
token_value = git_token.get_secret_value()
|
||||
if provider == ProviderType.GITLAB:
|
||||
remote_url = (
|
||||
f'{protocol}://oauth2:{token_value}@{domain}/{repo_name}.git'
|
||||
f'https://oauth2:{token_value}@{domain}/{repo_name}.git'
|
||||
)
|
||||
elif provider == ProviderType.BITBUCKET:
|
||||
# For Bitbucket, handle username:app_password format
|
||||
if ':' in token_value:
|
||||
# App token format: username:app_password
|
||||
remote_url = (
|
||||
f'{protocol}://{token_value}@{domain}/{repo_name}.git'
|
||||
)
|
||||
remote_url = f'https://{token_value}@{domain}/{repo_name}.git'
|
||||
else:
|
||||
# Access token format: use x-token-auth
|
||||
remote_url = f'{protocol}://x-token-auth:{token_value}@{domain}/{repo_name}.git'
|
||||
remote_url = f'https://x-token-auth:{token_value}@{domain}/{repo_name}.git'
|
||||
elif provider == ProviderType.AZURE_DEVOPS:
|
||||
# Azure DevOps uses PAT with Basic auth
|
||||
# Format: https://{anything}:{PAT}@dev.azure.com/{org}/{project}/_git/{repo}
|
||||
@@ -777,11 +759,11 @@ class ProviderHandler:
|
||||
)
|
||||
else:
|
||||
# GitHub, Forgejo
|
||||
remote_url = f'{protocol}://{token_value}@{domain}/{repo_name}.git'
|
||||
remote_url = f'https://{token_value}@{domain}/{repo_name}.git'
|
||||
else:
|
||||
remote_url = f'{protocol}://{domain}/{repo_name}.git'
|
||||
remote_url = f'https://{domain}/{repo_name}.git'
|
||||
else:
|
||||
remote_url = f'{protocol}://{domain}/{repo_name}.git'
|
||||
remote_url = f'https://{domain}/{repo_name}.git'
|
||||
|
||||
return remote_url
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# IMPORTANT: LEGACY V0 CODE - Deprecated since version 1.0.0, scheduled for removal April 1, 2026
|
||||
# IMPORTANT: LEGACY V0 CODE
|
||||
# This file is part of the legacy (V0) implementation of OpenHands and will be removed soon as we complete the migration to V1.
|
||||
# OpenHands V1 uses the Software Agent SDK for the agentic core and runs a new application server. Please refer to:
|
||||
# - V1 agentic core (SDK): https://github.com/OpenHands/software-agent-sdk
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user