devops-bot-sdk 1.4.150__tar.gz → 1.4.151__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 (95) hide show
  1. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/__init__.py +2 -2
  4. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/inventory.py +1 -0
  5. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/client.py +57 -0
  6. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/config.py +4 -1
  7. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/api.py +28 -2
  8. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/updater.py +3 -3
  9. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/README.md +0 -0
  10. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  11. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  12. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  13. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/devops_bot_sdk.egg-info/requires.txt +0 -0
  14. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  15. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/pyproject.toml +0 -0
  16. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agentd.py +0 -0
  17. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/__init__.py +0 -0
  18. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/builder.py +0 -0
  19. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/chat.py +0 -0
  20. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/connectors.py +0 -0
  21. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/engines/__init__.py +0 -0
  22. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/engines/claude_code.py +0 -0
  23. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/engines/http.py +0 -0
  24. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/engines/mcp.py +0 -0
  25. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/engines/n8n.py +0 -0
  26. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/engines/shell.py +0 -0
  27. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/integrations.py +0 -0
  28. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/library/__init__.py +0 -0
  29. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/manifest.py +0 -0
  30. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/prompt_studio.py +0 -0
  31. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/prompt_template.py +0 -0
  32. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/prompt_vars.py +0 -0
  33. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/run_log.py +0 -0
  34. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/secrets.py +0 -0
  35. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/usage.py +0 -0
  36. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/agents/varstore.py +0 -0
  37. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/cli.py +0 -0
  38. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/collectors/__init__.py +0 -0
  39. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/collectors/files.py +0 -0
  40. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/collectors/process.py +0 -0
  41. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/collectors/screenshot.py +0 -0
  42. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/crucial.py +0 -0
  43. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/design_verify/__init__.py +0 -0
  44. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/design_verify/assets.py +0 -0
  45. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/design_verify/bootstrap.py +0 -0
  46. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/design_verify/browser.py +0 -0
  47. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/design_verify/compare.py +0 -0
  48. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/design_verify/loop.py +0 -0
  49. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/exceptions.py +0 -0
  50. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/git_ops.py +0 -0
  51. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/graphify.py +0 -0
  52. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/hooks/__init__.py +0 -0
  53. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/hooks/crucial_guard.py +0 -0
  54. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ipc/__init__.py +0 -0
  55. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ipc/electron_bridge.py +0 -0
  56. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ipc/handlers.py +0 -0
  57. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/local_exec.py +0 -0
  58. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/models/__init__.py +0 -0
  59. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/models/envelope.py +0 -0
  60. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/models/requests.py +0 -0
  61. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/models/responses.py +0 -0
  62. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/models/snapshots.py +0 -0
  63. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/platform_compat.py +0 -0
  64. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/py.typed +0 -0
  65. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/run_auto.py +0 -0
  66. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/sse.py +0 -0
  67. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/test.py +0 -0
  68. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/test_pipeline.py +0 -0
  69. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/__init__.py +0 -0
  70. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/dashboard.py +0 -0
  71. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/health.py +0 -0
  72. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/ip_allowlist.py +0 -0
  73. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/jobs.py +0 -0
  74. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/models.py +0 -0
  75. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/runner.py +0 -0
  76. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/security.py +0 -0
  77. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/server.py +0 -0
  78. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/service.py +0 -0
  79. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/setup_manager.py +0 -0
  80. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/agents.html +0 -0
  81. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/app.js +0 -0
  82. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/custom-agents.html +0 -0
  83. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/default-agents.html +0 -0
  84. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/index.html +0 -0
  85. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/runs.html +0 -0
  86. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/setup.html +0 -0
  87. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/styles.css +0 -0
  88. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/sdk/ui/static/tickets.html +0 -0
  89. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/setup.cfg +0 -0
  90. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/tests/test_agent_chat_features.py +0 -0
  91. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/tests/test_agent_prompt_models.py +0 -0
  92. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/tests/test_agents_connectors_builder.py +0 -0
  93. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/tests/test_git_action.py +0 -0
  94. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/tests/test_health.py +0 -0
  95. {devops_bot_sdk-1.4.150 → devops_bot_sdk-1.4.151}/tests/test_updater_extras.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.150
3
+ Version: 1.4.151
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.150
3
+ Version: 1.4.151
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.4.150
3
+ Version: 1.4.151
4
4
 
5
5
  Public surface:
6
6
  BackendClient.from_config() — create client from ~/.agentos/config.toml
@@ -30,7 +30,7 @@ Rules:
30
30
  - All data egress through submit_webhook only
