devops-bot-sdk 1.4.60__tar.gz → 1.4.62__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.60 → devops_bot_sdk-1.4.62}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/ipc/handlers.py +96 -45
  7. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/README.md +0 -0
  8. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  9. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  10. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  11. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/requires.txt +0 -0
  12. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  13. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/collectors/__init__.py +0 -0
  14. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/collectors/files.py +0 -0
  15. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/collectors/process.py +0 -0
  16. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/collectors/screenshot.py +0 -0
  17. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/config.py +0 -0
  18. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/crucial.py +0 -0
  19. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/exceptions.py +0 -0
  20. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/git_ops.py +0 -0
  21. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/graphify.py +0 -0
  22. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/hooks/__init__.py +0 -0
  23. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/hooks/crucial_guard.py +0 -0
  24. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/ipc/__init__.py +0 -0
  25. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/ipc/electron_bridge.py +0 -0
  26. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/local_exec.py +0 -0
  27. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/models/__init__.py +0 -0
  28. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/models/envelope.py +0 -0
  29. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/models/requests.py +0 -0
  30. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/models/responses.py +0 -0
  31. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/models/snapshots.py +0 -0
  32. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/py.typed +0 -0
  33. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/run_auto.py +0 -0
  34. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.60 → devops_bot_sdk-1.4.62}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.60
