Compare commits

...

13 Commits

Author SHA1 Message Date
tofarr
1a3354404e Fix text wrap on context menu (#7468) 2025-03-24 16:44:04 +00:00
Ryan H. Tran
80279f9d36 Upgrade openhands-aci to 0.2.7 (#7462) 2025-03-25 00:15:59 +08:00
Zach
0b3d15a4d7 Fix missing 'fi' statement in GAIA benchmark scripts/run_infer.sh (#7465) 2025-03-24 16:04:25 +00:00
Marco Dalalba
8b68d086f0 fix #7267: adding base url to axios (#7267) 2025-03-24 09:25:52 -04:00
Engel Nyst
0f143a43c9 Add support for .openhands/setup.sh script in all entry points (#7459)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-24 13:37:08 +01:00
Engel Nyst
e61e4d57d9 Fix #7451: Add guidance to use git add . in system prompt (#7458)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
Co-authored-by: Robert Brennan <accounts@rbren.io>
2025-03-24 13:33:45 +01:00
Xingyao Wang
4e86bdf3d9 (frontend): Implement BrowseInteractiveAction in frontend (#7452)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-23 22:17:56 -04:00
Robert Brennan
3cef499b81 Fix conversation list: remove GitHub link and show created_at date (#7435)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-23 20:08:02 -06:00
Xingyao Wang
e2a0884ecd Update repo.md to remind the agent about PR template (#7456) 2025-03-24 02:03:56 +00:00
Robert Brennan
2849974729 [WIP] better code display (#7453)
Co-authored-by: openhands <openhands@all-hands.dev>
2025-03-23 18:10:07 -07:00
Kento Sugita
daa4af18d1 fix timeout to impove stability (#7443) 2025-03-23 15:06:05 -07:00
Xingyao Wang
1ec1076fee [microagent] Add pdflatex microagent (#7444)
Co-authored-by: Robert Brennan <accounts@rbren.io>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-03-24 03:08:22 +08:00
tofarr
65bd4be607 Fix for broken vscode url (#7442) 2025-03-23 10:17:12 -06:00
34 changed files with 227 additions and 95 deletions

View File

@@ -41,3 +41,8 @@ Frontend:
- Available variables: VITE_BACKEND_HOST, VITE_USE_TLS, VITE_INSECURE_SKIP_VERIFY, VITE_FRONTEND_PORT
- Internationalization:
- Generate i18n declaration file: `npm run make-i18n`
## Template for Github Pull Request
If you are starting a pull request (PR), please follow the template in `.github/pull_request_template.md`.

View File

@@ -57,7 +57,7 @@ docker run -it --rm --pull=always \
```
> [!WARNING]
> On a public network? See our [Hardened Docker Installation](https://docs.all-hands.dev/modules/usage/runtimes/docker#hardened-docker-installation) guide
> On a public network? See our [Hardened Docker Installation](https://docs.all-hands.dev/modules/usage/runtimes/docker#hardened-docker-installation) guide
> to secure your deployment by restricting network binding and implementing additional security measures.
You'll find OpenHands running at [http://localhost:3000](http://localhost:3000)!

View File

@@ -21,4 +21,4 @@ OpenHands supports several different runtime environments:
- [OpenHands Remote Runtime](./runtimes/remote.md) - Cloud-based runtime for parallel execution (beta)
- [Modal Runtime](./runtimes/modal.md) - Runtime provided by our partners at Modal
- [Daytona Runtime](./runtimes/daytona.md) - Runtime provided by Daytona
- [Local Runtime](./runtimes/local.md) - Direct execution on your local machine without Docker
- [Local Runtime](./runtimes/local.md) - Direct execution on your local machine without Docker

View File

@@ -29,4 +29,4 @@ bash -i <(curl -sL https://get.daytona.io/openhands)
Once executed, OpenHands should be running locally and ready for use.
For more details and manual initialization, view the entire [README.md](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/impl/daytona/README.md)
For more details and manual initialization, view the entire [README.md](https://github.com/All-Hands-AI/OpenHands/blob/main/openhands/runtime/impl/daytona/README.md)

View File

@@ -59,4 +59,4 @@ The Local Runtime is particularly useful for:
- CI/CD pipelines where Docker is not available.
- Testing and development of OpenHands itself.
- Environments where container usage is restricted.
- Scenarios where direct file system access is required.
- Scenarios where direct file system access is required.

View File

@@ -10,4 +10,4 @@ docker run # ...
-e RUNTIME=modal \
-e MODAL_API_TOKEN_ID="your-id" \
-e MODAL_API_TOKEN_SECRET="your-secret" \
```
```

View File

@@ -3,4 +3,4 @@
OpenHands Remote Runtime is currently in beta (read [here](https://runtime.all-hands.dev/) for more details), it allows you to launch runtimes in parallel in the cloud.
Fill out [this form](https://docs.google.com/forms/d/e/1FAIpQLSckVz_JFwg2_mOxNZjCtr7aoBFI2Mwdan3f75J_TrdMS1JV2g/viewform) to apply if you want to try this out!
NOTE: This runtime is specifically designed for agent evaluation purposes only through [OpenHands evaluation harness](https://github.com/All-Hands-AI/OpenHands/tree/main/evaluation). It should not be used to launch production OpenHands applications.
NOTE: This runtime is specifically designed for agent evaluation purposes only through [OpenHands evaluation harness](https://github.com/All-Hands-AI/OpenHands/tree/main/evaluation). It should not be used to launch production OpenHands applications.

View File

@@ -53,6 +53,7 @@ fi
if [ -n "$AGENT_CONFIG" ]; then
echo "AGENT_CONFIG: $AGENT_CONFIG"
COMMAND="$COMMAND --agent-config $AGENT_CONFIG"
fi
# Run the command
eval $COMMAND

View File

@@ -9,67 +9,67 @@ describe("formatTimeDelta", () => {
it("formats the yearly time correctly", () => {
const oneYearAgo = new Date("2023-01-01T00:00:00Z");
expect(formatTimeDelta(oneYearAgo)).toBe("1 year");
expect(formatTimeDelta(oneYearAgo)).toBe("1y");
const twoYearsAgo = new Date("2022-01-01T00:00:00Z");
expect(formatTimeDelta(twoYearsAgo)).toBe("2 years");
expect(formatTimeDelta(twoYearsAgo)).toBe("2y");
const threeYearsAgo = new Date("2021-01-01T00:00:00Z");
expect(formatTimeDelta(threeYearsAgo)).toBe("3 years");
expect(formatTimeDelta(threeYearsAgo)).toBe("3y");
});
it("formats the monthly time correctly", () => {
const oneMonthAgo = new Date("2023-12-01T00:00:00Z");
expect(formatTimeDelta(oneMonthAgo)).toBe("1 month");
expect(formatTimeDelta(oneMonthAgo)).toBe("1mo");
const twoMonthsAgo = new Date("2023-11-01T00:00:00Z");
expect(formatTimeDelta(twoMonthsAgo)).toBe("2 months");
expect(formatTimeDelta(twoMonthsAgo)).toBe("2mo");
const threeMonthsAgo = new Date("2023-10-01T00:00:00Z");
expect(formatTimeDelta(threeMonthsAgo)).toBe("3 months");
expect(formatTimeDelta(threeMonthsAgo)).toBe("3mo");
});
it("formats the daily time correctly", () => {
const oneDayAgo = new Date("2023-12-31T00:00:00Z");
expect(formatTimeDelta(oneDayAgo)).toBe("1 day");
expect(formatTimeDelta(oneDayAgo)).toBe("1d");
const twoDaysAgo = new Date("2023-12-30T00:00:00Z");
expect(formatTimeDelta(twoDaysAgo)).toBe("2 days");
expect(formatTimeDelta(twoDaysAgo)).toBe("2d");
const threeDaysAgo = new Date("2023-12-29T00:00:00Z");
expect(formatTimeDelta(threeDaysAgo)).toBe("3 days");
expect(formatTimeDelta(threeDaysAgo)).toBe("3d");
});
it("formats the hourly time correctly", () => {
const oneHourAgo = new Date("2023-12-31T23:00:00Z");
expect(formatTimeDelta(oneHourAgo)).toBe("1 hour");
expect(formatTimeDelta(oneHourAgo)).toBe("1h");
const twoHoursAgo = new Date("2023-12-31T22:00:00Z");
expect(formatTimeDelta(twoHoursAgo)).toBe("2 hours");
expect(formatTimeDelta(twoHoursAgo)).toBe("2h");
const threeHoursAgo = new Date("2023-12-31T21:00:00Z");
expect(formatTimeDelta(threeHoursAgo)).toBe("3 hours");
expect(formatTimeDelta(threeHoursAgo)).toBe("3h");
});
it("formats the minute time correctly", () => {
const oneMinuteAgo = new Date("2023-12-31T23:59:00Z");
expect(formatTimeDelta(oneMinuteAgo)).toBe("1 minute");
expect(formatTimeDelta(oneMinuteAgo)).toBe("1m");
const twoMinutesAgo = new Date("2023-12-31T23:58:00Z");
expect(formatTimeDelta(twoMinutesAgo)).toBe("2 minutes");
expect(formatTimeDelta(twoMinutesAgo)).toBe("2m");
const threeMinutesAgo = new Date("2023-12-31T23:57:00Z");
expect(formatTimeDelta(threeMinutesAgo)).toBe("3 minutes");
expect(formatTimeDelta(threeMinutesAgo)).toBe("3m");
});
it("formats the second time correctly", () => {
const oneSecondAgo = new Date("2023-12-31T23:59:59Z");
expect(formatTimeDelta(oneSecondAgo)).toBe("1 second");
expect(formatTimeDelta(oneSecondAgo)).toBe("1s");
const twoSecondsAgo = new Date("2023-12-31T23:59:58Z");
expect(formatTimeDelta(twoSecondsAgo)).toBe("2 seconds");
expect(formatTimeDelta(twoSecondsAgo)).toBe("2s");
const threeSecondsAgo = new Date("2023-12-31T23:59:57Z");
expect(formatTimeDelta(threeSecondsAgo)).toBe("3 seconds");
expect(formatTimeDelta(threeSecondsAgo)).toBe("3s");
});
});

View File

@@ -1,7 +1,8 @@
import axios from "axios";
export const openHands = axios.create();
export const openHands = axios.create({
baseURL: `${window.location.protocol}//${import.meta.env.VITE_BACKEND_BASE_URL || window?.location.host}`,
});
export const setAuthTokenHeader = (token: string) => {
openHands.defaults.headers.common.Authorization = `Bearer ${token}`;
};

View File

@@ -20,7 +20,7 @@ export function ContextMenuListItem({
disabled={isDisabled}
className={cn(
"text-sm px-4 py-2 w-full text-start hover:bg-white/10 first-of-type:rounded-t-md last-of-type:rounded-b-md",
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent text-nowrap",
)}
>
{children}

View File

@@ -18,7 +18,7 @@ export function ContextMenu({
<ul
data-testid={testId}
ref={ref}
className={cn("bg-tertiary rounded-md w-[140px]", className)}
className={cn("bg-tertiary rounded-md", className)}
>
{children}
</ul>

View File

@@ -22,11 +22,14 @@ interface ConversationCardProps {
title: string;
selectedRepository: string | null;
lastUpdatedAt: string; // ISO 8601
createdAt?: string; // ISO 8601
status?: ProjectStatus;
variant?: "compact" | "default";
conversationId?: string; // Optional conversation ID for VS Code URL
}
const MAX_TIME_BETWEEN_CREATION_AND_UPDATE = 1000 * 60 * 30; // 30 minutes
export function ConversationCard({
onClick,
onDelete,
@@ -35,7 +38,10 @@ export function ConversationCard({
isActive,
title,
selectedRepository,
// lastUpdatedAt is kept in props for backward compatibility
// eslint-disable-next-line @typescript-eslint/no-unused-vars
lastUpdatedAt,
createdAt,
status = "STOPPED",
variant = "default",
conversationId,
@@ -105,11 +111,10 @@ export function ConversationCard({
if (data.vscode_url) {
window.open(data.vscode_url, "_blank");
} else {
console.error("VS Code URL not available", data.error);
}
// VS Code URL not available
} catch (error) {
console.error("Failed to fetch VS Code URL", error);
// Failed to fetch VS Code URL
}
}
@@ -128,6 +133,12 @@ export function ConversationCard({
}, [titleMode]);
const hasContextMenu = !!(onDelete || onChangeTitle || showDisplayCostOption);
const timeBetweenUpdateAndCreation = createdAt
? new Date(lastUpdatedAt).getTime() - new Date(createdAt).getTime()
: 0;
const showUpdateTime =
createdAt &&
timeBetweenUpdateAndCreation > MAX_TIME_BETWEEN_CREATION_AND_UPDATE;
return (
<>
@@ -205,7 +216,16 @@ export function ConversationCard({
<ConversationRepoLink selectedRepository={selectedRepository} />
)}
<p className="text-xs text-neutral-400">
<time>{formatTimeDelta(new Date(lastUpdatedAt))} ago</time>
<span>Created </span>
<time>
{formatTimeDelta(new Date(createdAt || lastUpdatedAt))} ago
</time>
{showUpdateTime && (
<>
<span>, updated </span>
<time>{formatTimeDelta(new Date(lastUpdatedAt))} ago</time>
</>
)}
</p>
</div>
</div>

View File

@@ -108,7 +108,9 @@ export function ConversationPanel({ onClose }: ConversationPanelProps) {
title={project.title}
selectedRepository={project.selected_repository}
lastUpdatedAt={project.last_updated_at}
createdAt={project.created_at}
status={project.status}
conversationId={project.conversation_id}
/>
)}
</NavLink>

View File

@@ -5,23 +5,12 @@ interface ConversationRepoLinkProps {
export function ConversationRepoLink({
selectedRepository,
}: ConversationRepoLinkProps) {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
window.open(
`https://github.com/${selectedRepository}`,
"_blank",
"noopener,noreferrer",
);
};
return (
<button
type="button"
<span
data-testid="conversation-card-selected-repository"
onClick={handleClick}
className="text-xs text-neutral-400 hover:text-neutral-200"
className="text-xs text-neutral-400"
>
{selectedRepository}
</button>
</span>
);
}

View File

@@ -12,9 +12,7 @@ export const useVSCodeUrl = (config: { enabled: boolean }) => {
return OpenHands.getVSCodeUrl(conversationId);
},
enabled: !!conversationId && config.enabled,
refetchOnMount: false,
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 15, // 15 minutes
refetchOnMount: true,
});
return data;

View File

@@ -281,6 +281,7 @@ export enum I18nKey {
ACTION_MESSAGE$EDIT = "ACTION_MESSAGE$EDIT",
ACTION_MESSAGE$WRITE = "ACTION_MESSAGE$WRITE",
ACTION_MESSAGE$BROWSE = "ACTION_MESSAGE$BROWSE",
ACTION_MESSAGE$BROWSE_INTERACTIVE = "ACTION_MESSAGE$BROWSE_INTERACTIVE",
ACTION_MESSAGE$THINK = "ACTION_MESSAGE$THINK",
OBSERVATION_MESSAGE$RUN = "OBSERVATION_MESSAGE$RUN",
OBSERVATION_MESSAGE$RUN_IPYTHON = "OBSERVATION_MESSAGE$RUN_IPYTHON",

View File

@@ -4193,6 +4193,21 @@
"es": "Navegando en la web",
"tr": "Web'de geziniyor"
},
"ACTION_MESSAGE$BROWSE_INTERACTIVE": {
"en": "Interactive browsing in progress...",
"zh-CN": "交互式浏览进行中...",
"zh-TW": "互動式瀏覽進行中...",
"ko-KR": "인터랙티브 브라우징 진행 중...",
"ja": "インタラクティブブラウジング進行中...",
"no": "Interaktiv surfing pågår...",
"ar": "التصفح التفاعلي قيد التقدم...",
"de": "Interaktives Browsen läuft...",
"fr": "Navigation interactive en cours...",
"it": "Navigazione interattiva in corso...",
"pt": "Navegação interativa em andamento...",
"es": "Navegación interactiva en progreso...",
"tr": "Etkileşimli tarama devam ediyor..."
},
"ACTION_MESSAGE$THINK": {
"en": "Thinking",
"zh-CN": "思考",

View File

@@ -78,20 +78,18 @@ function FileViewer() {
<div className="flex h-full bg-base-secondary relative">
<FileExplorer isOpen={fileExplorerIsOpen} onToggle={toggleFileExplorer} />
<div className="w-full h-full flex flex-col">
{selectedPath && (
<div className="flex w-full items-center justify-between self-end p-2">
<span className="text-sm text-neutral-500">{selectedPath}</span>
</div>
)}
{selectedPath && files[selectedPath] && (
<div className="p-4 flex-1 overflow-auto">
<div className="h-full w-full overflow-auto">
<SyntaxHighlighter
language={getLanguageFromPath(selectedPath)}
style={vscDarkPlus}
customStyle={{
margin: 0,
padding: "10px",
height: "100%",
background: "#171717",
fontSize: "0.875rem",
borderRadius: 0,
}}
>
{files[selectedPath]}

View File

@@ -30,6 +30,7 @@ export function handleObservationMessage(message: ObservationMessage) {
store.dispatch(appendJupyterOutput(message.content));
break;
case ObservationType.BROWSE:
case ObservationType.BROWSE_INTERACTIVE:
if (message.extras?.screenshot) {
store.dispatch(setScreenshotSrc(message.extras?.screenshot));
}
@@ -178,6 +179,46 @@ export function handleObservationMessage(message: ObservationMessage) {
}),
);
break;
case "browse_interactive":
store.dispatch(
addAssistantObservation({
...baseObservation,
observation: "browse_interactive" as const,
extras: {
url: String(message.extras.url || ""),
screenshot: String(message.extras.screenshot || ""),
error: Boolean(message.extras.error),
open_page_urls: Array.isArray(message.extras.open_page_urls)
? message.extras.open_page_urls
: [],
active_page_index: Number(message.extras.active_page_index || 0),
dom_object:
typeof message.extras.dom_object === "object"
? (message.extras.dom_object as Record<string, unknown>)
: {},
axtree_object:
typeof message.extras.axtree_object === "object"
? (message.extras.axtree_object as Record<string, unknown>)
: {},
extra_element_properties:
typeof message.extras.extra_element_properties === "object"
? (message.extras.extra_element_properties as Record<
string,
unknown
>)
: {},
last_browser_action: String(
message.extras.last_browser_action || "",
),
last_browser_action_error:
message.extras.last_browser_action_error,
focused_element_bid: String(
message.extras.focused_element_bid || "",
),
},
}),
);
break;
case "error":
store.dispatch(
addAssistantObservation({

View File

@@ -20,6 +20,7 @@ const HANDLED_ACTIONS: OpenHandsEventType[] = [
"write",
"read",
"browse",
"browse_interactive",
"edit",
];
@@ -108,6 +109,9 @@ export const chatSlice = createSlice({
text = `${action.payload.args.path}\n${content}`;
} else if (actionID === "browse") {
text = `Browsing ${action.payload.args.url}`;
} else if (actionID === "browse_interactive") {
// Include the browser_actions in the content
text = `**Action:**\n\n\`\`\`python\n${action.payload.args.browser_actions}\n\`\`\``;
}
if (actionID === "run" || actionID === "run_ipython") {
if (
@@ -127,6 +131,7 @@ export const chatSlice = createSlice({
imageUrls: [],
timestamp: new Date().toISOString(),
};
state.messages.push(message);
},
@@ -191,11 +196,11 @@ export const chatSlice = createSlice({
} else if (observationID === "browse") {
let content = `**URL:** ${observation.payload.extras.url}\n`;
if (observation.payload.extras.error) {
content += `**Error:**\n${observation.payload.extras.error}\n`;
content += `\n\n**Error:**\n${observation.payload.extras.error}\n`;
}
content += `**Output:**\n${observation.payload.content}`;
content += `\n\n**Output:**\n${observation.payload.content}`;
if (content.length > MAX_CONTENT_LENGTH) {
content = `${content.slice(0, MAX_CONTENT_LENGTH)}...`;
content = `${content.slice(0, MAX_CONTENT_LENGTH)}...(truncated)`;
}
causeMessage.content = content;
}

View File

@@ -51,6 +51,24 @@ export interface BrowseObservation extends OpenHandsObservationEvent<"browse"> {
};
}
export interface BrowseInteractiveObservation
extends OpenHandsObservationEvent<"browse_interactive"> {
source: "agent";
extras: {
url: string;
screenshot: string;
error: boolean;
open_page_urls: string[];
active_page_index: number;
dom_object: Record<string, unknown>;
axtree_object: Record<string, unknown>;
extra_element_properties: Record<string, unknown>;
last_browser_action: string;
last_browser_action_error: unknown;
focused_element_bid: string;
};
}
export interface WriteObservation extends OpenHandsObservationEvent<"write"> {
source: "agent";
extras: {
@@ -98,6 +116,7 @@ export type OpenHandsObservation =
| IPythonObservation
| DelegateObservation
| BrowseObservation
| BrowseInteractiveObservation
| WriteObservation
| ReadObservation
| EditObservation

View File

@@ -8,6 +8,9 @@ enum ObservationType {
// The HTML contents of a URL
BROWSE = "browse",
// Interactive browsing
BROWSE_INTERACTIVE = "browse_interactive",
// The output of a command
RUN = "run",

View File

@@ -1,12 +1,12 @@
/**
* Formats a date into a human-readable string representing the time delta between the given date and the current date.
* Formats a date into a compact string representing the time delta between the given date and the current date.
* @param date The date to format
* @returns A human-readable string representing the time delta between the given date and the current date
* @returns A compact string representing the time delta between the given date and the current date
*
* @example
* // now is 2024-01-01T00:00:00Z
* formatTimeDelta(new Date("2023-12-31T23:59:59Z")); // "1 second"
* formatTimeDelta(new Date("2022-01-01T00:00:00Z")); // "2 years"
* formatTimeDelta(new Date("2023-12-31T23:59:59Z")); // "1s"
* formatTimeDelta(new Date("2022-01-01T00:00:00Z")); // "2y"
*/
export const formatTimeDelta = (date: Date) => {
const now = new Date();
@@ -19,11 +19,10 @@ export const formatTimeDelta = (date: Date) => {
const months = Math.floor(days / 30);
const years = Math.floor(months / 12);
if (seconds < 60) return seconds === 1 ? "1 second" : `${seconds} seconds`;
if (minutes < 60) return minutes === 1 ? "1 minute" : `${minutes} minutes`;
if (hours < 24) return hours === 1 ? "1 hour" : `${hours} hours`;
if (days < 30) return days === 1 ? "1 day" : `${days} days`;
if (months < 12) return months === 1 ? "1 month" : `${months} months`;
return years === 1 ? "1 year" : `${years} years`;
if (seconds < 60) return `${seconds}s`;
if (minutes < 60) return `${minutes}m`;
if (hours < 24) return `${hours}h`;
if (days < 30) return `${days}d`;
if (months < 12) return `${months}mo`;
return `${years}y`;
};

View File

@@ -0,0 +1,36 @@
---
name: pdflatex
type: knowledge
version: 1.0.0
agent: CodeActAgent
triggers:
- pdflatex
---
PdfLatex is a tool that converts Latex sources into PDF. This is specifically very important for researchers, as they use it to publish their findings. It could be installed very easily using Linux terminal, though this seems an annoying task on Windows. Installation commands are given below.
* Install the TexLive base
```
apt-get install texlive-latex-base
```
* Also install the recommended and extra fonts to avoid running into errors, when trying to use pdflatex on latex files with more fonts.
```
apt-get install texlive-fonts-recommended
apt-get install texlive-fonts-extra
```
* Install the extra packages,
```
apt-get install texlive-latex-extra
```
Once installed as above, you may be able to create PDF files from latex sources using PdfLatex as below.
```
pdflatex latex_source_name.tex
```
Ref: http://kkpradeeban.blogspot.com/2014/04/installing-latexpdflatex-on-ubuntu.html

View File

@@ -26,6 +26,7 @@ Your primary role is to assist users by executing commands, modifying code, and
<VERSION_CONTROL>
* When configuring git credentials, use "openhands" as the user.name and "openhands@all-hands.dev" as the user.email by default, unless explicitly instructed otherwise.
* Exercise caution with git operations. Do NOT make potentially dangerous changes (e.g., pushing to main, deleting repositories) unless explicitly asked to do so.
* When committing changes, use `git status` to see all modified files, and stage all files necessary for the commit. Use `git commit -a` whenever possible.
</VERSION_CONTROL>
<PROBLEM_SOLVING_WORKFLOW>

View File

@@ -122,6 +122,8 @@ def initialize_repository_for_runtime(
selected_repository,
None,
)
# Run setup script if it exists
runtime.maybe_run_setup_script()
return repo_directory

View File

@@ -238,7 +238,7 @@ class ActionExecutor:
await wait_all(
(self._init_plugin(plugin) for plugin in self.plugins_to_load),
timeout=30,
timeout=60,
)
logger.debug('All plugins initialized')

View File

@@ -60,7 +60,7 @@ class ActionExecutionClient(Runtime):
attach_to_existing: bool = False,
headless_mode: bool = True,
user_id: str | None = None,
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None
git_provider_tokens: PROVIDER_TOKEN_TYPE | None = None,
):
self.session = HttpSession()
self.action_semaphore = threading.Semaphore(1) # Ensure one action at a time

View File

@@ -17,6 +17,8 @@ class VSCodeRequirement(PluginRequirement):
class VSCodePlugin(Plugin):
name: str = 'vscode'
vscode_port: int | None = None
vscode_connection_token: str | None = None
async def initialize(self, username: str):
if username not in ['root', 'openhands']:

View File

@@ -1,4 +1,3 @@
from types import MappingProxyType
from pydantic import Field
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
@@ -16,4 +15,4 @@ class ConversationInitData(Settings):
model_config = {
'arbitrary_types_allowed': True,
}
}

View File

@@ -23,7 +23,6 @@ from openhands.events.observation import (
from openhands.events.observation.error import ErrorObservation
from openhands.events.serialization import event_from_dict, event_to_dict
from openhands.events.stream import EventStreamSubscriber
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
from openhands.llm.llm import LLM
from openhands.server.session.agent_session import AgentSession
from openhands.server.session.conversation_init_data import ConversationInitData

36
poetry.lock generated
View File

@@ -4943,18 +4943,20 @@ realtime = ["websockets (>=13,<15)"]
[[package]]
name = "openhands-aci"
version = "0.2.6"
version = "0.2.7"
description = "An Agent-Computer Interface (ACI) designed for software development agents OpenHands."
optional = false
python-versions = "^3.12"
python-versions = "<4.0,>=3.12"
groups = ["main"]
files = []
develop = false
files = [
{file = "openhands_aci-0.2.7-py3-none-any.whl", hash = "sha256:6b36fa465db6643d909efdf40ec303d27a03e6c9f568447df4bc1d9fdd7104b2"},
{file = "openhands_aci-0.2.7.tar.gz", hash = "sha256:892c33d741e94b78ec65df178afe018869e6039ea484f7f232ee8c1bb4e440ef"},
]
[package.dependencies]
binaryornot = "^0.4.4"
cachetools = "^5.5.2"
chardet = "^5.0.0"
binaryornot = ">=0.4.4,<0.5.0"
cachetools = ">=5.5.2,<6.0.0"
charset-normalizer = ">=3.4.1,<4.0.0"
flake8 = "*"
gitpython = "*"
grep-ast = "0.3.3"
@@ -4963,18 +4965,12 @@ networkx = "*"
numpy = "*"
pandas = "*"
scipy = "*"
tree-sitter = "^0.24.0"
tree-sitter-javascript = "^0.23.1"
tree-sitter-python = "^0.23.6"
tree-sitter-ruby = "^0.23.1"
tree-sitter-typescript = "^0.23.2"
whatthepatch = "^1.0.6"
[package.source]
type = "git"
url = "https://github.com/All-Hands-AI/openhands-aci.git"
reference = "add-encoding-detection"
resolved_reference = "040d9578d90894409f51ecca877b120fe696fe0b"
tree-sitter = ">=0.24.0,<0.25.0"
tree-sitter-javascript = ">=0.23.1,<0.24.0"
tree-sitter-python = ">=0.23.6,<0.24.0"
tree-sitter-ruby = ">=0.23.1,<0.24.0"
tree-sitter-typescript = ">=0.23.2,<0.24.0"
whatthepatch = ">=1.0.6,<2.0.0"
[[package]]
name = "opentelemetry-api"
@@ -9316,4 +9312,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.1"
python-versions = "^3.12"
content-hash = "d3ec6b8a6c7e48420d76b7e17d5f1a3f253fa603205f90d4a8e4a614ab5e2c67"
content-hash = "7c5c7d26747d7b42a1a7bbdc3b7e4d87bbbef851f3c51a022bf7a15082273e5a"

View File

@@ -67,7 +67,7 @@ runloop-api-client = "0.26.0"
libtmux = ">=0.37,<0.40"
pygithub = "^2.5.0"
joblib = "*"
openhands-aci = "^0.2.6"
openhands-aci = "^0.2.7"
python-socketio = "^5.11.4"
redis = "^5.2.0"
sse-starlette = "^2.1.3"