oagi-core 0.12.1__tar.gz → 0.13.1__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 (129) hide show
  1. {oagi_core-0.12.1 → oagi_core-0.13.1}/PKG-INFO +42 -1
  2. {oagi_core-0.12.1 → oagi_core-0.13.1}/README.md +40 -0
  3. {oagi_core-0.12.1 → oagi_core-0.13.1}/metapackage/pyproject.toml +2 -2
  4. {oagi_core-0.12.1 → oagi_core-0.13.1}/metapackage/uv.lock +34 -5
  5. {oagi_core-0.12.1 → oagi_core-0.13.1}/pyproject.toml +2 -1
  6. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/__init__.py +27 -0
  7. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/actor/base.py +7 -3
  8. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/agent.py +17 -7
  9. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/client/async_.py +6 -1
  10. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/client/base.py +2 -1
  11. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/client/sync.py +8 -1
  12. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/constants.py +2 -2
  13. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/__init__.py +8 -0
  14. oagi_core-0.13.1/src/oagi/handler/_ydotool.py +158 -0
  15. oagi_core-0.13.1/src/oagi/handler/async_ydotool_action_handler.py +52 -0
  16. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/pil_image.py +6 -0
  17. oagi_core-0.13.1/src/oagi/handler/wayland_support.py +219 -0
  18. oagi_core-0.13.1/src/oagi/handler/ydotool_action_handler.py +226 -0
  19. oagi_core-0.13.1/src/oagi/platform_info.py +51 -0
  20. {oagi_core-0.12.1 → oagi_core-0.13.1}/uv.lock +31 -1
  21. {oagi_core-0.12.1 → oagi_core-0.13.1}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
  22. {oagi_core-0.12.1 → oagi_core-0.13.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  23. {oagi_core-0.12.1 → oagi_core-0.13.1}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
  24. {oagi_core-0.12.1 → oagi_core-0.13.1}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  25. {oagi_core-0.12.1 → oagi_core-0.13.1}/.github/workflows/ci.yml +0 -0
  26. {oagi_core-0.12.1 → oagi_core-0.13.1}/.github/workflows/release.yml +0 -0
  27. {oagi_core-0.12.1 → oagi_core-0.13.1}/.gitignore +0 -0
  28. {oagi_core-0.12.1 → oagi_core-0.13.1}/.python-version +0 -0
  29. {oagi_core-0.12.1 → oagi_core-0.13.1}/CONTRIBUTING.md +0 -0
  30. {oagi_core-0.12.1 → oagi_core-0.13.1}/LICENSE +0 -0
  31. {oagi_core-0.12.1 → oagi_core-0.13.1}/Makefile +0 -0
  32. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/async_google_weather.py +0 -0
  33. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/execute_task_auto.py +0 -0
  34. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/execute_task_manual.py +0 -0
  35. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/google_weather.py +0 -0
  36. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/openai_agent_loop_example.py +0 -0
  37. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/screenshot_with_config.py +0 -0
  38. {oagi_core-0.12.1 → oagi_core-0.13.1}/examples/tasker_agent_example.py +0 -0
  39. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/actor/__init__.py +0 -0
  40. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/actor/async_.py +0 -0
  41. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/actor/async_short.py +0 -0
  42. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/actor/short.py +0 -0
  43. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/actor/sync.py +0 -0
  44. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/__init__.py +0 -0
  45. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/default.py +0 -0
  46. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/factories.py +0 -0
  47. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/observer/__init__.py +0 -0
  48. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/observer/agent_observer.py +0 -0
  49. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/observer/events.py +0 -0
  50. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/observer/exporters.py +0 -0
  51. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/observer/protocol.py +0 -0
  52. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/observer/report_template.html +0 -0
  53. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/protocol.py +0 -0
  54. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/registry.py +0 -0
  55. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/tasker/__init__.py +0 -0
  56. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/tasker/memory.py +0 -0
  57. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/tasker/models.py +0 -0
  58. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/tasker/planner.py +0 -0
  59. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/tasker/taskee_agent.py +0 -0
  60. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/agent/tasker/tasker_agent.py +0 -0
  61. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/__init__.py +0 -0
  62. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/display.py +0 -0
  63. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/main.py +0 -0
  64. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/server.py +0 -0
  65. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/tracking.py +0 -0
  66. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/cli/utils.py +0 -0
  67. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/client/__init__.py +0 -0
  68. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/exceptions.py +0 -0
  69. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/_macos.py +0 -0
  70. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/_windows.py +0 -0
  71. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/async_pyautogui_action_handler.py +0 -0
  72. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/async_screenshot_maker.py +0 -0
  73. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/capslock_manager.py +0 -0
  74. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/pyautogui_action_handler.py +0 -0
  75. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/screenshot_maker.py +0 -0
  76. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/handler/utils.py +0 -0
  77. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/logging.py +0 -0
  78. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/__init__.py +0 -0
  79. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/agent_wrappers.py +0 -0
  80. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/config.py +0 -0
  81. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/main.py +0 -0
  82. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/models.py +0 -0
  83. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/session_store.py +0 -0
  84. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/server/socketio_server.py +0 -0
  85. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/task/__init__.py +0 -0
  86. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/__init__.py +0 -0
  87. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/action_handler.py +0 -0
  88. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/async_action_handler.py +0 -0
  89. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/async_image_provider.py +0 -0
  90. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/image.py +0 -0
  91. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/image_provider.py +0 -0
  92. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/models/__init__.py +0 -0
  93. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/models/action.py +0 -0
  94. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/models/client.py +0 -0
  95. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/models/image_config.py +0 -0
  96. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/models/step.py +0 -0
  97. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/step_observer.py +0 -0
  98. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/types/url.py +0 -0
  99. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/utils/__init__.py +0 -0
  100. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/utils/output_parser.py +0 -0
  101. {oagi_core-0.12.1 → oagi_core-0.13.1}/src/oagi/utils/prompt_builder.py +0 -0
  102. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/__init__.py +0 -0
  103. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/conftest.py +0 -0
  104. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_action_parsing.py +0 -0
  105. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_actor.py +0 -0
  106. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_agent/test_agent_wrappers.py +0 -0
  107. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_agent/test_default_agent.py +0 -0
  108. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_agent_registry.py +0 -0
  109. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_async_actor.py +0 -0
  110. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_async_client.py +0 -0
  111. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_async_handlers.py +0 -0
  112. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_cli.py +0 -0
  113. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_logging.py +0 -0
  114. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_mac_double_click.py +0 -0
  115. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_observer.py +0 -0
  116. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_pil_image.py +0 -0
  117. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_planner.py +0 -0
  118. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_planner_memory.py +0 -0
  119. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_pyautogui_action_handler.py +0 -0
  120. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_screenshot_maker.py +0 -0
  121. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_server/__init__.py +0 -0
  122. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_server/test_config.py +0 -0
  123. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_server/test_session_store.py +0 -0
  124. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_server/test_socketio_integration.py +0 -0
  125. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_sync_client.py +0 -0
  126. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_taskee_agent.py +0 -0
  127. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/test_tasker_agent.py +0 -0
  128. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/utils/__init__.py +0 -0
  129. {oagi_core-0.12.1 → oagi_core-0.13.1}/tests/utils/test_output_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oagi-core
3
- Version: 0.12.1
3
+ Version: 0.13.1
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>
@@ -35,6 +35,7 @@ Requires-Dist: pillow>=9.0.0; extra == 'desktop'
35
35
  Requires-Dist: pyautogui>=0.9.54; extra == 'desktop'
36
36
  Requires-Dist: pyobjc-framework-applicationservices>=8.0; (sys_platform == 'darwin') and extra == 'desktop'
37
37
  Requires-Dist: pyobjc-framework-quartz>=8.0; (sys_platform == 'darwin') and extra == 'desktop'
38
+ Requires-Dist: screeninfo>=0.8.1; extra == 'desktop'
38
39
  Provides-Extra: server
39
40
  Requires-Dist: fastapi[standard]>=0.100.0; extra == 'server'
40
41
  Requires-Dist: pydantic-settings>=2.0.0; extra == 'server'
@@ -82,6 +83,7 @@ With Lux, possibilities are endless. Here are a few examples:
82
83
  - [Command Line Interface](#command-line-interface)
83
84
  - [Image Processing](#image-processing)
84
85
  - [Manual Control with Actor](#manual-control-with-actor)
86
+ - [Run On System With Wayland](#run-on-system-with-wayland)
85
87
  - [Examples](#examples)
86
88
  - [Socket.IO Server (Optional)](#socketio-server-optional)
87
89
  - [Installation](#installation-1)
@@ -239,6 +241,45 @@ async def main():
239
241
  asyncio.run(main())
240
242
  ```
241
243
 
244
+ ### Run On System With Wayland
245
+ The SDK includes support for desktop automation on systems with Wayland display, such as Ubuntu/Debain. It leverages `ydotool` and `flameshot` for mouse/keyboard actions and screenshot capture respectively. Please install these two tools on your system in advance and ensure `ydotoold` server is running in the background when running the script.
246
+
247
+ Refer to [ydotool](https://github.com/ReimuNotMoe/ydotool) and [flameshot](https://flameshot.org/#download) for installation instructions. Disable mouse acceleration for more precise mouse control. (In GNOME, run `gsettings set org.gnome.desktop.peripherals.mouse accel-profile 'flat'`)
248
+
249
+ Run tasks automatically with screenshot capture and action execution:
250
+ ```python
251
+ import asyncio
252
+ from oagi import AsyncDefaultAgent, AsyncYdotoolActionHandler, AsyncScreenshotMaker
253
+
254
+ async def main():
255
+ agent = AsyncDefaultAgent(max_steps=10)
256
+ completed = await agent.execute(
257
+ "Search weather on Google",
258
+ action_handler=AsyncYdotoolActionHandler(), # Executes mouse/keyboard actions, based on 'ydotool'
259
+ image_provider=AsyncScreenshotMaker(), # Captures screenshots, based on 'flameshot'
260
+ )
261
+ return completed
262
+
263
+ asyncio.run(main())
264
+ ```
265
+
266
+ Configure Ydotool behavior with custom settings:
267
+
268
+ ```python
269
+ from oagi import AsyncYdotoolActionHandler, YdotoolConfig
270
+
271
+ # Customize action behavior
272
+ config = YdotoolConfig(
273
+ scroll_amount=50, # Larger scroll steps (default: 20)
274
+ wait_duration=2.0, # Longer waits (default: 1.0)
275
+ action_pause=1.0, # More pause between actions (default: 0.5)
276
+ capslock_mode="session", # Caps lock mode: 'session' or 'system' (default: 'session')
277
+ socket_address="/tmp/ydotool.sock" # Customized Socket address for ydotool (ydotool uses 'YDOTOOL_SOCKET' environment variable by default)
278
+ )
279
+
280
+ action_handler = AsyncYdotoolActionHandler(config=config)
281
+ ```
282
+
242
283
  ## Examples
243
284
 
244
285
  See the [`examples/`](examples/) directory for more usage patterns:
@@ -38,6 +38,7 @@ With Lux, possibilities are endless. Here are a few examples:
38
38
  - [Command Line Interface](#command-line-interface)
39
39
  - [Image Processing](#image-processing)
40
40
  - [Manual Control with Actor](#manual-control-with-actor)
41
+ - [Run On System With Wayland](#run-on-system-with-wayland)
41
42
  - [Examples](#examples)
42
43
  - [Socket.IO Server (Optional)](#socketio-server-optional)
43
44
  - [Installation](#installation-1)
@@ -195,6 +196,45 @@ async def main():
195
196
  asyncio.run(main())
196
197
  ```
197
198
 
199
+ ### Run On System With Wayland
200
+ The SDK includes support for desktop automation on systems with Wayland display, such as Ubuntu/Debain. It leverages `ydotool` and `flameshot` for mouse/keyboard actions and screenshot capture respectively. Please install these two tools on your system in advance and ensure `ydotoold` server is running in the background when running the script.
201
+
202
+ Refer to [ydotool](https://github.com/ReimuNotMoe/ydotool) and [flameshot](https://flameshot.org/#download) for installation instructions. Disable mouse acceleration for more precise mouse control. (In GNOME, run `gsettings set org.gnome.desktop.peripherals.mouse accel-profile 'flat'`)
203
+
204
+ Run tasks automatically with screenshot capture and action execution:
205
+ ```python
206
+ import asyncio
207
+ from oagi import AsyncDefaultAgent, AsyncYdotoolActionHandler, AsyncScreenshotMaker
208
+
209
+ async def main():
210
+ agent = AsyncDefaultAgent(max_steps=10)
211
+ completed = await agent.execute(
212
+ "Search weather on Google",
213
+ action_handler=AsyncYdotoolActionHandler(), # Executes mouse/keyboard actions, based on 'ydotool'
214
+ image_provider=AsyncScreenshotMaker(), # Captures screenshots, based on 'flameshot'
215
+ )
216
+ return completed
217
+
218
+ asyncio.run(main())
219
+ ```
220
+
221
+ Configure Ydotool behavior with custom settings:
222
+
223
+ ```python
224
+ from oagi import AsyncYdotoolActionHandler, YdotoolConfig
225
+
226
+ # Customize action behavior
227
+ config = YdotoolConfig(
228
+ scroll_amount=50, # Larger scroll steps (default: 20)
229
+ wait_duration=2.0, # Longer waits (default: 1.0)
230
+ action_pause=1.0, # More pause between actions (default: 0.5)
231
+ capslock_mode="session", # Caps lock mode: 'session' or 'system' (default: 'session')
232
+ socket_address="/tmp/ydotool.sock" # Customized Socket address for ydotool (ydotool uses 'YDOTOOL_SOCKET' environment variable by default)
233
+ )
234
+
235
+ action_handler = AsyncYdotoolActionHandler(config=config)
236
+ ```
237
+
198
238
  ## Examples
199
239
 
200
240
  See the [`examples/`](examples/) directory for more usage patterns:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "oagi"
7
- version = "0.12.1"
7
+ version = "0.13.1"
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.12.1",
19
+ "oagi-core[desktop,server]==0.13.1",
20
20
  ]
21
21
 
22
22
  [project.urls]
@@ -79,6 +79,21 @@ wheels = [
79
79
  { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
80
80
  ]
81
81
 
82
+ [[package]]
83
+ name = "cython"
84
+ version = "3.2.4"
85
+ source = { registry = "https://pypi.org/simple" }
86
+ sdist = { url = "https://files.pythonhosted.org/packages/91/85/7574c9cd44b69a27210444b6650f6477f56c75fee1b70d7672d3e4166167/cython-3.2.4.tar.gz", hash = "sha256:84226ecd313b233da27dc2eb3601b4f222b8209c3a7216d8733b031da1dc64e6", size = 3280291, upload-time = "2026-01-04T14:14:14.473Z" }
87
+ wheels = [
88
+ { url = "https://files.pythonhosted.org/packages/a1/10/720e0fb84eab4c927c4dd6b61eb7993f7732dd83d29ba6d73083874eade9/cython-3.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02cb0cc0f23b9874ad262d7d2b9560aed9c7e2df07b49b920bda6f2cc9cb505e", size = 2960836, upload-time = "2026-01-04T14:14:51.103Z" },
89
+ { url = "https://files.pythonhosted.org/packages/85/cc/8f06145ec3efa121c8b1b67f06a640386ddacd77ee3e574da582a21b14ee/cython-3.2.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff9af2134c05e3734064808db95b4dd7341a39af06e8945d05ea358e1741aaed", size = 2953769, upload-time = "2026-01-04T14:15:00.361Z" },
90
+ { url = "https://files.pythonhosted.org/packages/91/4d/1eb0c7c196a136b1926f4d7f0492a96c6fabd604d77e6cd43b56a3a16d83/cython-3.2.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64d7f71be3dd6d6d4a4c575bb3a4674ea06d1e1e5e4cd1b9882a2bc40ed3c4c9", size = 2970064, upload-time = "2026-01-04T14:15:08.567Z" },
91
+ { url = "https://files.pythonhosted.org/packages/18/b5/1cfca43b7d20a0fdb1eac67313d6bb6b18d18897f82dd0f17436bdd2ba7f/cython-3.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:28e8075087a59756f2d059273184b8b639fe0f16cf17470bd91c39921bc154e0", size = 2960506, upload-time = "2026-01-04T14:15:16.733Z" },
92
+ { url = "https://files.pythonhosted.org/packages/ee/d7/3bda3efce0c5c6ce79cc21285dbe6f60369c20364e112f5a506ee8a1b067/cython-3.2.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d4b4fd5332ab093131fa6172e8362f16adef3eac3179fd24bbdc392531cb82fa", size = 2971496, upload-time = "2026-01-04T14:15:25.038Z" },
93
+ { url = "https://files.pythonhosted.org/packages/0a/8b/fd393f0923c82be4ec0db712fffb2ff0a7a131707b842c99bf24b549274d/cython-3.2.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:36bf3f5eb56d5281aafabecbaa6ed288bc11db87547bba4e1e52943ae6961ccf", size = 2875622, upload-time = "2026-01-04T14:15:39.749Z" },
94
+ { url = "https://files.pythonhosted.org/packages/ff/fa/d3c15189f7c52aaefbaea76fb012119b04b9013f4bf446cb4eb4c26c4e6b/cython-3.2.4-py3-none-any.whl", hash = "sha256:732fc93bc33ae4b14f6afaca663b916c2fdd5dcbfad7114e17fb2434eeaea45c", size = 1257078, upload-time = "2026-01-04T14:14:12.373Z" },
95
+ ]
96
+
82
97
  [[package]]
83
98
  name = "distro"
84
99
  version = "1.9.0"
@@ -503,18 +518,18 @@ sdist = { url = "https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6
503
518
 
504
519
  [[package]]
505
520
  name = "oagi"
506
- version = "0.12.1"
521
+ version = "0.13.1"
507
522
  source = { editable = "." }
508
523
  dependencies = [
509
524
  { name = "oagi-core", extra = ["desktop", "server"] },
510
525
  ]
511
526
 
512
527
  [package.metadata]
513
- requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.12.0" }]
528
+ requires-dist = [{ name = "oagi-core", extras = ["desktop", "server"], specifier = "==0.13.0" }]
514
529
 
515
530
  [[package]]
516
531
  name = "oagi-core"
517
- version = "0.12.0"
532
+ version = "0.13.0"
518
533
  source = { registry = "https://pypi.org/simple" }
519
534
  dependencies = [
520
535
  { name = "httpx" },
@@ -522,9 +537,9 @@ dependencies = [
522
537
  { name = "pydantic" },
523
538
  { name = "rich" },
524
539
  ]
525
- sdist = { url = "https://files.pythonhosted.org/packages/5f/20/431fe40c681194886fa51b841929a6be4d614409817f048ab88fab1ef4f8/oagi_core-0.12.0.tar.gz", hash = "sha256:3ffd93f217bac5357fd28442a65c679a3b1891e52419f833dd7d2bbd48015d7a", size = 286697, upload-time = "2025-12-11T10:56:26.121Z" }
540
+ sdist = { url = "https://files.pythonhosted.org/packages/9c/45/ace3fe538b40725d1b0dcd61fbe445fe73ad455249e303b8c66a54ec3691/oagi_core-0.13.0.tar.gz", hash = "sha256:4e6f5a260b542286d92c050995cfa3be1ff1c21899dba8786d6840e9eac21dd0", size = 303398, upload-time = "2025-12-23T16:16:24.033Z" }
526
541
  wheels = [
527
- { url = "https://files.pythonhosted.org/packages/6f/fb/4474f6f2dcb4ee9ac1699d437b5b133be695a919108b50e9ace92e763e9f/oagi_core-0.12.0-py3-none-any.whl", hash = "sha256:37c67c2b96a13166dd4fa89828e967fdd23f0eb871ec1b22acef8bf4ce777c8a", size = 99904, upload-time = "2025-12-11T10:56:24.684Z" },
542
+ { url = "https://files.pythonhosted.org/packages/37/07/39f4a2cd2dea6f7d815deb46395545225cf218f447fad3a9a2b4df848ba1/oagi_core-0.13.0-py3-none-any.whl", hash = "sha256:53045574e669ea8442b6b2d0d6acaf6eb56477260276d922cc7f08c0c92adad2", size = 110020, upload-time = "2025-12-23T16:16:23.1Z" },
528
543
  ]
529
544
 
530
545
  [package.optional-dependencies]
@@ -533,6 +548,7 @@ desktop = [
533
548
  { name = "pyautogui" },
534
549
  { name = "pyobjc-framework-applicationservices", marker = "sys_platform == 'darwin'" },
535
550
  { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" },
551
+ { name = "screeninfo" },
536
552
  ]
537
553
  server = [
538
554
  { name = "fastapi", extra = ["standard"] },
@@ -1246,6 +1262,19 @@ wheels = [
1246
1262
  { url = "https://files.pythonhosted.org/packages/9f/a4/11ad29100060af56408ed084dca76a16d2ce8eb31b75081bfc0eec45d755/rubicon_objc-0.5.2-py3-none-any.whl", hash = "sha256:829b253c579e51fc34f4bb6587c34806e78960dcc1eb24e62b38141a1fe02b39", size = 63512, upload-time = "2025-08-07T06:12:23.766Z" },
1247
1263
  ]
1248
1264
 
1265
+ [[package]]
1266
+ name = "screeninfo"
1267
+ version = "0.8.1"
1268
+ source = { registry = "https://pypi.org/simple" }
1269
+ dependencies = [
1270
+ { name = "cython", marker = "sys_platform == 'darwin'" },
1271
+ { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" },
1272
+ ]
1273
+ sdist = { url = "https://files.pythonhosted.org/packages/ec/bb/e69e5e628d43f118e0af4fc063c20058faa8635c95a1296764acc8167e27/screeninfo-0.8.1.tar.gz", hash = "sha256:9983076bcc7e34402a1a9e4d7dabf3729411fd2abb3f3b4be7eba73519cd2ed1", size = 10666, upload-time = "2022-09-09T11:35:23.419Z" }
1274
+ wheels = [
1275
+ { url = "https://files.pythonhosted.org/packages/6e/bf/c5205d480307bef660e56544b9e3d7ff687da776abb30c9cb3f330887570/screeninfo-0.8.1-py3-none-any.whl", hash = "sha256:e97d6b173856edcfa3bd282f81deb528188aff14b11ec3e195584e7641be733c", size = 12907, upload-time = "2022-09-09T11:35:21.351Z" },
1276
+ ]
1277
+
1249
1278
  [[package]]
1250
1279
  name = "sentry-sdk"
1251
1280
  version = "2.45.0"
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "oagi-core"
7
- version = "0.12.1"
7
+ version = "0.13.1"
8
8
  description = "Official API of OpenAGI Foundation"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -33,6 +33,7 @@ desktop = [
33
33
  "pyautogui>=0.9.54",
34
34
  "pyobjc-framework-Quartz>=8.0; sys_platform == 'darwin'",
35
35
  "pyobjc-framework-ApplicationServices>=8.0; sys_platform == 'darwin'",
36
+ "screeninfo>=0.8.1"
36
37
  ]
37
38
  server = [
38
39
  "fastapi[standard]>=0.100.0",
@@ -6,8 +6,11 @@
6
6
  # Licensed under the MIT License.
7
7
  # -----------------------------------------------------------------------------
8
8
  import importlib
9
+ import importlib.metadata
9
10
  from typing import TYPE_CHECKING
10
11
 
12
+ __version__ = importlib.metadata.version("oagi-core")
13
+
11
14
  from oagi.actor import Actor, AsyncActor, AsyncShortTask, AsyncTask, ShortTask, Task
12
15
  from oagi.client import AsyncClient, SyncClient
13
16
  from oagi.exceptions import (
@@ -62,6 +65,22 @@ _LAZY_IMPORTS_DATA: dict[str, tuple[str, str | None, str | None]] = {
62
65
  "create_app": ("oagi.server.main", "socketio", "server"),
63
66
  "ServerConfig": ("oagi.server.config", "pydantic_settings", "server"),
64
67
  "sio": ("oagi.server.socketio_server", "socketio", "server"),
68
+ # Wayland handlers
69
+ "AsyncYdotoolActionHandler": (
70
+ "oagi.handler.async_ydotool_action_handler",
71
+ "screeninfo",
72
+ "desktop",
73
+ ),
74
+ "YdotoolActionHandler": (
75
+ "oagi.handler.ydotool_action_handler",
76
+ "screeninfo",
77
+ "desktop",
78
+ ),
79
+ "YdotoolConfig": (
80
+ "oagi.handler.ydotool_action_handler",
81
+ "screeninfo",
82
+ "desktop",
83
+ ),
65
84
  }
66
85
 
67
86
  if TYPE_CHECKING:
@@ -70,12 +89,14 @@ if TYPE_CHECKING:
70
89
  from oagi.agent.tasker import TaskerAgent
71
90
  from oagi.handler.async_pyautogui_action_handler import AsyncPyautoguiActionHandler
72
91
  from oagi.handler.async_screenshot_maker import AsyncScreenshotMaker
92
+ from oagi.handler.async_ydotool_action_handler import AsyncYdotoolActionHandler
73
93
  from oagi.handler.pil_image import PILImage
74
94
  from oagi.handler.pyautogui_action_handler import (
75
95
  PyautoguiActionHandler,
76
96
  PyautoguiConfig,
77
97
  )
78
98
  from oagi.handler.screenshot_maker import ScreenshotMaker
99
+ from oagi.handler.ydotool_action_handler import YdotoolActionHandler, YdotoolConfig
79
100
  from oagi.server.config import ServerConfig
80
101
  from oagi.server.main import create_app
81
102
  from oagi.server.socketio_server import sio
@@ -98,6 +119,8 @@ def __dir__() -> list[str]:
98
119
 
99
120
 
100
121
  __all__ = [
122
+ # Version
123
+ "__version__",
101
124
  # Core sync classes
102
125
  "Actor",
103
126
  "AsyncActor",
@@ -143,4 +166,8 @@ __all__ = [
143
166
  "create_app",
144
167
  "ServerConfig",
145
168
  "sio",
169
+ # Lazy imports - Wayland handler classes
170
+ "AsyncYdotoolActionHandler",
171
+ "YdotoolActionHandler",
172
+ "YdotoolConfig",
146
173
  ]
@@ -10,8 +10,8 @@ from uuid import uuid4
10
10
 
11
11
  from ..constants import (
12
12
  DEFAULT_MAX_STEPS,
13
- MAX_STEPS_ACTOR,
14
- MAX_STEPS_THINKER,
13
+ MAX_STEPS_ALLOWED_ACTOR,
14
+ MAX_STEPS_ALLOWED_THINKER,
15
15
  MODEL_THINKER,
16
16
  )
17
17
  from ..logging import get_logger
@@ -51,7 +51,11 @@ class BaseActor:
51
51
  Returns:
52
52
  Validated max_steps (capped to model limit if exceeded)
53
53
  """
54
- limit = MAX_STEPS_THINKER if self.model == MODEL_THINKER else MAX_STEPS_ACTOR
54
+ limit = (
55
+ MAX_STEPS_ALLOWED_THINKER
56
+ if self.model == MODEL_THINKER
57
+ else MAX_STEPS_ALLOWED_ACTOR
58
+ )
55
59
  if max_steps > limit:
56
60
  logger.warning(
57
61
  f"max_steps ({max_steps}) exceeds limit for model '{self.model}'. "
@@ -206,14 +206,25 @@ def _warn_missing_permissions() -> None:
206
206
 
207
207
  def run_agent(args: argparse.Namespace) -> None:
208
208
  # Check if desktop extras are installed
209
- check_optional_dependency("pyautogui", "Agent execution", "desktop")
210
209
  check_optional_dependency("PIL", "Agent execution", "desktop")
211
210
 
212
- # Warn about missing macOS permissions (non-blocking)
213
- _warn_missing_permissions()
214
-
215
- from oagi import AsyncPyautoguiActionHandler, AsyncScreenshotMaker # noqa: PLC0415
211
+ from oagi import AsyncScreenshotMaker # noqa: PLC0415
216
212
  from oagi.agent import create_agent # noqa: PLC0415
213
+ from oagi.handler.wayland_support import is_wayland_display_server # noqa: PLC0415
214
+
215
+ # Select appropriate action handler based on display server
216
+ if is_wayland_display_server():
217
+ check_optional_dependency("screeninfo", "Agent execution (Wayland)", "desktop")
218
+ from oagi import AsyncYdotoolActionHandler # noqa: PLC0415
219
+
220
+ action_handler = AsyncYdotoolActionHandler()
221
+ else:
222
+ check_optional_dependency("pyautogui", "Agent execution", "desktop")
223
+ # Warn about missing macOS permissions (non-blocking)
224
+ _warn_missing_permissions()
225
+ from oagi import AsyncPyautoguiActionHandler # noqa: PLC0415
226
+
227
+ action_handler = AsyncPyautoguiActionHandler()
217
228
 
218
229
  # Get configuration
219
230
  api_key = args.oagi_api_key or os.getenv("OAGI_API_KEY")
@@ -266,8 +277,7 @@ def run_agent(args: argparse.Namespace) -> None:
266
277
  # Create agent
267
278
  agent = create_agent(**agent_kwargs)
268
279
 
269
- # Create handlers
270
- action_handler = AsyncPyautoguiActionHandler()
280
+ # Create image provider
271
281
  image_provider = AsyncScreenshotMaker()
272
282
 
273
283
  if args.instruction:
@@ -19,6 +19,7 @@ from ..constants import (
19
19
  HTTP_CLIENT_TIMEOUT,
20
20
  )
21
21
  from ..logging import get_logger
22
+ from ..platform_info import get_sdk_headers
22
23
  from ..types import Image
23
24
  from ..types.models import GenerateResponse, UploadFileResponse, Usage
24
25
  from ..types.models.step import Step
@@ -54,17 +55,21 @@ class AsyncClient(BaseClient[httpx.AsyncClient]):
54
55
  ):
55
56
  super().__init__(base_url, api_key, max_retries)
56
57
 
58
+ # Get SDK headers for all clients
59
+ sdk_headers = get_sdk_headers()
60
+
57
61
  # OpenAI client for chat completions (with retries)
58
62
  self.openai_client = AsyncOpenAI(
59
63
  api_key=self.api_key,
60
64
  base_url=f"{self.base_url}/v1",
61
65
  max_retries=self.max_retries,
66
+ default_headers=sdk_headers,
62
67
  )
63
68
 
64
69
  # httpx clients for S3 uploads and other endpoints (with retries)
65
70
  transport = AsyncHTTPTransport(retries=self.max_retries)
66
71
  self.http_client = httpx.AsyncClient(
67
- transport=transport, base_url=self.base_url
72
+ transport=transport, base_url=self.base_url, headers=sdk_headers
68
73
  )
69
74
  self.upload_client = httpx.AsyncClient(
70
75
  transport=transport, timeout=HTTP_CLIENT_TIMEOUT
@@ -29,6 +29,7 @@ from ..exceptions import (
29
29
  ValidationError,
30
30
  )
31
31
  from ..logging import get_logger
32
+ from ..platform_info import get_sdk_headers
32
33
  from ..types.models import (
33
34
  ErrorResponse,
34
35
  GenerateResponse,
@@ -73,7 +74,7 @@ class BaseClient(Generic[HttpClientT]):
73
74
  logger.info(f"Client initialized with base_url: {self.base_url}")
74
75
 
75
76
  def _build_headers(self, api_version: str | None = None) -> dict[str, str]:
76
- headers: dict[str, str] = {}
77
+ headers = get_sdk_headers()
77
78
  if api_version:
78
79
  headers["x-api-version"] = api_version
79
80
  if self.api_key:
@@ -19,6 +19,7 @@ from ..constants import (
19
19
  HTTP_CLIENT_TIMEOUT,
20
20
  )
21
21
  from ..logging import get_logger
22
+ from ..platform_info import get_sdk_headers
22
23
  from ..types import Image
23
24
  from ..types.models import GenerateResponse, UploadFileResponse, Usage
24
25
  from ..types.models.step import Step
@@ -54,16 +55,22 @@ class SyncClient(BaseClient[httpx.Client]):
54
55
  ):
55
56
  super().__init__(base_url, api_key, max_retries)
56
57
 
58
+ # Get SDK headers for all clients
59
+ sdk_headers = get_sdk_headers()
60
+
57
61
  # OpenAI client for chat completions (with retries)
58
62
  self.openai_client = OpenAI(
59
63
  api_key=self.api_key,
60
64
  base_url=f"{self.base_url}/v1",
61
65
  max_retries=self.max_retries,
66
+ default_headers=sdk_headers,
62
67
  )
63
68
 
64
69
  # httpx clients for S3 uploads and other endpoints (with retries)
65
70
  transport = HTTPTransport(retries=self.max_retries)
66
- self.http_client = httpx.Client(transport=transport, base_url=self.base_url)
71
+ self.http_client = httpx.Client(
72
+ transport=transport, base_url=self.base_url, headers=sdk_headers
73
+ )
67
74
  self.upload_client = httpx.Client(
68
75
  transport=transport, timeout=HTTP_CLIENT_TIMEOUT
69
76
  )
@@ -27,8 +27,8 @@ DEFAULT_MAX_STEPS_THINKER = 100
27
27
  DEFAULT_MAX_STEPS_TASKER = 60
28
28
 
29
29
  # Maximum allowed steps per model (hard limits)
30
- MAX_STEPS_ACTOR = 30
31
- MAX_STEPS_THINKER = 120
30
+ MAX_STEPS_ALLOWED_ACTOR = 100
31
+ MAX_STEPS_ALLOWED_THINKER = 300
32
32
 
33
33
  # Reflection intervals
34
34
  DEFAULT_REFLECTION_INTERVAL = 4
@@ -18,17 +18,22 @@ _LAZY_IMPORTS: dict[str, str] = {
18
18
  "PyautoguiActionHandler": "oagi.handler.pyautogui_action_handler",
19
19
  "PyautoguiConfig": "oagi.handler.pyautogui_action_handler",
20
20
  "ScreenshotMaker": "oagi.handler.screenshot_maker",
21
+ "AsyncYdotoolActionHandler": "oagi.handler.async_ydotool_action_handler",
22
+ "YdotoolActionHandler": "oagi.handler.ydotool_action_handler",
23
+ "YdotoolConfig": "oagi.handler.ydotool_action_handler",
21
24
  }
22
25
 
23
26
  if TYPE_CHECKING:
24
27
  from oagi.handler.async_pyautogui_action_handler import AsyncPyautoguiActionHandler
25
28
  from oagi.handler.async_screenshot_maker import AsyncScreenshotMaker
29
+ from oagi.handler.async_ydotool_action_handler import AsyncYdotoolActionHandler
26
30
  from oagi.handler.pil_image import PILImage
27
31
  from oagi.handler.pyautogui_action_handler import (
28
32
  PyautoguiActionHandler,
29
33
  PyautoguiConfig,
30
34
  )
31
35
  from oagi.handler.screenshot_maker import ScreenshotMaker
36
+ from oagi.handler.ydotool_action_handler import YdotoolActionHandler, YdotoolConfig
32
37
 
33
38
 
34
39
  def __getattr__(name: str):
@@ -52,4 +57,7 @@ __all__ = [
52
57
  "ScreenshotMaker",
53
58
  "AsyncScreenshotMaker",
54
59
  "reset_handler",
60
+ "YdotoolConfig",
61
+ "YdotoolActionHandler",
62
+ "AsyncYdotoolActionHandler",
55
63
  ]
@@ -0,0 +1,158 @@
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
+ Taken from /usr/include/linux/input-event-codes.h
10
+
11
+ The keys supported in this mapping are the same as the keys supported in pyautogui.
12
+ """
13
+
14
+ KEYCODE_MAP = {
15
+ # Letters
16
+ "a": 30,
17
+ "s": 31,
18
+ "d": 32,
19
+ "f": 33,
20
+ "h": 35,
21
+ "g": 34,
22
+ "z": 44,
23
+ "x": 45,
24
+ "c": 46,
25
+ "v": 47,
26
+ "b": 48,
27
+ "q": 16,
28
+ "w": 17,
29
+ "e": 18,
30
+ "r": 19,
31
+ "y": 21,
32
+ "t": 20,
33
+ "o": 24,
34
+ "u": 22,
35
+ "i": 23,
36
+ "p": 25,
37
+ "l": 38,
38
+ "j": 36,
39
+ "k": 37,
40
+ "n": 49,
41
+ "m": 50,
42
+ # Numbers and shifted symbols
43
+ "1": 2,
44
+ "!": 2,
45
+ "2": 3,
46
+ "@": 3,
47
+ "3": 4,
48
+ "#": 4,
49
+ "4": 5,
50
+ "$": 5,
51
+ "5": 6,
52
+ "%": 6,
53
+ "6": 7,
54
+ "^": 7,
55
+ "7": 8,
56
+ "&": 8,
57
+ "8": 9,
58
+ "*": 9,
59
+ "9": 10,
60
+ "(": 10,
61
+ "0": 11,
62
+ ")": 11,
63
+ # Punctuation and symbols
64
+ "-": 12,
65
+ "_": 12, # KEY_MINUS
66
+ "=": 13,
67
+ "+": 13, # KEY_EQUAL
68
+ "[": 26,
69
+ "{": 26, # KEY_LEFTBRACE
70
+ "]": 27,
71
+ "}": 27, # KEY_RIGHTBRACE
72
+ ";": 39,
73
+ ":": 39, # KEY_SEMICOLON
74
+ "'": 40,
75
+ '"': 40, # KEY_APOSTROPHE
76
+ "`": 41,
77
+ "~": 41, # KEY_GRAVE
78
+ "\\": 43,
79
+ "|": 43, # KEY_BACKSLASH
80
+ ",": 51,
81
+ "<": 51, # KEY_COMMA
82
+ ".": 52,
83
+ ">": 52, # KEY_DOT
84
+ "/": 53,
85
+ "?": 53, # KEY_SLASH
86
+ # Whitespace
87
+ " ": 57,
88
+ "space": 57,
89
+ "\t": 15,
90
+ "tab": 15,
91
+ # Enter / Backspace / Esc
92
+ "\r": 28,
93
+ "\n": 28,
94
+ "enter": 28,
95
+ "return": 28,
96
+ "backspace": 14,
97
+ "\b": 14,
98
+ "esc": 1,
99
+ "escape": 1,
100
+ # Modifiers
101
+ "shift": 42,
102
+ "shiftleft": 42,
103
+ "shiftright": 54,
104
+ "capslock": 58,
105
+ "ctrl": 29,
106
+ "ctrlleft": 29,
107
+ "ctrlright": 97,
108
+ "alt": 56,
109
+ "altleft": 56,
110
+ "option": 56,
111
+ "optionleft": 56,
112
+ "optionright": 100,
113
+ "command": 125, # map to KEY_LEFTMETA
114
+ "fn": 464, # KEY_FN (0x1d0)
115
+ # Function keys
116
+ "f1": 59,
117
+ "f2": 60,
118
+ "f3": 61,
119
+ "f4": 62,
120
+ "f5": 63,
121
+ "f6": 64,
122
+ "f7": 65,
123
+ "f8": 66,
124
+ "f9": 67,
125
+ "f10": 68,
126
+ "f11": 87,
127
+ "f12": 88,
128
+ "f13": 183,
129
+ "f14": 184,
130
+ "f15": 185,
131
+ "f16": 186,
132
+ "f17": 187,
133
+ "f18": 188,
134
+ "f19": 189,
135
+ "f20": 190,
136
+ # Navigation
137
+ "home": 102,
138
+ "end": 107,
139
+ "pageup": 104,
140
+ "pgup": 104,
141
+ "pagedown": 109,
142
+ "pgdn": 109,
143
+ "left": 105,
144
+ "right": 106,
145
+ "up": 103,
146
+ "down": 108,
147
+ "del": 111,
148
+ "delete": 111,
149
+ # Media
150
+ "volumeup": 115,
151
+ "volumedown": 114,
152
+ "volumemute": 113,
153
+ # Locale-specific keys
154
+ "yen": 124, # KEY_YEN
155
+ "eisu": 85, # mapped to KEY_ZENKAKUHANKAKU (common JIS toggle)
156
+ "kana": 90, # KEY_KATAKANA
157
+ "help": 138, # KEY_HELP
158
+ }