koru 0.1.331__py3-none-any.whl → 0.1.332__py3-none-any.whl

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.
@@ -281,6 +281,77 @@ class Nlp2UriDesktopBackend:
281
281
  return _nlp2uri_desktop_send(prompt, ide=ide, submit=submit, dry_run=self.dry_run)
282
282
 
283
283
 
284
+ def _build_plugin_socket_backend(client: IDEControlClient | None) -> AgentBackend:
285
+ if client is None:
286
+ raise ValueError("plugin_socket backend requires an IDEControlClient")
287
+ return PluginSocketBackend(client=client)
288
+
289
+ def _build_mcp_tool_backend(mcp_server: str | None) -> AgentBackend:
290
+ return McpToolBackend(mcp_server=mcp_server)
291
+
292
+ def _build_vendor_agent_cli_backend(shell_client_id: str | None) -> AgentBackend:
293
+ return TillmShellBackend(
294
+ client_id=shell_client_id or os.environ.get("KORU_TILLM_CLIENT", "aider"),
295
+ execute=os.environ.get("KORU_TILLM_DRY_RUN", "").strip().lower()
296
+ not in {"1", "true", "yes", "on"},
297
+ )
298
+
299
+ def _build_gillm_gui_backend() -> AgentBackend:
300
+ return GillmGuiBackend(client=build_gillm_ide_client())
301
+
302
+ def _build_os_injector_backend() -> AgentBackend:
303
+ profile = os.environ.get("KORU_OS_INJECTOR_PROFILE", "").strip()
304
+ if not profile:
305
+ raise ValueError("os_injector backend requires KORU_OS_INJECTOR_PROFILE")
306
+ raw_cfg = os.environ.get("KORU_OS_INJECTOR_CONFIG", "").strip()
307
+ cfg = Path(raw_cfg).expanduser().resolve() if raw_cfg else None
308
+ return OsInjectorBackend(profile_id=profile, config_path=cfg)
309
+
310
+ def _build_nlp2uri_desktop_backend() -> AgentBackend:
311
+ dry = os.environ.get("KORU_NLP2URI_DRY_RUN", "").strip().lower() in {
312
+ "1", "true", "yes", "on",
313
+ }
314
+ return Nlp2UriDesktopBackend(dry_run=dry)
315
+
316
+ def _build_imgl_desktop_backend() -> AgentBackend:
317
+ dry = os.environ.get("KORU_IMGL_DRY_RUN", "").strip().lower() in {
318
+ "1", "true", "yes", "on",
319
+ }
320
+ return ImglDesktopBackend(dry_run=dry)
321
+
322
+ def _build_vdisplay_control_backend() -> AgentBackend:
323
+ dry = os.environ.get("KORU_VDISPLAY_DRY_RUN", "").strip().lower() in {
324
+ "1", "true", "yes", "on",
325
+ }
326
+ return VdisplayControlBackend(dry_run=dry)
327
+
328
+ def _build_noop_backend(noop_reason: str) -> AgentBackend:
329
+ return NoopBackend(reason=noop_reason)
330
+
331
+ _BACKEND_BUILDERS = {
332
+ "plugin_socket": _build_plugin_socket_backend,
333
+ "vscode_family_plugin_socket": _build_plugin_socket_backend,
334
+ "mcp_tool": _build_mcp_tool_backend,
335
+ "mcp_stdio_server": _build_mcp_tool_backend,
336
+ "vendor_agent_cli": _build_vendor_agent_cli_backend,
337
+ "gillm_gui": _build_gillm_gui_backend,
338
+ "gillm_gui_driver": _build_gillm_gui_backend,
339
+ "os_injector": _build_os_injector_backend,
340
+ "os_keyboard_injector": _build_os_injector_backend,
341
+ "nlp2uri_desktop": _build_nlp2uri_desktop_backend,
342
+ "nlp2uri_desktop_window": _build_nlp2uri_desktop_backend,
343
+ "imgl": _build_imgl_desktop_backend,
344
+ "imgl_vision": _build_imgl_desktop_backend,
345
+ "imgl_desktop": _build_imgl_desktop_backend,
346
+ "imgl_vision_driver": _build_imgl_desktop_backend,
347
+ "vdisplay": _build_vdisplay_control_backend,
348
+ "vdisplay_control": _build_vdisplay_control_backend,
349
+ "vdisplay_semantic_control": _build_vdisplay_control_backend,
350
+ "none": _build_noop_backend,
351
+ "noop": _build_noop_backend,
352
+ "": _build_noop_backend,
353
+ }
354
+
284
355
  def build_agent_backend(
285
356
  *,
286
357
  backend_id: str,
@@ -296,44 +367,18 @@ def build_agent_backend(
296
367
  """
297
368
  bid = (backend_id or "").strip().lower().replace("-", "_")
298
369
  normalized = normalize_agent_backend_id(backend_id or "")
299
- if bid == "plugin_socket" or normalized == "vscode_family_plugin_socket":
300
- if client is None:
301
- raise ValueError("plugin_socket backend requires an IDEControlClient")
302
- return PluginSocketBackend(client=client)
303
- if bid == "mcp_tool" or normalized == "mcp_stdio_server":
304
- return McpToolBackend(mcp_server=mcp_server)
305
- if normalized == "vendor_agent_cli":
306
- return TillmShellBackend(
307
- client_id=shell_client_id or os.environ.get("KORU_TILLM_CLIENT", "aider"),
308
- execute=os.environ.get("KORU_TILLM_DRY_RUN", "").strip().lower()
309
- not in {"1", "true", "yes", "on"},
310
- )
311
- if bid == "gillm_gui" or normalized == "gillm_gui_driver":
312
- return GillmGuiBackend(client=build_gillm_ide_client())
313
- if bid == "os_injector" or normalized == "os_keyboard_injector":
314
- profile = os.environ.get("KORU_OS_INJECTOR_PROFILE", "").strip()
315
- if not profile:
316
- raise ValueError("os_injector backend requires KORU_OS_INJECTOR_PROFILE")
317
- raw_cfg = os.environ.get("KORU_OS_INJECTOR_CONFIG", "").strip()
318
- cfg = Path(raw_cfg).expanduser().resolve() if raw_cfg else None
319
- return OsInjectorBackend(profile_id=profile, config_path=cfg)
320
- if bid == "nlp2uri_desktop" or normalized == "nlp2uri_desktop_window":
321
- dry = os.environ.get("KORU_NLP2URI_DRY_RUN", "").strip().lower() in {
322
- "1", "true", "yes", "on",
323
- }
324
- return Nlp2UriDesktopBackend(dry_run=dry)
325
- if bid in ("imgl", "imgl_vision", "imgl_desktop") or normalized == "imgl_vision_driver":
326
- dry = os.environ.get("KORU_IMGL_DRY_RUN", "").strip().lower() in {
327
- "1", "true", "yes", "on",
328
- }
329
- return ImglDesktopBackend(dry_run=dry)
330
- if bid in ("vdisplay", "vdisplay_control") or normalized == "vdisplay_semantic_control":
331
- dry = os.environ.get("KORU_VDISPLAY_DRY_RUN", "").strip().lower() in {
332
- "1", "true", "yes", "on",
333
- }
334
- return VdisplayControlBackend(dry_run=dry)
335
- if bid in ("none", "noop", ""):
336
- return NoopBackend(reason=noop_reason)
370
+ builder = _BACKEND_BUILDERS.get(bid) or _BACKEND_BUILDERS.get(normalized)
371
+ if builder:
372
+ if "client" in builder.__code__.co_varnames and client is not None:
373
+ return builder(client=client)
374
+ elif "mcp_server" in builder.__code__.co_varnames and mcp_server is not None:
375
+ return builder(mcp_server=mcp_server)
376
+ elif "shell_client_id" in builder.__code__.co_varnames and shell_client_id is not None:
377
+ return builder(shell_client_id=shell_client_id)
378
+ elif "noop_reason" in builder.__code__.co_varnames:
379
+ return builder(noop_reason=noop_reason)
380
+ else:
381
+ return builder()
337
382
  raise ValueError(f"unknown agent backend id: {backend_id!r}")
338
383
 
339
384
 
@@ -87,7 +87,8 @@ def imgl_desktop_transport_enabled() -> bool:
87
87
 
88
88
  def default_image_path() -> Path:
89
89
  if _IMGL_DIRECT:
90
- return _default_image_path()
90
+ p = _default_image_path()
91
+ return Path(p) if isinstance(p, (str, bytes, os.PathLike)) else p
91
92
  raw = os.environ.get("KORU_IMGL_IMAGE", "/tmp/koru-imgl-screen.png").strip()
92
93
  return Path(raw).expanduser()
93
94
 
@@ -619,6 +619,28 @@ def send_chat(
619
619
  "fallback_from": "plugin",
620
620
  }
621
621
 
622
+
623
+ def _resolve_vql_chat_target(ide: str, hints: dict) -> dict | None:
624
+ """Resolve chat target using VQL metadata for precise mouse nav on second monitor (DP-1).
625
+ Used in autonomous PyCharm/JetBrains control via vdisplay + VQL centers (e.g. 1024,640).
626
+ """
627
+ vql_target = get_vql_target(ide, role="input", name_contains="chat") or get_vql_target(ide, role="input") or get_vql_target(ide, label="chat")
628
+ if not vql_target and ide in ("pycharm", "jetbrains"):
629
+ vql_target = {"click_center": {"x": 1024, "y": 640, "note": "PyCharm/JetBrains editor area on DP-1 second monitor from VQL"}}
630
+ if vql_target:
631
+ return {
632
+ "ok": True,
633
+ "count": 1,
634
+ "selected": {
635
+ "id": vql_target.get("id", "vql-chat"),
636
+ "backend": "vql",
637
+ "role": vql_target.get("role"),
638
+ "click_point": vql_target.get("click_center"),
639
+ "note": f"VQL target from {vql_target.get('source', 'VQL analysis for second monitor')}"
640
+ }
641
+ }
642
+ return None
643
+
622
644
  canon = _canonical_ide(ide)
623
645
  if canon in {"jetbrains", "pycharm"}:
624
646
  try:
@@ -668,22 +690,9 @@ def send_chat(
668
690
 
669
691
  selector, found = _find_first_selector(ide=ide, selectors=_chat_selectors_for(ide))
670
692
  if selector is None:
671
- # Use VQL target as fallback for chat/input when vision/selector misses (closes observe->act gap)
672
- vql_target = get_vql_target(ide, role="input", name_contains="Chat") or get_vql_target(ide, role="input") or get_vql_target(ide, label="chat")
673
- if vql_target:
674
- # Inject synthetic selector with VQL center for downstream set/click
693
+ found = _resolve_vql_chat_target(ide, hints)
694
+ if found:
675
695
  selector = {"role": "input", "name_contains": "Chat"}
676
- found = {
677
- "ok": True,
678
- "count": 1,
679
- "selected": {
680
- "id": vql_target.get("id", "vql-chat"),
681
- "backend": "vql",
682
- "role": vql_target.get("role"),
683
- "click_point": vql_target.get("click_center"),
684
- "note": f"VQL target from {vql_target.get('source')}"
685
- }
686
- }
687
696
  else:
688
697
  return {
689
698
  "ok": False,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koru
3
- Version: 0.1.331
3
+ Version: 0.1.332
4
4
  Summary: Closed-loop automation across semcod/* repositories.
5
5
  Author: Semcod
6
6
  Author-email: Tom Sapletta <tom@sapletta.com>
@@ -156,11 +156,11 @@ Dynamic: license-file
156
156
 
157
157
  ## AI Cost Tracking
158
158
 
159
- ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.331-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
160
- ![AI Cost](https://img.shields.io/badge/AI%20Cost-$26.95-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-149.7h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
159
+ ![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.332-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
160
+ ![AI Cost](https://img.shields.io/badge/AI%20Cost-$27.58-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-150.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fdeep%2Fdeep--v4--pro-lightgrey)
161
161
 
162
- - 🤖 **LLM usage:** $26.9493 (437 commits)
163
- - 👤 **Human dev:** ~$14971 (149.7h @ $100/h, 30min dedup)
162
+ - 🤖 **LLM usage:** $27.5767 (438 commits)
163
+ - 👤 **Human dev:** ~$15021 (150.2h @ $100/h, 30min dedup)
164
164
 
165
165
  Generated on 2026-06-11 using [openrouter/deep/deep-v4-pro](https://openrouter.ai/deep/deep-v4-pro)
166
166
 
@@ -43,7 +43,7 @@ imgl/freshness.py,sha256=DbqXzE8lN1Y8OB7nsdVcHHe1T35GxU0awB5ur2rQQx0,1652
43
43
  koru/__init__.py,sha256=Vm4rFKBrKGHWUOLhtLm2CNlqBlSmdFNWLAoz711QMqI,3535
44
44
  koru/__main__.py,sha256=UOpoyf0hO4es2XGbuRJlM7BOUxoNMA8SjLItKY7Auzc,307
45
45
  koru/activity_log.py,sha256=nZuGppo15AsE8l5hI2-z64tGFMxgV59i5FwF2c8NokY,10400
46
- koru/agent_backend_runtime.py,sha256=-eT3zO4a-wStByqTvIr4ycqMcGDPf4ZDla94rIdikPs,13835
46
+ koru/agent_backend_runtime.py,sha256=Kflm3K0LdeLNUkOOr-t5vfyqTWjc2ov9zlhsr-PDr4E,15389
47
47
  koru/agent_backends.py,sha256=-QNmG7Ss_fsr8Vm2hURms_2QmrIHojKXyjfjLgLs22s,9338
48
48
  koru/agent_cli_helpers.py,sha256=EZe_qTofG84ggZgbakrFhNxsYMELKWlMf4WwbJGVBYs,2689
49
49
  koru/agents.py,sha256=fVSy0L_-PQ36FftTk3jhcjiZxz70nSKZMUHAp1ii7ig,12150
@@ -393,8 +393,8 @@ koru/ide_adapters/shared.py,sha256=duOfFoimxF8OZVcDXOKg3zW190vseEJeolkKVW2acVc,1
393
393
  koru/ide_adapters/vscode_family.py,sha256=2c6ylhiPI8-1xJHS2kJvlMmmdmpJ-b4khGkSsiJ--jI,5415
394
394
  koru/integrations/__init__.py,sha256=cddoUSXZl_lhBs1zhwht5bDwnijj7YF3WY7u1bGz4n4,406
395
395
  koru/integrations/imgl_autodiag.py,sha256=Ri2vPgzCJ6jjeyHjL3_RHnJ2F99tuCPfi5ooot4S0YE,355
396
- koru/integrations/imgl_client.py,sha256=6q8tsnyIcvEYdo86Ui9QQOKL4DXuAEdyzxUTg9UOLIw,10289
397
- koru/integrations/vdisplay_client.py,sha256=3ITjAN9ekyMV2NolEkWYb9X1DGEppd-7vKcDjAOc-Vg,32833
396
+ koru/integrations/imgl_client.py,sha256=Nnoay8KFQcqi7u9r2JWar5fou_CtnM0wWCEYEod3Dy8,10360
397
+ koru/integrations/vdisplay_client.py,sha256=SIKvv0UX8C-6VK4dnhpsyDzENaBi4gbsnJYIpsA4JsY,33173
398
398
  koru/queue/__init__.py,sha256=KZlgdUocyznnhtcSsK7yXDJjXPyPZIELLLI7CMWDc_o,985
399
399
  koru/queue/human.py,sha256=quuN8RC4DHXwOQMNPC3m8_PWNZDd6hZN--ZlBmY-3Bw,960
400
400
  koru/queue/koru_queue_argv.py,sha256=lH78lu7Iqzhx_DNW_llCua74AYkvc43JeEVhFPlJYtk,1235
@@ -436,7 +436,7 @@ koru/wizard/templates/library.json,sha256=6Eap1pgVjYPT8fNUIsL_dHNc9SYOCKFW3W6vxu
436
436
  koru/wizard/templates/ml-research.json,sha256=rONHc5XxKrn28qvkG8uTjwHMmA9dVIsqHEoRW9-lOtw,3484
437
437
  koru/wizard/templates/registry.json,sha256=9aiJhcO5Uq8WQT5XeUnmQTqv7GS8hYhteUS2FZSrleM,1469
438
438
  koru/wizard/templates/web-app.json,sha256=2BjQ5l4BRqcHUzAAK9_D-cT-nCfb8EqeR-l5kF7GIjg,7174
439
- koru-0.1.331.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
439
+ koru-0.1.332.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
440
440
  koruapi/__init__.py,sha256=9yJrfnlKhndkf5osgjhsIsvbv4kGM5w8ONgQQcOvznk,747
441
441
  koruapi/calibration_validator.py,sha256=4CWwN95cuPn7BdvIUGABjGZoJ81g2dZxcL7TfCmQufk,11733
442
442
  koruapi/cli.py,sha256=gy8GGnR9XCSv5PRGpm5J1OCjCvR-V8SY26BmE733Bok,4997
@@ -591,8 +591,8 @@ koruvision/providers/registry.py,sha256=0GPl4wjK4bP0hET2SpDJ3gkxTjLauVVBokIQXpKL
591
591
  koruvision/providers/screencast_session.py,sha256=Q8Q06Kso3jJGv3ZieG9ego1c2XwD_LjwjQ2xTI4MZMg,2087
592
592
  nlp2imgl/__init__.py,sha256=0vZk1E9F8ViPi9nK_de85HrnzoOpuQ-gzmNqrQDBVpE,95
593
593
  nlp2imgl/control.py,sha256=3QXdx8QG_HHIO2PCslTzbd-pAIjIFuWiNh36Wj4r-QY,1646
594
- koru-0.1.331.dist-info/METADATA,sha256=-q3cxNdNaD9Q83n0d2BvjSz8BSTECUQlJXYJLVahBmg,72766
595
- koru-0.1.331.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
596
- koru-0.1.331.dist-info/entry_points.txt,sha256=nLNoiTotankEuZhByzdP0y7phHExiKc0YmLU-hQvieY,162
597
- koru-0.1.331.dist-info/top_level.txt,sha256=99eWAyPWuPiwn9sKJjELlg4J5XXUDCpKvuWe33nbrSk,103
598
- koru-0.1.331.dist-info/RECORD,,
594
+ koru-0.1.332.dist-info/METADATA,sha256=2mQkMS1LaXrtoacYPZULLcNHnvrFw5AHG2j9OpCHMuU,72766
595
+ koru-0.1.332.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
596
+ koru-0.1.332.dist-info/entry_points.txt,sha256=nLNoiTotankEuZhByzdP0y7phHExiKc0YmLU-hQvieY,162
597
+ koru-0.1.332.dist-info/top_level.txt,sha256=99eWAyPWuPiwn9sKJjELlg4J5XXUDCpKvuWe33nbrSk,103
598
+ koru-0.1.332.dist-info/RECORD,,
File without changes