devops-bot-sdk 1.4.74__tar.gz → 1.4.76__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 (38) hide show
  1. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/ipc/handlers.py +101 -7
  7. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/README.md +0 -0
  8. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  9. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  10. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  11. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/devops_bot_sdk.egg-info/requires.txt +0 -0
  12. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  13. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/collectors/__init__.py +0 -0
  14. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/collectors/files.py +0 -0
  15. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/collectors/process.py +0 -0
  16. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/collectors/screenshot.py +0 -0
  17. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/config.py +0 -0
  18. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/crucial.py +0 -0
  19. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/exceptions.py +0 -0
  20. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/git_ops.py +0 -0
  21. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/graphify.py +0 -0
  22. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/hooks/__init__.py +0 -0
  23. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/hooks/crucial_guard.py +0 -0
  24. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/ipc/__init__.py +0 -0
  25. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/ipc/electron_bridge.py +0 -0
  26. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/local_exec.py +0 -0
  27. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/models/__init__.py +0 -0
  28. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/models/envelope.py +0 -0
  29. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/models/requests.py +0 -0
  30. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/models/responses.py +0 -0
  31. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/models/snapshots.py +0 -0
  32. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/py.typed +0 -0
  33. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/run_auto.py +0 -0
  34. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.74 → devops_bot_sdk-1.4.76}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.74
3
+ Version: 1.4.76
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.74
3
+ Version: 1.4.76
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devops-bot-sdk"
7
- version = "1.4.74"
7
+ version = "1.4.76"
8
8
  description = "DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app"
9
9
  readme = "README.md"
10
10
  license = "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.74
3
+ Version: 1.4.76
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.74"
33
+ __version__ = "1.4.76"
34
34
  __author__ = "AgentOS"
35
35
 
36
36
  from sdk.client import BackendClient
@@ -58,7 +58,7 @@ def _notify_status(task_id: str, status: str) -> None:
58
58
  pass
59
59
 
60
60
 
61
- SDK_VERSION = "1.4.74"
61
+ SDK_VERSION = "1.4.76"
62
62
  _POLL_INTERVAL = 3.0
63
63
  _POLL_TIMEOUT = 600.0
64
64
  _ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — covers approval wait + VPS execution time
@@ -624,19 +624,17 @@ def _extract_email(text: str | None) -> str | None:
624
624
 
625
625
 
626
626
  def _epic_inherited_fields(task: dict) -> dict:
627
- """Parent-Epic fields a child Task should inherit (drops empties)."""
628
- def _date(v: object) -> str | None:
629
- s = str(v or "")
630
- return s[:10] if s else None # ISO datetime → YYYY-MM-DD
627
+ """Parent-Epic fields a child Task should inherit (drops empties).
631
628
 
629
+ Time-related fields (startDate, dueDate, slaTarget) are intentionally NOT
630
+ inherited/sent — they're optional on the POST and left for the backend/board to
631
+ set, so the create call never carries a stale or wrongly-formatted date.
632
+ """
632
633
  fields = {
633
634
  "projectKey": task.get("projectKey"),
634
635
  "priority": task.get("priority"),
635
636
  "assigneeEmail": (task.get("assignee") or {}).get("email"),
636
637
  "teamId": task.get("teamId"),
637
- "startDate": _date(task.get("startDate")),
638
- "dueDate": _date(task.get("dueDate")),
639
- "slaTarget": task.get("slaTarget"),
640
638
  }
641
639
  sprint = task.get("sprint") or {}
642
640
  if sprint.get("jiraSprintId"):
@@ -2995,6 +2993,60 @@ async def _poll_fresh_approval(
2995
2993
  return "timeout"
2996
2994
 
2997
2995
 
2996
+ async def _gate_transform_approval(
2997
+ client: BackendClient, tid: str, jira_key: str, summary: str | None, action: str,
2998
+ send: Callable, thread_id: str, should_stop: Callable[[], bool] | None,
2999
+ ) -> str:
3000
+ """Human-approval gate for a non-coding TRANSFORM (Epic decomposition /
3001
+ Story→Task conversion) when agentMode=True. Sends the WhatsApp request and WAITS
3002
+ for the decision. Returns 'approved' | 'rejected' | 'stopped'. On a 15-min timeout
3003
+ the ticket goes to Backlog and we keep polling for a late reply. Does NOT change
3004
+ ticket status itself — the handler owns that (an Epic marks itself In Progress; a
3005
+ Story never does)."""
3006
+ label = jira_key or tid
3007
+ wa_summary = summary or jira_key or "task"
3008
+ await send(Envelope(
3009
+ type=Envelope.TYPE_AWAITING_APPROVAL, thread_id=thread_id,
3010
+ data={"jira_key": jira_key, "task_id": tid,
3011
+ "current_step": f"{label}: awaiting approval to {action}"},
3012
+ ).model_dump())
3013
+
3014
+ async def _send_wa() -> bool:
3015
+ ok = await client.notify_whatsapp(
3016
+ tid, f"Approval needed for {label}: {action} — reply on WhatsApp to approve or reject.",
3017
+ summary=wa_summary,
3018
+ )
3019
+ note = "approval request sent" if ok else "approval request FAILED to send"
3020
+ await send(Envelope(
3021
+ type="step_update", thread_id=thread_id,
3022
+ data={"current_step": f"{label}: {note}"},
3023
+ ).model_dump())
3024
+ try:
3025
+ await client.log_activity(f"Task {tid}: {note} ({action})")
3026
+ except Exception: # noqa: BLE001
3027
+ pass
3028
+ return ok
3029
+
3030
+ await _send_wa()
3031
+ decision = await _poll_approval(
3032
+ client, tid, should_stop,
3033
+ fallback_notify=_send_wa, fallback_after_s=_FIRST_GATE_WA_FALLBACK_S,
3034
+ )
3035
+ if decision == "timeout":
3036
+ await client.move_to_backlog(tid)
3037
+ await send(Envelope(
3038
+ type="step_update", thread_id=thread_id,
3039
+ data={"current_step": f"{label}: no approval in 15m — Backlog; polling for late approval"},
3040
+ ).model_dump())
3041
+ while True:
3042
+ if should_stop is not None and should_stop():
3043
+ return "stopped"
3044
+ late = await client.approval_status(tid)
3045
+ if late in ("approved", "rejected"):
3046
+ return late
3047
+ await asyncio.sleep(_APPROVAL_POLL_INTERVAL_S)
3048
+ return decision
3049
+
2998
3050
 
2999
3051
  async def handle_orchestrate_auto_local_run(
3000
3052
  client: BackendClient, payload: dict, send: Callable,
@@ -3105,6 +3157,27 @@ async def handle_orchestrate_auto_local_run(
3105
3157
  if not await _validate_ticket_or_reject(client, task, "epic", payload, send, thread_id):
3106
3158
  processed += 1
3107
3159
  continue
3160
+ # agentMode gate: wait for human approval before decomposing.
3161
+ if agent_mode:
3162
+ dec = await _gate_transform_approval(
3163
+ client, tid, jira_key, task.get("summary"),
3164
+ "decompose this Epic into user stories",
3165
+ send, thread_id, should_stop,
3166
+ )
3167
+ if dec == "stopped":
3168
+ await send(Envelope(
3169
+ type=Envelope.TYPE_DONE, thread_id=thread_id,
3170
+ data={"mode": "orchestrate-auto-local", "status": "paused",
3171
+ "processed": processed, "task_count": total},
3172
+ ).model_dump())
3173
+ return
3174
+ if dec != "approved":
3175
+ await send(Envelope(
3176
+ type="step_update", thread_id=thread_id,
3177
+ data={"current_step": f"{jira_key}: {dec} — Epic not decomposed"},
3178
+ ).model_dump())
3179
+ processed += 1
3180
+ continue
3108
3181
  await _handle_epic(client, task, payload, send, thread_id)
3109
3182
  except Exception as exc: # noqa: BLE001
3110
3183
  logger.warning("auto_local.epic_failed task=%s err=%s", tid, exc)
@@ -3127,6 +3200,27 @@ async def handle_orchestrate_auto_local_run(
3127
3200
  ):
3128
3201
  processed += 1
3129
3202
  continue
3203
+ # agentMode gate: wait for human approval before converting Story→Task.
3204
+ if agent_mode:
3205
+ dec = await _gate_transform_approval(
3206
+ client, tid, jira_key, task.get("summary"),
3207
+ "convert this Story into a Task",
3208
+ send, thread_id, should_stop,
3209
+ )
3210
+ if dec == "stopped":
3211
+ await send(Envelope(
3212
+ type=Envelope.TYPE_DONE, thread_id=thread_id,
3213
+ data={"mode": "orchestrate-auto-local", "status": "paused",
3214
+ "processed": processed, "task_count": total},
3215
+ ).model_dump())
3216
+ return
3217
+ if dec != "approved":
3218
+ await send(Envelope(
3219
+ type="step_update", thread_id=thread_id,
3220
+ data={"current_step": f"{jira_key}: {dec} — Story not converted"},
3221
+ ).model_dump())
3222
+ processed += 1
3223
+ continue
3130
3224
  new_desc = await _handle_story(client, task, payload, send, thread_id)
3131
3225
  except Exception as exc: # noqa: BLE001
3132
3226
  logger.warning("auto_local.story_failed task=%s err=%s", tid, exc)