31
31
  """
32
32
 
33
- __version__ = "1.4.150" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
33
+ __version__ = "1.4.151" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
34
34
  __author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
35
35
 
36
36
  from sdk.client import BackendClient
@@ -26,6 +26,7 @@ def _map_backend_agent(row: dict, source: str) -> dict:
26
26
  sys_prompt = prompt.get("system_prompt", "") if isinstance(prompt, dict) else ""
27
27
  pvars = prompt.get("variables", []) if isinstance(prompt, dict) else []
28
28
  return {
29
+ "id": row.get("id"), # backend agentId — used for activity webhook
29
30
  "name": row.get("name"),
30
31
  "description": sys_prompt[:200],
31
32
  "variables": [v.get("name") for v in pvars if isinstance(v, dict) and v.get("name")],
@@ -37,6 +37,11 @@ from sdk.sse import _check_status, stream_with_reconnect
37
37
 
38
38
  logger = logging.getLogger(__name__)
39
39
 
40
+ # Process-lifetime cache of agent NAME → backend agentId (""=known non-backend).
41
+ # Used by resolve_agent_id so the agent-activity webhook fires only for backend
42
+ # agents without a lookup on every run.
43
+ _AGENT_ID_CACHE: dict[str, str] = {}
44
+
40
45
  # ── Ticket status-change observers ────────────────────────────────────────
41
46
  # Best-effort, synchronous hooks fired when the client changes a ticket's backend
42
47
  # status (In Progress / Backlog / To Do). The CLI loop registers one to track
@@ -1036,6 +1041,58 @@ class BackendClient:
1036
1041
  logger.warning("client.log_activity error err=%s", exc)
1037
1042
  return False
1038
1043
 
1044
+ async def log_agent_activity(self, activity: str, agent_id: str) -> bool:
1045
+ """POST {ML_GATEWAY_URL}/ml-api/agent-activity — record an agent-activity
1046
+ event for a **backend** agent (body ``{activity, agentId}``). Best-effort;
1047
+ never raises. No-op when ``agent_id`` is empty (local-only agents are not
1048
+ tracked — the backend is the source of truth for agents).
1049
+ """
1050
+ from sdk.config import ML_GATEWAY_URL
1051
+
1052
+ aid = (agent_id or "").strip()
1053
+ if not aid:
1054
+ return False
1055
+ url = f"{ML_GATEWAY_URL.rstrip('/')}/ml-api/agent-activity"
1056
+ headers = {**self._headers, "accept": "*/*", "developer-name": "null", "product": ""}
1057
+ try:
1058
+ async with httpx.AsyncClient(timeout=10.0) as client:
1059
+ resp = await client.post(
1060
+ url, json={"activity": str(activity), "agentId": aid}, headers=headers,
1061
+ )
1062
+ if resp.status_code >= 400:
1063
+ logger.warning("client.log_agent_activity failed status=%s body=%s",
1064
+ resp.status_code, resp.text[:200])
1065
+ return False
1066
+ return True
1067
+ except Exception as exc: # noqa: BLE001
1068
+ logger.warning("client.log_agent_activity error err=%s", exc)
1069
+ return False
1070
+
1071
+ async def resolve_agent_id(self, name: str) -> str | None:
1072
+ """Best-effort backend ``agentId`` for an agent NAME (custom + department).
1073
+
1074
+ Returns None when the name isn't a backend agent (e.g. a local-only manifest)
1075
+ — the caller then skips activity emit, so only backend agents are tracked.
1076
+ Cached per process (the id↔name mapping is stable within a session).
1077
+ """
1078
+ nm = (name or "").strip()
1079
+ if not nm:
1080
+ return None
1081
+ if nm in _AGENT_ID_CACHE:
1082
+ return _AGENT_ID_CACHE[nm] or None
1083
+ rows: list[dict] = []
1084
+ for fetch in (self.list_custom_agents, self.list_department_agents):
1085
+ try:
1086
+ rows += await fetch()
1087
+ except Exception: # noqa: BLE001 — best-effort across both sources
1088
+ pass
1089
+ for r in rows:
1090
+ if r.get("name") == nm and r.get("id"):
1091
+ _AGENT_ID_CACHE[nm] = str(r["id"])
1092
+ return _AGENT_ID_CACHE[nm]
1093
+ _AGENT_ID_CACHE[nm] = "" # negative-cache: not a backend agent
1094
+ return None
1095
+
1039
1096
  async def notify_whatsapp(
1040
1097
  self,
1041
1098
  task_id: str,
@@ -25,7 +25,10 @@ _CONFIG_FILE = _CONFIG_DIR / "config.toml"
25
25
  _SENTINEL = "co_"
26
26
 
27
27
  AI_DEFAULT_URL = "https://devopsbot-ai.apiswagger.co.uk"
28
- BE_DEFAULT_URL = "https://devopsbot-be.apiswagger.co.uk"
28
+ # ML backend / gateway — all /ml-api, /tasks-ml, /approvals-ml, agent-activity, etc.
29
+ # now go through the ML gateway (was devopsbot-be.apiswagger.co.uk).
30
+ BE_DEFAULT_URL = "https://gateway-ml.apiswagger.co.uk"
31
+ ML_GATEWAY_URL = "https://gateway-ml.apiswagger.co.uk"
29
32
 
30
33
 
31
34
  # ── Machine-derived Fernet key ────────────────────────────────────────
@@ -304,6 +304,28 @@ async def logs_stream() -> StreamingResponse:
304
304
  )
305
305
 
306
306
 
307
+ # ── Agent-activity webhook ──────────────────────────────────────────────────
308
+ # Every custom/default agent run/chat records to the LOCAL usage ledger AND emits
309
+ # a `{activity, agentId}` event to the ML gateway (POST /ml-api/agent-activity),
310
+ # which stores it for RBAC-scoped reporting via the chat endpoint. Only agents that
311
+ # exist in the backend (have an agentId) are emitted — local-only agents are skipped.
312
+ # Best-effort: never raises into the run.
313
+
314
+ async def _emit_agent_activity(name: str, kind: str, result: dict, duration_s: float = 0.0) -> None:
315
+ try:
316
+ from sdk.client import BackendClient
317
+ client = BackendClient.from_config()
318
+ agent_id = await client.resolve_agent_id(name)
319
+ if not agent_id: # local-only agent → not tracked
320
+ return
321
+ verb = "Chatted with" if kind == "chat" else "Ran"
322
+ status = "ok" if result.get("ok") else "error"
323
+ detail = f" ({round(duration_s, 1)}s)" if duration_s else ""
324
+ await client.log_agent_activity(f"{verb} agent '{name}' — {status}{detail}", agent_id)
325
+ except Exception: # noqa: BLE001 — activity emit must never break a run
326
+ pass
327
+
328
+
307
329
  # ── Agents: catalog, creation, chat, default agents ────────────────────────
308
330
 
309
331
  @router.get("/connectors", dependencies=[_auth])
@@ -689,12 +711,14 @@ async def agent_chat(body: AgentChatRequest) -> dict:
689
711
  started = _time.monotonic()
690
712
  result = await _run()
691
713
 
714
+ elapsed = _time.monotonic() - started
692
715
  usage_mod.record(
693
- name, kind="chat", duration_s=_time.monotonic() - started,
716
+ name, kind="chat", duration_s=elapsed,
694
717
  input_tokens=result.get("input_tokens", 0),
695
718
  output_tokens=result.get("output_tokens", 0),
696
719
  cost_usd=result.get("cost_usd", 0.0), ok=bool(result.get("ok")),
697
720
  )
721
+ await _emit_agent_activity(name, "chat", result, elapsed)
698
722
  return result
699
723
 
700
724
  # Run detached from this request: navigating away can't kill it. Poll
@@ -732,12 +756,14 @@ async def agent_run(body: AgentRunRequest) -> dict:
732
756
  started = _time.monotonic()
733
757
  result = await _run()
734
758
 
759
+ elapsed = _time.monotonic() - started
735
760
  usage_mod.record(
736
- name, kind="run", duration_s=_time.monotonic() - started,
761
+ name, kind="run", duration_s=elapsed,
737
762
  input_tokens=result.get("input_tokens", 0),
738
763
  output_tokens=result.get("output_tokens", 0),
739
764
  cost_usd=result.get("cost_usd", 0.0), ok=bool(result.get("ok")),
740
765
  )
766
+ await _emit_agent_activity(name, "run", result, elapsed)
741
767
  return result
742
768
 
743
769
  return {"job_id": jobs.start(work())}
@@ -132,10 +132,10 @@ def _ml_api_base() -> str:
132
132
  try:
133
133
  from sdk.config import get_base_url
134
134
  base = get_base_url()
135
- # devopsbot-ai.apiswagger.co.uk → devopsbot-be.apiswagger.co.uk
136
- return base.replace("devopsbot-ai", "devopsbot-be")
135
+ # devopsbot-ai.apiswagger.co.uk → gateway-ml.apiswagger.co.uk
136
+ return base.replace("devopsbot-ai.apiswagger.co.uk", "gateway-ml.apiswagger.co.uk")
137
137
  except Exception: # noqa: BLE001
138
- return "https://devopsbot-be.apiswagger.co.uk"
138
+ return "https://gateway-ml.apiswagger.co.uk"
139
139
 
140
140
 
141
141
  def _env_github_token() -> str | None: