oagi-core 0.10.0__tar.gz → 0.10.2__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.
Files changed (111) hide show
  1. {oagi_core-0.10.0 → oagi_core-0.10.2}/PKG-INFO +2 -1
  2. {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/tasker_agent_example.py +1 -7
  3. {oagi_core-0.10.0 → oagi_core-0.10.2}/metapackage/pyproject.toml +2 -2
  4. {oagi_core-0.10.0 → oagi_core-0.10.2}/metapackage/uv.lock +47 -5
  5. {oagi_core-0.10.0 → oagi_core-0.10.2}/pyproject.toml +2 -1
  6. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/default.py +7 -0
  7. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/factories.py +6 -0
  8. oagi_core-0.10.2/src/oagi/agent/observer/exporters.py +336 -0
  9. oagi_core-0.10.2/src/oagi/agent/observer/report_template.html +455 -0
  10. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/__init__.py +0 -2
  11. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/memory.py +3 -27
  12. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/models.py +0 -7
  13. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/planner.py +2 -11
  14. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/taskee_agent.py +8 -0
  15. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/tasker/tasker_agent.py +7 -17
  16. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/agent.py +108 -1
  17. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/async_.py +0 -3
  18. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/base.py +0 -4
  19. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/sync.py +0 -3
  20. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/pyautogui_action_handler.py +20 -24
  21. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/socketio_server.py +20 -19
  22. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/__init__.py +12 -1
  23. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/__init__.py +10 -1
  24. oagi_core-0.10.2/src/oagi/types/models/action.py +84 -0
  25. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/client.py +7 -3
  26. oagi_core-0.10.2/tests/test_action_parsing.py +98 -0
  27. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_observer.py +12 -9
  28. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_planner.py +5 -6
  29. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_planner_memory.py +0 -13
  30. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_taskee_agent.py +5 -5
  31. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_tasker_agent.py +10 -23
  32. {oagi_core-0.10.0 → oagi_core-0.10.2}/uv.lock +71 -28
  33. oagi_core-0.10.0/src/oagi/agent/observer/exporters.py +0 -445
  34. oagi_core-0.10.0/src/oagi/types/models/action.py +0 -33
  35. {oagi_core-0.10.0 → oagi_core-0.10.2}/.github/workflows/ci.yml +0 -0
  36. {oagi_core-0.10.0 → oagi_core-0.10.2}/.github/workflows/release.yml +0 -0
  37. {oagi_core-0.10.0 → oagi_core-0.10.2}/.gitignore +0 -0
  38. {oagi_core-0.10.0 → oagi_core-0.10.2}/.python-version +0 -0
  39. {oagi_core-0.10.0 → oagi_core-0.10.2}/CONTRIBUTING.md +0 -0
  40. {oagi_core-0.10.0 → oagi_core-0.10.2}/LICENSE +0 -0
  41. {oagi_core-0.10.0 → oagi_core-0.10.2}/Makefile +0 -0
  42. {oagi_core-0.10.0 → oagi_core-0.10.2}/README.md +0 -0
  43. {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/async_google_weather.py +0 -0
  44. {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/execute_task_auto.py +0 -0
  45. {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/execute_task_manual.py +0 -0
  46. {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/google_weather.py +0 -0
  47. {oagi_core-0.10.0 → oagi_core-0.10.2}/examples/screenshot_with_config.py +0 -0
  48. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/__init__.py +0 -0
  49. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/__init__.py +0 -0
  50. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/__init__.py +0 -0
  51. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/agent_observer.py +0 -0
  52. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/events.py +0 -0
  53. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/observer/protocol.py +0 -0
  54. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/protocol.py +0 -0
  55. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/agent/registry.py +0 -0
  56. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/__init__.py +0 -0
  57. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/display.py +0 -0
  58. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/main.py +0 -0
  59. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/server.py +0 -0
  60. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/tracking.py +0 -0
  61. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/cli/utils.py +0 -0
  62. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/client/__init__.py +0 -0
  63. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/exceptions.py +0 -0
  64. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/__init__.py +0 -0
  65. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/_macos.py +0 -0
  66. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/async_pyautogui_action_handler.py +0 -0
  67. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/async_screenshot_maker.py +0 -0
  68. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/pil_image.py +0 -0
  69. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/handler/screenshot_maker.py +0 -0
  70. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/logging.py +0 -0
  71. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/__init__.py +0 -0
  72. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/agent_wrappers.py +0 -0
  73. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/config.py +0 -0
  74. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/main.py +0 -0
  75. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/models.py +0 -0
  76. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/server/session_store.py +0 -0
  77. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/__init__.py +0 -0
  78. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/async_.py +0 -0
  79. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/async_short.py +0 -0
  80. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/base.py +0 -0
  81. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/short.py +0 -0
  82. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/task/sync.py +0 -0
  83. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/action_handler.py +0 -0
  84. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/async_action_handler.py +0 -0
  85. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/async_image_provider.py +0 -0
  86. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/image.py +0 -0
  87. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/image_provider.py +0 -0
  88. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/image_config.py +0 -0
  89. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/models/step.py +0 -0
  90. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/step_observer.py +0 -0
  91. {oagi_core-0.10.0 → oagi_core-0.10.2}/src/oagi/types/url.py +0 -0
  92. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/__init__.py +0 -0
  93. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/conftest.py +0 -0
  94. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_actor.py +0 -0
  95. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_agent/test_agent_wrappers.py +0 -0
  96. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_agent/test_default_agent.py +0 -0
  97. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_agent_registry.py +0 -0
  98. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_async_actor.py +0 -0
  99. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_async_client.py +0 -0
  100. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_async_handlers.py +0 -0
  101. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_cli.py +0 -0
  102. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_logging.py +0 -0
  103. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_mac_double_click.py +0 -0
  104. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_pil_image.py +0 -0
  105. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_pyautogui_action_handler.py +0 -0
  106. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_screenshot_maker.py +0 -0
  107. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/__init__.py +0 -0
  108. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/test_config.py +0 -0
  109. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/test_session_store.py +0 -0
  110. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_server/test_socketio_integration.py +0 -0
  111. {oagi_core-0.10.0 → oagi_core-0.10.2}/tests/test_sync_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oagi-core
3
- Version: 0.10.0
3
+ Version: 0.10.2
4
4
  Summary: Official API of OpenAGI Foundation
5
5
  Project-URL: Homepage, https://github.com/agiopen-org/oagi
6
6
  Author-email: OpenAGI Foundation <contact@agiopen.org>
@@ -32,6 +32,7 @@ Requires-Dist: rich>=13.0.0
32
32
  Provides-Extra: desktop
33
33
  Requires-Dist: pillow>=11.3.0; extra == 'desktop'
34
34
  Requires-Dist: pyautogui>=0.9.54; extra == 'desktop'
35
+ Requires-Dist: pyobjc-framework-applicationservices>=9.0; (sys_platform == 'darwin') and extra == 'desktop'
35
36
  Requires-Dist: pyobjc-framework-quartz>=9.0; (sys_platform == 'darwin') and extra == 'desktop'
36
37
  Provides-Extra: server
37
38
  Requires-Dist: fastapi[standard]>=0.115.0; extra == 'server'
@@ -27,7 +27,7 @@ async def main():
27
27
  tasker = TaskerAgent(
28
28
  api_key=os.getenv("OAGI_API_KEY"),
29
29
  base_url=os.getenv("OAGI_BASE_URL", "https://api.agiopen.org"),
30
- model="lux-actor-1",
30
+ model="sft-bigs1-1027-s2-1113-mixoc-1107-32b",
31
31
  max_steps=30,
32
32
  temperature=0.5,
33
33
  step_observer=observer,
@@ -42,16 +42,10 @@ async def main():
42
42
  "Click on the official Python.org website link",
43
43
  ]
44
44
 
45
- # Define deliverables to achieve
46
- deliverables = [
47
- "Python.org website is opened",
48
- ]
49
-
50
45
  # Set the task
51
46
  tasker.set_task(
52
47
  task=task_description,
53
48
  todos=todos,
54
- deliverables=deliverables,
55
49
  )
56
50
 
57
51
  image_provider = AsyncScreenshotMaker()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "oagi"
7
- version = "0.10.0"
7
+ version = "0.10.2"
8
8
  description = "Official API of OpenAGI Foundation (metapackage with all features)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -16,7 +16,7 @@ authors = [
16
16
  requires-python = ">= 3.10"
17
17
 
18
18
  dependencies = [
19
- "oagi-core[desktop,server]==0.10.0",
19
+ "oagi-core[desktop,server]==0.10.2",
20
20
  ]
21
21
 
22
22
  [project.urls]
@@ -397,33 +397,34 @@ sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6
397
397
 
398
398
  [[package]]
399
399
  name = "oagi"
400
- version = "0.10.0"
400
+ version = "0.10.2"
401
401
  source = { editable = "." }
402
402
  dependencies = [
403
403
  { name = "oagi-core", extra = ["desktop", "server"] },
404
404
  ]
405
405
 
406
406
  [package.metadata]
407
- requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.9.2" }]
407
+ requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.10.1" }]
408
408
 
409
409
  [[package]]
410
410
  name = "oagi-core"
411
- version = "0.9.2"
411
+ version = "0.10.1"
412
412
  source = { registry = "https://pypi.org/simple" }
413
413
  dependencies = [
414
414
  { name = "httpx" },
415
415
  { name = "pydantic" },
416
416
  { name = "rich" },
417
417
  ]
418
- sdist = { url = "https://files.pythonhosted.org/packages/4b/68/382a3096645de041f252f5ee8f01d02439f5a419476ab8e84fec638c3592/oagi_core-0.9.2.tar.gz", hash = "sha256:eb18c49f364561c94a26b09c98b855225ac19c19a456170e7a69c1467166f4e5", size = 253368, upload-time = "2025-11-19T04:29:14.85Z" }
418
+ sdist = { url = "https://files.pythonhosted.org/packages/9c/2d/83e5e2efe2591858e2aceaed4329d995c78cc9602b6a9fd9331096559351/oagi_core-0.10.1.tar.gz", hash = "sha256:264b88fb5b1e24f7284af3e82243d846817f6b1d0be108280491383e671e5b8d", size = 261938, upload-time = "2025-11-25T13:19:31.714Z" }
419
419
  wheels = [
420
- { url = "https://files.pythonhosted.org/packages/56/15/f6e55511b78622fea2265eb9137d9c03ebbac102b66ebbf49f26bcd713ab/oagi_core-0.9.2-py3-none-any.whl", hash = "sha256:6f14abeae8338f1703fd30ff7e7ae7316be12c025cb3b30307dfb2d35efbf4fc", size = 75648, upload-time = "2025-11-19T04:29:13.961Z" },
420
+ { url = "https://files.pythonhosted.org/packages/54/bb/3242e763f93360eeb6a5a5b1e61845cd3e27a485c442aeb9a918f6e4f3f6/oagi_core-0.10.1-py3-none-any.whl", hash = "sha256:5438e4310fab1aee3ffbe3d43963f04fc10d3747d24897b89f34df850bb76f89", size = 83830, upload-time = "2025-11-25T13:19:30.237Z" },
421
421
  ]
422
422
 
423
423
  [package.optional-dependencies]
424
424
  desktop = [
425
425
  { name = "pillow" },
426
426
  { name = "pyautogui" },
427
+ { name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" },
427
428
  { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
428
429
  ]
429
430
  server = [
@@ -741,6 +742,27 @@ wheels = [
741
742
  { url = "https://files.pythonhosted.org/packages/62/50/dc076965c96c7f0de25c0a32b7f8aa98133ed244deaeeacfc758783f1f30/pyobjc_core-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:453b191df1a4b80e756445b935491b974714456ae2cbae816840bd96f86db882", size = 712204, upload-time = "2025-11-14T09:35:24.148Z" },
742
743
  ]
743
744
 
745
+ [[package]]
746
+ name = "pyobjc-framework-applicationservices"
747
+ version = "12.1"
748
+ source = { registry = "https://pypi.org/simple" }
749
+ dependencies = [
750
+ { name = "pyobjc-core" },
751
+ { name = "pyobjc-framework-cocoa" },
752
+ { name = "pyobjc-framework-coretext" },
753
+ { name = "pyobjc-framework-quartz" },
754
+ ]
755
+ sdist = { url = "https://files.pythonhosted.org/packages/be/6a/d4e613c8e926a5744fc47a9e9fea08384a510dc4f27d844f7ad7a2d793bd/pyobjc_framework_applicationservices-12.1.tar.gz", hash = "sha256:c06abb74f119bc27aeb41bf1aef8102c0ae1288aec1ac8665ea186a067a8945b", size = 103247, upload-time = "2025-11-14T10:08:52.18Z" }
756
+ wheels = [
757
+ { url = "https://files.pythonhosted.org/packages/52/9d/3cf36e7b08832e71f5d48ddfa1047865cf2dfc53df8c0f2a82843ea9507a/pyobjc_framework_applicationservices-12.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c4fd1b008757182b9e2603a63c6ffa930cc412fab47294ec64260ab3f8ec695d", size = 32791, upload-time = "2025-11-14T09:36:05.576Z" },
758
+ { url = "https://files.pythonhosted.org/packages/17/86/d07eff705ff909a0ffa96d14fc14026e9fc9dd716233648c53dfd5056b8e/pyobjc_framework_applicationservices-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bdddd492eeac6d14ff2f5bd342aba29e30dffa72a2d358c08444da22129890e2", size = 32784, upload-time = "2025-11-14T09:36:08.755Z" },
759
+ { url = "https://files.pythonhosted.org/packages/37/a7/55fa88def5c02732c4b747606ff1cbce6e1f890734bbd00f5596b21eaa02/pyobjc_framework_applicationservices-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c8f6e2fb3b3e9214ab4864ef04eee18f592b46a986c86ea0113448b310520532", size = 32835, upload-time = "2025-11-14T09:36:11.855Z" },
760
+ { url = "https://files.pythonhosted.org/packages/fc/21/79e42ee836f1010f5fe9e97d2817a006736bd287c15a3674c399190a2e77/pyobjc_framework_applicationservices-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bd1f4dbb38234a24ae6819f5e22485cf7dd3dd4074ff3bf9a9fdb4c01a3b4a38", size = 32859, upload-time = "2025-11-14T09:36:15.208Z" },
761
+ { url = "https://files.pythonhosted.org/packages/66/3a/0f1d4dcf2345e875e5ea9761d5a70969e241d24089133d21f008dde596f5/pyobjc_framework_applicationservices-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8a5d2845249b6a85ba9e320a9848468c3f8cd6f59605a9a43f406a7810eaa830", size = 33115, upload-time = "2025-11-14T09:36:18.384Z" },
762
+ { url = "https://files.pythonhosted.org/packages/40/44/3196b40fec68b4413c92875311f17ccf4c3ff7d2e53676f8fc18ad29bd18/pyobjc_framework_applicationservices-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f43c9a24ad97a9121276d4d571aa04a924282c80d7291cfb3b29839c3e2013a8", size = 32997, upload-time = "2025-11-14T09:36:21.58Z" },
763
+ { url = "https://files.pythonhosted.org/packages/fd/bb/dab21d2210d3ef7dd0616df7e8ea89b5d8d62444133a25f76e649a947168/pyobjc_framework_applicationservices-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1f72e20009a4ebfd5ed5b23dc11c1528ad6b55cc63ee71952ddb2a5e5f1cb7da", size = 33238, upload-time = "2025-11-14T09:36:24.751Z" },
764
+ ]
765
+
744
766
  [[package]]
745
767
  name = "pyobjc-framework-cocoa"
746
768
  version = "12.1"
@@ -759,6 +781,26 @@ wheels = [
759
781
  { url = "https://files.pythonhosted.org/packages/58/27/b457b7b37089cad692c8aada90119162dfb4c4a16f513b79a8b2b022b33b/pyobjc_framework_cocoa-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6ba1dc1bfa4da42d04e93d2363491275fb2e2be5c20790e561c8a9e09b8cf2cc", size = 388970, upload-time = "2025-11-14T09:42:53.964Z" },
760
782
  ]
761
783
 
784
+ [[package]]
785
+ name = "pyobjc-framework-coretext"
786
+ version = "12.1"
787
+ source = { registry = "https://pypi.org/simple" }
788
+ dependencies = [
789
+ { name = "pyobjc-core" },
790
+ { name = "pyobjc-framework-cocoa" },
791
+ { name = "pyobjc-framework-quartz" },
792
+ ]
793
+ sdist = { url = "https://files.pythonhosted.org/packages/29/da/682c9c92a39f713bd3c56e7375fa8f1b10ad558ecb075258ab6f1cdd4a6d/pyobjc_framework_coretext-12.1.tar.gz", hash = "sha256:e0adb717738fae395dc645c9e8a10bb5f6a4277e73cba8fa2a57f3b518e71da5", size = 90124, upload-time = "2025-11-14T10:14:38.596Z" }
794
+ wheels = [
795
+ { url = "https://files.pythonhosted.org/packages/27/1c/ddecc72a672d681476c668bcedcfb8ade16383c028eac566ac7458fb91ef/pyobjc_framework_coretext-12.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1c8315dcef6699c2953461d97117fe81402f7c29cff36d2950dacce028a362fd", size = 29987, upload-time = "2025-11-14T09:46:58.028Z" },
796
+ { url = "https://files.pythonhosted.org/packages/f0/81/7b8efc41e743adfa2d74b92dec263c91bcebfb188d2a8f5eea1886a195ff/pyobjc_framework_coretext-12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f6742ba5b0bb7629c345e99eff928fbfd9e9d3d667421ac1a2a43bdb7ba9833", size = 29990, upload-time = "2025-11-14T09:47:01.206Z" },
797
+ { url = "https://files.pythonhosted.org/packages/cd/0f/ddf45bf0e3ba4fbdc7772de4728fd97ffc34a0b5a15e1ab1115b202fe4ae/pyobjc_framework_coretext-12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d246fa654bdbf43bae3969887d58f0b336c29b795ad55a54eb76397d0e62b93c", size = 30108, upload-time = "2025-11-14T09:47:04.228Z" },
798
+ { url = "https://files.pythonhosted.org/packages/20/a2/a3974e3e807c68e23a9d7db66fc38ac54f7ecd2b7a9237042006699a76e1/pyobjc_framework_coretext-12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7cbb2c28580e6704ce10b9a991ccd9563a22b3a75f67c36cf612544bd8b21b5f", size = 30110, upload-time = "2025-11-14T09:47:07.518Z" },
799
+ { url = "https://files.pythonhosted.org/packages/0f/5d/85e059349e9cfbd57269a1f11f56747b3ff5799a3bcbd95485f363c623d8/pyobjc_framework_coretext-12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:14100d1e39efb30f57869671fb6fce8d668f80c82e25e7930fb364866e5c0dab", size = 30697, upload-time = "2025-11-14T09:47:10.932Z" },
800
+ { url = "https://files.pythonhosted.org/packages/ef/c3/adf9d306e9ead108167ab7a974ab7d171dbacf31c72fad63e12585f58023/pyobjc_framework_coretext-12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:782a1a9617ea267c05226e9cd81a8dec529969a607fe1e037541ee1feb9524e9", size = 30095, upload-time = "2025-11-14T09:47:13.893Z" },
801
+ { url = "https://files.pythonhosted.org/packages/bd/ca/6321295f47a47b0fca7de7e751ddc0ddc360413f4e506335fe9b0f0fb085/pyobjc_framework_coretext-12.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:7afe379c5a870fa3e66e6f65231c3c1732d9ccd2cd2a4904b2cd5178c9e3c562", size = 30702, upload-time = "2025-11-14T09:47:17.292Z" },
802
+ ]
803
+
762
804
  [[package]]
763
805
  name = "pyobjc-framework-quartz"
764
806
  version = "12.1"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "oagi-core"
7
- version = "0.10.0"
7
+ version = "0.10.2"
8
8
  description = "Official API of OpenAGI Foundation"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -31,6 +31,7 @@ desktop = [
31
31
  "pillow>=11.3.0",
32
32
  "pyautogui>=0.9.54",
33
33
  "pyobjc-framework-Quartz>=9.0; sys_platform == 'darwin'",
34
+ "pyobjc-framework-ApplicationServices>=9.0; sys_platform == 'darwin'",
34
35
  ]
35
36
  server = [
36
37
  "fastapi[standard]>=0.115.0",
@@ -6,6 +6,7 @@
6
6
  # Licensed under the MIT License.
7
7
  # -----------------------------------------------------------------------------
8
8
 
9
+ import asyncio
9
10
  import logging
10
11
 
11
12
  from .. import AsyncActor
@@ -39,6 +40,7 @@ class AsyncDefaultAgent:
39
40
  max_steps: int = 20,
40
41
  temperature: float | None = 0.5,
41
42
  step_observer: AsyncObserver | None = None,
43
+ step_delay: float = 0.3,
42
44
  ):
43
45
  self.api_key = api_key
44
46
  self.base_url = base_url
@@ -46,6 +48,7 @@ class AsyncDefaultAgent:
46
48
  self.max_steps = max_steps
47
49
  self.temperature = temperature
48
50
  self.step_observer = step_observer
51
+ self.step_delay = step_delay
49
52
 
50
53
  async def execute(
51
54
  self,
@@ -113,6 +116,10 @@ class AsyncDefaultAgent:
113
116
  )
114
117
  )
115
118
 
119
+ # Wait after actions before next screenshot
120
+ if self.step_delay > 0:
121
+ await asyncio.sleep(self.step_delay)
122
+
116
123
  # Check if task is complete
117
124
  if step.stop:
118
125
  logger.info(f"Task completed successfully after {step_num} steps")
@@ -21,6 +21,7 @@ def create_default_agent(
21
21
  max_steps: int = 20,
22
22
  temperature: float = 0.1,
23
23
  step_observer: AsyncStepObserver | None = None,
24
+ step_delay: float = 0.3,
24
25
  ) -> AsyncAgent:
25
26
  return AsyncDefaultAgent(
26
27
  api_key=api_key,
@@ -29,6 +30,7 @@ def create_default_agent(
29
30
  max_steps=max_steps,
30
31
  temperature=temperature,
31
32
  step_observer=step_observer,
33
+ step_delay=step_delay,
32
34
  )
33
35
 
34
36
 
@@ -40,6 +42,7 @@ def create_thinker_agent(
40
42
  max_steps: int = 100,
41
43
  temperature: float = 0.1,
42
44
  step_observer: AsyncStepObserver | None = None,
45
+ step_delay: float = 0.3,
43
46
  ) -> AsyncAgent:
44
47
  return AsyncDefaultAgent(
45
48
  api_key=api_key,
@@ -48,6 +51,7 @@ def create_thinker_agent(
48
51
  max_steps=max_steps,
49
52
  temperature=temperature,
50
53
  step_observer=step_observer,
54
+ step_delay=step_delay,
51
55
  )
52
56
 
53
57
 
@@ -60,6 +64,7 @@ def create_planner_agent(
60
64
  temperature: float = 0.1,
61
65
  reflection_interval: int = 20,
62
66
  step_observer: AsyncStepObserver | None = None,
67
+ step_delay: float = 0.3,
63
68
  ) -> AsyncAgent:
64
69
  tasker = TaskerAgent(
65
70
  api_key=api_key,
@@ -69,6 +74,7 @@ def create_planner_agent(
69
74
  temperature=temperature,
70
75
  reflection_interval=reflection_interval,
71
76
  step_observer=step_observer,
77
+ step_delay=step_delay,
72
78
  )
73
79
  # tasker.set_task()
74
80
  return tasker
@@ -0,0 +1,336 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) OpenAGI Foundation
3
+ # All rights reserved.
4
+ #
5
+ # This file is part of the official API project.
6
+ # Licensed under the MIT License.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ import base64
10
+ import json
11
+ from pathlib import Path
12
+
13
+ from ...types import (
14
+ Action,
15
+ ActionEvent,
16
+ ActionType,
17
+ ImageEvent,
18
+ LogEvent,
19
+ ObserverEvent,
20
+ PlanEvent,
21
+ SplitEvent,
22
+ StepEvent,
23
+ parse_coords,
24
+ parse_drag_coords,
25
+ parse_scroll,
26
+ )
27
+
28
+
29
+ def _parse_action_coords(action: Action) -> dict | None:
30
+ """Parse coordinates from action argument for cursor indicators.
31
+
32
+ Returns:
33
+ Dict with coordinates based on action type, or None if not applicable.
34
+ - Click types: {"type": "click", "x": int, "y": int}
35
+ - Drag: {"type": "drag", "x1": int, "y1": int, "x2": int, "y2": int}
36
+ - Scroll: {"type": "scroll", "x": int, "y": int, "direction": str}
37
+ """
38
+ arg = action.argument.strip("()")
39
+
40
+ match action.type:
41
+ case (
42
+ ActionType.CLICK
43
+ | ActionType.LEFT_DOUBLE
44
+ | ActionType.LEFT_TRIPLE
45
+ | ActionType.RIGHT_SINGLE
46
+ ):
47
+ coords = parse_coords(arg)
48
+ if coords:
49
+ return {"type": "click", "x": coords[0], "y": coords[1]}
50
+ case ActionType.DRAG:
51
+ coords = parse_drag_coords(arg)
52
+ if coords:
53
+ return {
54
+ "type": "drag",
55
+ "x1": coords[0],
56
+ "y1": coords[1],
57
+ "x2": coords[2],
58
+ "y2": coords[3],
59
+ }
60
+ case ActionType.SCROLL:
61
+ result = parse_scroll(arg)
62
+ if result:
63
+ return {
64
+ "type": "scroll",
65
+ "x": result[0],
66
+ "y": result[1],
67
+ "direction": result[2],
68
+ }
69
+ return None
70
+
71
+
72
+ def export_to_markdown(
73
+ events: list[ObserverEvent],
74
+ path: str,
75
+ images_dir: str | None = None,
76
+ ) -> None:
77
+ """Export events to a Markdown file.
78
+
79
+ Args:
80
+ events: List of events to export.
81
+ path: Path to the output Markdown file.
82
+ images_dir: Directory to save images. If None, images are not saved.
83
+ """
84
+ output_path = Path(path)
85
+ output_path.parent.mkdir(parents=True, exist_ok=True)
86
+
87
+ if images_dir:
88
+ images_path = Path(images_dir)
89
+ images_path.mkdir(parents=True, exist_ok=True)
90
+
91
+ lines: list[str] = ["# Agent Execution Report\n"]
92
+ image_counter = 0
93
+
94
+ for event in events:
95
+ timestamp = event.timestamp.strftime("%H:%M:%S")
96
+
97
+ match event:
98
+ case StepEvent():
99
+ lines.append(f"\n## Step {event.step_num}\n")
100
+ lines.append(f"**Time:** {timestamp}\n")
101
+
102
+ if isinstance(event.image, bytes):
103
+ if images_dir:
104
+ image_counter += 1
105
+ image_filename = f"step_{event.step_num}.png"
106
+ image_path = Path(images_dir) / image_filename
107
+ image_path.write_bytes(event.image)
108
+ rel_path = Path(images_dir).name / Path(image_filename)
109
+ lines.append(f"\n![Step {event.step_num}]({rel_path})\n")
110
+ else:
111
+ lines.append(
112
+ f"\n*[Screenshot captured - {len(event.image)} bytes]*\n"
113
+ )
114
+ elif isinstance(event.image, str):
115
+ lines.append(f"\n**Screenshot URL:** {event.image}\n")
116
+
117
+ if event.step.reason:
118
+ lines.append(f"\n**Reasoning:**\n> {event.step.reason}\n")
119
+
120
+ if event.step.actions:
121
+ lines.append("\n**Planned Actions:**\n")
122
+ for action in event.step.actions:
123
+ count_str = (
124
+ f" (x{action.count})"
125
+ if action.count and action.count > 1
126
+ else ""
127
+ )
128
+ lines.append(
129
+ f"- `{action.type.value}`: {action.argument}{count_str}\n"
130
+ )
131
+
132
+ if event.step.stop:
133
+ lines.append("\n**Status:** Task Complete\n")
134
+
135
+ case ActionEvent():
136
+ lines.append(f"\n### Actions Executed ({timestamp})\n")
137
+ if event.error:
138
+ lines.append(f"\n**Error:** {event.error}\n")
139
+ else:
140
+ lines.append("\n**Result:** Success\n")
141
+
142
+ case LogEvent():
143
+ lines.append(f"\n> **Log ({timestamp}):** {event.message}\n")
144
+
145
+ case SplitEvent():
146
+ if event.label:
147
+ lines.append(f"\n---\n\n### {event.label}\n")
148
+ else:
149
+ lines.append("\n---\n")
150
+
151
+ case ImageEvent():
152
+ pass
153
+
154
+ case PlanEvent():
155
+ phase_titles = {
156
+ "initial": "Initial Planning",
157
+ "reflection": "Reflection",
158
+ "summary": "Summary",
159
+ }
160
+ phase_title = phase_titles.get(event.phase, event.phase.capitalize())
161
+ lines.append(f"\n### {phase_title} ({timestamp})\n")
162
+
163
+ if event.image:
164
+ if isinstance(event.image, bytes):
165
+ if images_dir:
166
+ image_counter += 1
167
+ image_filename = f"plan_{event.phase}_{image_counter}.png"
168
+ image_path = Path(images_dir) / image_filename
169
+ image_path.write_bytes(event.image)
170
+ rel_path = Path(images_dir).name / Path(image_filename)
171
+ lines.append(f"\n![{phase_title}]({rel_path})\n")
172
+ else:
173
+ lines.append(
174
+ f"\n*[Screenshot captured - {len(event.image)} bytes]*\n"
175
+ )
176
+ elif isinstance(event.image, str):
177
+ lines.append(f"\n**Screenshot URL:** {event.image}\n")
178
+
179
+ if event.reasoning:
180
+ lines.append(f"\n**Reasoning:**\n> {event.reasoning}\n")
181
+
182
+ if event.result:
183
+ lines.append(f"\n**Result:** {event.result}\n")
184
+
185
+ output_path.write_text("".join(lines))
186
+
187
+
188
+ def _convert_events_for_html(events: list[ObserverEvent]) -> list[dict]:
189
+ """Convert events to JSON-serializable format for HTML template."""
190
+ result = []
191
+
192
+ for event in events:
193
+ timestamp = event.timestamp.strftime("%H:%M:%S")
194
+
195
+ match event:
196
+ case StepEvent():
197
+ # Collect action coordinates for cursor indicators
198
+ action_coords = []
199
+ actions_list = []
200
+ if event.step.actions:
201
+ for action in event.step.actions:
202
+ coords = _parse_action_coords(action)
203
+ if coords:
204
+ action_coords.append(coords)
205
+ actions_list.append(
206
+ {
207
+ "type": action.type.value,
208
+ "argument": action.argument,
209
+ "count": action.count or 1,
210
+ }
211
+ )
212
+
213
+ # Handle image
214
+ image_data = None
215
+ if isinstance(event.image, bytes):
216
+ image_data = base64.b64encode(event.image).decode("utf-8")
217
+ elif isinstance(event.image, str):
218
+ image_data = event.image
219
+
220
+ result.append(
221
+ {
222
+ "event_type": "step",
223
+ "timestamp": timestamp,
224
+ "step_num": event.step_num,
225
+ "image": image_data,
226
+ "action_coords": action_coords,
227
+ "reason": event.step.reason,
228
+ "actions": actions_list,
229
+ "stop": event.step.stop,
230
+ }
231
+ )
232
+
233
+ case ActionEvent():
234
+ result.append(
235
+ {
236
+ "event_type": "action",
237
+ "timestamp": timestamp,
238
+ "error": event.error,
239
+ }
240
+ )
241
+
242
+ case LogEvent():
243
+ result.append(
244
+ {
245
+ "event_type": "log",
246
+ "timestamp": timestamp,
247
+ "message": event.message,
248
+ }
249
+ )
250
+
251
+ case SplitEvent():
252
+ result.append(
253
+ {
254
+ "event_type": "split",
255
+ "timestamp": timestamp,
256
+ "label": event.label,
257
+ }
258
+ )
259
+
260
+ case ImageEvent():
261
+ pass
262
+
263
+ case PlanEvent():
264
+ image_data = None
265
+ if isinstance(event.image, bytes):
266
+ image_data = base64.b64encode(event.image).decode("utf-8")
267
+ elif isinstance(event.image, str):
268
+ image_data = event.image
269
+
270
+ result.append(
271
+ {
272
+ "event_type": "plan",
273
+ "timestamp": timestamp,
274
+ "phase": event.phase,
275
+ "image": image_data,
276
+ "reasoning": event.reasoning,
277
+ "result": event.result,
278
+ }
279
+ )
280
+
281
+ return result
282
+
283
+
284
+ def export_to_html(events: list[ObserverEvent], path: str) -> None:
285
+ """Export events to a self-contained HTML file.
286
+
287
+ Args:
288
+ events: List of events to export.
289
+ path: Path to the output HTML file.
290
+ """
291
+ output_path = Path(path)
292
+ output_path.parent.mkdir(parents=True, exist_ok=True)
293
+
294
+ # Load template
295
+ template_path = Path(__file__).parent / "report_template.html"
296
+ template = template_path.read_text()
297
+
298
+ # Convert events to JSON
299
+ events_data = _convert_events_for_html(events)
300
+ events_json = json.dumps(events_data)
301
+
302
+ # Replace placeholder
303
+ html_content = template.replace("{EVENTS_DATA}", events_json)
304
+
305
+ output_path.write_text(html_content)
306
+
307
+
308
+ def export_to_json(events: list[ObserverEvent], path: str) -> None:
309
+ """Export events to a JSON file.
310
+
311
+ Args:
312
+ events: List of events to export.
313
+ path: Path to the output JSON file.
314
+ """
315
+ output_path = Path(path)
316
+ output_path.parent.mkdir(parents=True, exist_ok=True)
317
+
318
+ # Convert events to JSON-serializable format
319
+ json_events = []
320
+ for event in events:
321
+ # Handle bytes images before model_dump to avoid UTF-8 decode error
322
+ if isinstance(event, (StepEvent, ImageEvent, PlanEvent)) and isinstance(
323
+ getattr(event, "image", None), bytes
324
+ ):
325
+ # Dump without json mode first, then handle bytes manually
326
+ event_dict = event.model_dump()
327
+ event_dict["image"] = base64.b64encode(event.image).decode("utf-8")
328
+ event_dict["image_encoding"] = "base64"
329
+ # Convert datetime to string
330
+ if "timestamp" in event_dict:
331
+ event_dict["timestamp"] = event_dict["timestamp"].isoformat()
332
+ else:
333
+ event_dict = event.model_dump(mode="json")
334
+ json_events.append(event_dict)
335
+
336
+ output_path.write_text(json.dumps(json_events, indent=2, default=str))