From 8452cf6c60245961571f75617452be52b477b9c8 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Thu, 15 Oct 2026 11:49:43 -0700 Subject: [PATCH 01/13] ate-env manifest template: --env NAME=VALUE for the actor's container A runtime layer's daemon is configured through its environment (the OpenHands agent-server reads its API key from OH_SESSION_API_KEYS_0), and the guest passes its environment to sidecars, but the template command had no way to set a variable. --env adds one per flag after PORT, in order. Names must be POSIX names; PORT is the guest's and cannot be overridden; a name given twice is an error. Templates derived per rootfs image keep them. --- cmd/ate-env/manifest.go | 39 +++++++++++++++++++ cmd/ate-env/manifest_test.go | 73 ++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/cmd/ate-env/manifest.go b/cmd/ate-env/manifest.go index 9552aec2..f9908496 100644 --- a/cmd/ate-env/manifest.go +++ b/cmd/ate-env/manifest.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "io" + "regexp" "strings" "github.com/agent-substrate/env/internal/apiservice" @@ -107,6 +108,10 @@ type templateConfig struct { // Empty means defaultActorCPU and defaultActorMemory. cpu string memory string + // env are extra container environment variables, each NAME=VALUE. The + // guest passes its environment to sidecars, so this is how a runtime + // layer's daemon is configured. + env []string } // runtimeLayer is a parsed --layer value. @@ -200,6 +205,9 @@ binaries default it to the ate-env-guest image published with the release if c.snapshotsBucket == "" { return errors.New("--snapshots-bucket is required; use an object-storage bucket (e.g. gs://bucket/prefix/)") } + if _, err := parseEnv(c.env); err != nil { + return err + } if c.rootfsImage != "" { if _, err := apiservice.ImageDigest(c.rootfsImage); err != nil { return fmt.Errorf("--rootfs-image: %w", err) @@ -313,6 +321,7 @@ It prints YAML to stdout without touching the cluster.`, cmd.Flags().StringArrayVar(&tCfg.sidecarReadyz, "sidecar-readyz", nil, "URL the actor's readiness waits for, e.g. http://127.0.0.1:44772/ready (repeatable)") cmd.Flags().StringVar(&tCfg.cpu, "cpu", defaultActorCPU, "CPU limit of each actor, a Kubernetes quantity such as 2 or 500m; Substrate requires one") cmd.Flags().StringVar(&tCfg.memory, "memory", defaultActorMemory, "memory limit of each actor, a Kubernetes quantity such as 4Gi; Substrate requires one") + cmd.Flags().StringArrayVar(&tCfg.env, "env", nil, "environment variable for the actor's container as NAME=VALUE (repeatable); the guest passes it to sidecars, e.g. a layer daemon's API key") return cmd } @@ -513,6 +522,11 @@ func orDefault(v, def string) string { func buildTemplate(cfg templateConfig) (*ateapipb.ActorTemplate, error) { tmpl := buildActorTemplate(cfg) c := tmpl.Containers[0] + env, err := parseEnv(cfg.env) + if err != nil { + return nil, err + } + c.Env = append(c.Env, env...) command := append([]string(nil), c.Command...) if cfg.workspace != "" { command = append(command, "-workspace", cfg.workspace) @@ -626,3 +640,28 @@ func buildAPIService(cfg manifestConfig) *corev1.Service { }, } } + +// envName is a POSIX environment variable name. +var envName = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`) + +// parseEnv turns NAME=VALUE flags into container env vars. PORT is the guest's +// own and cannot be overridden; a name given twice is an error. +func parseEnv(flags []string) ([]*ateapipb.EnvVar, error) { + var out []*ateapipb.EnvVar + seen := map[string]bool{} + for _, f := range flags { + name, value, ok := strings.Cut(f, "=") + if !ok || !envName.MatchString(name) { + return nil, fmt.Errorf("--env %q: want NAME=VALUE with NAME of letters, digits and '_', not starting with a digit", f) + } + if name == "PORT" { + return nil, errors.New("--env PORT: PORT is the guest's listen port and is set by the template") + } + if seen[name] { + return nil, fmt.Errorf("--env %s is given twice", name) + } + seen[name] = true + out = append(out, &ateapipb.EnvVar{Name: name, Value: value}) + } + return out, nil +} diff --git a/cmd/ate-env/manifest_test.go b/cmd/ate-env/manifest_test.go index 95f26594..54ad433e 100644 --- a/cmd/ate-env/manifest_test.go +++ b/cmd/ate-env/manifest_test.go @@ -531,3 +531,76 @@ func TestImageFlagsDefaultToReleaseImages(t *testing.T) { t.Errorf("missing --worker-image should point at the Substrate repo, got %v", err) } } + +func envMap(c *ateapipb.Container) (map[string]string, []string) { + m := map[string]string{} + var order []string + for _, e := range c.GetEnv() { + m[e.GetName()] = e.GetValue() + order = append(order, e.GetName()) + } + return m, order +} + +func TestBuildTemplateEnv(t *testing.T) { + cfg := testTemplateConfig() + cfg.env = []string{"OH_SESSION_API_KEYS_0=s3cret", "EMPTY=", "URL=http://x/?a=b"} + tmpl, err := buildTemplate(cfg) + if err != nil { + t.Fatal(err) + } + got, order := envMap(tmpl.Containers[0]) + if strings.Join(order, ",") != "PORT,OH_SESSION_API_KEYS_0,EMPTY,URL" { + t.Errorf("env order = %v, want PORT first, then the flags in order", order) + } + if got["PORT"] != "80" || got["OH_SESSION_API_KEYS_0"] != "s3cret" || got["EMPTY"] != "" || got["URL"] != "http://x/?a=b" { + t.Errorf("env = %v", got) + } + + // A rootfs-image template keeps the variables: the sidecars that read them + // run there. + cfg.rootfsImage = "docker.io/library/python@sha256:" + testHex64 + cfg.guestImage = "example.com/guest@sha256:" + testHex64 + tmpl, err = buildTemplate(cfg) + if err != nil { + t.Fatal(err) + } + if got, _ := envMap(tmpl.Containers[0]); got["OH_SESSION_API_KEYS_0"] != "s3cret" { + t.Errorf("rootfs-image template env = %v, want the --env values", got) + } +} + +func TestTemplateConfigRejectsBadEnv(t *testing.T) { + for _, env := range [][]string{ + {"NOEQUALS"}, + {"1BAD=x"}, + {"BAD-NAME=x"}, + {"=x"}, + {"PORT=81"}, + {"A=1", "A=2"}, + } { + cfg := testTemplateConfig() + cfg.env = env + if err := cfg.resolveImages(); err == nil { + t.Errorf("env %q: want error, got nil", env) + } + } +} + +func TestManifestTemplateCommandEnv(t *testing.T) { + cmd := newManifestCommand() + var buf bytes.Buffer + cmd.SetOut(&buf) + cmd.SetArgs([]string{ + "template", + "--guest-image=example.com/guest@sha256:aaaa", + "--snapshots-bucket=gs://bucket/prefix/", + "--env=OH_SESSION_API_KEYS_0=k", + }) + if err := cmd.Execute(); err != nil { + t.Fatalf("execute: %v", err) + } + if out := buf.String(); !strings.Contains(out, "name: OH_SESSION_API_KEYS_0\n value: k") { + t.Errorf("output lacks the --env variable:\n%s", out) + } +} From 678c7420d4ee71d6b25599eade0053cf02f28da7 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Thu, 15 Oct 2026 11:49:43 -0700 Subject: [PATCH 02/13] examples: the OpenHands agent-server in any image, with OpenHands' CodeAct agent The OpenHands agent-server mounted read-only into an unmodified image as a runtime layer, started by the guest as a sidecar, readiness gated on its /health, and reached from outside through ate-env-api's port proxy. The OpenHands SDK takes the proxy's path form (/v1alpha/envs//ports/) as its workspace URL, so RemoteWorkspace and RemoteConversation, the agent loop with its event WebSocket, work unchanged. - Dockerfile: the layer, for amd64 and arm64. The published agent-server binary is a PyInstaller build that needs glibc 2.36 and fails on older distributions (Ubuntu 22.04 included), also through its own image's loader, so the layer is python-build-standalone CPython (checksum-pinned) with openhands-agent-server installed by pip on Debian bullseye: wheels for glibc 2.31 and newer, nothing used from the image but the kernel and /bin/sh. - demo.sh: registers a base template and drives the agent-server in one environment per image over HTTP; PORT, AGENT_SERVER_ENV, COMMAND, WORKDIR and KEEP make it general. - ate_env_workspace.py: workspace_for(env), a RemoteWorkspace on the path form. - codeact_agent.py: runs OpenHands' default (CodeAct) agent in an environment with any litellm model, or --scripted with scripted_llm.py, a stand-in model that replays a fixed trajectory through the real agent loop and tools when no model is reachable from the actor. - README: build, register, use, the glibc finding, live results, limits. --- docs/rootfs-images/RUNTIMES.md | 5 +- examples/openhands-agent-server/Dockerfile | 74 +++++++++ examples/openhands-agent-server/README.md | 154 ++++++++++++++++++ .../ate_env_workspace.py | 72 ++++++++ .../openhands-agent-server/codeact_agent.py | 129 +++++++++++++++ examples/openhands-agent-server/demo.sh | 137 ++++++++++++++++ .../openhands-agent-server/scripted_llm.py | 108 ++++++++++++ 7 files changed, 678 insertions(+), 1 deletion(-) create mode 100644 examples/openhands-agent-server/Dockerfile create mode 100644 examples/openhands-agent-server/README.md create mode 100644 examples/openhands-agent-server/ate_env_workspace.py create mode 100644 examples/openhands-agent-server/codeact_agent.py create mode 100755 examples/openhands-agent-server/demo.sh create mode 100644 examples/openhands-agent-server/scripted_llm.py diff --git a/docs/rootfs-images/RUNTIMES.md b/docs/rootfs-images/RUNTIMES.md index cd50f31d..c59930c1 100644 --- a/docs/rootfs-images/RUNTIMES.md +++ b/docs/rootfs-images/RUNTIMES.md @@ -16,6 +16,7 @@ A runtime layer is three things: | the binary | an image volume on the ActorTemplate (`--layer name=image@sha256:...=/mount`) | mounts the runtime's image read-only at a path of your choice, next to the guest at `/ate` | | the process | a guest flag (`--sidecar '/mount/binary args...'`) | the guest starts it after it is listening, logs its output with a prefix, and restarts it with backoff if it exits | | its readiness | a guest flag (`--sidecar-readyz http://127.0.0.1:PORT/health`) | the guest's `/readyz`, which is the actor's wakeup probe, answers 503 until every such URL has answered 2xx once | +| its configuration | a template flag (`--env NAME=VALUE`) | sets a variable on the container; the guest passes its environment to sidecars | The guest is the container's only process by contract, which is why it is the one that supervises the others. Sidecars run without a shell or `PATH` lookup, @@ -76,7 +77,9 @@ file endpoints. A verified stand-in for the mechanics, which needs nothing from any registry but Docker Hub, is in [`examples/runtime-layers`](../../examples/runtime-layers): the static `busybox:1.37-musl` mounted as the layer and its `httpd` as the -sidecar, run under two different rootfs images. +sidecar, run under two different rootfs images. A real daemon, the OpenHands agent-server in +any image, configured by `--env` and driven by OpenHands' own CodeAct agent, is +in [`examples/openhands-agent-server`](../../examples/openhands-agent-server). ## Who can reach the second runtime diff --git a/examples/openhands-agent-server/Dockerfile b/examples/openhands-agent-server/Dockerfile new file mode 100644 index 00000000..7d42d34f --- /dev/null +++ b/examples/openhands-agent-server/Dockerfile @@ -0,0 +1,74 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A runtime layer that carries the OpenHands agent-server, for mounting into +# unmodified images at /opt/oh. It brings everything it runs on: a relocatable +# Python and the agent-server's packages. From the image it is mounted into it +# uses only the kernel and /bin/sh, which the agent-server runs commands with. +# +# Why not the binary in the published ghcr.io/openhands/agent-server images: +# it is a PyInstaller build made on Debian bookworm and needs glibc 2.36, so it +# fails on older distributions, among them Ubuntu 22.04 (glibc 2.35), the base +# of many coding-task images. Starting it through its own image's loader does +# not help, because PyInstaller re-executes itself. Here: +# +# - python-build-standalone CPython, built for glibc 2.17 and relocatable, so +# it runs wherever the layer is mounted; +# - openhands-agent-server installed by pip on Debian bullseye (glibc 2.31), +# so pip only picks wheels that run on glibc 2.31 and newer. +# +# docker buildx build --platform linux/amd64,linux/arm64 \ +# -t /oh-agent-server-layer:1.55.0 --push . +# +# Override AGENT_SERVER_VERSION to build another release. The image has no +# shell or entrypoint of its own; it is only ever mounted. + +# The interpreter is fetched and verified in a small stage of its own, so the +# build stage needs no package manager. +FROM curlimages/curl:8.16.0 AS fetch + +ARG TARGETARCH +ARG PBS_RELEASE=20261013 +ARG PYTHON_VERSION=3.12.15 +ARG PBS_SHA256_AMD64=5d546fc004fc2435fa142eb8f42ed8de5eb4725963dc22f7e6671014a80eb560 +ARG PBS_SHA256_ARM64=34a43010cc05982d8b5debbad7d09f7b1200117ebf018f7acfd656895e408205 + +RUN set -eu; \ + case "${TARGETARCH}" in \ + amd64) arch=x86_64; sum="${PBS_SHA256_AMD64}" ;; \ + arm64) arch=aarch64; sum="${PBS_SHA256_ARM64}" ;; \ + *) echo "unsupported architecture ${TARGETARCH}" >&2; exit 1 ;; \ + esac; \ + f="cpython-${PYTHON_VERSION}+${PBS_RELEASE}-${arch}-unknown-linux-gnu-install_only.tar.gz"; \ + curl -fsSLo "/tmp/${f}" "https://github.com/astral-sh/python-build-standalone/releases/download/${PBS_RELEASE}/${f}"; \ + echo "${sum} /tmp/${f}" | sha256sum -c -; \ + mkdir -p /tmp/oh; \ + tar -xzf "/tmp/${f}" -C /tmp/oh + +FROM debian:bullseye-slim AS build + +ARG AGENT_SERVER_VERSION=1.55.0 + +COPY --from=fetch /tmp/oh/ /opt/oh/ + +# Wheels wherever they exist. The few pure-Python packages that ship only as +# source (func-timeout) build fine; a native one would fail here, because the +# slim image has no compiler, rather than link against this builder. +RUN /opt/oh/python/bin/python3 -m pip install --no-cache-dir --prefer-binary \ + "openhands-agent-server==${AGENT_SERVER_VERSION}" \ + && /opt/oh/python/bin/python3 -c "import openhands.agent_server" \ + && find /opt/oh/python -name __pycache__ -prune -exec rm -rf {} + + +FROM scratch +COPY --from=build /opt/oh/ / diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md new file mode 100644 index 00000000..879ab876 --- /dev/null +++ b/examples/openhands-agent-server/README.md @@ -0,0 +1,154 @@ +# Example: the OpenHands agent-server in any image + +Runs the [OpenHands](https://github.com/OpenHands/software-agent-sdk) agent-server inside an +unmodified image (a coding-task image, a language base image, anything with `/bin/sh`) and drives +it from outside the cluster, including OpenHands' own CodeAct agent: + +- the agent-server comes from a runtime layer mounted read-only at `/opt/oh` + ([docs/rootfs-images/RUNTIMES.md](../../docs/rootfs-images/RUNTIMES.md)); the image is not rebuilt; +- the guest starts it as a sidecar and holds the actor's readiness until its `/health` answers; +- callers reach it through ate-env-api's port proxy, by headers (`X-Env-Id`, `X-Env-Port`) or by + the path `/v1alpha/envs//ports//...`, plus the agent-server's own API key; +- the OpenHands SDK uses that path as its workspace URL, so `RemoteWorkspace` (commands, files) and + `RemoteConversation` (the agent loop and its event WebSocket) work without changes. + +| file | what it is | +|---|---| +| [`Dockerfile`](Dockerfile) | the runtime layer: a relocatable Python with `openhands-agent-server` | +| [`demo.sh`](demo.sh) | registers a base template and drives the agent-server in one environment per image | +| [`ate_env_workspace.py`](ate_env_workspace.py) | `workspace_for(env)`: an OpenHands `RemoteWorkspace` for an environment | +| [`codeact_agent.py`](codeact_agent.py) | runs OpenHands' CodeAct agent in an environment, with a model or `--scripted` | +| [`scripted_llm.py`](scripted_llm.py) | a stand-in model that replays a fixed CodeAct trajectory, for runs without a model | + +## 1. Build the layer + +```bash +docker buildx build --platform linux/amd64,linux/arm64 \ + -t /oh-agent-server-layer:1.55.0 --push examples/openhands-agent-server +crane digest --platform linux/amd64 /oh-agent-server-layer:1.55.0 +``` + +`AGENT_SERVER_VERSION` selects another release. The layer does not reuse the binary in the published +`ghcr.io/openhands/agent-server` images: that is a PyInstaller build made on Debian bookworm, it needs +glibc 2.36, and on older distributions it fails at start, for example on Ubuntu 22.04 (glibc 2.35): + +``` +ImportError: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.36' not found (required by /tmp/_MEI…/libstdc++.so.6) +``` + +Starting it through its own image's loader does not help either, because PyInstaller re-executes +itself. The layer is [python-build-standalone](https://github.com/astral-sh/python-build-standalone) +CPython (built for glibc 2.17, relocatable) with `openhands-agent-server` installed by pip on Debian +bullseye, so every wheel runs on glibc 2.31 and newer. From the image it is mounted into it uses only +the kernel and `/bin/sh`. It is about 210 MB compressed and 740 MB on disk. + +## 2. Register a base template + +```bash +ate-env manifest template --template oh-agent-server \ + --guest-image /ate-env-guest@sha256: \ + --layer oh=/oh-agent-server-layer@sha256:=/opt/oh \ + --sidecar '/opt/oh/python/bin/python3 -m openhands.agent_server --host 0.0.0.0 --port 8000' \ + --sidecar-readyz http://127.0.0.1:8000/health \ + --env OH_SESSION_API_KEYS_0= \ + --snapshots-bucket | kubectl-ate create actor-template -f - +``` + +Further agent-server settings are more `--env` flags. Any image then runs with it: + +```bash +ate-env create my-env --template oh-agent-server --rootfs-image @sha256: +``` + +The first create from an image derives a template for it (layer, sidecar and settings included) and +later creates reuse it. + +## 3. Use it + +Over HTTP, by path or by headers: + +```bash +curl -H 'X-Session-API-Key: ' -H 'Content-Type: application/json' \ + localhost:7777/v1alpha/envs/my-env/ports/8000/api/bash/execute_bash_command \ + -d '{"command": "uname -a"}' +``` + +With the OpenHands SDK (`pip install openhands-sdk==1.55.0 openhands-tools==1.55.0`): + +```python +from ate_env_workspace import workspace_for + +workspace = workspace_for("my-env", api_key="") # a RemoteWorkspace +print(workspace.execute_command("ls /").stdout) +``` + +The CodeAct agent, OpenHands' default agent (terminal, file editor, task tracker, finish) running on +the agent-server inside the environment: + +```bash +SESSION_KEY= LLM_MODEL= LLM_API_KEY= [LLM_BASE_URL=] \ + python examples/openhands-agent-server/codeact_agent.py my-env "Fix the failing test in /testbed" +``` + +The agent-server calls the model from inside the environment, so the model endpoint must be reachable +from the actor: allowed by Substrate's egress policy, or served inside the cluster. `--scripted` needs +no model at all. It starts `scripted_llm.py` inside the environment and points the agent at it; the +agent loop, its tools and the event stream run for real, only the decisions are canned: + +``` + task Inspect this sandbox: report the operating system, the working directory and the top-level… + action terminal: {'command': 'head -1 /etc/os-release; echo "cwd: $(pwd)"; ls | head -5; mkdir -p /tmp/codeact-demo', …} + result terminal: PRETTY_NAME="Ubuntu 22.04.5 LTS" cwd: / ate bin boot dev etc + action file_editor: {'command': 'create', 'path': '/tmp/codeact-demo/hello.txt', 'file_text': 'hello from CodeAct\n'} + result file_editor: File created successfully at: /tmp/codeact-demo/hello.txt + action terminal: {'command': 'cat /tmp/codeact-demo/hello.txt && wc -c /tmp/codeact-demo/hello.txt', …} + result terminal: hello from CodeAct 19 /tmp/codeact-demo/hello.txt + action finish: {'message': 'Inspected the sandbox and wrote /tmp/codeact-demo/hello.txt.'} +status: finished after 2.0s +``` + +`demo.sh` does steps 2 and 3 over plain HTTP for a list of images: + +```bash +kubectl -n ate-env port-forward deploy/ate-env-api 7777:7777 & +GUEST_IMAGE=/ate-env-guest@sha256: \ +SNAPSHOTS_BUCKET= \ +LAYER_IMAGE=/oh-agent-server-layer@sha256: \ +IMAGES="docker.io/library/ubuntu@sha256: docker.io/library/node@sha256:" \ +examples/openhands-agent-server/demo.sh +``` + +It also takes `PORT`, `AGENT_SERVER_ENV` (extra `NAME=VALUE` settings), `COMMAND`, `WORKDIR` and +`KEEP=1` (leave the environments up for the SDK scripts); see its header. + +## Verified + +GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port proxy, agent-server +1.55.0 from this Dockerfile, 2026-10-15: + +| image | base | agent-server answering after create | +|---|---|---| +| `ubuntu:20.04` | glibc 2.31 | 16 s | +| `swebench/sweb.eval.x86_64.django_1776_django-11099` | Ubuntu 22.04, glibc 2.35 | 16 to 23 s | +| `namanjain12/pandas_final` (R2E-Gym) | Ubuntu 22.04, glibc 2.35 | 20 s | +| `node:22-slim` | Debian 12, glibc 2.36 | 16 s | +| `python:3.12-slim` | Debian 13, glibc 2.41 | 16 s | + +- `demo.sh` on each: commands ran with the image's own shell and tools, file download worked, a call + without the key got 401; also with `PORT=3000` and an extra `--env` setting. +- `codeact_agent.py --scripted` in the SWE-bench environment: the CodeAct run above, its event + WebSocket through ate-env-api and the Substrate router. +- `ate_env_workspace.py` on `node:22-slim` saw the image's own `node v22`; on the SWE-bench image a + command saw the image's Python 3.11.5, not the layer's 3.12. After a suspend, the next SDK call woke + the environment and returned within 9 s. +- On that cluster actors had no route to the internet (connection reset), hence `--scripted`. +- The arm64 build of the layer: the agent-server answered `/health` within 9 s in an arm64 Ubuntu 22.04 + container (local Docker); the CodeAct flow ran the same way locally before the cluster run. + +## Limits + +- One API key per template: it is in the template's environment, readable by anyone who can read + templates, and shared by the templates derived from it. +- The agent-server runs as the image's user and keeps its state under that user's home. The first + environment on a node pulls the layer. +- The model is called from inside the environment (see above). diff --git a/examples/openhands-agent-server/ate_env_workspace.py b/examples/openhands-agent-server/ate_env_workspace.py new file mode 100644 index 00000000..899a256a --- /dev/null +++ b/examples/openhands-agent-server/ate_env_workspace.py @@ -0,0 +1,72 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""An OpenHands workspace for the agent-server inside an ate-env environment. + +ate-env-api forwards ``/v1alpha/envs//ports//...`` to that port in +the environment, prefix stripped. Used as the workspace host, that prefix is +all the OpenHands SDK needs: ``RemoteWorkspace`` appends its ``/api/...`` +paths to it, and ``RemoteConversation`` builds its event WebSocket from the +same host, so commands, files and the agent loop's events all reach the +agent-server through the Substrate router. The agent-server's own API key +travels as the SDK sends it. + + pip install openhands-sdk==1.55.0 + SESSION_KEY= python ate_env_workspace.py [command ...] + +Environment: ``API`` (ate-env-api address, default 127.0.0.1:7777), +``ATESPACE`` (default ate-env), ``PORT`` (agent-server port, default 8000), +``WORKDIR`` (directory the command runs in; default: the image's WORKDIR). +""" + +from __future__ import annotations + +import os +import sys + +from openhands.sdk.workspace import RemoteWorkspace + + +def agent_server_url(env_id: str, *, api: str | None = None, atespace: str | None = None, + port: int | None = None) -> str: + """URL of the agent-server in ``env_id``, through ate-env-api.""" + api = api or os.environ.get("API", "127.0.0.1:7777") + atespace = atespace or os.environ.get("ATESPACE", "ate-env") + port = port or int(os.environ.get("PORT", "8000")) + base = api if "://" in api else f"http://{api}" + scope = "" if atespace == "ate-env" else f"/atespaces/{atespace}" + return f"{base}/v1alpha{scope}/envs/{env_id}/ports/{port}" + + +def workspace_for(env_id: str, *, api_key: str | None = None, working_dir: str | None = None, + **url_kwargs) -> RemoteWorkspace: + """A ``RemoteWorkspace`` bound to the agent-server in ``env_id``.""" + return RemoteWorkspace( + host=agent_server_url(env_id, **url_kwargs), + api_key=api_key or os.environ["SESSION_KEY"], + working_dir=working_dir or os.environ.get("WORKDIR") or "/", + ) + + +def main() -> None: + if len(sys.argv) < 2: + sys.exit(f"usage: {sys.argv[0]} [command ...]") + workspace = workspace_for(sys.argv[1]) + command = " ".join(sys.argv[2:]) or 'head -1 /etc/os-release; echo "cwd: $(pwd)"' + result = workspace.execute_command(command, cwd=os.environ.get("WORKDIR"), timeout=60) + print(f"exit {result.exit_code}\n{result.stdout}{result.stderr}", end="") + + +if __name__ == "__main__": + main() diff --git a/examples/openhands-agent-server/codeact_agent.py b/examples/openhands-agent-server/codeact_agent.py new file mode 100644 index 00000000..bc05ee9a --- /dev/null +++ b/examples/openhands-agent-server/codeact_agent.py @@ -0,0 +1,129 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run the OpenHands CodeAct agent inside an ate-env environment. + +The agent is OpenHands' own default agent (``get_default_agent``: the +terminal, file editor and task tracker tools plus ``finish``), the successor +of CodeActAgent, unchanged. It runs on the agent-server inside the +environment as a ``RemoteConversation``; this script starts the conversation +through ate-env-api and prints the events the agent-server streams back over +its WebSocket. The image the environment runs is whatever the task needs; the +agent-server comes from the runtime layer. + +The agent-server calls the model, so the model endpoint must be reachable +from inside the environment (Substrate's egress policy decides that). +``--scripted`` needs no model: it starts ``scripted_llm.py`` inside the +environment and points the agent at it, which replays a fixed CodeAct +trajectory through the real agent loop and tools. + + pip install openhands-sdk==1.55.0 openhands-tools==1.55.0 + SESSION_KEY= LLM_MODEL= LLM_API_KEY= [LLM_BASE_URL=] \\ + python codeact_agent.py ["task"] + SESSION_KEY= python codeact_agent.py --scripted + +Environment as for ate_env_workspace.py (API, ATESPACE, PORT, WORKDIR). +""" + +from __future__ import annotations + +import argparse +import os +import re +import time +from pathlib import Path + +from ate_env_workspace import workspace_for +from openhands.sdk import LLM, Conversation +from openhands.sdk.event import ( + ActionEvent, + AgentErrorEvent, + MessageEvent, + ObservationEvent, +) +from openhands.tools.preset.default import get_default_agent + +DEFAULT_TASK = ( + "Inspect this sandbox: report the operating system, the working directory and the " + "top-level files. Then create /tmp/codeact-demo/hello.txt containing 'hello from " + "CodeAct', check its contents, and finish." +) +LAYER_PYTHON = "/opt/oh/python/bin/python3" +SCRIPTED_PORT = 9999 + + +def start_scripted_llm(workspace) -> str: + """Start scripted_llm.py inside the environment, on the layer's Python.""" + workspace.file_upload(Path(__file__).with_name("scripted_llm.py"), "/tmp/scripted_llm.py") + workspace.execute_command(f"{LAYER_PYTHON} /tmp/scripted_llm.py {SCRIPTED_PORT} --daemon", timeout=30) + probe = f"{LAYER_PYTHON} -c \"import socket; socket.create_connection(('127.0.0.1', {SCRIPTED_PORT}), 1)\"" + for _ in range(30): + if workspace.execute_command(probe, timeout=10).exit_code == 0: + return f"http://127.0.0.1:{SCRIPTED_PORT}/v1" + time.sleep(1) + raise RuntimeError("scripted model did not start inside the environment") + + +_CONTROL = re.compile(r"\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07]*\x07") + + +def short(value: object, limit: int = 160) -> str: + """One line, terminal control sequences removed, at most ``limit`` chars.""" + text = " ".join(_CONTROL.sub("", str(value)).split()) + return text if len(text) <= limit else text[: limit - 1] + "…" + + +def on_event(event) -> None: + """Print one line per agent step.""" + if isinstance(event, ActionEvent): + action = event.action.model_dump(exclude_none=True) if event.action else {} + action.pop("kind", None) + print(f" action {event.tool_name}: {short(action)}") + elif isinstance(event, ObservationEvent): + print(f" result {event.tool_name}: {short(event.observation.text)}") + elif isinstance(event, AgentErrorEvent): + print(f" error {event.tool_name}: {short(event.error)}") + elif isinstance(event, MessageEvent) and event.source == "user": + print(f" task {short(event.llm_message.content[0].text)}") + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("env_id") + parser.add_argument("task", nargs="?", default=DEFAULT_TASK) + parser.add_argument("--scripted", action="store_true", help="replay a fixed trajectory instead of calling a model") + args = parser.parse_args() + + workspace = workspace_for(args.env_id) + if args.scripted: + llm = LLM(model="openai/scripted", base_url=start_scripted_llm(workspace), api_key="scripted", + native_tool_calling=True, usage_id="agent") + else: + llm = LLM(model=os.environ["LLM_MODEL"], api_key=os.environ.get("LLM_API_KEY"), + base_url=os.environ.get("LLM_BASE_URL"), usage_id="agent") + + agent = get_default_agent(llm, cli_mode=True) + conversation = Conversation(agent=agent, workspace=workspace, callbacks=[on_event], visualizer=None) + started = time.monotonic() + try: + conversation.send_message(args.task) + conversation.run() + status = conversation.state.execution_status + print(f"status: {status.value} after {time.monotonic() - started:.1f}s") + finally: + conversation.close() + + +if __name__ == "__main__": + main() diff --git a/examples/openhands-agent-server/demo.sh b/examples/openhands-agent-server/demo.sh new file mode 100755 index 00000000..5cb50bb6 --- /dev/null +++ b/examples/openhands-agent-server/demo.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The OpenHands agent-server attached to unmodified images, end to end. +# +# The agent-server comes from a runtime layer (./Dockerfile) mounted read-only +# at /opt/oh; the guest starts it as a sidecar and holds the actor's readiness +# until it answers /health. Callers outside the cluster reach it through +# ate-env-api's port proxy with three routing headers plus the agent-server's +# own X-Session-API-Key. The script registers a base template, creates one +# environment per image, drives each agent-server from outside (health, a +# command, a file download, a call without the key) and deletes what it +# created, unless KEEP=1. +# +# Required: +# GUEST_IMAGE digest-pinned ate-env-guest image (with the -sidecar flags) +# SNAPSHOTS_BUCKET object-storage URL for actor snapshots +# LAYER_IMAGE digest-pinned layer image built from ./Dockerfile +# IMAGES space-separated digest-pinned images to run the +# agent-server in; any image with /bin/sh +# Optional: +# SESSION_KEY agent-server API key on the template (default: random) +# PORT agent-server port in the environment (default 8000) +# AGENT_SERVER_ENV space-separated NAME=VALUE settings for the agent-server, +# e.g. "LOG_JSON=true"; each becomes a template --env +# COMMAND shell command the demo runs through the agent-server +# (default: distribution, user and working directory) +# WORKDIR directory COMMAND runs in (default: the image's WORKDIR) +# KEEP 1 keeps the environments and prints how to reach them +# TEMPLATE base template name (default oh-agent-server) +# ATESPACE atespace (default ate-env) +# API ate-env-api address for the CLI and the proxy +# (default 127.0.0.1:7777; port-forward it) +# ATE_ENV, KUBECTL_ATE binaries (default: on PATH) +set -euo pipefail + +: "${GUEST_IMAGE:?set GUEST_IMAGE to the digest-pinned ate-env-guest image}" +: "${SNAPSHOTS_BUCKET:?set SNAPSHOTS_BUCKET to the snapshot object-storage URL}" +: "${LAYER_IMAGE:?set LAYER_IMAGE to the digest-pinned agent-server layer image}" +: "${IMAGES:?set IMAGES to one or more digest-pinned images}" +SESSION_KEY=${SESSION_KEY:-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')} +PORT=${PORT:-8000} +COMMAND=${COMMAND:-'head -1 /etc/os-release; echo "user: $(id -un)"; echo "cwd: $(pwd)"'} +TEMPLATE=${TEMPLATE:-oh-agent-server} +ATESPACE=${ATESPACE:-ate-env} +API=${API:-127.0.0.1:7777} +ATE_ENV=${ATE_ENV:-ate-env} +KUBECTL_ATE=${KUBECTL_ATE:-kubectl-ate} + +step() { printf '\n== %s\n' "$*"; } + +# oh : one request to the agent-server in , +# through ate-env-api. +oh() { + local id=$1 + shift + curl -sS --max-time 120 \ + -H "X-Env-Id: ${id}" -H "X-Env-Atespace: ${ATESPACE}" -H "X-Env-Port: ${PORT}" \ + -H "X-Session-API-Key: ${SESSION_KEY}" "$@" +} + +env_flags=(--env "OH_SESSION_API_KEYS_0=${SESSION_KEY}") +for kv in ${AGENT_SERVER_ENV:-}; do + env_flags+=(--env "${kv}") +done + +step "register ${ATESPACE}/${TEMPLATE}: guest + agent-server layer on port ${PORT}" +"${ATE_ENV}" manifest template --template "${TEMPLATE}" --atespace "${ATESPACE}" \ + --guest-image "${GUEST_IMAGE}" --snapshots-bucket "${SNAPSHOTS_BUCKET}" \ + --layer "oh=${LAYER_IMAGE}=/opt/oh" \ + --sidecar "/opt/oh/python/bin/python3 -m openhands.agent_server --host 0.0.0.0 --port ${PORT}" \ + --sidecar-readyz "http://127.0.0.1:${PORT}/health" \ + "${env_flags[@]}" | "${KUBECTL_ATE}" create actor-template -f - >/dev/null + +ids=() +cleanup() { + [ "${KEEP:-}" = 1 ] && return + for id in "${ids[@]:-}"; do + [ -n "${id}" ] && "${ATE_ENV}" --api "${API}" delete "${id}" --atespace "${ATESPACE}" >/dev/null 2>&1 || true + done +} +trap cleanup EXIT + +body=$(COMMAND="${COMMAND}" WORKDIR="${WORKDIR:-}" python3 -c 'import json,os; d={"command": os.environ["COMMAND"], "timeout": 60}; w=os.environ.get("WORKDIR"); d.update({"cwd": w} if w else {}); print(json.dumps(d))') + +n=0 +for image in ${IMAGES}; do + n=$((n + 1)) + id="oh-$(date +%s)-${n}" + ids+=("${id}") + step "${id}: ${image}" + t0=$(date +%s) + "${ATE_ENV}" --api "${API}" create "${id}" --atespace "${ATESPACE}" --template "${TEMPLATE}" --rootfs-image "${image}" >/dev/null + # The router wakes or waits for the actor; the agent-server answers once the + # actor is ready, which the guest gates on the same /health. + code=000 + for _ in $(seq 1 120); do + code=$(oh "${id}" -o /dev/null -w '%{http_code}' "http://${API}/health" || true) + [ "${code}" = 200 ] && break + sleep 5 + done + if [ "${code}" != 200 ]; then + echo "agent-server in ${id} did not answer (last status ${code})" >&2 + continue + fi + echo "agent-server answering after $(( $(date +%s) - t0 ))s" + + echo "-- command, run with the image's own /bin/sh:" + oh "${id}" -X POST -H 'Content-Type: application/json' "http://${API}/api/bash/execute_bash_command" -d "${body}" | + python3 -c 'import json,sys; r=json.load(sys.stdin); print(r.get("stdout", r).rstrip()); print("exit:", r.get("exit_code"))' + echo "-- file download (/etc/os-release, first line):" + oh "${id}" "http://${API}/api/file/download?path=/etc/os-release" | head -1 + echo "-- without the session key:" + curl -sS -o /dev/null -w 'HTTP %{http_code}\n' -X POST -H 'Content-Type: application/json' \ + -H "X-Env-Id: ${id}" -H "X-Env-Atespace: ${ATESPACE}" -H "X-Env-Port: ${PORT}" \ + -d '{"command": "true"}' "http://${API}/api/bash/execute_bash_command" +done + +if [ "${KEEP:-}" = 1 ]; then + step "kept: ${ids[*]}" + echo "reach one with: -H 'X-Env-Id: ' -H 'X-Env-Port: ${PORT}' -H 'X-Session-API-Key: ${SESSION_KEY}' http://${API}/..." + echo "delete with: ${ATE_ENV} --api ${API} delete --atespace ${ATESPACE}" +else + step "done; deleting the environments (the templates stay for reuse)" +fi diff --git a/examples/openhands-agent-server/scripted_llm.py b/examples/openhands-agent-server/scripted_llm.py new file mode 100644 index 00000000..4f964e6d --- /dev/null +++ b/examples/openhands-agent-server/scripted_llm.py @@ -0,0 +1,108 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A scripted stand-in for a model, for running the CodeAct agent without one. + +An OpenAI-compatible ``/v1/chat/completions`` endpoint (standard library +only) that answers with a fixed CodeAct trajectory: inspect the sandbox with +``terminal``, write a file with ``file_editor``, check it with ``terminal``, +then ``finish``. Each step is chosen by how many tool results the +conversation already holds, so the OpenHands agent loop, its tools and its +event stream all run for real; only the decisions are canned. Requests +without tools (a title, a summary) get plain text. + + python scripted_llm.py [port] [--daemon] # default port 9999 +""" + +from __future__ import annotations + +import json +import os +import sys +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +DEMO_DIR = "/tmp/codeact-demo" +STEPS = [ + ("Let me look at the sandbox first.", "terminal", + {"command": f'head -1 /etc/os-release; echo "cwd: $(pwd)"; ls | head -5; mkdir -p {DEMO_DIR}'}), + ("Now I create the file.", "file_editor", + {"command": "create", "path": f"{DEMO_DIR}/hello.txt", "file_text": "hello from CodeAct\n"}), + ("Checking the result.", "terminal", + {"command": f"cat {DEMO_DIR}/hello.txt && wc -c {DEMO_DIR}/hello.txt"}), + ("Done.", "finish", + {"message": f"Inspected the sandbox and wrote {DEMO_DIR}/hello.txt."}), +] + + +class Handler(BaseHTTPRequestHandler): + def do_POST(self) -> None: + if not self.path.endswith("/chat/completions"): + self.send_error(404) + return + req = json.loads(self.rfile.read(int(self.headers.get("Content-Length", 0))) or b"{}") + message: dict = {"role": "assistant"} + finish_reason = "stop" + if req.get("tools"): + done = sum(1 for m in req.get("messages", []) if m.get("role") == "tool") + text, tool, args = STEPS[min(done, len(STEPS) - 1)] + message["content"] = text + message["tool_calls"] = [{ + "id": f"call_{done}", + "type": "function", + "function": {"name": tool, "arguments": json.dumps(args)}, + }] + finish_reason = "tool_calls" + else: + message["content"] = "CodeAct demo" + body = json.dumps({ + "id": f"scripted-{time.time_ns()}", + "object": "chat.completion", + "created": int(time.time()), + "model": req.get("model", "scripted"), + "choices": [{"index": 0, "message": message, "finish_reason": finish_reason}], + "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}, + }).encode() + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def log_message(self, fmt: str, *args) -> None: + sys.stderr.write("scripted-llm: " + fmt % args + "\n") + + +def daemonize(log_path: str) -> None: + """Detach from the caller's session, so a short-lived shell that starts + this (the agent-server's command endpoint ends its process group when the + command returns) does not take it down.""" + if os.fork(): + os._exit(0) + os.setsid() + if os.fork(): + os._exit(0) + log = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644) + null = os.open(os.devnull, os.O_RDONLY) + os.dup2(null, 0) + os.dup2(log, 1) + os.dup2(log, 2) + + +if __name__ == "__main__": + args = [a for a in sys.argv[1:] if a != "--daemon"] + port = int(args[0]) if args else 9999 + if "--daemon" in sys.argv: + daemonize(f"/tmp/scripted_llm_{port}.log") + ThreadingHTTPServer(("127.0.0.1", port), Handler).serve_forever() From 8bea057b46b833e76c7c5ae9a9779588a20f10d2 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:33:21 -0700 Subject: [PATCH 03/13] examples/openhands-agent-server: execute a CodeAct trajectory from outside run_trajectory.py connects to an environment through ate-env-api and runs a CodeAct trajectory there, one tool call at a time, in OpenHands' vocabulary: terminal commands through the agent-server's command endpoint, file edits through OpenHands' own file_editor, served inside the environment by oh_tool.py on the layer's Python and reached through the port proxy's path form. Observations print and, with --out, are recorded next to their calls. Nothing inside the environment calls a model, so this is also the shape of a client-side agent: the model wherever it is reachable, the environment only executing. oh_tool.py is one long-lived server, so OpenHands' imports happen once (file edits 0.3 s instead of 9 s cold) and undo_edit sees earlier edits. It listens on all interfaces: the router delivers to the actor's address, and a loopback-only listener answers 502, which RUNTIMES.md and the README now say for any runtime. trajectories/: hello.json for any image; django-11099.json fixes SWE-bench django__django-11099 and django-11099-grade.json applies the instance's hidden test change and runs its target tests with and without the fix. --- docs/rootfs-images/RUNTIMES.md | 4 + examples/openhands-agent-server/README.md | 65 ++++++++- examples/openhands-agent-server/oh_tool.py | 96 +++++++++++++ .../openhands-agent-server/run_trajectory.py | 130 ++++++++++++++++++ .../trajectories/django-11099-grade.json | 45 ++++++ .../trajectories/django-11099.json | 78 +++++++++++ .../trajectories/hello.json | 46 +++++++ 7 files changed, 462 insertions(+), 2 deletions(-) create mode 100644 examples/openhands-agent-server/oh_tool.py create mode 100644 examples/openhands-agent-server/run_trajectory.py create mode 100644 examples/openhands-agent-server/trajectories/django-11099-grade.json create mode 100644 examples/openhands-agent-server/trajectories/django-11099.json create mode 100644 examples/openhands-agent-server/trajectories/hello.json diff --git a/docs/rootfs-images/RUNTIMES.md b/docs/rootfs-images/RUNTIMES.md index c59930c1..e382101e 100644 --- a/docs/rootfs-images/RUNTIMES.md +++ b/docs/rootfs-images/RUNTIMES.md @@ -102,6 +102,10 @@ in [`examples/openhands-agent-server`](../../examples/openhands-agent-server). curl localhost:7777/v1alpha/envs/py1/ports/44772/ready # the same, for clients without headers ``` + The runtime must listen on all interfaces, not 127.0.0.1: the router + delivers to the actor's address, and a loopback-only listener answers + `502 bad gateway`. + The path form, `/v1alpha/envs/{id}/ports/{port}/...` (or `/v1alpha/atespaces/{atespace}/envs/...`), is for clients that cannot set headers: the prefix is stripped, so it works as a base URL. The tunnel diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md index 879ab876..89050d54 100644 --- a/examples/openhands-agent-server/README.md +++ b/examples/openhands-agent-server/README.md @@ -2,7 +2,8 @@ Runs the [OpenHands](https://github.com/OpenHands/software-agent-sdk) agent-server inside an unmodified image (a coding-task image, a language base image, anything with `/bin/sh`) and drives -it from outside the cluster, including OpenHands' own CodeAct agent: +it from outside the cluster: commands and files, OpenHands' own CodeAct agent, and CodeAct +trajectories executed step by step from a client: - the agent-server comes from a runtime layer mounted read-only at `/opt/oh` ([docs/rootfs-images/RUNTIMES.md](../../docs/rootfs-images/RUNTIMES.md)); the image is not rebuilt; @@ -19,6 +20,9 @@ it from outside the cluster, including OpenHands' own CodeAct agent: | [`ate_env_workspace.py`](ate_env_workspace.py) | `workspace_for(env)`: an OpenHands `RemoteWorkspace` for an environment | | [`codeact_agent.py`](codeact_agent.py) | runs OpenHands' CodeAct agent in an environment, with a model or `--scripted` | | [`scripted_llm.py`](scripted_llm.py) | a stand-in model that replays a fixed CodeAct trajectory, for runs without a model | +| [`run_trajectory.py`](run_trajectory.py) | executes a CodeAct trajectory in an environment from outside and records the observations | +| [`oh_tool.py`](oh_tool.py) | serves OpenHands' `file_editor` inside the environment for `run_trajectory.py` | +| [`trajectories/`](trajectories) | `hello.json` for any image; `django-11099.json` and `-grade.json` for the SWE-bench `django__django-11099` image | ## 1. Build the layer @@ -107,6 +111,56 @@ agent loop, its tools and the event stream run for real, only the decisions are status: finished after 2.0s ``` +### Execute a trajectory from outside + +`run_trajectory.py` connects to an environment and executes a CodeAct trajectory there: the tool +calls an agent makes, in OpenHands' vocabulary (`terminal`, `file_editor`, `finish`), as a JSON list +of `{"tool", "args", "thought"}`. Terminal commands go through the agent-server's command endpoint. +File edits go through OpenHands' own `file_editor`, served inside the environment by `oh_tool.py` on +the layer's Python and reached through ate-env-api like the agent-server, so observations read +exactly as the agent sees them, and `undo_edit` works across steps. Each observation is printed and, +with `--out`, recorded next to its call. Nothing inside the environment calls a model: put a model +call before each step and this is a client-side agent, with the model wherever it is reachable. + +```bash +SESSION_KEY= python examples/openhands-agent-server/run_trajectory.py \ + examples/openhands-agent-server/trajectories/django-11099.json --out run.json +``` + +On the SWE-bench `django__django-11099` image (username validators accept a trailing newline): + +``` +[2] thought Reproduce: $ matches before a trailing newline. +[2] terminal {"command": "/opt/miniconda3/envs/testbed/bin/python -c \"from django.conf import settings; … + ASCIIUsernameValidator accepts a trailing newline + UnicodeUsernameValidator accepts a trailing newline + [exit 0] + -> ok in 0.41s +… +[4] thought Anchor at the true end of string with \Z, in both validators. +[4] file_editor{"command": "str_replace", "path": "/testbed/django/contrib/auth/validators.py", … + The file /testbed/django/contrib/auth/validators.py has been edited. … + 10 regex = r'^[\w.@+-]+\Z' + -> ok in 0.31s +… +[6] thought Check the reproduction again. + ASCIIUsernameValidator rejects a trailing newline + UnicodeUsernameValidator rejects a trailing newline + -> ok in 0.44s +[7] thought Run the validator tests. + Ran 22 tests in 0.208s + OK + -> ok in 0.84s +… +9 steps in 5.2s +``` + +`django-11099-grade.json` is the reward step, run the same way afterwards: it applies SWE-bench's +hidden test change for that instance and runs the target tests with the fix (`OK`) and with the fix +stashed (`FAILED (failures=2)`). + +### The plain-HTTP demo + `demo.sh` does steps 2 and 3 over plain HTTP for a list of images: ```bash @@ -142,6 +196,10 @@ GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port pr command saw the image's Python 3.11.5, not the layer's 3.12. After a suspend, the next SDK call woke the environment and returned within 9 s. - On that cluster actors had no route to the internet (connection reset), hence `--scripted`. +- `run_trajectory.py` in the same SWE-bench environment: `django-11099.json` fixed the issue in 9 + steps and 5.2 s (file edits 0.3 s each once the tool runner was up), and `django-11099-grade.json` + showed the instance's target tests failing without the fix and passing with it. `hello.json` ran on + the same image. - The arm64 build of the layer: the agent-server answered `/health` within 9 s in an arm64 Ubuntu 22.04 container (local Docker); the CodeAct flow ran the same way locally before the cluster run. @@ -151,4 +209,7 @@ GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port pr templates, and shared by the templates derived from it. - The agent-server runs as the image's user and keeps its state under that user's home. The first environment on a node pulls the layer. -- The model is called from inside the environment (see above). +- `codeact_agent.py` calls the model from inside the environment (see above); `run_trajectory.py` + and a client-side agent built on it do not. +- A daemon reached through ate-env-api must listen on all interfaces, not 127.0.0.1: the router + delivers to the actor's address, and a loopback-only listener answers `502 bad gateway`. diff --git a/examples/openhands-agent-server/oh_tool.py b/examples/openhands-agent-server/oh_tool.py new file mode 100644 index 00000000..0517a3b8 --- /dev/null +++ b/examples/openhands-agent-server/oh_tool.py @@ -0,0 +1,96 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""OpenHands tool actions inside the environment, for run_trajectory.py. + +Runs with the runtime layer's Python, which has openhands-tools installed, so +file edits go through OpenHands' own ``file_editor`` and come back in the +exact form the agent sees. One long-lived server holds the editor, so the +heavy imports happen once and ``undo_edit`` sees earlier edits: + + /opt/oh/python/bin/python3 oh_tool.py --serve 9100 --daemon + +It answers ``POST /`` with ``{"tool": "file_editor", "args": {...}}`` and +``GET /health``. Callers outside reach it through ate-env-api +(``/v1alpha/envs//ports/9100/``); the router delivers to the actor's +address, not loopback, so it listens on all interfaces like the agent-server. +""" + +from __future__ import annotations + +import json +import os +import sys +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + + +def run(step: dict) -> dict: + if step.get("tool") != "file_editor": + return {"text": f"oh_tool.py runs file_editor only, not {step.get('tool')!r}", "is_error": True} + from openhands.tools.file_editor.impl import file_editor + + try: + obs = file_editor(**step.get("args", {})) + except Exception as exc: # noqa: BLE001 (a bad call is an observation, not a crash) + return {"text": f"{type(exc).__name__}: {exc}", "is_error": True} + return {"text": obs.text, "is_error": bool(obs.is_error)} + + +class Handler(BaseHTTPRequestHandler): + def _reply(self, payload: dict, status: int = 200) -> None: + body = json.dumps(payload).encode() + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def do_GET(self) -> None: + self._reply({"status": "ok"}) + + def do_POST(self) -> None: + step = json.loads(self.rfile.read(int(self.headers.get("Content-Length", 0))) or b"{}") + self._reply(run(step)) + + def log_message(self, fmt: str, *args) -> None: + sys.stderr.write("oh-tool: " + fmt % args + "\n") + + +def daemonize(log_path: str) -> None: + """Detach from the caller's session (the agent-server's command endpoint + ends its process group when the command returns).""" + if os.fork(): + os._exit(0) + os.setsid() + if os.fork(): + os._exit(0) + log = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644) + null = os.open(os.devnull, os.O_RDONLY) + os.dup2(null, 0) + os.dup2(log, 1) + os.dup2(log, 2) + + +if __name__ == "__main__": + args = sys.argv[1:] + if not args or args[0] != "--serve": + sys.exit("usage: oh_tool.py --serve PORT [--daemon]") + port = int(args[1]) if len(args) > 1 and args[1].isdigit() else 9100 + if "--daemon" in args: + daemonize(f"/tmp/oh_tool_{port}.log") + from openhands.tools.file_editor.impl import ( + file_editor, # noqa: F401 (import once, up front) + ) + + ThreadingHTTPServer(("0.0.0.0", port), Handler).serve_forever() diff --git a/examples/openhands-agent-server/run_trajectory.py b/examples/openhands-agent-server/run_trajectory.py new file mode 100644 index 00000000..20ccdd7e --- /dev/null +++ b/examples/openhands-agent-server/run_trajectory.py @@ -0,0 +1,130 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Execute a CodeAct trajectory in an ate-env environment from outside. + +A trajectory is the list of tool calls a CodeAct agent makes, in OpenHands' +tool vocabulary: ``terminal`` (``command``, optional ``cwd`` and +``timeout``), ``file_editor`` (``command``, ``path``, ``file_text``, +``old_str``, ``new_str``, ``insert_line``, ``view_range``) and ``finish`` +(``message``). This script connects to the agent-server in the environment +through ate-env-api and runs each call there: terminal commands through the +agent-server's command endpoint, file edits through OpenHands' own +``file_editor``, served inside the environment by ``oh_tool.py`` on the +runtime layer's Python and reached through ate-env-api like the agent-server. +Every observation is printed and, with ``--out``, written next to its call. + +The decisions come from the trajectory file, so nothing inside the +environment needs to reach a model. The same loop with a model call before +each step is a client-side agent: the model stays wherever it is reachable, +the environment only executes. + + pip install openhands-sdk==1.55.0 + SESSION_KEY= python run_trajectory.py trajectories/hello.json [--out run.json] + +Trajectory file: a JSON list of ``{"tool": ..., "args": {...}, "thought": ...}`` +(``thought`` optional). Environment as for ate_env_workspace.py. +""" + +from __future__ import annotations + +import argparse +import json +import os +import time +import urllib.request +from pathlib import Path + +from ate_env_workspace import agent_server_url, workspace_for + +LAYER_PYTHON = "/opt/oh/python/bin/python3" +TOOL_RUNNER = "/tmp/oh_tool.py" +TOOL_PORT = 9100 + + +def post_json(url: str, payload: dict, timeout: float = 120) -> dict: + req = urllib.request.Request(url, data=json.dumps(payload).encode(), + headers={"Content-Type": "application/json"}) + with urllib.request.urlopen(req, timeout=timeout) as resp: + return json.loads(resp.read()) + + +def start_tool_runner(workspace, env_id: str) -> str: + """Start oh_tool.py inside the environment and return its URL.""" + workspace.file_upload(Path(__file__).with_name("oh_tool.py"), TOOL_RUNNER) + workspace.execute_command(f"{LAYER_PYTHON} {TOOL_RUNNER} --serve {TOOL_PORT} --daemon", timeout=30) + url = agent_server_url(env_id, port=TOOL_PORT) + "/" + deadline = time.monotonic() + 120 + while True: + try: + with urllib.request.urlopen(url, timeout=10) as resp: + if resp.status == 200: + return url + except OSError: + if time.monotonic() > deadline: + raise RuntimeError("oh_tool.py did not start inside the environment") from None + time.sleep(1) + + +def run_step(workspace, tool_url: str, step: dict) -> dict: + tool, args = step["tool"], step.get("args", {}) + if tool == "terminal": + res = workspace.execute_command( + args["command"], + cwd=args.get("cwd") or os.environ.get("WORKDIR"), + timeout=float(args.get("timeout", 120)), + ) + text = (res.stdout + res.stderr).rstrip() + return {"text": f"{text}\n[exit {res.exit_code}]", "is_error": res.exit_code != 0} + if tool == "file_editor": + return post_json(tool_url, step) + if tool == "finish": + return {"text": args.get("message", ""), "is_error": False} + raise ValueError(f"unknown tool {tool!r}") + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("env_id") + parser.add_argument("trajectory", type=Path) + parser.add_argument("--out", type=Path, help="write the calls with their observations here") + args = parser.parse_args() + + steps = json.loads(args.trajectory.read_text()) + workspace = workspace_for(args.env_id) + t0 = time.monotonic() + tool_url = start_tool_runner(workspace, args.env_id) + print(f"tool runner up in {time.monotonic() - t0:.1f}s") + + results = [] + started = time.monotonic() + for i, step in enumerate(steps, 1): + if step.get("thought"): + print(f"[{i}] thought {step['thought']}") + print(f"[{i}] {step['tool']:<11}{json.dumps(step.get('args', {}))[:150]}") + t0 = time.monotonic() + obs = run_step(workspace, tool_url, step) + results.append({**step, "observation": obs, "seconds": round(time.monotonic() - t0, 2)}) + marker = "error" if obs["is_error"] else "ok" + print("\n".join(f" {line}" for line in obs["text"].splitlines()[-12:]) or " (no output)") + print(f" -> {marker} in {results[-1]['seconds']}s") + if step["tool"] == "finish": + break + print(f"{len(results)} steps in {time.monotonic() - started:.1f}s") + if args.out: + args.out.write_text(json.dumps(results, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/examples/openhands-agent-server/trajectories/django-11099-grade.json b/examples/openhands-agent-server/trajectories/django-11099-grade.json new file mode 100644 index 00000000..cf676beb --- /dev/null +++ b/examples/openhands-agent-server/trajectories/django-11099-grade.json @@ -0,0 +1,45 @@ +[ + { + "thought": "Grader: apply SWE-bench's hidden test change for django__django-11099.", + "tool": "file_editor", + "args": { + "command": "str_replace", + "path": "/testbed/tests/auth_tests/test_validators.py", + "old_str": "\"en\\u2013dash\",\n ]", + "new_str": "\"en\\u2013dash\", 'trailingnewline\\u000A',\n ]" + } + }, + { + "tool": "file_editor", + "args": { + "command": "str_replace", + "path": "/testbed/tests/auth_tests/test_validators.py", + "old_str": "invalid_usernames = [\"o'connell\", 'Éric', 'jean marc', \"أحمد\"]", + "new_str": "invalid_usernames = [\"o'connell\", 'Éric', 'jean marc', \"أحمد\", 'trailingnewline\\n']" + } + }, + { + "thought": "FAIL_TO_PASS tests with the fix.", + "tool": "terminal", + "args": { + "command": "/opt/miniconda3/envs/testbed/bin/python tests/runtests.py auth_tests.test_validators.UsernameValidatorsTests 2>&1 | grep -E '^(Ran|OK|FAILED)'", + "cwd": "/testbed", + "timeout": 600 + } + }, + { + "thought": "Same tests with the fix stashed: they must fail.", + "tool": "terminal", + "args": { + "command": "git stash push -q django/contrib/auth/validators.py && { /opt/miniconda3/envs/testbed/bin/python tests/runtests.py auth_tests.test_validators.UsernameValidatorsTests 2>&1 | grep -E '^(Ran|OK|FAILED)'; }; git stash pop -q && git diff --stat", + "cwd": "/testbed", + "timeout": 600 + } + }, + { + "tool": "finish", + "args": { + "message": "Resolved: UsernameValidatorsTests fail without the patch and pass with it." + } + } +] diff --git a/examples/openhands-agent-server/trajectories/django-11099.json b/examples/openhands-agent-server/trajectories/django-11099.json new file mode 100644 index 00000000..78824294 --- /dev/null +++ b/examples/openhands-agent-server/trajectories/django-11099.json @@ -0,0 +1,78 @@ +[ + { + "thought": "The issue: UsernameValidator allows a trailing newline. Find the regexes.", + "tool": "terminal", + "args": { + "command": "grep -n 'regex' django/contrib/auth/validators.py", + "cwd": "/testbed" + } + }, + { + "thought": "Reproduce: $ matches before a trailing newline.", + "tool": "terminal", + "args": { + "command": "/opt/miniconda3/envs/testbed/bin/python -c \"from django.conf import settings; settings.configure()\nfrom django.contrib.auth.validators import ASCIIUsernameValidator as A, UnicodeUsernameValidator as U\nfrom django.core.exceptions import ValidationError\nfor V in (A, U):\n try: V()('joe\\\\n'); print(V.__name__, 'accepts a trailing newline')\n except ValidationError: print(V.__name__, 'rejects a trailing newline')\"", + "cwd": "/testbed" + } + }, + { + "tool": "file_editor", + "args": { + "command": "view", + "path": "/testbed/django/contrib/auth/validators.py", + "view_range": [ + 8, + 25 + ] + } + }, + { + "thought": "Anchor at the true end of string with \\Z, in both validators.", + "tool": "file_editor", + "args": { + "command": "str_replace", + "path": "/testbed/django/contrib/auth/validators.py", + "old_str": "class ASCIIUsernameValidator(validators.RegexValidator):\n regex = r'^[\\w.@+-]+$'", + "new_str": "class ASCIIUsernameValidator(validators.RegexValidator):\n regex = r'^[\\w.@+-]+\\Z'" + } + }, + { + "tool": "file_editor", + "args": { + "command": "str_replace", + "path": "/testbed/django/contrib/auth/validators.py", + "old_str": "class UnicodeUsernameValidator(validators.RegexValidator):\n regex = r'^[\\w.@+-]+$'", + "new_str": "class UnicodeUsernameValidator(validators.RegexValidator):\n regex = r'^[\\w.@+-]+\\Z'" + } + }, + { + "thought": "Check the reproduction again.", + "tool": "terminal", + "args": { + "command": "/opt/miniconda3/envs/testbed/bin/python -c \"from django.conf import settings; settings.configure()\nfrom django.contrib.auth.validators import ASCIIUsernameValidator as A, UnicodeUsernameValidator as U\nfrom django.core.exceptions import ValidationError\nfor V in (A, U):\n try: V()('joe\\\\n'); print(V.__name__, 'accepts a trailing newline')\n except ValidationError: print(V.__name__, 'rejects a trailing newline')\"", + "cwd": "/testbed" + } + }, + { + "thought": "Run the validator tests.", + "tool": "terminal", + "args": { + "command": "/opt/miniconda3/envs/testbed/bin/python tests/runtests.py auth_tests.test_validators 2>&1 | grep -E '^(Ran|OK|FAILED)'", + "cwd": "/testbed", + "timeout": 600 + } + }, + { + "tool": "terminal", + "args": { + "command": "git diff", + "cwd": "/testbed" + } + }, + { + "tool": "finish", + "args": { + "message": "UsernameValidators now reject a trailing newline: both regexes end in \\Z instead of $." + } + } +] diff --git a/examples/openhands-agent-server/trajectories/hello.json b/examples/openhands-agent-server/trajectories/hello.json new file mode 100644 index 00000000..6c5d37fd --- /dev/null +++ b/examples/openhands-agent-server/trajectories/hello.json @@ -0,0 +1,46 @@ +[ + { + "thought": "Look around first.", + "tool": "terminal", + "args": { + "command": "head -1 /etc/os-release; echo \"user: $(id -un)\"; echo \"cwd: $(pwd)\"; ls / | head -8" + } + }, + { + "tool": "terminal", + "args": { + "command": "mkdir -p /tmp/trajectory-demo" + } + }, + { + "thought": "Write a file with OpenHands' file editor.", + "tool": "file_editor", + "args": { + "command": "create", + "path": "/tmp/trajectory-demo/notes.md", + "file_text": "# notes\n\nwritten by a trajectory\n" + } + }, + { + "tool": "file_editor", + "args": { + "command": "str_replace", + "path": "/tmp/trajectory-demo/notes.md", + "old_str": "written by a trajectory", + "new_str": "written and edited by a trajectory" + } + }, + { + "tool": "file_editor", + "args": { + "command": "view", + "path": "/tmp/trajectory-demo/notes.md" + } + }, + { + "tool": "finish", + "args": { + "message": "Inspected the environment and wrote /tmp/trajectory-demo/notes.md." + } + } +] From e6116a4db8e48a969133e4f5f5920985f16563cd Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:34:24 -0700 Subject: [PATCH 04/13] examples/openhands-agent-server: hash-lock the layer's dependencies, pin base images, precompile bytecode The build stage moves from Debian bullseye (out of LTS) to trixie, pinned by digest like the other stages. uv installs the agent-server from a hash-locked requirements.txt for the target platform manylinux_2_31, so the layer still runs on glibc 2.31 whatever the builder's glibc; lock.sh regenerates the lock for amd64 and arm64. openhands-sdk and openhands-tools are pinned to the agent-server's version: the agent-server leaves them open, and the old build had picked up 1.55.1. Bytecode is precompiled with unchecked-hash instead of deleted: on the read-only mount Python cannot write a cache, and startup drops from about 6.5 s to 3.5 s on arm64. The README and the Dockerfile now say the layer needs glibc 2.31 or newer, not just /bin/sh, and that the client SDK must match the agent-server. --- examples/openhands-agent-server/Dockerfile | 67 +- examples/openhands-agent-server/README.md | 38 +- .../build-constraints.in | 3 + .../build-constraints.txt | 6 + examples/openhands-agent-server/lock.sh | 54 + .../openhands-agent-server/requirements.in | 8 + .../openhands-agent-server/requirements.txt | 4103 +++++++++++++++++ 7 files changed, 4254 insertions(+), 25 deletions(-) create mode 100644 examples/openhands-agent-server/build-constraints.in create mode 100644 examples/openhands-agent-server/build-constraints.txt create mode 100755 examples/openhands-agent-server/lock.sh create mode 100644 examples/openhands-agent-server/requirements.in create mode 100644 examples/openhands-agent-server/requirements.txt diff --git a/examples/openhands-agent-server/Dockerfile b/examples/openhands-agent-server/Dockerfile index 7d42d34f..c31d52a8 100644 --- a/examples/openhands-agent-server/Dockerfile +++ b/examples/openhands-agent-server/Dockerfile @@ -13,9 +13,11 @@ # limitations under the License. # A runtime layer that carries the OpenHands agent-server, for mounting into -# unmodified images at /opt/oh. It brings everything it runs on: a relocatable -# Python and the agent-server's packages. From the image it is mounted into it -# uses only the kernel and /bin/sh, which the agent-server runs commands with. +# unmodified images at /opt/oh. It brings its own relocatable Python and the +# agent-server's packages. From the image it is mounted into it uses the +# kernel, the C library and /bin/sh, which the agent-server runs commands +# with. The C library must be glibc 2.31 or newer: Debian 11, Ubuntu 20.04, +# RHEL 9 and later work; Alpine (musl), Ubuntu 18.04 and CentOS 7 do not. # # Why not the binary in the published ghcr.io/openhands/agent-server images: # it is a PyInstaller build made on Debian bookworm and needs glibc 2.36, so it @@ -25,18 +27,23 @@ # # - python-build-standalone CPython, built for glibc 2.17 and relocatable, so # it runs wherever the layer is mounted; -# - openhands-agent-server installed by pip on Debian bullseye (glibc 2.31), -# so pip only picks wheels that run on glibc 2.31 and newer. +# - the agent-server and its dependencies from requirements.txt, hash-locked, +# installed by uv for the target platform manylinux_2_31, so only wheels +# that run on glibc 2.31 and newer qualify, whatever the build stage's own +# glibc. The one source-only dependency (func-timeout, pure Python) is +# built with the hash-locked setuptools from build-constraints.txt. # # docker buildx build --platform linux/amd64,linux/arm64 \ # -t /oh-agent-server-layer:1.55.0 --push . # -# Override AGENT_SERVER_VERSION to build another release. The image has no -# shell or entrypoint of its own; it is only ever mounted. +# Another agent-server release: change the three versions in requirements.in +# and run ./lock.sh, then build with --build-arg AGENT_SERVER_VERSION=, +# which is checked against the installed agent-server, sdk and tools. The image +# has no shell or entrypoint of its own; it is only ever mounted. Base images are pinned by digest; update the tag and +# the digest together. -# The interpreter is fetched and verified in a small stage of its own, so the -# build stage needs no package manager. -FROM curlimages/curl:8.16.0 AS fetch +# The interpreter is fetched and verified in a small stage of its own. +FROM curlimages/curl:8.16.0@sha256:463eaf6072688fe96ac64fa623fe73e1dbe25d8ad6c34404a669ad3ce1f104b6 AS fetch ARG TARGETARCH ARG PBS_RELEASE=20261013 @@ -44,8 +51,8 @@ ARG PYTHON_VERSION=3.12.15 ARG PBS_SHA256_AMD64=5d546fc004fc2435fa142eb8f42ed8de5eb4725963dc22f7e6671014a80eb560 ARG PBS_SHA256_ARM64=34a43010cc05982d8b5debbad7d09f7b1200117ebf018f7acfd656895e408205 -RUN set -eu; \ - case "${TARGETARCH}" in \ +SHELL ["/bin/ash", "-euo", "pipefail", "-c"] +RUN case "${TARGETARCH}" in \ amd64) arch=x86_64; sum="${PBS_SHA256_AMD64}" ;; \ arm64) arch=aarch64; sum="${PBS_SHA256_ARM64}" ;; \ *) echo "unsupported architecture ${TARGETARCH}" >&2; exit 1 ;; \ @@ -56,19 +63,39 @@ RUN set -eu; \ mkdir -p /tmp/oh; \ tar -xzf "/tmp/${f}" -C /tmp/oh -FROM debian:bullseye-slim AS build +FROM ghcr.io/astral-sh/uv:0.11.18@sha256:78bc42400d77b0678ba95765305c826652ed5431f399257271dda681d0318f03 AS uv +# Debian 13. Its own glibc does not matter: uv selects wheels for the target +# platform, and the slim image has no compiler, so a native package without a +# suitable wheel fails here instead of linking against this builder. +FROM debian:trixie-slim@sha256:a29215f6a35e51e22adffa17f89e9d2ef06214e64a2bad10d765c46aea49f11f AS build + +ARG TARGETARCH ARG AGENT_SERVER_VERSION=1.55.0 +ENV UV_NO_CACHE=1 UV_PYTHON_DOWNLOADS=never +COPY --from=uv /uv /usr/local/bin/uv COPY --from=fetch /tmp/oh/ /opt/oh/ +COPY requirements.txt build-constraints.txt /tmp/lock/ -# Wheels wherever they exist. The few pure-Python packages that ship only as -# source (func-timeout) build fine; a native one would fail here, because the -# slim image has no compiler, rather than link against this builder. -RUN /opt/oh/python/bin/python3 -m pip install --no-cache-dir --prefer-binary \ - "openhands-agent-server==${AGENT_SERVER_VERSION}" \ - && /opt/oh/python/bin/python3 -c "import openhands.agent_server" \ - && find /opt/oh/python -name __pycache__ -prune -exec rm -rf {} + +# Precompiled with unchecked-hash bytecode: the layer is mounted read-only, so +# Python could not write a cache and would compile every module at each start; +# unchecked-hash .pyc files are used without looking at the sources' mtimes. +# func_timeout's py2_raise.py is Python 2 only and never imported on Python 3. +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +RUN case "${TARGETARCH}" in \ + amd64) platform=x86_64-manylinux_2_31 ;; \ + arm64) platform=aarch64-manylinux_2_31 ;; \ + *) echo "unsupported architecture ${TARGETARCH}" >&2; exit 1 ;; \ + esac \ + && uv pip install --index-url https://pypi.org/simple --require-hashes \ + --python /opt/oh/python/bin/python3 --python-platform "${platform}" --python-version 3.12 \ + --target /opt/oh/python/lib/python3.12/site-packages \ + --build-constraints /tmp/lock/build-constraints.txt -r /tmp/lock/requirements.txt \ + && /opt/oh/python/bin/python3 -c "import importlib.metadata as m, openhands.agent_server; v = {p: m.version(p) for p in ('openhands-agent-server', 'openhands-sdk', 'openhands-tools')}; assert set(v.values()) == {'${AGENT_SERVER_VERSION}'}, v" \ + && find /opt/oh/python -name __pycache__ -prune -exec rm -rf {} + \ + && /opt/oh/python/bin/python3 -m compileall -q -j 0 --invalidation-mode unchecked-hash \ + -x 'func_timeout/py2_raise\.py$' /opt/oh/python/lib FROM scratch COPY --from=build /opt/oh/ / diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md index 89050d54..1d86995e 100644 --- a/examples/openhands-agent-server/README.md +++ b/examples/openhands-agent-server/README.md @@ -1,7 +1,8 @@ # Example: the OpenHands agent-server in any image Runs the [OpenHands](https://github.com/OpenHands/software-agent-sdk) agent-server inside an -unmodified image (a coding-task image, a language base image, anything with `/bin/sh`) and drives +unmodified image (a coding-task image, a language base image: any image with `/bin/sh` and glibc +2.31 or newer) and drives it from outside the cluster: commands and files, OpenHands' own CodeAct agent, and CodeAct trajectories executed step by step from a client: @@ -16,6 +17,7 @@ trajectories executed step by step from a client: | file | what it is | |---|---| | [`Dockerfile`](Dockerfile) | the runtime layer: a relocatable Python with `openhands-agent-server` | +| [`requirements.txt`](requirements.txt), [`build-constraints.txt`](build-constraints.txt) | the layer's hash-locked dependencies, generated by [`lock.sh`](lock.sh) from the `.in` files | | [`demo.sh`](demo.sh) | registers a base template and drives the agent-server in one environment per image | | [`ate_env_workspace.py`](ate_env_workspace.py) | `workspace_for(env)`: an OpenHands `RemoteWorkspace` for an environment | | [`codeact_agent.py`](codeact_agent.py) | runs OpenHands' CodeAct agent in an environment, with a model or `--scripted` | @@ -32,7 +34,7 @@ docker buildx build --platform linux/amd64,linux/arm64 \ crane digest --platform linux/amd64 /oh-agent-server-layer:1.55.0 ``` -`AGENT_SERVER_VERSION` selects another release. The layer does not reuse the binary in the published +The layer does not reuse the binary in the published `ghcr.io/openhands/agent-server` images: that is a PyInstaller build made on Debian bookworm, it needs glibc 2.36, and on older distributions it fails at start, for example on Ubuntu 22.04 (glibc 2.35): @@ -42,9 +44,25 @@ ImportError: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.36' not found (re Starting it through its own image's loader does not help either, because PyInstaller re-executes itself. The layer is [python-build-standalone](https://github.com/astral-sh/python-build-standalone) -CPython (built for glibc 2.17, relocatable) with `openhands-agent-server` installed by pip on Debian -bullseye, so every wheel runs on glibc 2.31 and newer. From the image it is mounted into it uses only -the kernel and `/bin/sh`. It is about 210 MB compressed and 740 MB on disk. +CPython (built for glibc 2.17, relocatable) with `openhands-agent-server` and its dependencies +installed by uv for the target platform `manylinux_2_31`, so every wheel runs on glibc 2.31 and newer. +From the image it is mounted into it uses the kernel, the C library and `/bin/sh`. The C library +must be glibc 2.31 or newer: Debian 11, Ubuntu 20.04, RHEL 9 and later work; Alpine (musl), Ubuntu +18.04 (glibc 2.27) and CentOS 7 (glibc 2.17) do not. The layer's bytecode is precompiled +(`unchecked-hash`), because Python cannot write a cache on the read-only mount; the agent-server +answers `/health` in about 3.5 s instead of 6.5 s (arm64, local Docker). The layer is about 250 MB +compressed and 900 MB on disk. + +The dependency set is hash-locked: [`requirements.txt`](requirements.txt) (the agent-server and +everything it pulls in, the same for linux/amd64 and linux/arm64) and +[`build-constraints.txt`](build-constraints.txt) (setuptools, to build the one source-only +dependency, func-timeout). Base images are pinned by digest in the Dockerfile. To move to another +agent-server release, change the versions in [`requirements.in`](requirements.in) (the agent-server, +`openhands-sdk` and `openhands-tools`, which the agent-server does not pin itself), run +[`./lock.sh`](lock.sh) (needs uv; it re-resolves for both architectures and fails if they differ), +and build with `--build-arg AGENT_SERVER_VERSION=`; the build checks that the installed +agent-server, sdk and tools are that version. Change the client's `openhands-sdk` to the same version (see +[Versions](#versions)). ## 2. Register a base template @@ -175,6 +193,16 @@ examples/openhands-agent-server/demo.sh It also takes `PORT`, `AGENT_SERVER_ENV` (extra `NAME=VALUE` settings), `COMMAND`, `WORKDIR` and `KEEP=1` (leave the environments up for the SDK scripts); see its header. +## Versions + +The client scripts talk to the agent-server's REST and WebSocket API through the OpenHands SDK, and +that API changes between releases, so the client's `openhands-sdk` (and `openhands-tools`, for +`codeact_agent.py`) must be the same version as the agent-server in the layer: 1.55.0 here. To change +both, set the new versions in `requirements.in`, run `./lock.sh`, rebuild and push the layer, register +the template with the new layer digest, and install the client with +`pip install openhands-sdk== openhands-tools==`. The `pip install` lines in the +scripts' docstrings name the version too. + ## Verified GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port proxy, agent-server diff --git a/examples/openhands-agent-server/build-constraints.in b/examples/openhands-agent-server/build-constraints.in new file mode 100644 index 00000000..2b7d3e2b --- /dev/null +++ b/examples/openhands-agent-server/build-constraints.in @@ -0,0 +1,3 @@ +# Build backend for the one dependency that ships only as source +# (func-timeout, pure Python). ./lock.sh pins it with hashes. +setuptools diff --git a/examples/openhands-agent-server/build-constraints.txt b/examples/openhands-agent-server/build-constraints.txt new file mode 100644 index 00000000..1b23cf39 --- /dev/null +++ b/examples/openhands-agent-server/build-constraints.txt @@ -0,0 +1,6 @@ +# Generated by ./lock.sh from build-constraints.in for CPython 3.12, linux/amd64 and +# linux/arm64, manylinux_2_31. Do not edit; run ./lock.sh. +setuptools==84.0.0 \ + --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 \ + --hash=sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73 + # via -r build-constraints.in diff --git a/examples/openhands-agent-server/lock.sh b/examples/openhands-agent-server/lock.sh new file mode 100755 index 00000000..52910c28 --- /dev/null +++ b/examples/openhands-agent-server/lock.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Regenerates requirements.txt and build-constraints.txt, the hash-locked +# dependency set the Dockerfile installs. It resolves for CPython 3.12 on +# linux/amd64 and linux/arm64, each restricted to manylinux_2_31 wheels (glibc +# 2.31), and fails when the two resolutions differ, because the Dockerfile +# installs one file on both. Needs uv. +# +# ./lock.sh +set -euo pipefail +cd "$(dirname "$0")" + +INDEX_URL=${INDEX_URL:-https://pypi.org/simple} +tmp=$(mktemp -d) +trap 'rm -rf "${tmp}"' EXIT + +compile() { # compile [uv args...] + local in=$1 out=$2 + shift 2 + uv pip compile --quiet --no-header --generate-hashes --index-url "${INDEX_URL}" \ + --python-version 3.12 "$@" "${in}" -o "${out}" +} + +for arch in x86_64 aarch64; do + compile requirements.in "${tmp}/${arch}.txt" --python-platform "${arch}-manylinux_2_31" +done +if ! diff -q "${tmp}/x86_64.txt" "${tmp}/aarch64.txt" >/dev/null; then + diff "${tmp}/x86_64.txt" "${tmp}/aarch64.txt" >&2 || true + echo "lock.sh: amd64 and arm64 resolve differently; split requirements.txt per architecture" >&2 + exit 1 +fi +compile build-constraints.in "${tmp}/build.txt" + +header='# Generated by ./lock.sh from %s for CPython 3.12, linux/amd64 and +# linux/arm64, manylinux_2_31. Do not edit; run ./lock.sh. +' +# shellcheck disable=SC2059 # the header is the format +{ printf "${header}" requirements.in; cat "${tmp}/x86_64.txt"; } >requirements.txt +# shellcheck disable=SC2059 +{ printf "${header}" build-constraints.in; cat "${tmp}/build.txt"; } >build-constraints.txt +echo "wrote requirements.txt ($(grep -c '==' requirements.txt) packages) and build-constraints.txt" diff --git a/examples/openhands-agent-server/requirements.in b/examples/openhands-agent-server/requirements.in new file mode 100644 index 00000000..04a3f63f --- /dev/null +++ b/examples/openhands-agent-server/requirements.in @@ -0,0 +1,8 @@ +# The runtime layer's top-level requirement. Change the version here, then run +# ./lock.sh to regenerate requirements.txt; keep the client's openhands-sdk at +# the same version (see the README). +openhands-agent-server==1.55.0 +# The agent-server does not pin these; without the pins the layer would carry +# whatever newer sdk/tools release exists, not the version the client runs. +openhands-sdk==1.55.0 +openhands-tools==1.55.0 diff --git a/examples/openhands-agent-server/requirements.txt b/examples/openhands-agent-server/requirements.txt new file mode 100644 index 00000000..6d4fad6d --- /dev/null +++ b/examples/openhands-agent-server/requirements.txt @@ -0,0 +1,4103 @@ +# Generated by ./lock.sh from requirements.in for CPython 3.12, linux/amd64 and +# linux/arm64, manylinux_2_31. Do not edit; run ./lock.sh. +agent-client-protocol==0.12.1 \ + --hash=sha256:9963ccb590ed96d7dc542c9c023e90676fc76b1c6562e21a9ee112f4fbcb0095 \ + --hash=sha256:aaf3cc301ed87d9e2c4ccb91adfe1cd58784355f6ce7e513f50e7ca2391862ef + # via openhands-sdk +aiofile==3.12.3 \ + --hash=sha256:5c1bcc9e929c50834608e8cc1a4cc1d7503eb60c15a535b779fd39e2f372c017 \ + --hash=sha256:caa6aa746b5e47e2165f7abd741b6415e49cf4d44fddc0f61844612cc3924d41 + # via py-key-value-aio +aiofiles==25.1.0 \ + --hash=sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2 \ + --hash=sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695 + # via bubus +aiohappyeyeballs==2.7.1 \ + --hash=sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d \ + --hash=sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472 + # via aiohttp +aiohttp==3.14.5 \ + --hash=sha256:0133c3c3b54a0bf1e71fa5c1ad95c93f07fd54e24ef1fe182f5122e1573d2bf1 \ + --hash=sha256:038c2c7e8caa26b6c8423779b5eaf1893904048a512c19b32fe841ffa5592b50 \ + --hash=sha256:0684952aeae1f5dbfe02d46039338513b94009baecd15d8e4098a357c4c4a2a6 \ + --hash=sha256:0790ec66fa4013e83c53b9025a45d454723da1a2fce28b3208c9b32d08af162f \ + --hash=sha256:083673c7a94c3ea035caaa5ca04288bdb44887abfe1f5ba23294e6a4b03efd2d \ + --hash=sha256:09e0eb18c7e0c8777e2f9149de63799195b9b3ca1b5c81ba6f32f2c6b8628210 \ + --hash=sha256:09ec102b4b8c9a920275733bbc11fdbb615efe6f9231a06007c0218d336fb77a \ + --hash=sha256:0a8ea271867e360ac985ae607f4a23ad9a38414b9aca1d49ec98839ae660e49f \ + --hash=sha256:0e6f16f5e49c4b8267988c05ab07760d7064cea57d077c3d068d04b0fbb992cb \ + --hash=sha256:1061b364556e8172e8d46b0b183adeeb73e8c42d30ebc745591e1bd89acad52e \ + --hash=sha256:1220353657ad49493551f089ce02f1a348fd57ffd585bfec77f2f3c4fe3a7346 \ + --hash=sha256:137351bf20bbed9a65e839f4a4452ac377389bdb2f2857d2acffef38f5e9f2d1 \ + --hash=sha256:149fb56caf7acb67073126f675d0958d9c4b3125fcd3f6d4877df98aa8a97ce9 \ + --hash=sha256:14f04769cfefe4734016a856a83af36133cd17779cef9ae817f812b8ba9d6d51 \ + --hash=sha256:15a310d3c71398e3d7bfc93a1a73fbe664315cd9e9016b8efc1cff85eeab7155 \ + --hash=sha256:1612fa5857b37bf32e5c1eaeefb96e3b01e9c70679eec81f0934e8a600080863 \ + --hash=sha256:16c8abd5bca220a47efe667d26f8460124c81810787e79ee87b242677563d9dd \ + --hash=sha256:19e2ba471507c34f8252402ab50f5ab512398b9ea8c8f1cb26beb3f75793ba30 \ + --hash=sha256:1aead151c3abbac6b32942e452020cb66d7efc099d253cc6c20f748e926c858b \ + --hash=sha256:1b438b73c38111818d0c9d6a5c2bfed8584c8e503a49ef085d70e874ec846738 \ + --hash=sha256:1b5416552740edf07234cc9437d0706f2acb67b93c198670b1a68e1b2b587dec \ + --hash=sha256:1d2d981b53dd09a319e3570ef8cc3bbc3ef86f5a7abef0f6b2bff3867db3a9e7 \ + --hash=sha256:1eb8167961ec4dfcc8cb9dd50bd0ee72519f7ef496be95203e49e27b01618382 \ + --hash=sha256:1ffa3a523a36d8628f98c06492ae16a31a23d14c0b4ec721757b477319f656d6 \ + --hash=sha256:20064a177a070d789ee64a50b01a9161d3468e989baacfc6c714aa685c4b332f \ + --hash=sha256:20726f9782d5c2744c1c66255842d1d163bb3edcf768b8de25216bf47f7b6ccf \ + --hash=sha256:20f085697d7e911f1f73c43ed03fafbed1e7121797e2eb5428efa80398060584 \ + --hash=sha256:225c579c23b68b343cccea27a7e06e3bd8ec23a09c30b427eb3f1e4ca6239b20 \ + --hash=sha256:24409db442e2fb6e766bc7f3943851a8381dec3098140e43bb2e843b79e31b12 \ + --hash=sha256:248d779ad720b49d4fb355720e60c9e5f444f95887bc16974fea48fc56c41789 \ + --hash=sha256:2528cb4c6b92008c76ac9ac6298624069bb2db91ff4929905512d1d84485f658 \ + --hash=sha256:276a4fc00b1d9ae492b802763a789c5b86328b989c5ea169f2faa447d6a11c7c \ + --hash=sha256:27c2322e03f66101acb09869ce1cf1efc04994ee95e1735b69827bf8c8b9d781 \ + --hash=sha256:293d3ae7c6a0ed176a42e59a1b5fde825ead65c835360f734148e96729f928d2 \ + --hash=sha256:2cc38a4f2b516bef1714e690df87a0e043faf1a7693c82d860091684453d5111 \ + --hash=sha256:3093b72c215bda16ce961a6d073f6e71d46e022962a9d5d457c5d4d421c78b57 \ + --hash=sha256:32e8fa6644e541fcd7e02430588c7fc93b602c1778ea0bc345505db76b61cfb4 \ + --hash=sha256:330900acd0dc4cb8b27f9c127fbaad770964845338493e7906ae3822e82dbf8d \ + --hash=sha256:33f706574e32c6e694f352a856e05caf18f7f2c871b3e87b41c55ea452b409ab \ + --hash=sha256:3ae800a20947e2c2e53088047d021e6bf7d51560cc49f6a0737a1f79d2e3a13c \ + --hash=sha256:3be7dd397d64ca3e1869626fa9318aaebb54b7bf93bc72d7a205448d83e4f748 \ + --hash=sha256:3e0eb43bed3c6801a6cee315195377789e90b2a72c2277a475b578535312488d \ + --hash=sha256:3e51a27980c3788e6e6b3325d694fdd4898087fa8a86b2763af77b39353da41e \ + --hash=sha256:3edbece0379b8b4aaa67619b8aa2399bb66fce372cd5911098a434ea77220aa0 \ + --hash=sha256:3f2dcc00191fd563e9075181a14ec31d7dd63223ced7582cc70a15a499de0c79 \ + --hash=sha256:42b5e616946dbaf505e2bff18c9af2cd4ef9e7ef300ee58a6e951a5b7cf147ae \ + --hash=sha256:42f320d4a5b00b9af0bddcfec5407dc6f2d9816f006b2f79ebbaa31f16895df3 \ + --hash=sha256:43351bdb5e4c3cb7d1772368e988534e869a74db7778079a83782c11c69535c7 \ + --hash=sha256:43e1b7994a8b038125f722bff07492ef501110722c2727c408995d9fb864c421 \ + --hash=sha256:4887d130a7bbfed3a85493bb5a25e5b5b558d40c1d986dd16970d2bb26d63793 \ + --hash=sha256:4f07fe3ac408d8b3f768be471dc3f56d43843c47d97c66120534467a15ead197 \ + --hash=sha256:50343c1757b4b6f6708eeaf24534b32f19dfb99fb1b762c00420867a62fc81e0 \ + --hash=sha256:50983e3be33d8c0942ab88cec3905b10602f64c469b20153c48c5d4e558dd016 \ + --hash=sha256:50a195903119008fe9cc68710535eb37f556ffffd6a7759afe70a2c145587045 \ + --hash=sha256:5558a7f5a05af9ecf744af91e5baefc436f93c9333e656c27ec253f9a6bbe178 \ + --hash=sha256:56572c42e3ecd636de8d2c3dd54cf5fc939cb5c32eb56297f176a0d366fac622 \ + --hash=sha256:56d9828f204331a5ca8850fcfe2bcce95a149f1f223f60cc7216e5524978e480 \ + --hash=sha256:579f97d5120f2971876d2ddca2968135f6944d00c44c3a6590ad7d86ca9b403f \ + --hash=sha256:5af42135fdfebdadbc2bcd9c0842a48ccf0d62794c36a260b21dc4b94d1e0119 \ + --hash=sha256:5ba14a839fbe87cf7c12a6b5661c05f324a296eb8363141edb3944ba63d4c9d3 \ + --hash=sha256:5c76f1802bab718a68ac3cce447160605c734551f95c67ae90fa1132b215cb29 \ + --hash=sha256:5e8f97c0488ffda3082766ac0f2c8150a9a58c4d05788330e479cfd449b37939 \ + --hash=sha256:5f3e96071686755d9cd3600c3880183eb94b012178e92746d68101800f0ed8a3 \ + --hash=sha256:5fb6a6e919bfb703227bc1ce6579281b84b1a2ba57deb9794dfdbec7dcd1e40c \ + --hash=sha256:602c1e9b718a3275c580149f947e7fac65044c0a20e599553fb12e9700da9eca \ + --hash=sha256:657291433bf4dd3142f3abac495764cd47d0c7c92087751e6666c6447e65fcef \ + --hash=sha256:6774814fd5c338e72ee0da5cbb9432816df450e69c019f72b5d29bdec2a1792d \ + --hash=sha256:6da32b5ff3fd78d244e37300463434c7145162bfd2b6e9e915ab164da37f7343 \ + --hash=sha256:6e1d8637cf73eebc92eba2e11d4cfff98a3b562f2505bd75bba766d908926e8d \ + --hash=sha256:6e4251c0ba4624a68a2c11471a1ac54c3306876c21f0ae86de085cc9241c8905 \ + --hash=sha256:6f275c11d1aa6d4c458e05a68be084efe3c55a113d99e3f46a318098e52948fc \ + --hash=sha256:6f967dde489ca6a8c02d093ab245d2cbf50ccb5c36adf0188b17b0ca39d24b67 \ + --hash=sha256:6fdcd6af7e2e51d1ba1b4bea16e97b074bcb7b5dd0246a9d8201341bb28085a0 \ + --hash=sha256:70cb4008ac2ed1e0ca9e824deb4b53d3aa0d939109698ebf1e723a84337bd794 \ + --hash=sha256:7457580535e019e1247ea35d6a02bf081ad30c26d0cbc210c93f6c3ab67a0835 \ + --hash=sha256:74b0a9c8270f9b0a11410e124ff8d4f18bfc1f1837440ec84da5ae7b50927b5d \ + --hash=sha256:74efb69332b85675b1eabd760a8cfc2e2cf42c60607c66f88014c1bdfb40942d \ + --hash=sha256:755933b107ea7a6a9ac916f635a70595a5b1a32fac10a8ff0b9f2ab88555550c \ + --hash=sha256:7779cd97e61ebe583ec2f1c5616cdd038aa08a4453b1848c67842176d054948e \ + --hash=sha256:788ecaa9c10533b786ce5ba70c4f2df78ad41819fd00a6c99d92b66f9a32e1da \ + --hash=sha256:7bd8ac754ebd6733a3e2a0dd1674c4d8ab086196803fd8dcd776f07b4e2607d9 \ + --hash=sha256:81c2b3dfd56c62bee6108e4852d5970b4cf9086390b6983f52b666e878c1f115 \ + --hash=sha256:823c910f046f23f4c713b8d99a2242dc65f591cb45ee86418fa11762a3c2963c \ + --hash=sha256:82c7583cd3dfdc7dcc927835b4f6c7faae7ecc1ba3ca5879321621ae2e6f8e84 \ + --hash=sha256:8966ecac808dd5f473c9c4cefd10cd3ffda71c18a4d3493b7c7d2ae1803bf2cc \ + --hash=sha256:8df7d481654ac96fe1ba9a02a9f67770fdd367823e0d5ef01b922725c4bd2cfa \ + --hash=sha256:8e317e0fb6b16212c881d2205a7d87414c29acd69320b3aa6dce9d9c7b86fe4f \ + --hash=sha256:932ce7e694bbc29b2bf6f64f2343c27d148d4997c771d01bdade4639b6749ff4 \ + --hash=sha256:939042d5cda21d41a6f512e7cc8b8e33a2aebff863352251da495fbd91b673b5 \ + --hash=sha256:94684b879ac1d71e4238850c99b62dc1b28d9086b156a2555f082010b85a865c \ + --hash=sha256:96a2e584f0b9ed8f1fa33211397dcf67bb7069866402cb405d191c2f0defb9a3 \ + --hash=sha256:9ad7e6aa38c20da1be697874349c4c273c8a03b7887169665081706398d0439a \ + --hash=sha256:9b42db919715e91eb76acf3bc492a9a7ccd8bd9adc6745c1412b689735269f14 \ + --hash=sha256:9bab2045550c4fe0f7baf89574db1b455c195750702ba96fef1f16972b146617 \ + --hash=sha256:9bf1d5dcc15204d9ec8b8ea4c18fd66e6b80e5de1f4ecbafb3a2f2740f8039d4 \ + --hash=sha256:9c061aa954daaf57d2a4b8374f9fca621ef0e1b603584431c220c22458c59b6d \ + --hash=sha256:9c428eb2bd8817588d16a0ab898aa4eb5d141f896aa2b394cc79a4cf61d9a8e2 \ + --hash=sha256:9cc882cf8619109583c906b4d4a85d6a111a98afa34b7a450d1e08118d016820 \ + --hash=sha256:9ce66feae6ac65327379460380549bf1b8df8e17c4e25df2a2bcf168272e3bed \ + --hash=sha256:a23fe35d776bc03cb495938b9594450d047e3bc08c5255315a82323e9cb7d2dd \ + --hash=sha256:a2c473a355f9239efcb72c92d5abfd8fcdb0cc78c8e9af607e72ca12dbb36593 \ + --hash=sha256:a63afd1f757de949028387e65a7127b61ad0f775432dbb0e62816ae619fe69ac \ + --hash=sha256:a6d02b4c38de03d9c7617813433e6a0fb6b522797974177d69d9dad431900833 \ + --hash=sha256:a7d470cf7b206e6359fc77b1b860632fde400d5a2ed59cd0181b93a686bc81ee \ + --hash=sha256:a95529a92a446db351675f4aab518feaf5e99842f63f5dd17160c2b74f382db3 \ + --hash=sha256:a9918e58faf62ba2c7147927d06057aec78f42475aff5048047ec47e7265a600 \ + --hash=sha256:a9d3983bd6ab7aa1cfd573544ae98df9b6cb6912a5185a198263e024a636861d \ + --hash=sha256:ab52d8f1fc1b64821c1fbad64a647ed6203627004059a6d1ed4f0858a1499703 \ + --hash=sha256:abfda5cb094a829f7bc25216a32f7db2e85cc65bd59910f8e7b40b3d9b224764 \ + --hash=sha256:adbeee7d6fd4cf5fe0aece2fb3edc4243615d3180430ba8149d01a90670cac99 \ + --hash=sha256:ae53924aa853a7a2ca20ed4142c7c6b56338e4d4cd999e2980075b9efc2e257a \ + --hash=sha256:b032a0023eb41d768ce77d83210ab2a3c389bc0b09313273c7e1eca48c10a755 \ + --hash=sha256:b1b8ece1e71132d2afba4dbc0c3d62c766e25165990b25db1196c04969eb3d84 \ + --hash=sha256:b2966998927d7bed9db12c0a4647b0c7b179755878fc9c357fe1ffd3e3b0c1a5 \ + --hash=sha256:b3cc509327c7b27f6f4727a8830f4004f6df7766e179f2f4b8e54e65c0bec5d3 \ + --hash=sha256:b7806e804889231b0e06469fd4a5c06313d1c0a3377322b6d9237fa5e0fe4167 \ + --hash=sha256:bea559ad70218d230663e4210875735076a9bfea5994cef34a55a25faeaf2544 \ + --hash=sha256:bf163cc701f3d4ac43ba7d97771bf5fd955220ef5500ef3ee847bc0ecfbf4ec1 \ + --hash=sha256:c147451b4a58e7050f7f7394e6c467867c84161560001f9ad4fb2d1446743946 \ + --hash=sha256:c172db893e516e1358e65a95ee20b7ce7173963eefe318b6ab2a2220688b999e \ + --hash=sha256:c1d60eafd9c7e8e74abd03a5b00df44e7febfe6d9b89b559c0a6551eef0699d4 \ + --hash=sha256:c2c30484dd1417ef98b51021ffa2cc0d7f3c78918adaaaab7e70817335ab3e02 \ + --hash=sha256:c32e26310cc10e547f53cd13d39a369034f69dcb7d749d5cb0e5f67bc196b6ba \ + --hash=sha256:c5ed596aedb9c42afd3fe0aae3117725378ac73d2cc5ddc735056fbdb96c5d02 \ + --hash=sha256:c8859a013ae0de1074660992139a1a440df3e6b219b86cf0d3f11c2692bb4fe3 \ + --hash=sha256:c8c4478bef6d57fcfda15dae461ea3c9f06aa7b257c58df3f2300174ccbb185a \ + --hash=sha256:cb11a971a3aea10f9b8373be628f1df932964fc6c6b174516d318a48c3ac4412 \ + --hash=sha256:cb131d775a1573c1aee66656bd78b023577bbdb6cb8349a07773bd4f73e68a6e \ + --hash=sha256:cd88b01f3d37b7a2a34f91d98f14720206f1ea3d540843fab2d649dd5fb91fec \ + --hash=sha256:d05e94cdfe0d15d0206f970722d2554780ce562787b21b218b275447f8751319 \ + --hash=sha256:d079c0a0135c36e7beb6f1c88087c8f108dc5891cdd0b5eafa778421bda70ed2 \ + --hash=sha256:d3112585250b199296c26ca6e0131640b6a8d01bab8b232d2eb3763ed469de11 \ + --hash=sha256:d418ce2af40c6bb685b3f663e9e8de27cb0a22431d8e88a167348d7f01878073 \ + --hash=sha256:d51db97c96384fbfcaf8f4c65922183a68b94f891c3c10c862ef5f6df2adbb1f \ + --hash=sha256:d94e44be379e569758fee8a9a58431cfc3c2598c708b92b1cfe96c66b4c94aef \ + --hash=sha256:dab9ac5a67c8d1f070c00fa8fccb7cbd1b8dcc1a8d6b42f37540df9b3d4cc603 \ + --hash=sha256:dbf53ae2601b7fd5a93c3944deea3a78d40f495226d582c35ef7a433425ce2b2 \ + --hash=sha256:dca3fa8d8a0a26679862eccb0b1a9151b2b9f1cd2c212e7a6335778faaff5833 \ + --hash=sha256:df37b620684e19b5e25724412518ccafc3b1a49cdac706fdbd2f983fad943450 \ + --hash=sha256:e1cc2bfaee8c214f06080a7c7d5772419b8a1108e8e5349236189811823fb02a \ + --hash=sha256:e29347c142cf6e99e0dff5e2995ead1d50fa3b51bf37a7c726a7ccfe5419745a \ + --hash=sha256:e4f5cf4dc72a71c4cfa9751b4950be22f733626670230d46e7d606592aa22d59 \ + --hash=sha256:e724a7b6091f0b1ac064f9d1b15ff9ec52e6033a86cdae649e5f086e32a3c0db \ + --hash=sha256:e87046c8ff77a8decdb6a41d8ab25824b47531b2da933aeab0c1e21c7acff329 \ + --hash=sha256:e95c8def4b81c5d68d5cf1f54c07acd7c0d2577af244e5b6da802120825737c6 \ + --hash=sha256:eb324e2009fb54db30a071dad7caf6998ee2879c4704007efb244514dad1fec1 \ + --hash=sha256:ef60869969180ec2464f1349aff07138ae35ca2200f0946cb3552e49e8f301a8 \ + --hash=sha256:ef692a24087a699c0a4a26af45e746e0c1eae2116f6d8a5ff91d8aae2b867b45 \ + --hash=sha256:efc21a454892828368b11c2c780de0ff8bc991f73f6b99c6b66e56205470929b \ + --hash=sha256:f001b571ead90ca1770f1e616db255351a1703317f20374c361ef22f12c06d09 \ + --hash=sha256:f2a7966bda23dd85051f1661ce0ace38d6890e05ec6c357ecae9d2479cba377e \ + --hash=sha256:f2ebb54b3f932210503072f09974b4fb574d823e497a944adfdcd140a6a00255 \ + --hash=sha256:f2ed8b64dc0c651c0f5a9c926777719770021251b8f336d97c4b80b660836ce1 \ + --hash=sha256:f375db73a39f5cf83696d500e21a67f418dc9a988955756f254be8f03b7b3651 \ + --hash=sha256:f59c7673465908cbe506117176156c127f29f917677afceada34957179221d91 \ + --hash=sha256:f8d40ce41991e9d56fab4f5dc4a51fe59bc3b5c77c27f4b148963064d00232e8 \ + --hash=sha256:f9033b43f511f27547c557dcaba0177649e10a3725336ccd2cce0fdc1dc4850d \ + --hash=sha256:fba47bc2c3d7303c3d027c6cf4d07626c37b1314ac81f5820c31032e0ca1f677 \ + --hash=sha256:fbdc5ec49f9ca3cd24955cf3520b10a4d4c901ba2572094c84274e9e7eb30534 \ + --hash=sha256:fce9523df31cea6284f3e2c479876750d7687cf671d7b25d32b19effc0e86441 \ + --hash=sha256:ff75a7537413a86e7cafe98e0e1d6e3dc4b15c6349896e7d5c6b881bfdb6d550 + # via + # browser-use + # litellm +aiosignal==1.4.0 \ + --hash=sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e \ + --hash=sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7 + # via aiohttp +aiosqlite==0.22.1 \ + --hash=sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650 \ + --hash=sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb + # via openhands-agent-server +alembic==1.20.0 \ + --hash=sha256:77eb101048d95f982c0353e9233404889dcd7a6fc244c107836c0e2fc9cf7d9d \ + --hash=sha256:db505480647bc60386c5369402f4a57a506b7539c9e9ef5e270d45cbbe4939bf + # via openhands-agent-server +annotated-doc==0.0.5 \ + --hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101 \ + --hash=sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb + # via fastapi +annotated-types==0.8.0 \ + --hash=sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7 \ + --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 + # via pydantic +anthropic==0.125.0 \ + --hash=sha256:3486013602eca76d8b12540764e53654f02cf4951110bca86cf06e67428a9f21 \ + --hash=sha256:e0cdd336580cb7411c1cdab69f80973e9bf4bff7f8e08141811d46307d45c682 + # via browser-use +anyio==4.15.1 \ + --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \ + --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94 + # via + # anthropic + # browser-use + # bubus + # google-genai + # groq + # httpx + # mcp + # openai + # py-key-value-aio + # sse-starlette + # starlette + # watchfiles +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # aiohttp + # cyclopts + # jsonschema + # jsonschema-path + # referencing +authlib==1.9.1 \ + --hash=sha256:5c7d9848f47cac340f060f76ae0bc09b59f5c8e89cc1bb032d4370a0c55b5974 \ + --hash=sha256:8b9be8b1e5174dcbf5520e984f1a22dc2edae5b6e40a61d4cedbf86b97e9e69b + # via + # browser-use + # fastmcp-slim +backoff==2.2.1 \ + --hash=sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba \ + --hash=sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8 + # via posthog +beartype==0.23.1 \ + --hash=sha256:4461b4dc57e3fdd6c8a8464b22cf118002eea107c33e9fef651c69068ab3cce3 \ + --hash=sha256:8b805f246b32931c74f2b321ea0939fe5bb2ff43c12a3eff19cc4c854b832e1d + # via py-key-value-aio +beautifulsoup4==4.15.0 \ + --hash=sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7 \ + --hash=sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9 + # via markdownify +binaryornot==0.6.0 \ + --hash=sha256:900adfd5e1b821255ba7e63139b0396b14c88b9286e74e03b6f51e0200331337 \ + --hash=sha256:cc8d57cfa71d74ff8c28a7726734d53a851d02fad9e3a5581fb807f989f702f0 + # via openhands-tools +blake3==1.0.11 \ + --hash=sha256:021bbad3b9a5bf7c1bcf6752e80a83a9b46e55bd2cf610c44c7f0c9cd7f989b8 \ + --hash=sha256:044c8ebd1004b765e9560b3a7359fc58ec08df08dc68f4e3f9855f035c9ab229 \ + --hash=sha256:073b79266bbc73f415d2fe897afefc385f1846816fcec6ab04f3406a599172dd \ + --hash=sha256:0865231cb616e0c2b9b8c6279a85776de056b475036d2c32cb1bef751b3eb44b \ + --hash=sha256:0955e9ab4df8eb3aa8f40d8273a8a93a076eb643f15ad5353634e443c1dcaaf0 \ + --hash=sha256:0d00f2f9325dacae0ea2823a8233459c12cdb56fad52d60ffc6278d674921656 \ + --hash=sha256:0e73a067d47d89693bbbb0735af271a6510eab3374b8c0482126c2258185484f \ + --hash=sha256:121e727827291ad48773eaaf1e2c5ab07973e45891f2e24455ae4ec022ac7df9 \ + --hash=sha256:1454994740029eea25816c3be31845590aa7bb628eeb5ff4c270b8f56531c40e \ + --hash=sha256:1664f6c19fcba54924b04599930ade0e955d1320bb4a31235d5a818ff18a86ad \ + --hash=sha256:1d1b43d1daec35a715556808bc2db2c103b678b2c8c9e62975adb4e42b5dfb02 \ + --hash=sha256:1fa8a7233a10f92c1e17b49de2205945279df4eaf13659cb17909409c1d136d2 \ + --hash=sha256:201c6e37b6941724be04e5d33e07f00917fc74891c91323dccccb2a6fa77b063 \ + --hash=sha256:232ab7bbc0893026836b6ffde7c45380fbb057be1fa8551cbc0855386792c562 \ + --hash=sha256:235bbfdd1dd3b0bf82aee8de8df01c55ade5648daf978d41527763786d3b5aa8 \ + --hash=sha256:2b25a0bffc822160a474912a0428d2e5a62b864de126703993f501dd6cb3e744 \ + --hash=sha256:317ead7936cedd18983476f6ac54bbc8114c9100faaf0666b26d57e9d867e817 \ + --hash=sha256:33424c686b291c7682b5816fe9320466dbc0a457ef7e15c273c804a2d70fea70 \ + --hash=sha256:37efa250f2e4b00ffae40dd097720985b795e7ab1ecb7586f691df8b62efa5b7 \ + --hash=sha256:418410e4ebbc9f9d67e8a70651734341a61342a9a319c44fc8781fb9a7710dbc \ + --hash=sha256:44b3ba82cee106083d9908eff08677a7f4a87bfd1eb606806f0d7423c8bc1017 \ + --hash=sha256:44c8c42c48e8d4df59af1425a8bd0a20e20fb34bd604d975acc634692b4ea393 \ + --hash=sha256:4dae19db3ac72227df0240dfc83d421ff9f8c397f32036e96988b6c30c2428bd \ + --hash=sha256:51bc27bf5feccc7d1646e17e46aa045859820dea76d95bb9d26bce09c96a25d6 \ + --hash=sha256:52c15cdb0f1ecbd4b91f8df767bed9a38bc32a6ffe5cb7148a534feb48b88a88 \ + --hash=sha256:57c5e32608ec39667a5942ed4db5bc7a32d1153010be1676c57a0e25a579573b \ + --hash=sha256:57e97c07f8e308786e04fec106ac7b3fbc5cdfdfe9dd3ae59ae3f7bab6818b5b \ + --hash=sha256:5c3b5370d871184cd94d9a613e8c54e303703fb6cf24ef11b36869c45eee2c09 \ + --hash=sha256:5cd9fea839097f51d553166f330193c29b48653cf5ddf41f11e568809f1ec489 \ + --hash=sha256:5d101a022ad2714bcf0188391b050905933287711cc2cb262f2ae9a6ad87aa69 \ + --hash=sha256:5fe9f2e2b081d286c54338840de0b5261416bde9b55034dc1a8545693c4ac5fb \ + --hash=sha256:62686f32cd696e74b371b4be3e6e53b558f1190722aaea35307e1f082b197200 \ + --hash=sha256:6299ea0b7227942e22407c1680e2bee22dd2e9425721a65e24b5606aad129b81 \ + --hash=sha256:6518f6e777b17e477ffbe8de59fdd991dfa43c6c6041bff60a6ece91cd83929f \ + --hash=sha256:67829c3e768da5c4020e1e4351f8b07595ede9bf4673aa4d9fa66496495b3b3a \ + --hash=sha256:694ef0c4f2492690ccb69b10ba4bf58a74bc0fbc685f30a54cbc403944ca7112 \ + --hash=sha256:6b7794a82757778af858ab90b8fa882271508cb1cdcd8c3b569c4cfe9481a433 \ + --hash=sha256:6c2b5feb4330f85c9187cd57275ab81f3712ce0a3f81172e3ab0ff0e68584b89 \ + --hash=sha256:6f1d74149fadce093319f90147ef29aec29584f7f9c5451cba636ef358a520a8 \ + --hash=sha256:757ae06a0e36af4fb9a5c70ca50d2a9aa9a381b4755ccf6dcd94795759bc9288 \ + --hash=sha256:75b0dcea993dd8631909f472ff6dec77a3942b9be5142a3785aedfb7c5a64c22 \ + --hash=sha256:78e3f110fa8acdd64d1989aa0ffca0de2b2b62f9654b24cb0596cc7b9b4ce85f \ + --hash=sha256:7e0fbcc8a02965350b96698af901ce03a087d0f33db2ddfe90f425d00eb1e4e1 \ + --hash=sha256:7f23feaaf1e13f02f8239dd1fa7452f814a5a6a09db6f49356b1a9d5b7104d8c \ + --hash=sha256:87a38a109be8d83964de6344f70c9b7e320f9ee30d6c5a0af1483baab7908070 \ + --hash=sha256:8c5adadfb66f50bb0aa599b673df3fdccb79a106d30e832d85863067a101c0ce \ + --hash=sha256:8ce6c3d777f34716814ccb25f502f621f5567cd82da87d9e8d0894a4177eeb63 \ + --hash=sha256:8ee200e70ef167178774b3bf9321140a1f5abab2a595665a6ef42f7d4e723ce3 \ + --hash=sha256:8f81dc215f7913dce61d5304083f9b28f62caedeea4c4889086c708798b25d1c \ + --hash=sha256:937443acfda4d5b53f257eeb08bf0bbbc01493a5c9561ad6c985e7bda5d0ec67 \ + --hash=sha256:937c93185f81bc2c2fe2522c364b21a25cec2269fd1d4f3059742e725b24723f \ + --hash=sha256:971145f200691df825a8f0897911825f0fdafb1f99329e6a7a1e5e66802e0c0b \ + --hash=sha256:978a5c2da6f7cd8e2b16a2f14e5583d8f71173284f68b0d90d583121f6cdf5e4 \ + --hash=sha256:98b50ec4b2bcfeebd490a389c86fd79932a853a05f7e29dd10a37e4b71297d6c \ + --hash=sha256:9cad8fbd9a1634205adccb91663354dc148fdc4f18a0ef033a2ccc6b3ab61d4d \ + --hash=sha256:9fd321898f8a65292553b9d76924fc4a48f183c7d27020f123b642cce200f04c \ + --hash=sha256:a19238e5b789a8893fd23256488c4fb8ba69dd9b2584d9c222597e03d60bb97a \ + --hash=sha256:a7ff972740c02b3abc89048f27b90bc875412df04d7432d5e7ae64486ad43315 \ + --hash=sha256:a8970304ba38cfd705953b262256287443cb3d5b07cb7996ab05c7d148d2b3b9 \ + --hash=sha256:aa92e2a72bf3ecdeea98ae1c66a9b9813f8f561f6964da799b0f65a41a2c5621 \ + --hash=sha256:abc74f7ba46f0763c7d890569d1602a59b6d029f5db65fa1510b72c8ccb8e937 \ + --hash=sha256:ae2bf80548ee9bf4457bd5d4573c3384a0012e5df6d51026b6a799dd7eeed495 \ + --hash=sha256:b065100e99267e56b8db82b0561800d13c4f779d4ea2baba463f1592b06d63d0 \ + --hash=sha256:b1a2a2127a2b944c40f75c5d26f20781dcfd0e314dbedce81421442ef16330b3 \ + --hash=sha256:b1e850674703280bde3ab3fca1ca413ed43decc98774c359ca3b00c1ff6cdea4 \ + --hash=sha256:b1ecb5d226f4c067847f039156d7f9bdaa9e60b2af179a968de745afa3095410 \ + --hash=sha256:b1f1127f6022bb2bd2449540efff8e3608c1af2bf2ff0b16c5fe20de2667b4ad \ + --hash=sha256:b1f8e32020f81ca1173cb39c8eeacb892aae58cda475bc42ed85f00c08791548 \ + --hash=sha256:b20ecaa3ecb2ccf4931a95d4750c166e901cf4e113f8e6bf27608e5c6c950ddd \ + --hash=sha256:b28034185577899b7bbfc90b46715212b0fa73073895a457aa231ded2adc85d3 \ + --hash=sha256:b33672007492fc7f1a4a5e566f01ccafaa4fd1d33f9b200028e46a2557c3fdc1 \ + --hash=sha256:b8195b3e1d25c7d4358dbb98191c91aa85309089155368de0bdca24ceca26e3c \ + --hash=sha256:bf2c3e26a62d75c7420dd0c3e3d7c69fc09e358cf309d5d655d5f171be6fb404 \ + --hash=sha256:c19d14b9c5a09db54ea3a312dd7868045133777efa88941d1fad6fb9f93d0cec \ + --hash=sha256:c43adf6fc6a051f9267550615bac6acdebdd9c3eab64debf0fb1e67e235f8814 \ + --hash=sha256:c65b122659fde35a05cf8d5cc3dfee2747b4d04d8c316074a878950a4374f0ce \ + --hash=sha256:cae5a7fdcf3a6c5b07064a18ec341ebcef47160b2a1bd5e319e550a237786589 \ + --hash=sha256:cc6a412b97f4eeb1609a06c143993b0bddef17bef23251b3a0c9f99a8ab5c5ef \ + --hash=sha256:d10f674d8f274f6a8090ea824bac53863ae9b904f6c25c2b3d21355a5b0af6ae \ + --hash=sha256:d73c0a87304d41045f6753a922113bede3ab09eda2d20371566a5bbe357c3deb \ + --hash=sha256:d830e6791fab8e0dfd283e19b8ffc67dcfb401a942d4498985d8c36a23403c72 \ + --hash=sha256:d9a945f01318de35ddb0a401b1281cb98de6abc4d866d133b36c0adf519bd5c7 \ + --hash=sha256:dad7fc38101ec6fe0ff4ac1e4f89e0c20ee532d4c042a134b5fe83a2cb93bc2e \ + --hash=sha256:de3fbfeef38f68b32c23ae954a83bbfc0c69189c480b045f91ae55e0f0ef9007 \ + --hash=sha256:dee576680e40f15b3ce930be55b1c3ad3284768b7312c6a4269e11f10a4978f9 \ + --hash=sha256:dee8562d868567c2ceb4f91652b653bf57633c232b3e2e4de75da53d0253d4d9 \ + --hash=sha256:ea66216cbe8264615e94812fce253be5c60b75575f74b89edaee0be376aba764 \ + --hash=sha256:eb0ee342ef35ea2965d84321dc38ac40aca71ca6c023f76d126f22520beeaa26 \ + --hash=sha256:ec39afdb6f4f294a2da5d75af42eaa89d73b7f25131149ed8e1211ef4ad5d3b7 \ + --hash=sha256:ed35a808ee4b1f9a9940ea3537044cf432f157f150bd4df659048168e430cbe9 \ + --hash=sha256:f035e889bc0c68568e3f69c5d9d932ec66b3d5d206d8d43d8a34234619ccb368 \ + --hash=sha256:f0c450749b8dab468b04ed25718e6e2ed352ac883891233b1c67c1310b9fe72a \ + --hash=sha256:f49fc4dd5625ddf5a122cff702b2d56b0032eba9ac93dcaf46e472bbc5a0474c \ + --hash=sha256:f508f72a10356af882bed7f19542cb47e13df57e3f08492ca78997aacc1d56f5 \ + --hash=sha256:f688d52ff682b8d2dfe8d1dfb6c4cb5ede4aee2f658036a9545a62b8abc804bc \ + --hash=sha256:f7b88cb32e3cd49dc50185da3be8c7d7c14abd5539acaaee0da6b7211d4d120f \ + --hash=sha256:fa324f8aa4e6f8a44c2b77c05d8f4296bf4ba430c5b7283f18c6878e18637f56 \ + --hash=sha256:fdb80a774cb0a440fcb62c9f64a64662c740c5bca985f78e70a3aa787264cc41 \ + --hash=sha256:fe624bb87ee53d9770bec087631d7fd8f01eab0128693b8fe6b884d8c2cf0989 + # via lmnr +boto3==1.43.114 \ + --hash=sha256:be704857751564a5cf69c5bbaadbfa01c22806409815c73563db42fbffe583a2 \ + --hash=sha256:d9cac2eb921ce674970cef1c9ad750f85ee3a846aedcf188d18368fb9eb6da23 + # via litellm +botocore==1.43.114 \ + --hash=sha256:d1c441a22e93e158de5b1e026205f5d6d67a4545d10540c5090c62dccb3a9eca \ + --hash=sha256:f366fa4db518775632ad1eb128cd8203ca46396cecf37209d904f0bbc049ce90 + # via + # boto3 + # s3transfer +browser-use==0.11.13 \ + --hash=sha256:c20d029f17c44add2047a72c836cb589b85e90a31a91cf3632a22a2de1928dfe \ + --hash=sha256:f5232309213715e66e8f2079fb7097ac79a880728735968e4c7d41031ed15e83 + # via openhands-tools +browser-use-sdk==3.10.0 \ + --hash=sha256:34871b7f6df0e90d5dd2c8d1f07cbd9298015e2a181c82fdf352a8db75d6946b \ + --hash=sha256:4a65df36f702fab6d90b0a5785bf8456a002abb5b504f856b1a8ab60251b9430 + # via browser-use +bubus==1.5.6 \ + --hash=sha256:1a5456f0a576e86613a7bd66e819891b677778320b6e291094e339b0d9df2e0d \ + --hash=sha256:254ae37cd9299941f5e9d6afb11f8e3ce069f83e5b9476f88c6b2e32912f237d + # via browser-use +cachetools==7.2.2 \ + --hash=sha256:39b6c9291adde28c5de6622d25329375fadf3e8a678226e7872e2e72854e4549 \ + --hash=sha256:d521dc98d501ba9efd8d2b1663bbf88163509c0c0a090eea6dc154b421ec5410 + # via + # openhands-tools + # py-key-value-aio +caio==0.12.9 \ + --hash=sha256:0a571981c8724f69c34ed4c7619585d552d27fe38bad0daf970dca3e14d6922b \ + --hash=sha256:0b9922e7d5800b9aadafbe8ba956ed095ac37f85e0b38b96c97586e443548ff5 \ + --hash=sha256:14483697a27aefd265decb4b595e70814d284f46bc352c75eb9e9dae94a7b398 \ + --hash=sha256:17ec3d3cb28878e4771445f73161677a964f833f091fc88634a7214013dd8ed5 \ + --hash=sha256:1c043b15a19e33c0b18b1937493be6730c3b08acc33af6899f669ac8957a3e46 \ + --hash=sha256:25255807af48386c50a2b186eaa582ffd5347d67c4f1cb3aa1bb79b36ace7af9 \ + --hash=sha256:2e152e1a1970d49b6056c1c94547801fcc6b41fc12185db6628ed58ec7785b04 \ + --hash=sha256:32755af4459fed70d10a5e8c396de3ccb57e02365c7d818937a9794884b172da \ + --hash=sha256:3bf16c16b9318498ee7669a495d2198c44abdc0bc64483a500682c4eb41d5b52 \ + --hash=sha256:477d66e16948845d0f5ec6535e84c0c9ab7420b2f78f81bcf7b0f81d18a34ffb \ + --hash=sha256:4a69de19ef8780ea67f5fffa6fed95af32ed4c036e338a361307314306ac816c \ + --hash=sha256:4edc95d8dc49f91fc9e870174860729fd088f9529464a7a6a1deda8a14d59a4f \ + --hash=sha256:53d3febc3e46707786a023b28bd21213ac78332db46fae7df2a03c0cc208976d \ + --hash=sha256:554c6ee883a0f1e95236d85b8e7d8187b212668855ac3a858acb80f5e2e82e82 \ + --hash=sha256:5c43eaebf220aeb9a388d309a5352e82562a7b9b2ea0102502204413641cfed7 \ + --hash=sha256:6634c57de5883819e0fb423094fe5cb480e81b8f5f46f601eb143e2c762f4c14 \ + --hash=sha256:6c28c4789a9c6d8e8b36cac15f85727bd8e4d312c98fc5aa4fceaa4631669786 \ + --hash=sha256:6e72fb0ddd369f712a4ad229ee0f1d7df6852e9b59b36fe8a46210b4c9ea8e82 \ + --hash=sha256:7490517a72f4ad01b39311ff8e909c3cab77f12dddd370ba6deb035313038468 \ + --hash=sha256:7fee93281b220488a5e6949ac197a52c2f1e877c6e32aec5ce658e9add3eba60 \ + --hash=sha256:83718f0ba9ff56de9c3ce7a61b463466fbb064be4f087230064abac5d08b8100 \ + --hash=sha256:98e88c20217456113ae3021882aa310fab8ae77e54ec62a7c990f1c34a9f62a6 \ + --hash=sha256:99e99419b44ab5511f7468c6a452887dd125b8e4042672a7589f0cf01d254ea8 \ + --hash=sha256:a06afa038f76324f439595b7a3242abf090a80b03006ed3d7dbbb9c031baf8a3 \ + --hash=sha256:a11421fb4ac591e6fea5512d9a8ad1b488e7b28cf610ede973bbfb4be5177454 \ + --hash=sha256:aa0fe6b459ef45d9d1fe82e22d5d849dcdb07e3a87f56444d1ff799249a08576 \ + --hash=sha256:b17b9aca6360f72bf07f1062bd60d8849f3fd5c1cb77f43923d8dec7fdbbb5df \ + --hash=sha256:b30f3f36e45afb0814fc828cbb10ef9dd91043ff0b0a14ffe1cd8a348b461179 \ + --hash=sha256:bc63db6b4a54b2f1c519424acfb2b7198664e4ef8427d397215a81c0bed9e9e6 \ + --hash=sha256:bf12d4f014b2a33e642ed7905b5787656ede2fc24be21f7c086826ff0d32cec3 \ + --hash=sha256:c0432568cdf7cd36868f5d9800dfbb840d0ffaf0e8f2df0b42fbcbf83f2353ab \ + --hash=sha256:c327977b8174337c1aaff27da17ac249d176ef8ea6e2dbf70b49cdd8038d57d3 \ + --hash=sha256:c3f6dc05486ce4e1027f1d2da4d84c1d6bb815886e76f34a9228d370d86a5537 \ + --hash=sha256:cc30e0c458d2d6785e72bb5117086ffce61b5c058bee5637c5aea69042db5e86 \ + --hash=sha256:d3d3664c757d59d330381666683cdfa287c5bfff90819868e98ef0bb8c2d2382 \ + --hash=sha256:dada2e1ca5481e5c11201d269ac8e009ce86d34478f4426e9739470b0c2a1030 \ + --hash=sha256:de4458707370b9f13de2ead07e6719305624b4f0aad665ce6cb2f1452eb4965d \ + --hash=sha256:e10e3be34fca464cc5e27d3011e284fd0cde0cc488cda4742e765c8e9291647d \ + --hash=sha256:e600822c774fdb434e43163c7fc0d4dcfaa6540ebbf77e82e5f0dac071b27f8f \ + --hash=sha256:ed23f6ad6897fae4c02e3e8d236aa3cc18e766a7c5bb0352ea5e9fb26c5d3335 \ + --hash=sha256:ed343077d8178c003b1596f40b1f46955be963a2871a1f85519be823113b24b0 \ + --hash=sha256:f0698976f84dd40024204f0f77cf59ed7446e989575c8ad8cb64cd4b3e2871f3 \ + --hash=sha256:f6ffb3d448016d20d8c40c53864d81bdf7463157969de65841eeb370519dbf78 + # via aiofile +cdp-use==1.4.5 \ + --hash=sha256:0da3a32df46336a03ff5a22bc6bc442cd7d2f2d50a118fd4856f29d37f6d26a0 \ + --hash=sha256:8f8e2435e3a20e4009d2974144192cf3c132f6c2971338e156198814d9b91ecb + # via browser-use +certifi==2026.7.22 \ + --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ + --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 + # via + # httpcore + # httpx + # requests +cffi==2.1.1 \ + --hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \ + --hash=sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66 \ + --hash=sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2 \ + --hash=sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0 \ + --hash=sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6 \ + --hash=sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971 \ + --hash=sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c \ + --hash=sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d \ + --hash=sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9 \ + --hash=sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517 \ + --hash=sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735 \ + --hash=sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80 \ + --hash=sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f \ + --hash=sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1 \ + --hash=sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29 \ + --hash=sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8 \ + --hash=sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c \ + --hash=sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e \ + --hash=sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48 \ + --hash=sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813 \ + --hash=sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac \ + --hash=sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632 \ + --hash=sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6 \ + --hash=sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1 \ + --hash=sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659 \ + --hash=sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688 \ + --hash=sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004 \ + --hash=sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0 \ + --hash=sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062 \ + --hash=sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779 \ + --hash=sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94 \ + --hash=sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50 \ + --hash=sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab \ + --hash=sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac \ + --hash=sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6 \ + --hash=sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676 \ + --hash=sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1 \ + --hash=sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9 \ + --hash=sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf \ + --hash=sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13 \ + --hash=sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e \ + --hash=sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e \ + --hash=sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973 \ + --hash=sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527 \ + --hash=sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72 \ + --hash=sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890 \ + --hash=sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c \ + --hash=sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990 \ + --hash=sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd \ + --hash=sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9 \ + --hash=sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94 \ + --hash=sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3 \ + --hash=sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80 \ + --hash=sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41 \ + --hash=sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5 \ + --hash=sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c \ + --hash=sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a \ + --hash=sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4 \ + --hash=sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e \ + --hash=sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6 \ + --hash=sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98 \ + --hash=sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b \ + --hash=sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1 \ + --hash=sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03 \ + --hash=sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af \ + --hash=sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231 \ + --hash=sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2 \ + --hash=sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3 \ + --hash=sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836 \ + --hash=sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5 \ + --hash=sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399 \ + --hash=sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96 \ + --hash=sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e \ + --hash=sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be \ + --hash=sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf \ + --hash=sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc \ + --hash=sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455 \ + --hash=sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0 \ + --hash=sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12 \ + --hash=sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b \ + --hash=sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7 \ + --hash=sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692 \ + --hash=sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54 \ + --hash=sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3 \ + --hash=sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b \ + --hash=sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be \ + --hash=sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d \ + --hash=sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358 \ + --hash=sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a \ + --hash=sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7 \ + --hash=sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc \ + --hash=sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960 \ + --hash=sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125 \ + --hash=sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb \ + --hash=sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a \ + --hash=sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa \ + --hash=sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf \ + --hash=sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3 \ + --hash=sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4 \ + --hash=sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264 + # via cryptography +charset-normalizer==3.5.2 \ + --hash=sha256:01077390b03f7988f11d700a2194e69b119741a86b1a638b1db88891e3eced8e \ + --hash=sha256:01b0c0d2262a9e28e8484a278c7e1b5d650e3ac8cf2683d2967e25899f208bdf \ + --hash=sha256:04851f73ae72b8413dddadb16a49dfee95263553741fd42d546f7d66907e6be5 \ + --hash=sha256:0521c5665880b33d603717defa76c094048900010897909952397feb3039da56 \ + --hash=sha256:0774bf9bf620249fee3e0b8b9fd3065de213be30f3aa94ce2494b3b638949e26 \ + --hash=sha256:0891b9d3903c5571c03771ca669a4b0ec5618ca722a5c957d3d29cd4e5062848 \ + --hash=sha256:0c951d5e6dd9c2ff60609476752bee49da4206adde960ebc247766937f72e718 \ + --hash=sha256:0fed1d06615f022ee3b13caf5e8b180cfea32bb2c5aded8a9d44277afc040f93 \ + --hash=sha256:114e4d0c92d618409ed82a99e22b5c5e768fe995f2973f78265f4524f49d4640 \ + --hash=sha256:11912e4bb14baae7c5d8791aa55ba0a3a03ec6729073307b0f57270abaa713d3 \ + --hash=sha256:11a4d68a6ecda3292cb1e50239e111543ba5d709bb62a6b4ea1afcfa729d8875 \ + --hash=sha256:124fbf1a8ff966d87ae05bb8bd45a71f966055ed8bba320d0c7cf450bc5f4d0e \ + --hash=sha256:1461ac396c4fdb983a675f20aa555624f0ee18ac83d832b9244ffff3d8055275 \ + --hash=sha256:1503bccbeb36d5527790c3930327704c39af22de3112f1b1666a9f3ce15ee204 \ + --hash=sha256:15bb4005af6320d259dc7593ca84a38d7fe06a421dbcf7b910ae23979101e787 \ + --hash=sha256:15c44f7edfd477b06f517a5cc317fc1707edb9de2c865f43d4b6513907473234 \ + --hash=sha256:16fa0eccf81304b79c5cd87f9271c3b85dd9dd99245e4422ae9c0dd45e0f99d3 \ + --hash=sha256:183b88127acdb4fabe59d951ab424faf1af7b63cdbb5f776186c1ea2ffcaed98 \ + --hash=sha256:195c26fb65950f8fce54e26349852b7bdd7c5f120aeefbcc440b8a20faaed4a3 \ + --hash=sha256:1afb975bd5d68d5ce9f6b6d44fdf2f7e34b895a35e95708a7a91b20a3b51d187 \ + --hash=sha256:1b4cbc7c3491ccb4aa17fcd8165649d01cf39f76de1696da8631b5f71b85401d \ + --hash=sha256:1bc0baf5ef96b6ede57d47f4b8fe4d9d84019c3bfcbeb20a41edc6a6ee341f1f \ + --hash=sha256:1c50fe28bbc2ced33386f298650d91218076c05420e6cbd790b913adc41659e7 \ + --hash=sha256:1db38f4c5496827c1a501846d64d14c3b80c7e6714e406cd7dc36a9899fa1011 \ + --hash=sha256:211d5a3eb6af8f513b8d4ca19a8c1b7accab1b5f0d3175f9826b03c1a920dc1f \ + --hash=sha256:23851fb4e1b85ed3f6c2a27b777cdfe2e19fb5b38429a8faf38c7542b7665869 \ + --hash=sha256:254eb48b9fa5ee9898a3c445825a1f340fe53712a098904b39b0bddba8ea3cb1 \ + --hash=sha256:2625388c6c754520c37abaf3b41eb34d1cc4a373f457898f08606c8e362b891d \ + --hash=sha256:281cb91036248400f4cc957495cccd44c275c2e0c5854f7e45ac5cf7dc193847 \ + --hash=sha256:28a15fdad492a99b6eccfaaed66ef3f74050680545ea61ec8b2f4c538f1f1320 \ + --hash=sha256:28b4f0d66fb834ff90f28209ac7bce77868c45d8c93e26f906709d9b7c2e1af9 \ + --hash=sha256:2a925889534b3748302dae5dead07cc13480de1dac3aea80a941b729b471ef93 \ + --hash=sha256:2b7b3bbfb4fe8ef40600792d762fbaa9057559f9d3fad209525b7a22b99e91fd \ + --hash=sha256:2c9ad19a6cfcd5ea5c0d41161d22f9df1dcc277e9bef2751391334546a314c00 \ + --hash=sha256:2cc961b171b3f3440f410489ab3573e86aea8736134ebbb40ea1338b7f0831bc \ + --hash=sha256:2ce45c6627b22c47e390bc91a41c3d13032192e699fa0bea96e9671b373d69b0 \ + --hash=sha256:2e06a3a98f916dd41d27f3105e02e7a40181c98c94b9158733d03a6f80506c09 \ + --hash=sha256:304d5463e65a35d7bb0850550e0780395395f6fcf452f04db7d5ca7cecc425ac \ + --hash=sha256:304d8e4d493af723536393eee0c689eb7813f4a474c8b479dee63f1fdd98f621 \ + --hash=sha256:30fcd120b732aa79317f08dee04d7de0847822e4cf7ee0e9f445bb958832252c \ + --hash=sha256:31f3930700408d211f13378ccbe1c40845d8da54bd0681fac3a9b5aae81c7aa8 \ + --hash=sha256:34276fd796040bf0993ab33a369aa572e6979c7aab225a88893667ad8eac8f7a \ + --hash=sha256:355ad8011081dec5412240c087a9a0c9d4d5039f3ed11a3f13e18c2b29b56c51 \ + --hash=sha256:38a873987f3be698494da8b2e3085e29da02da7b633dce73e79c699a113d7bf0 \ + --hash=sha256:39de2a259fc954455c57274dc94c79d5842774e1247a016aff30bc0efed0f4ef \ + --hash=sha256:3d14b50de6bf4d0edf857a9386836846f982b8f524e188e2e68b96d702bcf4aa \ + --hash=sha256:3d21b8b13c7592db2ac5e544a6d83187b995257472b0c9e8351b6d507ae37ed6 \ + --hash=sha256:3d31298449090ab8d47b7b1b2a555ff73cac7ed438a08b7ac160980c7ebed649 \ + --hash=sha256:3ddacd27458c45bdacd6bd6db644bfb730efbf9e830310186e3045c9c5be8fb2 \ + --hash=sha256:3df041de8887954562c9b261cba85ca0e9ded74048daf125f45edcfaa4832229 \ + --hash=sha256:40ab6bffa02ae10a0581e6c198be7d2d8ca5c2a0c64e4ed3465d766df457573e \ + --hash=sha256:4275811936e2f06feff5e598fb42a1b7ae852da8e39605211892b56b81a34efd \ + --hash=sha256:443eae2bf318abeaf6f15d785138f71fd6de770e99a92158b8b814265e079115 \ + --hash=sha256:447441e76ec720b15e64418d32e092297340387053047c7c694f579efb0ee1d9 \ + --hash=sha256:4495c5002a7b28557e7e222e77e0b661183e432b7d6d2e788101e3f240e05b8c \ + --hash=sha256:44bd4fbb29dfbeba60e7d2bd000c59e4b21ddb3cc53912b14048d37092706d7c \ + --hash=sha256:4685902cf26edf013ed7a3da0f426ebba7a00ebb9541386d835afbf002c11cab \ + --hash=sha256:498dc3188ca05a68231ac3fdbfc7f57eb67e1343c30e0fea17f8218c1599b253 \ + --hash=sha256:4c2b5031f63e331e3839b40aed2dd6f191e9c07edbde303e7876846ea1946995 \ + --hash=sha256:4d48f2d08b9de5864e2c8744d4461b862fb149a18274abc8b698c45975573438 \ + --hash=sha256:4f87960d57feabfb618e4e0af6e7371645fa26a277860739d6e5d6e0012c92f0 \ + --hash=sha256:50e3adfb96fc189eb27b1cf62d3b598b89b4bb0420d93a3d3e42e137409011be \ + --hash=sha256:51cf45226a9b588d0d2b4880c62d686934b63ab0bd79ca23ab0e9762eb27441b \ + --hash=sha256:52aa6992700996af31f375de0c6bacd402b0097fe40b53c426b9f51a90ebabc7 \ + --hash=sha256:55ea99acb17b9325618de155a0cd6a2e8f5d10be008113e1d433bbb58db543b2 \ + --hash=sha256:56bc200a365efb37383b7852e4cc5898d3b2da5987289b543956cf8cad71018a \ + --hash=sha256:588461c2e8384d309bd63e5826019b6977bc66d629b99ac8737bb795d7b2cb5a \ + --hash=sha256:58ca3755ee7ff7f59b57789ec9833c9de9ea275405cdd240eda1f193112e398a \ + --hash=sha256:58f361dcbab699cf8f42db3f47c8e7fd1036f138c23a5d08de9fde5f425a730c \ + --hash=sha256:598a11a2c7ebaa5334bf698bf29568c9c390abac6a154d8170fedecd1cea38c5 \ + --hash=sha256:59f63901b0031c3136cf64704dcb21de0bbae62ce2c9529bc39d27665463de37 \ + --hash=sha256:5cde776b7cc66e4f6c99612cea4aa7269aa65863f7a15841b2c264f103822f4e \ + --hash=sha256:5e2b6b57e9733d39f0c9fd3185efa6b8e29652c4cd8fe94180272cf6ed9a78c4 \ + --hash=sha256:5fb29fb8cd1a46c27a1bf9613ad5ec2599310d46b4025d9556404a6b6a292800 \ + --hash=sha256:6045373d5a89a5ec71afde535db987ca28e76dfa276c2d4c818265b375d4b055 \ + --hash=sha256:619799369eeef6366ed3e8755a5670f4f2f0fb6b30a0fd7264dc0fdc2357058e \ + --hash=sha256:62588a277bfb59def052abd940703fa35107152bf479781a878617d60faf8fb5 \ + --hash=sha256:62603db9a7caa0802eaa28c1c46fecd7b3a263a774069c24c3c28c302448721c \ + --hash=sha256:65cd72beeeca9d3aaea1201e5923859f308f952f9c71de93f06063c79f0f7a3b \ + --hash=sha256:68eb192d85ab8e5f6ec69c2bc6ac0179fbf04a5ac1569d12fbef74883fe102d0 \ + --hash=sha256:6bd128f206a7752ae1f2ab6c61bf8a24ba28913a10df8b14c2637b973ff97a80 \ + --hash=sha256:6be488a102b8cf28d0391d8c4ba7748938ae28b78ad901f8585520fca33ead1a \ + --hash=sha256:7218e8f32b0956cfcd048fd42d9d5779809745ca1d86113ca56f66e7ae1549c4 \ + --hash=sha256:7441d755b7ab94f8d4eb3e43ec05482d760842fd263d003a99102d742cd835e2 \ + --hash=sha256:749e97e1b32313717a565abbe321bc2190bc8b35f1a67e4cdbc7c56c8d8ffe58 \ + --hash=sha256:75a3ceed0724d625d64b86ca20aba182e4df462e04c2414fc941c0f523f06aac \ + --hash=sha256:780fbe7cab297b81dad9fb8dc5eb003c0468ffb0d9e5f65068c53a34661a96bc \ + --hash=sha256:78456a747de8dc58360ffa581f30a002baf5aa28cb262536545e91f113ed7639 \ + --hash=sha256:7967d08cf06dee78443b874f98c98036f624f3a4e73e11f9f64f5be4d25393cf \ + --hash=sha256:7a881931aa470808df94a8c380eed2bbbc76cd9dc622310f99665658c821eb6d \ + --hash=sha256:7dcd882da75ef9adf94903b1e3b9419e8aa8fb4c7396822b834b9ef7fb96954f \ + --hash=sha256:7e841fb9010836c992c9f12fcbd43a831de93a5f726fc1ccd8ca1d0268c5014c \ + --hash=sha256:7fdde2c9fd9e3eca40631e024664cf2584272cc8f96308cbe5fdfc930f51d8bc \ + --hash=sha256:8024d00c3faf3fc0c16e07a69f4405e8eac7cc0ab15f65fe6cf43827c4cf72b4 \ + --hash=sha256:80d02b6f04e92601a081dd97b23d3128033098bff5d35d392ddcc0476ea11253 \ + --hash=sha256:838dcc90063569a0448120554591a1d6c4a4ffe11babf048908793154ab86ade \ + --hash=sha256:849df64e889b2e17230d58410a03dba311a65b163508fd33679b2b737d4b7858 \ + --hash=sha256:87475fabc8d9996fd9c27debb395e642e8c838d78a00b6e932227a0e06b81e26 \ + --hash=sha256:87e50a3e7cb90af586b6c5faf23e302a970415ac73bd7bd90a515a04b427ef96 \ + --hash=sha256:89b53f3cda69831909888e0494f4fa0bcd3537e3e138dabeb620bd6ad946bae8 \ + --hash=sha256:8a893cc101149f80a653f82062ebc95b34525a2614382e1da5458fe7c6997249 \ + --hash=sha256:8b2bfab86aa71ae13aa41a6a26aab338e0db2b8bc75434b05aea89e011ff35a4 \ + --hash=sha256:8d86d6fc60743dc916eb79e2eb1ec4818e21e427731543af40a3021851174a13 \ + --hash=sha256:915563965d418f986e7e145accc592eae9e1a1be3566ff98a05d7a9ec42a76e1 \ + --hash=sha256:92888bb3187c5ba50500b00b3b310c9f2c651709d28036077680cb5255450a03 \ + --hash=sha256:93223adc95033dd47133a46ccfc316a0139176fd79085762e27202ec56018f03 \ + --hash=sha256:9373ad13ef0d2c0fb761e04e55bfdee5a08b52cef2c882c8fbe9935b1517152e \ + --hash=sha256:9409a8bf35cf78353942504b24a57de3d75b708997a1e4bd8db71ac8633ce364 \ + --hash=sha256:9b7f416ff0978e2f2249330527f0ad6fa02f4932e6199692d3b52da2048c19e4 \ + --hash=sha256:9bde855991b7e362c146535e3136a50bfaffc0487d38b33ca7e5edefc6e23849 \ + --hash=sha256:9cae88599c7219005d879f98e5ed53341e9a122af585e1091200358a3003d2a0 \ + --hash=sha256:9cf9b1a857e25c4baceeb3624e92a56df3668f398c4acba74e174d81fb4d1d3a \ + --hash=sha256:9f56f72050826f63dcee7a7f55b0a77168cb3bfc553fd405e7f8f9ece75a4036 \ + --hash=sha256:a090bb2c68df85450502e3e20d665e3a5af9c65a84d6508ed477badd49166fd3 \ + --hash=sha256:a192e2c40070d92c3ccf777e3a5c4ff515573cd2bb7ed0c537fdadbbec5bbf21 \ + --hash=sha256:a19a731138fc27d5682277d3b9df22855cea1239bce7fcec5f78f42ef2d1f3c3 \ + --hash=sha256:a66c3bc5ab1f0ff2164fc9965ddd611ff0802173f4b9d24554c563f6ab7e1d6e \ + --hash=sha256:a815775b6c38d4e0ff7bcffbeba67feded90202bb6a226b8dd35f1c855217413 \ + --hash=sha256:a89012d6d5476ee112d20d998570ed58df2260a852afb1758809cd6900411d21 \ + --hash=sha256:ae4f5fea5b8b8ccff88238cc8569303e5ee95efae67fa62922a311397a71f346 \ + --hash=sha256:b6856554c4f44d79fc2307d5768854310a8f0096e501c75637542c82292b0429 \ + --hash=sha256:b6b751274acb69d77b3323d6b7dbaa3c7fdfc1eb829b7eb61d262f32e1af9685 \ + --hash=sha256:b736353c0a625bbd5fcec108576e2385db3496f4f771f785ff32e108d3c3bc45 \ + --hash=sha256:b7fd005a73d9e657273b7a10dc71a9e03c8fb9ee6999798d6918ce095b81ac7f \ + --hash=sha256:b91363207bd9dc966a691e959bb47f64b30f7ac4b072be9968b366982f7db77c \ + --hash=sha256:ba0b1d2620edf869789c3879223f52bf2afc5d31b3cb47cc57b3a12c05e2aa9d \ + --hash=sha256:bbbfc8e28816f19d7c0f1816664980c0a9875d01b27cdf8eedddb639d9e108ad \ + --hash=sha256:bd16aabe4a02a297c23417aa17ac6299dbd8c49f673bcd645b4929b11f5a4400 \ + --hash=sha256:c0afc6800ba57ccc350374c5bd6150419915d95ce93cdbab2d783d75eaf30ecb \ + --hash=sha256:c6708715abcf3c73b99508253e961a9967f02fe536532834149574eda6de0d1c \ + --hash=sha256:c7c9ab723cde841fefb34efbad91e87f00a674b1fe1cd0784fde742bf2c154dc \ + --hash=sha256:c8f3d67aeaf55f017982b73683f0e7342ba2f6635a78f69ce89ebb26aa411e5c \ + --hash=sha256:c9790464842f85f437dbbb54417eda1e0e6bfc52dd8d22d6fd1c994b73b2dc74 \ + --hash=sha256:ca403d7e4798f525fdfc78e258820419cbbd0f0ecbab9de7840e3c017cf6b8cf \ + --hash=sha256:d008d90a7f2471519aef0c90dfbe73b3e6e4d5e66ac48e19154c17e89e98b604 \ + --hash=sha256:d19fbd981a488e22cd04883659ca6b08f50b5974f9fd7c95655ef6a043e5893f \ + --hash=sha256:d1befeed746d247c81127bb14de9dc3d30edb6e5976d34f83f86ed262b1d9105 \ + --hash=sha256:d2374b62878abb00cd8309b32af6c0b715cd02dec0ca74ef12e5069bdc64144a \ + --hash=sha256:d376bbd28b3a8999db1a103b3b388aee6f1ddeb3e51bc2172993efdcd86e064d \ + --hash=sha256:d4a7319f304a774bed22115bc891618e45f85065ab44ea6acd07d274e750519a \ + --hash=sha256:d6734d2ef8a50fbf8445c139477da401f50d62a0606bf00e20ec6d87773fefb1 \ + --hash=sha256:d760fe2a4d7c3b226cb9026d6a842868d52a7901bd98420e1baf14e80da85cf5 \ + --hash=sha256:d913de495d90407cd859d263bee2e5d1a4ed3eb6573c04e70d9ec619a7cbed7f \ + --hash=sha256:db19d07e2e0129e974a0e65d0064fc222a446cd5122c2fd4184d2af9fc734a9e \ + --hash=sha256:dca9ab98072a5a54ebacebdc45f53e645336b320c667410b061be1ca588ae709 \ + --hash=sha256:ddc7dacc8ece3a182e7f15cb862d1fd616b46d076cb1ae9dd232b2c38b655874 \ + --hash=sha256:ddf19c062bea7a0cc80f519243d2c01dd091be0cf952a0750d4ad576709559f5 \ + --hash=sha256:def79fa35ef0cef8d2accec024f4fdc7ead3012ff02f5215c783f39f03ef8cfc \ + --hash=sha256:df29a0a7107f7011e77f4eebdddec4c7331e24d787a0b21a46d63bdf7445da95 \ + --hash=sha256:e09a3942ecbdee5cce73ea9d42da82b81b72ac1bf031ce069b93b5adf4eac8cd \ + --hash=sha256:e242bb1c5e76e97dfa9e7f209a71e93a01d7f19ffdd5cfbb2e2d55b4f08f8ab0 \ + --hash=sha256:e243bd13217235fc7290c621941c3f5cc8b66e4872495be821d7436ba2fb838d \ + --hash=sha256:e2af3aad578aa6bd1384bcf4750fc285e5a9de53f40b7d41e5a0bf748edeb2b3 \ + --hash=sha256:e4e81e09c1578b8df602e3db08b0b3ea0a6947ad612f52bf8dc5ea8d47691f0c \ + --hash=sha256:e54da4baf05720032d527874d40b65fa4d7e5c6c6a43d0c3adbeffcaf275a2b3 \ + --hash=sha256:e80e6c2f55656b4824d72065abb4ddd6a525c74bd78a0aab5d9fc2cf4fb5af50 \ + --hash=sha256:ed2a239c0ea213acc1908150a3037257083c7c083128f1a4cec2ec4b97dca491 \ + --hash=sha256:ed905975ab14056a2e5eb1c376cb2e1ebc5396baf84163939c518556fccde9f5 \ + --hash=sha256:ee21e28f0430bd6dc9086c6e525d5e818a44a5ad19720c8a0ef766792f3eb5e5 \ + --hash=sha256:ee43c17b173d46a3212baa6ead3ae258eeabdae48c263a01ccf0218c366dd655 \ + --hash=sha256:ef4fcbf3327382cd4c9f540babd61248208af7b93eec4de397b4d5f58a09e288 \ + --hash=sha256:eff0ac9dbe711a4aee69bf04a83896aa9b85f19641264053a9f6d48573abb7dd \ + --hash=sha256:f0aa869112ef88429ae17820d99c3dd9504c9e9c671d3c246f3d7442cb051084 \ + --hash=sha256:f3c96f633825733f735c5a9cf21d21a257d8e1edf0b1cee0a064b9c424ca0f7d \ + --hash=sha256:f5833ad231be5eb6553de524a70f48d71b2c8563101750531e0b80184e175cd4 \ + --hash=sha256:f5ec61164adcec446f8969a3358ec3f9b26bbda3b9213e5586d219afa8df2915 \ + --hash=sha256:f7d486c83842422badd511868fd8a9a20e9407ace71564b6af47ce7e60a336c1 \ + --hash=sha256:fb9e68df06293761f9fe66ade60a9bc6d0f5e42b8acf2939a9158af86ab0e5bd \ + --hash=sha256:fc14a032f813bf5fe624d991960ea83e9715adc27e4c1830a2361eb1d02ac341 \ + --hash=sha256:fcff63213e8e6e47770541a4607175404f47cbb3ebea7b6058cc82d524a0e424 \ + --hash=sha256:fd1fbe0f116b6e55da77aca2c6ddcddcfac2186cbf78bdebf40fc156efca389d \ + --hash=sha256:fe9753dfee015c570d73df76f899f18444d41388bffcde097deba51c4fadbb9f + # via + # reportlab + # requests +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 + # via + # browser-use + # huggingface-hub + # litellm + # uvicorn +cloudpickle==3.1.2 \ + --hash=sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414 \ + --hash=sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a + # via browser-use +cryptography==50.0.2 \ + --hash=sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602 \ + --hash=sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2 \ + --hash=sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047 \ + --hash=sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c \ + --hash=sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42 \ + --hash=sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18 \ + --hash=sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51 \ + --hash=sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81 \ + --hash=sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856 \ + --hash=sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2 \ + --hash=sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de \ + --hash=sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7 \ + --hash=sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd \ + --hash=sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2 \ + --hash=sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be \ + --hash=sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45 \ + --hash=sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0 \ + --hash=sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e \ + --hash=sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c \ + --hash=sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5 \ + --hash=sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452 \ + --hash=sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48 \ + --hash=sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05 \ + --hash=sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1 \ + --hash=sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93 \ + --hash=sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04 \ + --hash=sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e \ + --hash=sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67 \ + --hash=sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7 \ + --hash=sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107 \ + --hash=sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079 \ + --hash=sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134 \ + --hash=sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227 \ + --hash=sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1 \ + --hash=sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539 \ + --hash=sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e \ + --hash=sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d \ + --hash=sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c \ + --hash=sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd \ + --hash=sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020 \ + --hash=sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd \ + --hash=sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94 \ + --hash=sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a \ + --hash=sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408 \ + --hash=sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37 \ + --hash=sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e \ + --hash=sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454 \ + --hash=sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c \ + --hash=sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc \ + --hash=sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37 \ + --hash=sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767 \ + --hash=sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a \ + --hash=sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5 \ + --hash=sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc \ + --hash=sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67 \ + --hash=sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8 \ + --hash=sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480 \ + --hash=sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb \ + --hash=sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b + # via + # authlib + # google-auth + # joserfc + # pyjwt + # secretstorage +cyclopts==5.2.0 \ + --hash=sha256:5da2a5d65164e03008621fc4795946b88a722b881398b5dfa58afa6218342cff \ + --hash=sha256:b63c1b1beaadf3ead19214385a0f90b990f152c4107c174e1724c45dc71e9541 + # via fastmcp-slim +deprecation==2.1.0 \ + --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ + --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a + # via openhands-sdk +distro==1.9.0 \ + --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ + --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 + # via + # anthropic + # google-genai + # groq + # openai + # posthog +dnspython==2.9.0 \ + --hash=sha256:9a4aedb833c3c1b49214d04d44d3032ab7a9135f7c1d29a549b4ff78fd82fda9 \ + --hash=sha256:b44dc6b18f07a8b1c56676a19fbfdb5209415b046a9cece286baafa87ff3f7f1 + # via email-validator +docker==7.2.0 \ + --hash=sha256:a3f45fdeb9165e2d25d9a1d02ddf3bc70fb572cf5ebbf9b58558c22caf29b71f \ + --hash=sha256:cebb93773d334f778e023a7ee352a8d6e13ab1bd3b863a4d4a59dec897df43ac + # via openhands-agent-server +docstring-parser==0.18.0 \ + --hash=sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015 \ + --hash=sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b + # via + # anthropic + # cyclopts +email-validator==2.3.0 \ + --hash=sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4 \ + --hash=sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426 + # via pydantic +exceptiongroup==1.3.1 \ + --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ + --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 + # via fastmcp-slim +fakeredis==2.40.0 \ + --hash=sha256:16eb05a3e97c37a033c73d1da7e885eb2aa47ba7604cc377144339efa2780a02 \ + --hash=sha256:b155ef2442134372eb1cc5664cf5638ccbe0a6dde9d1942153708e2782f315c9 + # via openhands-sdk +fastapi==0.143.3 \ + --hash=sha256:75e8bd68e2f52f77d29b2879749c257d64de2d85282d25270caf2bfccce5efe5 \ + --hash=sha256:b9ab567ee57afb4454004ce04c889f8f0160ac47946b99795887cd6e88484848 + # via openhands-agent-server +fastmcp==3.4.8 \ + --hash=sha256:46012c42b84e054f159c90da7b7587cc320a60538ebcd1466a5475432776361d \ + --hash=sha256:ab4e8abe6c9d50b110f367e8c29f82b90c1478ac9180be61af10354f84f0ad8c + # via openhands-sdk +fastmcp-slim==3.4.8 \ + --hash=sha256:dbd165ba40cb3267ce8a4385b5ea43231dbda19a7b80da1e04d95608ef5152e0 \ + --hash=sha256:e72c6b74e90c5dcdc8f58087fbdf213636d8b5d339384a498de1c5df6fc037a7 + # via fastmcp +fastuuid==0.14.0 \ + --hash=sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1 \ + --hash=sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede \ + --hash=sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11 \ + --hash=sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995 \ + --hash=sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc \ + --hash=sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796 \ + --hash=sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed \ + --hash=sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7 \ + --hash=sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab \ + --hash=sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b \ + --hash=sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00 \ + --hash=sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26 \ + --hash=sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4 \ + --hash=sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219 \ + --hash=sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75 \ + --hash=sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714 \ + --hash=sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b \ + --hash=sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94 \ + --hash=sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36 \ + --hash=sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346 \ + --hash=sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4 \ + --hash=sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8 \ + --hash=sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3 \ + --hash=sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87 \ + --hash=sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4 \ + --hash=sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8 \ + --hash=sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3 \ + --hash=sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea \ + --hash=sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6 \ + --hash=sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722 \ + --hash=sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a \ + --hash=sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0 \ + --hash=sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85 \ + --hash=sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34 \ + --hash=sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021 \ + --hash=sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a \ + --hash=sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d \ + --hash=sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a \ + --hash=sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09 \ + --hash=sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8 \ + --hash=sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c \ + --hash=sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176 \ + --hash=sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4 \ + --hash=sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc \ + --hash=sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad \ + --hash=sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24 \ + --hash=sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f \ + --hash=sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f \ + --hash=sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f \ + --hash=sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741 \ + --hash=sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5 \ + --hash=sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4 \ + --hash=sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209 \ + --hash=sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470 \ + --hash=sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad \ + --hash=sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057 \ + --hash=sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8 \ + --hash=sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe \ + --hash=sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73 \ + --hash=sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836 \ + --hash=sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8 \ + --hash=sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779 \ + --hash=sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b \ + --hash=sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d \ + --hash=sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022 \ + --hash=sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7 \ + --hash=sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070 \ + --hash=sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105 \ + --hash=sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173 \ + --hash=sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397 \ + --hash=sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505 \ + --hash=sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a \ + --hash=sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06 \ + --hash=sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa \ + --hash=sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06 \ + --hash=sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8 \ + --hash=sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad \ + --hash=sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d + # via litellm +filelock==3.32.7 \ + --hash=sha256:37b8a3d9811b0f9aef7e5ec5c71bb320de52df51e6ca9bcd6f5ad81187660da7 \ + --hash=sha256:65ff0d0190ea42038b32bda4b77834fb05be2cad4c5b9b01aa4dfb3614536e52 + # via + # huggingface-hub + # litellm + # openhands-sdk + # openhands-tools +frozenlist==1.8.0 \ + --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ + --hash=sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0 \ + --hash=sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121 \ + --hash=sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd \ + --hash=sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7 \ + --hash=sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c \ + --hash=sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84 \ + --hash=sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d \ + --hash=sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b \ + --hash=sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79 \ + --hash=sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967 \ + --hash=sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f \ + --hash=sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4 \ + --hash=sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7 \ + --hash=sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef \ + --hash=sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9 \ + --hash=sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3 \ + --hash=sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd \ + --hash=sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087 \ + --hash=sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068 \ + --hash=sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7 \ + --hash=sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed \ + --hash=sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b \ + --hash=sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f \ + --hash=sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25 \ + --hash=sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe \ + --hash=sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143 \ + --hash=sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e \ + --hash=sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930 \ + --hash=sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37 \ + --hash=sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128 \ + --hash=sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2 \ + --hash=sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675 \ + --hash=sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f \ + --hash=sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746 \ + --hash=sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df \ + --hash=sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8 \ + --hash=sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c \ + --hash=sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0 \ + --hash=sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad \ + --hash=sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82 \ + --hash=sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29 \ + --hash=sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c \ + --hash=sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30 \ + --hash=sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf \ + --hash=sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62 \ + --hash=sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5 \ + --hash=sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383 \ + --hash=sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c \ + --hash=sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52 \ + --hash=sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d \ + --hash=sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1 \ + --hash=sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a \ + --hash=sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714 \ + --hash=sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65 \ + --hash=sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95 \ + --hash=sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1 \ + --hash=sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506 \ + --hash=sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888 \ + --hash=sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6 \ + --hash=sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41 \ + --hash=sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459 \ + --hash=sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a \ + --hash=sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608 \ + --hash=sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa \ + --hash=sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8 \ + --hash=sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1 \ + --hash=sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186 \ + --hash=sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6 \ + --hash=sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed \ + --hash=sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e \ + --hash=sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52 \ + --hash=sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231 \ + --hash=sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450 \ + --hash=sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496 \ + --hash=sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a \ + --hash=sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3 \ + --hash=sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24 \ + --hash=sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178 \ + --hash=sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695 \ + --hash=sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7 \ + --hash=sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4 \ + --hash=sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e \ + --hash=sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e \ + --hash=sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61 \ + --hash=sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca \ + --hash=sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad \ + --hash=sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b \ + --hash=sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a \ + --hash=sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8 \ + --hash=sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51 \ + --hash=sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011 \ + --hash=sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8 \ + --hash=sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103 \ + --hash=sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b \ + --hash=sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda \ + --hash=sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806 \ + --hash=sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042 \ + --hash=sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e \ + --hash=sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b \ + --hash=sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef \ + --hash=sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d \ + --hash=sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567 \ + --hash=sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a \ + --hash=sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2 \ + --hash=sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0 \ + --hash=sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e \ + --hash=sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b \ + --hash=sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d \ + --hash=sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a \ + --hash=sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52 \ + --hash=sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47 \ + --hash=sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1 \ + --hash=sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94 \ + --hash=sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f \ + --hash=sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff \ + --hash=sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822 \ + --hash=sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a \ + --hash=sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11 \ + --hash=sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581 \ + --hash=sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51 \ + --hash=sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565 \ + --hash=sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40 \ + --hash=sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92 \ + --hash=sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2 \ + --hash=sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5 \ + --hash=sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4 \ + --hash=sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93 \ + --hash=sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027 \ + --hash=sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd + # via + # aiohttp + # aiosignal +fsspec==2026.9.0 \ + --hash=sha256:0f08147951c8cb31d844c3547d631053b127863b60be04cf06e121333ee0e2fe \ + --hash=sha256:8dd6e646e99ea382bd85f97a45e6b526a442d79423a7dc673f1e2756d05fcb5f + # via huggingface-hub +func-timeout==4.3.5 \ + --hash=sha256:74cd3c428ec94f4edfba81f9b2f14904846d5ffccc27c92433b8b5939b5575dd + # via openhands-tools +google-api-core==2.42.0 \ + --hash=sha256:82cf5daa2ef1b456d4e29ff1de1a5c2995c7be3ccf4fc608184326e03390c1ee \ + --hash=sha256:b1bdf4f72dc4f910736ce4ba49038352effbbc309579215107649b22973a1317 + # via + # browser-use + # google-api-python-client +google-api-python-client==2.201.0 \ + --hash=sha256:2d9bf1ba3f12eee8ed3d0f1791ce0605d163432f496baa72d3677faa2cf097d6 \ + --hash=sha256:d5691982abd7287f53cb0b0e0c6a9984d4103cf864ea0a88cb6e4347bbaf70de + # via browser-use +google-auth==2.62.0 \ + --hash=sha256:0bef0ce54bdf9ce226c5d66e4264413bd918141c31bbe49fb52eac882f513d69 \ + --hash=sha256:4ff4319aeb4ad128409759d397a9fcafad126d0031d241cc0dd6b9a00b43e3f3 + # via + # browser-use + # google-api-core + # google-api-python-client + # google-auth-httplib2 + # google-auth-oauthlib + # google-genai +google-auth-httplib2==0.4.4 \ + --hash=sha256:b931de392c20cfaa351cd789274922bd8cdc001e0e9e96de31b39d71347f8e16 \ + --hash=sha256:bbe5d7b2401bb3a4017f4720e1e91bd273ab9a2bb60b84e65edbc0de127852da + # via google-api-python-client +google-auth-oauthlib==1.5.0 \ + --hash=sha256:71625fdea21c6c03217eb9ff13741c3096e6d257ebca0ad084dbd6e9f4aa4ef4 \ + --hash=sha256:b351107c7dd9017f426cbb0272ea1bc04f469020fd18f4443c7d40362e0b1510 + # via browser-use +google-genai==1.75.0 \ + --hash=sha256:56bac3991b311c93f980c0a2abcd287b672146905df1fbd71c92ed633d5a07cf \ + --hash=sha256:8dc4c096e7d6288c3087f6893f582fe52468932464781edb8193bd92b9fefb2c + # via browser-use +googleapis-common-protos==1.75.5 \ + --hash=sha256:c7a866fc34ed29a3b10af627a4b9b1dc2433313ca6e959f0ae4feb132047ed72 \ + --hash=sha256:d7285525c23039db98f2463e6d5a4f9b958b94d497f03a844ece3259c4e72d5d + # via + # google-api-core + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +griffelib==2.3.2 \ + --hash=sha256:8e710afededd5607f95bf3c8ccc175ee306e7084459faf27b9f9f44ef2a7a274 \ + --hash=sha256:df00c7a0dee3d86268d76788997a1859272cb1fb7b865658e043d2c0c3d52e60 + # via fastmcp-slim +groq==1.7.0 \ + --hash=sha256:cb1518f823423d4e52445859eb7d2918a927794cec12f8d5e50d9161e3690fc8 \ + --hash=sha256:d582dbb3f071b92ca339baba83af9f57ba6f46a51c34b04565d7cb9badb2785b + # via browser-use +grpcio==1.84.0 \ + --hash=sha256:026d757df86c5b7a41de8200b9a2cda454aaa5004cb0c7e3374c66eb82f61499 \ + --hash=sha256:06619ba1515e5ee69fb2a514e95dd8be05ce74cb3928d5b34f87f87c86fe3c27 \ + --hash=sha256:08735e3d08d24ab3132cf87e2e5dea8746cabcc7d676c2b0b7362f195feef9d9 \ + --hash=sha256:0d532ade4486dad9b302ffa4d4683d67561051c26d17c4023322845e9fa10140 \ + --hash=sha256:158c1c11cfb61b4849c3caf4d52de6f5ecd376e14446feb4a90dc95a90d616f5 \ + --hash=sha256:15bb76489e337fc492685c9758e2fd4d4ab516b901ad830dc5a91987decf00be \ + --hash=sha256:19aaf172fc2edbefccce3f6e92c5150975dbe56c45744e9e87cf72ebdf85bfbe \ + --hash=sha256:209414080da8c20af94df1395b635da52dd57b5edc9e917e1deca0dc1c4bb55e \ + --hash=sha256:210e4c32f907045eb8158273e60c6ab69a3947697df6245dbda381f26c59485b \ + --hash=sha256:23e6e8e8a75cff88e0a793bfd3becea03a13e2763ae90c1ff573bc19ca5b429a \ + --hash=sha256:27b8b36200a9fbee6e120246f4a8a41657549107ef19fb2c819c4b2fd524f39a \ + --hash=sha256:28d2609691da93051e998495108bbddd2a9f7a561253bae94828d81290f30c15 \ + --hash=sha256:2c024da73b296f040b8360e60bd73a659b230093684a438da0e1260f34cc724e \ + --hash=sha256:393d8a78bff6731ecc5ad2151a821f8fbc1709b137ebb9c25a4ef399fbdcc914 \ + --hash=sha256:3d6a82c4fc6c85f2fb7572c86bdb86f84c97b6580e5f6599f711800bac48a5d8 \ + --hash=sha256:3de427b05f244ba2c2a9bdc67e7a6731c8340811524ecc4435466549f8af1d17 \ + --hash=sha256:406583b4e8fb2282ebd392e12b963e601c1f82e07125a8c2cb5b144e7e024796 \ + --hash=sha256:4119efa6519871719ad81f33bc95ab87857dcb1c5801f30a6e592f2c41164169 \ + --hash=sha256:42959bd50dd660ffc3f2a9bec15a6da4f9aaa0dda555d59ff2d2e80b908456a8 \ + --hash=sha256:455ed6083353b8e938f1d58c765eab2fbb165731e5b507be30fee344915a2a11 \ + --hash=sha256:465eef3d17e59ad22a556fc0138f7c7c799df426734344daec42c797d49fda99 \ + --hash=sha256:47ecf0d9b81d981f07b61bd89eced9d2582f5eaacc3aaa36ad27f81aef70a27f \ + --hash=sha256:49717e857899f4136d7657bf5aded61ac479110a075438290923a4d86af7cd02 \ + --hash=sha256:4aaeceeb7fa7d824c322d1ec3208c8495c88478a927295553235435fc49043ad \ + --hash=sha256:57dc36a5ab0e676f5f6e171de2917fd0aef73f32a9aaf23956bfe19997a30bd1 \ + --hash=sha256:5933a052946873d01a42119a05420d669bdca436aeba2d1851988ccb12b421c0 \ + --hash=sha256:5deda5b4bf62769eb98c119cca43d40e1231e34846b19db5cdea821d446a2253 \ + --hash=sha256:61386101ecaa096b694d0dd278caf99a56aeec78440cc17e918eef0b50f2d567 \ + --hash=sha256:659728f20fc7a0933ed7b1945435e31014b97ab8a5a7edcbaa70da4794aeb191 \ + --hash=sha256:70bb4ce8be0c5606bec259cbd7152374470396413b7863a658a08c849e6b29ff \ + --hash=sha256:71fd60e6e426d293d0a2f685115ad0a0845117602cf13605a4be7524fb5f7bba \ + --hash=sha256:756ea5c2da00fa65c930284892d2a9706828704ca3ba40b4c51c4834eb39fcfd \ + --hash=sha256:800b7e00d92553313c0463c200087930aa78678ec1d528193aeb50906f55989b \ + --hash=sha256:82da34ae4f639c73ac46e521e00c0a49bf86f717b9fb1f405f133e98731e38dc \ + --hash=sha256:8e1a45d174b6b8589f51dce1cea804aa6c1f72c9c80cba91ae2caabeb6d90540 \ + --hash=sha256:8e3f508d0e9e6236ba2f08d56e33355e434e785e813149a1b8477d3edf69779d \ + --hash=sha256:986e9751d416d7a6eaa2fecdac38da63153d63a4b340ba7d624889c490451500 \ + --hash=sha256:9b73836ba0e16fcbb57c31cf6cbc2907c8d8c790b83679df454b74bd15e0be04 \ + --hash=sha256:9bab4cf571653a8afffb83ce21aa27b51dfe629b526b7b6adec35491fe1fc2ea \ + --hash=sha256:a71d24f40b0cc6798feaa978c7411dc1135b7018e9fc0442db611c139bf58344 \ + --hash=sha256:a9383401d9f116f98cacd4eba6c505a6edb80ba65badfc8e8ed8ae64983bcc44 \ + --hash=sha256:b44f0a0fc7bc6677d38cc80bca1a32814ce6c8f200fb8b3c1a61c9d77eaefbf3 \ + --hash=sha256:b5c6f20d657ae09ae4e30d9d3a21edd13f1219d58cc6f999b9d1bb63be9c1baa \ + --hash=sha256:b61692f0069b3eee2fc8a3a1b7f6c044df9e03fede6ce69b3ca832e1c39f26c5 \ + --hash=sha256:b8c62888c3e49debf37ad9773e3c02f77b0c1e811f8fb0962f2b6c3bbab5b97a \ + --hash=sha256:bd8ea8eb3817b226057cc1c0e7ec4b378dcda52043b972b6ff12b1152178967d \ + --hash=sha256:c5559b492007dc09b4de9b95dab05f0b5e53547aad230cf07e46c7dd017a3be5 \ + --hash=sha256:d0fdd25faece8a1f95e8a3a8006e29701b5cf8dadb4a8132e68f3134637004a5 \ + --hash=sha256:e094dd21f077af8194923fc263cad872eaa1802bb0156fd7e5ae18e99cd86715 \ + --hash=sha256:e41c3993eee896c617dbd8a505085d28b6e84a0445ed9a1f40f95808473cf678 \ + --hash=sha256:e88d304f094f4937bc27ec6a435e218a084168f11ec630c8d5d39b431d08d81d \ + --hash=sha256:e90e3bdf7b5eac005fef631adae9cafde16f922def207b80a7c46b253c18ad20 \ + --hash=sha256:ed2c1493c44d0932f1e55fdb5d1ead658c68288ec5d51b8c4928422d98633ef9 \ + --hash=sha256:edb6f87fc60ff438557291501b3e16c7a77c3b01a52d782cf276dccc7c5dd89c \ + --hash=sha256:efb29f8633bf6630dc89de4fe0353ac3d7e4b70ef7b6e29fb40f00e68c127fa5 \ + --hash=sha256:f6c972474ce691aca74e58d17625450cef153dc4760364cadeb167983ea6d589 \ + --hash=sha256:f6d178ba6dc8e82976c184b65fddde172d054c17237993a3e083efe4f134d55b \ + --hash=sha256:f9a456bdbed52a01c9ab8423bdebab04a5363c78676edc55ab9b58bd13bdf9e1 \ + --hash=sha256:fbdbcd06986ede3ce584083b1dc2afe6808e8943e5cf50ad11183c03aceda25a \ + --hash=sha256:fc66cb50c93554b86db0b6625ab5c6e9051dbf8847c08d93c84918e02e413fb7 \ + --hash=sha256:fff5ef3fe1bba7d6147e5f19e01e5e122ac2c076486887ddcb8d42e663400fbe + # via + # lmnr + # opentelemetry-exporter-otlp-proto-grpc +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # httpcore + # uvicorn + # wsproto +h2==4.4.1 \ + --hash=sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6 \ + --hash=sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516 + # via httpx +hf-xet==1.7.0 \ + --hash=sha256:0a9e802f33bf50c851abe45fc5380e61f959e2d369647d6742b79ad9d6c27cab \ + --hash=sha256:19c0e64f14175ccb6a1aff69e0d2ab9ec5269a560e6687abaf2b3fa4f73de7cd \ + --hash=sha256:2814a6e999d13464c4d679b788cc5d784eb5a4edfc638a31f10e9a11ab531ef8 \ + --hash=sha256:2b7bb5727889b0f2436dbaaad8fc4c3e66b8240d992716989e0c086b4278b1bc \ + --hash=sha256:4ee5e05a627f5ab5bad7a86582277d645556ea1e199903aae19e033a392aa13a \ + --hash=sha256:57bc157b8b7fe3bee9dcb9af7f3da8de41801c3b31a9ef68a77a33c6a6be382f \ + --hash=sha256:59fba37039233c7fcbe196817d6cdcf1b40dfb17b410f229d85b0cf0a1848da4 \ + --hash=sha256:757168feb5679647c0bb13ee5d0faebe799c4dff9051419885a566ebd79f949d \ + --hash=sha256:80f79dae613ce9e0ea1fd1ae15616ca9ac74aed4c770aabc199c4f03ebecc863 \ + --hash=sha256:87dab080f8f7d32781c2586904e3603f4e60d09bfc727706c3ae419e0829beeb \ + --hash=sha256:acc3851cf2576a8fb2ae926da863f4efabe21303cf292e9a44332802ab0dcc6a \ + --hash=sha256:b01fe18dbbd151a2403d2c64ed30dc6547b00d6babab9a617d77c7acdb81ee66 \ + --hash=sha256:b91569d5f1b61c34b043687da02c05dd3604f3d329e7868510bf3f7971599006 \ + --hash=sha256:d406ec79053c0871817f700c2ac8c36ba0d87f9c34b7458b0f0063bb218b0466 \ + --hash=sha256:e3e88a7a75d7d95cbee1f37dc31341d6201124cf21c6c4b1dfab8ccba9b09e0f \ + --hash=sha256:fa029678be1ba7f953c409b0b27bf15cc69cd1c9b3a674fbd78856ebefca1052 \ + --hash=sha256:fcfd6c22418e57dd5b3aea649e813b2e2cfb2aebf317b210d90f1fe4b3018b52 + # via huggingface-hub +hpack==4.2.0 \ + --hash=sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0 \ + --hash=sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986 + # via h2 +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via httpx +httplib2==0.32.0 \ + --hash=sha256:48a0ef30a42db65d8f3399045e1d09ab0ba66e3b9efc360d07f80ea55d286025 \ + --hash=sha256:dc6705cacdf3fb0a2aba7629fa33c90fd93e30035db0c157325826be177e4816 + # via + # google-api-python-client + # google-auth-httplib2 +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via + # anthropic + # browser-use + # browser-use-sdk + # cdp-use + # fastmcp-slim + # google-genai + # groq + # huggingface-hub + # litellm + # lmnr + # lmnr-claude-code-proxy + # mcp + # ollama + # openai + # openhands-sdk +httpx-sse==0.4.3 \ + --hash=sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc \ + --hash=sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d + # via mcp +huggingface-hub==1.33.0 \ + --hash=sha256:04e434b06e100eddbce9a6e817d72693a7884b10a79bd67ab48080d5c07eb899 \ + --hash=sha256:367be21a201db9523eddf8aeac7048f2602c1b308691c97640d5e72ed188007e + # via + # litellm + # tokenizers +hyperframe==6.1.0 \ + --hash=sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5 \ + --hash=sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08 + # via h2 +idna==3.20 \ + --hash=sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44 \ + --hash=sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c + # via + # anyio + # email-validator + # httpx + # requests + # yarl +importlib-metadata==8.9.0 \ + --hash=sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee \ + --hash=sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f + # via litellm +inquirerpy==0.3.4 \ + --hash=sha256:89d2ada0111f337483cb41ae31073108b2ec1e618a49d7110b0d7ade89fc197e \ + --hash=sha256:c65fdfbac1fa00e3ee4fb10679f4d3ed7a012abf4833910e63c295827fe2a7d4 + # via browser-use +jaraco-classes==3.4.0 \ + --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ + --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 + # via keyring +jaraco-context==6.1.2 \ + --hash=sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535 \ + --hash=sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3 + # via keyring +jaraco-functools==4.6.0 \ + --hash=sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280 \ + --hash=sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30 + # via keyring +jeepney==0.9.0 \ + --hash=sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683 \ + --hash=sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732 + # via + # keyring + # secretstorage +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # litellm + # tom-swe +jiter==0.17.0 \ + --hash=sha256:00b5a98df3e3a3e8cf7b619f4ac2f8bf975bbf3d95d02c5d17b8dbfe5c8b8245 \ + --hash=sha256:00d783a779c5664e16dbad5e3a3c3a75e128b07dd5f4765159658d9210a50ca5 \ + --hash=sha256:0239520085cac678e77a606fd7e3f1c60c371d719790c5e3807388d3da4354c2 \ + --hash=sha256:02a360707033d8cef53f7f3480817a1489177a259ec6ec01e98c37e0b922ddca \ + --hash=sha256:02adebb7ce6413c44d40af9ad59d1c1cd79630ccdcb6f7bdd2d461e48c03d8f9 \ + --hash=sha256:03e432f226a453851079fb84cd17c6da9991eab723e28d716f14ae3d906e0c12 \ + --hash=sha256:0619d806e260ecf0c2a64521942c94af5d547c9ec99b55ae4f51b538b5576a76 \ + --hash=sha256:073dc68c1a700c8fc480e877864a6b6ffc887533e261f4380c08c16bf09d057a \ + --hash=sha256:0b52d52035b3907c5b1f6277857b29c1cbfc965e24e0f27330dbed83edb591ec \ + --hash=sha256:10c5349312e5cb02b7a21e123a57665afa895953f05bf252a9dd4c13a572b7ab \ + --hash=sha256:10cd64a5720ad7f809ac5466ff1705813f1b6b510f195a73acafba0ac0e1f675 \ + --hash=sha256:10f5558eed511b830488003449d942bd75829ad6257dc58cb9a03e596a7777b1 \ + --hash=sha256:11902505d401691720f5785c15b02204248526edee11b635cd6c40cd52b81599 \ + --hash=sha256:155be7355bdb7ca76ab0961be8982c225f964a5c073a83984183f22391cc29fc \ + --hash=sha256:16dd0c1baf098ae70b8f3616574eb3fedf34e26670b89e16a7e67561f737ed2d \ + --hash=sha256:1b18434638228c0c184281609bf3d9459026a0f1ea48fb76c205e3ef72069caa \ + --hash=sha256:29f49b325e0234e4ad9ecca5b861ffbd09b95ccac9bd46fa55841b6e56eea5fe \ + --hash=sha256:2c45ad7c973ef33fe5114a953377b35a95240f4542c0724d9f781e47dc24bac7 \ + --hash=sha256:300ce01ab0215e3dea4d00090143c909aedc65c0f809b3c07983e1d038f291b9 \ + --hash=sha256:30793a24a31e968969757c9e08d830cbb15a2cd3c4959b4498b38f4b1c2258eb \ + --hash=sha256:30c692d567ba206c7cca38c9d1d0ccc70c9786290173c184d871ca12e9981ed7 \ + --hash=sha256:32aaaa764604496610a3ad2d98503ae88ccb2fbe769e892ff4533e778e85f708 \ + --hash=sha256:362bb47423886d45a9f705d2d9d4008c6eedd4e41eb1bab4e96fb6daa06b33fd \ + --hash=sha256:36ee6e69027396664e59995b9a635a947a5304ee9837279584a0bb8145c8f6b8 \ + --hash=sha256:370d8fe5bf201dc6925e8a84c81ac7291f74d9fd1778234fc79d517064a5c76b \ + --hash=sha256:37150a9e02e869475854fa20b7d0d5e26d18d0f8bc17293999973ff27e99ae7a \ + --hash=sha256:37f33d327900bf2879613b3363fd48df97b4232d0c41f54bcf2e790c2fc40a71 \ + --hash=sha256:3ad556afc289f15d2b181b941982d01f06190863c07440185b9f354e1bd2def3 \ + --hash=sha256:3bf4dc2b84a464117fb097d15a25c58d100d2692888e3b0d92df5b48ed16b7c0 \ + --hash=sha256:3c1a5336c04a41b1f1cf9572e294aec27cc569767ff73de7bf87a91f0bea7cb9 \ + --hash=sha256:3e05f5adbf68c4bd11e1610f394034d984152988e84be6f8314235ce6f2139e5 \ + --hash=sha256:40d2c240f8f80b5b0f201b29f0ae129c81448c60c772227a41747b5e0026f6a2 \ + --hash=sha256:42b0260445251b1bc520a63baa94a32d88e0f931fba234f1764db7feb7c72174 \ + --hash=sha256:454c4997d73cc466c71fd565d91e603b0274e48ea0c6b0b7a7aee6967e4ceb7c \ + --hash=sha256:455e4ab35cb2a4a91a8404e08fd3c621bae433922e59bf1c494fe20a426b013b \ + --hash=sha256:4607ec7d93355fbc25b8dc5189153cf21d66063b9f9cd04dd2774e6e783f9b6a \ + --hash=sha256:470e1b1e4c42f1ead2189166a299691871a2df5056c976e7fb96feafaf5f9d44 \ + --hash=sha256:492f37230bbf9581ab2c17bcda862c249afb9ae2e3ab2dd6db59943bc4cc3153 \ + --hash=sha256:4dfbfe5a6e1e80a7082af559f66386405025ec278833e0c649f69cbc6e1004cc \ + --hash=sha256:4e3f052c671d5f425cca5ea5901cf11a831369fba4a55a3862cab93c323b4c3b \ + --hash=sha256:5078ab00664307fab2019b522a93aeb191122789f085daf5fd9e362154021d4a \ + --hash=sha256:51e1519d676a9f14dad9c2a411170d43b022ddb7989562df4e849b261ce127b2 \ + --hash=sha256:523c499235fb65add25d4bb01b1c4709ce695efdc7deb6c0a7bc515b5c44e0fb \ + --hash=sha256:545c36a0f3b2238c242cc9785439d3242a871b7bc39fe3f441bcaa07bf3aa83e \ + --hash=sha256:55d0e0e613a3f9ad600cf436e0e2b8057d1b52bcf1d91b2d36ac53451231e6a8 \ + --hash=sha256:5888fe5abc1ca2fa834a3e1b4c7ef0dcece286a7d7e95a609ef0934b777b9fc9 \ + --hash=sha256:58df29268a95e910f17db7ec9178eb7f15aa8619aaca3575275c4e6b3f4fe4c5 \ + --hash=sha256:59bddbe6f9ffecc68d641e1e2d619ce64cf8a9e9eeb74e5c518f74fc87abf1b0 \ + --hash=sha256:5a52a430d04225ffde633e6840bf2381d34c019ff98526b5929755b9052fb199 \ + --hash=sha256:5bf350452a43173e69e1fc74847c57a60e3d7515807287f29849baa2a85d8718 \ + --hash=sha256:5c23849235d2142ce444b2b8c6eceee9f82f4cc0bd5c9081602e4155c6197807 \ + --hash=sha256:61aed66ee042b3b49ef85fdf75714234d055d89d8496ac1c6e47f89e7a30d5e4 \ + --hash=sha256:6219adaf59711ba7063a52496e8ec6d3fa3e209d7827d83eee3b2abc780a1744 \ + --hash=sha256:64846211a2debe7c071d2146d2283d2b0c1c93dc8fd5fb7794faac2ca6061b5c \ + --hash=sha256:686c93d86f2b426c803024b805bd161a6cd10e9627c23e901640eab646c0ad8a \ + --hash=sha256:6871973bfbd4408f7f1c632b30bbb5bbd9671c1bc8650af6823e24b7be13709b \ + --hash=sha256:6af5b74073bd25bae695e6d00919f6a9be7ed5a9f8836d981eb1ffe84139e6fb \ + --hash=sha256:6b303d88e6a0bda789ec4b7801c7bad68e27230ba1fe4baffc756d1fbd32dc9d \ + --hash=sha256:6cb41cd1432f1dc19a231cf70b54d42b2c9f05085155859263fce06fa4d41388 \ + --hash=sha256:6cf564d43c4388149ca58ee571d0f5ccf875e20d1fd4662fd94cc0d1ea3b10ef \ + --hash=sha256:6eb6aedeb7352b8f3b6af9cbd67983840165c00428e63f1b420a85885128ea31 \ + --hash=sha256:70f19a2ca8429f91e82eeffb2f51cb87bc2d6e953b009b91a92d29c3a16ccb03 \ + --hash=sha256:71dbd74314c5df52a1bccf7b8bca46d14e943af7a2012e73b23f49977ef194c8 \ + --hash=sha256:73b64e69c4150748e020356d958af94bec33c70a0a93d665cfa8f6d580fe1a63 \ + --hash=sha256:746243a080b4ca790b8499af3d7cf9825d5f5987933950cd818e767ee353d826 \ + --hash=sha256:755079792868ce5d4938e83b91a0939b34fb858a1ca65a104f2d771bea57faa1 \ + --hash=sha256:7573e80232c5bcf80c24c038cf7e53a463f5c3b1dd1dd4109d66304f4dccc233 \ + --hash=sha256:76eb4a5c20e86f9f848286f167024890f2862258a965d254774deb7fc1545ca1 \ + --hash=sha256:77f6aac0137309b31448c1bdcda4c6c77077664a6d018ece8d94019c68a5a5b9 \ + --hash=sha256:785a216bbaf8f15fc974e964ced7322cd3d774bb0e86949edd78c6bffd6ba35b \ + --hash=sha256:7b68d3495d95da120651a5628c7ebadee84ed001a1b76e6afc325c42482f15b5 \ + --hash=sha256:8079849db9a1371bfd90bad088458a8fb836261879df2233cc9632464ecf64e1 \ + --hash=sha256:81c83c0abe614446a283d994d2c07c4f58632dea2cdf66ba9e2921bb8ccd593e \ + --hash=sha256:826871c42cebaae22f0a2b5673a4a1a75c851bb2d13b3c17764a630a6b298984 \ + --hash=sha256:84963d3f395ef5e9a32ce47155e08a7962fa292c159a10cb98b931cef1416925 \ + --hash=sha256:84ac78df457e1ee3f7e733bd114823302ae8c5ad5542d7e6647d92ffaa090a04 \ + --hash=sha256:86d703d9faa1ffc8ae4e9de0fa007712ed2171b5c0d93811a8e2e105ac729b0d \ + --hash=sha256:86f3f9343a288eb85a81ef20a752b2f84564296636db54a9fff0b5c8deaf1df2 \ + --hash=sha256:8adca2e793288e5f1bb29279bb439d0d3cfbb50eddca7e7e6ffd42ff4f482406 \ + --hash=sha256:8c21265b251d99bbb40080d178a8953e35601d3a1564e05c4de4c0d2ca616797 \ + --hash=sha256:8c286860abfe8b100cac1c02e225e5776eb9216edd71ba17cdb237da4af32bc9 \ + --hash=sha256:8f770b0c77e5fac482e1ba03ca1a7e18286bfb213d749932a00a7e4cd5de5e06 \ + --hash=sha256:93946d89fa04d5ba64dd323a8dd8d901676cb8a3c81d99ae4f6c051a9b4c3f2f \ + --hash=sha256:96b8b0c6dc5d78682f54a450785e075aa929cde768304cad363cd4efba5a82ac \ + --hash=sha256:9bd3caac219df476dd0cc3fe01d2f1581ed588906feac767abd9614c1c12f8b3 \ + --hash=sha256:a277f97eba7d66b1ee27eb5dab5b774ff46a10c78d89a1d3dcce04ce1357c8ca \ + --hash=sha256:a3cebb1fe4a1abb00465f3f8a17e09112603e8b7c59e5c3adbcd9f7815a64acd \ + --hash=sha256:ac3c6ee3264d6f5c44c617f90bc7e8b9e1587e7d6708c9d8f811cb65582ee312 \ + --hash=sha256:af2f7501580f274b63c4b2283bc425f5df7edf06ae5b171e5f87d912ff359a20 \ + --hash=sha256:b550585523339b71cb852b811aae49d08d7601ad8ffe9f5dc1562f4c3d22fd87 \ + --hash=sha256:b75f85660108965a94be77911a25a253429307294d9415b3c597118977a614de \ + --hash=sha256:b847b18d066c46b3b7ae49d6c94a7634c5e4a8983146ee25562a092000f5e3ad \ + --hash=sha256:bcc064f99183a9cbe7f26ed648c352031a74145cd61ed75d34632c73eb46a5a8 \ + --hash=sha256:c19b9357309b8cc6de8a48fca8e44a8c9c2feaaa2f5896d037fa505d48fcab80 \ + --hash=sha256:c4289293e5278d9314b00f15c37f2120fa51d3d68565292e715524c750e775a9 \ + --hash=sha256:cfafd7be8b16ceadd298db542cead37cddc211c4c49e04ad2596924df18625b1 \ + --hash=sha256:d0ce4feb52493e3513335b2accdcd75605652e4632772d3c8c2f7b86954d7f39 \ + --hash=sha256:d2c0bf24c72fd0491405dce5d40194f2070e9021ce648c1a1d46234b93d848ff \ + --hash=sha256:d47687806f9c54c84ea38733507081337922beca90ce819c7d852dd485bc0f23 \ + --hash=sha256:d85c558c9f8532bba287a990ac63767c7daf756f0d8c030219f62499b1fa228a \ + --hash=sha256:da139721f4b7cafdbff580a4f511ea24cb91f4909330c6b926a1ca53836c0a59 \ + --hash=sha256:dbbfe4e3c21c8166980cddc5bee1a315df082454f007947dfb6fb73800768165 \ + --hash=sha256:dc0288ce39190ee33fe6e4ec73161eed34e7e2da509b525546ca061778d62b64 \ + --hash=sha256:e088612ff90ebc9247e1a43074b72835804261c47e6a6c01cb3ddcb55360d688 \ + --hash=sha256:e654b6b04e39c9cb19cb8b04c6ddf1f2db07751fa14156413969fd78bad0e5cb \ + --hash=sha256:eaba834b72d573547b9d966465b3394b749d5e14208cc70acb63aca37619ab33 \ + --hash=sha256:eae86b1f027031e39db2e0e9c4842221edb7b8cd474d23f87a79b3bd4b651768 \ + --hash=sha256:eb2295da7c3769f6719b227a237aa6a5cfa6550e478bc838001b592c57e16575 \ + --hash=sha256:ebf918dfd6a74adc1b9ad71f63c4ab00902fcd3b7fd39f2e24d871db8d713b91 \ + --hash=sha256:ec89771f4272b989487a6364e519db6bbaba323e8bbf949ac89a45ea9c18b7a3 \ + --hash=sha256:ed1a24005daac667d577402d75a2922f9775a165b146b883ff1ad3602d8be689 \ + --hash=sha256:efe9f61bb30174d2f5c8396445c360c96c44e78164d0815dfe627ccf57849574 \ + --hash=sha256:f0bc7f684b65bcda9c20434267577db71bf9905ceddd32b60d1d93278d8c8d3a \ + --hash=sha256:f3d7f7b34114f7ddc6d72a8e882d49de636b35d9fd12b4d420d3c5729f6c9812 \ + --hash=sha256:f753eb70b1474a29e635e7542ff7312e6d6b951e0b25e8a2e8c34eeb1ddcd478 \ + --hash=sha256:fa13acf1046f95df808c64b1310705e143fab87aee73ae00cc42d640867fd2c1 \ + --hash=sha256:fd7790aa79c8b518e512ebcdfce9f11d8ef5f30efd43720c8a19a548b39fa489 \ + --hash=sha256:fe15ddf316f1f1f643347d3a474e74ce61880c79a11ec5dca53df20c071bd3e8 \ + --hash=sha256:ffa0380ad091de7d3fc33e17a97ff479851ee18a0a2a3ee56ff3215cdc886656 + # via + # anthropic + # openai +jmespath==1.1.0 \ + --hash=sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d \ + --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64 + # via + # boto3 + # botocore +joserfc==1.7.5 \ + --hash=sha256:add2c2c84e8373b084d526a8b53daba5d7a513a118cd2dcd9fc9f979d0922159 \ + --hash=sha256:d5ff536e658e17664f8c1b1ab60dc4aa62aa973fcef1edd33cc44bda45d6f5ea + # via + # authlib + # fastmcp-slim + # openhands-sdk +json-repair==0.64.0 \ + --hash=sha256:2890be942a7ef20626e4eda4bd91b37485bc5271ac122efe7bb924232fef60ea \ + --hash=sha256:3bf14cf14d8ae96f7bc467e6964d8accd52aaad084f973e38ebe4e43f9d051e4 + # via tom-swe +jsonref==1.1.0 \ + --hash=sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552 \ + --hash=sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9 + # via fastmcp-slim +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via + # litellm + # mcp + # openhands-sdk +jsonschema-path==0.5.0 \ + --hash=sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2 \ + --hash=sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c + # via fastmcp-slim +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +keyring==25.7.0 \ + --hash=sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f \ + --hash=sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b + # via py-key-value-aio +libtmux==0.62.0 \ + --hash=sha256:41e9e80602b2656fd119b13253b27bad46af5cfef32099be53cdd391e2936b61 \ + --hash=sha256:626aa6fae45e3a423e1acc81604efafa63b7262b1d67b2c7a8778b1ad691456b + # via openhands-tools +litellm==1.105.1 \ + --hash=sha256:3253da7d4967e367fb7e4e404b598a26037c45ba6d67a79356a81d3f8b9f9f3a \ + --hash=sha256:633886584ffb6421485349376f2c1666d4854fecc3ab8b1cdb24537817d4e1cc \ + --hash=sha256:beb5044645144308b39560049c0f68875ad7de07e681ad0b73e2bc6c3705ba61 \ + --hash=sha256:d0392f5e773ab4bdb15daf2856d89afce74dc8b7e6d802f52060a23778ef650b \ + --hash=sha256:d422f4fa44b424c541276bf55355b3650e2db01c08922f250baaf61eeaf788cf \ + --hash=sha256:e2b46bc884b1aa7299a238158f7ff3f3dad49821a0dcfa4a77a54cf38e78bac2 \ + --hash=sha256:f1a274db0b2bcc78c34ed34c06df2c1dcd0dc038469193684927f6bb2cab2d97 \ + --hash=sha256:f3d743c2d274c6537f7341213607c927c486676012a9d94412a8a8ccbe2f51f1 + # via + # openhands-sdk + # tom-swe +lmnr==0.7.67 \ + --hash=sha256:ad25bc2d6a311b54588f69f8e5b9b8a5c6e0574e6c1fcbc86e8b7d6c1f02e13a \ + --hash=sha256:f98a3558fbac21585ca4e9f1092c219670e2c31f10dd4634d1e5580ca0aacc16 + # via openhands-sdk +lmnr-claude-code-proxy==0.1.24 \ + --hash=sha256:01129f186c2590eecf58c3bf96f63aef6bbf29c7240776fff77f389a258ce9ad \ + --hash=sha256:0a32cff0fb4c1b45f4c04b5c950754c58b9a7a7e82245bf6aefa12f794c47e60 \ + --hash=sha256:0aa6b6255509acec88df4614fec8b8fd8349c04c068bac36781d13f78f90defe \ + --hash=sha256:0c299582ff8d5c0d9f3d281400929fda01998315ab4042dfeaf9c7a014e26447 \ + --hash=sha256:0d6760df45a658e129968f946508acafe5a45d2e9bd3239391315ec255486733 \ + --hash=sha256:0df9d32b0312231a54665d54edceda94451788850da7628ed2edcf3c0f58e6a6 \ + --hash=sha256:0e19275f022d5799036f470a91f9569383aa5ccc6545b14c4bcd2af914a05636 \ + --hash=sha256:0fdad0ceb0831d7bba3a5ae45748b30b4b2790d619ae786fe825ff36371bd6cf \ + --hash=sha256:1120833d4d335a0b46caf9e384c0dd7d02f058021c19a443d57ac49aed3f005d \ + --hash=sha256:1133db0dd9fd4b725684782c23d8190c00d2029710c78f9d2b4071cab7140612 \ + --hash=sha256:11ddaf6a3e1d03170359f54482aa5e79ab527b3ed3fa563f0ba88f48906c8ec8 \ + --hash=sha256:124e1d4e53e6b1aacf64555fcb3d403510ccc38fe2d1bf798e80b4d9b388ad45 \ + --hash=sha256:12945685b98cfa2e6fc4d2cbb5d4e3f697b5ac47e73fae0d2a45e0ea0ee3422f \ + --hash=sha256:14a70525367c2eeb6be2421d48b4c465d5339e3eb13363c618c3a39f7b997452 \ + --hash=sha256:158a515701c5243b897fc51ffede0e33dc67654c3c5418b1c6f0e6719cc6eb51 \ + --hash=sha256:18ec927816c6952bf055d934795e8ea206661b8f797dbb6cd53cd1ed36a15b26 \ + --hash=sha256:1cb1937773434a516e59925892d2ed1cac2fe1234755b4e396184f7301d27404 \ + --hash=sha256:1d999344d04d90d1834208b3bc236ce71b3aee97b61864ae64dac2d2131c717b \ + --hash=sha256:1ef8be6479aa58a6a182c71b0c321ddad33af100d34aaa9412d8a7d28089356c \ + --hash=sha256:21cf7346f8df40ed72c075caa38b7cd90a3a69ab240fc6435981c90b518629a1 \ + --hash=sha256:2b8688427f539c148f2c02c18c6c952565559f8bca99f43789454f27b39a68b1 \ + --hash=sha256:2cbbc336ce3e1cabf45740ccf41fb5ef528929f23831d4b109e2ac3cca9db8fb \ + --hash=sha256:31b6482778359beae7b433b9625089e81223d0c6b444fb2d50aeb72a5bcafb5b \ + --hash=sha256:3d45ce57152275e669c12754d2600fbbded3ef4482fc2ef6186bcd0851f066e7 \ + --hash=sha256:41cd878504c4294b61bb213e5b5217f425e6411b929ff980e162a5315e73d6dc \ + --hash=sha256:48cf3c9d2ab6d5ebf7b34ed7849e4014c0e101ef6538d495ecfbc4e70ae96a45 \ + --hash=sha256:53ecd4af2e960c7f98396f07c41d68b1443e722497f5a5b51e4256e98807f633 \ + --hash=sha256:542fa7eaa58643e0c5ab474f16b53cf4ee0f86d204ba84523ed257c0ea44cb70 \ + --hash=sha256:5858af1271e76aa1f93d94b6351e720c52b0b770e3b989a91b4b65569456b4e5 \ + --hash=sha256:5ab210183ffe9760f811eb88b8a151036703a09be0724b84097e4d4860576c8f \ + --hash=sha256:5be03416f03ab8245e4a1504cfe0f2db4223eabe4eae48b3016232520793a5b8 \ + --hash=sha256:5db611fe827d55aa54b758892ce0c66f4d0463e8ffe40e1695593550853e20dc \ + --hash=sha256:60c31daab5f447e6cf83329f032c7673f746db535b7f4c6e1cbd1daa227c90b0 \ + --hash=sha256:619e0f9071672c362499a4959b083d27c754b798236bc7540fc7b73c2133114a \ + --hash=sha256:6235f7def2ac126a2f3ba4c4ce987bae120e9c293737a4d3e5f90982a2b9e75c \ + --hash=sha256:62796ac6328827a87309918dc1af88e1d5755f8500c5c1ec2ad8a7940d369b3c \ + --hash=sha256:69034b58353772b4ec42a97b3ff9bd2299b0101aac42aaef26246b40461e82b6 \ + --hash=sha256:6aad5a8ee6f24820b809a6dfef888f3a2204f950f161bcb0c7e9602093fdcbb9 \ + --hash=sha256:6e10d5d8649b052fb0e0bfaea59eb9d6afc9e293ffa0ce19f02248ab3d219686 \ + --hash=sha256:70032a7f38b1fc42c8b937cd645246898d2696b489f50fdf6588e024460dc0f5 \ + --hash=sha256:71e0e1a7724467190a4b39b9de1bd30897fe73ee34e284e608c838606abb63c6 \ + --hash=sha256:74a0adbf51d02a380fbcc1aedec95f58046eaca454d5927985f9bb722cf476d2 \ + --hash=sha256:76dfcd260133b816277fc6d95391402db1333c52e2230d39ec081d084830f7cb \ + --hash=sha256:78ede3479c8d555ec6a227ede3478d14e448a8d885cc559c30a86f6397abc5f3 \ + --hash=sha256:7b5856a70950fdb07287912cb6e1964fd3054cbfd78083d8810f225a45af8318 \ + --hash=sha256:8129f90708e917fac74504212e2f3390aa1879240dcc285048d6677667f9d2d7 \ + --hash=sha256:834fad32da7456e36e486a6ac7071c87f48463a3c0936a52915d7829b8966667 \ + --hash=sha256:8581ebfa6d59753d30df51fcf2ccd44ad2e2b68270587e0c2c85f5690dd06c11 \ + --hash=sha256:8ab30b9d9d82e8f8a870a824b244e04accf429c14d3d5c7c53ebc82590164bf3 \ + --hash=sha256:8b0c0da1e0271b105b1f280fb6f0c53bd437932efc6cd9f763cca55e16a254cf \ + --hash=sha256:94698d2a79b1998ed22f3746d7aa1b072d7f57049146dfdfc6ef9938e80e51a2 \ + --hash=sha256:9c55f43de6b6fe5070fe362c409ba82c80bf4a39b9fa12c1566227e736fff58f \ + --hash=sha256:9d94ccf035f27ef237c478f55ab4cc93dbcb24efb58f0c1c77f6b57ecf43235f \ + --hash=sha256:9da4169fa9fd5b348371bb62a5868d15e6c91335206404e6f00da0bf5ce788a9 \ + --hash=sha256:9e13390b3311bf945dc5b3861e987365449dd363d1e4e9463def1b7e94ee12c6 \ + --hash=sha256:9eb2082ad536e13346ab1926bf8be45072205a87e49fb0c551bcd6c43dec9a31 \ + --hash=sha256:a1506aee7f4a038eeff89fb8cc65591efe2b6df99491cd22d8f73e462a888016 \ + --hash=sha256:a2c2a99ae273cde1877ede2e3c12466fe5d07a0f9824954dbcb7a126ad53943b \ + --hash=sha256:a4726b3ff651011e19c4993dbcdb917e525069e26901d54f8381dd16cd629e52 \ + --hash=sha256:a4d5dd96156bbf58fc271f0ae01cbb2cf9fe7cfd5c63b358c96c7a70edcbfb61 \ + --hash=sha256:a57b10d690aded72c6548f9f721e0466a0df4eef5713dc1a842abc49059a81a1 \ + --hash=sha256:a9abe118498fda4c804dd7691cc3e3d45dce67b1e592a844ec2c0032401058bc \ + --hash=sha256:aa5391b33d398a7ca89f838cf23f1ab2b76b5bcfe4648da9f804a4256084478d \ + --hash=sha256:ae6d5d25c36792072ee3d18fb97611ea69fb9e8c91b2fcf103bb336bc84454ce \ + --hash=sha256:b36d0cf4cfa1794bbf3c68edb4380c7876c2b81c968f2fb5bcb8df5df1ee5da6 \ + --hash=sha256:b81c5ccf45a02b842710d36230f58cf55cbf0aa609d7df55fb4227b766a873ff \ + --hash=sha256:b883635f63d8b7cc34682384cc2f63dc1ab185ee57b7d6564e2146c3126e6b37 \ + --hash=sha256:b8e4f8b782c6de87b117be9681f26bd8f4869931b0c37421c68a8012acce227b \ + --hash=sha256:b9e3eac52d586e208af31ebc517fb1973315e2234ea272c0dc86017cda4010c5 \ + --hash=sha256:bccd80dd6e944a3c08667e208342d48f01809cf9f55ff9e4da8d35e028ac89da \ + --hash=sha256:be59e49cb2cc7c80c7fd3a07ad50eba105d61be8a97c03d24bc5b0fa809b6e01 \ + --hash=sha256:bea3767b28227b54944120d0a33678a5c6aa7207d06b1ce38bd6eb7cbe469e97 \ + --hash=sha256:c0fecd7b8194516c1da9fccbc046f6aaf99de9c48a6b494d3ba9b2f85e16bacd \ + --hash=sha256:c14cd2de309c693c1718a584eb8a705a68254ff1835733c857ca80f18620e051 \ + --hash=sha256:c24f8e0673f37ef56f049a03fcc26ba610249e4884a524186c7aeae7acb5988a \ + --hash=sha256:c329edfb61c63598ad19c1a44715b684093f2db33f0d7a0684ac7521635d5947 \ + --hash=sha256:c5685e1d99c5f73017237ff1db944b91a4b4df259bb237941db184ef5bff7720 \ + --hash=sha256:c710aeba55b600c6ae8a98476034aa65fddbc6d62eb7a9b99fabe4ad0c7626a3 \ + --hash=sha256:ca11450de9a7861cf05265671ed97d850398e8b169405cb6ed4c3265eccc88b3 \ + --hash=sha256:cb833f906e1b2790bb0fb8eed63ed5d3344025e4bd250252bb51de272ee91043 \ + --hash=sha256:cdf653934afcd4b673782a165c3e966c32bc3daf12d852f006db6213bf27c33d \ + --hash=sha256:d099f409cd0625cc87d12ffb6b6411eac0fd01f15a923ad6384c53ea55e843f7 \ + --hash=sha256:d5f13bee12723e2dae9811547ef58577428d3c3608fc1fd60d82c17b2b0c8f78 \ + --hash=sha256:d61d16540e8c8e1048d083bd53f8244893ecb939a7ffe0983cdf479606481d13 \ + --hash=sha256:db99c4a2c1b9f848375ae0156515ba6c5142141c42333f6ec429ebdac2d8483d \ + --hash=sha256:dd316d093f734e5a66813b3ff69c59f810d9d556c0750dee6ec2e6ef7798e839 \ + --hash=sha256:df4e053f3136c5841cc54150326cc81a8c75d93f83b1dce744fd361aa8c3f49d \ + --hash=sha256:df6ed3e22ee5dd9d11fb6dc6d02391fcbcf7f5f8befe9a9bcc294533b56639da \ + --hash=sha256:e23602571043be7951a0ba2a26e8dcd0afcb6fcc069837b07478a16f6162d03f \ + --hash=sha256:e71a83946b90dd117be1dee27f10dd4d81b8c7dc11f309fe93cbe989831759b7 \ + --hash=sha256:ea8cc21a674128b30116bb0cb0b79bf97b5f5838bad19c0bca55c15630cff6dd \ + --hash=sha256:ebfdb22bd1b44354abd3cd8990d0f7fad3646dd9188901290840aa6275367761 \ + --hash=sha256:ecca23a8e3e9f32c1a02525c6bcf57b8acb5bda9a35295038ab46c9fa3d30633 \ + --hash=sha256:ed19156db89dc91973f66c577de393fcca6574065f2fa1e2f00e273c4e0fe123 \ + --hash=sha256:ef4c751087296c603d9c147cedb54de139b6cde91b787721a1ec19b0a472ac14 \ + --hash=sha256:f27e9c6f0a5418ad52450217a116c53da100fcfda332c377d24cb7b3b9975ff4 \ + --hash=sha256:f3b267521c1f2f8cc113147d2362f7682642f372c716baa1a363baf8c263dfc0 \ + --hash=sha256:f8174577cb026b9032147aa89b7d1ba7e41826ce247b073c0fadb4b5f7588e15 \ + --hash=sha256:f8f7ebc7682b1cafe1f4c3c1bc3e27dd25ad804d6a9169b2b101a8bf4b0eedb2 \ + --hash=sha256:fb636a234051792c6bc7ea8f3b47350b1a5a86ff7e15cad881e41e3b6f2a4b93 \ + --hash=sha256:fc0008100c958255241caf29a850c4373b186a18edde3542c6e40443ca3e597d \ + --hash=sha256:fc092309ee3bc9972f97f28afe8fe56fb7e9dd4b7601e5ed538831dc20a08a59 + # via lmnr +lupa==2.8 \ + --hash=sha256:097e7d0f1719a88020b67c82e05d53d7973c166952393afcecfd8434c7e19a15 \ + --hash=sha256:0b5ebe1a13c45767919c86750b84fe2da9f6288b6f3cea4ce7660bb2abc9d921 \ + --hash=sha256:1628371c6592a6d5650497a9e31fb2bb3a7e9883c1f301d1111265e484045af9 \ + --hash=sha256:1ac2b1ec7504e6148cba1bc35ac36c74d18a0ca6d367ffe7e78a3773c2694c0e \ + --hash=sha256:24b4d8af5558e549b70daf1547f5c1c1d664ecea9fc790f83efe5d75e9a93797 \ + --hash=sha256:250e035fdaffe8c87093e3ebc206ac29a26131b1568ea711d780c26001ce96e7 \ + --hash=sha256:27044f3363047f946b3d3aab9157cbd172b3538ada9ec1baef43432bf7d03a78 \ + --hash=sha256:281bedc5deb92d31e649a3552edd662449365a635904fa4d5cb4509c7245e34e \ + --hash=sha256:2e64acbbd47e9b82a64405a39e0d2b36a5a7dad8ab41c0f3437f572f7d282ba3 \ + --hash=sha256:32e4e5103bbddcdd2458fb2ccae6c8ba11c9997c711d7e379e0d45551d109c76 \ + --hash=sha256:33e7e5aebca64b154b0a1679caf79e19254ff37bba51e87abab6848f97cb2de1 \ + --hash=sha256:348c3f8ecabb6324dcbc05c2740d762ef8fcec7b06c79e45262ab97a217684e3 \ + --hash=sha256:360056453a7a4eaa4ac5a204c31a5a014b1eb2ee5490603234d2ba831684f1f2 \ + --hash=sha256:3903c9cf628dae2f56405503247b77a61a3a61bd2dda470e336950c74776d55d \ + --hash=sha256:3ffcfd8e19f943ad459136b3f60f085ae4948f024192a93ca4b4ac3023ec88d8 \ + --hash=sha256:4203fa1659315e939a5304e75001b8cc14234fb3cbb3ed86c049b0cc5d90fcee \ + --hash=sha256:450650f91c48c2415b0d59ab3abfcfda3b6efb5b858205f4d4bda8ad141fa529 \ + --hash=sha256:45fc9da0145ecb0083ef5ff9975116cc784bd0258bdc2bd131ba15483ce18398 \ + --hash=sha256:4f7c553c1d8cfffbe85d81daef730d12cae4b6002d457542914da0ac8a1145b3 \ + --hash=sha256:4f81a02806e7c7ad26d8c6fa222c8bef1b0c1b124347c879be880b41339d41e4 \ + --hash=sha256:4fe5d7a810b64ea8511eb885fc8cdde042ee5ff7b7d08ae78f32449756acb177 \ + --hash=sha256:54cff414f21f8cd8c6be4aae52541f3b9cd39602b59e3a3db9b5c9f9f674ff18 \ + --hash=sha256:58e18afed57955b41130e269c78f53d4123ab86e236b53816f4cbffa25cb5d30 \ + --hash=sha256:5caf45d15d424cee52fd67341e96e2b1dde0658ae90eb156ac56aa0d8330bc38 \ + --hash=sha256:6c817d5421094507662e5f8feb8cd1e154c10879921c06079b6063be9d8f33c5 \ + --hash=sha256:6fbcc9911f05c67affbd225fc024268e61e98a18ad1b1c2aed6c8796e4056554 \ + --hash=sha256:7667001804657496dee9feced2daae5000b4604a3218dd8e6b7b754982ba88b8 \ + --hash=sha256:7bb223ee8f72d0dc076b0d65296ee72f1c69450f9d2fed5315f7707d98c4a03d \ + --hash=sha256:7f210d5a8353e510ea1199c42cf3cbdd630553bf2bc8fb4c00fea06fdec7c798 \ + --hash=sha256:81b283bfb13cc43fa4910fc98ec110ab861bcb39680f48b266f99d6e3be1049e \ + --hash=sha256:81f2d843ce668b653146c007467570210ae44be51dac6926666c51d49536f307 \ + --hash=sha256:86f6f668966965b15247dc32d064cfe7be67b71e584ccfacbe2f637575296878 \ + --hash=sha256:891f72e0bffbed1e4175f975aeb2a083956586a100066525e1be485f617f7b25 \ + --hash=sha256:8cf4f064a0e5531afce2d7d750120c10c10f9529139af6ca6150d13151034398 \ + --hash=sha256:91d622777febda3ab1bed1d45295f2f32a4680c7b3d7caf8c669998ed5c44118 \ + --hash=sha256:951496471056061598a7d1729a6cdf48d662fec777a9f2d8aa5a1e62fd30e5a5 \ + --hash=sha256:97bd01e90b8031e56a5fd5bb70605aea09f1dba675c1140308a52780f93d06f1 \ + --hash=sha256:9e0d11b8f3a8dac6413f704fef7161d048bb10c58bdac6cbffa5e60efa56e9a3 \ + --hash=sha256:9e304fb1c50cf23fd8882afbe1aa87525ef8a72667bcab3b37b2bbb2bc542269 \ + --hash=sha256:9e76e45057cfcaa20ee3422c2289a91f9d51783d020da3570ee226de8f6e71cd \ + --hash=sha256:9f3f3955f65f9fde2dc6eda3041ccd394cf54d4bf083f0cdf6feb3d58e5f38d3 \ + --hash=sha256:9f6f41c91366e7d0d474f87d81c1274af861f40812bf729c9f97ab4c8f3c7ac8 \ + --hash=sha256:a295f87b5b7ebbfd5191932e8cb0e51df3c7769101ac6b6c7d7c9fb27bfd1307 \ + --hash=sha256:a591b9947ca347b41a63370e121d6e2b1458fe6dde9ae065029ec10a37f25ff4 \ + --hash=sha256:ac6b6e8d0e617e26a98cbb44880bcd75de5d32b3ad7b3b3793583909292b47ed \ + --hash=sha256:b036738282a5acd2e71fdddb317c9df8b87c1673aa57f403d05fcc2be8abc4ba \ + --hash=sha256:b12e43c1fb787189dfc28cd604aef0baa2cb95e27da19498d520361d0ace070a \ + --hash=sha256:b9bddb09acfffb4f828f790f444b11dc0cca591afea1a244d9329eea2d20c003 \ + --hash=sha256:ba3a7dd839f90c3d2e53bebe3c192b1f3f9fd720a6781256405123211fd0dce6 \ + --hash=sha256:bfc470012ef66ad064c7bd77416af03a3452ef630b04b9012595ea13f2e54518 \ + --hash=sha256:c2a5fd15dc62374e1661a55f01744c9ec1c56f291ba4a0749d3af2174556e78f \ + --hash=sha256:ce86dff1ee7f7cf45f5622065ae991949dd7bb1703581cbc58a630137bb7ccf9 \ + --hash=sha256:ce9404c661dbac65cc9bed351ad45e797af93d30d70be309a3fa8209ac86d93b \ + --hash=sha256:d3d0cde2c77588d1c60875a4f34f059513476c6e1775351897195b51e0f3df08 \ + --hash=sha256:d7edb13a7a5250b5c6c22d1495d9e842b5c9fc5081c8fe6b5efe2112fe3e41f9 \ + --hash=sha256:d8022641b9ec8ecf2c5ecbe9f47e5a70e0b87c4b5ae921b92cb02a638e0acd08 \ + --hash=sha256:d8766aff03a78c80ad2d188a8bdb216de5ec838359cd87e05bbdfa56394a6105 \ + --hash=sha256:dc51250e76367a3e27fcd01dc769b9bfcbbc34f48df48dde53d6af6e75b7eaa5 \ + --hash=sha256:e8d4f4dd4acf4a0e42adc6b1ad220e1c86fe3028402c2f78bd0728a6d241bbe9 \ + --hash=sha256:f4342f4de76ae7ce2ab0672d36003bdb7e1a33252f293b569298ddd792e70e33 \ + --hash=sha256:f4d01b2a08c70bbb883a9e082b6b36b89121ed5910b710f1ba11c73295ff4fba \ + --hash=sha256:f5a6af145b0ea818f01d27bfe2583a4b538570bef61d22c8773e0eccf011234c \ + --hash=sha256:f6ddca4774d5ca451768a95e378a3aa041076e29f4613b8562f8e98efb6690fd \ + --hash=sha256:f6f603391dffb256e36a79fd2044084d5f4b8a0a4c0e5ad291cd3ab3aaf1fd0a \ + --hash=sha256:f711a8ab0486b9ac6fdda94a22ddcfbc9f0d4a27e3a8cf1bf79c6e48b33017c1 \ + --hash=sha256:f8a22088a552828958603323f0a5c4b3e11e03b75d0bf4c965ef879de9b60a8d \ + --hash=sha256:fc47f536ac13a79cef47d29a2b205576a22841f042a2bcec1676b95806e7706a + # via fakeredis +lxml==6.1.3 \ + --hash=sha256:032a0a97eed428bd143c75a11118238546424ceb2fa311cca5f073aa44658dc4 \ + --hash=sha256:05f5bce9af14fd1506997594bd81cee6d9c6b58ea80a39c058327aa6371ed9e9 \ + --hash=sha256:0794e04ba343852c6d78e996c58ef4b8e579b4ecc72f8df0d4058bf843b4c96e \ + --hash=sha256:0ab2467e405e748d93495fb5568e74044802b8d3ff2b2a1607c3f78c6e982de5 \ + --hash=sha256:0bf5a3e397df2ec4258eb5eea4c1ac6cf013ca1abd04a176903bff20a70021fe \ + --hash=sha256:0c0710ac085a157b593c38fbcacd950f15c4afa8e2057527185875ab302752bc \ + --hash=sha256:0dee106e9aa97fb00541b1ed7827070564d0549c3d3fba8920e6b20fd980f748 \ + --hash=sha256:0f17d83c48ee9dfd96abae3ac3e2108c76d2fc86ce96355e37b8da9f7f4ecc08 \ + --hash=sha256:0feebef8d0521188d0157f758356072e840173aa61ca45b8b3f87959ac283dd5 \ + --hash=sha256:13a620a3fcc20023f9e6ed5c383e00e826f1c2d5db554df2f67240760f9118e8 \ + --hash=sha256:13d22c0d57355366b393936acf6b98a5e0edeadddd3fccbc6a846c50a76b8741 \ + --hash=sha256:160fcf381f76c3aeac28a756bec44f48942a8f7245a87aa28e3a523b4d90cd87 \ + --hash=sha256:16148acd77ed1d8836a56db883af2f5eed720f9723088110b16a0d08582130a6 \ + --hash=sha256:170773d8a3cdc76259065523ddd978c44f9806e28605f08812e8f86783e44ac6 \ + --hash=sha256:18293f8a8d8b6a8e71ef37706b659e3846a4261232158167b1ddf35f6994f633 \ + --hash=sha256:18a4db52b5a7b53a3540b0b0f4123319334621ee8083d496de314d0bf06ff59a \ + --hash=sha256:1a635e837b50a1819bebfedaac5916498ea024120969da8790500148fb0a894d \ + --hash=sha256:1aeca87830c4fe649dcf93fe2b059525b71c72587f21be4ae4af7103082a79fa \ + --hash=sha256:1b7c37339d7e75cab9a123a04248e243cefefb302ad6db566ea0c77cbcde421e \ + --hash=sha256:1beb0f9909b26cee938df9ba56b15252a84429b1fc30ce6fca161390b9789a70 \ + --hash=sha256:20384c2bbcbf87180c8c61eb60869699c1ec0cd09b62cfd13804022d860b0867 \ + --hash=sha256:20428910dae17a1a93152a3ff2c0441d2f4932992c0797d65651dd0561f1792f \ + --hash=sha256:207dfc3d47cf0e575e643bbc140dacc8863b39abaa1e5307cd64c7f2365b8a12 \ + --hash=sha256:209c3ccbfe35a04ac6d24f0611f9d1cbf8025d49991b14acd935236234d6c156 \ + --hash=sha256:2123e5aa075ac20d23c7af489255efd129cbfe190dbe88fd42598cc9df3199b6 \ + --hash=sha256:21402998e4b78e7cce237d2788841aaa21ac9a4d1574d04dc2d12ee41ae807b5 \ + --hash=sha256:2221e88679d1351e9a40aaee54bc65679b9795bbd0160bc3d5e36b163344eb75 \ + --hash=sha256:22eec57e26c418cde02c051ce9914a365e52a7f135a565c6f0480242aeebab48 \ + --hash=sha256:23c366231259cd75ad06495174701afb3fcb36a92917fa47de2d1f1bd9d95739 \ + --hash=sha256:25f4118c438f96bb466e83108506d03d5c31b1bd2387e83e5b070bda6ded9c37 \ + --hash=sha256:28a23fefdb345b2d4d0ff2860571b5ff9a89a28b6a120f720e8fb0324d346626 \ + --hash=sha256:290f66b97ede0e552e1cb44a0fd8a74f9753ee635b50830a0b122fb72788d015 \ + --hash=sha256:2b9b1325ca1c2a9a2dbb6eb913ae563313f2082ae60b03210f7e83ee80712274 \ + --hash=sha256:2bec13085dc8ef48a3fe62f7dfcacfeda2c785cdf19cc8eeda2bb9ed081da165 \ + --hash=sha256:2cae5d5c90a62d9139c512a0cb1aad1d182b022b5740daea2617eb5bf7fc658e \ + --hash=sha256:2e01125896585139453cab8cb235893644d8815d7509520da95ae3ee8d1c1f79 \ + --hash=sha256:2e62c569ec7531b679b184cbfe335c501c1d13c4b363560013019962eb630e6d \ + --hash=sha256:2f5b2a2b9811b853b39bfa41367c6d78747b8e3e80e07fc5a24aae295c1a4d7d \ + --hash=sha256:302f72413251c03f671e063c9414bed5dc8c927069e5abb69245521e51a4e81b \ + --hash=sha256:32a409be3190b088f960ac92bfedfbef2f86c49ff940765e1548177592d20026 \ + --hash=sha256:33cadd956b667997e4de1635fce9541f2e8ede2038fcde8cf55aa14d571d1bad \ + --hash=sha256:379f8a75cf6eb7eef0af074b55f49ab73b868388a98de14646abcdfa4564bb11 \ + --hash=sha256:3847e71a78cbbc1aff955dbbbaf2fff12153f611d3162c5beaa3395636cbc2f9 \ + --hash=sha256:38fc4e4e4e084e0bd491949482527d406788045c546d4f8789e93fc527b91385 \ + --hash=sha256:3a27ac6c780c8b8a1cd231b58407634cafc1c4cc28cd6c7141362df0f36351e7 \ + --hash=sha256:3a48093cdb058a93af842ede9703520e810b05dcd0fc6d7190a06376c3bfb6bd \ + --hash=sha256:3e42265103fb385d8642a78672edf376c6f7e1d3598a7a4f9cb1278f2f6b5f6f \ + --hash=sha256:3e9a00d1c2c30936f7add097c41afc5da6556c580909104aafd382cac92a855c \ + --hash=sha256:40983eabefd13da003e68170928c7acc011f0d095eefce5871a3c71c9385fb9a \ + --hash=sha256:40bcbd9f94166ffe925811e730607385cec959f42fb1bb7dad83748680465221 \ + --hash=sha256:41096ec0740a58dad03d3ae0c7486d306d20becefb13ceb1649835ab3eb64167 \ + --hash=sha256:415e3a115c0d510e329020012834d1c0aa1c581ee53a218603e38abbc1dea70a \ + --hash=sha256:41e2d428110b408e963b6fb18f9bbf1f5c027b56bd4b498d54556476c0aeb1c3 \ + --hash=sha256:424aa5657141d306ba9ad1baab4b2c0a0719040075ee6c66aee9bb2dea2b5054 \ + --hash=sha256:42632b4024ab24a6b488f559ac851312509888b6b80ae2aa11cf29a646a0d245 \ + --hash=sha256:45222d94ddd511536f3b2f7d9deae3b2339b4ce0f075f1ca25703b07cad9dd21 \ + --hash=sha256:4736e6c87e603146d8949d8501da621ad20c31015060d3fcf95ace2859f3e3e6 \ + --hash=sha256:48542c9acba9ff9450bd18d871d2c2c8787fdb283572b623d206f1b927cd7d9e \ + --hash=sha256:49fbc2682a9306135b7ec49e93f97f9c26689b9b7f96ed2742d8d6497e994d13 \ + --hash=sha256:4a579dfb9c835f8ab47f4b8ed33440cbc75b806b73297208e6ec2a33e903740b \ + --hash=sha256:4b061064b4a2fe8598a466d723d43dbcd5a610a5d5cfe02fb6226f5c17349f75 \ + --hash=sha256:4e11e885e0704be185867fcf71b904d8f65d7d6877bc121f69870b0d0479ba7b \ + --hash=sha256:4f4db7c7e954d289d71878938348b3d91b904a3e8210a11939359fb758a58e7d \ + --hash=sha256:527195c188d7d0af748cd48d220ab8cdc5cb99be3d49ac4d9be7324d8abf9bc0 \ + --hash=sha256:53258656846f5c48996b882fb4b135885e088a3ad3d96b4bc0530f95124d1f69 \ + --hash=sha256:545ccc14fb05485f48b4439ec35beb16d5b5280eb6c81c658bd4707a2a119414 \ + --hash=sha256:5609efdb0d3c95499c00046bc53648b3482ec2175b5503d6e611b3f0555dc71d \ + --hash=sha256:5929d9df5e7e3379183be0e21f7d559618a5b61cb63280df6164019242e337ed \ + --hash=sha256:5a143e6207579de8baeded4eaac9134413200359f1969d636f0bfb98ee8c3c8f \ + --hash=sha256:5a721a98c649855963811b59b55755b30566e7f7fc40bdc9803d66dee9f811cf \ + --hash=sha256:5cffe18571ccc51d742cd08cbb3f8b756de9311d18c7ea98f5d92f37b8fb60c2 \ + --hash=sha256:5d12669a2c419b0e8dc423d23dea24bb82f6f9cb829f32e04674b0ba40322a7c \ + --hash=sha256:5d582042c69857c364e8153de6e18e0da9b7b515a6a8113caf69a6ec8e0520f2 \ + --hash=sha256:61116cec57ed69aebc70f37a545eec095339bb829efbdabcfb97c51e9536e158 \ + --hash=sha256:611a51e61c92f62345a50b0035df6fc0d678f9299f33728826d831598862f59d \ + --hash=sha256:623c8799c17128753c65699f1c3aa32402657393a9ad6db09ed8b98ddf76611d \ + --hash=sha256:6374e9e382e5a98c9c5e66d41b357b470da1c54bce30f17f9dc4bcc58436cc1c \ + --hash=sha256:66299564c046bc7e0cc5de5106601eae907e9fa5904cd68a323380a8502f7861 \ + --hash=sha256:69cafd61aea04ebb3502c93c2aaa568b12931ca0802231e0b5de76bf8b6e74bd \ + --hash=sha256:6a406d0b3cb207b0fa460ed4dc93e866f44f105da0169361cb18ff998a44c7f0 \ + --hash=sha256:6ba4fe5bfbef6811a8e49b3719cde373ad399006c0c1ac184b7297116ecbba5d \ + --hash=sha256:6cd11e7550d89e551a87dcec30f04b1fca32e86b68708aa01a4daa455d8605e5 \ + --hash=sha256:6e1eb8a4cbffd5553680ad96be6680e364710656eced73d1dc90ec489df599a3 \ + --hash=sha256:6ea2f13dce778ca072ccee598bca46a092ce192e8fd907b6c1f0e52c800529a0 \ + --hash=sha256:71532ebf30be0048a45559b4fab15333fbaaf9042f658e878d918ecd0cf09805 \ + --hash=sha256:73fc05988ed20809450474ba760a87c8ad4e455fc09783c02195e56ec634b41a \ + --hash=sha256:75cc6569e86be5785b6188ef1642670c6adbc984e81ec35e224842ecd9eefcc8 \ + --hash=sha256:773062aec2f2e56b2b22d37054123f0de8a22a4688a0c3376c3fe42685f975cf \ + --hash=sha256:7ae4949f212a53b007dbc355884fda122545c5764a54256c9217e419a62a6559 \ + --hash=sha256:7b2bb7d703bed7ac893bf7f40d97b5d9279d35d2ce460624ca28929eab0d5a3d \ + --hash=sha256:7d0f5976aa2701996f759b30172925829867547bb073af0ae67d1307a0f0262c \ + --hash=sha256:7d5a748d12dd9b535e0a130f60dae9ddf0adafbabe61e7864f55c7436c84547a \ + --hash=sha256:7dd624c1eaa629ad44b59a1a0145fdf2d67895592dce94c9358b938b3d075e65 \ + --hash=sha256:7f75b9b9fec2a9c6b18095c81865580e795b1441c429e42d22fcc82a77f40039 \ + --hash=sha256:83e3a51e7933db700a0da0db31849db3a24022d9970da9bb73001e1d0326fd92 \ + --hash=sha256:8499d464de86fab0f102313cce32a9bed9ab1f06ec813cf025cb790964fbb765 \ + --hash=sha256:869dfcd4d381cb0ea87085cc4f011b9171b494ef21e76ad8665f6d5e2d1dc8a1 \ + --hash=sha256:8753b8d51dbc86fd335ee31fcf7f3658e9f5c016d4edfb23f76ad295f4b8c9d0 \ + --hash=sha256:887c021d9a977cff89cb273047c1352997b772a8908a25c21836861f69b92be1 \ + --hash=sha256:88e719b9437f148f7e1465df845c758dd1598618cbea3a2fd1e61a715542f2b2 \ + --hash=sha256:8a330c0ee5fa318c7b5cbbaad882baeca3f570357e7eb25ab34bf31008150758 \ + --hash=sha256:8db38ff3fb7aee7d6a82ae4da2eef1178656fe1216841fbd24870062a9d60473 \ + --hash=sha256:8e49a646acfab83c68974f4aa1d0a2acca9e88d7d627ae0fc13201b14b76d310 \ + --hash=sha256:909f4e927bb051f7740d6367285fc60cdcfdaf0258c2dba4ff5ba7eadadc250c \ + --hash=sha256:90f709b9accab6b2e4d14f5c8718203877a0486bcb3afd74d8b539ecd1e961d4 \ + --hash=sha256:92d96586376fb79a33474797186bf993250152ee5c32650b67db78d54b92e6f3 \ + --hash=sha256:93476b6514b373fc6ca67d26c442784f7807c86f00635bfe79f935c3eab2af17 \ + --hash=sha256:97acecb11cbc411473f15b8d780df06d7a9f3a2aad9aca78364f56640c8fb70e \ + --hash=sha256:97ce49699d87ebf8aad631b55d65b33219a4f1bfefbbf5bff19dc9af160aeaf9 \ + --hash=sha256:9bde9ae026a55b9a192078dfa6e27dd0ca4a050171ab6272e92f97b757dfdf48 \ + --hash=sha256:9e67324961ac9bbe616cce5100514d2e34d88665aeb07071e8b16eac55d06d94 \ + --hash=sha256:9efe56a68179f3adc4de41861c9358931db03837c48dd5e1c78077b84dd07f3a \ + --hash=sha256:a1932d7ce78a561367512c594fe66eac2b2ec9b9264cfd9b5f950622f4a116e2 \ + --hash=sha256:a1cec0f99b9b914d39176347a93b7610dc09324491aee1cbc57cd291a41a1d55 \ + --hash=sha256:a2e3f70673a1d5b82f38255f777d26cd855bf2092b1436c4867464a7892f9238 \ + --hash=sha256:a43b3bdf11e477dc7770609d3477316f974354dfc8425d596f64f471cc8daf6e \ + --hash=sha256:a5c18810318303ce9afb3f95e2ddb54834f96fa699a8600433fd5a93dcf44c56 \ + --hash=sha256:a7eb78ba28b187e1e9203a55c60fcf70df2d22cb205fe6d51b9383d6097419f0 \ + --hash=sha256:aa633613ff907ea91b9b0489a1f0da1b8725d8c6ccec6b77e8a1c9c235044bb0 \ + --hash=sha256:aa9fd1ee2a5dacfc41039ed49ffeeacfa75bafbd255b69f3b578e11897a0e623 \ + --hash=sha256:ace1d2c83b2bd24db5940600541140e87a325e119cb32d5fa9ad720d7e76648e \ + --hash=sha256:b1cc980905221a5d8b3c476330730b3adb40ff80add71ffbdb6215ba055656f1 \ + --hash=sha256:b37772102d44bb6628186accca3a121b1fa3a6b3d97518a8c29a5229ca4c0d0a \ + --hash=sha256:b3ff39654f0ce6ebd4db154211136dbe7e8157bcc3bed2344c87f32c7c6ecb6c \ + --hash=sha256:b477912f42c5c33405a10c759d22f80cf5af043ae02d95b9d8e5e5bc555739ed \ + --hash=sha256:b49638355ea3bebba70da783ccbc630fd72afa16bc46c54474bfa1f9a915bbc6 \ + --hash=sha256:b4fc6b03b9d9d90557274f571ab30e7fbbfc527955536935d96f98b6817a86e4 \ + --hash=sha256:b50343241eb69fd85f7791cf8bcc7b1c4729826b7d59ba2f6b27db29638fa745 \ + --hash=sha256:bc8dd3d9c93e70c3df974a201ac2958b6d77b465d813c51d1f15fa8e645763ae \ + --hash=sha256:be5346653c0b0e34be96869ff9dbeba23860156f89a2896a64c64fb419260cb6 \ + --hash=sha256:c00e26288784460885fe76e4d4b293573e0f791f52e6d60e27b42edf005922eb \ + --hash=sha256:c1b50797ac246bb2942a04b6c0f69af0667aba7cf7535f39bbb1b3208fd5d128 \ + --hash=sha256:c34ca1dc41bd86d9ff830d5bdf4e4a752bba6c54f7d2707027ce0eabd36084c9 \ + --hash=sha256:c55e71a9b1db1f107efb60da49c093689b74c5c31a708e5379e2fd9439d4fbb5 \ + --hash=sha256:c581b1d68b3845fb86c6b2983e755b29bf001461c59fa411d2c26a911b6559a9 \ + --hash=sha256:c59e4265608da6a041f54646ecc0c9ecdbb19aaf14c4c684bb6c2114998cc415 \ + --hash=sha256:c5e7ce578aa8a80910a72a8ca0bbea3baae10100827249001999726a788456d8 \ + --hash=sha256:c66f858b82497173f73366795fc6ee8171620e75a338506d6b2e7bc16f5fca11 \ + --hash=sha256:c6c0c13128a32eb04a51357e56a094e13aa8e6d3d1884de2e9ae923f6915e1a8 \ + --hash=sha256:c9389b3784b56c58d933b5e0aecdf28f901b073ff385358d8a7d40907f6e14b2 \ + --hash=sha256:ca0ec532ad2f5ba1e5ec120ac157769c57f01855b3d8bf37213f5d88abd9ba0a \ + --hash=sha256:cad7617727a96d189bd6f979d0fadf765198c7934e85f4edaba9bf3ad919a300 \ + --hash=sha256:cae82b5ca24b0c2beedb269f6e2a96f466acd926879ab00ae19f1a65cbf9ffb0 \ + --hash=sha256:cc669256d28736f7f3a149df5c380c50ace2692ba3e62203d10656fade4a2145 \ + --hash=sha256:ce1f220114959941170e22b8ad44279f6dee2dcef7591814d01ae805dc058889 \ + --hash=sha256:cfb398886a7eb4c719161c3efcff2a1248febc53a4d8e5072d2d8a87fed84ac9 \ + --hash=sha256:d077f21f4b16f0471353883748f126f62038760397c107bb9fad2ca94dc0dfb7 \ + --hash=sha256:d0c5c362bc94f1929dc7e96e715bbe7bd17037f802e6d8f0d1545df9133c0559 \ + --hash=sha256:d2765c18ce303149ee804b1f3dad11232726dd0a702d73a15cf19179ac8cc962 \ + --hash=sha256:d44442effeb8781f392340c5dc8c6716fba41dbeacb82fd4c0f09026fb5ff682 \ + --hash=sha256:d85dfab42dd672f87a7f76e9de7172962aee69fa12044f0d6e1a23cbd53fb80e \ + --hash=sha256:d97c5227621af74b111882a290b10f371780a38eef9d9e730408fba2259b52fb \ + --hash=sha256:d9a0d12846d6ce434fb3857918eef4315ec9b4769deb020c75828798614bfcfd \ + --hash=sha256:d9b3e7d71bf6acff341233417abbdface29c647e3113892d9aaedc02eb4aa2bc \ + --hash=sha256:da707f14ea3c35ee463d50acd596d6488e4b2b4ae7cf77a5bf93f55c023d63e8 \ + --hash=sha256:da85db328e507da922d586c3c7416ec360ec22e9cd9e0700691afacde0c81f53 \ + --hash=sha256:dc205732d593118cf701d986f40e9de7801bb2e371cb189ddbda9b7348f4d97e \ + --hash=sha256:dc3a44689eea43eab836e5c98a8ab015dc2419987d1ea6eafc7c590cdff86bed \ + --hash=sha256:dd5e90f34cffcfed97f36cf066325773d2b6021c60c29942e53a18b028501b1d \ + --hash=sha256:ddcf547bea2aee967d6a77779376a45e77e610e8465147a1f3d7e20d539d6e32 \ + --hash=sha256:e477aca0bc0d19f3b4ae9e4f2a1cfd687c31bf772d78734910658186b40b2477 \ + --hash=sha256:e8b17e23df3e827a69d25af70990ca2420e92668aaffaeeb3cd2351d7916a023 \ + --hash=sha256:e99e09ab7741f1281e2677f4c0058c7f5267d182530b09c87e4f6aa26adf3887 \ + --hash=sha256:ea2c01cdb16dc12156e455007c406dfaaece0c89aa4ba0e3b47586779f951d41 \ + --hash=sha256:ea6b1e9105b4b24a34c722432d9fb578f9ed83af21fa1abda639011e0f22bbb6 \ + --hash=sha256:ebd054ad1737a68fb7c5c073d405cef2b88bb824e294de3b4a4e995b47f0e376 \ + --hash=sha256:ec295280f4b37769256da025acf5890370355ac589c27e89caae0b5e9eedc702 \ + --hash=sha256:f6449672f9c93316deb5e2839e18931f468670e44d5bd9b1301a5a9655d45c07 \ + --hash=sha256:f683dc6300317700025e41d89a43e0276692ded16113a3c43eab704d605c58e5 \ + --hash=sha256:f6b9d2aad499c769ee8287609ab0e6de99d8bcea99c6e6c2e64945259fd52fb2 \ + --hash=sha256:f8b9c8ceebae6387d0dc77f7f4dbbfbfc962dba2efbfe6877486075a480726b4 \ + --hash=sha256:fad67b12ffe0f71e02b4932b04883cbc76a9072bbd30731409d3523cf058b011 \ + --hash=sha256:fbfb70ba01355251faf6b293171df49f73a88a1b6494db109ffea85442574458 \ + --hash=sha256:fe91993149523aa59941b9e3c90e2eb45f57ad014697aef6c8b13339a59c019e \ + --hash=sha256:febd35ef45f603c2d74b74655efdbf45e14f55fc0aef4ac82b663ca829b283e0 \ + --hash=sha256:ff88a92cafde90888511242d1c54afcc1a8adbb6dc0a88fa7f87e29e92400d4a + # via python-docx +mako==1.4.3 \ + --hash=sha256:723296007c870bfd6b3f0c3230dba7198096e5269297ebf5e4eff9e7ffa39d4f \ + --hash=sha256:cd6537fe88d5fec315c55c2f8529bc4ce7a9a352ad7db3eeaa6a66e2dd4ec37a + # via alembic +markdown-it-py==4.2.0 \ + --hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \ + --hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a + # via rich +markdownify==1.2.3 \ + --hash=sha256:1a176f05522c8a2cb1dd3ab9d307dcdadbed5c26ae717855bfc42b3b6d38d937 \ + --hash=sha256:a189a0bedfd14009030fde5f85bb6f77c56897cb839b5c25315dd7d4e3e290ba + # via browser-use +markupsafe==3.0.4 \ + --hash=sha256:007e1ffd9bf65bb6ee96df7b258fc632a4868dd5566037986c64781f35a36e98 \ + --hash=sha256:02fa4acbc6a3fc5c693c34d4dd8c1130b7fe99cc915181b0ddd6f72aeb296002 \ + --hash=sha256:03470d1a8268e692ecf79ecd565593e59d44219377a7ead61f1f1b94c1f7ff6b \ + --hash=sha256:04e7902ba80ee4bac1d50a549606527a1dcf0476cd81403db41099d3b60ec653 \ + --hash=sha256:051417f74bcaaefa316276e0ff723f541616ca51043d070da00249d9bddd3e3c \ + --hash=sha256:05295589e619b9bed252a86b532b8e27350abc372d18ba89b59375325e91ec1e \ + --hash=sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc \ + --hash=sha256:0764a13d34cae40db7bbf3a09b7e9b491bf4603e20b263a7a9d6b8e324975d0a \ + --hash=sha256:077293e425f28ec737dbcad442a71752e28f8ae27cde3d68acd1fb212091cd92 \ + --hash=sha256:0930db9bdc62d22944e10b066448bb65dc9abe9112880c7cab8da54db4284d5f \ + --hash=sha256:0cee7cb0f9a1b6892ea482237d9403b3d1b4603aee057d0ff01f0fac2d019a97 \ + --hash=sha256:0d9c47709875fdb321452056622e930c52afbc07a7d780762fbb8b4d91ce6fa4 \ + --hash=sha256:11935df9bf455ed0c04eb87bcd720f02b1fe5e02128a9430f23aed6f93336fc7 \ + --hash=sha256:12a606a492de952afcb43b59a14aaaaad120e708d3663dd0fdf2d738d427a691 \ + --hash=sha256:14bd2d845d62ab678eaf81da89d7b621b51756c72346745c1a594c09d49207a2 \ + --hash=sha256:15ba9e28640feef770374b116a6f019c21f52404aeabe516aa7f800587b98cfc \ + --hash=sha256:18a801868a884f216e784d7d14db2a4077143ce7610440aee2ce8f734e7cfcde \ + --hash=sha256:1c0df495a977d10460a94941799c72d5b5ab03d3858d949b55b5a66c8f371c99 \ + --hash=sha256:1caa2fa5a6184fb233153b35f654e6687bd555476f6170f29d8ee9be1a8b0af9 \ + --hash=sha256:1e1451fab512d1bcc3dc26988ec1edb0b82c2db909132872cd9356070a6b63df \ + --hash=sha256:1f1f9477e174582b0a1b583d60b66e1f2cf5d3fe12cee985e4aedf44766600e5 \ + --hash=sha256:2628d3a8cb648ecebb3c5d6b0a1052d400e4d8b7ac0fb786be8d285b50040d17 \ + --hash=sha256:26e9867520db70d37f7fb421a7f0d8adb40171011fb84ce869afa1a83370dfa8 \ + --hash=sha256:2a6ef68ae94aed8721934072b27a3b654ea2100b97e4ab864cf1489c90926fbc \ + --hash=sha256:2b2b1e18af909b448bb3cf9e3433366f7a8726271fc214e8b10e0f62a78c724b \ + --hash=sha256:2cb3dd71fc6be918ad4264346a8ed69485f9b7ed7bf35495d8e22807cd6b8bea \ + --hash=sha256:2d1b7d9308288661f56672b1b157d75fc536714d3638487bbea17b6318a78248 \ + --hash=sha256:2dad610540cb2e6272855c178f08ae9a1c7ac258a7fb71660553a5f104b42741 \ + --hash=sha256:2e5a7cd7fdd14fcb1ae5d7d8bf23d24fbd1daefd1fbca2580132e1ea75f098b5 \ + --hash=sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6 \ + --hash=sha256:340cbb1957ba99929cbf19a75626d36ba1ae21d1730b287d1cf7f824a20c4fc7 \ + --hash=sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1 \ + --hash=sha256:353bd63081912ab8cfa6a0c7d185934cdf8426f04c618bba6bc4b394f2069b67 \ + --hash=sha256:387d8cd30e69b3f0a72877b9ae717033396404e19095b17fe89753a981fda44f \ + --hash=sha256:3882fb412298575bae3b9c46868251f15cc69307359f87bb1b382e53d6e5a2c9 \ + --hash=sha256:38fc55594dab834470b6733dead2ee9e3f657fb0608c769dcafa0ba5ab52f45c \ + --hash=sha256:396ec4e65cc889f69786b3b89478b471cee5a3bcf468b9d9bb03e1a30fb291fc \ + --hash=sha256:39dbacefc411633db5b4378b066a9aca70a3d7e2922c9e578d825f844026eeba \ + --hash=sha256:3a93d9616ddecfb393727a0041a562cf0b15a244e20f2bd25efc7949be4c4f17 \ + --hash=sha256:3d23795802fc8bd72534836d64489bbf0f67c088959091bdb22e10735a5107bf \ + --hash=sha256:434139499bb20b502ed3baa1f169e618f924a97e7a777fea1a49446d80106cf6 \ + --hash=sha256:436e3ffc6310d3c41878c601db29098102fe5d8a467c49da4a4125254e0980f2 \ + --hash=sha256:489505b03f692c3f376394e49194fa7a7f9e8558d6e293a7056a0032b0c38163 \ + --hash=sha256:4a540e2d3192792fc84eced57bef37851ccb2b41f73291bb17408eea77bcd278 \ + --hash=sha256:4a7cdc2a420ca01058182da4253329764d4bfa055564d1eced90e6ba1e8b1d3d \ + --hash=sha256:4bced6e2a6dba6a28f7dd3c6ce14df1b2dd495923f16ea484cad03decd463b2b \ + --hash=sha256:4cf3468d5ec187ffffcaca8e61929a37448f215dafc1386a12c750a72fe53634 \ + --hash=sha256:4e2c4809c14559aa7ef426f27fb35afbb38104c349a903bf8f3600456764bb38 \ + --hash=sha256:4ed644d75aa94a2baf7ec3a96eaa160ea58c742eb9d27c6506053c5c40fc84ed \ + --hash=sha256:4f6e0852a0283b1b1fd776eeb7b766a5f440b3e2bd31ab51af3b400585f3965c \ + --hash=sha256:5066b244f576f91afc8ee3ba029a89f99d39c79b1853fe9d39bea9f0afbec148 \ + --hash=sha256:5086f9975abb1ab531ee6afca1761e4b59a19b446f3f6522ed776963228cfe5a \ + --hash=sha256:50b5bedc9ed8a94fc8857a42ef4f84a81ea88f8d4f05dc8705fb23ee6d8dcca7 \ + --hash=sha256:52704c5d36eb6dda8866493decd61111fff86244c9b1ad225ca01b9e91e5970f \ + --hash=sha256:55ffd6ce583d97dc71dc92e930324c8c0d25aea7e3ade6ae54ef77cedb096811 \ + --hash=sha256:569d65055d367e3dcdf30c3f41119467b73d9ee9faf332bdf40402644f5ac08e \ + --hash=sha256:57f9947a7e57a081c1e3e0a2dd0d2dcf290a4531450e6f611e30084c222a7295 \ + --hash=sha256:5989cb26b2e1efc6a42216a9f6b5ee495ce5ace2e5b352a9af489976b32d1ee2 \ + --hash=sha256:5c22873ad1f0532ba40fa1727f3c0fc1bbbaab6d373d4cbe3f0dc74b2e2521c7 \ + --hash=sha256:5e8b3d0b18fd623afa12ecb2ce8d8becef69f9b5440c6330c7972200e0bb84b0 \ + --hash=sha256:61631e08084be9e21a8967ec3139c7616ed7c5e9368e05c86d1b39562c8a57b6 \ + --hash=sha256:64511c54db4e4987aef4c41923235927428729e8174c5dba488429be70a998ed \ + --hash=sha256:6669c1bf34080161ce49c589cc512ef24d4c704ac9d2b2d3667f519c60418378 \ + --hash=sha256:672d207103e6b16ca098611b0f9efad6bc00afd47c03d6ef62186495ca677dc0 \ + --hash=sha256:6768d67d1bce64270e0fdc2e69309d68b9b18ae56ddf6c711d168e9d051c2cac \ + --hash=sha256:6a45c3d514f2436064db00d7fc8778d888f0236ebfed649b53d13a59e69ad51b \ + --hash=sha256:6bd9e1788e15bfcf6a9082de42e30387e7b85d211ab21e57a939bb8cfaaf8d96 \ + --hash=sha256:6d2a9efe686f9de00d0d1ea32a4a5a86d558a2277501bd78d964214eab625e59 \ + --hash=sha256:6da83a088f8ef93b2d483a8232a4dbf4d69d3d8496b568a03c56becac43e1808 \ + --hash=sha256:7018d4af1cd272e847aa5917983ab5e83e4f6579f9dbfecd4a79c0ca80b144c2 \ + --hash=sha256:71f88e749ea29f67f21f3b36433c1dc54c7729ed2a6d9e2da2e0d9e0d7b224eb \ + --hash=sha256:737c9c3981998eba27f11786f84fddcbabc74068b72a4a1f454ea02094b57b65 \ + --hash=sha256:73e77980c7207854f00fc4e71fb1626868d5740ab4012623d55c7a99ad122a72 \ + --hash=sha256:799c39bdf5e2f1292fedd3009f7b3c9e760f10b2420cb9638d56920840ff6db8 \ + --hash=sha256:7a83aa6e4805df46fed18e989d3d16f86ef60cb50bbc8d9ce3a6be89165fbf6e \ + --hash=sha256:7d3391b2188d18737cb2fa147028b1096236eaa7e156446c650a489fa2cadc91 \ + --hash=sha256:7e1636da3d8dfc220b6dd10264db5f2b165e4888c4518594898fbe381049af8a \ + --hash=sha256:805c8b84534fa10891890f0e4be39f3a99e94615d93e8836bf9fa1fdca2feeb2 \ + --hash=sha256:811d02d5122171c1941357efd8f9bf4ffe907b7f0a1a4e729a880e4be3f46e3e \ + --hash=sha256:8138eb83940ec7299024d92d4dee45f601b9e6c5ffde9d25f4e35e326203c707 \ + --hash=sha256:83b3944fea42a8400edf92fd1770fb8d0d4f7de651353bd2d8525a92dba69a21 \ + --hash=sha256:849dd2bb0e5e4ab2b71c7191726a4a8d5aa8a610daa584728cbee0b710ddc4ef \ + --hash=sha256:8698d70a8081ee8c090dbb394768b5789a1da8b131b5499f89d071dd3cfaf6be \ + --hash=sha256:8781a792a070cf2bd1b86d3aa943894115faaba6e88122a7bf32d62072742453 \ + --hash=sha256:88d59b473bfb03259722600839af9bbd7fa13a2eb514beefeedb95997882f69a \ + --hash=sha256:8909c2f1c6dd65e054ac4b573a91c8384d1492281e55d82d159d653f7a13adf6 \ + --hash=sha256:8965520ac587c94a4ac48b729be3d8b8de00af39699b17585dfb599babe77977 \ + --hash=sha256:8b5d563170ff8ba3181caa967c99a3c804d1dedb702c7cb93a6a7c32247da978 \ + --hash=sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581 \ + --hash=sha256:8f0fac8b13d14bb06c68195f849371924ae53dd7b1c00fed24650f704383b692 \ + --hash=sha256:9240187afb63d2f9ddc3e032c670356fe941f6e20662ea168a5dc3f1f317e1b3 \ + --hash=sha256:925f929d6b59a8b3f8b8c6ac363cd0af7eecc81efb3071770b3c6717c450a369 \ + --hash=sha256:9348cbb300d224fe3b89793262cb093504d4ae927004468463f745188a193e4a \ + --hash=sha256:9388003072b95f2f1e3fd908604194d653ba21330d811961a78b7da1a77e9e36 \ + --hash=sha256:9438a2648b2195980cb2dd8e53ed7b8df91319e2d0b70ae61a9e1d1bc8d3bec9 \ + --hash=sha256:94e4c421742086aeee4c32a506eec8859d7634aad943f7e6aacf70f813478768 \ + --hash=sha256:94f5407f7bc64fa6463906b896f9904beeeb7dd8dc116ee8e9056c8714ff9916 \ + --hash=sha256:971a3bbb75d97ae4e2e8f7d4834236f86f85f0c85e04ab2e191db1123b04f80b \ + --hash=sha256:9e227f3dbe6bde7491cf0a9965d00b88c6b1a4a95d11480ddf88bb96d397c19f \ + --hash=sha256:9e25feb9e330b63edb0278a0acdf85e50d0cb0fbf49c3084abbe4e24ae195346 \ + --hash=sha256:9f098115c247e11d138ab83a28fa0323c77015007ea2df73ba5fd714dfefd67c \ + --hash=sha256:a18f38cafc329bac5e3c2b96c765b4c96d3d103421ed22ab7988c1e3fce27464 \ + --hash=sha256:a4bbd2d87dd233b9fc5812160c3d0ffbe42edc22a26ce0469f58479ede633fe9 \ + --hash=sha256:a5fcffb37e602b0b3c1638a97746b9b96125caa9bcf6fa41d337a9261de231ee \ + --hash=sha256:a8e9f292fcda89b324f2f5c91d13f1424a153e40fc2756f38ee23b15835ff300 \ + --hash=sha256:a9f54054101545a9a9cccefddf54316aa6e4491611fcbef9e91b3b6bebec04f6 \ + --hash=sha256:aa2c838cc024642cc04c6854232f32b43e5e22833dd11119c1766c7873b8370d \ + --hash=sha256:ac0c7c9f1609b0c4c114feb1d7a3409564c7fb77e360bed9e97e5d25dfeaf868 \ + --hash=sha256:add96447a86d205ab616665d53b2950ee81083757f56e6ea833c8b2917646b46 \ + --hash=sha256:ae9dcb8fbe244cb82f8a6458b455b927a03685e383d9bacf1ea5ce180b96dc97 \ + --hash=sha256:b4a635a0487774f841cb1fb62e907e7195cc95bc761e053184b8acc3ceb20733 \ + --hash=sha256:b4d12837e0203bbace818ff4a7461afdcd78bcd782351cea148139180d7bcffe \ + --hash=sha256:b61687d0828e72bf5cda24a2690188f37170bd31c9359ac97e4e66569f120a16 \ + --hash=sha256:b807e598953730f82e4eae3bd30f6a122cf6b31c398c6b504c0e04c13c170429 \ + --hash=sha256:b8cd1f918b26fd7b1832ece557cc18f2d8747309ff8b3f0ef9d4250c5ad67a39 \ + --hash=sha256:b91cc9d336957239ff200f30097e6fea2dc6d6fb3c81e853eaa09eac904fd894 \ + --hash=sha256:bd3ce56ae2cbae3ba82b683bc425cd7e48d2ed8b10f3e818186b6f5646d9271c \ + --hash=sha256:be6cb0c799abb0e2ba3e618e6d28ddddf7e485f6c2ce938dfa237daf3905072c \ + --hash=sha256:befb4158af32106b9a93db8d6d1d1cbbd418c0d5aca0cabb7b1780abf0c89169 \ + --hash=sha256:bf053da3c97a4bc5ecfbb218cdd2983febd91c617be8367d139882aa11e490aa \ + --hash=sha256:c02e8f18bdedba082cef725942ac823b9b60656db07f7e265cb31618dfd00d77 \ + --hash=sha256:c1bc67752d5f21013cfe430df4062441714eab79f65a6a05e01505957e9c35fe \ + --hash=sha256:c61750fadcd119d0825bcb7d7d675dd264dcc89cc05292aab5be68ebdbb374ad \ + --hash=sha256:c90d5b3d4e944e065a301d741b3c1d784f6bd1f503aa68b4967e32b2ba313d85 \ + --hash=sha256:c9a7f43c0b202b334cc9184af09bb8f21d3a209e038efaf106936fb69e6b026e \ + --hash=sha256:cb96e6e088d6cf71c1ea977510948320234824cf226e32f6f6e044f7a9c82b34 \ + --hash=sha256:cf63c214fe879a65e69a386f915e36104fc84254ab141240f8854602d8e0be2a \ + --hash=sha256:d1aca03ede943eb80ab3d63bb082c84b7aab85ea83bd0fd0c200260945fb49d9 \ + --hash=sha256:d2e56fd3b00222722abfb3f5f0759ddbae4b90811b5ad4343c64030ad1bde70c \ + --hash=sha256:d5f93ebbeb8032d47e349328ec8662d973d9b05a70b3c35df1f91fe419b84749 \ + --hash=sha256:d882a373d8093c2941e01291b7ced96e9cbe4781da9a7751ca7e6c70385e5214 \ + --hash=sha256:d920abdfa61279ba1a2ef9484aab07bf03331f8c08a10120fa332353d06e6932 \ + --hash=sha256:da2af0d7aebfc2074080d72efa6ab8317c62481ef1f896f65d9999c1c01f4494 \ + --hash=sha256:dd8ea6ebee7aedbf7c749fa80521d9ccf1ba473e0d1e14805caafbaad281c889 \ + --hash=sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1 \ + --hash=sha256:df1ae86ff54725a01fa1a0510b914ca53a161b7050be74f6204e24aded5971d0 \ + --hash=sha256:dff05cb7016dff1e9fd68f4122c127b65dfc59de5306cfb7ad92f956f230bee2 \ + --hash=sha256:e1a622f13970d81f95d0c72f9dc090dce9085fccfa4c9f2174377ee32bd15786 \ + --hash=sha256:e49fb0d1ce92cfa0cb198cc5b1b11cdf9d0638658e2a2db2687e39db7c87fc78 \ + --hash=sha256:e5c802729725bd07e2bc3ab7b76dc7e0bbfc53129d8f1eb1c002c24cf774717e \ + --hash=sha256:e841068dc0be4cb6dfb5c890eb88cbdcff2f4a332393c7ec94e8e618bd32c1a8 \ + --hash=sha256:e916035e3e9930cbdfdd10abf48861340221857f45509565898e012263f7b289 \ + --hash=sha256:eba154571c16e032112afac0dc2dfe9e63c2ceb7aedd07bb7eecf2ce26d4dd4c \ + --hash=sha256:f03460ff076f70ab595bb45a0205ccea1971443575b6920c52e755dec2b3fbfe \ + --hash=sha256:f0ec3b750b59375eab5b0fb2b9254810c00a3375be6d789899f1055a1d556237 \ + --hash=sha256:f291bcf42ae98eb5107edb162c3c998b4a89648fd8e99ed4cbd12705292788cd \ + --hash=sha256:f61efe1d2fe0de16158a5fe1d1cf3c14bdb6aecd54d8938fd26512c525c1f624 \ + --hash=sha256:f68edfc67aabac33708941f26f22a7b8e9f81429bc0cf249fcf7d66b23af8d19 \ + --hash=sha256:fa95848c929b6a75f6848d3c9793e59db365ee436776e57db835cdbfa79ba977 \ + --hash=sha256:fd9f8797427910198f95bced71ddfed61130d7e349213bfb8466c9c99e2c46a8 \ + --hash=sha256:fdb4ca07ab75ffadab4a8b135ad59cdbb3156b99310f3d565370da74a15d6bd3 + # via + # jinja2 + # mako +mcp==1.30.0 \ + --hash=sha256:445414625fce5c295faa505bb11bacece661ab6f4028d57c935db57820b7a3e4 \ + --hash=sha256:666edb5009503e1047c9d60346a756f94b261f05cc2625f23d41c728ffc484d0 + # via + # browser-use + # fastmcp-slim +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +more-itertools==11.2.1 \ + --hash=sha256:35a7377edd1dd6608dcb2cdf534ded55ea32d49448875ad042cd3f879fb1ded0 \ + --hash=sha256:cbf08fd0af284dc69718b9b76a8e6203df624d70209ea511e4219d350c856f63 + # via + # jaraco-classes + # jaraco-functools +multidict==7.1.0 \ + --hash=sha256:0179698c3c913eb64f32397083747fad20ed0f0a2b7469a08cd1a8a95d14d90e \ + --hash=sha256:034b0dc1b7fb8279599c5d8563f86abb4d2454735b06544ecab23c54572ad2bd \ + --hash=sha256:05d12b4bac53abe0c65f3163af2b45894e2e1c0cc55493ac784d52a350047d88 \ + --hash=sha256:0604ff025497a050a2b2dcc4ae0e5cb6477c525e57b89825152c707e88d74d28 \ + --hash=sha256:0631eb5f49f67de10bbdc3f64141326dbc62e8d319900966648381ce0845d8ca \ + --hash=sha256:0747a83e7ae617793181a4763ee8b84863cec5c0bbbde70c4394e4c0276c36de \ + --hash=sha256:08834fb8b20e1a985c70e8380a10940234b4162de62694458727330376e58b33 \ + --hash=sha256:0aa1ba3ff7cdda05a1242490612976b2ae1c90fc6200903ef8f53815dcb35c5d \ + --hash=sha256:0ae91de396d5c4ac97cb24dbada3d5c91a51454781e0a70476b008f4e879e4f0 \ + --hash=sha256:0e79ed92b1dece6bb57e9b46effd74d7a5d3d00187c85466d880ed184239a698 \ + --hash=sha256:0ead852a5e906a43fcb6784eeac480f6a67919a51d480c1f80d32ddf9d615475 \ + --hash=sha256:10202ba98cfb3f7eb60da7ca87a2c458a69b7d0d6e4d4388cd6773ebbce89085 \ + --hash=sha256:1101aea5c3eb1d26e090b931c693488af0db9f3d52e68be8d4cdd807dad9841d \ + --hash=sha256:128ea4142f81a79d430f3d0eb55206093e5eda03a12abbc7b03c34748ff6116b \ + --hash=sha256:1348ddc076251cd542f4a99ccda4b7c1f8444e8ab489d3541a978ca5901c7c1f \ + --hash=sha256:1401caec21fd7f002e79ab6806bbfd1f54bb3de6d5e12bd91c6685dce16ad2be \ + --hash=sha256:14b1ce8579a43dfc0e592d93fb1d63dea693e4977980ac4166f26d494cc7a358 \ + --hash=sha256:159976f9c40f96e3fe0952b708846a43a76bacb114e9cc828816f5080bddd5ec \ + --hash=sha256:160bdb3520fdadcaa21e1b98aab2e011265070814ecab3804eb61674becbd400 \ + --hash=sha256:16b21164797bde6f417066d02775975cc2e15ab8abf80efa55fe85e0b4894020 \ + --hash=sha256:170ba61761f59ab92afcc86ce5534a3f3d0b07c38b339b950a83213f22dd86ec \ + --hash=sha256:18a447d46a3a2f1e61b365cbf5627db7030fdb707dad70c4f2760e5144166ecc \ + --hash=sha256:1df055e51fe7491120cc84f3362bd43db186be78d0e4c476acad45e435af9ffb \ + --hash=sha256:1fed3d721f75c25a9fcdd0e362af53f4b20acbcdc63081112f85419ba0ce3444 \ + --hash=sha256:2128f3358335e0c83688ecb40c19d9d6606cd60784dfbf2e24e980ac2ba87b0d \ + --hash=sha256:23f6d325241b0db006ca2841309ed17622137e134930a740a8f1331ec4404791 \ + --hash=sha256:248dabb89b5aa90b2f7e43e045f048f7e5392ec77b6446d80853ba7117d7bbdf \ + --hash=sha256:24ad4921135a1410d95b1f1504f4901e1c64cea680014ce2c3c7a825f4f259fc \ + --hash=sha256:274023bf952f849e0d05eba28a4c1f65f9796430d2b09ec16539386c0f76554c \ + --hash=sha256:2a964dfeb2aba3663f0536c809aa1ff385f065e89fae57e883fb7edfb4067c2f \ + --hash=sha256:2ba6611fc93c4b169d0e0ea376ebf4b8a529933d1f5f2c2ec7d8f8b93ef58ec2 \ + --hash=sha256:33376418ab2846b931a72b36cfa16810befc4f49485d0b3f4dc054a4d6d00038 \ + --hash=sha256:33389fe084e5426d9fd85d7d9ca91a29cd0d88a83c7c96e411aca49a3f9967bc \ + --hash=sha256:34a35be8fb82d37087e8176aba907b9459f03d0e293c80f574c6337a436f4eaa \ + --hash=sha256:34d2ee98e15d5cfe782a431bc913fce3b58cf3fdb34fcb437aeb275cdf9007ab \ + --hash=sha256:35534b366410a36bb3d6f788691e37a76e4d1da48326b0ada3e5032580dd76af \ + --hash=sha256:36b14886aa3e0b8786ecdaa196374422c7b1c1dcc8764d02b2409f74d47914bc \ + --hash=sha256:379f477b98a1e9a77ddc3ccaa8c709d3fb4a288ff54b96e171e637b55b4adbae \ + --hash=sha256:396ba9917fe489ec3a5942ae3e29e91324c8b9956f371f7e124c971c71379e7a \ + --hash=sha256:3dbaa7f7c2f0ca8578895fc61fb8c8e50ebb405dad8982f92f4343285c7a3fda \ + --hash=sha256:40f586bc8a084a3671ddcae9e5fbd3228a596bfb63d9f0380f153f9a65b69f08 \ + --hash=sha256:41e0c3350d08994ee8640c39884e16514e282f70ba40f5b2299582509a327774 \ + --hash=sha256:41ff3202cc23c800507777df5a4805b402f262b31008c60fdc652aeb6db2f278 \ + --hash=sha256:439a19f7fbbff232ce96682c57e27030b8ac3a4b8121484c94f04bf99d08bfff \ + --hash=sha256:44f7e5dd83a615636b80182bdf446ece57ed61d5d51854acc5d9840631136d4e \ + --hash=sha256:46d4af0afc6eb9867b3ae50605787c80b868e2f52eac3801246034925fe578b8 \ + --hash=sha256:4b5c41e44da74383c924cc5d75ef0a268f301d69305b3c42bd17af685d55e412 \ + --hash=sha256:4b87ad54e8d4adeb0a1f04889504d6ec7f04fb02609220810f51f1b6c66bc1cc \ + --hash=sha256:4cba2b0b9235fe10e12301d6b4cfba0f353fa668d635f6e988b03623c2cd42ba \ + --hash=sha256:4e11e7299079718c78f8147e7206c22fe35bab4466d38992420795288a0b8096 \ + --hash=sha256:507151e1e3dee95e9e8159e329aed4f75aa5205ecd6505a4f6be546890eafbe1 \ + --hash=sha256:50acd7ee7096949b04482cd7720cb6b85eb9cd9dd5d7ffb6704bfda250261a22 \ + --hash=sha256:50fdfcb03be719d9573597b095b1175d2e9d0b30d065791dfd9fca727c499442 \ + --hash=sha256:5129cc1f5fec6888e2db0be936dab67242e32c738811c8769aeea93aab4257a8 \ + --hash=sha256:51d7f33be9a4a1a2801430846d72841deea0894eae8381a07e7d90e0f71b3c4b \ + --hash=sha256:535173fbcc3933d84f9929d49d7a59a0faec259ee07d07c34c7d2a980b4e3683 \ + --hash=sha256:53daa47dd176db64bb35170e3d5d0ae2388c060121201883696278f055a0e70c \ + --hash=sha256:542429c796430de924d03b68a6173bb6d79d5c4967d4e9a18de3e501cad55593 \ + --hash=sha256:544f2642a456fa264614e975d921540ee8c3b368b04d5aa1ddbec33241b13e08 \ + --hash=sha256:55392202cb374dd1a1f89a8ce1586644870d9e936752059d053e576acc50bc89 \ + --hash=sha256:5c6455f2c11daeee40665c67494cedb426f67dba7375710524071c0c56d739a6 \ + --hash=sha256:5c8074ad4d67067c87bd0663dfda654f786336078c8fd7d2f6c1aa41de8494cc \ + --hash=sha256:5c93473d0d7cd9bbb370973a9679a62f381c7050d7dff4ad6aaa92e8650f5a79 \ + --hash=sha256:5cc58ebb731200ddb64d55f1b345630fb5f7a8138cdbd242af9dce964a7cb03d \ + --hash=sha256:5d19bb1ec12e385c09215d5d53a243c060c7e8a0aacdba16d933e22902ee380d \ + --hash=sha256:5f21fda91bd6c34455bd5c312e42aa1334da46cdafb4c533ecd01e0f7f19250b \ + --hash=sha256:5fa1484f74d011addf2e5f5a0378ec41521989839a05d6051d8067d8ce732423 \ + --hash=sha256:5fa296f14068538fced53c6eec86520a2ef3d3d27a0fb134640d03e067986d5f \ + --hash=sha256:6120aab922bb3e15800b6655558cf8e0a5cc79518e954d457f064e5b3d5e9bf6 \ + --hash=sha256:61a4e5d81b8d4e4ad61964b230129e7a2b914793d96289029078fc9009f074ec \ + --hash=sha256:67fcf28db77b385820881521db7435e9f1c607cfaf07db6eb78aa9d1146bde86 \ + --hash=sha256:6ab323f0c5490abaf35a78563e1043c7a772eb86d93f359ecc0fd286d1cd3807 \ + --hash=sha256:6ad60de1f4c702448fc8f1449f05e810f6b7957c08a5b3950c8a792dfb13b50a \ + --hash=sha256:6b7cd1cb0b363cd43ebf499beca26d201dd8b89eee49fae60205c82ba13ee03a \ + --hash=sha256:6c2144785e42527404bbd5cfd11981fee4abe59a22aded0e498eb711a831d3f3 \ + --hash=sha256:6c9fd50f636a8fa9cb6324cd3eac962fec2bc5bb432452a3b583583a1059acfc \ + --hash=sha256:6e7f70d912a589e30290ed926f90ddbc3160998359cbad7c9ede1bcee481748c \ + --hash=sha256:71196ebb8d523148e5975396a444de02367f204b53b14e26794c96b2be0ed742 \ + --hash=sha256:71acdc6eded0f4b86b5e16c96314887cf2572a8eb5d8038b78583d0c0eb3aa1c \ + --hash=sha256:77024596b9046572c4e90b34c1ff212346756dc48933f90c53cf6e233660788d \ + --hash=sha256:7a90453a79423cd7145cc08fc92322dcd7aca4862258f533e03f473226d4b835 \ + --hash=sha256:7b25c335fc53acf29d4d21dbc19fe39d2824201cdda0448623152cc5917bd259 \ + --hash=sha256:7d0b4fec6a8d02d7e95de5cfa913261820f1ce04bd4c0381924de0da523179b8 \ + --hash=sha256:7de54b49e6da811b0321e412d14efdaa1ee0c0b6609296ea5b9022bc5b2bd843 \ + --hash=sha256:7e0bfa161df365ba3c88899ee3b7c94755200967284bdedef8c1b8b43e2c0f2b \ + --hash=sha256:7fac4250b37d994e3fe42b46ba3c8bfa1614d1d7d8cf1cf23f303099082a9565 \ + --hash=sha256:7ff8dd079e7b5f3438332499233a2a5acfca0741fd0eb3d4ddba0c2d9bc04d19 \ + --hash=sha256:8090c35199d6b7bc6426bb8bdaf341e64f295cc2624a1fda7860c0837f1acc03 \ + --hash=sha256:81a0e08c64dfdad27dab687b96f572b23bafa1999a39d1b6f70b3ddbb73e8bd0 \ + --hash=sha256:852c921217f330b3e81a822647ebadeae7e42cf503ec1992d0bfbc90121c09fb \ + --hash=sha256:85cb3ced4fa84949cee12bfe78208b6ece7baf3cbd242b26dcaf773efff8d206 \ + --hash=sha256:86bc779a0896e59e4be30a5be5cd6eeffd0b40b6f0e75e730218736b7bfc6f5c \ + --hash=sha256:88811f890db240a1c82bf0bcd52973763707a552c8113ac3fcebca183afb2fa8 \ + --hash=sha256:88ec4d16e9f58071c9896ea01c4da97cce9d01418fe844ff06eebb00e0a1386a \ + --hash=sha256:8a844b8b1685f38a2e8b2f3213b286e2a7abfe67508381780a0d4599ac337c1c \ + --hash=sha256:8b8429361241da973e594d15344a0989f44fd288ea58d33a6221fb7cc0daf27e \ + --hash=sha256:9161eb81b8062da824426d3700d4b0d287f0cb0b05923713adfe3bd25e7937ac \ + --hash=sha256:9267bf8261a779abb2a6eab5f107f5db85b2d1745f2494081c731aaf28738ce3 \ + --hash=sha256:939d8cd2d8c35e3956f6bc858390b6ccb611e6152b4920d64ab5e98f3fcf39e4 \ + --hash=sha256:943a9bce22180ad0f4d32d1b402a0949a4ecfe5a1257b47f54a1b51981d81b86 \ + --hash=sha256:966ae0588ac9959a040220063733b33f321d04eaf4e60349b42cd855d232202f \ + --hash=sha256:98beff85392ce435b28a0971ec21cade61ce8be8b632c9d855475a28ef92d31a \ + --hash=sha256:99cf27791129d37e191ff013bfc29bf6631c29edb21680c00978567b91fc5d6b \ + --hash=sha256:9a8c826caeb7c08264e0a556df1267531c6ed90cc70506e7e5f4119e2d09f3d7 \ + --hash=sha256:9b24e1f93b9b586ec03bc7bea1bf021ec90bf2528c729195028a3ca1c266b3f9 \ + --hash=sha256:9bc5e7f843d14a167cdc26fe2d22f6f3aa2feb57919cf3ff034262a57d8d95d0 \ + --hash=sha256:9c10791e9f5ef132effc8fdce2009482c1cfb26618c5fc1b7952a47dd5eb632e \ + --hash=sha256:a177a0ee5cf19931dcaeb3f662bc562754cfa4f4ace2351d9da24a954ef7db94 \ + --hash=sha256:a2e575129c048bc286d696ed8e49ca148591768b2d77debcc6569f6fb64d0668 \ + --hash=sha256:a5f0bebb10aae010d3c9ee3abaf83ab2069c718457aea09c15532355dd7e061f \ + --hash=sha256:a5f721a2437390ab69c10c6df5c142478d399af8dfb02e6d823cf2358e8a4748 \ + --hash=sha256:a60b720c329c0007feae692b7bf91cf17b3f9bd3727be96cc6f9a3336651041b \ + --hash=sha256:a8bba9d1f6db4ef2a6ebfc937a65d36e80e3aada00b382eaf56fea8f639322d5 \ + --hash=sha256:abeec7a89d698aa1c9b4c36bd5e3c746faef0867076e6a2ca27fa5077c4ece26 \ + --hash=sha256:aeba2c750102051aa51e087c2ccbc79f2724a41c94168f8731e36f54c453551a \ + --hash=sha256:afe36ca503c2ffe30fb6df82b20389fa3c4035b5d65888a61310921cf3ae91c5 \ + --hash=sha256:b0e0040b0d8dd89bd0af9ab18901981e344ffba68bb30b8eabb4eab6c303279b \ + --hash=sha256:b117ed1cd1a23df0902461c38093408b95971833dcee629112acda25b603c8d0 \ + --hash=sha256:b4674b12701c3fcbdf7f88b9e4479701c93bec5da9eb576140d5fcc0092990af \ + --hash=sha256:b4908e17867930b7ac77f89a18dc67308c67c511f037d8580489be86fb585912 \ + --hash=sha256:b57d4d7021bfd159db9f8f6f862a85a7a6027934643c512f028d6e5c60c4cbd2 \ + --hash=sha256:b5ed78742502b8d90ff2816688d407a097c8b5cc6af4343fc5ad7a98df53a7cd \ + --hash=sha256:b78de22bae456a976f33df34d598dfd16edc9a03df8f4cc8b7c17bdba4c97b4a \ + --hash=sha256:b7cc5333fcbfb27327d12612ed72322f221b61c2b69deb1155078c964f86e1a1 \ + --hash=sha256:b9d9b7d72975521434368fe8aed3f6b522060bf271adabaa5ca6c87c0c08e168 \ + --hash=sha256:bbcae7a54050b7ad7bc7bf425ba63dea7d2cd31a92246ba787a2ce69a9b98dbc \ + --hash=sha256:bf14cfcc30b097583d698a6e2b8b68c9bcffab277c485d481881958360c2938d \ + --hash=sha256:c39dfcaa0bf23443474c0cb58d8d8aea9529c1841d99654cb38e4dada7b1948a \ + --hash=sha256:c44ca6d3cdf4cfcbcd4f928fdcbe87af5fd7319f6ad4169617b7fd6b4527c33c \ + --hash=sha256:c44ced5e5168cdf677f0ae39900863bf2bda7d14a5e13502014005cfe040b8b4 \ + --hash=sha256:c45629c0049fbdef932dbe408ac2b271fdc8c7d9962ca31160f4a0fc3455fe4f \ + --hash=sha256:c53be0dd676484a660acc56e4f1cd0dd74bc1255d12fa285e86a3fa9d5f22bf9 \ + --hash=sha256:c54ae1b89e582aa25f213cd8b5eac0bda1724e79299f486baeb3f562bbf82ca5 \ + --hash=sha256:c564d0758748f38aec56a6b98c6801a427b3a63f39b7cac538b2b2d18ca32740 \ + --hash=sha256:c5e4a362a95b85301d262ef6bed06cc8e4a144ac7e2be874cb4c3c46ae89d754 \ + --hash=sha256:c6b67f08014bfc4aedc22cf6a21010c2530cd5fbeb655730406827fe196296be \ + --hash=sha256:c7aafa4dd2f702ee2198005d6cba4309c1e25ed1c201d77beddefa47411bead8 \ + --hash=sha256:c81062e947f4b5a624135a843f6ac4b3c7fe6508300c9fb27347f022ba0c513d \ + --hash=sha256:cbec738d2ad551c6f70955d7eec95e339380ee1564e2afe86bfee05fed52ceec \ + --hash=sha256:ccf98ee859fe29f874ddd8e637f14ba59108a333492b521acb885a9095244a9c \ + --hash=sha256:ccfb950359a80de0fcd2030ad60ac1b1a861462de3e2ef746697c9256659af21 \ + --hash=sha256:cf606cfe3f67984b4064ac605d71e1eba12515fbabf5bd5a34a8952b8800dc66 \ + --hash=sha256:d02cd23b5af182a49d635ee72be38053767711987a9fd82625b16b93828a0d8c \ + --hash=sha256:d1b1b32f3c32f734dde8f36ac1df8e275e768a7b333241cd637cb2538628a4b4 \ + --hash=sha256:d7dd46a8fcd7653c09ebe67eae9d4cb6636c7a905d9cbaf587dabcbd4eca6013 \ + --hash=sha256:d9ef29cfd98e17085b4f91bba8fa1570bec6787d5c52ce653ed33a58785585d0 \ + --hash=sha256:db77888081431aaa69f3fd3480891746ddce6c2a571f6201869a24e2f06cf423 \ + --hash=sha256:dd8a6b3e8f9edb07fe671b02d8c3241c8b641fecce7eb1e36432db3e55e243da \ + --hash=sha256:df03e392cae1e05462918abbae06d6100f1e53f67db971ff0ac6c07d9edf7321 \ + --hash=sha256:e0d91a4bcb59ac0d7af0d8e0da737332e1b7fe6831e53e47819b1b5349d431b2 \ + --hash=sha256:e2e718fa9d1d900decbc240a533d5d0baf0947ef464c78a8cd4fa32b4e8f590c \ + --hash=sha256:e4ef15d0a29fc2da67fe8ba2301ecabd6f8733696cc2bf0a0cf96a144a20328c \ + --hash=sha256:e50f7775b66c7802f4cb697e986c5acf30ec07301efee95b396c08114e890d67 \ + --hash=sha256:e6906aa4bc62cde2c8aeb8a99a7b4401b241e274ae7b11df67d863d61ab3d5de \ + --hash=sha256:e96d67914ddbf5466e4476a1cd7ff30a332cbab85ed895207acc3e58c979b6a7 \ + --hash=sha256:ea027bdeca1d7e498237634ee4e3a852e2723eef39996dec0ff0f77dff8a2336 \ + --hash=sha256:eb0228c809b2e7eb47921876050af0bc4214b351bad8d8112f70b6ed4288763c \ + --hash=sha256:ecc68f5e47bc6f6f889bbed5bc657b22bb2237ad9ccab8229cb5a0d64f4cb536 \ + --hash=sha256:ed6b7f402f3dabd1d72c798b96cf947005ddd796a5bea7b041bccbd517859a42 \ + --hash=sha256:f16ac8af2804855d3cae5fc3c5ab609c9fd0fc8ecacd92579c05ed3c173396fd \ + --hash=sha256:f376224572d1f5da1c871f969ab04765727f180e70d012d93e07bfc08442c64b \ + --hash=sha256:f76ceb623f7ff50df46ac57e1587c479d87a5766319c4f43d0c0a5158896afab \ + --hash=sha256:f79def86aee67b5ba01b2565f1610f262bf88ae53c379f93e5fa29c50fe793be \ + --hash=sha256:f8e95c95039eab6a2dad8c83c38ab87fc5431d28849e0c8a7e2a4e70ba38710d \ + --hash=sha256:f979a077d1c0a9a36dd4fab0d3a36b8de7b593bf935e13df85a380395b2c11ad \ + --hash=sha256:f996b19ac89e0dae65821ce65f788619e4286f78c62d005ecd3b75b5d9c0892b \ + --hash=sha256:fab380fcff8b3555eb2bd04304fa4330909a771a9a9b0dc07666cfc23148a711 \ + --hash=sha256:fabfdd4cf97db033196b51af46b8a681d4785c2a66347f2a5af1b4bbb1182629 \ + --hash=sha256:fadcc96cd6155f35e6d85845fa4fcd37b35885dc8fda77b9f851cdfa538194c1 \ + --hash=sha256:fed6b7705d49dd07e5e0dd5f5c873fc44047e92d714299b13245b5fecac49d01 \ + --hash=sha256:ff15531a376dc6f35984443fd1429e4b150c36ce27633e7cc52a9e5318546e20 + # via + # aiohttp + # yarl +oauthlib==4.0.0 \ + --hash=sha256:624c28c13a0a59cabf9747dfa52af63be3e512a7f2714df16e91b5b3a145e6cd \ + --hash=sha256:efb274799819440f95b4ab3b818869f1ce9ae26c5beacba0201d1a1b76b54f86 + # via requests-oauthlib +ollama==0.6.3 \ + --hash=sha256:41fc49a8095c4a75939c4c1f8582e4d0671692fb6eac2a5a7ede8c9872b67096 \ + --hash=sha256:6a20bc42c1a5f889295d7ec490d35e5132fc31f339561530f43a8abd4dbfe508 + # via browser-use +openai==2.54.0 \ + --hash=sha256:89089789197ccdb87f173a03145ed1598d00795220c93e96cf712b1cbf5e5f2b \ + --hash=sha256:e3e6f8bc1ba30ddf381ace1a14340eed381cb984a1a59bd0f34b5be3b5d49cfa + # via + # browser-use + # litellm + # openhands-agent-server +openapi-pydantic==0.6.0 \ + --hash=sha256:11f3ac6ad41521fc156381ec587246b0c0ecea523bd9565ed74d3e7fac9d91cd \ + --hash=sha256:8bd6d548a250da37db96f461bcd52b3468e8834ad7d6db14b00a6b2ebbd163a3 + # via fastmcp-slim +openhands-agent-server==1.55.0 \ + --hash=sha256:b837f8e7a5ad3865ca035a70e44d7c1293942d828efac1e0a1cee807aca50f36 \ + --hash=sha256:f2fb0240633a6b601f0351c2b1de5e1ea7dfb6aa321c1e18cb62e8e234ac9188 + # via -r requirements.in +openhands-sdk==1.55.0 \ + --hash=sha256:07adef406892248a5c1f39a2c74ee2e2fa1bfc703cbeecacd0a02c677c16ffb2 \ + --hash=sha256:61395250825de5e732e2a7938dbef630d2eeb95e305b1a5595d281b5cbddd4bd + # via + # -r requirements.in + # openhands-agent-server + # openhands-tools +openhands-tools==1.55.0 \ + --hash=sha256:9331647429952aa1fba00849e4997651bf29d351b4ff8628c69da2194e576214 \ + --hash=sha256:99a3d29d3c48d08aa04bef91cfb5fd69b8e7c7b6ebbff4f41896fdbba7260995 + # via + # -r requirements.in + # openhands-agent-server +opentelemetry-api==1.45.1 \ + --hash=sha256:aa38ed19bcc084ba42782a73255b3582283eced7ad6dddbd6695189e69adfb75 \ + --hash=sha256:b31553efa588ae44bc306f863c785c5333a9ecc091248c6ee68b4b6c87fdedfb + # via + # fastapi + # fastmcp-slim + # google-api-core + # lmnr + # opentelemetry-exporter-http-transport + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-instrumentation + # opentelemetry-instrumentation-threading + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-exporter-http-transport==0.66b1 \ + --hash=sha256:2f95404bdee7f9d2d529c7de56c7bd86d014d774d8fbf137810e0167f8a492bf \ + --hash=sha256:443080203bf52586ce0b2ad901e8951c61833eab1aa539ae6f1f16fe9e8e7952 + # via opentelemetry-exporter-otlp-proto-http +opentelemetry-exporter-otlp-common==0.66b1 \ + --hash=sha256:00ff8592c3a7cb729ff3fdc7ffa12372c243bdf2163e80c180994d0c7bd83ee9 \ + --hash=sha256:6b1403487a2185ac1feb45fd5546fdf8630ce71c36bcefaadf51e2130e9e23f9 + # via + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-exporter-otlp-proto-common==1.45.1 \ + --hash=sha256:2e4adcc3a67bcf57804fc49514f0ef64974ca7590aa3491da389852b4a0628f6 \ + --hash=sha256:2f446183ae7047b036226f1d846c41a834b0e8755ad13b51a51dd38952eb466c + # via + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-exporter-otlp-proto-grpc==1.45.1 \ + --hash=sha256:3b3dcfbfdcb4e35149fcf309972282054b45228f5c10547d0095d6578510a9a0 \ + --hash=sha256:e42ecb789d2fc5d8145e3dadc3e2991c9f18cd166d7c7514e234702540274b76 + # via lmnr +opentelemetry-exporter-otlp-proto-http==1.45.1 \ + --hash=sha256:24a97cf3753c7fb52fad44a696e452ff371686339e2acf3309e2eda3d0230700 \ + --hash=sha256:45c218405ce3fd879596924b1874bf9a8f6880206d61065c5a912c8e5c297fb7 + # via lmnr +opentelemetry-instrumentation==0.66b1 \ + --hash=sha256:4c4aa14dc9a24a02325a9d4c42c4d0208dbb1374c2b1b8fe6c9392d59f3e1008 \ + --hash=sha256:e79a510f7d87c72d95e964ddb42193a0d9a75668c027d980eab032ea1322a5ce + # via + # lmnr + # opentelemetry-instrumentation-threading +opentelemetry-instrumentation-threading==0.66b1 \ + --hash=sha256:2c217359439d09ca4a4be524db368c05a3cc7f218b85e8086f22664db0a03538 \ + --hash=sha256:94039c60603060d26524e31aed1181a5d153e43f58462d110dbeb4830687d7d7 + # via lmnr +opentelemetry-proto==1.45.1 \ + --hash=sha256:79e0fb95e4616691a469439238aa9224d75779b3e108e895d1aa125ab29ca77c \ + --hash=sha256:f38e2a8413053c180cd3d2637fbb279673ec2f6a6e09c995aafa2f452c52b46e + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-sdk==1.45.1 \ + --hash=sha256:63d24a6ca645019a631e6a51999c73e93adcac1196ca640b8ae78a7cc4762bf3 \ + --hash=sha256:c604c11dc429810812348989115fa44bd558772a3d7442afc43d024f2c250ca4 + # via + # lmnr + # opentelemetry-exporter-otlp-common + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-semantic-conventions==0.66b1 \ + --hash=sha256:497ca63bf383723411e8eaf60c8779e9877633c936bb641080adab59d0eb6ec8 \ + --hash=sha256:d4cddeb4315490b35213f55e2bdc9ac54bb1e4d318927475bed62b35545e581b + # via + # lmnr + # opentelemetry-instrumentation + # opentelemetry-sdk +opentelemetry-semantic-conventions-ai==0.4.13 \ + --hash=sha256:883a30a6bb5deaec0d646912b5f9f6dcbb9f6f72557b73d0f2560bf25d13e2d5 \ + --hash=sha256:94efa9fb4ffac18c45f54a3a338ffeb7eedb7e1bb4d147786e77202e159f0036 + # via lmnr +orjson==3.13.0 \ + --hash=sha256:0526a3456db67b264c6d661b5f090077f326b6cd074d0ef53a72763595dec5d7 \ + --hash=sha256:08bf722f923d2100bc5e5a5dcf72c656db557049c1bea26582fdd5dd9d5395a1 \ + --hash=sha256:1807c2fa49d393c7ee95fd1ef1b39cbb24aa3ccd81f30b84503ba59407666960 \ + --hash=sha256:1d84820b2ec4ac975cba482214032de5b0dbdd17046170c98e642ef9c4a4ee4b \ + --hash=sha256:2715c4808d1571029ed18fd07a82140bf3ba7def0dc89f8d015c416e3649bf87 \ + --hash=sha256:3ef75ed7e81dae34a3649f82df52cd85f9ac839a7d6ec78ab355b33b3b27ef7f \ + --hash=sha256:4329c19b8a25693f60a77b867c9d2a3ab637b20e36f5b7bea7f5acb492b44b15 \ + --hash=sha256:45e34deb3437509f4ec9888dd9ee5dc426cfe21be10f1eb4ea3a9e4d33034f9e \ + --hash=sha256:4e5c8175e1574dcbe446ee654275d353c1d78bbd9a0dc9f209bf35c9df72d171 \ + --hash=sha256:4ee06e53b998c71ce3eb93b86222912fdd9dcced685ac64d4525d36fac338ea4 \ + --hash=sha256:4f66eac85b072092e9941c3111882afd7527bf926cbc717038fa3654b582002b \ + --hash=sha256:50a5202ba388b3850ba24437951727d3aa6d79a21964a30ae8dc6a059a5fd34c \ + --hash=sha256:51d11525bc3ca736fa97ce4e4c7da9999cc00bf261522bede43b4e7531bd7965 \ + --hash=sha256:554948becd1110123ef9f6a6e1310fd92b2d07d2cbac6dbf65df3de75702e736 \ + --hash=sha256:58a9619d88f8818d9ab6b39d70d203789457ba13c1ed5d274f33ce9ae7e81a36 \ + --hash=sha256:5ef4d4157392a0439b74f7e49e5636b4ea43d9616bd0884effc0195fffcaa2d5 \ + --hash=sha256:637dbca1fccffe83780e806fbc0f17427c0c59bf822528eb0acc8f0aa9f19acb \ + --hash=sha256:64e8f345048d988c8b68d3882e5d41028fca1219a9939b32e4a77be34c8ae8e3 \ + --hash=sha256:65c4e0e106ccc7265b488385659117a6805c37d042f737558ecd68aa0c67ad8f \ + --hash=sha256:6adcaa85d79977659a448b4123a88eb33511a11ed2db243535ad7ea88a6668e0 \ + --hash=sha256:6c8bfe728b81b0fd58a3c7f3f9c5a113f87f2992c9948e0f28707aafd737c0bc \ + --hash=sha256:6d0684895b119ad167fb4ec05113639dc7f728022deec4756a710e838ed92e7a \ + --hash=sha256:6ff2a2c67f35202f7d823753d38ad371a9b7fc297567cdfff4420e763cb9f6f8 \ + --hash=sha256:7804dd1d6161da0e53b284c2aebf20f23e78eaac617300803e1467d1828d987f \ + --hash=sha256:78a12d4f8d740cc9ae197f5223682e5e960ba61b4fb2ce5a6a3bb54e83fde28e \ + --hash=sha256:7991921c5da527a963b6d4cffd0e4ea89c7e71d4be0c8be1bfe6edb223ce7d96 \ + --hash=sha256:7b3bc6b81835ce65f4729ae401607583d41139c6de95bc7453f450f1391d3e7b \ + --hash=sha256:83705c12b4afde10c62a5dd3fe6fdb21b7900bd0dcd5af1c85612ae94d0ee590 \ + --hash=sha256:84d87e322e1674408f85adea63f11aa19201eba082755aec20ebc217f493bbd2 \ + --hash=sha256:8594956a75223f657e1e68c568c0eeb3dd145f02cd6b78a47fd9a8095dbc4eae \ + --hash=sha256:89bcf2d4bc6c9a7e1763c8cf534f38712e66b76a0fefda7fb7785462f0d635e4 \ + --hash=sha256:89efecad02515df7f318d0613b5dfd6d2a1acd323a2b8294712789a715945525 \ + --hash=sha256:8c2ac5c09b017c484df1b4c68b2cf250b4e8ba08204cb58e7cd6cbbc71a9c902 \ + --hash=sha256:91d933e668ff0ffe164d7c2daec36beba6d1ce7fadb71538fbe142a71f8a1e6e \ + --hash=sha256:93c70a5e22bbbbdeafc7b273441e8452a196041d67fd4d9a9c450c66370a8486 \ + --hash=sha256:948bad47f2e2e43527f14248364a0e5dee26dd3184691010ec4a1ebeb0fd6771 \ + --hash=sha256:9825b954155b345c4759f24e5f8d652b9aec2261bb5d4e1abe06bba0a1200535 \ + --hash=sha256:a0377d6962fa431c93ecd78fdea771bb62ec545b24ee0c5d4e32acf2260af259 \ + --hash=sha256:a79cdc4934fe81f593072c94e13da3095e9d41c2deef8f6ff2901794ca1c5042 \ + --hash=sha256:a7bfc7db961c7d96cb75889dc6a1e4ae1e91d87ee61da564f582bd742b8dfeef \ + --hash=sha256:ac81530647c3423107cf61c3481e91f57134e9ddfb6ef83f5150ccbdcbc3a3ee \ + --hash=sha256:ae1d895cf7bbfd50ef34bb63bb727b14514f259f3e3f8dd010783bd38e864c6e \ + --hash=sha256:b081f0e7b600ff24513dec4ca75507fa05e904607847e386e8310d5b7b96b6c7 \ + --hash=sha256:b571236d8393edcd3236e07423f762bfcf571f852aad667a3bce9e7b755e0790 \ + --hash=sha256:b74c30e56346aad067937d766846ee74c231d1d18aad3f324e9b9261de3b2d5e \ + --hash=sha256:bceadfd314bd238f584fc229a4bbaf0e573597e7a026dec5429fbf29fd66c641 \ + --hash=sha256:c5e3ccaac3106e8fa6e2f2f6962449d7c757d7b067e41b395a19d6f0d6cec892 \ + --hash=sha256:c749ab3ac30b5ab1ffb7677f8b92eacfdfdc5260210baa398f845bc3714c05d8 \ + --hash=sha256:cbed5f4c4b88d94bcc36115f4c3bb3aa25da1563a5c3328aa3acebce2b083040 \ + --hash=sha256:d1de5eb04485110c5da4c657e49168995d55e076b1ce60f1a042e254f4186c4f \ + --hash=sha256:dd61e64802d51d1e4f16531c64536354fc3bc67932dc0cff254044f72bf0f187 \ + --hash=sha256:dd9d9a101bd8dbfad112170f009cd155e52bb8c936468821a0d03cbb96c0e426 \ + --hash=sha256:ded33b972cffdaf4ca0ac917338ab61d2bb10d68987dbcae641c313fbfdbf499 \ + --hash=sha256:e8e05549f3b30f9d8a8e28c5aba11cc2a4b90b90961ec685ca58444b0815fc09 \ + --hash=sha256:e9b61676116f755126b90e740a9cff36b91562f47ec330056cc88cc3b9f02f4b \ + --hash=sha256:efa160215c4630836d3b1250af4c7a305acd8239e0d75aff986b8088c2fcacb6 \ + --hash=sha256:f5c05a8fee59309f537590a1ff12d3c1009c485e96a50a9ac60dd085c09d0fc0 \ + --hash=sha256:fb8644dc6d705e1269ed2842bf4dbe2b4e50d670de503bf79d5cef3a5148a4c7 \ + --hash=sha256:fbbad6b9b1da43f25c1f5b20cd5a268e028a2fc95d5a8d1ade6059973bc71584 + # via lmnr +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c + # via + # deprecation + # fastmcp-slim + # huggingface-hub + # litellm + # lmnr + # opentelemetry-instrumentation +pathable==0.6.0 \ + --hash=sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58 \ + --hash=sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566 + # via jsonschema-path +pfzy==0.3.4 \ + --hash=sha256:5f50d5b2b3207fa72e7ec0ef08372ef652685470974a107d0d4999fc5a903a96 \ + --hash=sha256:717ea765dd10b63618e7298b2d98efd819e0b30cd5905c9707223dceeb94b3f1 + # via inquirerpy +pillow==12.3.0 \ + --hash=sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756 \ + --hash=sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a \ + --hash=sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59 \ + --hash=sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45 \ + --hash=sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 \ + --hash=sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df \ + --hash=sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139 \ + --hash=sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b \ + --hash=sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39 \ + --hash=sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e \ + --hash=sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8 \ + --hash=sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1 \ + --hash=sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8 \ + --hash=sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89 \ + --hash=sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5 \ + --hash=sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130 \ + --hash=sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd \ + --hash=sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d \ + --hash=sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b \ + --hash=sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed \ + --hash=sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace \ + --hash=sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb \ + --hash=sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931 \ + --hash=sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510 \ + --hash=sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6 \ + --hash=sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1 \ + --hash=sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce \ + --hash=sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385 \ + --hash=sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e \ + --hash=sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c \ + --hash=sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7 \ + --hash=sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace \ + --hash=sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c \ + --hash=sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f \ + --hash=sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64 \ + --hash=sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f \ + --hash=sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a \ + --hash=sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827 \ + --hash=sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17 \ + --hash=sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4 \ + --hash=sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a \ + --hash=sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701 \ + --hash=sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e \ + --hash=sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91 \ + --hash=sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66 \ + --hash=sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468 \ + --hash=sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217 \ + --hash=sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658 \ + --hash=sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418 \ + --hash=sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a \ + --hash=sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c \ + --hash=sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330 \ + --hash=sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402 \ + --hash=sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09 \ + --hash=sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930 \ + --hash=sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f \ + --hash=sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec \ + --hash=sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a \ + --hash=sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94 \ + --hash=sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468 \ + --hash=sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b \ + --hash=sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965 \ + --hash=sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8 \ + --hash=sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd \ + --hash=sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 \ + --hash=sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c \ + --hash=sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777 \ + --hash=sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 \ + --hash=sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9 \ + --hash=sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f \ + --hash=sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f \ + --hash=sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0 \ + --hash=sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c \ + --hash=sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71 \ + --hash=sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3 \ + --hash=sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838 \ + --hash=sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf \ + --hash=sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321 \ + --hash=sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26 \ + --hash=sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec \ + --hash=sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9 \ + --hash=sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65 \ + --hash=sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5 \ + --hash=sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e \ + --hash=sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d \ + --hash=sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198 \ + --hash=sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7 + # via + # browser-use + # openhands-sdk + # reportlab +platformdirs==4.13.3 \ + --hash=sha256:5e567f664eb087ab8521c0179cd8d1bd60857d271136567a39719e28e2d383ce \ + --hash=sha256:f6ad7f447f24f8a3b82cce5976387428bff894a0eca6c3488f4a17f153c130c4 + # via fastmcp-slim +portalocker==2.10.1 \ + --hash=sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf \ + --hash=sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f + # via + # browser-use + # bubus +posthog==7.71.0 \ + --hash=sha256:00950999edf7f613a90015126bacc431d3ab3964a70d91c2f792e6a0dc81f358 \ + --hash=sha256:52cb1320b7e178b36ef7594cf15b4a8c492c24d4d1c46700f5bee0089ba5c6ac + # via browser-use +prompt-toolkit==3.0.53 \ + --hash=sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2 \ + --hash=sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6 + # via inquirerpy +propcache==0.5.4 \ + --hash=sha256:004e685b315646c410771836e72a44f143bbe624f29653a42687815069a303d5 \ + --hash=sha256:02c0a34f16889cf800f10f0247a564d8ce6eeab6ffcd7c87198f769067eb8432 \ + --hash=sha256:03969626faf0783a592dfa17e28eac06018bd0b44dafae6943d53b92421a7f72 \ + --hash=sha256:03b229037d25b801e7af53fd52b9fc49d9439b036fca1e087e02780631adfa97 \ + --hash=sha256:0951315a6b3142ee2167404d707743f0157c110091342b1aa0accac5cf0e4acf \ + --hash=sha256:0a095db8e15a6020db149ecbed6461939fe74f6acaa3ae8b702a1fe8c38cd983 \ + --hash=sha256:0c889f6fa84957bc7e8b4eab71fd16a0455068d5045e3aa40c733071d2b2fd77 \ + --hash=sha256:0d21d0d2c82bbfeb1677a9711f38df968f9837576102bb4add1bd449d28d88f1 \ + --hash=sha256:10ef33a68a61ce317e095fd2e202a592ea92392b90944a78c993f0d9a73ab06c \ + --hash=sha256:12682126712ddc19b70ff819debbd279e58adf1f0c8f8f8138c18ade2044b284 \ + --hash=sha256:135036c5cfc93864affb0f9af9a27e5d7a71cb7bd745e7b6dbfc2d56cc30e827 \ + --hash=sha256:13e52b6e0bde97dee98ab66552dbff2931649c96f1ac432eac299fe689ec373b \ + --hash=sha256:141fdbd73748db0cf7636035030aaac383d2efde8f34e7bc24594cc776d225b8 \ + --hash=sha256:146f48a9e4812611a7581003b1a39de56c34967046310c4171a68ef908c9a745 \ + --hash=sha256:174507f82d3594622acb1dd2dafecf2d899d6d506335494e7107767bf05f3aae \ + --hash=sha256:1783582065a1f07f9d9ee1e992e13f15d7dc8fb1eb3a7476d43eb3f2e69d26bb \ + --hash=sha256:17a7400cec0256f0a71ae71f9da398f9894c956ff6668a1c9d317b3367316320 \ + --hash=sha256:1b2f3bec4261a94019575481c726c29850f72e27907773c75b1de421e20e9f9d \ + --hash=sha256:1d759d05634f1b038fb625a66662a8c85e5a8fec912da381b5149ddac107482b \ + --hash=sha256:1df8d8561b21465c5dd56110a01caf897e026d065b4b84e98a488209094272ec \ + --hash=sha256:213bb68d9ced5cf2bf717b1071bf2b09b4b04c426256f9fe6d054c60318424c4 \ + --hash=sha256:23278f808cd81d5ada7184a76606b925fb3389c60e1077b2cd7da7b1fcf0553c \ + --hash=sha256:251c63dd46a0659bb875cb254dc4c1e79ee91a847c737cd62373295afc2235dc \ + --hash=sha256:279655a16973f1ee2bd2fe79973137681642fd9ae0d89215bba263726eb0dc3a \ + --hash=sha256:2814ecd8e818f487bee4b0f921bc4d1c176cc5fc71ac0f072d0fa67eda4ac14b \ + --hash=sha256:286867fb156488c251a3721766e380ac4495e4fd6b51aaa1403d89ce7f4359d9 \ + --hash=sha256:2dba2f02d2d5c09ef8a0e6c1a42aeaa451f4be9898cb00b04fe98717da2eb23b \ + --hash=sha256:30cc1cebaf9aef49db06357a50398323ae04d70460c0491837d026ab7d6452ea \ + --hash=sha256:31eb43ba2edc704ab2ec27815315dd8a19def0fb16215be4cfe8d32fe78ffd51 \ + --hash=sha256:350b272b2279f4135a64fc0c304a5d08e28a137c9573442c606152446638a831 \ + --hash=sha256:36c0d9db44b523ef93d03341b1c42d69ff01d673c053d1b1c6c3a363bcaa39ba \ + --hash=sha256:3af0c8642b2da4815d86e631232ac8286e17644fad907c19508aa8e7cb4ba8ad \ + --hash=sha256:3cd3a7edb6b95b9b33998135ebfa18d709da82290fb8f27c858970b5a12c8b56 \ + --hash=sha256:3d605bb239b796e82a81c6709548b2bd460ab73b4590cb0c83de8a2dd9694d0f \ + --hash=sha256:3e413d7a4a9b4866b7a761d6060d434b64d23cd35122eda3b026a0bbe8196b25 \ + --hash=sha256:3eb2e820e8e2101407da93f17c57cbb7d225461955fc60105daaba14cd421ee2 \ + --hash=sha256:3fa15757fea1dfcd5b7745cad9f4638929605531bd4018ab2adff7955f1a403d \ + --hash=sha256:3fc24f209c1b7f7f688b66b98293954f5504279760999b58920ee12dd8471c1d \ + --hash=sha256:4054acf80d40456a0537f2913b349718649d8d6458a14ab7f48d0ce28c30869d \ + --hash=sha256:40e94adb1e7d39ff28a8bd8d8b8fbd1df6b9f40976dbe379134f1ce058e532dd \ + --hash=sha256:420162a77f94eb1cf5ef7893f500016dabd548e73de956785a1dd899cc73006a \ + --hash=sha256:425f8cc86ab5018b4b8d4a23bc8e74d964bd3d757c3702e301aa79be76c53f6c \ + --hash=sha256:44149f46500a0a41b95b4d99c2e586a77319539730607b9892974a092788b111 \ + --hash=sha256:445ee3bfb46e85838387fb3c536a73cc0b994dc192b004e40e170adc54aa2a7e \ + --hash=sha256:45488d1a5f9ab5bd90aaa1ca20f50fe1922b8ffad71a2009d2adf41355897aac \ + --hash=sha256:45bebbe252550fec975ba3b62bc6f931643cfd3b5464ef47619cf3fef154e01c \ + --hash=sha256:45bf2e730ab8905d0527fe05a86500f406e64305c34cc81ebe64b4617cab9760 \ + --hash=sha256:48cb48c5346a97de792254af77715aa2529c2a1ebc5f586aa0aae44a02f1fe57 \ + --hash=sha256:4a1f4f5ffa55dce6307631f3cb2948e117e665966ea512e0d502b16c24f567e7 \ + --hash=sha256:4cfe0a92ae30151869e67a4b5f5e105e4e03ad30b3f38e5211b5bf77d0881993 \ + --hash=sha256:4d86476a935c88963d9b8e1a9a0d38188790e9622169bfbafa173046846709d3 \ + --hash=sha256:4e985382be6d15da8d0c2710a6fa7b9070fc9ecdeefb7f580e88373984ec8be3 \ + --hash=sha256:4f2d880ff60f45898f4acfa152aac8d04e3ee627d90ff4003491bf92239d5757 \ + --hash=sha256:4fbc1a15dc8cd1689508758d626b372b1f09d28d9577667feaf9e6bfcd8efcbc \ + --hash=sha256:50e337653721d20ead710da33bf44487fbe8a0db8782714b60306481e9f95b51 \ + --hash=sha256:53eaa697c4d0422ff4cb714d00231b43352064d97b944033b30c1d57cc506ec0 \ + --hash=sha256:56fc3f7599528db40b1efa0889a620116e2704144495273d66066e8164e45838 \ + --hash=sha256:58134228927cee6c047d626c08e60a81be604a20578a12ce752cc5c9a84d4826 \ + --hash=sha256:594eb4c6ec35e7179b058481f4e9f02521b56de16fa577c4b85c76fb1bf8a9f8 \ + --hash=sha256:5cacf3c9efd09df409dc33654dd077e1c245ba8fb747b0f0236ef41b7c49b589 \ + --hash=sha256:60a64cbccaa11b7760ce705a14ada17ba459e7ca9f23ba587eb013821032d7ef \ + --hash=sha256:62530ca89187827e4a4fe733f971abe81a7542eeea48ff61995f19b64d7199c8 \ + --hash=sha256:62c60aec739ed00124573cce1178138fd690c7676352d67a37328c1cf51d7468 \ + --hash=sha256:69fc35c0779522da366c563e5faf203ffc1f8ff0021d5b1337fa4efa5be73177 \ + --hash=sha256:6af4693716bfb03f1752ef1b30faa593db2c01d5272e9b8564a1549452a979ab \ + --hash=sha256:6c7599df2b57ebeea8de011b5f2f7b85de95e76037d43d34b95e328430275487 \ + --hash=sha256:6e9368e87a3efc285e559131092c5db643eb8e56de4ee42064d5baec22ef2bb5 \ + --hash=sha256:6f0093ac3e9daada202c2082439d414a625c57184727a46e112a3fb2a81cb788 \ + --hash=sha256:7177c43eddf10a0893c4fec52ebb408fdcd7f7d63962caace9180d8f81b14ece \ + --hash=sha256:720cf832eb2d0b0dfee129cb3335a26f6ce3cc45ee1187e8f0731758caa16792 \ + --hash=sha256:770e8209d018175fc0063936fa9583b6d27e88c5ad31543f3383d66080efdd62 \ + --hash=sha256:7a8d5ff04eb1f85698a78d20c62a14676e7b960dcafde09a388d60ad377d355d \ + --hash=sha256:7b9100a93b372418d8688f3f2a3e5b45c64d70ca4d6176e121aca1e3bfc1e32f \ + --hash=sha256:7cc528e760a8af06f2b13e9b9f362cd90c7c718ea61228a96dbd31ba16ed7f47 \ + --hash=sha256:7ffafcbfc7b549ab940047e505c831eabac5e67de53e1bc174adbc5285c55944 \ + --hash=sha256:87a3caecf8095e48dc72f84bfa42e23a848cf410cc9cc13031fba4869b706a21 \ + --hash=sha256:886b59c4d28ca97dd23b025fdfc50a0356be934efbbbca89ad26230067f86fe5 \ + --hash=sha256:8876b39961e33d912afe3c1bee18ee564fdad0206f873cc15d522756b7f50737 \ + --hash=sha256:897d1ddf6716e8f47200f7aad9a0efa6cc7586df66c6defa572f9eab379c078e \ + --hash=sha256:8a1fc236528c457cd739c88abe823da851b7ab645d72792f88658114cc340c12 \ + --hash=sha256:8a235f73d6e020855dc29dff012d920c02ee0feab8d73a24185a7569f4be1161 \ + --hash=sha256:8f911c395cef73c510bac566da9507bb6a43e7763d0c79138dc60ee53f11207e \ + --hash=sha256:96f7c5c15656040ddcbc51e56dc59b58aa25999d743c126abd425b9766ab43e9 \ + --hash=sha256:978f28401afbc76cdc3df9e1717b4229a06b626a1dcc75db4e1f2beb3884c3e9 \ + --hash=sha256:98914de2c4d7f0f9f4a8c6ea4bf05841f4175796941e3ef7d47eb718f22311fb \ + --hash=sha256:9a2a8a50a93dee0268a860a07fa3b4bd968f8ce4dbd794957da772f395368526 \ + --hash=sha256:9cbfff4423eef4cc6cafc021469641a2b835f610b2647a6c5281903e21b8670d \ + --hash=sha256:9e9ab13760aa8b6d0881ae7cb04fd891d8d490cd2554ea8e79bb278399169bcc \ + --hash=sha256:9f3551b8a35c1df3e7ea4d2d86edee15f0dde1bddd434a71744048683544d0ef \ + --hash=sha256:9f86f7259efe2c951f43e57d471c9b41daa5bfc7db9f67189059cf1ae6d77fd9 \ + --hash=sha256:9fb0a5be8d9aa213150e8d8148a42aca4984b285bcad1e69587dc4298edd929b \ + --hash=sha256:a219f0ac59817a9114dd2aa57c13180f993e819ba658c7ddab4b66ed1ee0d370 \ + --hash=sha256:a419ee85e654927baabda3929c03c0cc1112bf472ff0dfd6142f4e3a81ca4162 \ + --hash=sha256:a4d7a54719b67338a305dca2ce6aafe366817df94ddfd4b5514374356f5ca546 \ + --hash=sha256:a5793c7698a53f56f4a1889a4737c7eeb1b7ad0842fa6b1abca22913ff79c8c1 \ + --hash=sha256:a5e8ef588c109725dc713ba69aadcac00a1ef90c2ce9c0a8c7075128f569f47f \ + --hash=sha256:a74bfa37147cc08fb29df10bd9c16f40fa7f860cd3a6d2fff853323a94f6e17f \ + --hash=sha256:ada748108a43d29b7c328ba7db3755327cd94f028bcc1a7ee3f0addcfacd9c38 \ + --hash=sha256:ae58f361bd5dae942717c65d3413b478c70aea9c462599e7b9adad3731db3894 \ + --hash=sha256:b28f41fa3b8c6900457f858ec5b03998f3a6d535fbc1bb2edec5961ea05ec429 \ + --hash=sha256:b3083bfe87f95c756e610bd8025f26cbd1cd4aaa03a422f2d65efb7a97cd53d8 \ + --hash=sha256:b61805357d966680acf68b3b6d49772631ed9df44ebece10ff1460e117a7da8a \ + --hash=sha256:b77c313314524ca9c38fbd70f73515d04597ac58c40c939bc0e71eeb4abff680 \ + --hash=sha256:bee7d3aed13d56f54e681df38c3a23031bc9e3863f687d9d598825c9146acd7d \ + --hash=sha256:c02c0e570c5c7e077b0181a9f3cdb7d4c3617d1cda6b5c95bd5d34022923d82c \ + --hash=sha256:c174bfd1c48a1b51a3078e95586dde718374bac79719ab3541ec9e74aec40574 \ + --hash=sha256:c2ba30a89035b57b73e00475de948521602f543d79ce01db10b04b36c4c76fc8 \ + --hash=sha256:c3e98c55bde2bcf7db3c70d1aed7ae9aa8aebbf19a250c66645cde44cdb8b867 \ + --hash=sha256:c3ef2818d63bc86071e9d2989ae75a1bc32b8f7059cfd9f5abbbee70c32e2ed6 \ + --hash=sha256:c83acbce9f2b5e3f5f5eda9e53d2001fed22fcdfef81274a9e02d8fd53b70a30 \ + --hash=sha256:c9281e922c072158c91974d4589f1dbe0fee6d467f284c28e463f9f5a4d933f4 \ + --hash=sha256:cc07876cfb079b6f6f36d21ce75784ad6c2c6b563eeac0ed26c2fa2669b85df9 \ + --hash=sha256:ccf4f7a79e26bb7efb06ecd50c177833b71df05cbc748701372325e6bcc17f6f \ + --hash=sha256:cdee8205a44d0be91bbac4c41b95d86641b72dfc7aef1279400e4fda3f26a937 \ + --hash=sha256:ceb3e879afac028f93d272c957814695dc5569e4904262dbee92f6c41bd5e4a3 \ + --hash=sha256:d1f5a500bfcbb2c0ab85e98a0dcd70f5899d34efe365a0187700369a79603031 \ + --hash=sha256:d42a9a856a4a6e2f6c10f1318c07e7daa498d6593abe745c71dae4521a26ca39 \ + --hash=sha256:d83b12902eb8bce151259c86c03ba746600b2d994543de46e370cecf96c452f2 \ + --hash=sha256:d8e017eeb7482bed34cdb0d61cf2bcfc88d104bbab296a17cd16a6af8aabc70e \ + --hash=sha256:db3ae52ccc150dbc84704e9d642743897f3e1c54742ff34cacb661e52e3818a9 \ + --hash=sha256:dbab5f5ff6897c81f355d079010cdae85b02e5a0b518b5251523b8ad8ae9ac3c \ + --hash=sha256:dc4242ca653c9b30ab51c5f8193323e7bc0928f897ee9103201e59a43abcb72e \ + --hash=sha256:dcbf346a318a5e30063f547630b02bb787ce2f45b6368d5da143660b6a3835d8 \ + --hash=sha256:dd2ac8f5b643454c2cc6b6118b13da16e88f4a6434fc3ba61aca384029f04f36 \ + --hash=sha256:e1d52a05dc417279f7e5c7618c5dfbbc29923aaf9bc0a5c1802ddcebf54c61a0 \ + --hash=sha256:e6720ba44ad7e72174314d0e1fb0172494cff5c73a3a8a2159c3d2402ff15565 \ + --hash=sha256:e738ab81179510ce79b2eac9a6ecf47feffd9e76d1c72e403005dddb6e36c06c \ + --hash=sha256:e904d4d01f36bd6e197590be1533c44e06058771e0746dd073a8ebb3ef880858 \ + --hash=sha256:e9f165403b81fea7e89c932d89046a1e3d9a3a60e8d7ef2f249dccdcb0982bf5 \ + --hash=sha256:ec6a85f424afa8d23e0d9a094e5dbb6eda01da91c92b9183cd433768247ffc97 \ + --hash=sha256:ee19113bce2f3acd46432050688b70f61acd6857d75abb9ec96341b7e9ced123 \ + --hash=sha256:ef3b928d9c984322b5c44e6964d8dbc653da87d2d8ee1647fa6da43072e650a9 \ + --hash=sha256:f273dcf7149a50527c4fd1f55cfe9eac0f60753f5af544b4c9352578e20c0874 \ + --hash=sha256:f5470694918830da62fac9e69133b53d23b736d7070e587b27a4a2be37e08e68 \ + --hash=sha256:f574e460d1c8a08384a016fdb09ccf3543433263ed6b2f97104f979e64ea57c2 \ + --hash=sha256:f85915e00dcb1cd9f2f890ead064ed40a27df06f0db65be427b29482ae357572 \ + --hash=sha256:fc2461ecc45f17893f8207e73b46ea8ba93e33630e51cf4af3fbc21d47462b1a \ + --hash=sha256:ff6b113f50bc066a698db5d944d2c6dc7507168dd3341e255a8892fd0715a558 + # via + # aiohttp + # yarl +proto-plus==1.29.0 \ + --hash=sha256:8acd070469a7aaf43f440b022ef9757c8cac1a9f866e933f59ae98669ddc6c8b \ + --hash=sha256:cfb4e62ad7e13dd18f346cabbda00cab39930d36a05791fd81ddb074d6ee884f + # via google-api-core +protobuf==7.36.2 \ + --hash=sha256:497d0463ff3316681da6c0b9e8d06cb465d61abce00b613ab42226175644d1bb \ + --hash=sha256:89f23aa53c24553a2416fd4fd1ec06f74fa42b14b546d8883128813f775bbfd2 \ + --hash=sha256:912c1221170e16c08d1f086762f563dd61ff83c18b5fa6652952dfaded66f728 \ + --hash=sha256:a300819d441e078a5608c0d3c709796bb548136058fda017ae51d425b44fd353 \ + --hash=sha256:bdb3a345d48db958e6ce1f18e508beb0cc981d64f24088427549c866cd039f1e \ + --hash=sha256:cbc70b17ee27e28894c7fee8bb04be1abead49e936bc70eb60052531eee2079e \ + --hash=sha256:e11e1f0180583a2af89db6a2ecd9e8dc40aa6d2988ca175bfd0e6d12ea72d74e \ + --hash=sha256:f4fee11ec330d238b34a05c9b675f693c20415d1c5bd7d5320cc2f8a798eb9cf + # via + # google-api-core + # googleapis-common-protos + # opentelemetry-proto + # proto-plus +psutil==7.2.2 \ + --hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \ + --hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \ + --hash=sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841 \ + --hash=sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63 \ + --hash=sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979 \ + --hash=sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a \ + --hash=sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b \ + --hash=sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9 \ + --hash=sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee \ + --hash=sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312 \ + --hash=sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b \ + --hash=sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9 \ + --hash=sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e \ + --hash=sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc \ + --hash=sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1 \ + --hash=sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf \ + --hash=sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea \ + --hash=sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988 \ + --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ + --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ + --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 + # via browser-use +py-key-value-aio==0.4.6 \ + --hash=sha256:267c03c3e24cb99d3097612f8a5cfd8e11785c6a2975e272db0e303ea1850bfd \ + --hash=sha256:820b30c7959fc738ea3d00160cdb10cd258f51e38737b2add530defeea1ded77 + # via fastmcp-slim +pyasn1==0.6.4 \ + --hash=sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81 \ + --hash=sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b + # via pyasn1-modules +pyasn1-modules==0.4.2 \ + --hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \ + --hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6 + # via google-auth +pycparser==3.11 \ + --hash=sha256:51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80 \ + --hash=sha256:d875f09c3507d00e1aba0eecc6dcadc1352f30fff09dc6bff2f1c2935e97c2bc + # via cffi +pydantic==2.14.1 \ + --hash=sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454 \ + --hash=sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26 + # via + # agent-client-protocol + # anthropic + # browser-use + # browser-use-sdk + # bubus + # fastapi + # fastmcp-slim + # google-genai + # groq + # litellm + # lmnr + # mcp + # ollama + # openai + # openapi-pydantic + # openhands-agent-server + # openhands-sdk + # openhands-tools + # pydantic-settings + # tom-swe +pydantic-core==2.50.1 \ + --hash=sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c \ + --hash=sha256:0048b6dddc8ef4b64fccaad878bd143b0c3882ea9936279dc11d613f6b7dd1bc \ + --hash=sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b \ + --hash=sha256:028e2f212273d4a39b1ec1e0de8166b1165a65fc0f1111452a9d94fc7c625c63 \ + --hash=sha256:062e891facce5ca296a1c37098e5e466780457f86413894b399f0cf22934f769 \ + --hash=sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019 \ + --hash=sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685 \ + --hash=sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b \ + --hash=sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482 \ + --hash=sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658 \ + --hash=sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498 \ + --hash=sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec \ + --hash=sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a \ + --hash=sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b \ + --hash=sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4 \ + --hash=sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a \ + --hash=sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8 \ + --hash=sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb \ + --hash=sha256:1fa4c8bc12c1354c5550c0c35c1852c8c1901e89e06561724e03f8d0342e1f87 \ + --hash=sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e \ + --hash=sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8 \ + --hash=sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7 \ + --hash=sha256:2ab756b72bd5054e4c7ef3ded331b35786cbd3cf931531a508f79a9537517064 \ + --hash=sha256:2cbd1b75b09e976ed0d6b6ca297675632ca35df86130088457cdc60ef36970ae \ + --hash=sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a \ + --hash=sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e \ + --hash=sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396 \ + --hash=sha256:2eedf82ee4753cdab8e50044c6bd569577eebc3859b11fecf4eb9223761ff966 \ + --hash=sha256:30ddf019d082c117b5d309e5b86710c2a78909907ec1a9381feec3eec02eca0b \ + --hash=sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255 \ + --hash=sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899 \ + --hash=sha256:36c426eac0af8d1529ff8467e612b933346caec1fdc0d774f78f67a1a11e16c1 \ + --hash=sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43 \ + --hash=sha256:3aa9de446b793de2beb6fa2d9d0961803126c4e2a99c2f25ab59b9fd6ea125c0 \ + --hash=sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb \ + --hash=sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f \ + --hash=sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d \ + --hash=sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415 \ + --hash=sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c \ + --hash=sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea \ + --hash=sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2 \ + --hash=sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e \ + --hash=sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568 \ + --hash=sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6 \ + --hash=sha256:476f6ed8e43cd1e0b460920e23571700872b284e77331cb30c4faf459cf48a4b \ + --hash=sha256:48569b0ade9edfbe065cad1d700175546592aebbb42f02adcebcc26e75b896fe \ + --hash=sha256:49c2cbb2397fe4d0987e84606e691af6cb87bc0ee1bd3e7b737f7e10b4c142f9 \ + --hash=sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a \ + --hash=sha256:4be846f55c9477f5f3ddde8f2ce941137e16862a56d018ed885d422bb6ae02f2 \ + --hash=sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9 \ + --hash=sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1 \ + --hash=sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff \ + --hash=sha256:5958c72adb417c39b12ac87525ac60b0d73315fcdc59e21f44ee4a5e2512c9ef \ + --hash=sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f \ + --hash=sha256:5f3cae32fc46121f787cb2486de9cf95a8bf72aec5cc78f64c606fa1735a6ef5 \ + --hash=sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5 \ + --hash=sha256:6a733778df2f7087ec1100ed0b41533e4f3001976e99570fa34f57c66e7f8e3e \ + --hash=sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb \ + --hash=sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4 \ + --hash=sha256:6ed4f3cef55164b026fefb41341b7754cc6b624c75dfe7142d2ecceb5ad21c87 \ + --hash=sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0 \ + --hash=sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597 \ + --hash=sha256:7456d699b13954e9c0164dcb267250a10ae0dfb03e6e26d6796ab0d46e189c84 \ + --hash=sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b \ + --hash=sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa \ + --hash=sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242 \ + --hash=sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f \ + --hash=sha256:79490e33c4c0fcb933bbbcfc3a62184d8803b99f535863dfbb925e1bcb6945ad \ + --hash=sha256:7f476456ac2bb0d937f75191494a09c83a30765fea4f70f3b404942fe25f6cdf \ + --hash=sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc \ + --hash=sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f \ + --hash=sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c \ + --hash=sha256:8812592c85d0edf423f10eadcef42716d71e8219085ad9e85b775057b7306133 \ + --hash=sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61 \ + --hash=sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102 \ + --hash=sha256:8b4c3df25bd323bf1d36a648d563cf1fc69d717451569927151bdad7cad07a77 \ + --hash=sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4 \ + --hash=sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e \ + --hash=sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54 \ + --hash=sha256:94be440c03fede26969a5ce75468e0e6a9927a1b46d9b679ee8adc1b057b0350 \ + --hash=sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070 \ + --hash=sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed \ + --hash=sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb \ + --hash=sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5 \ + --hash=sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19 \ + --hash=sha256:9e4472072de0137ee0d8e72d6620e85939c271d2f90f6bbb4b15c24638b79f92 \ + --hash=sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5 \ + --hash=sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112 \ + --hash=sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9 \ + --hash=sha256:a44101320cfe99432db74237545a63057dc7a88dfe792cbcad0647f2af56cb81 \ + --hash=sha256:a4aaaa791bdae1c972a7e81765f4f3571c926b8e0b9b6e47346499fb80079665 \ + --hash=sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5 \ + --hash=sha256:a7c58106de36ac6a56314182958de20db8d3a29dfd5db527192cc754e4f8e7fb \ + --hash=sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72 \ + --hash=sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2 \ + --hash=sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8 \ + --hash=sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295 \ + --hash=sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e \ + --hash=sha256:b087b1c5be7ac687cf22eabfe4b6b608d40df23610651e93611e1f49118baf84 \ + --hash=sha256:b0d955195bbbe489ad343fcc956eacea9357b79cb22192c66cacdefcbc14b32f \ + --hash=sha256:b281a3b0f0822618fe5e3e0d8a2048b6356b14388505dc9374ccffeb69989713 \ + --hash=sha256:b6d0c2183008c188e19f4906d426b293bdc4f67ab17df8e180fe16cda208fa71 \ + --hash=sha256:bbce99252ba3167b2b6277f1829d5bf4b43b754524bddf7f944707c3db7d2253 \ + --hash=sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe \ + --hash=sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290 \ + --hash=sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea \ + --hash=sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662 \ + --hash=sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41 \ + --hash=sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44 \ + --hash=sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807 \ + --hash=sha256:c531166c42ea7bdfecc8c50049581f05dd1993b09cc7c52bb36a14e96deaec7d \ + --hash=sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78 \ + --hash=sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c \ + --hash=sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3 \ + --hash=sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831 \ + --hash=sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86 \ + --hash=sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2 \ + --hash=sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f \ + --hash=sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f \ + --hash=sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10 \ + --hash=sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5 \ + --hash=sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b \ + --hash=sha256:d8f9e8a6c4ab04b78d61f78627370d834eb004b2869dcb28cfffa647b4ea1980 \ + --hash=sha256:d939de9c82e2126f7f48a7e658f8a85ed46d57662d53f44c49b8895fe94a3eb7 \ + --hash=sha256:de531ce1e2a3364e8767878b58f4ff728a434b4fde089781fe30b1e08e2396e0 \ + --hash=sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09 \ + --hash=sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a \ + --hash=sha256:e6f0cc1bb9900dc558960894adeb30b0c083366fc1d69b856209fb2ca5c36fe5 \ + --hash=sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e \ + --hash=sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459 \ + --hash=sha256:ee6db2fbed51a7991302e8fac498cd67e336246026d0dfa84cf5166ce1412760 \ + --hash=sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08 \ + --hash=sha256:f2c634642694e6a0dad2ab1d375589fa671fd442edd5caf7d9737b8f6ca22906 \ + --hash=sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709 \ + --hash=sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a \ + --hash=sha256:f77ac30b19221cd9bd3fcfa3d4614eff93140d0572ab730cded17b64adca05f3 \ + --hash=sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20 + # via pydantic +pydantic-settings==2.16.0 \ + --hash=sha256:5b6c578049ede4db0e2ef3b4eaa4ad4069cfa9211f83fb38df899dfade50a614 \ + --hash=sha256:7e73acf7f61936a15e5a3b6eedaea29f133357faf7272f2607ba479b049dd7f2 + # via + # fastmcp-slim + # litellm + # mcp +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c + # via + # rich + # rich-rst +pyjwt==2.15.1 \ + --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 \ + --hash=sha256:4f259e80cdfb6b3fc18a7de51fd1ef9ec79652f25019bae68975ca2468a34df8 + # via mcp +pyotp==2.10.0 \ + --hash=sha256:1df2f6a1bcc3bb0716172a5215ddc2f8c7c7fd26a13df9927d52e1746934836c \ + --hash=sha256:d01e9703443616b03c57c700b5cbffd56a1f929c1b0f8f03131bc78c1fca9d3f + # via browser-use +pyparsing==3.3.3 \ + --hash=sha256:928ae7e20211f3b6f3915a72f06a0cfd29ab9d24279dd6346b6b1a7146397d36 \ + --hash=sha256:ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4 + # via httplib2 +pypdf==6.21.0 \ + --hash=sha256:3aec619699dacb1b73f57e87d9adaa4424814517b2cc028de20adba5a37af4c4 \ + --hash=sha256:c16174c8653abafbe4d023b3c9227d226938d9579ffb5a73b4fac7756c0895a6 + # via browser-use +pyperclip==1.11.0 \ + --hash=sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6 \ + --hash=sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273 + # via fastmcp-slim +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via botocore +python-docx==1.2.0 \ + --hash=sha256:3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7 \ + --hash=sha256:7bc9d7b7d8a69c9c02ca09216118c86552704edc23bac179283f2e38f86220ce + # via browser-use +python-dotenv==1.2.4 \ + --hash=sha256:42269a8a5b3fd54ffa6f3d84b18abed50064717576b4ecf03dc4a55d8aa04fdc \ + --hash=sha256:f0d53e69935a851c0dcc78f3ab7aaccd8cabef0b92382b576b824212902873c0 + # via + # browser-use + # fastmcp-slim + # litellm + # lmnr + # pydantic-settings + # tom-swe +python-frontmatter==1.3.0 \ + --hash=sha256:9f7dd9260bec99044219159a329f64f039087f9d1a2124c9442556f2fe6f82ec \ + --hash=sha256:acc73e477a568dc2a25c9e130c6c68ae8daa8c204c8f7e813db47d6a7280dcf2 + # via openhands-sdk +python-json-logger==4.2.0 \ + --hash=sha256:158a52126fcd6869e09574d2b66272666f3dc8f468c62637ef9a1fa883719cb9 \ + --hash=sha256:e371ebe22ec01e289850102091a2b1f6fc9e655c7f1f5f29073936756c290afa + # via openhands-sdk +python-multipart==0.0.32 \ + --hash=sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e \ + --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23 + # via + # fastmcp-slim + # mcp +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via + # fastmcp-slim + # huggingface-hub + # jsonschema-path + # litellm + # python-frontmatter +redis==8.1.0 \ + --hash=sha256:6e1a19beef9225c83efd689c7e6b7da2d5215b1f42cd13b7fc3714d0a09c7b25 \ + --hash=sha256:a4fe1aac3d3b3cc791d4b3d5931c5a956045dc951ee74d1c913ee3ac4d2ee9fb + # via fakeredis +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # jsonschema + # jsonschema-path + # jsonschema-specifications +regex==2026.9.29 \ + --hash=sha256:01000ddf0e3ffef97f2413ceb514f6313040106b6d18a03ee00a4fe35c1eb1db \ + --hash=sha256:0166844493626c5015c6088ee15c9ca2fd060ca15b7641d1657da6a58432ae33 \ + --hash=sha256:044265d77d94f5e3cb2fd72c76723807c429cb8c533e9d4672d0334a6f14f588 \ + --hash=sha256:0476e5bcbe6e1ba3d1c4cc7bbb1c3ba78e3b979b5c8a88d0a6a8cdd4992b8c84 \ + --hash=sha256:066d0e3dbfdd739bce2bf8c2a41dd16f73e3d8adc2eb06dd803a36a307f56075 \ + --hash=sha256:0b65c72739f981377c9c22e0c5c3cd7f42da7bd8a3c9209330fac772c7d893ed \ + --hash=sha256:0c992c19cd45058a4b92f68f139c93db168b48fb1f322c9a7cd620806afb6b51 \ + --hash=sha256:0cc63b5e47c12a48d90c7e9d7de6a035dd14f62868aaedbb4e0ff8ba2b8bfe7b \ + --hash=sha256:0dd8af32e9f7b56b7f95cc1fd79b23054c3bdc172392ae560acc24d57b7ffe71 \ + --hash=sha256:0def9fb6abac55492d6d51cddb7225d07d6f279e774e0adc08569a54a5fc8d46 \ + --hash=sha256:0fd2c901cc307a745ad4bc87f20060d7a0825a3371d1e93488af22e7a387f78f \ + --hash=sha256:1043aedf5917caa861bcb25a9c11460049656bdf0017a90a309fa8f255467725 \ + --hash=sha256:121a76a0985db80ceae9e171c337f8c927868e37d01b54e3ce87bc87f9c6a208 \ + --hash=sha256:143533cc4b6fbc5b95aca0a5b8d541088d374831593def000ec89322c220221d \ + --hash=sha256:14e953ff3607c92d7675bf79c4d4509ef6782aa8c08509f179f9b3d6d0679e86 \ + --hash=sha256:18ae8eed4526e35bdb754d61562b90bf5c00a67fdcf3cc1380dd59597486631b \ + --hash=sha256:19959129885356df0e97556856f77eb2888380dac18bed075a7c05c5128c618d \ + --hash=sha256:1ba8c6a416569ce0d37e83e28a254a61dc99a419084dfb6476cea02d997f74fa \ + --hash=sha256:1c2a0026062abcc321a53db4a185ceba0b59a66b5d37b0808917a88b55a5257f \ + --hash=sha256:1d9fe8091b2e89d470df68a9331111ed008ae8aae6bf1e8e1fba4086a495c84e \ + --hash=sha256:2089fe39c406784d90101c726755ffa1497bb74638fd434300d2b88006186de8 \ + --hash=sha256:23ae6fdad9e63e54038f5ef78aba2933faca61e24d432786589e737bc5522ebb \ + --hash=sha256:26ec4ccce55aa533fbd603d08911b01101a8fcfec987845ac3ae2c7087b2bde3 \ + --hash=sha256:2f7f7aa47b229f2b39a2ae2596d2ad5625d77b5eb9856fac2dab3eb506cdd0a0 \ + --hash=sha256:31b003f9a070335e2a8233ee9b14a3ca8e6d792012ae011f741bf0aaf11744c5 \ + --hash=sha256:32ab11df9677ca80bcbb5fe4eb1da9109a5019239a054836efc6fa1c64e683cf \ + --hash=sha256:33026515aebc0e70d1c89978e53e8d695d35d9e472f8d5b34465ba3c74028650 \ + --hash=sha256:34b6925af9853bf461950e6508910f179fd6e9b1a7ec8548e069606b7e51a26b \ + --hash=sha256:352cf115a810b357caa35193ab656ecf5ef41056855e82f292c99e8514f8d954 \ + --hash=sha256:39ab5894d971f9ac68baa6eca5c50387db579cfcacf36ae8df3feceb1815e6d0 \ + --hash=sha256:3a21a9509d0ee88e7a70e1ad228cd2f0e0fd1e187458db132e8a8d18c97daf9d \ + --hash=sha256:3c5c2ef13797466aa64170cbb66ad98a32351dd4127694cea7199f80f213750d \ + --hash=sha256:3e778bfccd63075167709136afbc251c1f683758d5bf49c803c60ac3f894ce6b \ + --hash=sha256:3f1e6cb402a89457582cd696f982559217d13484a193202c394015297968c86d \ + --hash=sha256:42e82e578c904445d4c8a35b8f28052cf567593215fa5db06266fbc6f77aaa2e \ + --hash=sha256:4408b2b27a95ca8cc48b7411945753773353b5c93b307754781086c99d3a576f \ + --hash=sha256:446654b29bfaa30500d80947eda42cef1449dc8a87f4e3cf061cc8485d3a1f0b \ + --hash=sha256:45010bcfe66df41522d56c9b6114e87ecc597a08970ff6a2ced24415c141ae5f \ + --hash=sha256:49ee178ca31c94621294bf9b8b676a92a2e6bba8af0529591753719e57edb621 \ + --hash=sha256:4d7d93613b01b0199961330e49cfc52d479b3d5776c56c691db31130c0a07d91 \ + --hash=sha256:4fb41211d2333eb930a51e0546a65999761cf1f572a4da56ef9b8a62966c06f2 \ + --hash=sha256:4fe97894d1b306c919b4e50def1e6f6c522f4d03a7283811f4d108f1ce5d3ac2 \ + --hash=sha256:554bffadcbcb6d5f4e5fb10a61cc52084b9a63d1dab5f10bcd2c4343972e8e2c \ + --hash=sha256:59b49507f47479e299a9e1bc41b5cb83a7afda0540625f1dbae886615978acbf \ + --hash=sha256:5eeb8edc6110d9194a4d0d54610f64c37a31c605b5dbb7e407fc6ec7fa34a4a1 \ + --hash=sha256:612b709381c0355b70d89cdb51b7f670591ed5cbbc0e3b5337488019dc667b65 \ + --hash=sha256:61956f074ecd123f55adca68ee3eab46e6a07ad3f8e64e6db95dfacb444f55c4 \ + --hash=sha256:6398d5145689503412cc1748895242598d8846b8967b851133b20dc2ed1e21e8 \ + --hash=sha256:65b408d8fcb273e3499e7ef2ce796810da1becd208c7fb4373692a242d79d461 \ + --hash=sha256:686ac5350fceae63830bb98805fcb8039325bf4c06d9f6f048ff65229d5bffa5 \ + --hash=sha256:6a1a824fbed817e0a891103886b68f063b1e83cc51bc97192a90a60195a9291f \ + --hash=sha256:6abb75ab16bc3281714a5b99548a2225db70dba1f995f6d7f7419b76eb5a8fbe \ + --hash=sha256:6f7121a8914ed13fcfe2099f895341bfb789f004d4c5a0bdece8fa667da10849 \ + --hash=sha256:7020ed44df30b3aa492c00ee3b52d0548c1f30c2c6c5bb13ae897680900d3413 \ + --hash=sha256:720537c7ea6f80dc61913184edb0ce2497a306b39ef19f28505b322553d52bdb \ + --hash=sha256:724184b4aafed865e4f13ca313fdcb43024300c028ec67319cfa16847d84685e \ + --hash=sha256:7c03031610e3e6ed1768a2b7a8fc84637c1257b50c5eacaf094c6e17a84fc563 \ + --hash=sha256:80a5ea3b4fd9d6a5b9a44f7976a9acaaab35aa3c1f6b29e5bd857dfabaded223 \ + --hash=sha256:80c7cadd3fd2bfde5df8aa0787e315812cad0c313a753095d02f4c2b6c01677b \ + --hash=sha256:80ea96f5c1a30bf09007d48466521d9c294bebe197c708c3359096e3e3691632 \ + --hash=sha256:864e9b87ac33c3fb9fb4ad48166d4fdb579c351d5c77deb0d34bccb36a775cd9 \ + --hash=sha256:87fb80cbe3557e27e7b28b995c2b2eedf689b8886f941ab93e0e288f0976518a \ + --hash=sha256:8873c4a11c50b9989168881aeb3f08859f469d809941866aa1feefd8be5431f6 \ + --hash=sha256:888d60953908dcf761aa320c3e390ab8556efbdb551ace63921de90f6ae0848d \ + --hash=sha256:8b5fcc4771732191b2b7d1dd68d8f0353f47f8d90b6150f6dce58bf1112442cb \ + --hash=sha256:8f39588af4731c8923c26810eb3b33f76f17633985e40f59c3cd45a33805a895 \ + --hash=sha256:9173db3be74a35cb6731701094b98120f7ee4876a287882a59cdea1fa7da342f \ + --hash=sha256:92f05c9c42bde5785dc48770bc2194d9f7442544156f951e19cd31b096cec562 \ + --hash=sha256:951733b1bbdb71e377cec567b409f1a7881b47cfcad84121aa74cb575fa425ea \ + --hash=sha256:957bb708e8057ab1649ba566456429d691ec9b90d1c9ad1af1ba7ffbbeaf05f2 \ + --hash=sha256:9916fda742cd4eede63b286f58c06718324265d727ce0856eb1aac86d0d150d6 \ + --hash=sha256:9e1d3a4cb7993b708f0ada8d0c84590efd853f169e7147d2202c9da503180242 \ + --hash=sha256:9e4482589065c8ecd761cff522dcd85f2d39e62f551e37e025d1c7d54772def3 \ + --hash=sha256:a5300757f8a68f5b6cc33f57338d72a0e3589c5cc9ad5f8504ea06f028be582a \ + --hash=sha256:a540abfab208e1b7ef2df231c40ef3b6cbb30a0aad6204e9b6a81c10a6794628 \ + --hash=sha256:a5758353650079898dc1b2b0e95aa51fa23a30d020e06f62c430dd08ee56cdd8 \ + --hash=sha256:a64b85a4760337cfefdb27d42da6ed8b58e8cde3f2d57b6ef43e76ef6ea9ef47 \ + --hash=sha256:a655d34b2a6943af32401f3d94f72e9d731f6ad16285815550bf2b4ee69d420a \ + --hash=sha256:a714befaacbd10092ffe4cea0d3c5f008fb9efe9bc322c715bcdfdee414b9a3d \ + --hash=sha256:a760da040b47767b4b873adfb7c3b691e9ba2fc60f113f9d0b88f1a62f323e85 \ + --hash=sha256:addd736a0547d553283adaf4e05d7104e7f2c7b0b092e9b4d28756825f14531f \ + --hash=sha256:ae4613d7d9dda60fcba95f846cc6f808017f1843f392cf9daad14a6534493d71 \ + --hash=sha256:b11b589e00095ec69cf79841a76360f9b079e95b0368a25b5ebb951ab0c157ff \ + --hash=sha256:b3e445b66c80b4eb4234e855ce94d9adc183eedbd632816228d89930b91b2c5b \ + --hash=sha256:b7b893976e7fe42053da64f2aa27239c24252fd2ec6df471e1be197c0addc3b1 \ + --hash=sha256:b84f186a7f0536fe4ff9a9fa12d06d007b9b71d4b5352ddcc41f59ad6522a312 \ + --hash=sha256:b89efc38431793d28b7cd91227e2f952ad7c48df19132b17f43a5fec3c14143b \ + --hash=sha256:b97a38fb4c732b6832db6bf108963adbcd82ef1268ba2025dce390f45af75efa \ + --hash=sha256:b9d74e4eee9ddb64c2e92d5d61472c59c21684c059eb7b68767be9628e977859 \ + --hash=sha256:bb90e7177944b6684738c1fc36aabd2dd00d1de3be7dbe09f91e196f1bc0dc81 \ + --hash=sha256:bec37990e3d6121f29ecfb594bd8f1bf009e9f7926daba2e50e3b27d3892a783 \ + --hash=sha256:bf3c49863c23a1ad6da9c30351aed6cff8d5ddbeb63c5c8420ae54e98c7d0138 \ + --hash=sha256:bf48516e35cf848390ea68850aba53e7c333720d2945b4d2c25b69fc5171723f \ + --hash=sha256:bfc71e6d970419c1309b3640305298643e2a734cad3f7cfb6d2ddee4175ab53d \ + --hash=sha256:c0094897d7d01f184b2d7fe8c56c66d64efe01b31f4b7d34205b391387df1111 \ + --hash=sha256:c03c6eb6ece86dfdcbb34799efaa339b093132e1aceed491ba5e08fe06cdf699 \ + --hash=sha256:c1a9a6651197fbed6f0212591418b9def774fc3f8324f78d1bf0e6a63e5f8aa1 \ + --hash=sha256:c3589f40749acce747510bf5d589d54e376cb0930ea58b35effac97e5312b0c1 \ + --hash=sha256:c4e38dd8f39c43a91d2410ad2b85610701b0979342c3df1d69eaf8e838c757d8 \ + --hash=sha256:c6c8fabf1dafc1f1ddcbb67896d3f93efb092e8c4b6322d7389b944e76a484e5 \ + --hash=sha256:c90fcf7804ea0a54b896ce0f2b9565350220b8d4890fd0db461a476a4c687963 \ + --hash=sha256:c9b602fae1e00b7c035d661ce85575365719192a7b46784bd71cf64c68053aa0 \ + --hash=sha256:ccb64d887a9db1cd76dbc0f92051a1a478a2a67e7f56c62d915cb881d7734704 \ + --hash=sha256:d06fcdecc10fc7954d7c8f27a03c96055fe525274dc84a7b0dbdc3d6b9e03dab \ + --hash=sha256:d0c3082bf79bcd6a614d55916590ad4b8f93200e10b97f463ea5d9d07c9b5f23 \ + --hash=sha256:d49c18f1ea294cf4adde2e5ac256e98c82ea9d708462ce4bf799dffa7cfe8a2c \ + --hash=sha256:d60030baaa7bfbb02d650c126cdcddcb6e33dbff14d819434c8fa2fdcaeeeba5 \ + --hash=sha256:d7cab119d0df0b9413f106b4d7fc34f2872d3574ed3806fb48959c830b1537da \ + --hash=sha256:d9b77b25b4f395f92de6099ab08e8ae2bc7e51dfe157f22900902243a5cc90c7 \ + --hash=sha256:dabee8f4935e731fb46b2a3091bdda0d3d94b3bbfb907d2b4f12eefce4009619 \ + --hash=sha256:db5e82ba15c142425b8406690032df89e39cca4a2e8afbbb9a3d84edc2373ac3 \ + --hash=sha256:dc79d36d0618752265f0d575915bdc5c5130ecb9c9f6b3bcefeae32e4bdfafcf \ + --hash=sha256:ddfa987262763c3c22a8367d2a49c244b018a74c3a8e3ab1a864119ad45c5633 \ + --hash=sha256:e1172147d28d8fbcf8cb8d26c41506169f5ad8fe9ec969cb116835a19d4d8eca \ + --hash=sha256:e11edba5bc344a32b029a7af9d4b3173982dd79eeafa0b9dbd787364414b0509 \ + --hash=sha256:e2c89e9b762c57f59d5e99ee8b20202adb892e35f8d3485741340999ca55058e \ + --hash=sha256:e31f72490b7c12f7790e1e25c3afffd20503ee1bfb43461d7838b871ff244b19 \ + --hash=sha256:e8c65ef3862a8ad6e86492b6ed9327805dd66904c012bd3649dc67d822ed6c34 \ + --hash=sha256:ebb8912f565b8cdbbf27debfe00df04202c20e2f651b9e32767930c5eace3621 \ + --hash=sha256:ed511a0708e2297e1d6431e7fb217e3402791e491e02da800658ace4973df1bb \ + --hash=sha256:edf06545875f3efa31560d94121e95c7fd70d98b1dfedc0157097d79b13b52ea \ + --hash=sha256:f0fe9834e5aeccaf19a0d8feb296d66a24be1a7c9922002f842a682cd5abb787 \ + --hash=sha256:f1a0d5117230dd46b399a30a38afa44f79c99f3168988fdc4f425c3f928b39df \ + --hash=sha256:f37964e4a5e993d2fd45147741e9dff7f34a2d8c00ab94c4ea0514a4677f959e \ + --hash=sha256:f57dc6b8fef170f105d2cf5cdce254f47b137d7755086cf7050f47e16582abba \ + --hash=sha256:f93bc1c3486ef3747e07c9d7c1d0a147b8fbaab975f80e348aed6f71309dfaca \ + --hash=sha256:fb00027a09a8f9f08028b40dce4c933cf73e4833240ed356583fdc9cfa721566 \ + --hash=sha256:fb99cc9d45f48895d9d67f6a0b8a57f08d39c174d9f25ad97a313e0470267b1c \ + --hash=sha256:fdd88ed5e20b1bcdd234421e454962c971aa44b653bdb7f1ea9ef683e90fb649 \ + --hash=sha256:fe3fa1dd453ed5c7f5ea23a26218329790ed7197a99b90e94330e313959a7f52 + # via tiktoken +reportlab==5.0.1 \ + --hash=sha256:1c36e6bb0e71780c72331eba60da7f602e8d4389a8723825af71342e49d791e8 \ + --hash=sha256:ebd13154be1c8515e665de70bd2d303ae9ddc3ef47e44afd5116441ca0283a26 + # via browser-use +requests==2.34.2 \ + --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ + --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed + # via + # browser-use + # docker + # google-api-core + # google-auth + # google-genai + # opentelemetry-exporter-http-transport + # opentelemetry-exporter-otlp-proto-http + # posthog + # requests-oauthlib + # tiktoken +requests-oauthlib==2.0.0 \ + --hash=sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36 \ + --hash=sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9 + # via google-auth-oauthlib +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 + # via + # browser-use + # cyclopts + # fastmcp-slim + # rich-rst +rich-rst==2.2.0 \ + --hash=sha256:1ea1c43813dc4a8d86475fce27ffb74e0ada3d4656c9f8130ba2a59868055fe9 \ + --hash=sha256:b1e6a67f8f694a6f36035624bf73e2b1a0a4be13edaf3ba5e654d9758b61073a + # via cyclopts +rpds-py==2026.9.1 \ + --hash=sha256:00ba2d8c7dd4ee537978ddf4b3fbd712bef2d8751603f7f3146b3f4287768e25 \ + --hash=sha256:01445c8d194aa032a08e944f16567672da1c62dbdbefd8b6d0693032e290cf68 \ + --hash=sha256:028ad274ea951dac64491b5d1e65712a4aeabfdbdb9fccf797b57bd899b0c495 \ + --hash=sha256:0483515261947e4e8b8e1375bf7463e7eb6ccfb3d86e7b554d90cd5285f20f32 \ + --hash=sha256:068c37bba854ec2fe42f7365c640af11dd9895890ccbf2df5070d0c059bd7f96 \ + --hash=sha256:074a4d198bc34d9a8ea425114fc3ded6d11ec01f6a314a8db67454a5152d8834 \ + --hash=sha256:07deecbfce94c78473018bc7d10b337cc651d12df87a1eb2cb3e4024bc9c33d0 \ + --hash=sha256:08dae4a4095150a7c4545a1fb40b98e1ab1744fbc2770d92c977b9dadaa49ab6 \ + --hash=sha256:0da298fb372dc192610a4b9ecbc68a0cd8b675bbbd1fc519d01b41cfd658333e \ + --hash=sha256:0f045bb053c9057720d72c56dffe30dffdc05997b2897a827b9325f0ab6623fa \ + --hash=sha256:10e208f2425d973938afcd56e28a7c4be32e27b6a60b5d381f49fb9d8acf9759 \ + --hash=sha256:136a1c3fe4402b7008bc81cb62ee538481795b61a7e83df88dff3b3f02b726ff \ + --hash=sha256:159a7aab5c5e8b112c8830f54717ce56da1252ebdbb526f5be2df2309280b9e7 \ + --hash=sha256:172e47169583f46ce118cbec68e6795d0da0f4606b488b6434f8276bca0a058c \ + --hash=sha256:1c2d1f6da5128eabf34e963d7163a818846075a52568250d006c4c953b40f903 \ + --hash=sha256:1d55198263bb51f557550c6ed2e6d1cb6a6fed6eb5c9120b741c5926bef8a45d \ + --hash=sha256:1d77b649e6f7cdf12ca5c2a98dad0ad37f9ea9b6f960408a92f0cb12bb3d04d9 \ + --hash=sha256:1e8d4d79d828299bf44a55db22a9388ab967b49d17132c88eab0f4360b48da8e \ + --hash=sha256:22ffd29a63d71fb1b81552c21f2c2b734949b7ac751a9be70675a939a900839b \ + --hash=sha256:2693b2728bbcc48d09a981a356954b0c47c53ff25b545856f28a889ea619f69a \ + --hash=sha256:270bdcdaac5d5b6f73c5e22e7e135c7f2a50e789f71d9e241d5be8d90026e19a \ + --hash=sha256:2711d29b653b3bce48a63d18b9c6b53274669e6d6c4094dddeb4d9a0e45128b2 \ + --hash=sha256:2c16ab111bc27c646ba8aa005d0527754edc538ebb636f0b1bf8e244b48d1945 \ + --hash=sha256:306ee1850d8105b5baf977e78d45fcadd12c1a54678d614c9baf217708446e91 \ + --hash=sha256:3231c4c0e521dafa5be0c9f114ee2c2ad46650836f2d72caa86801950c3e7044 \ + --hash=sha256:3890a6aa36e6baa53d5258a2a25d3ef8b37ad165a6ab27a892d7c3e3a432cd69 \ + --hash=sha256:3a72c11530d71abfb66c8d7696a2f86c43e63fca8b948f1a784ac490f4ec688e \ + --hash=sha256:3b5a6f40f0a1486b4b36c888123afc67acdbd9f33235927acf5ff295429a0ba3 \ + --hash=sha256:3c91c210ae7645626c608400e3519b4a642f837cce09ca830db3beb2e9f274d4 \ + --hash=sha256:3cd182d7291d29b92c521a0069d9c01ba6193628a9a105531d11b40a6d731a33 \ + --hash=sha256:3e524c7874ac72884d28e16dd5b8d839fd09e0fe76b020d3fbca23212a7b8c52 \ + --hash=sha256:3e93b2cd69a9830be33e03945cd7cda940a0a8bfcfbff41d6144f0cb0d3d8bd9 \ + --hash=sha256:3edae8c5ddfdb6985d49ae9d150516e5076888879022f91a26c2de9276ce0bdb \ + --hash=sha256:3f0e9ac28fc067d4d34b88ae43c48e9489455c97fee9633d851f7eeed5a05d35 \ + --hash=sha256:42e75466f83cd43f6026c81eab74246efb2bdadafb307b85700632d06c68f299 \ + --hash=sha256:44b32a7c4f0da3d28af31c259e38ddcff096f855e205ed0671d02fcf44f1ea1c \ + --hash=sha256:457866b85daf5034296666168b84a69e0b2e89dc4f1af102b46f6448a60b9063 \ + --hash=sha256:45bc6bccf78b20fd834237d18db64965d7ee68ba7f60440a26c7ab71e7b8d51a \ + --hash=sha256:46d80bc76b51a6c24f9944368c28d38b8bcbcea1da4f2f8d3ebc31a67e8c6ec6 \ + --hash=sha256:4793ef7f78268b124b73fa933440f01d258bbae01de9fa53e9080c9ab0425a12 \ + --hash=sha256:492e5e428cbe126221611f47e068f01660352feec4ad18bc0f5ea9b2ae88fb14 \ + --hash=sha256:4b26b03d9d2658ee2fa234f8f4f19f38a09773fe5261028025032e26d4d35af0 \ + --hash=sha256:4c0d2cb595a420b34d5086db0add011e26e2c09d6a024afbac4228bf8f863a30 \ + --hash=sha256:4cfaf02209061880210819934de2f4f6aa83dc04dafe6770276acc240a56da31 \ + --hash=sha256:501909f2e4a1e2dee528ef766fe3c469060ebc17e54a8383d404ba07a81a6f02 \ + --hash=sha256:50906f5aea24b5a865cbd0a589698288631d9f3a54c3a937c83aefa95a0d14af \ + --hash=sha256:54ac2158a6f96cfbabff0b2eedaf94b90c5ec7ca8317fcadc61e1c2b2e0ff6ef \ + --hash=sha256:56c6952a9b15047466d0c2347c446a761d4527f89976156341e68f0ce5cc08b0 \ + --hash=sha256:56cd8b3f77d7b6812f533b662186a1f28316931166ddc00fb893b1b0db7e9888 \ + --hash=sha256:57492a550a1d88d29d003247e5f78dd8cf04a701fac0e4c8db8745a6d2504e0a \ + --hash=sha256:5943980471829f6de242a20b109de3111ba6b77e3af0ffc587028ac854b05e6c \ + --hash=sha256:5c6ee90dee3e85e055ddfd502d611643d9b0fd94c818220bda84ec3dacd9b27b \ + --hash=sha256:5c90e7fa02e8f5de0d10c17595c568ada48c5302e749462c0ea1a4c362111a86 \ + --hash=sha256:5ce8943f79c2210f7abcc28e86367b03b28d95027fd01c46d2472373ae70c86f \ + --hash=sha256:617f59cde379b4f648a09797b7f683d04b90a46344cddab85639da5aff0f5531 \ + --hash=sha256:6307a0da524939decb8ca4a3933b8ab62525794411d6984fca6726e732804af6 \ + --hash=sha256:684fd492fff4fead00587544e059be2bbcb6f93454f21fa2a91b66fc7508be82 \ + --hash=sha256:6b5b393eda5ea42cca1c1a6665f2a4882b4fd5d1777e41ce0545a107fb008c9d \ + --hash=sha256:6b723eb406dec5bc9ec516c73ab9c3239a3284e017f7eb89ee2b3258bd504fb7 \ + --hash=sha256:6b9bf3135b4ad5981df9a73d71a35272d650a2985ae9c2746357b24d59de2448 \ + --hash=sha256:6beb738155fe8ab8091afdfa5a3226b21c2b1593f1e50ebb90eb25b44dbc0391 \ + --hash=sha256:6c0dbbcc19735fe5f8b0a54c07659d154a9e69f47e15d0a6ab7299215daf62cb \ + --hash=sha256:6cdc537c8633d7fd92a82e2e0d2ab74320a3f63d5e59fb9cf08711e08fe151c4 \ + --hash=sha256:6eae33003518fd4cb4f83a218d5371469dd3001aa3b87128c005b07762f7fe5e \ + --hash=sha256:740d0a99cf9de0b17a3943388e9294a59becf75e7c43421f387bd3c7a9901f7c \ + --hash=sha256:75c38c50ab9aca840225d9a9a3810bf11d04bd5c1f186cabbb8aee56db3e9b15 \ + --hash=sha256:761fdae6728ceb99ab182fad2f0cc1e262f610834dc891aea1d1a2a2e634776f \ + --hash=sha256:7664419f27db41d4f1c43a78dccda7dd6e8ef2428df3ee01d0c2a07a6b071297 \ + --hash=sha256:76d3af9732d2dab69f28179b40ba2d87e2f1d5824b4a694780aa787d685e8f36 \ + --hash=sha256:78326f4cb4427a56ba4996c0762b63be45f06b85f086526420d2b3a66e40f84d \ + --hash=sha256:7868b85224291c6cb6759f9b5adb9745f486d226f62b16a614dd5a2a5ab2b35b \ + --hash=sha256:815d26356930846a40c7bc1366e7b1b0320ab8a063e66c11298a208bed0fd237 \ + --hash=sha256:8171b44a054e5c67fd748ada04187f1250bf35b95f85e52ab64bcf3331a923bb \ + --hash=sha256:821b2755db9194409254012f429c56643416fb96ef9be090be82ec8826b7f477 \ + --hash=sha256:837c6b305e26fe0f75b15c92cf3b2ba29e0ae19dc40b1c557b026cb426347d0c \ + --hash=sha256:839dde845559254f34885267c6878f60d61d5205180226d976fe488d45fa128e \ + --hash=sha256:84a6ecc0c940169190d2c23bd969debd48c94dbc855acd60188a68d71d421608 \ + --hash=sha256:8601470267d938bcb7f3ab1a336100af51a4fd5b6ed030ef52461bb3ef5e7e07 \ + --hash=sha256:88b5268892fde430d5531f95bc560b6efbbd67c929662c586afd729a96e7461c \ + --hash=sha256:8aa5dda18d39b6143eb24809d158f9252c88f402749b6f1b62a506cc7d96cc35 \ + --hash=sha256:926bdd3e3b5998ddf70cc64bc8cf57209571f9044542913afb673799fec77dd0 \ + --hash=sha256:96beca19ec79de272e8668585380ff9092c47077c1d7a1e098e00bbd921f4785 \ + --hash=sha256:9a0460d43603d1fd9ef59c30278531e15d78581721ddb538fa560aa7817ea4ad \ + --hash=sha256:a03d57b86d2a51d0a66c92177e2be154ad015f357791d306e714569999cdb4cc \ + --hash=sha256:a36b70596407634ca82d4b989a3729074a008537a0522e4c8046a67c729103e9 \ + --hash=sha256:a3a52a3ba86436ab3aef510fbe21512abc2ddd1993005dfe50514bd2284ef025 \ + --hash=sha256:a3dbc5ed9514908d5046107d7b1346bde71eea61de6e0e4919c19354f97e769f \ + --hash=sha256:a431156bb41865fc14cd5d79bb9d7bbed83110b0159e34e62ae30951f96c0009 \ + --hash=sha256:a575404ebc9cf2e91edd32eaf570ec1430eb900d4f56724ba7dd4bc1fc9c176d \ + --hash=sha256:a5cf77eb04f20b720be95265a3e00eb2a14814074255cc27069c551b2db53118 \ + --hash=sha256:a8763f20692da7df39b0afdd1ba3042b004c50a45994f76c2d9a25641f7673db \ + --hash=sha256:ab4b2fda7c2b542f7f9d886cc6a838c5079d2b76f72e6081411faba11adde2c9 \ + --hash=sha256:addeda51556dac7c1a2f14cda62db8b621cd12afba3091d03a96c72932387eab \ + --hash=sha256:b242c27c8f836305a4a72df9cdd564386ac57b807bd252a063223331c9316b37 \ + --hash=sha256:b4f062343e7ad3fa94f2c66e5ae667dee47ee74dd41a9057c4fbe163236a123d \ + --hash=sha256:b5b8b0753718d258fd454283fbd57e14545d3b40583fa672e27cb4f987626bcc \ + --hash=sha256:be3e47e2d91aa3942ff9bf4077a505226005abfc39b6f7554a91c1b9393986b9 \ + --hash=sha256:befc2d6a953e563f8a7bfd87a42c22ebf8a3e980dcb7b6a4d17b70b0e914e8a3 \ + --hash=sha256:bf35d0568abda97233239ce32896d3ad53fccc537832c104e30c94aa5fb93569 \ + --hash=sha256:c933c6678c6f116ff8af47a4c6db0868b8ace74af0343016c0ef00f00272ea69 \ + --hash=sha256:c9d1aca01f49170fdcf5c92761b1fafe97f554b721ca4570c5949fff778f0d4b \ + --hash=sha256:cdeaa99ce822dca76cfb1b993e9120c5ea212f2eb66d48950ad63c349668a018 \ + --hash=sha256:ce4d4f52e2a4324396caddbd45a97d8d7be5f42edd25d2355282a9c34f9b2f7f \ + --hash=sha256:d1028417bb44037eb3069c1009bd7b7277212876cda22fbe565b0bca9fab6d2c \ + --hash=sha256:d151e148117294133bf8af7eeace085e7e87432db15ab6adf640330298a47f6f \ + --hash=sha256:d7841166b7fa64c9c56404617ae4341448847482d45933b13135d26c130519e5 \ + --hash=sha256:d7fca4eb6df565e2a928f1c7dad92d27db8f9df0f449e76423ed5d7e713ed445 \ + --hash=sha256:d95a354e02393eada6d7351184671aced9d4cce109dabf927cb7aa99624352a1 \ + --hash=sha256:d9edf30457d74eebfd76b045535e36f1cd89062566a128a0db2145ca042d787e \ + --hash=sha256:dbc2673f9223d420c91145599b3ba45a8a50c207d1976908e5fb5ddb0c9b9429 \ + --hash=sha256:e01b3c878c8641913e688edd1b3f08658c6783d29cf6b826bd3c0d1ae7a1ffaa \ + --hash=sha256:e21c1429e205828ea886a2293a4a2c8e01f4c25d9893ca330e97a6cf73f52e7b \ + --hash=sha256:e43d4a1f673e8a1cbd8533e809e02b4bf9d4f2280269bb640436556312121250 \ + --hash=sha256:e6d198bad4e49dd6732fbd636e2fc5c082f45c8cad0b4acb756b00c82c76072e \ + --hash=sha256:e6ea1cda8d8c688278430e4268a42f5e5da3bdd74578dfadc0820c3f1766ce83 \ + --hash=sha256:ea394a937f17a54c51239348bdbe2e3518124c8d4a8951ba04a311d3095bd18f \ + --hash=sha256:eac2f5dbafd585dfe31f86a23ebf0d3ba480a9d49ebc87947267b5608d4ea0cd \ + --hash=sha256:eb61be926bb81567c1f48bdc8aa22b9855048dc2efd53871f9f7e6e9a5632346 \ + --hash=sha256:eba5d173f7d5708b22a93815017a4611873ed54db9f268077c0dd1ed99cfc858 \ + --hash=sha256:ec450527cbf485e13c8d3602a54f428ab0432fdade0ede75efd74b735421c871 \ + --hash=sha256:eef6a03b0b6d08d0835ccfa8ec8d1bc70525e3801387567137b50c557695e6da \ + --hash=sha256:ef0d8c843e2827d6c120ab4687e9423fb1d893db1df27b7c1506615bcb9734a0 \ + --hash=sha256:ef6b65b03247c54692ad4fd9ee97cb772781927db72e3cb05e70b3db6d1ff14f \ + --hash=sha256:f3d6ed6a98cfd19155996605474982cc470d7601746a6439078f1a5a3fa8b050 \ + --hash=sha256:fce4b85234a0cbad67bf8e6e1201ee815d172c9aebad75f25645bc4d834f8e31 \ + --hash=sha256:fda1d96e542c37b6c804547dbf489c129fe7c97183a76a5ec275909ba1a063df \ + --hash=sha256:fdcd198979b4ecffcc1beba366a7fbcf4eb41243691a82fe52ceb0b902f09c12 \ + --hash=sha256:fe5ad0664ec772b02c45859041aa17655709cced7a31005817fbbbd988c25567 + # via + # jsonschema + # referencing +s3transfer==0.19.2 \ + --hash=sha256:ba0309fd86be3c27dbf78cdd813c13c5e1df16e5874b99d2535ebbdfb9892993 \ + --hash=sha256:d8168eccca828cbb2cd573675333f3bddd254313a9c42494b84c76b539e8ba25 + # via boto3 +screeninfo==0.8.1 \ + --hash=sha256:9983076bcc7e34402a1a9e4d7dabf3729411fd2abb3f3b4be7eba73519cd2ed1 \ + --hash=sha256:e97d6b173856edcfa3bd282f81deb528188aff14b11ec3e195584e7641be733c + # via browser-use +secretstorage==3.5.0 \ + --hash=sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137 \ + --hash=sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be + # via keyring +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # markdownify + # python-dateutil +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via + # anthropic + # google-genai + # groq + # openai +socksio==1.0.0 \ + --hash=sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3 \ + --hash=sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac + # via httpx +sortedcontainers==2.4.0 \ + --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \ + --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 + # via fakeredis +soupsieve==3.0.3 \ + --hash=sha256:7dcf6022eed0399eb9934a75e020148f7a2024c37b7dfcd3cf2c5505d69c364e \ + --hash=sha256:fa30e3ba4809cb81ce1f3209f2fbe3e779fc445f0439bc147a0d7c4601743f21 + # via beautifulsoup4 +sqlalchemy==2.1.4 \ + --hash=sha256:07c60abaffb980b7382f2c75be8a5279c2b5df2626a0f5d751dd942799bf3b5c \ + --hash=sha256:080f8d853aac5bb5620f0ae6f46527397cf18dce0ec2b478b478469ef3cae2c4 \ + --hash=sha256:0970394ec5d9e397aafc5bc5fa2b7f8b58cb191f2703006b19a96ef4bf00b8d9 \ + --hash=sha256:0a9a464bc360856b7ea9bf8aa26aab92ca115dd08149cb0e004063d5db13584b \ + --hash=sha256:0b96edcc2cd60fe1e35f67a46f4eb076e57297841b9eae949ac5f196593f00a7 \ + --hash=sha256:0d1ca95e42ce3c18818f170b741d30a33b292c6f6b9a202ffd717e28fc99b8c7 \ + --hash=sha256:0e01a3e199ae219381c4889993c5584b1b905fffe6830f639adb6770036a8913 \ + --hash=sha256:0f672ed6972164fec94a8f0b21dcf8545080d0727866335fb8adf9f4764ce6ec \ + --hash=sha256:12642e105b4e0cb2ca8428037368c1cbcded7b9d0344174607174d82b700e1eb \ + --hash=sha256:14528d37d7d46a92f2a483f188f7fecd86cdd789254a0412b960c9fc5e9efd6d \ + --hash=sha256:1541ba5bf0f232cd61f9ef3df78c93977c72ba6031506a0e6d057b2a3ddb76e9 \ + --hash=sha256:1ac64fce94c5b389062d2e3806db5dc780447591e0dfd5ead218c884f0703f2e \ + --hash=sha256:1d66fdcc5506e0f8bb8d3f4f95125220a7cd6c46e8b1762750f01e9639973dd8 \ + --hash=sha256:22129e7d00ac66b291840c4dc83a9c497456ab5bffa682dcbfdc2356f9e49e5a \ + --hash=sha256:283914efed30e4d44301e36ac90ad048570538b8a70f072fe01578d9b205d09c \ + --hash=sha256:2e1b5343d315b10a4a71da481729f66f830a561595e02b61e8a5a65d658325ac \ + --hash=sha256:308f96d24e773d64609a2a0d1161a068f9f6e9165523bc4e07aa9c45f0c4213f \ + --hash=sha256:3341ddc430733cd961bc064889f42712a0b4056733a21c83176842aad67d12a6 \ + --hash=sha256:343a0493a81278bfe30be1ec81214a55f2f44aaa4662d230be359ab2aa18cc2a \ + --hash=sha256:346d144e8912ae087b10d3c2081657cb634728600693eee6dbb71d7eb4768101 \ + --hash=sha256:3c998d70e60fc95e93e5971395818c50f8a34396a6352075256fefac6b5cf81b \ + --hash=sha256:3d2eacdbeb990b80235763860923c60a8393745b66f7149a734980c65896da72 \ + --hash=sha256:3d675b0856b6703b29d023517a4c19fecfbb55214ff5c72cd813527e40aed9b4 \ + --hash=sha256:3e5045fb6aadbb0f978ab9b9d8822f7b7a97d2281814e7d13d791155664eace3 \ + --hash=sha256:3e5de57c71b3460e2ca6137e82cd3cb8c9f711f301f50d5c77156fdb9c822999 \ + --hash=sha256:3fd608a06bafa768ad5711df4e17eb058bdc490e9df7d39b12a90947471e8712 \ + --hash=sha256:418786f05387ddb66ee683a1d016c5a8d9bf7be921e6ee8f285c7b6ac961a731 \ + --hash=sha256:42c37c06adcecf444e8c981f7e9237a41bdd445c83da0df9e08b4ad958becbbc \ + --hash=sha256:55072780d1aae84dea443ce27edeb745f6cc4d19ad89416abbb6b49712080e7c \ + --hash=sha256:596a95611c217cb19c21f02f43c637cb507cab71dcf0467c5c7d98fcdd703007 \ + --hash=sha256:6005f2f5fcd67fdd721446128e6a2a1d18f77387a604fbd26b0006a086b33096 \ + --hash=sha256:61a2c48771cf314b6613d327c795902bbc0eb6d6169deb23b35004ba6ad6cc0d \ + --hash=sha256:63dc25b21fd9a41dc09b7aada4b3b0d97cf4b6414f74bced6ac45326bc799ac9 \ + --hash=sha256:64d41be1dd88f184de1931f0173f4827122a1b49fd1150656641200c0bdf640c \ + --hash=sha256:6929a11ad26a91a4efd891c1252b373c2e88f056910b83ec6030ed3f2cbcb734 \ + --hash=sha256:6c79e0c824d51c586757ecd342160bbdede9010df04bb71b9bbfffd5c7b6ee29 \ + --hash=sha256:70006e9e6157200b795beeee04bd5cb15bccb40a14de595eb9f5dcf5945ed244 \ + --hash=sha256:71040390ef01c85e9d26e5c83cb0c5942dcc8725c49186430af160ce2f54234d \ + --hash=sha256:72e3fa41d1fdab87d4e88bbdd69c9522e2795549fbe7b07bcf4ae9ec175f4b11 \ + --hash=sha256:778094c83e36c430756a7e1a1ac66fc3cffb2c6a1067958fe6b920abcec7bc5a \ + --hash=sha256:7a2f6164c0527cd8fc4cea79a5c9d8369ffee417b8ba444a42342f36b91deb75 \ + --hash=sha256:7b3f58bd26fc010ea28976d401845e4e6ce02e1b7c0288b3ea9c9a3c396f0bcc \ + --hash=sha256:7bd7ad604487daa7eab8716471c29a7185f17b5287ce73bb7bc79fea050d8cfd \ + --hash=sha256:8080022e101afb17565dc5a358a165ff4a20cd97b20b4db49ebed66315b3c733 \ + --hash=sha256:81f802c96dbf96e59c6982fa1b87da7868920fb0c27b9b81e560a62f57c2ccfb \ + --hash=sha256:82d728075d42bd457d09655cf22e99d772a648c6f67e86743a4f05b7d063ca18 \ + --hash=sha256:84272f329c15081a1e09b4a7261118b4e8a547f43e00fca98e55bbdf19eff3be \ + --hash=sha256:89db94855287fdac98d74595cf13ea59fbffa608d6400ff972b0fd4c036d873f \ + --hash=sha256:93b9416b9011a3b7689a933e04ac9f61d15686b6cb1948ebc1f41467153116c3 \ + --hash=sha256:948dff080b5ac00c8e63bf9e59fa70e386cca1476f55c672a72b6ec12e5cdb05 \ + --hash=sha256:963348422b22f760e9462e56bc32bf4d95d224cc5b8c79a3c6e3b786d3d2a2b2 \ + --hash=sha256:976bd3fecfcfa58d69eab67e76325f564ed775aa0c0accf138ae17324b461431 \ + --hash=sha256:98f7a4bfeaed3722804f737ae2bd4077b35e57d6f4531fe612bac8160cda5acd \ + --hash=sha256:a0bb9ee6a38cb36240dc88da11888348f61506047be54de3f09496c3b0ead6f5 \ + --hash=sha256:a577e2127e52b0fe2bc54c73abb375a20ffe6f59fbc5568ccafc233f5bfcf8ef \ + --hash=sha256:a64d54015233f824f171009977bfbb6b08bd0347b700cf17cb047ffb94c4148f \ + --hash=sha256:a6d147c31e189541ae7cd990482c4f960f9e8abce186551225fa355856dbf1a5 \ + --hash=sha256:acf8982c70471a68aa90d1aba08b48860c55b3357ec84ccb0f09368ead2ce099 \ + --hash=sha256:b756d74527c56a7e4cfae297f7930c1d75bdf4b23f214c8c13779746d28060cb \ + --hash=sha256:bab7f51d38766d6a64da2b41976f1b3f9cc2ff37d3f2f63bdbac876199f3a48e \ + --hash=sha256:bc33d3e59d4e84b8866cc9ba13732585e37212dbe3542cb09f232682b36f47a5 \ + --hash=sha256:cb2cb98d056e63e353ed697750004e07c79b054d73059ba3184ca3bb07296bea \ + --hash=sha256:d045e63095828d2f1fd84d499936e6791522c15c390373fc755f118e4040393a \ + --hash=sha256:d2cb669c6bd1f19caf51db6e3c4fdd4cbb76f9db3ef81c3aeb5e288d9bae101b \ + --hash=sha256:dffa69d2f3ba1933c1c1882dbef8fb3231b33eb19263e8b8c5cea24995071f06 \ + --hash=sha256:e2ace725a430e5b303fc3c422196966328ce77fb4fd053ad85572b46ed5fb71a \ + --hash=sha256:e30524ae24e31d83e1b5f734862882c442f4158e3566f2c5f5e9bd3c659bb517 \ + --hash=sha256:e3a026436c51f296aa1d01243909a3b76490950e927824b10899a083cc26e7c3 \ + --hash=sha256:e43fca5fdd5f34a3f8c54107a3648d3139de8bbf596a189f3f0de94bd84949bb \ + --hash=sha256:ec5d079935f67febe0ab8a3a203ad591b99508adc34ae0027f696dcb20373537 \ + --hash=sha256:f953be9ba26039a24a5205c65d33518b608ce6f4f0f4e9b9c14eaf42a10dfc52 \ + --hash=sha256:fba3500e170d25f581e053009edeb0b158116084d91d465de218718d336b67c3 + # via + # alembic + # openhands-agent-server +sse-starlette==3.5.0 \ + --hash=sha256:3e6e1070df3f0f5d9cea81496de92dbb72f6721871d99748ece67441dd8b7997 \ + --hash=sha256:75de713aa8a9441513cc283220826da079d982770965b951e9437720e8bafdb2 + # via mcp +starlette==1.8.0 \ + --hash=sha256:1565dc0b35d5737a271ed1e0e04e949f4e81198799f216d2667b0a0fb9cf9522 \ + --hash=sha256:dfdd6b29c26483288088d990eee59631dedadd66ce20d203402a7ca8e3c4656f + # via + # fastapi + # fastmcp-slim + # mcp + # sse-starlette +tenacity==9.1.4 \ + --hash=sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55 \ + --hash=sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a + # via + # google-genai + # lmnr + # openhands-sdk +tiktoken==0.14.0 \ + --hash=sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3 \ + --hash=sha256:10f31e63e40313f2e518d87f7086cfa44e45f64cc14d8ae14103b41220c30a14 \ + --hash=sha256:11d8211b290855d2721334ff17dd9b3a17bfb26872be01f25d73612ef7ece890 \ + --hash=sha256:144a3fc369f92b7d548995217c5d6e84038d3572157a0f6f34080d65291d0f78 \ + --hash=sha256:149d97453c4c98c04b081d64a85e635921269b532710d6faf81e9e82b790e7d3 \ + --hash=sha256:14b47e3674f2624803a8acc8fb367b7e24fc53055f9df3296482fe9a3a34a232 \ + --hash=sha256:151d37a150c8f3dfc5f4345597b10e101876bd1bd13494e0185af6b508758d2e \ + --hash=sha256:18a1b651c4b032004bf7b4f1713391a54b2a341a52c6e8a2b59acae9d16e13c7 \ + --hash=sha256:19d643d701fdaa70e5b9c7f8f96abcaffe77ca5e482a3a1a7dde46feb4284695 \ + --hash=sha256:1b6e4adcfd285c44502aed51df98aaaca4f0fea028165dbf8a9e857b9f98d8ea \ + --hash=sha256:1f83081065ee5833d35b49e9180f3d8d15622a603dd1c435da0da6cc12b3662f \ + --hash=sha256:2157f52e4b4d7ac5ecc7457b3716834706e7ef9a46f5144029bfeb7cf71f4e06 \ + --hash=sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874 \ + --hash=sha256:26cc4b4840fa0e9f4b72ed489883e12f57e00d1021ca794720e3c29a12f0edef \ + --hash=sha256:26e60f6a956ee171ab728b37b8439905d7ea1db435c30f9822f291e9861c861d \ + --hash=sha256:2cc19ac87b41c9493c9778ff5847f0c8bbcf5bd0ec6b87ce06c1c802adc8a771 \ + --hash=sha256:2ea70afba6b9eddbf22c165142e5f0a2ad7aa36a452873c48b57bb2aeb8492ae \ + --hash=sha256:2ec16eb585332c55d022d86354e209ddf27326b1ea3477585ab248e7776d3b1f \ + --hash=sha256:2fc834fbe3f6a0736905c36ab709537e6840dbd63b982dc9e0216ae7d305ba1a \ + --hash=sha256:380873f330b741c4435574f37edb20813d04603ace2d53e0a63560e1fec83010 \ + --hash=sha256:3b12e54f8bec91433e41aff65d8d1f209a4f678081163747079806e5361f6c91 \ + --hash=sha256:3c5349c9f916283bba32bec8af69b763e4faa304dc004d0eaaea66a3cf004c1f \ + --hash=sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6 \ + --hash=sha256:3fd7c14b1cb45b486c39fc9b3443bb341f3e2fc7e6f31247f3435a5836651632 \ + --hash=sha256:447ada49af4898b5e992f0b5799d2f3af385921102c211947ce3fe960dd919da \ + --hash=sha256:4d8d91d68353bd167fdf26467e5ff9e56aaa5f87d6410c0238608629e4dc0d33 \ + --hash=sha256:50a7e5646cbac2a8f7c3e8c0934ffda1a4357ee9c44b652434b23c3ed54d0900 \ + --hash=sha256:561e7580f84a79859af1ef6f676968e9030fcc3fe195700b15235bca64f009c9 \ + --hash=sha256:60c47ca69ddda0dea8256fffd12e1b86f4b59734a20e4a70c61f63cc5f021df4 \ + --hash=sha256:6eb94895c45f26bb8f5546e5fd8a069efcf6e3f108ea9d5cbe3bf6f7f3983438 \ + --hash=sha256:728303a072163130c5b477b1f20d6211895569c1d5302c24ffc93a3009160871 \ + --hash=sha256:78571efc311c30b73f31eb949a921d6dac39a5d9dc42d1cfa8f8db157b3447b1 \ + --hash=sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d \ + --hash=sha256:7aab286a020660a039097912a088236b985d18a3090d73f136c4413d29d37ca0 \ + --hash=sha256:7b7acbb7a4b8383707bce22ad3c162006478c27b56368acd3e1fcb1658a80425 \ + --hash=sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6 \ + --hash=sha256:86951a971c53979ec857bd8c4a32dc227ab0fd33f6c12a3bd62d3fbf5f0bfcaa \ + --hash=sha256:86f66c85e796f5d05d5c4a60ec1d40cbfebc47a32464053528c797163fa9ab89 \ + --hash=sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36 \ + --hash=sha256:90a762670c7f968184723769a06ed51f5cf5ce5dcd1e30164f25c72d85c2d1f1 \ + --hash=sha256:94f77b60a8ab23580db19ae822744c9716c1720020d2179ca5605112d12326f1 \ + --hash=sha256:979c1524f753b662b0f3cd261b135afe6659cce33caaa7a5ea00dd1756b3055c \ + --hash=sha256:a140e83317fef02faeeb78d9a8efac623887f2feaf0055c55dcdb2b17f0226ad \ + --hash=sha256:aa428a559d5fd02ae619aacaace86c7474a1f2702d2c01fc828908dd60f20f7a \ + --hash=sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482 \ + --hash=sha256:c2edf09b381fafbc014ae8e018ed25087abb9a3dafa8465a0ea63c6558c47a79 \ + --hash=sha256:c3093001ddce822b4587e6e94bf6de36a5f97b3f31de1c9fc8d4fda144c59ff4 \ + --hash=sha256:c6cb9896a82b9ee44e15ba0b5c8044072f2e4d48acaa704c8d3feeef5ad9487c \ + --hash=sha256:c77d4a3e1deb2707819df92046b89aad1ac81d27e07616b797cbff3f62c037da \ + --hash=sha256:ca4db6ff5c5bf600f9b7761a0070ed44dfe5797a76bd432fb978bc480ef40c58 \ + --hash=sha256:cbe2cc3bba939bcdaf103e03df9d5039d33887080b315624be28ec69059e5f94 \ + --hash=sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948 \ + --hash=sha256:d0781223705199b289faa59601bb9c2441712d4c600dd13c43d8fd6a33d22cd5 \ + --hash=sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4 \ + --hash=sha256:e067f4cbcc5d036e8aff7fe7a6b530a8f4de2e4616ad9005a24a1879e24e6450 \ + --hash=sha256:e2eca764c53490f8930dbce329e0769f11108d87d908282a80c5c130e26e7037 \ + --hash=sha256:e3442bbb2f0c588cec876061e37ae67b455b9df9978b003c8fe30e45f2ef5b42 \ + --hash=sha256:e4ddf863b59347deaa92302dcd90e5eb003cdc9be06ec2b692c38d1bdd9efd49 \ + --hash=sha256:e9c5fe393aab56469f04e432ff851216d3def3436cf5f07e442a240164bf500f \ + --hash=sha256:eceeff0c62419bc78d4b6e70a4762a4d25df3ae8f2d5946e3853ce93e7a57098 \ + --hash=sha256:f2af4a336ea56d6c14f27741a0e1d8294a35dd0b038bcf990d232ebb54eb994b \ + --hash=sha256:f3d6cf93fbe2e7117eb7bedca684216fbe328a41f0843ce34245451d8eb2df1c \ + --hash=sha256:f5e7665f6624e052e5e7f6a36919ab69279decdc976d7b16b4fa15e1897d0513 \ + --hash=sha256:f702e0aeeb6506e57687e881c59e844ebe8f0a6a097ddafe20e3ab25f387be4e + # via + # litellm + # tom-swe +tokenizers==0.23.3 \ + --hash=sha256:114e2b55ed177179d59f4ab98200a4471e11e78f9e4b5a922d146740f96fcf52 \ + --hash=sha256:1554a6eed34d9d6a78d23360f4e06df8dffab1ae08c7e8488e0b3e3b36cc266f \ + --hash=sha256:1ebf28794e7e4954e20a7f70fbea410b2d1f0418f7dbbca97ca384fcfef38c25 \ + --hash=sha256:1f0823bb00c5fdc98e487354d54dd55a03848d61a1a0bf29a68c77f24f3b26c3 \ + --hash=sha256:2a89614730d7b80940a5d2ed9320e1ec8add5a745c6151d8d05071b7215505b6 \ + --hash=sha256:376851d22bcf9d650a5c3090bb83e6cf9e895fbf0595369fa4cd43c1f69b5f87 \ + --hash=sha256:5cc24bb457dd4a8af89c8fcb40074d570129ec473df2a866c276ee55db4749d7 \ + --hash=sha256:68649e97d5b43c44c031d8d848874a6eecae8f8fe40ea989aa777a5a83aca716 \ + --hash=sha256:7e48734d2de9260d86f03ab056d2cfeeff3869f61dbd49aaa15a2793b5f3458b \ + --hash=sha256:82eb480f6f1c21cea3349dec32cf1a6384c6c1e775f00f83b0d51197bc013687 \ + --hash=sha256:84513ef0aeb8bf8f4ea11a2e8a7ac163ec5288aa115e649a59b470ac5c3107df \ + --hash=sha256:9d2b5c97daf61688c2ad1803ca851800feaba50fb68d5821779e9ea5880d968c \ + --hash=sha256:a4fbb3662f9f59d199d61338e54b4bcc11d07ebbb1aeb3540dacb2be9c521cb7 \ + --hash=sha256:acd5c57b4bd3e56e246e2731a3a3a6825a7a7d89b7e3b761ba80bc521710f04b \ + --hash=sha256:bf501c40b72d2d5c8623620210430e9cac1ce47a46e45b34107b70a1557d46b0 \ + --hash=sha256:c64a0713180ff16829d4e7f39a658b77ea11443af4e1aa46523692943c9b1414 \ + --hash=sha256:cded33237c77caeef62944d32aa9a7ef42bdce2b3497e18d137e072a8c4be438 \ + --hash=sha256:d3407fb7b9c4d75dd68850ffd7180bc0a5d2dbaf0762d888e612f31fec3f9c6b \ + --hash=sha256:ddedfd4b3b4be6be24ff6ca645c4a37fddfd305f6f3e354c54cf10b715c48215 \ + --hash=sha256:de536665495cb4b409d25bade41963f801aff4225c19a6b804b048f7d14e34c7 \ + --hash=sha256:e05ab7baf7f47b406a95fea6f3b0a484b2ddcd9e1d14b68844c457eb755085a3 \ + --hash=sha256:e88646b8580c5ad7f4361477f1298e9cc01771a1ee9aecfe32c47b8ff614cc38 \ + --hash=sha256:ec82e80e65a862275b97c3d90b7a523df8d9519ee48aeb4e9625b2cc909274e0 \ + --hash=sha256:efa3d7318406b4d115dce61ad5061953f1f44b128e79c020ce4615d763e23b6e + # via litellm +tom-swe==1.0.3 \ + --hash=sha256:57c97d0104e563f15bd39edaf2aa6ac4c3e9444afd437fb92458700d22c6c0f5 \ + --hash=sha256:7b1172b29eb5c8fb7f1975016e7b6a238511b9ac2a7a980bd400dcb4e29773f2 + # via openhands-tools +tqdm==4.70.1 \ + --hash=sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73 \ + --hash=sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4 + # via + # huggingface-hub + # lmnr + # openai + # tom-swe +tree-sitter==0.26.0 \ + --hash=sha256:00289bfe7978f3e0dc0ce69813a20fa9f44ea4c100b3ec62043e5eb74ccfc3a2 \ + --hash=sha256:0f8793fd18ad7eec276ed4b51c097b4bf2002b357259b66b0d75db1f3f41c754 \ + --hash=sha256:10f0d4eb94aa7242dcb7f554bcd24dd7ba1c114f00d58759ba08c7a46c8ec51a \ + --hash=sha256:17a1c5cfd3a05d5c7c86bf4282b6ef8092c91dc0a98390499669c3fedb7d1814 \ + --hash=sha256:1d6fe0e8fb4df77b5ee816228e2c4475a63d8cc1d4d3a7ffd7097b2b87fc3e95 \ + --hash=sha256:253df7ab82cc0a9d311cd65f06e9f99fb3eac55996ae9fc94da22f123a861b90 \ + --hash=sha256:26c996c1edfee86e977bb3f5462e74fcec0d0b0db1e85a3c475875763caa03be \ + --hash=sha256:2f941cea06128c1f74f8937a8e2a90c7db49cf4be6647cd9e07d92a306d91517 \ + --hash=sha256:30a88be89ff1f2755297f81e8080d88b795dd98720c3f9fa2acf93873182cc95 \ + --hash=sha256:335294ce0504fcefde5245dff596778ffaf820205b98ae0b549c72e48855f1d8 \ + --hash=sha256:3f3c44339dd34fe8eb2b8d5aa7610660499a795f70376b130bbee7a437337280 \ + --hash=sha256:514a9bf8993e5210e7970736aaf6020d1759b670e195ef17b1c48f586aa30736 \ + --hash=sha256:526a165a2cb1d1f79e247d400f0e0acd8d49a817d6f312d543513af200b1f886 \ + --hash=sha256:5a3c93a352b7e6f70f73e121bbfa2d0117ba7478bd51114ed35c91b0b78814fa \ + --hash=sha256:5a6b333b0282d8bb0af741f9b018bd2523d4eecb2686bf6717066a625fecfaa4 \ + --hash=sha256:5fc2f41bf246ff2f70a9cc3690be35ec7580a4923151873d898c8bcb1a4503d3 \ + --hash=sha256:6189c6c340c7384357711e3d92645e96bfb79f7a502f86de1ebdb23eb43f7dab \ + --hash=sha256:6cb2bd20efb2544c19ac54486ab7cb8ec7b36f913bbe1ce95df84acb96743d9c \ + --hash=sha256:7075ef857ef86f327dbb72d1e2574dda78db5754b3a1fca6506acd7fe5d561a7 \ + --hash=sha256:763627db05db34f12333081bd7422cc1c675893d373cc870b3e9249e200700e4 \ + --hash=sha256:7bcbadfa614326debef581957d5c780a9d7f66065c13deea61aa21d1dd36263f \ + --hash=sha256:823251c4b6725a7c03ed497a339135ede7ae4bdde75bb8be7ef5e305aeb4ff52 \ + --hash=sha256:8ff2e0750b7daa722302838356d7b65e303829b7eb73c915df127ddba115e1d1 \ + --hash=sha256:918d89529786873f0982a0f59c2a303cd065fbfd1b903d71a8e4e1584f67b42e \ + --hash=sha256:93e220cab7e6a823efeb2046c49171427de92ef71c7c681c01820d14d8d3721f \ + --hash=sha256:94550e13b6ae576969da40246f4c4abb206380b5375ad43f26dd9151d55438e3 \ + --hash=sha256:a4033fecc8f606c7f2e8b8014d0057b74668a7f0152763606f7bc25c5f9ec64c \ + --hash=sha256:b31a8195d2f224224c530ac814632d98c1dcc123d227442c07c736e86b70d564 \ + --hash=sha256:b40c219edccc4564530c96f8f1556f6202b37cda964d1cbd7bd2b7e68b40a245 \ + --hash=sha256:b8ea92a255c91671a7ec4625aba3ab7bb5220c423630ffbf83c45d7312abe084 \ + --hash=sha256:bc6cb01d5ee75c85424aa1f1c72a82d8f07fd52539a0f3c4a6ed3e8721079b84 \ + --hash=sha256:c56581ad256c4195a21bfe449fed5d44a02fe83a4a7d6e70e6ec302c881191c7 \ + --hash=sha256:ca89e361a276dbc934b28a43dd881199e25d34ff5493ee0ce45f3c52a6124a37 \ + --hash=sha256:dea4b4e27d49e9ec5b785d4f994da000e6726882fcc6ad05ec98478500c71aef \ + --hash=sha256:e9e46b664887d8c1014f1fb33e09454bbdd9ec1fe29b7fd02dde7b46bc1bb81a \ + --hash=sha256:ed0889dbed843ce45ede9f5169c0b2dea2222f12685844a03fadb81f12705867 \ + --hash=sha256:f289be0225ba2ace8e87d6c9639b2bc9ff2b5271afb7c5d39282a4a00e248682 \ + --hash=sha256:f665510f0fcf4636fb9696f1f7853bed7a3bd764b7bb0cb8494e619c14ed5a0c \ + --hash=sha256:f9997ba61368c48ed54e715676afadf703947a1542464e39d047764fb3624b01 \ + --hash=sha256:ff527388df14cb5009f9274faf78cc69a7393ae6acf3b04784b8acca249519c5 \ + --hash=sha256:ff80d4833d330a73184a3ac5132abe93c575d2dea31975c6f15c0d21fef238aa + # via + # openhands-sdk + # openhands-tools +tree-sitter-bash==0.25.1 \ + --hash=sha256:0e6235f59e366d220dde7d830196bed597d01e853e44d8ccd1a82c5dd2500acf \ + --hash=sha256:3f484c4bb8796cde7a87ca351e6116f09653edac0eb3c6d238566359dd28b117 \ + --hash=sha256:52a6802d9218f86278aa3e8b459c3abdad67eed0fde1f9f13aca5b6c634217a6 \ + --hash=sha256:59115057ec2bae319e8082ff29559861045002964c3431ccb0fc92aa4bc9bccb \ + --hash=sha256:5e76af6df46d958c7f5b6d5884c9743218e3902a00ccb493ec92728b1084430b \ + --hash=sha256:a3332d71c7b7d5f78259b19d02d0ea111fcb82b72712ee4a93aaa5b226d3f0a8 \ + --hash=sha256:bfc0bdaa77bc1e86e3c6652e5a6e140c40c0a16b84185c2b63ad7cd809b88f14 \ + --hash=sha256:e76c4cfb20b076552406782b7f8c2a3946835993df0a44df006de54b7030c7dc \ + --hash=sha256:f4a34a6504c7c5b2a9b8c5c4065531dea19ca2c35026e706cf2eeeebe2c92512 + # via + # openhands-sdk + # openhands-tools +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via + # aiohttp + # aiosignal + # alembic + # anthropic + # anyio + # beautifulsoup4 + # browser-use + # bubus + # cdp-use + # exceptiongroup + # fastapi + # fastmcp-slim + # google-genai + # groq + # grpcio + # huggingface-hub + # mcp + # openai + # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # posthog + # py-key-value-aio + # pydantic + # pydantic-core + # pydantic-settings + # python-docx + # referencing + # sqlalchemy + # starlette + # typing-inspection +typing-inspection==0.4.4 \ + --hash=sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47 \ + --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 + # via + # fastapi + # mcp + # pydantic + # pydantic-settings +uncalled-for==0.4.1 \ + --hash=sha256:6412b19d1b1e7d431981fee01f8b47be7802f48328a48cbe9603acee757ec553 \ + --hash=sha256:b611b38879d9fe5088297e7bb28f2ddd12d2d73dad933d93da107fb63eb4058e + # via fastmcp-slim +uritemplate==4.2.0 \ + --hash=sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e \ + --hash=sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686 + # via google-api-python-client +urllib3==2.8.0 \ + --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 \ + --hash=sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63 + # via + # botocore + # docker + # requests +uuid7==0.1.0 \ + --hash=sha256:5e259bb63c8cb4aded5927ff41b444a80d0c7124e8a0ced7cf44efa1f5cccf61 \ + --hash=sha256:8c57aa32ee7456d3cc68c95c4530bc571646defac01895cfc73545449894a63c + # via + # browser-use + # bubus +uvicorn==0.54.0 \ + --hash=sha256:505bdb0f318731d45f1f712071fc781a8981f6847a31c902c9f5e652d4f67faf \ + --hash=sha256:a2e33cbfaa0306f8e6b0c13e0cb89d7d7a2da3e62b90c66e18c33d9807b28620 + # via + # fastmcp-slim + # mcp + # openhands-agent-server +watchfiles==1.3.0 \ + --hash=sha256:000b9688fc8133037a8b075c8ebf98f32844ff8964dda61e85c1db547dafc441 \ + --hash=sha256:025b108f2d5cc2799cb941f79380df3e8b22bfb589c218e801767fe78d598c95 \ + --hash=sha256:04c5500faa725d69a99b0f63750e80795e455bb951849e9c98a811b4324997c4 \ + --hash=sha256:1acabde19b67e673274e89a04d613b7fb1f122e2c4b8ec9a581e59c149264b61 \ + --hash=sha256:26b81bc515a0f03f66a69a6fea86eea54c8eca4173c46dd7222569f79dd2f977 \ + --hash=sha256:380f513d26cc2e598266b88d66456e07d67ef4be8f0f435a1154d9c44b43d509 \ + --hash=sha256:45726c6b5a8d67c8087eb2933c4398948546d04206ef40f629dc4881d65d23d1 \ + --hash=sha256:4af9c464d410e8c44b58ddf1cbec7c3a02c1cb2c6c80df9c74659ba2fbda0ab5 \ + --hash=sha256:509d9f74d2bec5c1f4cd868ddcfe0c9601f7ea53066dfcb19d8ede742c4de661 \ + --hash=sha256:591de392458bca26f6130fea4e667c06b657e873ad334ea06fad5a79f5086cef \ + --hash=sha256:5ec46a1ff4c1d81405a5fc645abdbff5e4e9388f7c9a6cdb392316a7c29b884f \ + --hash=sha256:6398d250994baff581147ccba846223076fe6492ca93c0d1b3588fcf5355eb5c \ + --hash=sha256:7398930a2b76b9dc67a4e6b8bfc7baf30946892a86f417a3920c07bdd6febc30 \ + --hash=sha256:7c1125e99f84934a92996ae343501285617373acd66f16b59fd5654b27ad8d80 \ + --hash=sha256:81c989d7267bfb7b676cd32c83bec97914e396870983cbf493e29415037bff5e \ + --hash=sha256:99aee4a07847c06820765fd7b1b49ceac4f3f711ccb7d104655a33231de1c207 \ + --hash=sha256:a5b631db08fd3032db57ec6a92d1777cd60f88803287e4e941a4858c53aab1e0 \ + --hash=sha256:a85e243c87109d9f48a2e0d10d6abdee8768cc114848b32281ec83d7f81d86ef \ + --hash=sha256:b5768b49e426fd5b550b012c866db347cdf15c398ef98dc557b6e6b72fa74cd1 \ + --hash=sha256:bbc1198edfdc90fda0600f825aa94150f428dfcbf8138746f55998e0e660d64c \ + --hash=sha256:be9ef3cd403d756a304a0a08a112163c63da7ec72c8c9d083c98818291ff8f29 \ + --hash=sha256:ca39661934749df580d89f3dde266f2af7c6968f4c21c72ce2dc387d299e6aa7 \ + --hash=sha256:d36c72fcdc08f143d87316a4c4c3b064e340da2c7383d39803950740ed418935 \ + --hash=sha256:d4593392e87669836670f87d62fc754059eaeb0e6157af44060a46558cebe5a3 \ + --hash=sha256:d754e049006e81a9be49dc78ba7802081cee426e968cbc63ee19e164fa2d0e39 \ + --hash=sha256:d978cc1dd7ba44f5590d7de74f7a9c8be7262d9e469c2c0efb8bbfc2574321dc \ + --hash=sha256:da213822ec9082b62cadf3008e07a8c690ec7a3c89f6dc5e55a8cf07b56a1b26 \ + --hash=sha256:dd538c71766c59c732e2c5cedc39323c99c11897b99f93eac773438e54af206a \ + --hash=sha256:e0204f90677b7fb8d3a824279a71a01166bea9f6e53923b8056592d87b33b0b6 \ + --hash=sha256:e5871d4a7f788a9f64fb06f5793389d29d171e5c32d37c3ebfd520b017cc7694 \ + --hash=sha256:eb94e40b9a0db19636b5e3fed0a9803452ecc7381df13b385272abb779ee83ca \ + --hash=sha256:ec3cd4bb181a7b6329134204c2277c7144ab5f62cce9c5335d6f901ee7ce8d6a \ + --hash=sha256:f0c9865240e065a2247f4b5248528ae3d01e0e8ae071462c250c884dc16e3ee3 + # via fastmcp-slim +wcwidth==0.9.2 \ + --hash=sha256:0a47e03d8293590ecce66c45dc20ff7b4b885e3c78093722239585eca0d77ab2 \ + --hash=sha256:0cd4f7f2e53905dcb110d213a4c8529b6733fa3d232d8c717f946cc69a10349b \ + --hash=sha256:138e1f8898e431b2f2d7881f8ca8d75591c1d3c21aa53f54e989bd6b39811da2 \ + --hash=sha256:196b47cf32f9df27ccda6dc513237f3c2429c4c659db428d60a5bc443d10f270 \ + --hash=sha256:1bf361c8705576760623b4724ae564666d73b016f9a778bcfd1c7345378ef4ec \ + --hash=sha256:2a9746de704242bd4fdaabb31dd46b82f694a56a8d21081ad89b679a89da9fec \ + --hash=sha256:33df042f96c61ed3cd5fb3742fba427553a635bc578799857a48aa79f774a0b9 \ + --hash=sha256:42dbcb76ce8af39e2c9db410ac3f9bdf4e47eb41d6f44525952f172d3d98f724 \ + --hash=sha256:48719a9bc76c2f84238693fe5013571fa5beffa3621cf228f1f3a9e30dae84b8 \ + --hash=sha256:5175609bf8cc7398a5f48aa35207bd64ebf9f45e4c70df65f7fdc7a988041a3c \ + --hash=sha256:59dab4049cbd982b478bca098528df2c79a9160636a3a163ffebffcbd7d1b892 \ + --hash=sha256:674b518af28d38ee645ff97b74f5760abee5fad4bac74413bfc4b881ef2ce724 \ + --hash=sha256:67d901a4ad99249eb775b4ee4769ca97fa405d35a75f46e83166910a47003f04 \ + --hash=sha256:734aa9405b321d1042301aa19c943c4731ee9e3460e4f8feea3299c064c97a14 \ + --hash=sha256:751bef0ab404b6a1dc028b56b4b85d46486be1c55833f80da533e42dc691f389 \ + --hash=sha256:7ef5a940bd5e30bac6e721f1a48fce0cd7bb3ece19e9c5d139e72c76c35cfd07 \ + --hash=sha256:89ca642c5bf0101157a09366be69fad0379db1f700ae39a920e103234573670e \ + --hash=sha256:8b4e381590b9b7390e07e22b2c0c1bb96ce50e1d2243c866d9387600362d51ed \ + --hash=sha256:97b878d1e158da5ed9ac5aac53fa3a55e282103af6a09ec353865613d1a31a76 \ + --hash=sha256:9e542f1f8475b78452a295495d7a5bc3ead565112e9446a64dc93462a41c2a79 \ + --hash=sha256:ae0800c5339423cc53d33a266ad264b42ba8aaa16d4464f6e6b1bee607f50b17 \ + --hash=sha256:ae0ef90b90f6af38b54f1fe6d58662ec33b3cb4b8391958a62416d654231727b \ + --hash=sha256:b9c6ab615e03723b7f8760ea2f27758d656e7e13b51515c9dca5c3e8b04612fa \ + --hash=sha256:bb08ceb501d6aaf94066c3ee122dd825b152df40ff0bd0df4dc27126233b948e \ + --hash=sha256:c3d80f39ba4653a595edae9aa46a509d14883790a8fc23c5db221ceb207f64b7 \ + --hash=sha256:e5f669ae8c3d969c72032f9cdee019674b666e522d45e1e2099a2e9dda4a341d \ + --hash=sha256:eda88ffdc97c0fbf193d407114f2c7a54b379f67f6e52a7531ee3b9fe749eca7 \ + --hash=sha256:ee1fd0db9d9fd711a70f3e7765e0e04c05d26982fa05361456163062549d7da4 \ + --hash=sha256:f2f7b3bba5a5d5f31fc350fd36ce5b84b693c83b7eb95ee630b720da5a5ce06f + # via prompt-toolkit +websockets==16.1.1 \ + --hash=sha256:01fbdcbac298efe19360b94bc0039c8f746f0220ba570f327577bfee81059175 \ + --hash=sha256:024193f8551a2b0eafbdd160911012c4e6c228c28430c84433253299a9e42d6a \ + --hash=sha256:04fd29a0e2fe9414a95b00e92c67ae51bf900c50c0f8a4b2dafdad621f49ea1d \ + --hash=sha256:056ae37939ed7e9974f364f5864e76e49182622d8f9751ac1903c0d09b013985 \ + --hash=sha256:0f62863e8a00a6d33c3d6566ec0b89f23787b747ffe0c3bc71ec0e76b82c94b1 \ + --hash=sha256:0ffd3031ea8bda8d61762e84220186105ba3b748b3c8da2ae4f7816fac03e573 \ + --hash=sha256:1214e673c404684b9bf7154f5cf43b45025b1a6160fac3a9e438e9c1a97e22cb \ + --hash=sha256:125f22dbefaf1554fea66fc83851490edb284ce4f501d37ffed2752f418332d9 \ + --hash=sha256:130937b167a52af203c8d58e78d67705874e82759862e3b9671a452fec4abc87 \ + --hash=sha256:1427fb4cf0d72f66333e2cacc3ff5f575bf2d7008166ce991a4a470b21d51a22 \ + --hash=sha256:195c978b065fa40910582464f99d6b15c8b314c68e0546549a55ed83f4735328 \ + --hash=sha256:1d27fa8462ad6a1cb36206a3d0640b2333340def181fae11ed7f9adeaa5c0747 \ + --hash=sha256:1db4de4a0e95673f7545d393c49eeb0c2f18ac1ef93073218c79d5cdb2ee75ab \ + --hash=sha256:1f79c89b5eb034d1722938a891916582f8f7f503f58ca22518a63c3f2cd18499 \ + --hash=sha256:23253dd5bcae3f9aaee0a1d30967a8dbd52e5d3cff93a2e5b84df57b77d4750d \ + --hash=sha256:249116b4a76063d930a46391ad56e135c286e4562a18309029fc2c73f4ed4c62 \ + --hash=sha256:29dfa8114c4a620c69591c5973860f768eac29d3fd6904f37f34266cb219c512 \ + --hash=sha256:2a606d9c24035242a3e256e9d5b77ed9cd6bccfcb7cf993e5ca3c0f6f68fb6a7 \ + --hash=sha256:2a636ff1e7a5c4edf71ef0e79adae7f25dba93b4fcbe3dc958733477ffeb0eaf \ + --hash=sha256:2bb5d041a8307d2e18782e7ce777f6fdb1e8c2f5d09291484b18c294b789d9aa \ + --hash=sha256:2e28e602bb13da44fbe518c1781a88e3b9d4c3d48d02c9bad83e546164336f57 \ + --hash=sha256:30bbe120437b5648a77d3519b7024ea09530e0b5b18d3698c5a0ae536fe0cc2e \ + --hash=sha256:34420aaa64440ebd51ac72ca8a45ef4626429438c9b02e633ae412ed43f925d3 \ + --hash=sha256:38565aca3e01ea8734e578fb2118dade0ecb0250533f29e22b8d1a7a196cf4d0 \ + --hash=sha256:387e8e4aa5df2f90b198fa3cad3478822a89cf905b6a6d6c97dc3664689640cc \ + --hash=sha256:39f2a024af5c345ffe8fcf1ee18c049c024c94df393bb09b044a6917c77bde43 \ + --hash=sha256:3df13f73af9b3b38ab1195eb299ecb67a4330c911c97ae04043ff74085728abe \ + --hash=sha256:414e596c75f74e0994084694189d7dc9229fb278e33064d6784b73ffbba3ca31 \ + --hash=sha256:41c8e77f17294c0ac18008a7309b99b34ee72247ef10b6dff4c3f8b5ac29896b \ + --hash=sha256:42290eb6db4ccaca7012656738214f8514082fb6fa40cdeb61bb9a471b52e383 \ + --hash=sha256:42f599f4d48c7e1a3338fdaac3acd075be3b3cf02d4b274f3bf2767aedd3d217 \ + --hash=sha256:43e3a9fdd7cbf7ba6040c31fae0faf84ca1474fef777c4e37912f1540f854499 \ + --hash=sha256:443aefe96b7fdb132e2a70806cca1f2af49bb3f28e47abcd7c2e9dcf4d8fa1b8 \ + --hash=sha256:46dcaa042cd1de6c59e7d9269fa63ff7572b6df40510600b678f0826b3c7af51 \ + --hash=sha256:496af849a472b531f758dbd4d61338f5000538cb1a7b3d20d9d32a264517f509 \ + --hash=sha256:49ae99bdfcae803a885c926bf14f886196e84925395bb3f568fef5c0f0979d7d \ + --hash=sha256:4b57693728576d84ede0a77987ab16881b783d2cd9f1dc180a8fbbc3f79c4428 \ + --hash=sha256:4e3b680b1e0a27457e727a0d572fd81dffa87b6dbf8b228ab57da64f7d85aead \ + --hash=sha256:4e8d01cc3bcae7bbf8167f944aeafefed590fae5693552bba9794a9df68371cc \ + --hash=sha256:5283810d2646741a0d8da2aa733d6aefa0545809afccb2a5d105a26bc45125f1 \ + --hash=sha256:53260c8930da5771cec89439bff99c20c8cb03ddb9588b980697355a83cd4bd3 \ + --hash=sha256:536676848fc5961aca9d20389951f59169508f765637a172403dc5434d722fa0 \ + --hash=sha256:54509b8e92fee4453e152b7558ddef37ce9705a044922f2095a6105e3f80c96f \ + --hash=sha256:56cd5fc4f10a9ea8aa0804bddb7b42506cf9e136046f3b4c27de8fec9e2ecba5 \ + --hash=sha256:5bfd1ac19b1b9986a9c95a82d5e23a391ebb09e12c34d7be6094b86efcc35731 \ + --hash=sha256:5c31aa7e39ee3e8a358573257f1c0bb5c52430d1b637030dd9c8cc2c282926be \ + --hash=sha256:5e3b7d601f6f84156b08cc4a5e541c2b50ad7b36cfc302b657a12477c904a5df \ + --hash=sha256:61922544a0587a13fd3f53e4c0e5e606510c7b0d9d22c8444e5fae22a06b38cb \ + --hash=sha256:6456ff333092d509127d75a638cb411afae8ff17f092635015d1902efec8a293 \ + --hash=sha256:69159730a823dde3ea8d08783e8d47ef135a6d7e8d44eb127e32b321c9db8e3e \ + --hash=sha256:69e52d175a0a7d1e13b4b67ad41c560b7d98e8c6f6126eb0bda496c784faf8c7 \ + --hash=sha256:6aaface73b9c71974c6497366d8b9628357f6c9749e09c4ea3610176c63f2ae3 \ + --hash=sha256:6abbd3e82c731c8e531714466acd5d87b5e88ac3243465337ba71d68e23ae7e3 \ + --hash=sha256:6ff9417c0ada4d0f7d212f928303e5579bdf3ace4c802fa4afabb30995da58c3 \ + --hash=sha256:7421fad442de870a8cbf2287d1cad7e706ece0dbfeba5e911df132cbdc1cb56a \ + --hash=sha256:7883388947767080f094950b342b30d35a2a06b849cd967c422fa0db72b40ea9 \ + --hash=sha256:79eace538c6a97e96d0d03d4f9d314f9677f5ed85a8a984992ffd90b13cb8a56 \ + --hash=sha256:7b1b19636af86a3c7995d4d028dbe376f39b4bf31541146f9c123582a6c94562 \ + --hash=sha256:7dfcad78ea1492ee3a9ec765cb7f51bbc17d477107aaf6b22abf7b2558d1c5a0 \ + --hash=sha256:8087e82f842609734c9b5a1330464f8e94e346ba0e18c832c08bafa4b0d63c15 \ + --hash=sha256:820fb8450edddae3812fd58cbc08e2bf22812cb248ecb5f06dbb82119a56e869 \ + --hash=sha256:8483c2096363120eea8b07c06ae7304d520f686665fffd4811fad423930a65d7 \ + --hash=sha256:84a2cef8deffbd9ab8ee0ea546a2a6a7030c28f44e6cdd4547dbfeb489eb8999 \ + --hash=sha256:86d7f0f8bdb25d2c632b72527325e4776430fd5bc61b9118de4e2b8ddb5f5b01 \ + --hash=sha256:8fe0b50da2d84535fb4f7b4bfa951280f97ce3d558a0443b541166d609e67b57 \ + --hash=sha256:90001d893bc368e302ef168d82130b4e4fdd27b85fa094682df9b667c2d48838 \ + --hash=sha256:9246a0d063cfcbcc85f2359dd6876d681213f4790832272aa16641b4ed5d64d4 \ + --hash=sha256:92b820d345f7a3fc7b8163949ee92df910f290c3fc517b3d5301c78065adafe1 \ + --hash=sha256:952303a7318d4cbe1011400839bb2051c9f84fa0a35923267f5daba34b15d458 \ + --hash=sha256:97fd3a0e8b53efa41970ac1dff3d8cf0d2884cadeb4caaf95db7ad1526926ee3 \ + --hash=sha256:9c1c5705e314449e3308872fe084b8571ce078ee4fc55a98a769bdefe5917392 \ + --hash=sha256:9c9f23004a3d40e89c01a7955d186a6cc83418d93b749701944ce2de3e95a1f3 \ + --hash=sha256:9f63bcef7f4b02b06b35fc01c93b96c43b5e88e1e8868676caacf493d5a31f3a \ + --hash=sha256:a0eadbbf2c30f01efa58e1f110eb6fa293261f6b0b1aa38f7f48707107690af9 \ + --hash=sha256:a28fcbc9b6baf54a2e23f8655f308e4ccc6afdd7266f8fe7954f320dcda0f785 \ + --hash=sha256:a6a61aff018180c9c50b7b0da33bfd29d378af3497429c95006c589a23a11648 \ + --hash=sha256:aabe464bfd13bd25f4821faf111da6fefdc389f870265a53105580e45b0a2e49 \ + --hash=sha256:ab59169ace05dcb49a1d4118f0bde139557adf45091bd85747e36bf5de984dd1 \ + --hash=sha256:b436f6ec4fc3a6b4237c84d3f83170ed2b40bb584222f0ac47a0c8a5921980c7 \ + --hash=sha256:b6b9dadbef0cccd9f4c4ee96b08898afa73e26803bbe0f6aeb5bb12b0074206d \ + --hash=sha256:b852788aa51764e2d8e4cf5493d559326bcae5e38d16ba25ffa322b034df272a \ + --hash=sha256:bae954c382e013d5ea5b190d2830526bfa45ad121c326da0049b8c769f185db6 \ + --hash=sha256:bcce07e23e5769375158f5efdcdafa8d5cd014b93c6683865b840ed65b96f231 \ + --hash=sha256:cc97814dfb786a83b6e2dc2e79351e1b83e6d715647d6887fcabd83026417a00 \ + --hash=sha256:cd2ca96a082a36964aca83e992f72abeb61b7306c1a6cba4c7d06a7b93750cac \ + --hash=sha256:cfb70b4eb56cac4da0a83588f3ad50d46beb0690391082f3d4e2d488c70b68ea \ + --hash=sha256:d0fcf657e9f13ff4b177960ab2200237b12994232dfb6df16f1cfe1d4339f93c \ + --hash=sha256:d14bfb217eb4701e850f1525c9d29d79c44794cdf1c299ead25f39f8c78dea81 \ + --hash=sha256:d57685547e0060cc6fd90ee6a28405d6bd395e525545f13c8d7cd99c78afd79f \ + --hash=sha256:d6bec75c290fe484a8ba4cacdf838501e17c06ecfbbf31eede81a9e431bd7751 \ + --hash=sha256:d9531d9cbeac99af6f038fb1bc351403531f7d634a2c2e10e2f7c854c6ed5b68 \ + --hash=sha256:da4ca1a9d72f9030b3146b8d7022719a9f3d478f61efe6f7dd51d243f61c51b2 \ + --hash=sha256:dab9eb87869da2d6ed3af3f3adf28414baae6ec9d4df355ffc18889132f3436c \ + --hash=sha256:db234eda965dcce15df96bb9709f587cd87d4d52aaf0e80e2f34ec04c7670c57 \ + --hash=sha256:dc0fad4933f427acd5b1cec210f3ea6dce7089e1724e4b9ec6ef47c6c04d1b3b \ + --hash=sha256:dc385593a42e31cd6fb60c19f0ecb015b386603818fc2c6c274fb42bd2bb4165 \ + --hash=sha256:dcc04fedf83effaeb9cce98abc9469bb1b42ef85f03e01c8c1f4438ef7555737 \ + --hash=sha256:e047dc87ef7ca50f4d309bf775ad4a71711c58556d75d7bd0604b2317f43e94b \ + --hash=sha256:e09f753a169951eb4f28c2c774f71069304f66e7277e0f5a2892423599cfa854 \ + --hash=sha256:ed5bb271084b46530ee2ddc0410537a9961152c5ccba2fc98c5276d992ccba87 \ + --hash=sha256:f0aa4aad3b1b69ad3fd85a0fd0952ec64331c762bd77ec51cc814170873890b2 \ + --hash=sha256:f17dbe07eb3ea7f99e4df9b7e0efefe80fbf30d37a8cc4d561a0aed310bc8847 \ + --hash=sha256:f2769a0344a09e9ccf5b3cce538bc75a51b53eff3275d3896310c8552049195d \ + --hash=sha256:f55f0b01956a094c8587146d9558c91937e78789c333860ffaf35931a6e5dbc4 \ + --hash=sha256:f5d497865f05bb222cab7016c6034542e84e5f29f49c6fd3f4939cda7197b5b8 \ + --hash=sha256:f70541f3104339f59f830522d94ebadb1bf47426287381623443d8bb1cdbf33d \ + --hash=sha256:fb9a0a6dc3d1b3986cb88091b6899f0396651e0f74e2c9766ab8d6ffc3842e29 \ + --hash=sha256:fce6c48559c86d1ac3632ccb1bebc7d5442fbe79bd9bb0e40379ee54be2a4051 \ + --hash=sha256:fd46fff7eb62c24804d234f0051c7a8ea81285ad63e0337d3dcf33ca82aee58a + # via + # cdp-use + # fastmcp-slim + # google-genai + # openhands-agent-server + # openhands-sdk +wrapt==1.17.3 \ + --hash=sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56 \ + --hash=sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828 \ + --hash=sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f \ + --hash=sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396 \ + --hash=sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77 \ + --hash=sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d \ + --hash=sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139 \ + --hash=sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7 \ + --hash=sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb \ + --hash=sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f \ + --hash=sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f \ + --hash=sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067 \ + --hash=sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f \ + --hash=sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7 \ + --hash=sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b \ + --hash=sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc \ + --hash=sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05 \ + --hash=sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd \ + --hash=sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7 \ + --hash=sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9 \ + --hash=sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81 \ + --hash=sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977 \ + --hash=sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa \ + --hash=sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b \ + --hash=sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe \ + --hash=sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58 \ + --hash=sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8 \ + --hash=sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77 \ + --hash=sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85 \ + --hash=sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c \ + --hash=sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df \ + --hash=sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454 \ + --hash=sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a \ + --hash=sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e \ + --hash=sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c \ + --hash=sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6 \ + --hash=sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5 \ + --hash=sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9 \ + --hash=sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd \ + --hash=sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277 \ + --hash=sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225 \ + --hash=sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22 \ + --hash=sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116 \ + --hash=sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16 \ + --hash=sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc \ + --hash=sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00 \ + --hash=sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2 \ + --hash=sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a \ + --hash=sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804 \ + --hash=sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04 \ + --hash=sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1 \ + --hash=sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba \ + --hash=sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390 \ + --hash=sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0 \ + --hash=sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d \ + --hash=sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22 \ + --hash=sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0 \ + --hash=sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2 \ + --hash=sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18 \ + --hash=sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6 \ + --hash=sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311 \ + --hash=sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89 \ + --hash=sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f \ + --hash=sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39 \ + --hash=sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4 \ + --hash=sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5 \ + --hash=sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa \ + --hash=sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a \ + --hash=sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050 \ + --hash=sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6 \ + --hash=sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235 \ + --hash=sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056 \ + --hash=sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2 \ + --hash=sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418 \ + --hash=sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c \ + --hash=sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a \ + --hash=sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6 \ + --hash=sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0 \ + --hash=sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775 \ + --hash=sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10 \ + --hash=sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c + # via + # lmnr + # opentelemetry-instrumentation + # opentelemetry-instrumentation-threading +wsproto==1.3.2 \ + --hash=sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584 \ + --hash=sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294 + # via openhands-agent-server +yarl==1.25.1 \ + --hash=sha256:0136d640dfa9b0523853e411430a99f8a91eca85774c6420285a33b755bc6de3 \ + --hash=sha256:03dd38de09bc213e9a8b29761eec33ee1d5318dac0e49d8af36e4d27830e23a7 \ + --hash=sha256:0a191bfdb30a79b98e5d175d75285f9fcb78bf0e46ba5efda042e1c72071a0de \ + --hash=sha256:0a66db89ea473abeac4b70523cafd94db3772380e565f9d28af7a179b7af71fa \ + --hash=sha256:0ae12ff2b805fa02c4dab838005caef735e39986322698c48588d3beacb65c62 \ + --hash=sha256:0f12afda4eea8c8994a76d4df1875c765194f5fbe8a9d197929ea303caee29ec \ + --hash=sha256:10b2fd95332f0d716d5eee3c9fb2ce8eada19082de7fee83d32e37992fd75c26 \ + --hash=sha256:126a2533570c554719ca40a1288fdee1700b6bc82e7131aa69fa85252d92e651 \ + --hash=sha256:12b6bc4906e11f5e1a1cdcb12296e7afbd366c783cc8073403cd2fb74334e453 \ + --hash=sha256:142c06c4d6a35ee3ec5da08499805e879cb3ca7c1fbfbecb0140fe72403818d6 \ + --hash=sha256:14b79a30a93a3ce2e8832603fd0ab780ada281b0ba5110b519a634f2d7d7d1fc \ + --hash=sha256:17c9877a89fb6e2bca6f9087eb24cd7fb434653946ef5075e470d23d49b52287 \ + --hash=sha256:192a866877a49993949ef1975864ad8728bea28ee810f6abe1a0729c2b500426 \ + --hash=sha256:1ab7618921a93767387a4b83776f751588f5b5ae9bb5bc96620e2e2e00bca868 \ + --hash=sha256:1e80dcf1446e1b080b1932b0d103c464a04112f5bc31f0f983ad418172063cde \ + --hash=sha256:1f51020b2eb8a003c84925638ec63c21a750a4bddd3a22ec8eac6a742dadf1b9 \ + --hash=sha256:1fb2a01ba8cd9c5d2c5dc1ec35e0fc951d04b4f037541d4ac090c993ce58b3d7 \ + --hash=sha256:2239a02249d9326655419e0168a28ca9008938eaab31dc29fc875c217927a6c0 \ + --hash=sha256:23bf5b403c879a54964e0feac7285688e04bb220074878d737d331522da0a5bf \ + --hash=sha256:24ce942011a61953e7d313438038f4d32ff21387b775f58a957f7a07dd55ef95 \ + --hash=sha256:25868beca8b6765f8f7d0e11fe6dd7c66dd4b0793b9500286d20cc92352126a5 \ + --hash=sha256:287e99ff5aa4dc1c7630bfc683ded6f106d756c99dec432a2d7f197a784f51c6 \ + --hash=sha256:29273edf1530e397bd07cb784db1fbe0d2590b77569f2e24679a9c0a2d763b94 \ + --hash=sha256:2b49375d22299b0a834c2bca72f39aaecc270d96fb24c30424899676f487b22a \ + --hash=sha256:30eec96e8a91bd588ce897c9543f6d5d8d34b28fbcba28a4dedf20ebeae9fe57 \ + --hash=sha256:319e070a01db9920fb63761843f96a104c8e2b9427266731810dc1e22595b17c \ + --hash=sha256:35dcbea443fafb3eece757ad4e514560ddeb6c34cfae1582c620d7b293d7feee \ + --hash=sha256:3f4d48a6112712973e676bd792121fee470e432d749177162d9949d5c9460a1b \ + --hash=sha256:3feb99222553a8cbedfa52c2f59dd84c3f50d5b582c728d522caf8d72769a54b \ + --hash=sha256:419f392a1da624877975709e3864dfe833af6cc7671b39318086d456e288380c \ + --hash=sha256:42a66563d8cc056ee32e6191e05097a7b2b3bc302e0bc3133daf8710eb18bd26 \ + --hash=sha256:48796ea00a303961507dc6c8437c4b325a6fc3f95f7c36c71b91ea9a8150963c \ + --hash=sha256:4bd6340d20ae2c7ca719b87b426e808e90743b676d05d4c26c4fb5ca71f41184 \ + --hash=sha256:4ca89e4e21854ed27ec753297dde84b16c9f8e53b14a4866fb44457d643c19f8 \ + --hash=sha256:4d781294bb815ecb5ea57ff6bbf8038e0a31a95fdf3e1788f66e0dc100d64b58 \ + --hash=sha256:4f1c91f5a5980a937ff8e238e98e6897e1ad74a4b1e2c0d68c73b5ffbb3f5c0b \ + --hash=sha256:564fdc7085d2245ab84f88882fdb1d6ac0723124bff6ded35bfb1c00f812630d \ + --hash=sha256:59ba3a6e1aa8cfe5adf4bd270fd965db21955401b7ca6f1696010c55ed4daec2 \ + --hash=sha256:5df89f769cc8ff94c3d7e7603386fba309d25ce5240132d26c15baa8d0e96c4c \ + --hash=sha256:632da579b2d879f6bad20f2cfa35ded1efe2f4f77f8abb26a6234a5b236acd2f \ + --hash=sha256:65b5b2066651b7432d389e9799d979c703bcc6ef44266bb8153ef54e91e4aab3 \ + --hash=sha256:664ec6a520b74a1df2810666eb67695fcb77fa663e6ea0a25aaf2e529cb24dfa \ + --hash=sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3 \ + --hash=sha256:683e362b8ba453080f7489c66f4ea794e751c35b72e7eab3575ef784c2fbc7fb \ + --hash=sha256:68782fdb4027b8d1eee25ec35e9a6db05e863b899eb0310b3a33b6c3fef55707 \ + --hash=sha256:6efaf45df6a849cef613a03a94c845647456662f85438c886bb67a9c027c8c2c \ + --hash=sha256:71f42c5b9a948c113bbdebfa544598321431d064ff959d32e99b1feb61d68345 \ + --hash=sha256:72849d892954be4d09e569b8b831ac39ce58417fedc767d4308a0fe542018a40 \ + --hash=sha256:72c34ac7ad4314c19362d5ce27626dcc8429bd30bbf8c179f4234078851f9492 \ + --hash=sha256:734f6e5400352ac4254456003d462866c684703570929cff7a7bde015d0cb371 \ + --hash=sha256:75baa6cf9b6d1c52f3e111a130e202fd8cf0a5b3a066c3f73d615e885092e4ec \ + --hash=sha256:77716e245c90f058466a05e6a465bb8600f767a8f4b18b4d40f3aff958e5f73c \ + --hash=sha256:77e5099b99b37f3cf79c246998ca9f7313a78054cd1809ec46bc1afad47e1c4c \ + --hash=sha256:783dd1467083f4d3f7722ad6a313f24c173e7571372738fcb7a6e6d1ba48df25 \ + --hash=sha256:7a5c3115595995779ee21f2567035793911c3802a43c74f3fbb0314929ec67ac \ + --hash=sha256:7c88edaec8c349ad4c5ad4c486a3defcc4b80ceb2f074436ffa0a87caf5e76a6 \ + --hash=sha256:7cb414a73e21a7ab58254926073f2930cb22f5b4314ea4260a687e2b3fd4dce3 \ + --hash=sha256:7d42e7e3ca399555578b4d617e3a6ecf13371b3743a115995fa010c7bf341459 \ + --hash=sha256:7d575b54cb3863ef9bc290ea4b009999d55dc237326131e4853cf33e888fee03 \ + --hash=sha256:7e4de3ac4adbad3d0bc7c6f4360a7dbff5de2f15e3b723be3198074e17fd9c40 \ + --hash=sha256:7efc9f082dfed77c316edffa9deb52888e1bc6789171887cc1f68e06d65465c8 \ + --hash=sha256:80a063f8297fc796296f00f100be520f209b23dc98f93ce8eba6ee7122598209 \ + --hash=sha256:80e47012e730da131c9f059c80936783f9659aae22dc31c03c0595590d11ed54 \ + --hash=sha256:83d4a37e4b95da4d8bda930d6d35b75b4cdadbacbb4980cae290ea3100b5d51d \ + --hash=sha256:83e9f4a25085bd4b7214701a0794ff1f50fc633ffb8bdfebf07abdd81c2db126 \ + --hash=sha256:85a18376073f8a39aa07be34f9fc77e2869aa72c55c441efdd2cf79a0407504d \ + --hash=sha256:87796fedc3ba97ec14fab55acb48584276e6c1e4c1e89c422bda62c838e754a9 \ + --hash=sha256:882569ff613758cac762a457a5d72d6e211b28d4bcfea89d1d71ea942b02eac0 \ + --hash=sha256:8ce4d6ccafb33d39bd78444612d14938ead674c25702ded2ee9c54a47735d225 \ + --hash=sha256:8e7d98cdbb6d71e726f7d525952867096053d1f290dd4e3c50d7d313a136f414 \ + --hash=sha256:8ee202350cf57abf0e9502a41601841019c25d3db7ff52d980aaf31446254059 \ + --hash=sha256:8fb0eb4955adf0579001581f2f71a126e8781ba61bcd120f127b0401163c6c2d \ + --hash=sha256:90c30ed53546da833c700115c0064c22120d1b1560f474699fd31f22dd668233 \ + --hash=sha256:9489e6abf47ba37f332075a91444c7cfedb03e6ce99fbb2f116bfe1ce810da3b \ + --hash=sha256:94d7aa6debf92a1dd14cb5280b083a764169a13cfb23a452111160274ed989f4 \ + --hash=sha256:98d370568f393215d605304cdb77b3d5539bd192c75b623c7304c42c8d6d8273 \ + --hash=sha256:9b1bdaae98bc016825dd3c9d8ee1832f829b3341f9cc6ebd1a1b0a7fef7367cc \ + --hash=sha256:9d693bf4bf534e9ba3ae2780cfd577f5135629f7b5ac653490859d0b77864865 \ + --hash=sha256:9d6ed3d17bccce4c05343e1ca8da13bc5c02c812a4e7282ddd05e8769322d3fc \ + --hash=sha256:9e23c82b63cd7652fc24d33ed6cc17099d607aa3b4fc4ddc75e95062f3d82df4 \ + --hash=sha256:a1daf47cd95a7c3a63456336bc5aaa8c86dd3a47d07ed3d0e76132ae4666a5a1 \ + --hash=sha256:a1e32763e641a1566507d90a8d3b19bfc3cc04a9d4e5ae3e32189874ed4b58a3 \ + --hash=sha256:a2059a2d891bd156bc5184e7ab7a56e78a84dfcfdeac8c501b552533ad1c36ee \ + --hash=sha256:a2ed0ba415ccdf08f14bf544cb78346d0f76086707ffee24921a2c84dbf1305a \ + --hash=sha256:a3faadac7d812ddac258feb57b9846b60c1b437c4f4b9ad42595c6f6fe4390df \ + --hash=sha256:a5877f2255aab518ebe528289037699201d5dc5f045f2396cb30aa02db22f57f \ + --hash=sha256:a78b50b4f7918a3de71105d5c0b93bbc57bb8339a4d03a9dfd449f9068e76f3d \ + --hash=sha256:a8c2b841478068440d8b733005d13a5ef535b9928cbc05f17182d410f32ba449 \ + --hash=sha256:a9ca696eb02e5c02a8afd872ada510eba9b7fe6e68b9572c2e9a9b1941e31e2e \ + --hash=sha256:aa4ed3dd308548f9e707d9caaf005d2d7f8c1e7868f858dfeb47fe76e16b391d \ + --hash=sha256:ab2054c5531af2a9ba7b69b8ec91e4f884420e83a8c5e579b013084cb57e5e5d \ + --hash=sha256:abb1384477f5901d436b5d2e5465954de46ea6098f59163d243660b5c4461d35 \ + --hash=sha256:acae6b45d1ace09b6ba3876da43b88366ef368f73b988c7f57e14231753d4420 \ + --hash=sha256:acfa7e22aa6c6e7a5996a41d275bfa01efa7ea56ab890590280e9063e2cf5c1b \ + --hash=sha256:af4ea5b37403ef4e30f3927eaed540db942bde01d8d3ff083527c0704d1c9c68 \ + --hash=sha256:b10dd0557ba422715b5206b3743192135a6022acca8baec51aa127d0a75db8fe \ + --hash=sha256:b13b88747769537f3d32e89e3a735da10c0a9e35d7322928c701b5f93d3afffd \ + --hash=sha256:b51c159a9794633f5e0db7ecec7b2b6e3734eca1f5d17dc989ff3552a43ff78b \ + --hash=sha256:b5402a340723fa7da00b5cff987ddab61276be6d11251ea71ae02bcac54890d8 \ + --hash=sha256:b7abffdf37af1cec6a2ad69b827aa84320db5894791bc8ed932dc93fb274b7e9 \ + --hash=sha256:b8075fe90bc08e40b8b8a1874fab42ee4c7b56af05c5886e9cc841397f916908 \ + --hash=sha256:bc3ac7bf569f6b64dad04dd7808c7872dae8a97df657856eac05e9b7e3614a85 \ + --hash=sha256:bd0912757081f89b107d6c00b2ff8a194401b0b87eadcf4481de2b865a8fd44f \ + --hash=sha256:bdc8d8b8c22e9e43ac68316b5e6cf083dec537f4ec213cb4aa967b583bc3fa64 \ + --hash=sha256:be80550d9bfe83d9b62398a37081a90434e6df2d978ec345c3d2820de6beddab \ + --hash=sha256:c6f117789d22dce188e5754e8bc65b7e6ebf8cb73963b9fa761f672a5883769d \ + --hash=sha256:ca32926d7d77bcc8838425c4c95e040a3ace1cb7dfdae599013458dcda2607ca \ + --hash=sha256:cce0727fd5ac04d372fa9bbfde9febc2bcf209aadfcf0468e45dec72719895d1 \ + --hash=sha256:d0f1489233a254bb3643d2f05de7d59019254d81daeca6b9162fe9edef57e0c7 \ + --hash=sha256:d1c557dfd5e3db046053a0bdc72261ade790ebe8e2c7a41b36b0ca1f14cb95f3 \ + --hash=sha256:d21f0fa80a02d05299207eeaafef345d812ace96d5306e4ef265e1d419a615fa \ + --hash=sha256:d45673badd08456d0340e9364eddafe1c53a9d2896424294de4d7dd71ad3ee57 \ + --hash=sha256:d5add7b4ca7afeea91d52e4d4e4db3b1fe9885b71f07054560d8c4296b7441a2 \ + --hash=sha256:d5f90e44653c4e0f78501ed9bb7d3fce835a8d62b7c6ed0cb16557534087e743 \ + --hash=sha256:d7306dee25b8a0e737363f347362b875094b4dc4e367311470656ae420fdbf8e \ + --hash=sha256:dbcef5a9119ef653653132cccaf999b30a0af6f33bb0a4ba80bec30056868487 \ + --hash=sha256:def538065f9e4d4cf1ae164bd59aba00dfa84f03923e0de4c3788f252d6bcd17 \ + --hash=sha256:df23df54b5114a17c2d0ef192433e2e5a9f0c5178c32375e90b7cfc965f349d0 \ + --hash=sha256:dfbf531053a0935f2e871bcd4753f90313688772ff8c017f5ea402e315a78c1f \ + --hash=sha256:e029648f9c951db30e98a7d7ec90835db88ec4b32820efe2a9bdc2287e032eb6 \ + --hash=sha256:e07595c7d6f4db270ceede356a1bd1c07a34f1c26f958d1ed0cd7b48e0d2bba3 \ + --hash=sha256:e12c538e00e7c1b286a07061046b90e8124e6a9793efae2c70db6a4aad07faad \ + --hash=sha256:e546fe1d4a93ebc2910f0d768baff19faa09843ab3f2036a67ed6e69fae4419d \ + --hash=sha256:e5637ca8d0bd7fb72648a6c7934af4baaccb697657f7438c9d264fc2abb8b0b1 \ + --hash=sha256:e636b64d24fd9c38053c5e389a1174c66361fa49dcfd220f4dd35b4abde7cb89 \ + --hash=sha256:e7011b8fb8c4054bf0c12e5edc6cd83778b0028e99ce59b18586ed036f92cfdc \ + --hash=sha256:e80f557716fd765439577131e526b8942ffc2c07bdbc5e39fa62f660ba1e963f \ + --hash=sha256:e92b6bcc741b86d67606c40d3cb9c7cc8e6c737f81e31f4a94efc204456c92e3 \ + --hash=sha256:eb96ed1ae6c7d072d60840c0434aef07a2df611812810807fbc54263a6053e9a \ + --hash=sha256:eda19ea5ee88742f47a2340816e6f2d40b53bed3ab5b69794769f36af9f35bb4 \ + --hash=sha256:ef74070ac553c59eb4f04258722066d6c6135b7baa03b2e9f2da65c096e96d98 \ + --hash=sha256:efb01a106f971cb3752856bca2318bbdf7f01bd8823779c461586cbe5ffd5258 \ + --hash=sha256:f074e8d4aa0a5798920ddb6de3d08b228c614ff3724c3e8bd7577f4bafea867b \ + --hash=sha256:f38a70074041d3b7e138e452799f5174198bae5bd5ab2000917badf403908c5f \ + --hash=sha256:f41753a76f4f63927d03a0d8ba8f5ce0f2083bec29a8cfaccc55371b1564b96b \ + --hash=sha256:f53dcd26694f148f738edc052b5a69234833e739f10f4c3287bdfd8ec0f7b326 \ + --hash=sha256:f61964f235a43738bfac50da46fc4254943a7eea3051aeb0b6fc7c992c29fadc \ + --hash=sha256:fe01645169a2112aa1d4ebc3e4c5f029c5c8f97adfc32e5d37c993b39a994d75 + # via aiohttp +zipp==4.1.1 \ + --hash=sha256:7ebb7a44c021b29fd8dbd7cce6812d0d7b5b454521f93cc71af6ccd155aaa70b \ + --hash=sha256:8979f52d874162f485ff2981e3891f3a3317b7a3dd43ff1e1775b9304f307a9c + # via importlib-metadata From db91af0fa8847890840f596ac2b3bb35ebcddce1 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:44:57 -0700 Subject: [PATCH 05/13] examples/openhands-agent-server: require the session key in oh_tool.py, record failed steps in run_trajectory.py oh_tool.py listened on all interfaces without authentication, so anyone who reached ate-env-api reached the file editor, and it stayed up after the run. It now requires X-Session-API-Key to match OH_SESSION_API_KEYS_0 (constant-time compare), refuses to start without it, answers 400 on a malformed body, rejects a non-numeric port, and stops on an authenticated POST /shutdown, which run_trajectory.py sends at the end of each run. run_trajectory.py records a step that cannot be executed (HTTP error, timeout, malformed step) as a failed observation instead of aborting, stops there unless --keep-going, exits 1, and rewrites --out after each step. The working directory is an explicit --workdir (default $WORKDIR, else /); the docs no longer claim the image's WORKDIR applies, because the agent-server runs in /. The grade trajectory only pops the stash it pushed. --- examples/openhands-agent-server/README.md | 26 ++- .../ate_env_workspace.py | 14 +- examples/openhands-agent-server/oh_tool.py | 118 ++++++++-- .../openhands-agent-server/run_trajectory.py | 212 ++++++++++++------ .../trajectories/django-11099-grade.json | 2 +- 5 files changed, 285 insertions(+), 87 deletions(-) diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md index 1d86995e..4ba70484 100644 --- a/examples/openhands-agent-server/README.md +++ b/examples/openhands-agent-server/README.md @@ -137,14 +137,24 @@ of `{"tool", "args", "thought"}`. Terminal commands go through the agent-server' File edits go through OpenHands' own `file_editor`, served inside the environment by `oh_tool.py` on the layer's Python and reached through ate-env-api like the agent-server, so observations read exactly as the agent sees them, and `undo_edit` works across steps. Each observation is printed and, -with `--out`, recorded next to its call. Nothing inside the environment calls a model: put a model -call before each step and this is a client-side agent, with the model wherever it is reachable. +with `--out`, recorded next to its call; the file is rewritten after each step. Nothing inside the +environment calls a model: put a model call before each step and this is a client-side agent, with +the model wherever it is reachable. ```bash SESSION_KEY= python examples/openhands-agent-server/run_trajectory.py \ - examples/openhands-agent-server/trajectories/django-11099.json --out run.json + examples/openhands-agent-server/trajectories/django-11099.json --workdir /testbed --out run.json ``` +- Working directory: a terminal step runs in its `cwd`, else in `--workdir` (default `$WORKDIR`, + else `/`). The image's WORKDIR does not apply, because the agent-server runs in `/` where the guest + starts it; the same holds for `ate_env_workspace.py` and `codeact_agent.py` (`WORKDIR`). +- Errors: a tool that reports an error (a non-zero exit status, a `file_editor` error) is an + observation like any other and the run goes on. A step that cannot be executed (a malformed step, + an unknown tool, an HTTP error, a timeout) is recorded with `"is_error": true, "failed": true`; the + run stops there unless `--keep-going` is given, and the script exits with status 1. +- `oh_tool.py` requires the session key (below) and is stopped and removed at the end of the run. + On the SWE-bench `django__django-11099` image (username validators accept a trailing newline): ``` @@ -239,5 +249,15 @@ GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port pr environment on a node pulls the layer. - `codeact_agent.py` calls the model from inside the environment (see above); `run_trajectory.py` and a client-side agent built on it do not. +- `oh_tool.py` listens on all interfaces (port 9100), so it is reachable through ate-env-api like the + agent-server. It requires the agent-server's session key in `X-Session-API-Key` on every request + (401 otherwise, compared in constant time) and refuses to start without `OH_SESSION_API_KEYS_0`. + The agent-server removes that variable from the commands it runs, so `run_trajectory.py` passes it + in the start command; the agent-server keeps that command in its record of commands, which only + holders of the key can read. A malformed request gets 400, a non-numeric port is an error. + `run_trajectory.py` stops it with an authenticated `POST /shutdown` and deletes `/tmp/oh_tool.py` + and its log at the end of the run, also after an error; if the client dies first, the server + keeps running in the environment (and in its snapshots) until the environment is deleted or + someone with the key posts `/shutdown`. The file editor's undo history stays under `/tmp`. - A daemon reached through ate-env-api must listen on all interfaces, not 127.0.0.1: the router delivers to the actor's address, and a loopback-only listener answers `502 bad gateway`. diff --git a/examples/openhands-agent-server/ate_env_workspace.py b/examples/openhands-agent-server/ate_env_workspace.py index 899a256a..d542d80e 100644 --- a/examples/openhands-agent-server/ate_env_workspace.py +++ b/examples/openhands-agent-server/ate_env_workspace.py @@ -27,7 +27,11 @@ Environment: ``API`` (ate-env-api address, default 127.0.0.1:7777), ``ATESPACE`` (default ate-env), ``PORT`` (agent-server port, default 8000), -``WORKDIR`` (directory the command runs in; default: the image's WORKDIR). +``WORKDIR`` (the workspace's working directory: where commands run and what +relative paths resolve against; default ``/``). The image's WORKDIR does not +apply: the agent-server runs in ``/``, where the guest starts it, and the SDK +sends no directory unless given one, so set ``WORKDIR`` (``/testbed`` on a +SWE-bench image) or pass ``working_dir`` to ``workspace_for``. """ from __future__ import annotations @@ -51,7 +55,11 @@ def agent_server_url(env_id: str, *, api: str | None = None, atespace: str | Non def workspace_for(env_id: str, *, api_key: str | None = None, working_dir: str | None = None, **url_kwargs) -> RemoteWorkspace: - """A ``RemoteWorkspace`` bound to the agent-server in ``env_id``.""" + """A ``RemoteWorkspace`` bound to the agent-server in ``env_id``. + + ``working_dir`` defaults to ``$WORKDIR``, else ``/``. Pass it as ``cwd`` to + ``execute_command``, which otherwise runs in the agent-server's directory. + """ return RemoteWorkspace( host=agent_server_url(env_id, **url_kwargs), api_key=api_key or os.environ["SESSION_KEY"], @@ -64,7 +72,7 @@ def main() -> None: sys.exit(f"usage: {sys.argv[0]} [command ...]") workspace = workspace_for(sys.argv[1]) command = " ".join(sys.argv[2:]) or 'head -1 /etc/os-release; echo "cwd: $(pwd)"' - result = workspace.execute_command(command, cwd=os.environ.get("WORKDIR"), timeout=60) + result = workspace.execute_command(command, cwd=workspace.working_dir, timeout=60) print(f"exit {result.exit_code}\n{result.stdout}{result.stderr}", end="") diff --git a/examples/openhands-agent-server/oh_tool.py b/examples/openhands-agent-server/oh_tool.py index 0517a3b8..d7bc1ba9 100644 --- a/examples/openhands-agent-server/oh_tool.py +++ b/examples/openhands-agent-server/oh_tool.py @@ -19,35 +19,65 @@ exact form the agent sees. One long-lived server holds the editor, so the heavy imports happen once and ``undo_edit`` sees earlier edits: - /opt/oh/python/bin/python3 oh_tool.py --serve 9100 --daemon - -It answers ``POST /`` with ``{"tool": "file_editor", "args": {...}}`` and -``GET /health``. Callers outside reach it through ate-env-api + OH_SESSION_API_KEYS_0= /opt/oh/python/bin/python3 oh_tool.py --serve 9100 [--daemon] + +Each request must carry the agent-server's session key in the +``X-Session-API-Key`` header; the server reads it from +``OH_SESSION_API_KEYS_0``, the agent-server's own variable, and refuses to +start without it. The agent-server removes that variable from the commands it +runs, so run_trajectory.py passes it in the start command. Endpoints: + +- ``GET /health``: ``{"status": "ok"}``; +- ``POST /`` with ``{"tool": "file_editor", "args": {...}}``: the observation, + ``{"text": ..., "is_error": ...}``; +- ``POST /shutdown``: stops the server, so it does not outlive the run or end + up in a snapshot. + +A missing or wrong key gets 401, a malformed body 400, both as JSON +``{"error": ...}``. Callers outside reach the server through ate-env-api (``/v1alpha/envs//ports/9100/``); the router delivers to the actor's address, not loopback, so it listens on all interfaces like the agent-server. """ from __future__ import annotations +import hmac import json import os import sys +import threading from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +KEY_ENV = "OH_SESSION_API_KEYS_0" +MAX_BODY = 64 << 20 + def run(step: dict) -> dict: if step.get("tool") != "file_editor": return {"text": f"oh_tool.py runs file_editor only, not {step.get('tool')!r}", "is_error": True} + args = step.get("args", {}) + if not isinstance(args, dict): + return {"text": "file_editor args must be a JSON object", "is_error": True} from openhands.tools.file_editor.impl import file_editor try: - obs = file_editor(**step.get("args", {})) + obs = file_editor(**args) except Exception as exc: # noqa: BLE001 (a bad call is an observation, not a crash) return {"text": f"{type(exc).__name__}: {exc}", "is_error": True} return {"text": obs.text, "is_error": bool(obs.is_error)} +class Server(ThreadingHTTPServer): + daemon_threads = True + + def __init__(self, address: tuple[str, int], key: str) -> None: + super().__init__(address, Handler) + self.key = key.encode() + + class Handler(BaseHTTPRequestHandler): + server: Server + def _reply(self, payload: dict, status: int = 200) -> None: body = json.dumps(payload).encode() self.send_response(status) @@ -56,12 +86,49 @@ def _reply(self, payload: dict, status: int = 200) -> None: self.end_headers() self.wfile.write(body) + def _authorized(self) -> bool: + given = self.headers.get("X-Session-API-Key", "").encode() + if hmac.compare_digest(given, self.server.key): + return True + self._reply({"error": "missing or wrong X-Session-API-Key"}, 401) + return False + + def _body(self) -> dict | None: + """The request body as a JSON object, or None after a 400 reply.""" + try: + length = int(self.headers.get("Content-Length", 0)) + if not 0 <= length <= MAX_BODY: + raise ValueError(f"Content-Length {length} out of range") + body = json.loads(self.rfile.read(length) or b"{}") + if not isinstance(body, dict): + raise TypeError("body must be a JSON object") + except (ValueError, TypeError) as exc: # UnicodeDecodeError is a ValueError + self.close_connection = True + self._reply({"error": f"malformed request: {exc}"}, 400) + return None + return body + def do_GET(self) -> None: - self._reply({"status": "ok"}) + if not self._authorized(): + return + if self.path.rstrip("/") in ("", "/health"): + self._reply({"status": "ok"}) + else: + self._reply({"error": f"no such path {self.path}"}, 404) def do_POST(self) -> None: - step = json.loads(self.rfile.read(int(self.headers.get("Content-Length", 0))) or b"{}") - self._reply(run(step)) + if not self._authorized(): + return + if self.path == "/shutdown": + self._reply({"status": "shutting down"}) + threading.Thread(target=self.server.shutdown, daemon=True).start() + return + if self.path != "/": + self._reply({"error": f"no such path {self.path}"}, 404) + return + step = self._body() + if step is not None: + self._reply(run(step)) def log_message(self, fmt: str, *args) -> None: sys.stderr.write("oh-tool: " + fmt % args + "\n") @@ -75,22 +142,39 @@ def daemonize(log_path: str) -> None: os.setsid() if os.fork(): os._exit(0) - log = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o644) + log = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600) null = os.open(os.devnull, os.O_RDONLY) os.dup2(null, 0) os.dup2(log, 1) os.dup2(log, 2) -if __name__ == "__main__": - args = sys.argv[1:] - if not args or args[0] != "--serve": - sys.exit("usage: oh_tool.py --serve PORT [--daemon]") - port = int(args[1]) if len(args) > 1 and args[1].isdigit() else 9100 - if "--daemon" in args: +def main(argv: list[str]) -> None: + usage = "usage: oh_tool.py --serve PORT [--daemon]" + if len(argv) < 2 or argv[0] != "--serve" or not set(argv[2:]) <= {"--daemon"}: + sys.exit(usage) + try: + port = int(argv[1]) + except ValueError: + sys.exit(f"oh_tool.py: port {argv[1]!r} is not a number\n{usage}") + if not 0 < port < 65536: + sys.exit(f"oh_tool.py: port {port} out of range") + key = os.environ.get(KEY_ENV, "") + if not key: + sys.exit(f"oh_tool.py: {KEY_ENV} is empty; set it to the agent-server's session key") + os.environ.pop(KEY_ENV) # not passed on to anything the editor starts + server = Server(("0.0.0.0", port), key) # bind before detaching: errors reach the caller + if "--daemon" in argv: daemonize(f"/tmp/oh_tool_{port}.log") from openhands.tools.file_editor.impl import ( - file_editor, # noqa: F401 (import once, up front) + file_editor, # noqa: F401 (import once, up front, after the fork) ) - ThreadingHTTPServer(("0.0.0.0", port), Handler).serve_forever() + try: + server.serve_forever() + finally: + server.server_close() + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/examples/openhands-agent-server/run_trajectory.py b/examples/openhands-agent-server/run_trajectory.py index 20ccdd7e..3739b33f 100644 --- a/examples/openhands-agent-server/run_trajectory.py +++ b/examples/openhands-agent-server/run_trajectory.py @@ -25,6 +25,23 @@ runtime layer's Python and reached through ate-env-api like the agent-server. Every observation is printed and, with ``--out``, written next to its call. +Errors. A tool that reports an error (a non-zero exit status, a file_editor +error) is an observation like any other, as the agent would see it, and the +run goes on. A step that cannot be executed at all (a malformed step, an +unknown tool, an HTTP error, a timeout, a lost connection) is recorded as an +observation with ``"is_error": true`` and ``"failed": true``; the run stops +there unless ``--keep-going`` is given, and the script exits with status 1. +``--out`` is rewritten after each step, so it holds every step that ran even +when the script is interrupted. + +Working directory. Terminal commands run in a step's ``cwd``, else in +``--workdir`` (default: ``$WORKDIR``, else ``/``). The image's WORKDIR does +not apply: the agent-server runs in ``/``, where the guest starts it, and the +SDK sends no directory of its own. + +The tool runner is started at the beginning and stopped at the end (also on +an error), so it does not outlive the run or end up in a snapshot. + The decisions come from the trajectory file, so nothing inside the environment needs to reach a model. The same loop with a model call before each step is a client-side agent: the model stays wherever it is reachable, @@ -42,7 +59,10 @@ import argparse import json import os +import shlex +import sys import time +import urllib.error import urllib.request from pathlib import Path @@ -53,78 +73,144 @@ TOOL_PORT = 9100 -def post_json(url: str, payload: dict, timeout: float = 120) -> dict: - req = urllib.request.Request(url, data=json.dumps(payload).encode(), - headers={"Content-Type": "application/json"}) - with urllib.request.urlopen(req, timeout=timeout) as resp: - return json.loads(resp.read()) - +class StepFailed(Exception): + """A step could not be executed (as opposed to a tool reporting an error).""" + + +class ToolRunner: + """oh_tool.py inside the environment, reached through ate-env-api.""" + + def __init__(self, workspace, env_id: str, key: str) -> None: + self.workspace = workspace + self.key = key + self.url = agent_server_url(env_id, port=TOOL_PORT) + + def request(self, path: str, payload: dict | None = None, timeout: float = 120) -> dict: + data = None if payload is None else json.dumps(payload).encode() + req = urllib.request.Request(self.url + path, data=data, headers={ + "Content-Type": "application/json", "X-Session-API-Key": self.key}) + with urllib.request.urlopen(req, timeout=timeout) as resp: + return json.loads(resp.read()) + + def start(self) -> None: + self.workspace.file_upload(Path(__file__).with_name("oh_tool.py"), TOOL_RUNNER) + # The agent-server strips its key from the commands it runs; hand it over + # here. The shell execs the command, so the key is in no process's argv. + res = self.workspace.execute_command( + f"OH_SESSION_API_KEYS_0={shlex.quote(self.key)} " + f"{LAYER_PYTHON} {TOOL_RUNNER} --serve {TOOL_PORT} --daemon", timeout=30) + if res.exit_code != 0: + raise RuntimeError(f"oh_tool.py did not start: {(res.stdout + res.stderr).strip()}") + deadline = time.monotonic() + 120 + while True: + try: + self.request("/health", timeout=10) + return + except urllib.error.HTTPError as exc: + if exc.code == 401: + raise RuntimeError("oh_tool.py refused the session key") from None + except OSError: + pass + if time.monotonic() > deadline: + raise RuntimeError(f"oh_tool.py did not answer; see /tmp/oh_tool_{TOOL_PORT}.log in the environment") + time.sleep(1) -def start_tool_runner(workspace, env_id: str) -> str: - """Start oh_tool.py inside the environment and return its URL.""" - workspace.file_upload(Path(__file__).with_name("oh_tool.py"), TOOL_RUNNER) - workspace.execute_command(f"{LAYER_PYTHON} {TOOL_RUNNER} --serve {TOOL_PORT} --daemon", timeout=30) - url = agent_server_url(env_id, port=TOOL_PORT) + "/" - deadline = time.monotonic() + 120 - while True: + def stop(self) -> None: + """Stop the server and remove its files; best effort.""" try: - with urllib.request.urlopen(url, timeout=10) as resp: - if resp.status == 200: - return url - except OSError: - if time.monotonic() > deadline: - raise RuntimeError("oh_tool.py did not start inside the environment") from None - time.sleep(1) - - -def run_step(workspace, tool_url: str, step: dict) -> dict: - tool, args = step["tool"], step.get("args", {}) - if tool == "terminal": - res = workspace.execute_command( - args["command"], - cwd=args.get("cwd") or os.environ.get("WORKDIR"), - timeout=float(args.get("timeout", 120)), - ) - text = (res.stdout + res.stderr).rstrip() - return {"text": f"{text}\n[exit {res.exit_code}]", "is_error": res.exit_code != 0} - if tool == "file_editor": - return post_json(tool_url, step) - if tool == "finish": - return {"text": args.get("message", ""), "is_error": False} - raise ValueError(f"unknown tool {tool!r}") - - -def main() -> None: + self.request("/shutdown", {}, timeout=10) + except (OSError, ValueError) as exc: + print(f"warning: oh_tool.py shutdown: {exc}", file=sys.stderr) + try: + self.workspace.execute_command(f"rm -f {TOOL_RUNNER} /tmp/oh_tool_{TOOL_PORT}.log", timeout=30) + except Exception as exc: # noqa: BLE001 (cleanup must not hide the run's result) + print(f"warning: removing oh_tool.py: {exc}", file=sys.stderr) + + +def run_step(workspace, tools: ToolRunner, step: object, workdir: str) -> dict: + """The observation for one step. Raises StepFailed if it cannot be executed.""" + if not isinstance(step, dict) or not isinstance(step.get("args", {}), dict): + raise StepFailed("a step must be an object with an object 'args'") + tool, args = step.get("tool"), step.get("args", {}) + try: + if tool == "terminal": + if not isinstance(args.get("command"), str): + raise StepFailed("terminal needs a string 'command'") + res = workspace.execute_command( + args["command"], cwd=args.get("cwd") or workdir, timeout=float(args.get("timeout", 120))) + text = (res.stdout + res.stderr).rstrip() + return {"text": f"{text}\n[exit {res.exit_code}]", "is_error": res.exit_code != 0} + if tool == "file_editor": + return tools.request("/", step) + if tool == "finish": + return {"text": str(args.get("message", "")), "is_error": False} + except StepFailed: + raise + except urllib.error.HTTPError as exc: + raise StepFailed(f"HTTP {exc.code} from the tool runner: {exc.read()[:500].decode(errors='replace')}") from exc + except Exception as exc: # transport errors, timeouts, bad arguments + raise StepFailed(f"{type(exc).__name__}: {exc}") from exc + raise StepFailed(f"unknown tool {tool!r}") + + +def write_out(path: Path | None, results: list) -> None: + if path is None: + return + tmp = path.with_name(path.name + ".tmp") + tmp.write_text(json.dumps(results, indent=2)) + tmp.replace(path) + + +def main() -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("env_id") parser.add_argument("trajectory", type=Path) - parser.add_argument("--out", type=Path, help="write the calls with their observations here") + parser.add_argument("--out", type=Path, help="write the calls with their observations here, after each step") + parser.add_argument("--workdir", default=os.environ.get("WORKDIR") or "/", + help="directory terminal steps without a cwd run in (default: $WORKDIR, else /)") + parser.add_argument("--keep-going", action="store_true", + help="go on after a step that cannot be executed instead of stopping") args = parser.parse_args() steps = json.loads(args.trajectory.read_text()) - workspace = workspace_for(args.env_id) - t0 = time.monotonic() - tool_url = start_tool_runner(workspace, args.env_id) - print(f"tool runner up in {time.monotonic() - t0:.1f}s") - - results = [] - started = time.monotonic() - for i, step in enumerate(steps, 1): - if step.get("thought"): - print(f"[{i}] thought {step['thought']}") - print(f"[{i}] {step['tool']:<11}{json.dumps(step.get('args', {}))[:150]}") + if not isinstance(steps, list): + sys.exit(f"{args.trajectory}: want a JSON list of steps") + key = os.environ["SESSION_KEY"] + workspace = workspace_for(args.env_id, api_key=key, working_dir=args.workdir) + tools = ToolRunner(workspace, args.env_id, key) + results: list[dict] = [] + failed = 0 + try: t0 = time.monotonic() - obs = run_step(workspace, tool_url, step) - results.append({**step, "observation": obs, "seconds": round(time.monotonic() - t0, 2)}) - marker = "error" if obs["is_error"] else "ok" - print("\n".join(f" {line}" for line in obs["text"].splitlines()[-12:]) or " (no output)") - print(f" -> {marker} in {results[-1]['seconds']}s") - if step["tool"] == "finish": - break - print(f"{len(results)} steps in {time.monotonic() - started:.1f}s") - if args.out: - args.out.write_text(json.dumps(results, indent=2)) + tools.start() + print(f"tool runner up in {time.monotonic() - t0:.1f}s") + started = time.monotonic() + for i, step in enumerate(steps, 1): + entry = dict(step) if isinstance(step, dict) else {"step": step} + name = entry.get("tool", "?") + if entry.get("thought"): + print(f"[{i}] thought {entry['thought']}") + print(f"[{i}] {name!s:<11}{json.dumps(entry.get('args', {}))[:150]}") + t0 = time.monotonic() + try: + obs = run_step(workspace, tools, step, args.workdir) + except StepFailed as exc: + obs = {"text": str(exc), "is_error": True, "failed": True} + failed += 1 + results.append({**entry, "observation": obs, "seconds": round(time.monotonic() - t0, 2)}) + write_out(args.out, results) + marker = "failed" if obs.get("failed") else "error" if obs["is_error"] else "ok" + print("\n".join(f" {line}" for line in obs["text"].splitlines()[-12:]) or " (no output)") + print(f" -> {marker} in {results[-1]['seconds']}s") + if name == "finish" or (obs.get("failed") and not args.keep_going): + break + print(f"{len(results)} steps in {time.monotonic() - started:.1f}s" + + (f", {failed} could not be executed" if failed else "")) + finally: + write_out(args.out, results) + tools.stop() + return 1 if failed else 0 if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/examples/openhands-agent-server/trajectories/django-11099-grade.json b/examples/openhands-agent-server/trajectories/django-11099-grade.json index cf676beb..7be665e1 100644 --- a/examples/openhands-agent-server/trajectories/django-11099-grade.json +++ b/examples/openhands-agent-server/trajectories/django-11099-grade.json @@ -31,7 +31,7 @@ "thought": "Same tests with the fix stashed: they must fail.", "tool": "terminal", "args": { - "command": "git stash push -q django/contrib/auth/validators.py && { /opt/miniconda3/envs/testbed/bin/python tests/runtests.py auth_tests.test_validators.UsernameValidatorsTests 2>&1 | grep -E '^(Ran|OK|FAILED)'; }; git stash pop -q && git diff --stat", + "command": "if git diff --quiet -- django/contrib/auth/validators.py; then echo 'no fix to stash' >&2; exit 1; fi; git stash push -q -- django/contrib/auth/validators.py && { /opt/miniconda3/envs/testbed/bin/python tests/runtests.py auth_tests.test_validators.UsernameValidatorsTests 2>&1 | grep -E '^(Ran|OK|FAILED)'; git stash pop -q; } && git diff --stat", "cwd": "/testbed", "timeout": 600 } From ddd7e68ff4db0e74de616c3e3afd2f6930f264e0 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:46:48 -0700 Subject: [PATCH 06/13] examples/openhands-agent-server: make demo.sh rerunnable with a fixed or fresh session key A second run failed at kubectl-ate create because the base template existed, and deleting only the base left the derived per-image templates with the old key, so creates got 401 while the demo exited 0. demo.sh now deletes the base template and the templates derived from it (-<12 hex>) before registering, or keeps them with REUSE=1 and a fixed SESSION_KEY. It never prints the key (and requires SESSION_KEY with KEEP=1 or REUSE=1), exits 1 if any agent-server does not answer, splits IMAGES and AGENT_SERVER_ENV into arrays, and states the glibc 2.31 floor and the real default working directory. --- examples/openhands-agent-server/README.md | 9 ++- examples/openhands-agent-server/demo.sh | 84 ++++++++++++++++++----- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md index 4ba70484..290a4037 100644 --- a/examples/openhands-agent-server/README.md +++ b/examples/openhands-agent-server/README.md @@ -201,7 +201,14 @@ examples/openhands-agent-server/demo.sh ``` It also takes `PORT`, `AGENT_SERVER_ENV` (extra `NAME=VALUE` settings), `COMMAND`, `WORKDIR` and -`KEEP=1` (leave the environments up for the SDK scripts); see its header. +`KEEP=1` (leave the environments up for the SDK scripts); see its header. It exits 1 if any +agent-server did not answer. + +The session key is `SESSION_KEY`, else a random one that is never printed; set it with `KEEP=1`, so +that you can reach the environments afterwards. Templates are immutable and the key is in them, also +in the per-image templates that creates derive (`-<12 hex digits of the image digest>`). So +before registering, the demo deletes the base template and every template derived from it, with +their golden snapshots; with `REUSE=1` (and the same `SESSION_KEY`) it keeps them instead. ## Versions diff --git a/examples/openhands-agent-server/demo.sh b/examples/openhands-agent-server/demo.sh index 5cb50bb6..259301a9 100755 --- a/examples/openhands-agent-server/demo.sh +++ b/examples/openhands-agent-server/demo.sh @@ -21,23 +21,37 @@ # ate-env-api's port proxy with three routing headers plus the agent-server's # own X-Session-API-Key. The script registers a base template, creates one # environment per image, drives each agent-server from outside (health, a -# command, a file download, a call without the key) and deletes what it -# created, unless KEEP=1. +# command, a file download, a call without the key) and deletes the +# environments, unless KEEP=1. It exits 1 if any agent-server did not answer. +# +# Templates are immutable and carry the session key, and the first create from +# each image derives a template (-<12 hex digits of the image digest>) +# that copies the key. So before registering, the script deletes the base +# template and every template derived from it, with their golden snapshots; +# REUSE=1 keeps an existing base and its derived templates instead, for runs +# with the same SESSION_KEY. # # Required: # GUEST_IMAGE digest-pinned ate-env-guest image (with the -sidecar flags) # SNAPSHOTS_BUCKET object-storage URL for actor snapshots # LAYER_IMAGE digest-pinned layer image built from ./Dockerfile # IMAGES space-separated digest-pinned images to run the -# agent-server in; any image with /bin/sh +# agent-server in; any image with /bin/sh and glibc 2.31 +# or newer # Optional: -# SESSION_KEY agent-server API key on the template (default: random) +# SESSION_KEY agent-server API key on the template (default: random, +# never printed; required with KEEP=1 or REUSE=1) +# REUSE 1 keeps an existing base template and its derived +# templates (they must carry SESSION_KEY) # PORT agent-server port in the environment (default 8000) # AGENT_SERVER_ENV space-separated NAME=VALUE settings for the agent-server, # e.g. "LOG_JSON=true"; each becomes a template --env +# (values cannot contain spaces) # COMMAND shell command the demo runs through the agent-server # (default: distribution, user and working directory) -# WORKDIR directory COMMAND runs in (default: the image's WORKDIR) +# WORKDIR directory COMMAND runs in (default: the agent-server's, +# which is / under the guest; the image's WORKDIR does +# not apply) # KEEP 1 keeps the environments and prints how to reach them # TEMPLATE base template name (default oh-agent-server) # ATESPACE atespace (default ate-env) @@ -50,8 +64,15 @@ set -euo pipefail : "${SNAPSHOTS_BUCKET:?set SNAPSHOTS_BUCKET to the snapshot object-storage URL}" : "${LAYER_IMAGE:?set LAYER_IMAGE to the digest-pinned agent-server layer image}" : "${IMAGES:?set IMAGES to one or more digest-pinned images}" -SESSION_KEY=${SESSION_KEY:-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n')} +if [ -z "${SESSION_KEY:-}" ]; then + if [ "${KEEP:-}" = 1 ] || [ "${REUSE:-}" = 1 ]; then + echo "set SESSION_KEY: with KEEP=1 or REUSE=1 you need the key later, and a random one is never printed" >&2 + exit 2 + fi + SESSION_KEY=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n') +fi PORT=${PORT:-8000} +# shellcheck disable=SC2016 # expanded by the environment's shell, not here COMMAND=${COMMAND:-'head -1 /etc/os-release; echo "user: $(id -un)"; echo "cwd: $(pwd)"'} TEMPLATE=${TEMPLATE:-oh-agent-server} ATESPACE=${ATESPACE:-ate-env} @@ -71,18 +92,43 @@ oh() { -H "X-Session-API-Key: ${SESSION_KEY}" "$@" } +read -r -a images <<<"${IMAGES}" +read -r -a settings <<<"${AGENT_SERVER_ENV:-}" env_flags=(--env "OH_SESSION_API_KEYS_0=${SESSION_KEY}") -for kv in ${AGENT_SERVER_ENV:-}; do +for kv in ${settings[@]+"${settings[@]}"}; do env_flags+=(--env "${kv}") done -step "register ${ATESPACE}/${TEMPLATE}: guest + agent-server layer on port ${PORT}" -"${ATE_ENV}" manifest template --template "${TEMPLATE}" --atespace "${ATESPACE}" \ - --guest-image "${GUEST_IMAGE}" --snapshots-bucket "${SNAPSHOTS_BUCKET}" \ - --layer "oh=${LAYER_IMAGE}=/opt/oh" \ - --sidecar "/opt/oh/python/bin/python3 -m openhands.agent_server --host 0.0.0.0 --port ${PORT}" \ - --sidecar-readyz "http://127.0.0.1:${PORT}/health" \ - "${env_flags[@]}" | "${KUBECTL_ATE}" create actor-template -f - >/dev/null +# templates: the names of the base template and the templates derived from it. +templates() { + "${KUBECTL_ATE}" get actor-template --atespace "${ATESPACE}" -o json | + TEMPLATE="${TEMPLATE}" python3 -c ' +import json, os, re, sys +base = os.environ["TEMPLATE"] +own = re.compile(re.escape(base) + r"(-[0-9a-f]{12})?") +d = json.load(sys.stdin) +for t in d.get("actorTemplates", d.get("actor_templates", [])): + name = t.get("metadata", {}).get("name", "") + if own.fullmatch(name): + print(name)' +} + +existing=$(templates) +if [ "${REUSE:-}" = 1 ] && [ -n "${existing}" ]; then + step "reusing ${ATESPACE}/${TEMPLATE} and the templates derived from it (REUSE=1)" +else + for name in ${existing}; do + step "delete ${ATESPACE}/${name} (it carries an older session key)" + "${KUBECTL_ATE}" delete actor-template --atespace "${ATESPACE}" "${name}" >/dev/null + done + step "register ${ATESPACE}/${TEMPLATE}: guest + agent-server layer on port ${PORT}" + "${ATE_ENV}" manifest template --template "${TEMPLATE}" --atespace "${ATESPACE}" \ + --guest-image "${GUEST_IMAGE}" --snapshots-bucket "${SNAPSHOTS_BUCKET}" \ + --layer "oh=${LAYER_IMAGE}=/opt/oh" \ + --sidecar "/opt/oh/python/bin/python3 -m openhands.agent_server --host 0.0.0.0 --port ${PORT}" \ + --sidecar-readyz "http://127.0.0.1:${PORT}/health" \ + "${env_flags[@]}" | "${KUBECTL_ATE}" create actor-template -f - >/dev/null +fi ids=() cleanup() { @@ -96,7 +142,8 @@ trap cleanup EXIT body=$(COMMAND="${COMMAND}" WORKDIR="${WORKDIR:-}" python3 -c 'import json,os; d={"command": os.environ["COMMAND"], "timeout": 60}; w=os.environ.get("WORKDIR"); d.update({"cwd": w} if w else {}); print(json.dumps(d))') n=0 -for image in ${IMAGES}; do +failed=0 +for image in "${images[@]}"; do n=$((n + 1)) id="oh-$(date +%s)-${n}" ids+=("${id}") @@ -113,6 +160,7 @@ for image in ${IMAGES}; do done if [ "${code}" != 200 ]; then echo "agent-server in ${id} did not answer (last status ${code})" >&2 + failed=$((failed + 1)) continue fi echo "agent-server answering after $(( $(date +%s) - t0 ))s" @@ -130,8 +178,12 @@ done if [ "${KEEP:-}" = 1 ]; then step "kept: ${ids[*]}" - echo "reach one with: -H 'X-Env-Id: ' -H 'X-Env-Port: ${PORT}' -H 'X-Session-API-Key: ${SESSION_KEY}' http://${API}/..." + echo "reach one with: -H 'X-Env-Id: ' -H 'X-Env-Port: ${PORT}' -H \"X-Session-API-Key: \$SESSION_KEY\" http://${API}/..." echo "delete with: ${ATE_ENV} --api ${API} delete --atespace ${ATESPACE}" else step "done; deleting the environments (the templates stay for reuse)" fi +if [ "${failed}" -gt 0 ]; then + echo "${failed} of ${n} agent-servers did not answer" >&2 + exit 1 +fi From 0296dda16467b295d0266dc61bb6f2d563ecc85f Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:47:36 -0700 Subject: [PATCH 07/13] examples/openhands-agent-server: document where LLM_API_KEY goes, delete the conversation on close codeact_agent.py hands the model key to the agent-server inside the sandbox, where the agent's commands run as the same user; the README and the docstring now say so and what to use instead. The conversation is created with delete_on_close=True (openhands-sdk 1.55.0's parameter), so the agent-server removes it and its files when the script ends. The task line no longer fails on a user message without text content. --- examples/openhands-agent-server/README.md | 10 +++++++++- .../openhands-agent-server/codeact_agent.py | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md index 290a4037..ab5390f2 100644 --- a/examples/openhands-agent-server/README.md +++ b/examples/openhands-agent-server/README.md @@ -113,7 +113,8 @@ SESSION_KEY= LLM_MODEL= LLM_API_KEY= [LLM_BASE_URL= None: elif isinstance(event, AgentErrorEvent): print(f" error {event.tool_name}: {short(event.error)}") elif isinstance(event, MessageEvent) and event.source == "user": - print(f" task {short(event.llm_message.content[0].text)}") + text = " ".join(getattr(c, "text", "") for c in event.llm_message.content) + print(f" task {short(text) or '(no text)'}") def main() -> None: @@ -114,7 +120,10 @@ def main() -> None: base_url=os.environ.get("LLM_BASE_URL"), usage_id="agent") agent = get_default_agent(llm, cli_mode=True) - conversation = Conversation(agent=agent, workspace=workspace, callbacks=[on_event], visualizer=None) + # delete_on_close: the agent-server drops the conversation (its events, the + # agent with the model settings) when this script closes it. + conversation = Conversation(agent=agent, workspace=workspace, callbacks=[on_event], visualizer=None, + delete_on_close=True) started = time.monotonic() try: conversation.send_message(args.task) From 903daa0c36fb798b487ab465a09f62c12d5d2e29 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:47:54 -0700 Subject: [PATCH 08/13] ate-env manifest template: validate --env in its own step, not in resolveImages --- cmd/ate-env/manifest.go | 12 +++++++++--- cmd/ate-env/manifest_test.go | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/cmd/ate-env/manifest.go b/cmd/ate-env/manifest.go index f9908496..84951d84 100644 --- a/cmd/ate-env/manifest.go +++ b/cmd/ate-env/manifest.go @@ -196,6 +196,12 @@ func (c *templateConfig) validateResources() error { return nil } +// validateEnv checks the --env flags before anything is built. +func (c *templateConfig) validateEnv() error { + _, err := parseEnv(c.env) + return err +} + func (c *templateConfig) resolveImages() error { if c.guestImage == "" { return errors.New(`--guest-image is required when ate-env is built from source; release @@ -205,9 +211,6 @@ binaries default it to the ate-env-guest image published with the release if c.snapshotsBucket == "" { return errors.New("--snapshots-bucket is required; use an object-storage bucket (e.g. gs://bucket/prefix/)") } - if _, err := parseEnv(c.env); err != nil { - return err - } if c.rootfsImage != "" { if _, err := apiservice.ImageDigest(c.rootfsImage); err != nil { return fmt.Errorf("--rootfs-image: %w", err) @@ -301,6 +304,9 @@ It prints YAML to stdout without touching the cluster.`, if err := tCfg.validateResources(); err != nil { return err } + if err := tCfg.validateEnv(); err != nil { + return err + } tmpl, err := buildTemplate(tCfg) if err != nil { return err diff --git a/cmd/ate-env/manifest_test.go b/cmd/ate-env/manifest_test.go index 54ad433e..43e3fb3e 100644 --- a/cmd/ate-env/manifest_test.go +++ b/cmd/ate-env/manifest_test.go @@ -16,6 +16,7 @@ package main import ( "bytes" + "io" "strings" "testing" @@ -581,7 +582,7 @@ func TestTemplateConfigRejectsBadEnv(t *testing.T) { } { cfg := testTemplateConfig() cfg.env = env - if err := cfg.resolveImages(); err == nil { + if err := cfg.validateEnv(); err == nil { t.Errorf("env %q: want error, got nil", env) } } @@ -604,3 +605,18 @@ func TestManifestTemplateCommandEnv(t *testing.T) { t.Errorf("output lacks the --env variable:\n%s", out) } } + +func TestManifestTemplateCommandRejectsBadEnv(t *testing.T) { + cmd := newManifestCommand() + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs([]string{ + "template", + "--guest-image=example.com/guest@sha256:aaaa", + "--snapshots-bucket=gs://bucket/prefix/", + "--env=PORT=81", + }) + if err := cmd.Execute(); err == nil || !strings.Contains(err.Error(), "--env PORT") { + t.Errorf("execute with --env PORT=81: want the --env error, got %v", err) + } +} From 9eb10d19e990fcd4c2e4967dd3238e228a250360 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:48:06 -0700 Subject: [PATCH 09/13] docs/rootfs-images: a layer that is not statically linked needs the image's C library --- docs/rootfs-images/RUNTIMES.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/rootfs-images/RUNTIMES.md b/docs/rootfs-images/RUNTIMES.md index e382101e..96cdb818 100644 --- a/docs/rootfs-images/RUNTIMES.md +++ b/docs/rootfs-images/RUNTIMES.md @@ -21,7 +21,8 @@ A runtime layer is three things: The guest is the container's only process by contract, which is why it is the one that supervises the others. Sidecars run without a shell or `PATH` lookup, so the binary is an absolute path into its mount, and the rootfs image needs -nothing for it. They inherit the container's user, environment and filesystem. +nothing for it beyond, for a binary that is not statically linked, a +compatible C library (the OpenHands layer needs glibc 2.31 or newer). They inherit the container's user, environment and filesystem. Template derivation keeps all of it. A base with layers yields derived templates with the same layers and the same guest flags, so environments @@ -78,7 +79,7 @@ A verified stand-in for the mechanics, which needs nothing from any registry but Docker Hub, is in [`examples/runtime-layers`](../../examples/runtime-layers): the static `busybox:1.37-musl` mounted as the layer and its `httpd` as the sidecar, run under two different rootfs images. A real daemon, the OpenHands agent-server in -any image, configured by `--env` and driven by OpenHands' own CodeAct agent, is +any image with glibc 2.31 or newer, configured by `--env` and driven by OpenHands' own CodeAct agent, is in [`examples/openhands-agent-server`](../../examples/openhands-agent-server). ## Who can reach the second runtime From fce684c2719a8452506515836f9da098aa145804 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:48:16 -0700 Subject: [PATCH 10/13] examples/openhands-agent-server: record what the rebuilt layer was verified on --- examples/openhands-agent-server/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/openhands-agent-server/README.md b/examples/openhands-agent-server/README.md index ab5390f2..ccbff427 100644 --- a/examples/openhands-agent-server/README.md +++ b/examples/openhands-agent-server/README.md @@ -249,6 +249,12 @@ GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port pr - The arm64 build of the layer: the agent-server answered `/health` within 9 s in an arm64 Ubuntu 22.04 container (local Docker); the CodeAct flow ran the same way locally before the cluster run. +- 2026-10-16, after the hash-locked rebuild, local Docker on arm64 only (the cluster runs above used + the earlier layer): the layer mounted read-only at `/opt/oh` in `debian:bullseye-slim` (glibc + 2.31) and `ubuntu:24.04` answered `/health` and ran a command with the key (401 without); + `run_trajectory.py` ran `hello.json` and recorded a failed step in `--out`; `codeact_agent.py + --scripted` finished and its conversation was gone afterwards. The amd64 build is not yet verified. + ## Limits - One API key per template: it is in the template's environment, readable by anyone who can read From 7c2ba6ae5801592cb697c540e2c91f6b8e756bd8 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:51:57 -0700 Subject: [PATCH 11/13] examples/openhands-agent-server: license headers on the lock inputs, import order make verify-boilerplate requires the header on requirements.in and build-constraints.in; uv pip compile ignores the comments, and lock.sh still writes the same requirements.txt apart from upstream releases since the lock. ruff's import order in codeact_agent.py. --- .../openhands-agent-server/build-constraints.in | 14 ++++++++++++++ examples/openhands-agent-server/codeact_agent.py | 3 +-- examples/openhands-agent-server/requirements.in | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/examples/openhands-agent-server/build-constraints.in b/examples/openhands-agent-server/build-constraints.in index 2b7d3e2b..6a70646a 100644 --- a/examples/openhands-agent-server/build-constraints.in +++ b/examples/openhands-agent-server/build-constraints.in @@ -1,3 +1,17 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # Build backend for the one dependency that ships only as source # (func-timeout, pure Python). ./lock.sh pins it with hashes. setuptools diff --git a/examples/openhands-agent-server/codeact_agent.py b/examples/openhands-agent-server/codeact_agent.py index 58bce14b..ae781a9d 100644 --- a/examples/openhands-agent-server/codeact_agent.py +++ b/examples/openhands-agent-server/codeact_agent.py @@ -48,6 +48,7 @@ import time from pathlib import Path +from ate_env_workspace import workspace_for from openhands.sdk import LLM, Conversation from openhands.sdk.event import ( ActionEvent, @@ -57,8 +58,6 @@ ) from openhands.tools.preset.default import get_default_agent -from ate_env_workspace import workspace_for - DEFAULT_TASK = ( "Inspect this sandbox: report the operating system, the working directory and the " "top-level files. Then create /tmp/codeact-demo/hello.txt containing 'hello from " diff --git a/examples/openhands-agent-server/requirements.in b/examples/openhands-agent-server/requirements.in index 04a3f63f..8e020adc 100644 --- a/examples/openhands-agent-server/requirements.in +++ b/examples/openhands-agent-server/requirements.in @@ -1,3 +1,17 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # The runtime layer's top-level requirement. Change the version here, then run # ./lock.sh to regenerate requirements.txt; keep the client's openhands-sdk at # the same version (see the README). From 3649b72797dc5dc0c7fe95ff3b3843adccc5fb00 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 16:01:37 -0700 Subject: [PATCH 12/13] integrations/openhands: AteEnvWorkspace, an AgentSandboxWorkspace-compatible OpenHands workspace A pip package, ate-env-openhands, with one class: AteEnvWorkspace, an OpenHands RemoteWorkspace on an ate-env environment. It mirrors OpenHands' AgentSandboxWorkspace (openhands-workspace[agent-sandbox]): the same lifecycle order (create, health wait, parent init, clean up on any failure after the create), pause()/resume(), __exit__ then cleanup(), idempotent cleanup(), and the same field names where the meaning carries over (server_port, working_dir, health_check_timeout, api_key). The warm pool maps to `template`; the task image, which agent-sandbox fixes in its SandboxTemplate, is a field, because the agent-server comes from a runtime layer and runs in any image. - The host is the port proxy's path form (/v1alpha/atespaces//envs//ports/

), thus no kubectl port-forward; RemoteConversation's event WebSocket goes through it unchanged. - A lost create answer is deleted by name; a health timeout or a rejected session key deletes the environment before the constructor raises. The session key is probed once, because the SDK turns a 401 on a command into a failed result rather than an error. - env_id attaches to an existing environment and leaves it on exit. - pause() suspends; resume() waits for /health, which wakes the environment. - api_token goes to the gRPC API only: the port proxy would forward it. - One loop thread and one gRPC channel per endpoint for all workspaces. Tests run the workspace against an in-process fake of ate-env-api (gRPC) and of its port proxy (path form, wake on request, upgrade relay), in front of a real agent-server 1.55.0 on localhost: 18 tests, plus a live test gated on SUBSTRATE_E2E_API_URL. CI gets an "OpenHands integration" job. --- .github/workflows/ci.yaml | 18 +- integrations/openhands/.gitignore | 6 + integrations/openhands/pyproject.toml | 58 +++ .../src/ate_env_openhands/__init__.py | 26 ++ .../src/ate_env_openhands/_runtime.py | 62 ++++ .../src/ate_env_openhands/workspace.py | 341 ++++++++++++++++++ integrations/openhands/tests/__init__.py | 13 + integrations/openhands/tests/conftest.py | 73 ++++ integrations/openhands/tests/fakes.py | 245 +++++++++++++ integrations/openhands/tests/test_e2e.py | 61 ++++ .../openhands/tests/test_workspace.py | 225 ++++++++++++ 11 files changed, 1127 insertions(+), 1 deletion(-) create mode 100644 integrations/openhands/.gitignore create mode 100644 integrations/openhands/pyproject.toml create mode 100644 integrations/openhands/src/ate_env_openhands/__init__.py create mode 100644 integrations/openhands/src/ate_env_openhands/_runtime.py create mode 100644 integrations/openhands/src/ate_env_openhands/workspace.py create mode 100644 integrations/openhands/tests/__init__.py create mode 100644 integrations/openhands/tests/conftest.py create mode 100644 integrations/openhands/tests/fakes.py create mode 100644 integrations/openhands/tests/test_e2e.py create mode 100644 integrations/openhands/tests/test_workspace.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1fa209ce..3c17462b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,7 +13,7 @@ # limitations under the License. # Checks for pull requests and main: the Go build, vet and tests, gofmt, the -# license headers, and the Python client's unit tests. Release publishing lives in +# license headers, the Python client's unit tests, and the integrations' tests. Release publishing lives in # release.yaml. Actions are pinned by commit SHA (version in the trailing # comment), as the organization's workflow security scan requires. name: ci @@ -63,3 +63,19 @@ jobs: - run: python -m pytest internal/benchmarking/swebench/trajectories -q - run: ruff check clients/python - run: python -m pytest integrations/nemo-gym/tests -q + + openhands: + # A job of its own: openhands-sdk and the agent-server the tests start pull + # in a large dependency set that the other Python checks do not need. + name: OpenHands integration + runs-on: ubuntu-24.04 # pinned: ubuntu-latest moves to 26.04 from 2026-10-19; bump deliberately + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + - run: python -m pip install -e 'clients/python[dev]' -e 'integrations/openhands[test]' + - run: python -m pytest integrations/openhands/tests -q + - run: ruff check integrations/openhands diff --git a/integrations/openhands/.gitignore b/integrations/openhands/.gitignore new file mode 100644 index 00000000..e05d4abe --- /dev/null +++ b/integrations/openhands/.gitignore @@ -0,0 +1,6 @@ +*.egg-info/ +__pycache__/ +.pytest_cache/ +.venv/ +build/ +dist/ diff --git a/integrations/openhands/pyproject.toml b/integrations/openhands/pyproject.toml new file mode 100644 index 00000000..800cbcb7 --- /dev/null +++ b/integrations/openhands/pyproject.toml @@ -0,0 +1,58 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" + +[project] +name = "ate-env-openhands" +version = "0.1.0" +description = "OpenHands workspaces on Agent Substrate environments (ate-env), compatible with AgentSandboxWorkspace" +readme = "README.md" +license = { text = "Apache-2.0" } +requires-python = ">=3.12" # what openhands-sdk requires +# ate-env-client is this repo's Python client (clients/python); it is not on +# PyPI yet, so install it from the checkout first: pip install ./clients/python +# openhands-sdk is pinned to the agent-server in layer/requirements.in: the SDK +# and the agent-server it talks to move together. +dependencies = [ + "ate-env-client>=0.1.0", + "openhands-sdk==1.55.0", + "httpx>=0.27", +] + +[project.optional-dependencies] +# The examples run OpenHands' default agent and tools. +examples = ["openhands-tools==1.55.0"] +# The integration tests start a real agent-server on localhost behind a fake +# port proxy. +test = ["pytest>=8", "openhands-agent-server==1.55.0", "openhands-tools==1.55.0"] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.uv.sources] +ate-env-client = { path = "../../clients/python", editable = true } + +[tool.ruff] +target-version = "py312" +extend-exclude = ["examples/trajectories"] + +[tool.ruff.lint] +# The same rules as clients/python. +select = ["E4", "E7", "E9", "F", "I", "UP"] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/integrations/openhands/src/ate_env_openhands/__init__.py b/integrations/openhands/src/ate_env_openhands/__init__.py new file mode 100644 index 00000000..5579289e --- /dev/null +++ b/integrations/openhands/src/ate_env_openhands/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""OpenHands workspaces on Agent Substrate environments (ate-env). + +``AteEnvWorkspace`` runs the OpenHands agent-server in an ate-env environment +created from any task image, and connects the OpenHands SDK to it through +ate-env-api's port proxy. It mirrors OpenHands' ``AgentSandboxWorkspace`` +(openhands-workspace, kubernetes-sigs/agent-sandbox), so code written against +that workspace moves over by changing the constructor. +""" + +from .workspace import AteEnvWorkspace, port_url + +__all__ = ["AteEnvWorkspace", "port_url"] diff --git a/integrations/openhands/src/ate_env_openhands/_runtime.py b/integrations/openhands/src/ate_env_openhands/_runtime.py new file mode 100644 index 00000000..333e7dfc --- /dev/null +++ b/integrations/openhands/src/ate_env_openhands/_runtime.py @@ -0,0 +1,62 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""One background event loop, and one ate-env client per endpoint, for all workspaces. + +The OpenHands workspace API is synchronous and ``ate_env.Client`` is asyncio +gRPC. A fleet of workspaces shares one loop thread and one channel per +(endpoint, token) instead of a thread and a channel each. +""" + +from __future__ import annotations + +import threading +from collections.abc import Coroutine +from typing import Any + +from ate_env import Client +from ate_env.loop_runner import LoopRunner + +_lock = threading.Lock() +_runner: LoopRunner | None = None +_clients: dict[tuple[str, str | None], Client] = {} + + +def _get_runner() -> LoopRunner: + global _runner + with _lock: + if _runner is None: + _runner = LoopRunner(name="ate-env-openhands") + return _runner + + +def run[T](coro: Coroutine[Any, Any, T]) -> T: + """Run a coroutine on the shared loop and block for its result.""" + return _get_runner().run(coro) + + +def client(endpoint: str, token: str | None) -> Client: + """The shared client for an ate-env-api endpoint and bearer token.""" + key = (endpoint, token) + with _lock: + existing = _clients.get(key) + if existing is not None: + return existing + + async def make() -> Client: + return Client(endpoint, auth_token=token) + + made = run(make()) # the channel binds to the shared loop + with _lock: + return _clients.setdefault(key, made) diff --git a/integrations/openhands/src/ate_env_openhands/workspace.py b/integrations/openhands/src/ate_env_openhands/workspace.py new file mode 100644 index 00000000..c1a66ad6 --- /dev/null +++ b/integrations/openhands/src/ate_env_openhands/workspace.py @@ -0,0 +1,341 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""An OpenHands remote workspace on an ate-env environment. + +The environment runs an unmodified task image. A base template mounts the +OpenHands agent-server into it as a runtime layer, and the guest starts the +agent-server next to itself (see this package's README and ``layer/``). +ate-env-api's port proxy forwards ``/v1alpha/atespaces//envs//ports/

/...`` +to that port in the environment, so the proxy URL is the workspace host: the +SDK appends its ``/api/...`` paths to it, and ``RemoteConversation`` builds +its event WebSocket from it. + +Mirrors ``openhands.workspace.AgentSandboxWorkspace``: same lifecycle order +(create, health wait, parent init; clean up on any failure after create), +``pause()`` / ``resume()``, ``__exit__`` then ``cleanup()``, idempotent +``cleanup()``. The agent-sandbox warm pool maps to ``template``; the task +image, which agent-sandbox fixes in its SandboxTemplate, is a field here. +""" + +from __future__ import annotations + +import asyncio +import secrets +import shlex +import time +from typing import Any + +import httpx +from ate_env import NotFoundError +from openhands.sdk.logger import get_logger +from openhands.sdk.workspace import RemoteWorkspace +from pydantic import Field, PrivateAttr + +from . import _runtime + +logger = get_logger(__name__) + +DEFAULT_ATESPACE = "ate-env" + + +def _base_url(url: str) -> str: + url = url.rstrip("/") + return url if "://" in url else f"http://{url}" + + +def port_url(api_url: str, atespace: str, env_id: str, port: int) -> str: + """URL of ``port`` in an environment, through ate-env-api's port proxy.""" + return f"{_base_url(api_url)}/v1alpha/atespaces/{atespace}/envs/{env_id}/ports/{port}" + + +def _new_env_id() -> str: + return f"oh-{secrets.token_hex(6)}" + + +class AteEnvWorkspace(RemoteWorkspace): + """Remote workspace backed by an ate-env environment. + + Creates an environment from ``template`` (a base template whose runtime + layer carries the agent-server), with ``image`` as its root filesystem, + waits until the agent-server answers ``/health`` through ate-env-api, and + deletes the environment on exit. With ``env_id`` it attaches to an + existing environment instead and leaves it in place on exit. + + Example: + with AteEnvWorkspace( + template="oh-agent-server", + image="docker.io/library/python@sha256:...", + api_key=SESSION_KEY, + ) as workspace: + result = workspace.execute_command("python3 --version") + """ + + working_dir: str = Field( + default="/workspace", + description="Working directory in the environment. Created when missing. " + "Use /testbed for SWE-bench images.", + ) + host: str = Field( + default="", + description="Agent-server URL through the port proxy. Set by the workspace.", + ) + + # ate-env configuration + template: str | None = Field( + default=None, + description="Base template with the agent-server layer and sidecar. Required " + "unless env_id names an existing environment. Counterpart of agent-sandbox's " + "warmpool.", + ) + template_atespace: str | None = Field( + default=None, + description="Atespace of the template. Unset means the server default.", + ) + image: str | None = Field( + default=None, + description="Task image (repo@sha256:...) to run unmodified. Unset means the " + "template's own image.", + ) + atespace: str = Field( + default=DEFAULT_ATESPACE, + description="Atespace of the environment. Counterpart of agent-sandbox's namespace.", + ) + api_url: str = Field( + default="127.0.0.1:7777", + description="ate-env-api address, host:port or http(s) URL (gRPC and the port " + "proxy share its listener).", + ) + proxy_url: str | None = Field( + default=None, + description="Base URL of the port proxy when it differs from api_url.", + ) + api_token: str | None = Field( + default=None, + description="Bearer token for ate-env-api's gRPC API. Not sent through the " + "port proxy, which would forward it to the agent-server.", + ) + env_id: str | None = Field( + default=None, + description="Attach to this existing environment instead of creating one.", + ) + server_port: int = Field( + default=8000, + ge=1, + le=65535, + description="Port the agent-server listens on in the environment.", + ) + create_timeout: float = Field( + default=600.0, + gt=0.0, + description="Seconds to wait for CreateEnvironment (a new image pulls and " + "snapshots first).", + ) + health_check_timeout: float = Field( + default=120.0, + gt=0.0, + description="Seconds to wait for the agent-server's /health through the proxy.", + ) + delete_on_exit: bool | None = Field( + default=None, + description="Delete the environment in cleanup(). Unset means: delete it when " + "this workspace created it, keep it when attached with env_id.", + ) + + _created: bool = PrivateAttr(default=False) + _deleted: bool = PrivateAttr(default=False) + + def model_post_init(self, context: Any) -> None: + """Create or attach the environment, wait for the agent-server, initialize.""" + if self.env_id is None and not self.template: + raise ValueError("template is required to create an environment; " + "pass env_id to attach to an existing one") + if self.env_id is not None and self.image: + raise ValueError("image applies only when creating; it cannot change " + "on an existing environment") + if self.image and "@sha256:" not in self.image: + raise ValueError(f"image {self.image!r} must be pinned by digest " + "(repo@sha256:...); resolve a tag with " + "`crane digest --platform linux/amd64 `") + + if self.env_id is None: + self._create() + else: + _runtime.run(self._ate().get(self.env_id, atespace=self.atespace)) + logger.info("Attached to environment %s/%s", self.atespace, self.env_id) + + # Everything past the create must clean up on failure: the caller gets + # no object to close when the constructor raises. + try: + object.__setattr__(self, "host", self.port_url(self.server_port)) + self._wait_for_health(timeout=self.health_check_timeout) + super().model_post_init(context) + self._check_session_key() + self._ensure_working_dir() + self._check_versions() + logger.info("ate-env workspace is ready at %s", self.host) + except Exception: + self.cleanup() + raise + + def _ate(self): + return _runtime.client(self.api_url, self.api_token) + + def _create(self) -> None: + env_id = _new_env_id() + object.__setattr__(self, "env_id", env_id) + logger.info("Creating environment %s/%s from template %r%s", + self.atespace, env_id, self.template, + f" with image {self.image}" if self.image else "") + try: + _runtime.run(asyncio.wait_for( + self._ate().create( + env_id, + atespace=self.atespace, + template_name=self.template, + template_atespace=self.template_atespace, + rootfs_image=self.image, + ), + self.create_timeout, + )) + except BaseException: + # The server can finish a create whose call failed or timed out on + # this side; delete by name so it cannot leak. + self._created = True + self.cleanup() + raise + self._created = True + + def port_url(self, port: int) -> str: + """URL of another port in this environment, through the port proxy.""" + if self.env_id is None: + raise RuntimeError("no environment") + return port_url(self.proxy_url or self.api_url, self.atespace, self.env_id, port) + + def _wait_for_health(self, *, timeout: float) -> None: + """Poll the agent-server's /health through the proxy until it answers. + + The first request also wakes a suspended environment. A router 404 or + 503 and a refused connection are retried; the last answer is in the + error when the deadline passes. + """ + deadline = time.monotonic() + timeout + url = f"{self.host}/health" + headers = {"X-Session-API-Key": self.api_key} if self.api_key else {} + last = "no attempt" + while True: + remaining = deadline - time.monotonic() + if remaining <= 0: + break + try: + resp = httpx.get(url, headers=headers, timeout=min(30.0, max(remaining, 1.0))) + if 200 <= resp.status_code < 300: + return + last = f"HTTP {resp.status_code}: {resp.text.strip()[:200]}" + except httpx.HTTPError as exc: + last = f"{type(exc).__name__}: {exc}" + time.sleep(min(1.0, max(deadline - time.monotonic(), 0))) + raise RuntimeError(f"agent-server in {self.atespace}/{self.env_id} did not answer " + f"/health within {timeout}s; last: {last}") + + def _check_session_key(self) -> None: + """Fail at construction when the agent-server rejects api_key. + + /health answers without a key; the command API does not. The SDK turns + a rejected command into a failed result, not an error, thus check once + here. + """ + resp = self.client.get(f"{self.api_prefix}/bash/bash_events/search", params={"limit": 1}) + if resp.status_code in (401, 403): + raise PermissionError( + f"agent-server in {self.atespace}/{self.env_id} rejected the session key " + f"(HTTP {resp.status_code}); api_key must match the template's " + "OH_SESSION_API_KEYS_0") + resp.raise_for_status() + + def _ensure_working_dir(self) -> None: + result = self.execute_command(f"mkdir -p {shlex.quote(self.working_dir)}", timeout=30) + if result.exit_code != 0: + logger.warning("Could not create working_dir %s: %s", self.working_dir, + (result.stderr or result.stdout).strip()[:200]) + + def _check_versions(self) -> None: + """Warn when the SDK and the agent-server in the layer differ in version.""" + try: + from importlib.metadata import version + + local = version("openhands-sdk") + remote = str(self.get_server_info().get("version", "")) + except Exception as exc: # informational only + logger.debug("version check skipped: %s", exc) + return + if remote and remote != local: + logger.warning("openhands-sdk %s talks to agent-server %s; keep both at one " + "version (see the layer's requirements.in)", local, remote) + + def pause(self) -> None: + """Suspend the environment. Its filesystem is kept in the snapshot.""" + if self.env_id is None or self._deleted: + raise RuntimeError("Cannot pause: no active environment") + logger.info("Suspending environment %s/%s", self.atespace, self.env_id) + # Pooled connections tunnel to the actor that is going away. + self.reset_client() + _runtime.run(self._ate().suspend(self.env_id, atespace=self.atespace)) + + def resume(self) -> None: + """Wake the environment and wait for the agent-server. + + Any request wakes a suspended environment; this one makes the wait + explicit. The host does not change. + """ + if self.env_id is None or self._deleted: + raise RuntimeError("Cannot resume: no active environment") + logger.info("Resuming environment %s/%s", self.atespace, self.env_id) + self._wait_for_health(timeout=self.health_check_timeout) + self.reset_client() + + def __enter__(self) -> AteEnvWorkspace: + return self + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore[no-untyped-def] + """Send completion callbacks, then clean up.""" + try: + super().__exit__(exc_type, exc_val, exc_tb) + finally: + self.cleanup() + + def __del__(self) -> None: + try: + if getattr(self, "__pydantic_private__", None) is not None: + self.cleanup() + except Exception: + pass # never raise from __del__ + + def cleanup(self) -> None: + """Drop HTTP state and delete the environment when owned (idempotent).""" + try: + self.reset_client() + except Exception: + pass + delete = self.delete_on_exit if self.delete_on_exit is not None else self._created + if not delete or self._deleted or self.env_id is None: + return + self._deleted = True + try: + logger.info("Deleting environment %s/%s", self.atespace, self.env_id) + _runtime.run(self._ate().delete(self.env_id, atespace=self.atespace)) + except NotFoundError: + pass + except Exception as exc: + logger.warning("Error deleting environment %s/%s: %s", self.atespace, self.env_id, exc) diff --git a/integrations/openhands/tests/__init__.py b/integrations/openhands/tests/__init__.py new file mode 100644 index 00000000..58d482ea --- /dev/null +++ b/integrations/openhands/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/integrations/openhands/tests/conftest.py b/integrations/openhands/tests/conftest.py new file mode 100644 index 00000000..41eeb3fb --- /dev/null +++ b/integrations/openhands/tests/conftest.py @@ -0,0 +1,73 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A real agent-server on localhost for the whole session, behind the fakes.""" + +from __future__ import annotations + +import os +import socket +import subprocess +import sys +import time +import urllib.request + +import pytest + +from .fakes import FakeAteEnv, serve + +SESSION_KEY = "test-session-key" +IMAGE = "docker.io/library/python@sha256:" + "a" * 64 + + +def _free_port() -> int: + with socket.socket() as s: + s.bind(("127.0.0.1", 0)) + return s.getsockname()[1] + + +@pytest.fixture(scope="session") +def agent_server(tmp_path_factory): + """host:port of an openhands agent-server, as the layer runs it.""" + pytest.importorskip("openhands.agent_server", reason="needs the [test] extra") + port = _free_port() + home = tmp_path_factory.mktemp("agent-server") + env = dict(os.environ, OH_SESSION_API_KEYS_0=SESSION_KEY, OPENHANDS_SUPPRESS_BANNER="1") + proc = subprocess.Popen( + [sys.executable, "-m", "openhands.agent_server", "--host", "127.0.0.1", "--port", str(port)], + cwd=home, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, + ) + deadline = time.monotonic() + 60 + while True: + try: + with urllib.request.urlopen(f"http://127.0.0.1:{port}/health", timeout=1): + break + except OSError: + if proc.poll() is not None or time.monotonic() > deadline: + proc.kill() + pytest.fail("agent-server did not start") + time.sleep(0.3) + yield f"127.0.0.1:{port}" + proc.terminate() + try: + proc.wait(timeout=10) + except subprocess.TimeoutExpired: + proc.kill() + + +@pytest.fixture +def stack(agent_server): + """(api_url, state): fake ate-env-api and port proxy, agent-server on port 8000.""" + with serve(FakeAteEnv(upstreams={8000: agent_server})) as (api_url, state): + yield api_url, state diff --git a/integrations/openhands/tests/fakes.py b/integrations/openhands/tests/fakes.py new file mode 100644 index 00000000..9fa1464f --- /dev/null +++ b/integrations/openhands/tests/fakes.py @@ -0,0 +1,245 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Fakes for the workspace tests: ate-env-api's environment service over gRPC, +and its port proxy over HTTP in front of a real agent-server on localhost. + +The fake proxy keeps the router's behaviour that the workspace relies on: an +unknown environment is a 404, a suspended one is woken by the request, the +routing prefix is stripped, and HTTP upgrades are relayed both ways. +""" + +from __future__ import annotations + +import contextlib +import http.client +import re +import socket +import threading +from collections.abc import Iterator +from dataclasses import dataclass, field +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +import grpc +import grpc.aio +from ate_env import EnvironmentStatus +from ate_env._gen.ateenv.v1alpha import env_pb2, env_pb2_grpc +from ate_env.loop_runner import LoopRunner + +DEFAULT_ATESPACE = "ate-env" +DEFAULT_TEMPLATE = "default-template" + + +@dataclass +class FakeAteEnv: + """State shared by the fake services, plus the knobs tests script.""" + + envs: dict[str, dict] = field(default_factory=dict) + creates: list[dict] = field(default_factory=list) + deletes: list[str] = field(default_factory=list) + suspends: list[str] = field(default_factory=list) + create_code: grpc.StatusCode | None = None + create_but_fail: bool = False # record the env, then fail the call + upstreams: dict[int, str] = field(default_factory=dict) # port -> host:port + unhealthy_polls: int = 0 # answer this many /health requests with 503 first + wakes: int = 0 + proxied: list[dict] = field(default_factory=list) + lock: threading.Lock = field(default_factory=threading.Lock) + + def env_pb(self, env_id: str) -> env_pb2.Environment: + e = self.envs[env_id] + return env_pb2.Environment( + id=env_id, + atespace=e["atespace"], + template=env_pb2.Template(name=e["template"], atespace=e["template_atespace"]), + status=e["status"], + ) + + +class FakeEnvironmentService(env_pb2_grpc.EnvironmentServiceServicer): + def __init__(self, state: FakeAteEnv) -> None: + self.state = state + + async def CreateEnvironment(self, request, context): + s = self.state + s.creates.append({ + "id": request.id, + "atespace": request.atespace, + "template": request.template.name, + "template_atespace": request.template.atespace, + "rootfs_image": request.rootfs_image, + }) + if s.create_code is not None: + await context.abort(s.create_code, "create refused") + if request.id in s.envs: + await context.abort(grpc.StatusCode.ALREADY_EXISTS, "exists") + s.envs[request.id] = { + "atespace": request.atespace or DEFAULT_ATESPACE, + "template": request.template.name or DEFAULT_TEMPLATE, + "template_atespace": request.template.atespace or DEFAULT_ATESPACE, + "status": int(EnvironmentStatus.RUNNING), + } + if s.create_but_fail: + await context.abort(grpc.StatusCode.DEADLINE_EXCEEDED, "lost the answer") + return env_pb2.CreateEnvironmentResponse(environment=s.env_pb(request.id)) + + async def GetEnvironment(self, request, context): + if request.id not in self.state.envs: + await context.abort(grpc.StatusCode.NOT_FOUND, "no such env") + return env_pb2.GetEnvironmentResponse(environment=self.state.env_pb(request.id)) + + async def SuspendEnvironment(self, request, context): + if request.id not in self.state.envs: + await context.abort(grpc.StatusCode.NOT_FOUND, "no such env") + self.state.suspends.append(request.id) + self.state.envs[request.id]["status"] = int(EnvironmentStatus.SUSPENDED) + return env_pb2.SuspendEnvironmentResponse() + + async def DeleteEnvironment(self, request, context): + self.state.deletes.append(request.id) + if self.state.envs.pop(request.id, None) is None: + await context.abort(grpc.StatusCode.NOT_FOUND, "no such env") + return env_pb2.DeleteEnvironmentResponse() + + +_PATH = re.compile(r"^/v1alpha/(?:atespaces/(?P[^/]+)/)?envs/(?P[^/]+)/ports/(?P[0-9]+)(?P/.*)?$") +_HOP = {"connection", "keep-alive", "proxy-connection", "transfer-encoding", "upgrade", "host", "te", "trailer"} + + +def _relay(a: socket.socket, b: socket.socket) -> None: + try: + while data := a.recv(65536): + b.sendall(data) + except OSError: + pass + finally: + with contextlib.suppress(OSError): + b.shutdown(socket.SHUT_WR) + + +def _handler(state: FakeAteEnv): + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *args) -> None: + pass + + def _route(self): + path, _, query = self.path.partition("?") + m = _PATH.match(path) + if not m: + self._reply(404, "not a port-proxy path") + return None + env_id, atespace = m["id"], m["a"] or DEFAULT_ATESPACE + with state.lock: + env = state.envs.get(env_id) + if env is None or env["atespace"] != atespace: + self._reply(404, f"no actor {env_id}") + return None + if env["status"] == int(EnvironmentStatus.SUSPENDED): + env["status"] = int(EnvironmentStatus.RUNNING) + state.wakes += 1 + rest = (m["rest"] or "/") + (f"?{query}" if query else "") + if rest.startswith("/health") and state.unhealthy_polls > 0: + state.unhealthy_polls -= 1 + self._reply(503, f"actor {env_id} is not running") + return None + upstream = state.upstreams.get(int(m["port"])) + state.proxied.append({"method": self.command, "path": rest, "headers": dict(self.headers)}) + if upstream is None: + self._reply(502, "connection refused") + return None + return upstream, rest + + def _reply(self, code: int, text: str) -> None: + body = text.encode() + self.send_response(code) + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def _forward(self) -> None: + routed = self._route() + if routed is None: + return + upstream, rest = routed + if self.headers.get("Upgrade"): + self._upgrade(upstream, rest) + return + length = int(self.headers.get("Content-Length") or 0) + body = self.rfile.read(length) if length else None + headers = {k: v for k, v in self.headers.items() if k.lower() not in _HOP} + conn = http.client.HTTPConnection(upstream, timeout=120) + try: + conn.request(self.command, rest, body=body, headers=headers) + resp = conn.getresponse() + data = resp.read() + finally: + conn.close() + self.send_response(resp.status) + for k, v in resp.getheaders(): + if k.lower() not in _HOP and k.lower() != "content-length": + self.send_header(k, v) + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def _upgrade(self, upstream: str, rest: str) -> None: + host, port = upstream.rsplit(":", 1) + up = socket.create_connection((host, int(port)), timeout=30) + lines = [f"{self.command} {rest} HTTP/1.1", f"Host: {upstream}"] + lines += [f"{k}: {v}" for k, v in self.headers.items() if k.lower() != "host"] + up.sendall(("\r\n".join(lines) + "\r\n\r\n").encode()) + up.settimeout(None) + self.connection.settimeout(None) + t = threading.Thread(target=_relay, args=(up, self.connection), daemon=True) + t.start() + _relay(self.connection, up) + t.join(timeout=5) + up.close() + self.close_connection = True + + do_GET = do_POST = do_PUT = do_DELETE = do_PATCH = do_HEAD = _forward + + return Handler + + +@contextlib.contextmanager +def serve(state: FakeAteEnv | None = None) -> Iterator[tuple[str, FakeAteEnv]]: + """Run the fake API (gRPC) and port proxy (HTTP) on one loopback port each. + + Yields ``(api_url, state)``; the proxy URL is ``state.proxy_url``. + """ + state = state or FakeAteEnv() + runner = LoopRunner(name="fake-ate-env") + + async def start(): + server = grpc.aio.server() + env_pb2_grpc.add_EnvironmentServiceServicer_to_server(FakeEnvironmentService(state), server) + port = server.add_insecure_port("127.0.0.1:0") + await server.start() + return server, port + + server, grpc_port = runner.run(start()) + proxy = ThreadingHTTPServer(("127.0.0.1", 0), _handler(state)) + proxy.daemon_threads = True + threading.Thread(target=proxy.serve_forever, daemon=True).start() + state.proxy_url = f"http://127.0.0.1:{proxy.server_address[1]}" # type: ignore[attr-defined] + try: + yield f"127.0.0.1:{grpc_port}", state + finally: + proxy.shutdown() + proxy.server_close() + runner.run(server.stop(None)) + runner.stop() diff --git a/integrations/openhands/tests/test_e2e.py b/integrations/openhands/tests/test_e2e.py new file mode 100644 index 00000000..b1343b3f --- /dev/null +++ b/integrations/openhands/tests/test_e2e.py @@ -0,0 +1,61 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AteEnvWorkspace against a live ate-env-api. + +Skipped unless SUBSTRATE_E2E_API_URL points at a reachable ate-env-api with the +port proxy, for example after + + kubectl port-forward -n ate-env svc/ate-env-api 7777:7777 + export SUBSTRATE_E2E_API_URL=127.0.0.1:7777 + +and OH_E2E_TEMPLATE names a base template with the agent-server layer (README, +"Register the base template"), OH_E2E_SESSION_KEY is its session key, and +OH_E2E_IMAGE is optionally a digest-pinned task image. +""" + +from __future__ import annotations + +import os + +import pytest + +from ate_env_openhands import AteEnvWorkspace + +API_URL = os.environ.get("SUBSTRATE_E2E_API_URL") +TEMPLATE = os.environ.get("OH_E2E_TEMPLATE") +KEY = os.environ.get("OH_E2E_SESSION_KEY") + +pytestmark = pytest.mark.skipif( + not (API_URL and TEMPLATE and KEY), + reason="needs SUBSTRATE_E2E_API_URL, OH_E2E_TEMPLATE and OH_E2E_SESSION_KEY", +) + + +def test_create_run_suspend_resume_delete(): + with AteEnvWorkspace( + template=TEMPLATE, + image=os.environ.get("OH_E2E_IMAGE") or None, + api_url=API_URL, + api_key=KEY, + working_dir="/tmp/oh-e2e", + ) as ws: + env_id = ws.env_id + result = ws.execute_command("echo kept > /tmp/oh-e2e/marker && uname -s", timeout=60) + assert (result.exit_code, result.stdout.strip()) == (0, "Linux") + ws.pause() + ws.resume() + assert ws.execute_command("cat /tmp/oh-e2e/marker", timeout=60).stdout.strip() == "kept" + with pytest.raises(Exception): + AteEnvWorkspace(env_id=env_id, api_url=API_URL, api_key=KEY) diff --git a/integrations/openhands/tests/test_workspace.py b/integrations/openhands/tests/test_workspace.py new file mode 100644 index 00000000..3f76ac43 --- /dev/null +++ b/integrations/openhands/tests/test_workspace.py @@ -0,0 +1,225 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AteEnvWorkspace against a fake ate-env-api and port proxy, and a real agent-server.""" + +from __future__ import annotations + +import os + +import httpx +import pytest +from ate_env import NotFoundError +from openhands.sdk.workspace import RemoteWorkspace + +from ate_env_openhands import AteEnvWorkspace, port_url + +from .conftest import IMAGE, SESSION_KEY +from .fakes import DEFAULT_ATESPACE, FakeAteEnv, serve + + +def make(api_url, state, tmp_path, **kw) -> AteEnvWorkspace: + args = dict( + template="oh-agent-server", + image=IMAGE, + api_url=api_url, + proxy_url=state.proxy_url, + api_key=SESSION_KEY, + working_dir=str(tmp_path / "work"), + health_check_timeout=15, + ) + args.update(kw) + return AteEnvWorkspace(**args) + + +# -- validation, before any call --------------------------------------------- + + +def test_requires_template_or_env_id(): + with pytest.raises(ValueError, match="template is required"): + AteEnvWorkspace(api_url="127.0.0.1:1") + + +def test_rejects_an_image_tag(): + with pytest.raises(ValueError, match="pinned by digest"): + AteEnvWorkspace(template="t", image="python:3.12", api_url="127.0.0.1:1") + + +def test_rejects_an_image_when_attaching(): + with pytest.raises(ValueError, match="only when creating"): + AteEnvWorkspace(env_id="e1", image=IMAGE, api_url="127.0.0.1:1") + + +def test_is_a_remote_workspace(): + assert issubclass(AteEnvWorkspace, RemoteWorkspace) + for name in ("template", "image", "atespace", "env_id", "server_port", + "health_check_timeout", "delete_on_exit", "working_dir", "api_key"): + assert name in AteEnvWorkspace.model_fields + + +def test_port_url_always_names_the_atespace(): + assert port_url("ate-env-api.ate-env:7777", "ate-env", "e1", 8000) == ( + "http://ate-env-api.ate-env:7777/v1alpha/atespaces/ate-env/envs/e1/ports/8000") + assert port_url("https://env.example.com/", "team-a", "e1", 9100) == ( + "https://env.example.com/v1alpha/atespaces/team-a/envs/e1/ports/9100") + + +# -- lifecycle ------------------------------------------------------------------- + + +def test_creates_runs_and_deletes_on_exit(stack, tmp_path): + api_url, state = stack + with make(api_url, state, tmp_path, template_atespace="ate-env") as ws: + env_id = ws.env_id + assert state.creates == [{ + "id": env_id, "atespace": DEFAULT_ATESPACE, "template": "oh-agent-server", + "template_atespace": "ate-env", "rootfs_image": IMAGE, + }] + assert ws.host == port_url(state.proxy_url, DEFAULT_ATESPACE, env_id, 8000) + result = ws.execute_command("echo hello", timeout=30) + assert (result.exit_code, result.stdout.strip()) == (0, "hello") + assert os.path.isdir(tmp_path / "work"), "working_dir is created" + assert ws.alive + assert state.deletes == [env_id] + assert env_id not in state.envs + + +def test_files_round_trip(stack, tmp_path): + api_url, state = stack + with make(api_url, state, tmp_path) as ws: + remote = str(tmp_path / "work" / "data.bin") + up = ws.file_upload(b"\x00\x01payload", remote) + assert up.success, up.error + down = ws.file_download(remote, tmp_path / "out.bin") + assert down.success, down.error + assert (tmp_path / "out.bin").read_bytes() == b"\x00\x01payload" + + +def test_health_waits_through_a_booting_environment(stack, tmp_path): + api_url, state = stack + state.unhealthy_polls = 3 + with make(api_url, state, tmp_path) as ws: + assert ws.execute_command("true", timeout=30).exit_code == 0 + assert state.unhealthy_polls == 0, "the three 503s were waited out" + + +def test_health_timeout_deletes_the_environment(tmp_path): + with serve(FakeAteEnv()) as (api_url, state): # nothing listens on the port + with pytest.raises(RuntimeError, match="did not answer /health.*502"): + make(api_url, state, tmp_path, health_check_timeout=1.5) + assert len(state.creates) == 1 + assert state.deletes == [state.creates[0]["id"]] + assert state.envs == {} + + +def test_a_lost_create_answer_still_deletes_by_name(stack, tmp_path): + api_url, state = stack + state.create_but_fail = True + with pytest.raises(Exception, match="DEADLINE_EXCEEDED|lost the answer"): + make(api_url, state, tmp_path) + assert state.deletes == [state.creates[0]["id"]] + assert state.envs == {} + + +def test_a_wrong_session_key_fails_and_cleans_up(stack, tmp_path): + api_url, state = stack + with pytest.raises(PermissionError, match="rejected the session key"): + make(api_url, state, tmp_path, api_key="wrong") + assert state.deletes == [state.creates[0]["id"]] + + +def test_attach_neither_creates_nor_deletes(stack, tmp_path): + api_url, state = stack + owner = make(api_url, state, tmp_path, delete_on_exit=False) + owner.cleanup() + env_id = owner.env_id + assert env_id in state.envs and state.deletes == [] + with AteEnvWorkspace(env_id=env_id, api_url=api_url, proxy_url=state.proxy_url, + api_key=SESSION_KEY, working_dir=str(tmp_path)) as ws: + assert ws.execute_command("echo attached", timeout=30).stdout.strip() == "attached" + assert len(state.creates) == 1 + assert state.deletes == [] + assert env_id in state.envs + + +def test_attach_to_an_unknown_environment_raises(stack, tmp_path): + api_url, state = stack + with pytest.raises(NotFoundError): + AteEnvWorkspace(env_id="no-such-env", api_url=api_url, proxy_url=state.proxy_url) + assert state.creates == [] and state.deletes == [] + + +def test_pause_suspends_and_resume_wakes(stack, tmp_path): + api_url, state = stack + with make(api_url, state, tmp_path) as ws: + marker = tmp_path / "work" / "marker" + ws.execute_command(f"echo kept > {marker}", timeout=30) + ws.pause() + assert state.suspends == [ws.env_id] + assert state.envs[ws.env_id]["status"] != 0 + ws.resume() + assert state.wakes == 1 + assert ws.execute_command(f"cat {marker}", timeout=30).stdout.strip() == "kept" + + +def test_exit_cleans_up_even_when_the_parent_exit_raises(stack, tmp_path, monkeypatch): + api_url, state = stack + ws = make(api_url, state, tmp_path) + + def boom(self, *args): + raise RuntimeError("callback failed") + + monkeypatch.setattr(RemoteWorkspace, "__exit__", boom) + with pytest.raises(RuntimeError, match="callback failed"): + ws.__exit__(None, None, None) + assert state.deletes == [ws.env_id] + ws.cleanup() # idempotent + assert state.deletes == [ws.env_id] + + +def test_the_api_token_never_reaches_the_sandbox(stack, tmp_path): + api_url, state = stack + with make(api_url, state, tmp_path, api_token="api-secret") as ws: + ws.execute_command("true", timeout=30) + for request in state.proxied: + assert "api-secret" not in str(request["headers"]) + assert "authorization" not in {k.lower() for k in request["headers"]} + + +def test_port_url_reaches_another_port(stack, tmp_path): + api_url, state = stack + with make(api_url, state, tmp_path) as ws: + assert ws.port_url(9100) == port_url(state.proxy_url, DEFAULT_ATESPACE, ws.env_id, 9100) + assert httpx.get(f"{ws.port_url(9100)}/x").status_code == 502 # nothing listens + + +def test_a_conversation_runs_through_the_port_proxy(stack, tmp_path): + """RemoteConversation: REST calls and the event WebSocket through the path form.""" + pytest.importorskip("openhands.tools") + from openhands.sdk import LLM, Conversation, RemoteConversation + from openhands.tools.preset.default import get_default_agent + from pydantic import SecretStr + + api_url, state = stack + llm = LLM(usage_id="agent", model="openai/unused", api_key=SecretStr("x"), + base_url="http://127.0.0.1:9") # never called: the conversation is not run + with make(api_url, state, tmp_path) as ws: + conversation = Conversation(agent=get_default_agent(llm=llm, cli_mode=True), workspace=ws) + try: + assert isinstance(conversation, RemoteConversation) + assert conversation.state.id + finally: + conversation.close() + assert any(p["headers"].get("Upgrade", "").lower() == "websocket" for p in state.proxied), ( + "the event stream went through the proxy as a WebSocket") From 54c2d6de71cad8e3ecce7a6ec170fc49d2adac65 Mon Sep 17 00:00:00 2001 From: Tomer Glottman <83163454+tomergee@users.noreply.github.com> Date: Fri, 16 Oct 2026 16:01:47 -0700 Subject: [PATCH 13/13] integrations/openhands: move the example's layer and scripts into the package examples/openhands-agent-server becomes part of integrations/openhands: - layer/: the Dockerfile, lock.sh and the hash-locked dependency files, unchanged (the build context is now integrations/openhands/layer). - examples/: demo.sh, codeact_agent.py, scripted_llm.py, run_trajectory.py, oh_tool.py and the trajectories. The scripts attach an AteEnvWorkspace to an existing environment (_common.attach) instead of the removed ate_env_workspace.py, and run_trajectory.py reaches its tool runner with workspace.port_url(). - examples/convo_with_ate_env.py: OpenHands' agent-sandbox example 17 with the workspace line changed. - README: install, the layer, the base template (the counterpart of a SandboxTemplate and warm pool), AteEnvWorkspace's fields next to AgentSandboxWorkspace's, the migration diff, what is not mapped yet (TTL, labels, the agent-sandbox-rl fleet and R2E-Gym adapters), examples, versions, testing, the verification record and the limits. - RUNTIMES.md and the top-level README point at the package. --- README.md | 3 +- docs/rootfs-images/RUNTIMES.md | 6 +- .../ate_env_workspace.py | 80 ------- .../openhands}/README.md | 222 ++++++++++++------ integrations/openhands/examples/_common.py | 42 ++++ .../openhands/examples}/codeact_agent.py | 8 +- .../openhands/examples/convo_with_ate_env.py | 108 +++++++++ .../openhands/examples}/demo.sh | 4 +- .../openhands/examples}/oh_tool.py | 0 .../openhands/examples}/run_trajectory.py | 14 +- .../openhands/examples}/scripted_llm.py | 0 .../trajectories/django-11099-grade.json | 0 .../examples}/trajectories/django-11099.json | 0 .../examples}/trajectories/hello.json | 0 .../openhands/layer}/Dockerfile | 0 .../openhands/layer}/build-constraints.in | 0 .../openhands/layer}/build-constraints.txt | 0 .../openhands/layer}/lock.sh | 0 .../openhands/layer}/requirements.in | 0 .../openhands/layer}/requirements.txt | 0 20 files changed, 317 insertions(+), 170 deletions(-) delete mode 100644 examples/openhands-agent-server/ate_env_workspace.py rename {examples/openhands-agent-server => integrations/openhands}/README.md (57%) create mode 100644 integrations/openhands/examples/_common.py rename {examples/openhands-agent-server => integrations/openhands/examples}/codeact_agent.py (96%) create mode 100644 integrations/openhands/examples/convo_with_ate_env.py rename {examples/openhands-agent-server => integrations/openhands/examples}/demo.sh (98%) rename {examples/openhands-agent-server => integrations/openhands/examples}/oh_tool.py (100%) rename {examples/openhands-agent-server => integrations/openhands/examples}/run_trajectory.py (95%) rename {examples/openhands-agent-server => integrations/openhands/examples}/scripted_llm.py (100%) rename {examples/openhands-agent-server => integrations/openhands/examples}/trajectories/django-11099-grade.json (100%) rename {examples/openhands-agent-server => integrations/openhands/examples}/trajectories/django-11099.json (100%) rename {examples/openhands-agent-server => integrations/openhands/examples}/trajectories/hello.json (100%) rename {examples/openhands-agent-server => integrations/openhands/layer}/Dockerfile (100%) rename {examples/openhands-agent-server => integrations/openhands/layer}/build-constraints.in (100%) rename {examples/openhands-agent-server => integrations/openhands/layer}/build-constraints.txt (100%) rename {examples/openhands-agent-server => integrations/openhands/layer}/lock.sh (100%) rename {examples/openhands-agent-server => integrations/openhands/layer}/requirements.in (100%) rename {examples/openhands-agent-server => integrations/openhands/layer}/requirements.txt (100%) diff --git a/README.md b/README.md index cf18493e..90ce5e66 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ while this project adds the environment-shaped API on top. - **`clients/go`** — The Go client library to manage environments, run commands, and perform file operations. - **`clients/python`** — Python client for environment lifecycle management and batch sandbox orchestration ([README](clients/python/README.md)). - **`integrations/nemo-gym`** — A [NeMo Gym](https://github.com/NVIDIA-NeMo/Gym) sandbox provider that runs rollout sandboxes as environments, built on the Python client ([README](integrations/nemo-gym/README.md)). +- **`integrations/openhands`** — An [OpenHands](https://github.com/OpenHands/software-agent-sdk) workspace, `AteEnvWorkspace`, that runs the agent-server in any task image and mirrors OpenHands' `AgentSandboxWorkspace` ([README](integrations/openhands/README.md)). ## Installation @@ -412,7 +413,7 @@ For complete runnable Go programs: - **MCP**: See [mcp](examples/mcp/main.go) to connect to an environment's MCP endpoint, discover tools, and execute tool calls. - **Guest Daemon**: See [guest-daemon](examples/guest-daemon/main.go) to run a standalone in-actor gRPC service for asynchronous process execution and chunked file transfer. -For using environments from a framework, see [integrations/nemo-gym](integrations/nemo-gym/README.md): a NeMo Gym sandbox provider registered through the `nemo_gym.sandbox_providers` entry point. +For using environments from a framework, see [integrations/nemo-gym](integrations/nemo-gym/README.md), a NeMo Gym sandbox provider registered through the `nemo_gym.sandbox_providers` entry point, and [integrations/openhands](integrations/openhands/README.md), an OpenHands SDK workspace with the agent-server mounted into any task image. ## Cleanup diff --git a/docs/rootfs-images/RUNTIMES.md b/docs/rootfs-images/RUNTIMES.md index 96cdb818..b7287554 100644 --- a/docs/rootfs-images/RUNTIMES.md +++ b/docs/rootfs-images/RUNTIMES.md @@ -79,8 +79,10 @@ A verified stand-in for the mechanics, which needs nothing from any registry but Docker Hub, is in [`examples/runtime-layers`](../../examples/runtime-layers): the static `busybox:1.37-musl` mounted as the layer and its `httpd` as the sidecar, run under two different rootfs images. A real daemon, the OpenHands agent-server in -any image with glibc 2.31 or newer, configured by `--env` and driven by OpenHands' own CodeAct agent, is -in [`examples/openhands-agent-server`](../../examples/openhands-agent-server). +any image with glibc 2.31 or newer, configured by `--env`, is the +[`integrations/openhands`](../../integrations/openhands) package: an OpenHands SDK workspace +(`AteEnvWorkspace`, compatible with OpenHands' `AgentSandboxWorkspace`), the layer, and examples +that drive it with OpenHands' own CodeAct agent. ## Who can reach the second runtime diff --git a/examples/openhands-agent-server/ate_env_workspace.py b/examples/openhands-agent-server/ate_env_workspace.py deleted file mode 100644 index d542d80e..00000000 --- a/examples/openhands-agent-server/ate_env_workspace.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2026 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An OpenHands workspace for the agent-server inside an ate-env environment. - -ate-env-api forwards ``/v1alpha/envs//ports//...`` to that port in -the environment, prefix stripped. Used as the workspace host, that prefix is -all the OpenHands SDK needs: ``RemoteWorkspace`` appends its ``/api/...`` -paths to it, and ``RemoteConversation`` builds its event WebSocket from the -same host, so commands, files and the agent loop's events all reach the -agent-server through the Substrate router. The agent-server's own API key -travels as the SDK sends it. - - pip install openhands-sdk==1.55.0 - SESSION_KEY= python ate_env_workspace.py [command ...] - -Environment: ``API`` (ate-env-api address, default 127.0.0.1:7777), -``ATESPACE`` (default ate-env), ``PORT`` (agent-server port, default 8000), -``WORKDIR`` (the workspace's working directory: where commands run and what -relative paths resolve against; default ``/``). The image's WORKDIR does not -apply: the agent-server runs in ``/``, where the guest starts it, and the SDK -sends no directory unless given one, so set ``WORKDIR`` (``/testbed`` on a -SWE-bench image) or pass ``working_dir`` to ``workspace_for``. -""" - -from __future__ import annotations - -import os -import sys - -from openhands.sdk.workspace import RemoteWorkspace - - -def agent_server_url(env_id: str, *, api: str | None = None, atespace: str | None = None, - port: int | None = None) -> str: - """URL of the agent-server in ``env_id``, through ate-env-api.""" - api = api or os.environ.get("API", "127.0.0.1:7777") - atespace = atespace or os.environ.get("ATESPACE", "ate-env") - port = port or int(os.environ.get("PORT", "8000")) - base = api if "://" in api else f"http://{api}" - scope = "" if atespace == "ate-env" else f"/atespaces/{atespace}" - return f"{base}/v1alpha{scope}/envs/{env_id}/ports/{port}" - - -def workspace_for(env_id: str, *, api_key: str | None = None, working_dir: str | None = None, - **url_kwargs) -> RemoteWorkspace: - """A ``RemoteWorkspace`` bound to the agent-server in ``env_id``. - - ``working_dir`` defaults to ``$WORKDIR``, else ``/``. Pass it as ``cwd`` to - ``execute_command``, which otherwise runs in the agent-server's directory. - """ - return RemoteWorkspace( - host=agent_server_url(env_id, **url_kwargs), - api_key=api_key or os.environ["SESSION_KEY"], - working_dir=working_dir or os.environ.get("WORKDIR") or "/", - ) - - -def main() -> None: - if len(sys.argv) < 2: - sys.exit(f"usage: {sys.argv[0]} [command ...]") - workspace = workspace_for(sys.argv[1]) - command = " ".join(sys.argv[2:]) or 'head -1 /etc/os-release; echo "cwd: $(pwd)"' - result = workspace.execute_command(command, cwd=workspace.working_dir, timeout=60) - print(f"exit {result.exit_code}\n{result.stdout}{result.stderr}", end="") - - -if __name__ == "__main__": - main() diff --git a/examples/openhands-agent-server/README.md b/integrations/openhands/README.md similarity index 57% rename from examples/openhands-agent-server/README.md rename to integrations/openhands/README.md index ccbff427..36bd7ccd 100644 --- a/examples/openhands-agent-server/README.md +++ b/integrations/openhands/README.md @@ -1,36 +1,62 @@ -# Example: the OpenHands agent-server in any image - -Runs the [OpenHands](https://github.com/OpenHands/software-agent-sdk) agent-server inside an -unmodified image (a coding-task image, a language base image: any image with `/bin/sh` and glibc -2.31 or newer) and drives -it from outside the cluster: commands and files, OpenHands' own CodeAct agent, and CodeAct -trajectories executed step by step from a client: - -- the agent-server comes from a runtime layer mounted read-only at `/opt/oh` - ([docs/rootfs-images/RUNTIMES.md](../../docs/rootfs-images/RUNTIMES.md)); the image is not rebuilt; -- the guest starts it as a sidecar and holds the actor's readiness until its `/health` answers; -- callers reach it through ate-env-api's port proxy, by headers (`X-Env-Id`, `X-Env-Port`) or by - the path `/v1alpha/envs//ports//...`, plus the agent-server's own API key; -- the OpenHands SDK uses that path as its workspace URL, so `RemoteWorkspace` (commands, files) and - `RemoteConversation` (the agent loop and its event WebSocket) work without changes. - -| file | what it is | +# OpenHands on ate-env: `ate-env-openhands` + +Runs the [OpenHands](https://github.com/OpenHands/software-agent-sdk) agent-server inside any +unmodified task image (glibc 2.31 or newer) and connects the OpenHands SDK to it. +`AteEnvWorkspace` is an OpenHands `RemoteWorkspace` that mirrors OpenHands' own +`AgentSandboxWorkspace` (`openhands-workspace[agent-sandbox]`, kubernetes-sigs/agent-sandbox): +code written against that workspace moves to Agent Substrate by changing the constructor. + +```python +from ate_env_openhands import AteEnvWorkspace + +with AteEnvWorkspace( + template="oh-agent-server", # base template with the agent-server layer + image="docker.io/swebench/sweb.eval.x86_64.django_1776_django-11099@sha256:…", + api_key=SESSION_KEY, # the template's OH_SESSION_API_KEYS_0 + working_dir="/testbed", +) as workspace: + print(workspace.execute_command("git status").stdout) + conversation = Conversation(agent=agent, workspace=workspace) # RemoteConversation +``` + +How it fits together: + +- The agent-server comes from a runtime layer mounted read-only at `/opt/oh` + ([docs/rootfs-images/RUNTIMES.md](../../docs/rootfs-images/RUNTIMES.md)). The task image is not + rebuilt; agent-sandbox, by contrast, runs the stock agent-server image. +- The guest starts the agent-server as a sidecar and holds the actor's readiness until its + `/health` answers. +- The workspace reaches it through ate-env-api's port proxy, at + `/v1alpha/atespaces//envs//ports//...`, with the agent-server's own session + key. The SDK uses that URL as its workspace host, so commands, files and `RemoteConversation` + (the agent loop and its event WebSocket) work unchanged. No `kubectl port-forward`. + +| path | what it is | |---|---| -| [`Dockerfile`](Dockerfile) | the runtime layer: a relocatable Python with `openhands-agent-server` | -| [`requirements.txt`](requirements.txt), [`build-constraints.txt`](build-constraints.txt) | the layer's hash-locked dependencies, generated by [`lock.sh`](lock.sh) from the `.in` files | -| [`demo.sh`](demo.sh) | registers a base template and drives the agent-server in one environment per image | -| [`ate_env_workspace.py`](ate_env_workspace.py) | `workspace_for(env)`: an OpenHands `RemoteWorkspace` for an environment | -| [`codeact_agent.py`](codeact_agent.py) | runs OpenHands' CodeAct agent in an environment, with a model or `--scripted` | -| [`scripted_llm.py`](scripted_llm.py) | a stand-in model that replays a fixed CodeAct trajectory, for runs without a model | -| [`run_trajectory.py`](run_trajectory.py) | executes a CodeAct trajectory in an environment from outside and records the observations | -| [`oh_tool.py`](oh_tool.py) | serves OpenHands' `file_editor` inside the environment for `run_trajectory.py` | -| [`trajectories/`](trajectories) | `hello.json` for any image; `django-11099.json` and `-grade.json` for the SWE-bench `django__django-11099` image | - -## 1. Build the layer +| [`src/ate_env_openhands/`](src/ate_env_openhands) | `AteEnvWorkspace`, `port_url` | +| [`layer/`](layer) | the runtime layer: Dockerfile, hash-locked dependencies, `lock.sh` | +| [`examples/convo_with_ate_env.py`](examples/convo_with_ate_env.py) | OpenHands' agent-sandbox example 17 with the workspace line changed | +| [`examples/codeact_agent.py`](examples/codeact_agent.py) | OpenHands' CodeAct agent in an environment, with a model or `--scripted` | +| [`examples/run_trajectory.py`](examples/run_trajectory.py), [`oh_tool.py`](examples/oh_tool.py), [`trajectories/`](examples/trajectories) | executes a CodeAct trajectory from outside and records the observations | +| [`examples/demo.sh`](examples/demo.sh) | registers the base template and drives the agent-server over plain HTTP, one environment per image | +| [`tests/`](tests) | unit and integration tests: a fake ate-env-api and port proxy in front of a real agent-server | + +## 1. Install + +```bash +pip install ./clients/python ./integrations/openhands # the workspace +pip install './integrations/openhands[examples]' # + openhands-tools, for the agent examples +``` + +`ate-env-client` (this repo's Python client) is not on PyPI yet, thus install it from the checkout. +The package pins `openhands-sdk` to the agent-server version in the layer (see +[Versions](#versions)). Python 3.12 or newer, as openhands-sdk requires. + +## 2. Build the layer ```bash docker buildx build --platform linux/amd64,linux/arm64 \ - -t /oh-agent-server-layer:1.55.0 --push examples/openhands-agent-server + -t /oh-agent-server-layer:1.55.0 --push integrations/openhands/layer crane digest --platform linux/amd64 /oh-agent-server-layer:1.55.0 ``` @@ -53,18 +79,18 @@ must be glibc 2.31 or newer: Debian 11, Ubuntu 20.04, RHEL 9 and later work; Alp answers `/health` in about 3.5 s instead of 6.5 s (arm64, local Docker). The layer is about 250 MB compressed and 900 MB on disk. -The dependency set is hash-locked: [`requirements.txt`](requirements.txt) (the agent-server and +The dependency set is hash-locked: [`layer/requirements.txt`](layer/requirements.txt) (the agent-server and everything it pulls in, the same for linux/amd64 and linux/arm64) and -[`build-constraints.txt`](build-constraints.txt) (setuptools, to build the one source-only -dependency, func-timeout). Base images are pinned by digest in the Dockerfile. To move to another -agent-server release, change the versions in [`requirements.in`](requirements.in) (the agent-server, +[`layer/build-constraints.txt`](layer/build-constraints.txt) (setuptools, to build the one source-only +dependency, func-timeout). Base images are pinned by digest in the layer's Dockerfile. To move to another +agent-server release, change the versions in [`layer/requirements.in`](layer/requirements.in) (the agent-server, `openhands-sdk` and `openhands-tools`, which the agent-server does not pin itself), run -[`./lock.sh`](lock.sh) (needs uv; it re-resolves for both architectures and fails if they differ), +[`layer/lock.sh`](layer/lock.sh) (needs uv; it re-resolves for both architectures and fails if they differ), and build with `--build-arg AGENT_SERVER_VERSION=`; the build checks that the installed agent-server, sdk and tools are that version. Change the client's `openhands-sdk` to the same version (see [Versions](#versions)). -## 2. Register a base template +## 3. Register the base template ```bash ate-env manifest template --template oh-agent-server \ @@ -76,40 +102,70 @@ ate-env manifest template --template oh-agent-server \ --snapshots-bucket | kubectl-ate create actor-template -f - ``` -Further agent-server settings are more `--env` flags. Any image then runs with it: - -```bash -ate-env create my-env --template oh-agent-server --rootfs-image @sha256: +This is the counterpart of agent-sandbox's SandboxTemplate and warm pool. Further agent-server +settings are more `--env` flags. The first environment created from an image derives a template +for it (layer, sidecar and settings included); later ones reuse it. + +## 4. Use `AteEnvWorkspace` + +`AteEnvWorkspace(template=..., image=...)` creates an environment, waits until the agent-server +answers `/health` through the proxy, checks the session key, creates `working_dir`, and deletes the +environment on exit. `AteEnvWorkspace(env_id=...)` attaches to an existing environment and leaves +it in place. Any failure after the create deletes the environment before the error reaches the +caller. + +| Field | Default | Meaning | `AgentSandboxWorkspace` | +|---|---|---|---| +| `template` | required to create | base template with the agent-server layer | `warmpool` | +| `image` | the template's image | task image, `repo@sha256:…` | none (fixed in the SandboxTemplate) | +| `atespace` | `ate-env` | atespace of the environment | `namespace` | +| `api_url` | `127.0.0.1:7777` | ate-env-api, host:port or URL | `host` in `direct` mode | +| `proxy_url` | `api_url` | port proxy base URL, when it differs | none | +| `api_token` | none | bearer for ate-env-api's gRPC API; never sent through the proxy | none | +| `env_id` | generated | attach to this environment instead of creating one | none | +| `server_port` | `8000` | agent-server port | `server_port` | +| `working_dir` | `/workspace` (created) | where commands run; `/testbed` on SWE-bench images | `working_dir` | +| `api_key` | none | the agent-server's session key | `api_key` | +| `create_timeout` | `600` | seconds for the create (a new image pulls and snapshots) | `sandbox_ready_timeout` | +| `health_check_timeout` | `120` | seconds for `/health` | `health_check_timeout` | +| `delete_on_exit` | created: yes, attached: no | delete in `cleanup()` | always deletes the claim | + +- `pause()` suspends the environment; its filesystem stays in Substrate's snapshot. + `resume()` waits for `/health`, which also wakes it; any request wakes it as well. The host does + not change, unlike a port-forward. +- `port_url(port)` is the proxy URL of another port in the same environment. +- A rejected session key fails the constructor with `PermissionError`; a version difference between + `openhands-sdk` and the agent-server is logged as a warning. + +From `AgentSandboxWorkspace`: + +```diff +-from openhands.workspace import AgentSandboxWorkspace ++from ate_env_openhands import AteEnvWorkspace +-with AgentSandboxWorkspace(warmpool="openhands-pool", namespace="default", +- shutdown_after_seconds=30 * 60) as workspace: ++with AteEnvWorkspace(template="oh-agent-server", image=TASK_IMAGE, ++ api_url="ate-env-api.ate-env:7777", api_key=SESSION_KEY) as workspace: + agent = get_default_agent(llm=llm, cli_mode=True) + conversation = Conversation(agent=agent, workspace=workspace) ``` -The first create from an image derives a template for it (layer, sidecar and settings included) and -later creates reuse it. +Not mapped yet: `shutdown_after_seconds` (ate-env has no environment TTL), `labels`, +`pod_labels` and `pod_annotations` (environments have no labels), and agent-sandbox-rl's fleet +adapters (`make_handle_workspace`, `make_fleet_workspace`) and R2E-Gym adapter, which follow in +later changes. -## 3. Use it +## 5. Examples -Over HTTP, by path or by headers: - -```bash -curl -H 'X-Session-API-Key: ' -H 'Content-Type: application/json' \ - localhost:7777/v1alpha/envs/my-env/ports/8000/api/bash/execute_bash_command \ - -d '{"command": "uname -a"}' -``` - -With the OpenHands SDK (`pip install openhands-sdk==1.55.0 openhands-tools==1.55.0`): - -```python -from ate_env_workspace import workspace_for - -workspace = workspace_for("my-env", api_key="") # a RemoteWorkspace -print(workspace.execute_command("ls /").stdout) -``` +[`convo_with_ate_env.py`](examples/convo_with_ate_env.py) is OpenHands' agent-sandbox example 17 +with the workspace line changed: a command, a conversation, a pause and resume, a second message. The CodeAct agent, OpenHands' default agent (terminal, file editor, task tracker, finish) running on -the agent-server inside the environment: +the agent-server inside an existing environment: ```bash SESSION_KEY= LLM_MODEL= LLM_API_KEY= [LLM_BASE_URL=] \ - python examples/openhands-agent-server/codeact_agent.py my-env "Fix the failing test in /testbed" + python integrations/openhands/examples/codeact_agent.py my-env "Fix the failing test in /testbed" ``` The agent-server calls the model from inside the environment, so the model endpoint must be reachable @@ -143,13 +199,13 @@ environment calls a model: put a model call before each step and this is a clien the model wherever it is reachable. ```bash -SESSION_KEY= python examples/openhands-agent-server/run_trajectory.py \ - examples/openhands-agent-server/trajectories/django-11099.json --workdir /testbed --out run.json +SESSION_KEY= python integrations/openhands/examples/run_trajectory.py \ + integrations/openhands/examples/trajectories/django-11099.json --workdir /testbed --out run.json ``` - Working directory: a terminal step runs in its `cwd`, else in `--workdir` (default `$WORKDIR`, else `/`). The image's WORKDIR does not apply, because the agent-server runs in `/` where the guest - starts it; the same holds for `ate_env_workspace.py` and `codeact_agent.py` (`WORKDIR`). + starts it; the same holds for `AteEnvWorkspace`'s `working_dir` and the examples' `WORKDIR`. - Errors: a tool that reports an error (a non-zero exit status, a `file_editor` error) is an observation like any other and the run goes on. A step that cannot be executed (a malformed step, an unknown tool, an HTTP error, a timeout) is recorded with `"is_error": true, "failed": true`; the @@ -190,7 +246,8 @@ stashed (`FAILED (failures=2)`). ### The plain-HTTP demo -`demo.sh` does steps 2 and 3 over plain HTTP for a list of images: +`demo.sh` registers the base template and checks the agent-server over plain HTTP for a list of +images: ```bash kubectl -n ate-env port-forward deploy/ate-env-api 7777:7777 & @@ -198,7 +255,7 @@ GUEST_IMAGE=/ate-env-guest@sha256: \ SNAPSHOTS_BUCKET= \ LAYER_IMAGE=/oh-agent-server-layer@sha256: \ IMAGES="docker.io/library/ubuntu@sha256: docker.io/library/node@sha256:" \ -examples/openhands-agent-server/demo.sh +integrations/openhands/examples/demo.sh ``` It also takes `PORT`, `AGENT_SERVER_ENV` (extra `NAME=VALUE` settings), `COMMAND`, `WORKDIR` and @@ -213,18 +270,31 @@ their golden snapshots; with `REUSE=1` (and the same `SESSION_KEY`) it keeps the ## Versions -The client scripts talk to the agent-server's REST and WebSocket API through the OpenHands SDK, and -that API changes between releases, so the client's `openhands-sdk` (and `openhands-tools`, for -`codeact_agent.py`) must be the same version as the agent-server in the layer: 1.55.0 here. To change -both, set the new versions in `requirements.in`, run `./lock.sh`, rebuild and push the layer, register -the template with the new layer digest, and install the client with -`pip install openhands-sdk== openhands-tools==`. The `pip install` lines in the -scripts' docstrings name the version too. +The workspace talks to the agent-server's REST and WebSocket API through the OpenHands SDK, and that +API changes between releases, so the client's `openhands-sdk` (and `openhands-tools`, for the agent +examples) must be the same version as the agent-server in the layer: 1.55.0 here. To change both, +set the new versions in [`layer/requirements.in`](layer/requirements.in), run +[`layer/lock.sh`](layer/lock.sh), rebuild and push the layer, register the template with the new +layer digest, and change the pins in [`pyproject.toml`](pyproject.toml). + +## Testing + +```bash +pip install ./clients/python './integrations/openhands[test]' +python -m pytest integrations/openhands/tests -q +``` + +The tests run `AteEnvWorkspace` against an in-process fake of ate-env-api (environment lifecycle +over gRPC) and of its port proxy (path form, wake on request, upgrade relay), in front of a real +agent-server started on localhost, as the layer runs it. They cover the create arguments, the +health wait, cleanup after each failure (lost create answer, health timeout, wrong session key), +attach, pause and resume, exit ordering, files, and a `RemoteConversation` whose event WebSocket +goes through the proxy. ## Verified GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port proxy, agent-server -1.55.0 from this Dockerfile, 2026-10-15: +1.55.0 from this layer, 2026-10-15: | image | base | agent-server answering after create | |---|---|---| @@ -238,7 +308,7 @@ GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port pr without the key got 401; also with `PORT=3000` and an extra `--env` setting. - `codeact_agent.py --scripted` in the SWE-bench environment: the CodeAct run above, its event WebSocket through ate-env-api and the Substrate router. -- `ate_env_workspace.py` on `node:22-slim` saw the image's own `node v22`; on the SWE-bench image a +- The SDK workspace on `node:22-slim` saw the image's own `node v22`; on the SWE-bench image a command saw the image's Python 3.11.5, not the layer's 3.12. After a suspend, the next SDK call woke the environment and returned within 9 s. - On that cluster actors had no route to the internet (connection reset), hence `--scripted`. @@ -257,6 +327,10 @@ GKE with gVisor workers, Substrate from 2026-10-12, ate-env-api with the port pr ## Limits +- The port proxy has no caller authentication yet: whoever reaches ate-env-api reaches the + agent-server port and needs only the session key. `api_token` therefore goes to the gRPC API + only; the proxy would forward it to the sandbox. + - One API key per template: it is in the template's environment, readable by anyone who can read templates, and shared by the templates derived from it. - The agent-server runs as the image's user and keeps its state under that user's home. The first diff --git a/integrations/openhands/examples/_common.py b/integrations/openhands/examples/_common.py new file mode 100644 index 00000000..195373ad --- /dev/null +++ b/integrations/openhands/examples/_common.py @@ -0,0 +1,42 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Shared by the examples: a workspace attached to an existing environment. + +Environment: ``API`` (ate-env-api address, default 127.0.0.1:7777), +``ATESPACE`` (default ate-env), ``PORT`` (agent-server port, default 8000), +``SESSION_KEY`` (the template's ``OH_SESSION_API_KEYS_0``), ``WORKDIR`` (where +commands run and what relative paths resolve against; default ``/``). The +image's WORKDIR does not apply: the agent-server runs in ``/``, where the +guest starts it, and the SDK sends no directory unless given one, thus set +``WORKDIR=/testbed`` on a SWE-bench image. +""" + +from __future__ import annotations + +import os + +from ate_env_openhands import AteEnvWorkspace + + +def attach(env_id: str, *, working_dir: str | None = None, api_key: str | None = None) -> AteEnvWorkspace: + """An ``AteEnvWorkspace`` on an existing environment; it stays on exit.""" + return AteEnvWorkspace( + env_id=env_id, + api_url=os.environ.get("API", "127.0.0.1:7777"), + atespace=os.environ.get("ATESPACE", "ate-env"), + server_port=int(os.environ.get("PORT", "8000")), + api_key=api_key or os.environ["SESSION_KEY"], + working_dir=working_dir or os.environ.get("WORKDIR") or "/", + ) diff --git a/examples/openhands-agent-server/codeact_agent.py b/integrations/openhands/examples/codeact_agent.py similarity index 96% rename from examples/openhands-agent-server/codeact_agent.py rename to integrations/openhands/examples/codeact_agent.py index ae781a9d..fe70bae2 100644 --- a/examples/openhands-agent-server/codeact_agent.py +++ b/integrations/openhands/examples/codeact_agent.py @@ -32,12 +32,12 @@ environment and points the agent at it, which replays a fixed CodeAct trajectory through the real agent loop and tools. - pip install openhands-sdk==1.55.0 openhands-tools==1.55.0 + pip install ./clients/python './integrations/openhands[examples]' SESSION_KEY= LLM_MODEL= LLM_API_KEY= [LLM_BASE_URL=] \\ python codeact_agent.py ["task"] SESSION_KEY= python codeact_agent.py --scripted -Environment as for ate_env_workspace.py (API, ATESPACE, PORT, WORKDIR). +Environment as in _common.py (API, ATESPACE, PORT, SESSION_KEY, WORKDIR). """ from __future__ import annotations @@ -48,7 +48,7 @@ import time from pathlib import Path -from ate_env_workspace import workspace_for +from _common import attach from openhands.sdk import LLM, Conversation from openhands.sdk.event import ( ActionEvent, @@ -110,7 +110,7 @@ def main() -> None: parser.add_argument("--scripted", action="store_true", help="replay a fixed trajectory instead of calling a model") args = parser.parse_args() - workspace = workspace_for(args.env_id) + workspace = attach(args.env_id) if args.scripted: llm = LLM(model="openai/scripted", base_url=start_scripted_llm(workspace), api_key="scripted", native_tool_calling=True, usage_id="agent") diff --git a/integrations/openhands/examples/convo_with_ate_env.py b/integrations/openhands/examples/convo_with_ate_env.py new file mode 100644 index 00000000..2cd3e195 --- /dev/null +++ b/integrations/openhands/examples/convo_with_ate_env.py @@ -0,0 +1,108 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run a conversation whose agent-server lives in an ate-env environment. + +The ate-env counterpart of OpenHands' +examples/02_remote_agent_server/17_convo_with_agent_sandbox_server.py: the +workspace line changes, the rest is the same. The environment runs TASK_IMAGE +unmodified; the agent-server comes from the base template's runtime layer. + +Prerequisites (see ../README.md): + 1. ate-env-api reachable, for example + kubectl port-forward -n ate-env svc/ate-env-api 7777:7777 + 2. The base template with the agent-server layer, registered once + (README, "Register the base template"). + 3. pip install ./clients/python './integrations/openhands[examples]' + 4. export LLM_API_KEY=... (and optionally LLM_MODEL, LLM_BASE_URL) + export SESSION_KEY= + export TASK_IMAGE=@sha256: (optional; default: the template's image) + +The LLM is called from inside the environment (the agent runs on the +agent-server), thus Substrate's egress policy must allow the model endpoint. +""" + +import os +import time + +from openhands.sdk import ( + LLM, + Conversation, + RemoteConversation, + get_logger, +) +from openhands.tools.preset.default import get_default_agent +from pydantic import SecretStr + +from ate_env_openhands import AteEnvWorkspace + +logger = get_logger(__name__) + +# 1) LLM configuration +api_key = os.getenv("LLM_API_KEY") +assert api_key is not None, "LLM_API_KEY environment variable is not set." + +llm = LLM( + usage_id="agent", + model=os.getenv("LLM_MODEL", "gpt-5.5"), + base_url=os.getenv("LLM_BASE_URL"), + api_key=SecretStr(api_key), +) + +# 2) Create an environment from the task image. The agent-server answers once +# the environment is ready; a warm or snapshotted image starts in seconds. +template = os.getenv("ATE_ENV_TEMPLATE", "oh-agent-server") +logger.info(f"Creating an environment from template {template!r}...") +with AteEnvWorkspace( + template=template, + image=os.getenv("TASK_IMAGE"), + api_url=os.getenv("API", "127.0.0.1:7777"), + api_key=os.environ["SESSION_KEY"], +) as workspace: + # 3) Create the agent + agent = get_default_agent(llm=llm, cli_mode=True) + + # 4) Sanity-check the workspace with a direct command + result = workspace.execute_command("echo 'Hello from ate-env!' && pwd") + logger.info(f"Command exit code: {result.exit_code}") + logger.info(f"Output: {result.stdout}") + + # 5) Run a conversation + conversation = Conversation(agent=agent, workspace=workspace) + assert isinstance(conversation, RemoteConversation) + try: + logger.info(f"Conversation ID: {conversation.state.id}") + conversation.send_message( + "Read the current repo and write 3 facts about the project into FACTS.txt." + ) + conversation.run() + logger.info(f"Agent status: {conversation.state.execution_status}") + + # 6) Pause and resume. Substrate suspends the environment and keeps its + # filesystem in a snapshot; the next request resumes it. + logger.info("Suspending the environment...") + workspace.pause() + time.sleep(3) + logger.info("Resuming the environment...") + workspace.resume() + + conversation.send_message("Great! Now append a 4th fact to FACTS.txt.") + conversation.run() + logger.info("Second task completed after resume.") + + cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost + print(f"EXAMPLE_COST: {cost}") + finally: + print("\nCleaning up conversation...") + conversation.close() diff --git a/examples/openhands-agent-server/demo.sh b/integrations/openhands/examples/demo.sh similarity index 98% rename from examples/openhands-agent-server/demo.sh rename to integrations/openhands/examples/demo.sh index 259301a9..a4779373 100755 --- a/examples/openhands-agent-server/demo.sh +++ b/integrations/openhands/examples/demo.sh @@ -15,7 +15,7 @@ # The OpenHands agent-server attached to unmodified images, end to end. # -# The agent-server comes from a runtime layer (./Dockerfile) mounted read-only +# The agent-server comes from a runtime layer (../layer/Dockerfile) mounted read-only # at /opt/oh; the guest starts it as a sidecar and holds the actor's readiness # until it answers /health. Callers outside the cluster reach it through # ate-env-api's port proxy with three routing headers plus the agent-server's @@ -34,7 +34,7 @@ # Required: # GUEST_IMAGE digest-pinned ate-env-guest image (with the -sidecar flags) # SNAPSHOTS_BUCKET object-storage URL for actor snapshots -# LAYER_IMAGE digest-pinned layer image built from ./Dockerfile +# LAYER_IMAGE digest-pinned layer image built from ../layer/Dockerfile # IMAGES space-separated digest-pinned images to run the # agent-server in; any image with /bin/sh and glibc 2.31 # or newer diff --git a/examples/openhands-agent-server/oh_tool.py b/integrations/openhands/examples/oh_tool.py similarity index 100% rename from examples/openhands-agent-server/oh_tool.py rename to integrations/openhands/examples/oh_tool.py diff --git a/examples/openhands-agent-server/run_trajectory.py b/integrations/openhands/examples/run_trajectory.py similarity index 95% rename from examples/openhands-agent-server/run_trajectory.py rename to integrations/openhands/examples/run_trajectory.py index 3739b33f..f8fabcd5 100644 --- a/examples/openhands-agent-server/run_trajectory.py +++ b/integrations/openhands/examples/run_trajectory.py @@ -47,11 +47,11 @@ each step is a client-side agent: the model stays wherever it is reachable, the environment only executes. - pip install openhands-sdk==1.55.0 + pip install ./clients/python ./integrations/openhands SESSION_KEY= python run_trajectory.py trajectories/hello.json [--out run.json] Trajectory file: a JSON list of ``{"tool": ..., "args": {...}, "thought": ...}`` -(``thought`` optional). Environment as for ate_env_workspace.py. +(``thought`` optional). Environment as in _common.py. """ from __future__ import annotations @@ -66,7 +66,7 @@ import urllib.request from pathlib import Path -from ate_env_workspace import agent_server_url, workspace_for +from _common import attach LAYER_PYTHON = "/opt/oh/python/bin/python3" TOOL_RUNNER = "/tmp/oh_tool.py" @@ -80,10 +80,10 @@ class StepFailed(Exception): class ToolRunner: """oh_tool.py inside the environment, reached through ate-env-api.""" - def __init__(self, workspace, env_id: str, key: str) -> None: + def __init__(self, workspace, key: str) -> None: self.workspace = workspace self.key = key - self.url = agent_server_url(env_id, port=TOOL_PORT) + self.url = workspace.port_url(TOOL_PORT) def request(self, path: str, payload: dict | None = None, timeout: float = 120) -> dict: data = None if payload is None else json.dumps(payload).encode() @@ -176,8 +176,8 @@ def main() -> int: if not isinstance(steps, list): sys.exit(f"{args.trajectory}: want a JSON list of steps") key = os.environ["SESSION_KEY"] - workspace = workspace_for(args.env_id, api_key=key, working_dir=args.workdir) - tools = ToolRunner(workspace, args.env_id, key) + workspace = attach(args.env_id, api_key=key, working_dir=args.workdir) + tools = ToolRunner(workspace, key) results: list[dict] = [] failed = 0 try: diff --git a/examples/openhands-agent-server/scripted_llm.py b/integrations/openhands/examples/scripted_llm.py similarity index 100% rename from examples/openhands-agent-server/scripted_llm.py rename to integrations/openhands/examples/scripted_llm.py diff --git a/examples/openhands-agent-server/trajectories/django-11099-grade.json b/integrations/openhands/examples/trajectories/django-11099-grade.json similarity index 100% rename from examples/openhands-agent-server/trajectories/django-11099-grade.json rename to integrations/openhands/examples/trajectories/django-11099-grade.json diff --git a/examples/openhands-agent-server/trajectories/django-11099.json b/integrations/openhands/examples/trajectories/django-11099.json similarity index 100% rename from examples/openhands-agent-server/trajectories/django-11099.json rename to integrations/openhands/examples/trajectories/django-11099.json diff --git a/examples/openhands-agent-server/trajectories/hello.json b/integrations/openhands/examples/trajectories/hello.json similarity index 100% rename from examples/openhands-agent-server/trajectories/hello.json rename to integrations/openhands/examples/trajectories/hello.json diff --git a/examples/openhands-agent-server/Dockerfile b/integrations/openhands/layer/Dockerfile similarity index 100% rename from examples/openhands-agent-server/Dockerfile rename to integrations/openhands/layer/Dockerfile diff --git a/examples/openhands-agent-server/build-constraints.in b/integrations/openhands/layer/build-constraints.in similarity index 100% rename from examples/openhands-agent-server/build-constraints.in rename to integrations/openhands/layer/build-constraints.in diff --git a/examples/openhands-agent-server/build-constraints.txt b/integrations/openhands/layer/build-constraints.txt similarity index 100% rename from examples/openhands-agent-server/build-constraints.txt rename to integrations/openhands/layer/build-constraints.txt diff --git a/examples/openhands-agent-server/lock.sh b/integrations/openhands/layer/lock.sh similarity index 100% rename from examples/openhands-agent-server/lock.sh rename to integrations/openhands/layer/lock.sh diff --git a/examples/openhands-agent-server/requirements.in b/integrations/openhands/layer/requirements.in similarity index 100% rename from examples/openhands-agent-server/requirements.in rename to integrations/openhands/layer/requirements.in diff --git a/examples/openhands-agent-server/requirements.txt b/integrations/openhands/layer/requirements.txt similarity index 100% rename from examples/openhands-agent-server/requirements.txt rename to integrations/openhands/layer/requirements.txt