3
+ Version: 1.4.62
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.60
3
+ Version: 1.4.62
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.60"
7
+ version = "1.4.62"
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.60
3
+ Version: 1.4.62
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.60"
33
+ __version__ = "1.4.62"
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.60"
61
+ SDK_VERSION = "1.4.62"
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
@@ -854,10 +854,14 @@ def _build_prd_prompt(jira_key: str, summary: str, user_story: str | None,
854
854
 
855
855
  async def _handle_story(
856
856
  client: BackendClient, task: dict, payload: dict, send: Callable, thread_id: str,
857
- ) -> None:
857
+ ) -> str | None:
858
858
  """Run a Story: optionally generate a PRD (when instructed) into the description,
859
859
  then convert the ticket to a Task (To Do) for the coding run.
860
860
 
861
+ Returns the effective (PRD-enriched) description on success, or None on failure.
862
+ The caller uses the returned description to code the now-Task in the SAME run
863
+ (default `agentos-auto`); the `story` command discards it (convert-only).
864
+
861
865
  Best-effort. If a PRD was explicitly instructed but generation fails, the ticket
862
866
  goes to Backlog for retry (so the PRD requirement isn't silently dropped); a
863
867
  conversion failure likewise backlogs it.
@@ -955,9 +959,11 @@ async def _handle_story(
955
959
  type="step_update", thread_id=thread_id,
956
960
  data={"jira_task_id": tid,
957
961
  "current_step": f"Story {jira_key} → Task (To Do)"
958
- + (" + PRD in description" if prd_in_desc else "")
959
- + " — run again to implement"},
962
+ + (" + PRD in description" if prd_in_desc else "")},
960
963
  ).model_dump())
964
+ # The effective description (PRD-enriched when generated/present) so the caller
965
+ # can code the now-Task in the same run.
966
+ return new_description or user_story
961
967
 
962
968
 
963
969
  async def _run_local_pipeline(
@@ -1298,35 +1304,55 @@ async def _code_one_repo(
1298
1304
  except Exception: # noqa: BLE001
1299
1305
  branch_info = None
1300
1306
 
1301
- # 3a. Technical PRD for a CODEBASE task that doesn't have one yet. A Task created
1302
- # directly (not via a Story) may carry only a user-story/feature description —
1303
- # for codebase work we first turn the recon understanding into a technical PRD
1304
- # and build to it, exactly like a Story-derived Task. Resumes the recon session
1305
- # (already understands the codebase) so it's cheap; persists the PRD to the
1306
- # ticket and to pending (shared → other repos reuse it and skip regeneration).
1307
- # Skipped when a PRD is already present, on a resume, or for non-git targets.
1308
- if (is_git and recon_session and not resume_session_id
1309
- and _PRD_MARKER not in (pending.get("user_input") or "")):
1310
- await send(Envelope(
1311
- type="step_update", thread_id=thread_id,
1312
- data={"status": "running",
1313
- "current_step": f"[{name}] Codebase task — writing a technical PRD from the codebase"},
1314
- ).model_dump())
1307
+ # 3a. Technical PRD grounding (codebase tasks). The BACKEND converts a Story to a
1308
+ # Task and adds a FIRST-PASS PRD to the description; here we GROUND that PRD
1309
+ # against the REAL codebase (correcting file paths, contracts, data models to
1310
+ # what actually exists) before building to it. If no PRD is present at all
1311
+ # (edge case), we generate one. Reuses the recon session (already understands
1312
+ # the codebase), persists the result to the ticket + pending (shared → other
1313
+ # repos reuse it), and runs once per ticket. Skipped on a resume / non-git.
1314
+ if is_git and recon_session and not resume_session_id and not pending.get("_prd_grounded"):
1315
+ current = pending.get("user_input") or ""
1316
+ has_prd = _PRD_MARKER in current
1317
+ if has_prd:
1318
+ before, _, existing_prd = current.partition(_PRD_MARKER)
1319
+ await send(Envelope(
1320
+ type="step_update", thread_id=thread_id,
1321
+ data={"status": "running",
1322
+ "current_step": f"[{name}] Grounding the PRD against the current codebase"},
1323
+ ).model_dump())
1324
+ instruction = (
1325
+ "The task description contains a PRD (below), drafted by the planner and "
1326
+ "NOT necessarily written against THIS exact codebase. Using your "
1327
+ "exploration above, REFINE it so every file path, module, API contract, "
1328
+ "data model and integration matches what ACTUALLY exists in this repo. "
1329
+ "Keep the same PRD sections/format; correct anything inaccurate; do NOT "
1330
+ "invent. Output ONLY the refined PRD as Markdown.\n\n"
1331
+ "=== Current PRD ===\n" + existing_prd.strip()
1332
+ )
1333
+ else:
1334
+ before = current.rstrip() + "\n\n---\n\n"
1335
+ await send(Envelope(
1336
+ type="step_update", thread_id=thread_id,
1337
+ data={"status": "running",
1338
+ "current_step": f"[{name}] Codebase task — writing a technical PRD from the codebase"},
1339
+ ).model_dump())
1340
+ instruction = (
1341
+ "Using your exploration above, write a TECHNICAL PRD for this task that "
1342
+ "fully specifies the mechanism (how it works end-to-end) against the REAL "
1343
+ "code you found. Output ONLY the PRD as Markdown, following this format "
1344
+ "exactly:\n\n" + _PRD_FORMAT
1345
+ )
1315
1346
  prd_res = await local_exec.run_claude_local(
1316
- "Using your exploration above, write a TECHNICAL PRD for this task that fully "
1317
- "specifies the mechanism (how it works end-to-end) against the REAL code you "
1318
- "found. Output ONLY the PRD as Markdown, following this format exactly:\n\n"
1319
- + _PRD_FORMAT,
1320
- repo_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only
1347
+ instruction, repo_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only
1321
1348
  github_token=gh_token, resume_session_id=recon_session,
1322
1349
  )
1323
1350
  recon_session = prd_res.get("session_id") or recon_session
1324
1351
  prd = (prd_res.get("result") or "").strip() if prd_res.get("ok") else ""
1325
1352
  if prd:
1326
- enriched = (
1327
- f"{pending.get('user_input') or ''}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"
1328
- )[:20000]
1329
- pending["user_input"] = enriched # shared across repos → reused, not regenerated
1353
+ enriched = f"{before}{_PRD_MARKER}\n\n{prd}"[:20000]
1354
+ pending["user_input"] = enriched # shared across repos → reused
1355
+ pending["_prd_grounded"] = True # ground once per ticket
1330
1356
  try:
1331
1357
  await client.update_task_ml(task_id, {"description": enriched}, model_used=_EPIC_MODELS)
1332
1358
  except Exception: # noqa: BLE001 — persisting the PRD is best-effort
@@ -2846,38 +2872,63 @@ async def handle_orchestrate_auto_local_run(
2846
2872
  # never marked In Progress/Done — only ever converted
2847
2873
  # Task → implemented by the normal pipeline below (In Progress → Done)
2848
2874
  # Each is isolated: a failure must not abort the remaining queue.
2849
- if ttype in ("epic", "story"):
2850
- handler = _handle_epic if ttype == "epic" else _handle_story
2851
- code = "EPIC_DECOMPOSE_FAILED" if ttype == "epic" else "STORY_HANDLE_FAILED"
2852
- # Validation gate: Epics are always validated; a Story only when it
2853
- # is USER-driven (a SYSTEM/bot-created Story is trusted). A rejected
2854
- # ticket is commented + moved to Backlog inside the gate — skip it.
2855
- driven_by = str(task.get("drivenBy") or "").strip().upper()
2856
- needs_validation = (ttype == "epic") or (ttype == "story" and driven_by != "SYSTEM")
2875
+ # Validation gate: Epics always; a Story only when USER-driven (a
2876
+ # SYSTEM/bot-created Story is trusted). A rejected ticket is commented +
2877
+ # moved to Backlog inside the gate — skip it here.
2878
+ driven_by = str(task.get("drivenBy") or "").strip().upper()
2879
+
2880
+ if ttype == "epic":
2857
2881
  try:
2858
- if needs_validation and not await _validate_ticket_or_reject(
2859
- client, task, ttype, payload, send, thread_id
2860
- ):
2882
+ if not await _validate_ticket_or_reject(client, task, "epic", payload, send, thread_id):
2861
2883
  processed += 1
2862
2884
  continue
2863
- await handler(client, task, payload, send, thread_id)
2885
+ await _handle_epic(client, task, payload, send, thread_id)
2864
2886
  except Exception as exc: # noqa: BLE001
2865
- logger.warning("auto_local.%s_failed task=%s err=%s", ttype, tid, exc)
2887
+ logger.warning("auto_local.epic_failed task=%s err=%s", tid, exc)
2866
2888
  try:
2867
- await client.log_activity(f"Task {tid}: {ttype} handling errored — {str(exc)[:200]}")
2868
- # Backlog only Epics for retry; a Story is left To Do (never
2869
- # marked In Progress/Done/Backlog) so developers keep it.
2870
- if ttype == "epic":
2871
- await client.move_to_backlog(tid)
2889
+ await client.log_activity(f"Task {tid}: epic handling errored — {str(exc)[:200]}")
2890
+ await client.move_to_backlog(tid)
2872
2891
  except Exception: # noqa: BLE001
2873
2892
  pass
2874
2893
  await send(Envelope(
2875
2894
  type=Envelope.TYPE_ERROR, thread_id=thread_id,
2876
- data={"code": code, "task_id": tid, "message": str(exc)[:200]},
2895
+ data={"code": "EPIC_DECOMPOSE_FAILED", "task_id": tid, "message": str(exc)[:200]},
2877
2896
  ).model_dump())
2878
2897
  processed += 1
2879
2898
  continue
2880
2899
 
2900
+ if ttype == "story":
2901
+ try:
2902
+ if driven_by != "SYSTEM" and not await _validate_ticket_or_reject(
2903
+ client, task, "story", payload, send, thread_id
2904
+ ):
2905
+ processed += 1
2906
+ continue
2907
+ new_desc = await _handle_story(client, task, payload, send, thread_id)
2908
+ except Exception as exc: # noqa: BLE001
2909
+ logger.warning("auto_local.story_failed task=%s err=%s", tid, exc)
2910
+ await client.log_activity(f"Task {tid}: story handling errored — {str(exc)[:200]}")
2911
+ await send(Envelope(
2912
+ type=Envelope.TYPE_ERROR, thread_id=thread_id,
2913
+ data={"code": "STORY_HANDLE_FAILED", "task_id": tid, "message": str(exc)[:200]},
2914
+ ).model_dump())
2915
+ processed += 1
2916
+ continue
2917
+ # `agentos-auto story` (stories_only) = convert-only, stop here.
2918
+ # Conversion failure (new_desc None) also stops. Otherwise fall
2919
+ # through and code the now-Task IN THE SAME RUN (default agentos-auto).
2920
+ if stories_only or not new_desc:
2921
+ processed += 1
2922
+ continue
2923
+ task = {**task, "type": "Task", "description": new_desc}
2924
+ ttype = "task"
2925
+ await send(Envelope(
2926
+ type="step_update", thread_id=thread_id,
2927
+ data={"jira_task_id": tid,
2928
+ "current_step": f"{jira_key}: implementing the converted Task now (In Progress → Done)"},
2929
+ ).model_dump())
2930
+ # ↓ falls through to the normal Task pipeline below
2931
+
2881
2932
  task_payload = {
2882
2933
  "thread_id": thread_id,
2883
2934
  "jira_task_id": tid,