lybic-guiagents 0.4.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lybic-guiagents might be problematic. Click here for more details.

Files changed (128) hide show
  1. {lybic_guiagents-0.4.0/lybic_guiagents.egg-info → lybic_guiagents-0.5.0}/PKG-INFO +1 -1
  2. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/__init__.py +1 -1
  3. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/agent_s.py +45 -8
  4. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/global_state.py +13 -1
  5. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/grounding.py +5 -0
  6. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/manager.py +5 -0
  7. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/worker.py +12 -2
  8. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/cli_app.py +13 -4
  9. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/grpc_app.py +172 -65
  10. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/store/registry.py +1 -1
  11. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0/lybic_guiagents.egg-info}/PKG-INFO +1 -1
  12. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/pyproject.toml +1 -1
  13. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/LICENSE +0 -0
  14. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/README.md +0 -0
  15. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/__init__.py +0 -0
  16. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/actions.py +0 -0
  17. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/controllers/__init__.py +0 -0
  18. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/controllers/python.py +0 -0
  19. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/controllers/setup.py +0 -0
  20. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/desktop_env.py +0 -0
  21. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/__init__.py +0 -0
  22. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/__init__.py +0 -0
  23. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/calc.py +0 -0
  24. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/chrome.py +0 -0
  25. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/file.py +0 -0
  26. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/general.py +0 -0
  27. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/gimp.py +0 -0
  28. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/impress.py +0 -0
  29. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/info.py +0 -0
  30. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/misc.py +0 -0
  31. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/replay.py +0 -0
  32. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/vlc.py +0 -0
  33. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/getters/vscode.py +0 -0
  34. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/__init__.py +0 -0
  35. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/basic_os.py +0 -0
  36. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/chrome.py +0 -0
  37. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/docs.py +0 -0
  38. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/general.py +0 -0
  39. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/gimp.py +0 -0
  40. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/libreoffice.py +0 -0
  41. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/others.py +0 -0
  42. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/pdf.py +0 -0
  43. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/slides.py +0 -0
  44. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/table.py +0 -0
  45. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/thunderbird.py +0 -0
  46. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/utils.py +0 -0
  47. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/vlc.py +0 -0
  48. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/evaluators/metrics/vscode.py +0 -0
  49. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/__init__.py +0 -0
  50. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/aws/__init__.py +0 -0
  51. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/aws/manager.py +0 -0
  52. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/aws/provider.py +0 -0
  53. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/aws/provider_with_proxy.py +0 -0
  54. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/aws/proxy_pool.py +0 -0
  55. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/azure/__init__.py +0 -0
  56. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/azure/manager.py +0 -0
  57. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/azure/provider.py +0 -0
  58. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/base.py +0 -0
  59. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/gcp/__init__.py +0 -0
  60. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/gcp/manager.py +0 -0
  61. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/gcp/provider.py +0 -0
  62. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/virtualbox/__init__.py +0 -0
  63. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/virtualbox/manager.py +0 -0
  64. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/virtualbox/provider.py +0 -0
  65. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/vmware/__init__.py +0 -0
  66. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/vmware/manager.py +0 -0
  67. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/desktop_env/providers/vmware/provider.py +0 -0
  68. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Action.py +0 -0
  69. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Backend/ADBBackend.py +0 -0
  70. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Backend/Backend.py +0 -0
  71. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Backend/LybicBackend.py +0 -0
  72. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Backend/PyAutoGUIBackend.py +0 -0
  73. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Backend/PyAutoGUIVMwareBackend.py +0 -0
  74. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/Backend/__init__.py +0 -0
  75. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/__init__.py +0 -0
  76. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/hardware_interface.py +0 -0
  77. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/stream_manager.py +0 -0
  78. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/agents/translator.py +0 -0
  79. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/core/__init__.py +0 -0
  80. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/core/engine.py +0 -0
  81. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/core/knowledge.py +0 -0
  82. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/core/mllm.py +0 -0
  83. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/prompts/__init__.py +0 -0
  84. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/prompts/prompts.py +0 -0
  85. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/proto/__init__.py +0 -0
  86. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/proto/pb/__init__.py +0 -0
  87. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/service/__init__.py +0 -0
  88. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/service/agent_service.py +0 -0
  89. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/service/api_models.py +0 -0
  90. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/service/config.py +0 -0
  91. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/service/exceptions.py +0 -0
  92. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/store/__init__.py +0 -0
  93. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/tools/__init__.py +0 -0
  94. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/tools/model.md +0 -0
  95. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/tools/tools.py +0 -0
  96. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/tools/tools_config.json +0 -0
  97. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/tools/tools_config_cn.json +0 -0
  98. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/tools/tools_config_en.json +0 -0
  99. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/unit_test/__init__.py +0 -0
  100. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/unit_test/run_tests.py +0 -0
  101. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/unit_test/test_manager.py +0 -0
  102. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/unit_test/test_worker.py +0 -0
  103. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/utils/__init__.py +0 -0
  104. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/utils/analyze_display.py +0 -0
  105. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/utils/common_utils.py +0 -0
  106. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/utils/display_viewer.py +0 -0
  107. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/utils/embedding_manager.py +0 -0
  108. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/gui_agents/utils/image_axis_utils.py +0 -0
  109. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/lybic_guiagents.egg-info/SOURCES.txt +0 -0
  110. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/lybic_guiagents.egg-info/dependency_links.txt +0 -0
  111. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/lybic_guiagents.egg-info/entry_points.txt +0 -0
  112. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/lybic_guiagents.egg-info/requires.txt +0 -0
  113. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/lybic_guiagents.egg-info/top_level.txt +0 -0
  114. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/setup.cfg +0 -0
  115. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/setup.py +0 -0
  116. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_aci.py +0 -0
  117. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_api_masking.py +0 -0
  118. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_app_switching.py +0 -0
  119. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_global_instance.py +0 -0
  120. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_grpc_config.py +0 -0
  121. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_hardware_interface.py +0 -0
  122. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_registry.py +0 -0
  123. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_streaming.py +0 -0
  124. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_translator.py +0 -0
  125. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_uielement_base.py +0 -0
  126. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_uielement_linux.py +0 -0
  127. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_uielement_macos.py +0 -0
  128. {lybic_guiagents-0.4.0 → lybic_guiagents-0.5.0}/tests/test_uielement_osworld.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lybic-guiagents
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: An open-source agentic framework that enables AI to use computers like humans and can provide a multi-agent runtime environment as an infrastructure capability
5
5
  Author: Lybic Development Team
