From 6a69210508d5c1371988e131cc119621b8111f5c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 6 Sep 2026 23:16:05 +0000 Subject: [PATCH 1/5] feat: add Meta Computer Use agent Expose sync and async Muse Spark 1.1 computer-use managers with TypedDict and legacy request models. Co-authored-by: nikhil --- CLAUDE.md | 2 +- .../managers/async_manager/agents/__init__.py | 2 + .../async_manager/agents/meta_computer_use.py | 84 ++++++++++ .../managers/sync_manager/agents/__init__.py | 2 + .../sync_manager/agents/meta_computer_use.py | 84 ++++++++++ hyperbrowser/models/__init__.py | 22 +++ .../models/agents/meta_computer_use.py | 152 ++++++++++++++++++ hyperbrowser/models/consts.py | 8 + hyperbrowser/types/__init__.py | 4 + hyperbrowser/types/agents.py | 26 +++ tests/test_meta_computer_use_models.py | 37 +++++ tests/test_request_compat.py | 34 ++++ tests/typecheck/valid_requests.py | 16 ++ 13 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 hyperbrowser/client/managers/async_manager/agents/meta_computer_use.py create mode 100644 hyperbrowser/client/managers/sync_manager/agents/meta_computer_use.py create mode 100644 hyperbrowser/models/agents/meta_computer_use.py create mode 100644 tests/test_meta_computer_use_models.py diff --git a/CLAUDE.md b/CLAUDE.md index 013f13e7..1a8af886 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,7 +47,7 @@ client.crawl # Website crawling jobs client.extract # Data extraction jobs client.web # Web fetch/search operations client.web.batch_fetch # Batch web fetch operations -client.agents # AI agent integrations (browser_use, cua, claude_computer_use, hyper_agent, gemini_computer_use) +client.agents # AI agent integrations (browser_use, cua, claude_computer_use, hyper_agent, gemini_computer_use, grok_computer_use, meta_computer_use) client.profiles # Browser profile management client.extensions # Browser extension management client.team # Team/credit info diff --git a/hyperbrowser/client/managers/async_manager/agents/__init__.py b/hyperbrowser/client/managers/async_manager/agents/__init__.py index d73f92ac..601993bd 100644 --- a/hyperbrowser/client/managers/async_manager/agents/__init__.py +++ b/hyperbrowser/client/managers/async_manager/agents/__init__.py @@ -4,6 +4,7 @@ from .hyper_agent import HyperAgentManager from .gemini_computer_use import GeminiComputerUseManager from .grok_computer_use import GrokComputerUseManager +from .meta_computer_use import MetaComputerUseManager class Agents: @@ -14,3 +15,4 @@ def __init__(self, client): self.hyper_agent = HyperAgentManager(client) self.gemini_computer_use = GeminiComputerUseManager(client) self.grok_computer_use = GrokComputerUseManager(client) + self.meta_computer_use = MetaComputerUseManager(client) diff --git a/hyperbrowser/client/managers/async_manager/agents/meta_computer_use.py b/hyperbrowser/client/managers/async_manager/agents/meta_computer_use.py new file mode 100644 index 00000000..01084dd8 --- /dev/null +++ b/hyperbrowser/client/managers/async_manager/agents/meta_computer_use.py @@ -0,0 +1,84 @@ +import asyncio +from typing import Union + +from hyperbrowser.client._request import dump_request +from hyperbrowser.exceptions import HyperbrowserError +from hyperbrowser.types import ( + StartMetaComputerUseTaskParams as StartMetaComputerUseTaskParamsDict, +) + +from .....models import ( + POLLING_ATTEMPTS, + BasicResponse, + MetaComputerUseTaskResponse, + MetaComputerUseTaskStatusResponse, + StartMetaComputerUseTaskParams, + StartMetaComputerUseTaskResponse, +) + + +class MetaComputerUseManager: + def __init__(self, client): + self._client = client + + async def start( + self, + params: Union[ + StartMetaComputerUseTaskParamsDict, + StartMetaComputerUseTaskParams, + ], + ) -> StartMetaComputerUseTaskResponse: + response = await self._client.transport.post( + self._client._build_url("/task/meta-computer-use"), + data=dump_request(params, StartMetaComputerUseTaskParams), + ) + return StartMetaComputerUseTaskResponse(**response.data) + + async def get(self, job_id: str) -> MetaComputerUseTaskResponse: + response = await self._client.transport.get( + self._client._build_url(f"/task/meta-computer-use/{job_id}") + ) + return MetaComputerUseTaskResponse(**response.data) + + async def get_status(self, job_id: str) -> MetaComputerUseTaskStatusResponse: + response = await self._client.transport.get( + self._client._build_url(f"/task/meta-computer-use/{job_id}/status") + ) + return MetaComputerUseTaskStatusResponse(**response.data) + + async def stop(self, job_id: str) -> BasicResponse: + response = await self._client.transport.put( + self._client._build_url(f"/task/meta-computer-use/{job_id}/stop") + ) + return BasicResponse(**response.data) + + async def start_and_wait( + self, + params: Union[ + StartMetaComputerUseTaskParamsDict, + StartMetaComputerUseTaskParams, + ], + ) -> MetaComputerUseTaskResponse: + job_start_resp = await self.start(params) + job_id = job_start_resp.job_id + if not job_id: + raise HyperbrowserError("Failed to start Meta Computer Use task job") + + failures = 0 + while True: + try: + job_response = await self.get_status(job_id) + if ( + job_response.status == "completed" + or job_response.status == "failed" + or job_response.status == "stopped" + ): + return await self.get(job_id) + failures = 0 + except Exception as e: + failures += 1 + if failures >= POLLING_ATTEMPTS: + raise HyperbrowserError( + f"Failed to poll Meta Computer Use task job {job_id} after {POLLING_ATTEMPTS} attempts: {e}" + ) + await asyncio.sleep(2) diff --git a/hyperbrowser/client/managers/sync_manager/agents/__init__.py b/hyperbrowser/client/managers/sync_manager/agents/__init__.py index d73f92ac..601993bd 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/__init__.py +++ b/hyperbrowser/client/managers/sync_manager/agents/__init__.py @@ -4,6 +4,7 @@ from .hyper_agent import HyperAgentManager from .gemini_computer_use import GeminiComputerUseManager from .grok_computer_use import GrokComputerUseManager +from .meta_computer_use import MetaComputerUseManager class Agents: @@ -14,3 +15,4 @@ def __init__(self, client): self.hyper_agent = HyperAgentManager(client) self.gemini_computer_use = GeminiComputerUseManager(client) self.grok_computer_use = GrokComputerUseManager(client) + self.meta_computer_use = MetaComputerUseManager(client) diff --git a/hyperbrowser/client/managers/sync_manager/agents/meta_computer_use.py b/hyperbrowser/client/managers/sync_manager/agents/meta_computer_use.py new file mode 100644 index 00000000..d9996a49 --- /dev/null +++ b/hyperbrowser/client/managers/sync_manager/agents/meta_computer_use.py @@ -0,0 +1,84 @@ +import time +from typing import Union + +from hyperbrowser.client._request import dump_request +from hyperbrowser.exceptions import HyperbrowserError +from hyperbrowser.types import ( + StartMetaComputerUseTaskParams as StartMetaComputerUseTaskParamsDict, +) + +from .....models import ( + POLLING_ATTEMPTS, + BasicResponse, + MetaComputerUseTaskResponse, + MetaComputerUseTaskStatusResponse, + StartMetaComputerUseTaskParams, + StartMetaComputerUseTaskResponse, +) + + +class MetaComputerUseManager: + def __init__(self, client): + self._client = client + + def start( + self, + params: Union[ + StartMetaComputerUseTaskParamsDict, + StartMetaComputerUseTaskParams, + ], + ) -> StartMetaComputerUseTaskResponse: + response = self._client.transport.post( + self._client._build_url("/task/meta-computer-use"), + data=dump_request(params, StartMetaComputerUseTaskParams), + ) + return StartMetaComputerUseTaskResponse(**response.data) + + def get(self, job_id: str) -> MetaComputerUseTaskResponse: + response = self._client.transport.get( + self._client._build_url(f"/task/meta-computer-use/{job_id}") + ) + return MetaComputerUseTaskResponse(**response.data) + + def get_status(self, job_id: str) -> MetaComputerUseTaskStatusResponse: + response = self._client.transport.get( + self._client._build_url(f"/task/meta-computer-use/{job_id}/status") + ) + return MetaComputerUseTaskStatusResponse(**response.data) + + def stop(self, job_id: str) -> BasicResponse: + response = self._client.transport.put( + self._client._build_url(f"/task/meta-computer-use/{job_id}/stop") + ) + return BasicResponse(**response.data) + + def start_and_wait( + self, + params: Union[ + StartMetaComputerUseTaskParamsDict, + StartMetaComputerUseTaskParams, + ], + ) -> MetaComputerUseTaskResponse: + job_start_resp = self.start(params) + job_id = job_start_resp.job_id + if not job_id: + raise HyperbrowserError("Failed to start Meta Computer Use task job") + + failures = 0 + while True: + try: + job_response = self.get_status(job_id) + if ( + job_response.status == "completed" + or job_response.status == "failed" + or job_response.status == "stopped" + ): + return self.get(job_id) + failures = 0 + except Exception as e: + failures += 1 + if failures >= POLLING_ATTEMPTS: + raise HyperbrowserError( + f"Failed to poll Meta Computer Use task job {job_id} after {POLLING_ATTEMPTS} attempts: {e}" + ) + time.sleep(2) diff --git a/hyperbrowser/models/__init__.py b/hyperbrowser/models/__init__.py index d51bb155..0de0fc1b 100644 --- a/hyperbrowser/models/__init__.py +++ b/hyperbrowser/models/__init__.py @@ -127,6 +127,16 @@ StartGrokComputerUseTaskResponse, GrokComputerUseApiKeys, ) +from .agents.meta_computer_use import ( + MetaComputerUseTaskStatus, + MetaComputerUseStepResponse, + MetaComputerUseTaskData, + MetaComputerUseTaskResponse, + MetaComputerUseTaskStatusResponse, + StartMetaComputerUseTaskParams, + StartMetaComputerUseTaskResponse, + MetaComputerUseApiKeys, +) from .agents.cua import ( CuaTaskData, CuaTaskResponse, @@ -160,6 +170,8 @@ CuaLlm, GrokComputerUseLlm, GrokReasoningEffort, + MetaComputerUseLlm, + MetaReasoningEffort, Country, DownloadsStatus, FetchScreenshotFormat, @@ -496,6 +508,13 @@ "GrokComputerUseTaskStatusResponse", "StartGrokComputerUseTaskParams", "StartGrokComputerUseTaskResponse", + "MetaComputerUseTaskStatus", + "MetaComputerUseStepResponse", + "MetaComputerUseTaskData", + "MetaComputerUseTaskResponse", + "MetaComputerUseTaskStatusResponse", + "StartMetaComputerUseTaskParams", + "StartMetaComputerUseTaskResponse", "CuaTaskStatus", "CuaTaskData", "CuaTaskResponse", @@ -506,11 +525,14 @@ "ClaudeComputerUseApiKeys", "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", + "MetaComputerUseApiKeys", "CuaApiKeys", "CuaBaseUrls", "HyperAgentApiKeys", "GrokComputerUseLlm", "GrokReasoningEffort", + "MetaComputerUseLlm", + "MetaReasoningEffort", "HyperAgentOutputV110", "HyperAgentStepV110", # crawl diff --git a/hyperbrowser/models/agents/meta_computer_use.py b/hyperbrowser/models/agents/meta_computer_use.py new file mode 100644 index 00000000..b3821a58 --- /dev/null +++ b/hyperbrowser/models/agents/meta_computer_use.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +from typing import Any, List, Literal, Optional + +from pydantic import BaseModel, ConfigDict, Field + +from .cua import CuaStepIncompleteDetails, CuaStepReasoning, CuaStepResponseError +from ..session import CreateSessionParams +from ..consts import MetaComputerUseLlm, MetaReasoningEffort + +MetaComputerUseTaskStatus = Literal[ + "pending", "running", "completed", "failed", "stopped" +] + + +class MetaComputerUseApiKeys(BaseModel): + """ + API keys for the Meta Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + meta: Optional[str] = Field(default=None, serialization_alias="meta") + + +class StartMetaComputerUseTaskParams(BaseModel): + """ + Parameters for creating a new Meta Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + task: str + llm: Optional[MetaComputerUseLlm] = Field(default=None, serialization_alias="llm") + reasoning_effort: Optional[MetaReasoningEffort] = Field( + default=None, serialization_alias="reasoningEffort" + ) + session_id: Optional[str] = Field(default=None, serialization_alias="sessionId") + max_failures: Optional[int] = Field(default=None, serialization_alias="maxFailures") + max_steps: Optional[int] = Field(default=None, serialization_alias="maxSteps") + keep_browser_open: Optional[bool] = Field( + default=None, serialization_alias="keepBrowserOpen" + ) + session_options: Optional[CreateSessionParams] = Field( + default=None, serialization_alias="sessionOptions" + ) + use_custom_api_keys: Optional[bool] = Field( + default=None, serialization_alias="useCustomApiKeys" + ) + api_keys: Optional[MetaComputerUseApiKeys] = Field( + default=None, serialization_alias="apiKeys" + ) + use_computer_action: Optional[bool] = Field( + default=None, serialization_alias="useComputerAction" + ) + + +class StartMetaComputerUseTaskResponse(BaseModel): + """ + Response from starting a Meta Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + job_id: str = Field(alias="jobId") + live_url: Optional[str] = Field(default=None, alias="liveUrl") + + +class MetaComputerUseTaskStatusResponse(BaseModel): + """ + Response from getting a Meta Computer Use task status. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + status: MetaComputerUseTaskStatus + + +class MetaComputerUseStepResponse(BaseModel): + """ + Response from a single Meta Computer Use step. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + created_at: Optional[int] = Field(default=None, serialization_alias="created_at") + completed_at: Optional[int] = Field( + default=None, serialization_alias="completed_at" + ) + output_text: Optional[str] = Field(default=None, serialization_alias="output_text") + error: Optional[CuaStepResponseError] = Field( + default=None, serialization_alias="error" + ) + incomplete_details: Optional[CuaStepIncompleteDetails] = Field( + default=None, serialization_alias="incomplete_details" + ) + model: Optional[str] = Field(default=None, serialization_alias="model") + output: Optional[List[Any]] = Field(default=None, serialization_alias="output") + reasoning: Optional[CuaStepReasoning] = Field( + default=None, serialization_alias="reasoning" + ) + status: Optional[str] = Field(default=None, serialization_alias="status") + + +class MetaComputerUseTaskData(BaseModel): + model_config = ConfigDict( + populate_by_alias=True, + ) + + steps: List[MetaComputerUseStepResponse] + final_result: Optional[str] = Field(default=None, alias="finalResult") + + +class MetaComputerUseTaskMetadata(BaseModel): + model_config = ConfigDict( + populate_by_alias=True, + ) + + input_tokens: Optional[int] = Field(default=None, alias="inputTokens") + output_tokens: Optional[int] = Field(default=None, alias="outputTokens") + num_task_steps_completed: Optional[int] = Field( + default=None, alias="numTaskStepsCompleted" + ) + + +class MetaComputerUseTaskResponse(BaseModel): + """ + Response from a Meta Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + job_id: str = Field(alias="jobId") + status: MetaComputerUseTaskStatus + metadata: Optional[MetaComputerUseTaskMetadata] = Field( + default=None, alias="metadata" + ) + data: Optional[MetaComputerUseTaskData] = Field(default=None, alias="data") + error: Optional[str] = Field(default=None, alias="error") + live_url: Optional[str] = Field(default=None, alias="liveUrl") diff --git a/hyperbrowser/models/consts.py b/hyperbrowser/models/consts.py index 85269f8e..4f750ab3 100644 --- a/hyperbrowser/models/consts.py +++ b/hyperbrowser/models/consts.py @@ -106,6 +106,14 @@ "medium", "high", ] +MetaComputerUseLlm = Literal["muse-spark-1.1",] +MetaReasoningEffort = Literal[ + "minimal", + "low", + "medium", + "high", + "xhigh", +] SessionRegion = Literal[ "us", "us-central", diff --git a/hyperbrowser/types/__init__.py b/hyperbrowser/types/__init__.py index 4620519e..f0ebdfc8 100644 --- a/hyperbrowser/types/__init__.py +++ b/hyperbrowser/types/__init__.py @@ -20,12 +20,14 @@ GeminiComputerUseApiKeys, GrokComputerUseApiKeys, HyperAgentApiKeys, + MetaComputerUseApiKeys, StartBrowserUseTaskParams, StartClaudeComputerUseTaskParams, StartCuaTaskParams, StartGeminiComputerUseTaskParams, StartGrokComputerUseTaskParams, StartHyperAgentTaskParams, + StartMetaComputerUseTaskParams, ) from .computer_action import ( ClickActionParams, @@ -184,12 +186,14 @@ "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", "HyperAgentApiKeys", + "MetaComputerUseApiKeys", "StartBrowserUseTaskParams", "StartClaudeComputerUseTaskParams", "StartCuaTaskParams", "StartGeminiComputerUseTaskParams", "StartGrokComputerUseTaskParams", "StartHyperAgentTaskParams", + "StartMetaComputerUseTaskParams", "ClickActionParams", "ComputerActionMouseButton", "ComputerActionParams", diff --git a/hyperbrowser/types/agents.py b/hyperbrowser/types/agents.py index 56fa9a31..63ca110f 100644 --- a/hyperbrowser/types/agents.py +++ b/hyperbrowser/types/agents.py @@ -10,6 +10,8 @@ GeminiComputerUseLlm, GrokComputerUseLlm, GrokReasoningEffort, + MetaComputerUseLlm, + MetaReasoningEffort, HyperAgentLlm, HyperAgentVersion, ) @@ -117,6 +119,28 @@ class StartGrokComputerUseTaskParams(TypedDict, total=False): use_computer_action: Optional[bool] +class MetaComputerUseApiKeys(TypedDict, total=False): + """Provider API keys for a Meta Computer Use task.""" + + meta: Optional[str] + + +class StartMetaComputerUseTaskParams(TypedDict, total=False): + """Parameters for starting a Meta Computer Use task.""" + + task: Required[str] + llm: Optional[MetaComputerUseLlm] + reasoning_effort: Optional[MetaReasoningEffort] + session_id: Optional[str] + max_failures: Optional[int] + max_steps: Optional[int] + keep_browser_open: Optional[bool] + session_options: Optional[CreateSessionParams] + use_custom_api_keys: Optional[bool] + api_keys: Optional[MetaComputerUseApiKeys] + use_computer_action: Optional[bool] + + class CuaApiKeys(TypedDict, total=False): """Provider API keys for an OpenAI CUA task.""" @@ -176,10 +200,12 @@ class StartHyperAgentTaskParams(TypedDict, total=False): "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", "HyperAgentApiKeys", + "MetaComputerUseApiKeys", "StartBrowserUseTaskParams", "StartClaudeComputerUseTaskParams", "StartCuaTaskParams", "StartGeminiComputerUseTaskParams", "StartGrokComputerUseTaskParams", "StartHyperAgentTaskParams", + "StartMetaComputerUseTaskParams", ] diff --git a/tests/test_meta_computer_use_models.py b/tests/test_meta_computer_use_models.py new file mode 100644 index 00000000..d7b85094 --- /dev/null +++ b/tests/test_meta_computer_use_models.py @@ -0,0 +1,37 @@ +from hyperbrowser.models.agents.meta_computer_use import MetaComputerUseTaskResponse + + +def test_meta_computer_use_task_response_parses_responses_style_step_payload() -> None: + payload = { + "jobId": "job-123", + "status": "completed", + "data": { + "steps": [ + { + "created_at": 1710000000, + "completed_at": 1710000001, + "output_text": "done", + "error": {"code": "ERR", "message": "something failed"}, + "incomplete_details": {"reason": "timeout"}, + "model": "muse-spark-1.1", + "output": [], + "reasoning": {"effort": "high"}, + "status": "completed", + } + ], + "finalResult": "result", + }, + } + + result = MetaComputerUseTaskResponse(**payload) + + step = result.data.steps[0] + assert step.created_at == 1710000000 + assert step.completed_at == 1710000001 + assert step.error is not None + assert step.error.code == "ERR" + assert step.error.message == "something failed" + assert step.incomplete_details is not None + assert step.incomplete_details.reason == "timeout" + assert step.reasoning is not None + assert step.reasoning.effort == "high" diff --git a/tests/test_request_compat.py b/tests/test_request_compat.py index b052881f..638e06a4 100644 --- a/tests/test_request_compat.py +++ b/tests/test_request_compat.py @@ -11,8 +11,10 @@ CuaBaseUrls, CreateSandboxParams, CreateSessionParams, + MetaComputerUseApiKeys, StartBrowserUseTaskParams, StartCuaTaskParams, + StartMetaComputerUseTaskParams, ) from hyperbrowser.tools import _normalize_extract_tool_params @@ -108,6 +110,38 @@ def test_cua_custom_base_url_serializes_for_mapping_and_legacy_model(): } +def test_meta_computer_use_serializes_for_mapping_and_legacy_model(): + mapping = { + "task": "Complete the task", + "llm": "muse-spark-1.1", + "reasoning_effort": "xhigh", + "use_custom_api_keys": True, + "api_keys": {"meta": "meta-key"}, + "use_computer_action": True, + } + legacy = StartMetaComputerUseTaskParams( + task="Complete the task", + llm="muse-spark-1.1", + reasoning_effort="xhigh", + use_custom_api_keys=True, + api_keys=MetaComputerUseApiKeys(meta="meta-key"), + use_computer_action=True, + ) + + assert dump_request(mapping, StartMetaComputerUseTaskParams) == dump_request( + legacy, + StartMetaComputerUseTaskParams, + ) + assert dump_request(mapping, StartMetaComputerUseTaskParams) == { + "task": "Complete the task", + "llm": "muse-spark-1.1", + "reasoningEffort": "xhigh", + "useCustomApiKeys": True, + "apiKeys": {"meta": "meta-key"}, + "useComputerAction": True, + } + + def test_raw_json_schema_is_copied_without_rewriting_or_dereferencing(): schema = { "$defs": { diff --git a/tests/typecheck/valid_requests.py b/tests/typecheck/valid_requests.py index 40d3323c..5a4908c0 100644 --- a/tests/typecheck/valid_requests.py +++ b/tests/typecheck/valid_requests.py @@ -106,6 +106,15 @@ def valid_sync_requests(client: Hyperbrowser) -> None: }, } ) + client.agents.meta_computer_use.start( + { + "task": "Complete the task", + "llm": "muse-spark-1.1", + "reasoning_effort": "medium", + "use_custom_api_keys": True, + "api_keys": {"meta": "meta-key"}, + } + ) client.sandboxes.create( { "image_name": "node", @@ -233,6 +242,13 @@ async def valid_async_requests(client: AsyncHyperbrowser) -> None: }, } ) + await client.agents.meta_computer_use.start( + { + "task": "Complete the task", + "llm": "muse-spark-1.1", + "reasoning_effort": "xhigh", + } + ) await client.sandboxes.create( { "snapshot_name": "ready-to-run", From 803e34cc9397da1a749ed18f48d99214d85dc8ce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 6 Sep 2026 23:22:33 +0000 Subject: [PATCH 2/5] feat: add muse-spark-1.2/1.3 and max reasoning for Meta Computer Use Keep TypedDict and legacy Pydantic request paths aligned with the current API. Co-authored-by: nikhil --- hyperbrowser/models/consts.py | 7 ++++++- tests/test_request_compat.py | 12 ++++++------ tests/typecheck/valid_requests.py | 6 +++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/hyperbrowser/models/consts.py b/hyperbrowser/models/consts.py index 4f750ab3..2335afef 100644 --- a/hyperbrowser/models/consts.py +++ b/hyperbrowser/models/consts.py @@ -106,13 +106,18 @@ "medium", "high", ] -MetaComputerUseLlm = Literal["muse-spark-1.1",] +MetaComputerUseLlm = Literal[ + "muse-spark-1.1", + "muse-spark-1.2", + "muse-spark-1.3", +] MetaReasoningEffort = Literal[ "minimal", "low", "medium", "high", "xhigh", + "max", ] SessionRegion = Literal[ "us", diff --git a/tests/test_request_compat.py b/tests/test_request_compat.py index 638e06a4..db19c13d 100644 --- a/tests/test_request_compat.py +++ b/tests/test_request_compat.py @@ -113,16 +113,16 @@ def test_cua_custom_base_url_serializes_for_mapping_and_legacy_model(): def test_meta_computer_use_serializes_for_mapping_and_legacy_model(): mapping = { "task": "Complete the task", - "llm": "muse-spark-1.1", - "reasoning_effort": "xhigh", + "llm": "muse-spark-1.3", + "reasoning_effort": "max", "use_custom_api_keys": True, "api_keys": {"meta": "meta-key"}, "use_computer_action": True, } legacy = StartMetaComputerUseTaskParams( task="Complete the task", - llm="muse-spark-1.1", - reasoning_effort="xhigh", + llm="muse-spark-1.3", + reasoning_effort="max", use_custom_api_keys=True, api_keys=MetaComputerUseApiKeys(meta="meta-key"), use_computer_action=True, @@ -134,8 +134,8 @@ def test_meta_computer_use_serializes_for_mapping_and_legacy_model(): ) assert dump_request(mapping, StartMetaComputerUseTaskParams) == { "task": "Complete the task", - "llm": "muse-spark-1.1", - "reasoningEffort": "xhigh", + "llm": "muse-spark-1.3", + "reasoningEffort": "max", "useCustomApiKeys": True, "apiKeys": {"meta": "meta-key"}, "useComputerAction": True, diff --git a/tests/typecheck/valid_requests.py b/tests/typecheck/valid_requests.py index 5a4908c0..7e3587e2 100644 --- a/tests/typecheck/valid_requests.py +++ b/tests/typecheck/valid_requests.py @@ -109,8 +109,8 @@ def valid_sync_requests(client: Hyperbrowser) -> None: client.agents.meta_computer_use.start( { "task": "Complete the task", - "llm": "muse-spark-1.1", - "reasoning_effort": "medium", + "llm": "muse-spark-1.3", + "reasoning_effort": "max", "use_custom_api_keys": True, "api_keys": {"meta": "meta-key"}, } @@ -245,7 +245,7 @@ async def valid_async_requests(client: AsyncHyperbrowser) -> None: await client.agents.meta_computer_use.start( { "task": "Complete the task", - "llm": "muse-spark-1.1", + "llm": "muse-spark-1.2", "reasoning_effort": "xhigh", } ) From c6ea51f1193ec9e5d9869609d5659b0258535ce0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 7 Sep 2026 01:29:03 +0000 Subject: [PATCH 3/5] Keep Meta Computer Use reasoning.summary on parsed steps. Meta history uses effort/summary, not CUA's generate_summary field. Co-authored-by: nikhil --- hyperbrowser/models/__init__.py | 2 + .../models/agents/meta_computer_use.py | 17 +++- tests/test_meta_computer_use_models.py | 91 +++++++++++++++---- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/hyperbrowser/models/__init__.py b/hyperbrowser/models/__init__.py index 0de0fc1b..43d483d5 100644 --- a/hyperbrowser/models/__init__.py +++ b/hyperbrowser/models/__init__.py @@ -129,6 +129,7 @@ ) from .agents.meta_computer_use import ( MetaComputerUseTaskStatus, + MetaComputerUseStepReasoning, MetaComputerUseStepResponse, MetaComputerUseTaskData, MetaComputerUseTaskResponse, @@ -509,6 +510,7 @@ "StartGrokComputerUseTaskParams", "StartGrokComputerUseTaskResponse", "MetaComputerUseTaskStatus", + "MetaComputerUseStepReasoning", "MetaComputerUseStepResponse", "MetaComputerUseTaskData", "MetaComputerUseTaskResponse", diff --git a/hyperbrowser/models/agents/meta_computer_use.py b/hyperbrowser/models/agents/meta_computer_use.py index b3821a58..e2da9fc7 100644 --- a/hyperbrowser/models/agents/meta_computer_use.py +++ b/hyperbrowser/models/agents/meta_computer_use.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, ConfigDict, Field -from .cua import CuaStepIncompleteDetails, CuaStepReasoning, CuaStepResponseError +from .cua import CuaStepIncompleteDetails, CuaStepResponseError from ..session import CreateSessionParams from ..consts import MetaComputerUseLlm, MetaReasoningEffort @@ -84,6 +84,19 @@ class MetaComputerUseTaskStatusResponse(BaseModel): status: MetaComputerUseTaskStatus +class MetaComputerUseStepReasoning(BaseModel): + """ + Reasoning metadata on a compacted Meta Computer Use step. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + effort: Optional[str] = Field(default=None, serialization_alias="effort") + summary: Optional[str] = Field(default=None, serialization_alias="summary") + + class MetaComputerUseStepResponse(BaseModel): """ Response from a single Meta Computer Use step. @@ -106,7 +119,7 @@ class MetaComputerUseStepResponse(BaseModel): ) model: Optional[str] = Field(default=None, serialization_alias="model") output: Optional[List[Any]] = Field(default=None, serialization_alias="output") - reasoning: Optional[CuaStepReasoning] = Field( + reasoning: Optional[MetaComputerUseStepReasoning] = Field( default=None, serialization_alias="reasoning" ) status: Optional[str] = Field(default=None, serialization_alias="status") diff --git a/tests/test_meta_computer_use_models.py b/tests/test_meta_computer_use_models.py index d7b85094..0b9063ce 100644 --- a/tests/test_meta_computer_use_models.py +++ b/tests/test_meta_computer_use_models.py @@ -8,16 +8,64 @@ def test_meta_computer_use_task_response_parses_responses_style_step_payload() - "data": { "steps": [ { - "created_at": 1710000000, - "completed_at": 1710000001, - "output_text": "done", + "created_at": 1788743504, + "completed_at": 1788743511, + "output_text": ( + "Second sentence in the Ecology section " + '(Population subsection) of the Dog Wikipedia page:\n\n' + '"In 2020, the estimated global dog population was ' + 'between 700 million and 1 billion."' + ), + "error": None, + "incomplete_details": None, + "model": "muse-spark-1.3", + "output": [ + { + "type": "reasoning", + "status": "completed", + "summary": [ + { + "text": "Interpreting the ecology section structure.", + "type": "summary_text", + } + ], + }, + { + "type": "message", + "role": "assistant", + "status": "completed", + "content": [ + { + "type": "output_text", + "text": "Second sentence in the Ecology section.", + } + ], + }, + ], + "reasoning": { + "effort": "medium", + "summary": "concise", + }, + "status": "completed", + }, + { + "created_at": 1788743409, + "completed_at": 1788743410, + "output_text": "", "error": {"code": "ERR", "message": "something failed"}, "incomplete_details": {"reason": "timeout"}, - "model": "muse-spark-1.1", - "output": [], - "reasoning": {"effort": "high"}, + "model": "muse-spark-1.3", + "output": [ + { + "type": "function_call", + "name": "computer.computer", + "arguments": '{"actions":[{"action":"type","text":"google.com\\n"}]}', + "status": "completed", + } + ], + "reasoning": {"effort": "medium", "summary": "concise"}, "status": "completed", - } + }, ], "finalResult": "result", }, @@ -25,13 +73,22 @@ def test_meta_computer_use_task_response_parses_responses_style_step_payload() - result = MetaComputerUseTaskResponse(**payload) - step = result.data.steps[0] - assert step.created_at == 1710000000 - assert step.completed_at == 1710000001 - assert step.error is not None - assert step.error.code == "ERR" - assert step.error.message == "something failed" - assert step.incomplete_details is not None - assert step.incomplete_details.reason == "timeout" - assert step.reasoning is not None - assert step.reasoning.effort == "high" + final_step = result.data.steps[0] + assert final_step.created_at == 1788743504 + assert final_step.completed_at == 1788743511 + assert final_step.error is None + assert final_step.incomplete_details is None + assert final_step.model == "muse-spark-1.3" + assert final_step.reasoning is not None + assert final_step.reasoning.effort == "medium" + assert final_step.reasoning.summary == "concise" + assert final_step.output[0]["type"] == "reasoning" + assert final_step.output[1]["type"] == "message" + + action_step = result.data.steps[1] + assert action_step.error is not None + assert action_step.error.code == "ERR" + assert action_step.error.message == "something failed" + assert action_step.incomplete_details is not None + assert action_step.incomplete_details.reason == "timeout" + assert action_step.output[0]["name"] == "computer.computer" From 27669294042b1c74d0c8329c852633a308b2549c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 7 Sep 2026 01:36:39 +0000 Subject: [PATCH 4/5] Parse live Meta Computer Use envelope fields on get responses. Keep createdAt, finishedAt, liveDomain, and jobParams instead of dropping them from the production task payload. Co-authored-by: nikhil --- hyperbrowser/models/__init__.py | 2 ++ .../models/agents/meta_computer_use.py | 34 +++++++++++++++++++ tests/test_meta_computer_use_models.py | 23 +++++++++++++ 3 files changed, 59 insertions(+) diff --git a/hyperbrowser/models/__init__.py b/hyperbrowser/models/__init__.py index 43d483d5..e01de2e8 100644 --- a/hyperbrowser/models/__init__.py +++ b/hyperbrowser/models/__init__.py @@ -137,6 +137,7 @@ StartMetaComputerUseTaskParams, StartMetaComputerUseTaskResponse, MetaComputerUseApiKeys, + MetaComputerUseJobParams, ) from .agents.cua import ( CuaTaskData, @@ -528,6 +529,7 @@ "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", "MetaComputerUseApiKeys", + "MetaComputerUseJobParams", "CuaApiKeys", "CuaBaseUrls", "HyperAgentApiKeys", diff --git a/hyperbrowser/models/agents/meta_computer_use.py b/hyperbrowser/models/agents/meta_computer_use.py index e2da9fc7..31873f0e 100644 --- a/hyperbrowser/models/agents/meta_computer_use.py +++ b/hyperbrowser/models/agents/meta_computer_use.py @@ -1,5 +1,6 @@ from __future__ import annotations +from datetime import datetime from typing import Any, List, Literal, Optional from pydantic import BaseModel, ConfigDict, Field @@ -70,6 +71,7 @@ class StartMetaComputerUseTaskResponse(BaseModel): job_id: str = Field(alias="jobId") live_url: Optional[str] = Field(default=None, alias="liveUrl") + live_domain: Optional[str] = Field(default=None, alias="liveDomain") class MetaComputerUseTaskStatusResponse(BaseModel): @@ -146,6 +148,32 @@ class MetaComputerUseTaskMetadata(BaseModel): ) +class MetaComputerUseJobParams(BaseModel): + """ + Echoed start params returned on a Meta Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + task: Optional[str] = Field(default=None, alias="task") + llm: Optional[MetaComputerUseLlm] = Field(default=None, alias="llm") + reasoning_effort: Optional[MetaReasoningEffort] = Field( + default=None, alias="reasoningEffort" + ) + session_id: Optional[str] = Field(default=None, alias="sessionId") + max_steps: Optional[int] = Field(default=None, alias="maxSteps") + keep_browser_open: Optional[bool] = Field(default=None, alias="keepBrowserOpen") + max_failures: Optional[int] = Field(default=None, alias="maxFailures") + use_custom_api_keys: Optional[bool] = Field( + default=None, alias="useCustomApiKeys" + ) + use_computer_action: Optional[bool] = Field( + default=None, alias="useComputerAction" + ) + + class MetaComputerUseTaskResponse(BaseModel): """ Response from a Meta Computer Use task. @@ -157,9 +185,15 @@ class MetaComputerUseTaskResponse(BaseModel): job_id: str = Field(alias="jobId") status: MetaComputerUseTaskStatus + created_at: Optional[datetime] = Field(default=None, alias="createdAt") + finished_at: Optional[datetime] = Field(default=None, alias="finishedAt") metadata: Optional[MetaComputerUseTaskMetadata] = Field( default=None, alias="metadata" ) data: Optional[MetaComputerUseTaskData] = Field(default=None, alias="data") error: Optional[str] = Field(default=None, alias="error") live_url: Optional[str] = Field(default=None, alias="liveUrl") + live_domain: Optional[str] = Field(default=None, alias="liveDomain") + job_params: Optional[MetaComputerUseJobParams] = Field( + default=None, alias="jobParams" + ) diff --git a/tests/test_meta_computer_use_models.py b/tests/test_meta_computer_use_models.py index 0b9063ce..4c9f93e0 100644 --- a/tests/test_meta_computer_use_models.py +++ b/tests/test_meta_computer_use_models.py @@ -5,6 +5,21 @@ def test_meta_computer_use_task_response_parses_responses_style_step_payload() - payload = { "jobId": "job-123", "status": "completed", + "createdAt": "2026-09-07T01:33:08.205Z", + "finishedAt": "2026-09-07T01:33:33.038Z", + "liveUrl": None, + "liveDomain": None, + "jobParams": { + "task": "Go to example.com", + "llm": "muse-spark-1.3", + "reasoningEffort": "medium", + "sessionId": "session-123", + "maxSteps": 5, + "keepBrowserOpen": False, + "maxFailures": 3, + "useCustomApiKeys": False, + "useComputerAction": True, + }, "data": { "steps": [ { @@ -73,6 +88,14 @@ def test_meta_computer_use_task_response_parses_responses_style_step_payload() - result = MetaComputerUseTaskResponse(**payload) + assert result.created_at is not None + assert result.finished_at is not None + assert result.live_domain is None + assert result.job_params is not None + assert result.job_params.llm == "muse-spark-1.3" + assert result.job_params.reasoning_effort == "medium" + assert result.job_params.session_id == "session-123" + final_step = result.data.steps[0] assert final_step.created_at == 1788743504 assert final_step.completed_at == 1788743511 From f01d1aeaf4375aa03baa3cf3c81776250b029302 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 7 Sep 2026 01:42:34 +0000 Subject: [PATCH 5/5] Drop Meta envelope fields that other agents omit. Keep the task response aligned with Grok/CUA: no createdAt, finishedAt, liveDomain, or jobParams. Co-authored-by: nikhil --- hyperbrowser/models/__init__.py | 2 -- .../models/agents/meta_computer_use.py | 34 ------------------- tests/test_meta_computer_use_models.py | 23 ------------- 3 files changed, 59 deletions(-) diff --git a/hyperbrowser/models/__init__.py b/hyperbrowser/models/__init__.py index e01de2e8..43d483d5 100644 --- a/hyperbrowser/models/__init__.py +++ b/hyperbrowser/models/__init__.py @@ -137,7 +137,6 @@ StartMetaComputerUseTaskParams, StartMetaComputerUseTaskResponse, MetaComputerUseApiKeys, - MetaComputerUseJobParams, ) from .agents.cua import ( CuaTaskData, @@ -529,7 +528,6 @@ "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", "MetaComputerUseApiKeys", - "MetaComputerUseJobParams", "CuaApiKeys", "CuaBaseUrls", "HyperAgentApiKeys", diff --git a/hyperbrowser/models/agents/meta_computer_use.py b/hyperbrowser/models/agents/meta_computer_use.py index 31873f0e..e2da9fc7 100644 --- a/hyperbrowser/models/agents/meta_computer_use.py +++ b/hyperbrowser/models/agents/meta_computer_use.py @@ -1,6 +1,5 @@ from __future__ import annotations -from datetime import datetime from typing import Any, List, Literal, Optional from pydantic import BaseModel, ConfigDict, Field @@ -71,7 +70,6 @@ class StartMetaComputerUseTaskResponse(BaseModel): job_id: str = Field(alias="jobId") live_url: Optional[str] = Field(default=None, alias="liveUrl") - live_domain: Optional[str] = Field(default=None, alias="liveDomain") class MetaComputerUseTaskStatusResponse(BaseModel): @@ -148,32 +146,6 @@ class MetaComputerUseTaskMetadata(BaseModel): ) -class MetaComputerUseJobParams(BaseModel): - """ - Echoed start params returned on a Meta Computer Use task. - """ - - model_config = ConfigDict( - populate_by_alias=True, - ) - - task: Optional[str] = Field(default=None, alias="task") - llm: Optional[MetaComputerUseLlm] = Field(default=None, alias="llm") - reasoning_effort: Optional[MetaReasoningEffort] = Field( - default=None, alias="reasoningEffort" - ) - session_id: Optional[str] = Field(default=None, alias="sessionId") - max_steps: Optional[int] = Field(default=None, alias="maxSteps") - keep_browser_open: Optional[bool] = Field(default=None, alias="keepBrowserOpen") - max_failures: Optional[int] = Field(default=None, alias="maxFailures") - use_custom_api_keys: Optional[bool] = Field( - default=None, alias="useCustomApiKeys" - ) - use_computer_action: Optional[bool] = Field( - default=None, alias="useComputerAction" - ) - - class MetaComputerUseTaskResponse(BaseModel): """ Response from a Meta Computer Use task. @@ -185,15 +157,9 @@ class MetaComputerUseTaskResponse(BaseModel): job_id: str = Field(alias="jobId") status: MetaComputerUseTaskStatus - created_at: Optional[datetime] = Field(default=None, alias="createdAt") - finished_at: Optional[datetime] = Field(default=None, alias="finishedAt") metadata: Optional[MetaComputerUseTaskMetadata] = Field( default=None, alias="metadata" ) data: Optional[MetaComputerUseTaskData] = Field(default=None, alias="data") error: Optional[str] = Field(default=None, alias="error") live_url: Optional[str] = Field(default=None, alias="liveUrl") - live_domain: Optional[str] = Field(default=None, alias="liveDomain") - job_params: Optional[MetaComputerUseJobParams] = Field( - default=None, alias="jobParams" - ) diff --git a/tests/test_meta_computer_use_models.py b/tests/test_meta_computer_use_models.py index 4c9f93e0..0b9063ce 100644 --- a/tests/test_meta_computer_use_models.py +++ b/tests/test_meta_computer_use_models.py @@ -5,21 +5,6 @@ def test_meta_computer_use_task_response_parses_responses_style_step_payload() - payload = { "jobId": "job-123", "status": "completed", - "createdAt": "2026-09-07T01:33:08.205Z", - "finishedAt": "2026-09-07T01:33:33.038Z", - "liveUrl": None, - "liveDomain": None, - "jobParams": { - "task": "Go to example.com", - "llm": "muse-spark-1.3", - "reasoningEffort": "medium", - "sessionId": "session-123", - "maxSteps": 5, - "keepBrowserOpen": False, - "maxFailures": 3, - "useCustomApiKeys": False, - "useComputerAction": True, - }, "data": { "steps": [ { @@ -88,14 +73,6 @@ def test_meta_computer_use_task_response_parses_responses_style_step_payload() - result = MetaComputerUseTaskResponse(**payload) - assert result.created_at is not None - assert result.finished_at is not None - assert result.live_domain is None - assert result.job_params is not None - assert result.job_params.llm == "muse-spark-1.3" - assert result.job_params.reasoning_effort == "medium" - assert result.job_params.session_id == "session-123" - final_step = result.data.steps[0] assert final_step.created_at == 1788743504 assert final_step.completed_at == 1788743511