6
6
  Author-email: Lybic Development Team <lybic@tingyutech.com>
@@ -37,7 +37,7 @@ from .agents.hardware_interface import HardwareInterface
37
37
  from .store.registry import Registry
38
38
  from .agents.global_state import GlobalState
39
39
 
40
- __version__ = "0.4.0"
40
+ __version__ = "0.5.0"
41
41
 
42
42
  # Primary exports (what users should typically use)
43
43
  __all__ = [
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import json
3
2
  import logging
4
3
  import os
@@ -13,7 +12,6 @@ from gui_agents.utils.common_utils import Node
13
12
  from gui_agents.agents.global_state import GlobalState
14
13
  from gui_agents.store.registry import Registry
15
14
  from gui_agents.utils.common_utils import (
16
- # call_llm_safe,
17
15
  parse_single_code_from_string,
18
16
  sanitize_code,
19
17
  extract_first_agent_function,
@@ -253,17 +251,28 @@ class AgentS2(UIAgent):
253
251
  # Initialize the three info dictionaries
254
252
  """
255
253
  Produce the next executor actions and diagnostic information for the current task step.
256
-
254
+
257
255
  This method coordinates planning, subtask selection, action generation, grounding (code extraction and execution), and status updates. It may trigger replanning, advance to the next subtask, mark subtasks as completed or failed, and emit stream messages and logs. The returned info merges planner, executor, and evaluator metadata and includes current subtask details.
258
-
256
+
259
257
  Parameters:
260
258
  instruction (str): The user or system instruction describing the task to accomplish; forwarded to the manager/worker as the task utterance.
261
259
  observation (Dict): Current environment observation/state used for grounding and coordinate assignment.
262
-
260
+
263
261
  Returns:
264
262
  info (Dict): A merged dictionary containing planner_info, executor_info, evaluator_info and the keys `subtask`, `subtask_info`, and `subtask_status`.
265
263
  actions (List[Dict]): List of action dictionaries produced for execution (may include actions with type "DONE", failure indicators, or other executor-generated actions).
266
264
  """
265
+ # Check for cancellation before starting prediction
266
+ if self.global_state.is_cancelled():
267
+ logger.info("AgentS2 prediction cancelled by user request")
268
+ return {
269
+ "subtask": "cancelled",
270
+ "subtask_info": "",
271
+ "subtask_status": "cancelled",
272
+ "reflection": "Task was cancelled",
273
+ "executor_plan": "agent.done()"
274
+ }, ["done"]
275
+
267
276
  planner_info = {}
268
277
  executor_info = {}
269
278
  evaluator_info = {
@@ -280,6 +289,16 @@ class AgentS2(UIAgent):
280
289
 
281
290
  # If the DONE response by the executor is for a subtask, then the agent should continue with the next subtask without sending the action to the environment
282
291
  while not self.should_send_action:
292
+ # Check for cancellation in each iteration
293
+ if self.global_state.is_cancelled():
294
+ logger.info("AgentS2 prediction loop cancelled by user request")
295
+ return {
296
+ "subtask": "cancelled",
297
+ "subtask_info": "",
298
+ "subtask_status": "cancelled",
299
+ "reflection": "Task was cancelled",
300
+ "executor_plan": "agent.done()"
301
+ }, [{"type": "DONE"}]
283
302
  time.sleep(5.0)
284
303
  self.subtask_status = "In"
285
304
  # Always time get_action_queue, even if not called
@@ -434,6 +453,15 @@ class AgentS2(UIAgent):
434
453
  }
435
454
  )
436
455
  except Exception as e:
456
+ if self.global_state.is_cancelled():
457
+ logger.info("Cancelled during grounding; stopping without action")
458
+ return {
459
+ "subtask": "cancelled",
460
+ "subtask_info": "",
461
+ "subtask_status": "cancelled",
462
+ "reflection": "Task was cancelled",
463
+ "executor_plan": "agent.done()"
464
+ }, [{"type": "DONE"}]
437
465
  logger.error("Error in parsing plan code: %s", e)
438
466
  plan_code = "agent.wait(1.0)"
439
467
  agent: Grounding = self.grounding # this agent will be used in next code
@@ -854,18 +882,27 @@ class AgentSFast(UIAgent):
854
882
  def predict(self, instruction: str, observation: Dict) -> Tuple[Dict, List[str]]:
855
883
  """
856
884
  Generate the next executor plan and corresponding actions using the configured fast action generator.
857
-
885
+
858
886
  Parameters:
859
887
  instruction (str): Natural language task description.
860
888
  observation (Dict): Current UI state; must include a "screenshot" entry with the screen image.
861
-
889
+
862
890
  Returns:
863
891
  executor_info (dict): Contains at least the keys `executor_plan` (raw plan text), `reflection` (reflection text or empty string), and `plan_code` (the latest extracted/used action code).
864
892
  actions (List[dict]): List of action dictionaries produced by grounding execution; typically a single action dict describing the operation to perform.
865
893
  """
894
+ # Check for cancellation before starting prediction
895
+ if self.global_state.is_cancelled():
896
+ logger.info("AgentSFast prediction cancelled by user request")
897
+ return {
898
+ "executor_plan": "agent.done()",
899
+ "reflection": "Task was cancelled",
900
+ "plan_code": "agent.done()"
901
+ }, [{"type": "DONE"}]
902
+
866
903
  import time
867
904
  predict_start_time = time.time()
868
-
905
+
869
906
  fast_action_start_time = time.time()
870
907
 
871
908
  reflection = None
@@ -467,7 +467,9 @@ class GlobalState:
467
467
  return "stopped"
468
468
 
469
469
  def set_running_state(self, state: str) -> None:
470
- assert state in {"running", "stopped"}
470
+ if state not in {"running", "stopped", "cancelled"}:
471
+ raise ValueError(f"Invalid running state: {state}")
472
+
471
473
  tmp = self.running_state_path.with_suffix(".tmp")
472
474
  try:
473
475
  with locked(tmp, "w") as f:
@@ -486,6 +488,16 @@ class GlobalState:
486
488
  pass
487
489
  raise
488
490
 
491
+ def is_cancelled(self) -> bool:
492
+ """Check if the current execution has been cancelled"""
493
+ try:
494
+ with locked(self.running_state_path, "r") as f:
495
+ data = safe_json_load(f)
496
+ return data == "cancelled"
497
+ except Exception as e:
498
+ logger.warning(f"Failed to check cancellation state: {e}")
499
+ return False
500
+
489
501
  # ---------- High-level Wrappers ----------
490
502
  def get_obs_for_manager(self):
491
503
  return {
@@ -101,6 +101,11 @@ class Grounding(ACI):
101
101
  self.global_state = Registry.get_from_context("GlobalStateStore", task_id) # type: ignore
102
102
 
103
103
  def generate_coords(self, ref_expr: str, obs: Dict) -> List[int]:
104
+ # Check for cancellation before starting coordinate generation
105
+ if self.global_state.is_cancelled():
106
+ logger.info("Grounding coordinate generation cancelled by user request")
107
+ raise RuntimeError("cancelled") # Return default coordinates when cancelled
108
+
104
109
  grounding_start_time = time.time()
105
110
  self.grounding_model.tools["grounding"].llm_agent.reset()
106
111
  prompt = (
@@ -576,6 +576,11 @@ class Manager:
576
576
  """Generate the action list based on the instruction
577
577
  instruction:str: Instruction for the task
578
578
  """
579
+ # Check for cancellation before starting action queue generation
580
+ if self.global_state.is_cancelled():
581
+ logger.info("Manager action queue generation cancelled by user request")
582
+ return {"cancelled": True}, []
583
+
579
584
  import time
580
585
  action_queue_start = time.time()
581
586
 
@@ -178,7 +178,7 @@ class Worker:
178
178
  ) -> Dict:
179
179
  """
180
180
  Generate the next executor action plan and related metadata for the current subtask given the observation and context.
181
-
181
+
182
182
  Parameters:
183
183
  Tu (str): Full task description or task context.
184
184
  search_query (str): Search string used for retrieving episodic/subtask experience.
@@ -188,7 +188,7 @@ class Worker:
188
188
  done_task (List[Node]): List of completed task nodes.
189
189
  obs (Dict): Current observation dictionary; must include a "screenshot" key with the current screen image.
190
190
  running_state (str): Current executor running state (default "running").
191
-
191
+
192
192
  Returns:
193
193
  Dict: Executor information containing:
194
194
  - "current_subtask" (str): The provided subtask.
@@ -196,6 +196,16 @@ class Worker:
196
196
  - "executor_plan" (str): The raw plan produced by the action generator.
197
197
  - "reflection" (str|None): Reflection text produced by the trajectory reflector, or None if reflection is disabled.
198
198
  """
199
+ # Check for cancellation before starting action generation
200
+ if self.global_state.is_cancelled():
201
+ logger.info("Worker action generation cancelled by user request")
202
+ return {
203
+ "current_subtask": subtask,
204
+ "current_subtask_info": subtask_info,
205
+ "executor_plan": "agent.done()",
206
+ "reflection": "Task was cancelled"
207
+ }
208
+
199
209
  import time
200
210
  action_start = time.time()
201
211
 
@@ -275,7 +275,7 @@ def run_agent_normal(agent, instruction: str, hwi_para: HardwareInterface, max_s
275
275
  """
276
276
  if task_registry:
277
277
  Registry.set_task_registry(task_id, task_registry)
278
-
278
+
279
279
  try:
280
280
  import time
281
281
  obs = {}
@@ -297,6 +297,11 @@ def run_agent_normal(agent, instruction: str, hwi_para: HardwareInterface, max_s
297
297
  break
298
298
  time.sleep(0.5)
299
299
 
300
+ # Check for cancellation
301
+ if global_state.is_cancelled():
302
+ logger.info("Agent execution cancelled by user request")
303
+ return
304
+
300
305
  screenshot: Image.Image = hwi.dispatch(Screenshot()) # type: ignore
301
306
  global_state.set_screenshot(
302
307
  scale_screenshot_dimensions(screenshot, hwi_para)) # type: ignore
@@ -393,7 +398,7 @@ def run_agent_normal(agent, instruction: str, hwi_para: HardwareInterface, max_s
393
398
  global_state.log_operation(module="other",
394
399
  operation="total_execution_time",
395
400
  data={"duration": total_duration})
396
-
401
+
397
402
  # Auto-analyze execution statistics after task completion
398
403
  timestamp_dir = os.path.join(log_dir, datetime_str)
399
404
  auto_analyze_execution(timestamp_dir)
@@ -409,7 +414,7 @@ def run_agent_fast(agent,
409
414
  enable_takeover: bool = False, task_id: str | None = None, task_registry: Registry | None = None):
410
415
  if task_registry:
411
416
  Registry.set_task_registry(task_id, task_registry)
412
-
417
+
413
418
  try:
414
419
  import time
415
420
  obs = {}
@@ -428,6 +433,10 @@ def run_agent_fast(agent,
428
433
  logger.info("[Fast Mode] Agent execution resumed by user")
429
434
  break
430
435
  time.sleep(0.5)
436
+ # Check for cancellation
437
+ if global_state.is_cancelled():
438
+ logger.info("[Fast Mode] Agent execution cancelled by user request")
439
+ return
431
440
 
432
441
  screenshot: Image.Image = hwi.dispatch(Screenshot()) # type: ignore
433
442
  global_state.set_screenshot(
@@ -519,7 +528,7 @@ def run_agent_fast(agent,
519
528
  global_state.log_operation(module="other",
520
529
  operation="total_execution_time_fast",
521
530
  data={"duration": total_duration})
522
-
531
+
523
532
  # Auto-analyze execution statistics after task completion
524
533
  timestamp_dir = os.path.join(log_dir, datetime_str)
525
534
  auto_analyze_execution(timestamp_dir)
@@ -53,13 +53,10 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
53
53
  max_concurrent_task_num (int): Maximum number of agent tasks allowed to run concurrently; defaults to 1.
54
54
  log_dir (str): Directory for logging and task-related files.
55
55
  """
56
- self.lybic_auth: LybicAuth | None = None
57
56
  self.max_concurrent_task_num = max_concurrent_task_num
58
57
  self.tasks = {}
59
58
  self.global_common_config = agent_pb2.CommonConfig(id="global")
60
59
  self.task_lock = asyncio.Lock()
61
- self.lybic_client: LybicClient | None = None
62
- self.sandbox: Sandbox | None = None
63
60
  self.log_dir = log_dir
64
61
 
65
62
  async def GetAgentTaskStream(self, request, context):
@@ -113,7 +110,7 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
113
110
  domain=platform.node(),
114
111
  )
115
112
 
116
- async def _setup_task_state(self, task_id: str) -> Registry:
113
+ def _setup_task_state(self, task_id: str) -> Registry:
117
114
  """Setup global state and registry for task execution with task isolation"""
118
115
  # Create timestamp-based directory structure like cli_app.py
119
116
  datetime_str = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -152,15 +149,16 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
152
149
  async def _run_task(self, task_id: str, backend_kwargs):
153
150
  """
154
151
  Run the lifecycle of a single agent task: mark it running, execute the agent, record final state, emit stream messages, and unregister the task.
155
-
152
+
156
153
  Parameters:
157
154
  task_id (str): Identifier of the task to run.
158
155
  backend_kwargs (dict): Backend configuration passed to the HardwareInterface (e.g., platform, org/api fields, sandbox id).
159
-
156
+
160
157
  Notes:
161
158
  - Updates the task entry in self.tasks (status and final_state).
162
159
  - Emits task lifecycle messages via stream_manager and unregisters the task when finished.
163
160
  - Exceptions are caught, the task status is set to "error", and an error message is emitted.
161
+ - Supports task cancellation via asyncio.CancelledError.
164
162
  """
165
163
  async with self.task_lock:
166
164
  self.tasks[task_id]["status"] = "running"
@@ -176,7 +174,7 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
176
174
  await stream_manager.add_message(task_id, "starting", "Task starting")
177
175
 
178
176
  # Create task-specific registry
179
- task_registry = await self._setup_task_state(task_id)
177
+ task_registry = self._setup_task_state(task_id)
180
178
 
181
179
  # Set task_id for the agent. This is needed so that agent.reset() can find the right components.
182
180
  if hasattr(agent, 'set_task_id'):
@@ -209,6 +207,11 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
209
207
  status = final_state if final_state else 'unknown'
210
208
  await stream_manager.add_message(task_id, "finished", f"Task finished with status: {status}")
211
209
 
210
+ except asyncio.CancelledError:
211
+ logger.info(f"Task {task_id} was cancelled")
212
+ async with self.task_lock:
213
+ self.tasks[task_id]["status"] = "cancelled"
214
+ await stream_manager.add_message(task_id, "cancelled", "Task was cancelled by user request")
212
215
  except Exception as e:
213
216
  logger.exception(f"Error during task execution for {task_id}: {e}")
214
217
  async with self.task_lock:
@@ -236,7 +239,6 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
236
239
  - "endpoint": Lybic API endpoint.
237
240
 
238
241
  Side effects:
239
- - May initialize or replace self.lybic_auth from request.runningConfig.authorizationInfo.
240
242
  - May call self._create_sandbox(...) to create or retrieve a sandbox and determine the platform.
241
243
  """
242
244
  backend_kwargs = {}
@@ -249,12 +251,6 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
249
251
  if request.HasField("runningConfig"):
250
252
  if request.runningConfig.backend:
251
253
  backend = request.runningConfig.backend
252
- if request.runningConfig.HasField("authorizationInfo"):
253
- self.lybic_auth = LybicAuth(
254
- org_id=request.runningConfig.authorizationInfo.orgID,
255
- api_key=request.runningConfig.authorizationInfo.apiKey,
256
- endpoint=request.runningConfig.authorizationInfo.apiEndpoint or "https://api.lybic.cn/"
257
- )
258
254
  backend_kwargs["mode"] = request.runningConfig.mode
259
255
 
260
256
  platform_str = platform.system()
@@ -262,22 +258,34 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
262
258
  sandbox_pb = None
263
259
 
264
260
  if backend == 'lybic':
261
+ auth_info = (request.runningConfig.authorizationInfo
262
+ if request.HasField("runningConfig") and request.runningConfig.HasField("authorizationInfo")
263
+ else self.global_common_config.authorizationInfo)
264
+ if not auth_info or not auth_info.orgID or not auth_info.apiKey:
265
+ raise ValueError("Lybic backend requires valid authorization (orgID and apiKey)")
266
+
267
+ lybic_auth = LybicAuth(
268
+ org_id=auth_info.orgID,
269
+ api_key=auth_info.apiKey,
270
+ endpoint=auth_info.apiEndpoint or "https://api.lybic.cn/"
271
+ )
272
+
265
273
  if request.HasField("sandbox"):
266
274
  shape = request.sandbox.shapeName
267
275
  sid = request.sandbox.id
268
276
  if sid:
269
277
  logger.info(f"Using existing sandbox with id: {sid}")
270
- sandbox_pb = await self._get_sandbox_pb(sid) # if not exist raise NotFound
271
- platform_str = sandbox_pb.os
278
+ sandbox_pb = await self._get_sandbox_pb(sid, lybic_auth) # if not exist raise NotFound
279
+ platform_str = platform_map.get(sandbox_pb.os, platform.system())
272
280
  else:
273
- sandbox_pb = await self._create_sandbox(shape)
274
- sid, platform_str = sandbox_pb.id, sandbox_pb.os
281
+ sandbox_pb = await self._create_sandbox(shape, lybic_auth)
282
+ sid, platform_str = sandbox_pb.id, platform_map.get(sandbox_pb.os, platform.system())
275
283
 
276
284
  if request.sandbox.os != agent_pb2.SandboxOS.OSUNDEFINED:
277
285
  platform_str = platform_map.get(request.sandbox.os, platform.system())
278
286
  else:
279
- sandbox_pb = await self._create_sandbox(shape)
280
- sid, platform_str = sandbox_pb.id, sandbox_pb.os
287
+ sandbox_pb = await self._create_sandbox(shape, lybic_auth)
288
+ sid, platform_str = sandbox_pb.id, platform_map.get(sandbox_pb.os, platform.system())
281
289
  else:
282
290
  if request.HasField("sandbox") and request.sandbox.os != agent_pb2.SandboxOS.OSUNDEFINED:
283
291
  platform_str = platform_map.get(request.sandbox.os, platform.system())
@@ -457,6 +465,16 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
457
465
  logger.info(f"RunAgentInstruction stream for {task_id} cancelled by client.")
458
466
  if task_future:
459
467
  task_future.cancel()
468
+ # Set cancellation flag in global state for agents to check
469
+ try:
470
+ global_state: GlobalState = Registry.get_from_context("GlobalStateStore", task_id)
471
+ if global_state:
472
+ global_state.set_running_state("cancelled")
473
+ logger.info(f"Set running state to 'cancelled' for task {task_id} due to client disconnect.")
474
+ else:
475
+ logger.warning(f"Could not find GlobalState for task {task_id} to set cancellation flag on client disconnect.")
476
+ except Exception as e:
477
+ logger.error(f"Error setting cancellation flag for task {task_id} on client disconnect: {e}")
460
478
  except Exception as e:
461
479
  logger.exception(f"Error in RunAgentInstruction stream for task {task_id}")
462
480
  context.set_code(grpc.StatusCode.INTERNAL)
@@ -502,6 +520,9 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
502
520
  "max_steps": max_steps,
503
521
  "sandbox": backend_kwargs["sandbox"],
504
522
  }
523
+ # This property is used to pass sandbox information.
524
+ # It has now completed its mission and needs to be deleted, otherwise other backends may crash.
525
+ del backend_kwargs["sandbox"]
505
526
 
506
527
  # Start the task in background
507
528
  task_future = asyncio.create_task(self._run_task(task_id, backend_kwargs))
@@ -542,6 +563,7 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
542
563
  "running": agent_pb2.TaskStatus.RUNNING,
543
564
  "fulfilled": agent_pb2.TaskStatus.SUCCESS,
544
565
  "rejected": agent_pb2.TaskStatus.FAILURE,
566
+ "cancelled": agent_pb2.TaskStatus.CANCELLED,
545
567
  }
546
568
 
547
569
  if status == "finished":
@@ -552,6 +574,10 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
552
574
  task_status = agent_pb2.TaskStatus.FAILURE
553
575
  message = "Task failed with an exception."
554
576
  result = ""
577
+ elif status == "cancelled":
578
+ task_status = agent_pb2.TaskStatus.CANCELLED
579
+ message = "Task was cancelled by user request."
580
+ result = ""
555
581
  else: # pending or running
556
582
  task_status = status_map.get(status, agent_pb2.TaskStatus.TASKSTATUSUNDEFINED)
557
583
  message = "Task is running."
@@ -565,6 +591,82 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
565
591
  sandbox=task_info["sandbox"]
566
592
  )
567
593
 
594
+ async def CancelTask(self, request, context):
595
+ """
596
+ Cancel a running task by its taskId.
597
+
598
+ If the task exists and is running, it will be cancelled and a success response is returned.
599
+ If the task is not found or already completed, an appropriate response is returned.
600
+
601
+ Parameters:
602
+ request: CancelTaskRequest containing the taskId to cancel
603
+ context: gRPC context for setting status codes and details
604
+
605
+ Returns:
606
+ CancelTaskResponse: Response containing taskId, success status, and message
607
+ """
608
+ task_id = request.taskId
609
+ logger.info(f"Received CancelTask request for taskId: {task_id}")
610
+
611
+ async with self.task_lock:
612
+ task_info = self.tasks.get(task_id)
613
+
614
+ if not task_info:
615
+ return agent_pb2.CancelTaskResponse(
616
+ taskId=task_id,
617
+ success=False,
618
+ message=f"Task with ID {task_id} not found."
619
+ )
620
+
621
+ status = task_info["status"]
622
+ task_future = task_info.get("future")
623
+
624
+ # Check if task can be cancelled
625
+ if status in ["finished", "error"]:
626
+ return agent_pb2.CancelTaskResponse(
627
+ taskId=task_id,
628
+ success=False,
629
+ message=f"Task {task_id} is already {status} and cannot be cancelled."
630
+ )
631
+ elif status == "cancelled":
632
+ return agent_pb2.CancelTaskResponse(
633
+ taskId=task_id,
634
+ success=True,
635
+ message=f"Task {task_id} was already cancelled."
636
+ )
637
+ elif status in ["pending", "running"] and task_future:
638
+ try:
639
+ # Cancel the task future
640
+ task_future.cancel()
641
+ task_info["status"] = "cancelled"
642
+
643
+ # Set cancellation flag in global state for agents to check
644
+ global_state: GlobalState = Registry.get_from_context("GlobalStateStore", task_id) # type: ignore
645
+ global_state.set_running_state("cancelled")
646
+
647
+ # Send cancellation message through stream manager
648
+ await stream_manager.add_message(task_id, "cancelled", "Task was cancelled by user request")
649
+
650
+ logger.info(f"Task {task_id} successfully cancelled")
651
+ return agent_pb2.CancelTaskResponse(
652
+ taskId=task_id,
653
+ success=True,
654
+ message=f"Task {task_id} has been successfully cancelled."
655
+ )
656
+ except Exception as e:
657
+ logger.error(f"Failed to cancel task {task_id}: {e}")
658
+ return agent_pb2.CancelTaskResponse(
659
+ taskId=task_id,
660
+ success=False,
661
+ message=f"Failed to cancel task {task_id}: {e}"
662
+ )
663
+ else:
664
+ return agent_pb2.CancelTaskResponse(
665
+ taskId=task_id,
666
+ success=False,
667
+ message=f"Task {task_id} is in state '{status}' and cannot be cancelled."
668
+ )
669
+
568
670
  def _mask_config_secrets(self, config: CommonConfig) -> CommonConfig:
569
671
  """
570
672
  Return a deep copy of a CommonConfig with sensitive API keys replaced by "********".
@@ -667,20 +769,17 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
667
769
  context.set_details(f"Config for task {request.id} not found.")
668
770
  return agent_pb2.CommonConfig()
669
771
 
670
- async def _new_lybic_client(self):
772
+ def _new_lybic_client(self, lybic_auth: LybicAuth) -> LybicClient:
671
773
  """
672
- Create and store a new LybicClient using the servicer's current LybicAuth.
673
-
674
- This replaces the servicer's `lybic_client` attribute with a newly constructed
675
- LybicClient initialized from `self.lybic_auth`.
774
+ Create and return a new LybicClient.
676
775
  """
677
- self.lybic_client = LybicClient(self.lybic_auth)
776
+ return LybicClient(lybic_auth)
678
777
 
679
778
  async def SetGlobalCommonConfig(self, request, context):
680
779
  """
681
- Set the server's global common configuration and initialize Lybic authorization if provided.
780
+ Set the server's global common configuration.
682
781
 
683
- Sets request.commonConfig.id to "global" and stores it as the servicer's global_common_config. If the provided config contains authorizationInfo, initializes or updates self.lybic_auth with the org ID, API key, and API endpoint (defaulting to "https://api.lybic.cn/" when endpoint is empty).
782
+ Sets request.commonConfig.id to "global" and stores it as the servicer's global_common_config.
684
783
 
685
784
  Parameters:
686
785
  request: gRPC request containing `commonConfig` to apply.
@@ -688,17 +787,14 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
688
787
  Returns:
689
788
  agent_pb2.SetCommonConfigResponse: Response with `success=True` and the configuration `id`.
690
789
  """
790
+ if os.environ.get("ALLOW_SET_GLOBAL_CONFIG", "0")=="0":
791
+ context.set_code(grpc.StatusCode.PERMISSION_DENIED)
792
+ context.set_details("Permission denied.")
793
+ return agent_pb2.SetCommonConfigResponse()
691
794
  logger.info("Setting new global common config.")
692
795
  request.commonConfig.id = "global"
693
796
  self.global_common_config = request.commonConfig
694
797
 
695
- if self.global_common_config.HasField("authorizationInfo"): # lybic
696
- self.lybic_auth = LybicAuth(
697
- org_id=self.global_common_config.authorizationInfo.orgID,
698
- api_key=self.global_common_config.authorizationInfo.apiKey,
699
- endpoint=self.global_common_config.authorizationInfo.apiEndpoint or "https://api.lybic.cn/"
700
- )
701
-
702
798
  return agent_pb2.SetCommonConfigResponse(success=True, id=self.global_common_config.id)
703
799
 
704
800
  async def SetGlobalCommonLLMConfig(self, request, context):
@@ -710,6 +806,10 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
710
806
  Returns:
711
807
  llmConfig: The `LLMConfig` message that was stored in the global configuration.
712
808
  """
809
+ if os.environ.get("ALLOW_SET_GLOBAL_CONFIG", "0")=="0":
810
+ context.set_code(grpc.StatusCode.PERMISSION_DENIED)
811
+ context.set_details("Permission denied.")
812
+ return agent_pb2.LLMConfig()
713
813
  if not self.global_common_config.HasField("stageModelConfig"):
714
814
  self.global_common_config.stageModelConfig.SetInParent()
715
815
  self.global_common_config.stageModelConfig.actionGeneratorModel.CopyFrom(request.llmConfig)
@@ -730,6 +830,10 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
730
830
  Returns:
731
831
  LLMConfig: The `llmConfig` that was applied.
732
832
  """
833
+ if os.environ.get("ALLOW_SET_GLOBAL_CONFIG", "0")=="0":
834
+ context.set_code(grpc.StatusCode.PERMISSION_DENIED)
835
+ context.set_details("Permission denied.")
836
+ return agent_pb2.LLMConfig()
733
837
  if not self.global_common_config.HasField("stageModelConfig"):
734
838
  self.global_common_config.stageModelConfig.SetInParent()
735
839
  self.global_common_config.stageModelConfig.groundingModel.CopyFrom(request.llmConfig)
@@ -746,58 +850,48 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
746
850
  Returns:
747
851
  The `llmConfig` that was set as the global embedding model.
748
852
  """
853
+ if os.environ.get("ALLOW_SET_GLOBAL_CONFIG", "0")=="0":
854
+ context.set_code(grpc.StatusCode.PERMISSION_DENIED)
855
+ context.set_details("Permission denied.")
856
+ return agent_pb2.LLMConfig()
749
857
  if not self.global_common_config.HasField("stageModelConfig"):
750
858
  self.global_common_config.stageModelConfig.SetInParent()
751
859
  self.global_common_config.stageModelConfig.embeddingModel.CopyFrom(request.llmConfig)
752
860
  logger.info(f"Global embedding LLM config updated to: {request.llmConfig.modelName}")
753
861
  return request.llmConfig
754
862
 
755
- async def _create_sandbox(self,shape:str)->agent_pb2.Sandbox:
863
+ async def _create_sandbox(self, shape: str, lybic_auth: LybicAuth) -> agent_pb2.Sandbox:
756
864
  """
757
865
  Create a sandbox with the given shape via the Lybic service and return its identifier and operating system.
758
-
866
+
759
867
  Parameters:
760
868
  shape (str): The sandbox shape to create (provider-specific size/OS configuration).
761
-
869
+ lybic_auth (LybicAuth): The authentication object for Lybic.
870
+
762
871
  Returns:
763
- tuple: A pair (sandbox_id, platform_os) where `sandbox_id` is the created sandbox identifier and `platform_os` is the sandbox operating system string.
764
-
765
- Raises:
766
- Exception: If Lybic authorization is not initialized (call SetGlobalCommonConfig first).
872
+ agent_pb2.Sandbox: A protobuf message containing sandbox details.
767
873
  """
768
- if not self.lybic_auth:
769
- raise Exception("Lybic client not initialized. Please call SetGlobalCommonConfig before")
770
-
771
- await self._new_lybic_client()
772
- if not self.sandbox:
773
- self.sandbox = Sandbox(self.lybic_client)
774
- result = await self.sandbox.create(shape=shape)
775
- sandbox = await self.sandbox.get(result.id)
874
+ lybic_client = self._new_lybic_client(lybic_auth)
875
+ sandbox_service = Sandbox(lybic_client)
876
+ result = await sandbox_service.create(shape=shape)
877
+ sandbox = await sandbox_service.get(result.id)
878
+ await lybic_client.close()
776
879
 
777
880
  return agent_pb2.Sandbox(
778
881
  id=sandbox.sandbox.id,
779
- os=sandbox.sandbox.shape.os.upper(),
882
+ os=self._lybic_sandbox_os_to_pb_enum(sandbox.sandbox.shape),
780
883
  shapeName=sandbox.sandbox.shapeName,
781
884
  hardwareAcceleratedEncoding=sandbox.sandbox.shape.hardwareAcceleratedEncoding,
782
885
  virtualization=sandbox.sandbox.shape.virtualization,
783
886
  architecture=sandbox.sandbox.shape.architecture,
784
887
  )
785
888
 
786
- async def _get_sandbox_pb(self, sid: str) -> agent_pb2.Sandbox:
889
+ @staticmethod
890
+ def _lybic_sandbox_os_to_pb_enum(os) -> agent_pb2.SandboxOS:
787
891
  """
788
- Retrieves sandbox details for a given sandbox ID and returns them as a protobuf message.
892
+ Converts a sandbox OS string to an enum value.
789
893
  """
790
- if not self.lybic_auth:
791
- raise ValueError("Lybic client not initialized. Please call SetGlobalCommonConfig before")
792
-
793
- if not self.lybic_client:
794
- await self._new_lybic_client()
795
- if not self.sandbox:
796
- self.sandbox = Sandbox(self.lybic_client)
797
-
798
- sandbox_details = await self.sandbox.get(sid)
799
-
800
- os_raw = getattr(sandbox_details.sandbox.shape, "os", "") or ""
894
+ os_raw = getattr(os, "os", "") or ""
801
895
  os_upper = str(os_raw).upper()
802
896
  if "WIN" in os_upper:
803
897
  os_enum = agent_pb2.SandboxOS.WINDOWS
@@ -807,10 +901,23 @@ class AgentServicer(agent_pb2_grpc.AgentServicer):
807
901
  os_enum = agent_pb2.SandboxOS.ANDROID
808
902
  else:
809
903
  os_enum = agent_pb2.SandboxOS.OSUNDEFINED
904
+ return os_enum
905
+
906
+ async def _get_sandbox_pb(self, sid: str, lybic_auth: LybicAuth) -> agent_pb2.Sandbox:
907
+ """
908
+ Retrieves sandbox details for a given sandbox ID and returns them as a protobuf message.
909
+ """
910
+ if not lybic_auth:
911
+ raise ValueError("Lybic client not initialized. Please call SetGlobalCommonConfig before")
912
+
913
+ lybic_client = self._new_lybic_client(lybic_auth)
914
+ sandbox_service = Sandbox(lybic_client)
915
+ sandbox_details = await sandbox_service.get(sid)
916
+ await lybic_client.close()
810
917
 
811
918
  return agent_pb2.Sandbox(
812
919
  id=sandbox_details.sandbox.id,
813
- os=os_enum,
920
+ os=self._lybic_sandbox_os_to_pb_enum(sandbox_details.sandbox.shape),
814
921
  shapeName=sandbox_details.sandbox.shapeName,
815
922
  hardwareAcceleratedEncoding=sandbox_details.sandbox.shape.hardwareAcceleratedEncoding,
816
923
  virtualization=sandbox_details.sandbox.shape.virtualization,
@@ -127,4 +127,4 @@ class Registry:
127
127
  pass # Fall back to global registry
128
128
 
129
129
  # Fall back to global registry for CLI mode or if not in task registry
130
- return cls.get(name)
130
+ return cls.get(name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lybic-guiagents
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: An open-source agentic framework that enables AI to use computers like humans and can provide a multi-agent runtime environment as an infrastructure capability
5
5
  Author: Lybic Development Team
6
6
  Author-email: Lybic Development Team <lybic@tingyutech.com>
@@ -3,7 +3,7 @@ requires = ["setuptools >= 77.0.3"]
3
3
  build-backend = "setuptools.build_meta"
4
4
  [project]
5
5
  name = "lybic-guiagents"
6
- version = "0.4.0"
6
+ version = "0.5.0"
7
7
  authors = [
8
8
  { name="Lybic Development Team", email="lybic@tingyutech.com" },
9
9
  ]
File without changes