devops-bot-sdk 1.4.58__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.58 → devops_bot_sdk-1.4.62}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/ipc/handlers.py +124 -56
  7. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/README.md +0 -0
  8. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  9. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  10. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  11. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/requires.txt +0 -0
  12. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  13. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/__init__.py +0 -0
  14. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/files.py +0 -0
  15. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/process.py +0 -0
  16. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/screenshot.py +0 -0
  17. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/config.py +0 -0
  18. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/crucial.py +0 -0
  19. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/exceptions.py +0 -0
  20. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/git_ops.py +0 -0
  21. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/graphify.py +0 -0
  22. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/hooks/__init__.py +0 -0
  23. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/hooks/crucial_guard.py +0 -0
  24. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/ipc/__init__.py +0 -0
  25. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/ipc/electron_bridge.py +0 -0
  26. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/local_exec.py +0 -0
  27. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/__init__.py +0 -0
  28. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/envelope.py +0 -0
  29. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/requests.py +0 -0
  30. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/responses.py +0 -0
  31. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/snapshots.py +0 -0
  32. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/py.typed +0 -0
  33. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/run_auto.py +0 -0
  34. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.58 → 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.58
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.58
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.58"
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.58
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.58"
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.58"
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
@@ -573,27 +573,37 @@ def _build_validation_prompt(kind: str, jira_key: str, summary: str,
573
573
  "Human Instructions:\n" + (human.strip() if human and human.strip() else "(none provided)"),
574
574
  ]
575
575
  parts.append(
576
+ "Judge the ticket in the context of what it ACTUALLY asks for — it may be "
577
+ "software, documentation, analysis, Finance, HR or operations work. Do NOT "
578
+ "impose software requirements on a non-software ticket.\n\n"
576
579
  "A VALID ticket MUST have:\n"
577
580
  "- A clear, unambiguous goal and scope (no hand-waving).\n"
578
- "- Explicit Human Instructions: for codebase work, the repository link(s) "
579
- "and/or file path(s), the branch/PR convention, and build/run/test steps.\n"
580
- "- Observable, testable acceptance criteria.\n"
581
+ "- A clearly-defined deliverable or outcome, and when a location matters — "
582
+ "WHERE to produce it (a folder path, a repository, or a document).\n"
583
+ "- Observable, testable acceptance criteria (or an equally concrete definition of done).\n"
584
+ "- ONLY IF the work requires CODE CHANGES to a software project: the target "
585
+ "repository link(s) and/or file path(s), the branch/PR convention, and "
586
+ "build/run/test steps. Do NOT demand these for documentation, content, analysis "
587
+ "or other non-code deliverables.\n"
581
588
  + ("- Enough concrete detail to decompose into independent user stories.\n"
582
589
  if kind == "epic" else
583
590
  "- A clear 'As a <role>, I want <capability>, so that <benefit>' intent plus "
584
591
  "testable acceptance criteria.\n")
585
- + "- Any THIRD-PARTY INTEGRATION or human-effort dependency (API keys, OAuth "
586
- "credentials, external accounts, secrets, manual provisioning, DNS, billing) "
587
- "MUST be named explicitly WITH who provides it. If such a dependency is implied "
588
- "but undefined/missing, REJECT.\n\n"
592
+ + "- Any THIRD-PARTY INTEGRATION or human-effort dependency the work GENUINELY "
593
+ "needs (API keys, OAuth credentials, external accounts, secrets, manual "
594
+ "provisioning, DNS, billing) MUST be named WITH who provides it; if such a "
595
+ "REQUIRED dependency is undefined, REJECT.\n\n"
596
+ "A ticket that clearly states its deliverable and where to produce it is VALID "
597
+ "even without repository/branch details. Reject only for genuinely missing or "
598
+ "ambiguous information — not for absent software details the task does not need.\n\n"
589
599
  "Output ONLY a fenced ```json block, nothing else:\n"
590
600
  "{\n"
591
601
  ' "valid": true or false,\n'
592
602
  ' "comment": "When invalid: a concise, TECHNICAL rejection that lists EXACTLY '
593
- "what is missing or ambiguous and must be added. Use precise engineering "
594
- "terminology (API contract, data model/schema, auth/OAuth flow, env/config, "
595
- "acceptance criteria, target repository/branch, integration credentials & "
596
- 'ownership). When valid: empty string."\n'
603
+ "what is missing or ambiguous and must be added, in terms appropriate to the "
604
+ "ticket's domain (for software: API contract, data model/schema, auth flow, "
605
+ "env/config, target repository/branch; for other work: the missing deliverable, "
606
+ 'location, criteria, or dependency). When valid: empty string."\n'
597
607
  "}"
598
608
  )
599
609
  return "\n\n".join(parts)
@@ -651,6 +661,13 @@ async def _validate_ticket_or_reject(
651
661
  "criteria; and the credentials/ownership for any third-party integration.")
652
662
  body = f"{_AI_COMMENT_LABEL}\n\n{comment or fallback}"
653
663
  await client.add_comment(tid, body)
664
+ # The backend only accepts a Backlog transition when a timer is running, so start
665
+ # one (mark In Progress) then park it — otherwise move_to_backlog 404s with
666
+ # "No running timer found". This In-Progress blip is the reject/park path only.
667
+ try:
668
+ await client.mark_in_progress(tid)
669
+ except Exception: # noqa: BLE001
670
+ pass
654
671
  await client.move_to_backlog(tid)
655
672
  await client.log_activity(f"{kind} {jira_key}: REJECTED by validation gate — commented + moved to Backlog")
656
673
  await send(Envelope(
@@ -837,10 +854,14 @@ def _build_prd_prompt(jira_key: str, summary: str, user_story: str | None,
837
854
 
838
855
  async def _handle_story(
839
856
  client: BackendClient, task: dict, payload: dict, send: Callable, thread_id: str,
840
- ) -> None:
857
+ ) -> str | None:
841
858
  """Run a Story: optionally generate a PRD (when instructed) into the description,
842
859
  then convert the ticket to a Task (To Do) for the coding run.
843
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
+
844
865
  Best-effort. If a PRD was explicitly instructed but generation fails, the ticket
845
866
  goes to Backlog for retry (so the PRD requirement isn't silently dropped); a
846
867
  conversion failure likewise backlogs it.
@@ -938,9 +959,11 @@ async def _handle_story(
938
959
  type="step_update", thread_id=thread_id,
939
960
  data={"jira_task_id": tid,
940
961
  "current_step": f"Story {jira_key} → Task (To Do)"
941
- + (" + PRD in description" if prd_in_desc else "")
942
- + " — run again to implement"},
962
+ + (" + PRD in description" if prd_in_desc else "")},
943
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
944
967
 
945
968
 
946
969
  async def _run_local_pipeline(
@@ -1281,35 +1304,55 @@ async def _code_one_repo(
1281
1304
  except Exception: # noqa: BLE001
1282
1305
  branch_info = None
1283
1306
 
1284
- # 3a. Technical PRD for a CODEBASE task that doesn't have one yet. A Task created
1285
- # directly (not via a Story) may carry only a user-story/feature description
1286
- # for codebase work we first turn the recon understanding into a technical PRD
1287
- # and build to it, exactly like a Story-derived Task. Resumes the recon session
1288
- # (already understands the codebase) so it's cheap; persists the PRD to the
1289
- # ticket and to pending (shared → other repos reuse it and skip regeneration).
1290
- # Skipped when a PRD is already present, on a resume, or for non-git targets.
1291
- if (is_git and recon_session and not resume_session_id
1292
- and _PRD_MARKER not in (pending.get("user_input") or "")):
1293
- await send(Envelope(
1294
- type="step_update", thread_id=thread_id,
1295
- data={"status": "running",
1296
- "current_step": f"[{name}] Codebase task — writing a technical PRD from the codebase"},
1297
- ).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
+ )
1298
1346
  prd_res = await local_exec.run_claude_local(
1299
- "Using your exploration above, write a TECHNICAL PRD for this task that fully "
1300
- "specifies the mechanism (how it works end-to-end) against the REAL code you "
1301
- "found. Output ONLY the PRD as Markdown, following this format exactly:\n\n"
1302
- + _PRD_FORMAT,
1303
- repo_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only
1347
+ instruction, repo_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only
1304
1348
  github_token=gh_token, resume_session_id=recon_session,
1305
1349
  )
1306
1350
  recon_session = prd_res.get("session_id") or recon_session
1307
1351
  prd = (prd_res.get("result") or "").strip() if prd_res.get("ok") else ""
1308
1352
  if prd:
1309
- enriched = (
1310
- f"{pending.get('user_input') or ''}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"
1311
- )[:20000]
1312
- 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
1313
1356
  try:
1314
1357
  await client.update_task_ml(task_id, {"description": enriched}, model_used=_EPIC_MODELS)
1315
1358
  except Exception: # noqa: BLE001 — persisting the PRD is best-effort
@@ -2829,38 +2872,63 @@ async def handle_orchestrate_auto_local_run(
2829
2872
  # never marked In Progress/Done — only ever converted
2830
2873
  # Task → implemented by the normal pipeline below (In Progress → Done)
2831
2874
  # Each is isolated: a failure must not abort the remaining queue.
2832
- if ttype in ("epic", "story"):
2833
- handler = _handle_epic if ttype == "epic" else _handle_story
2834
- code = "EPIC_DECOMPOSE_FAILED" if ttype == "epic" else "STORY_HANDLE_FAILED"
2835
- # Validation gate: Epics are always validated; a Story only when it
2836
- # is USER-driven (a SYSTEM/bot-created Story is trusted). A rejected
2837
- # ticket is commented + moved to Backlog inside the gate — skip it.
2838
- driven_by = str(task.get("drivenBy") or "").strip().upper()
2839
- 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":
2840
2881
  try:
2841
- if needs_validation and not await _validate_ticket_or_reject(
2842
- client, task, ttype, payload, send, thread_id
2843
- ):
2882
+ if not await _validate_ticket_or_reject(client, task, "epic", payload, send, thread_id):
2844
2883
  processed += 1
2845
2884
  continue
2846
- await handler(client, task, payload, send, thread_id)
2885
+ await _handle_epic(client, task, payload, send, thread_id)
2847
2886
  except Exception as exc: # noqa: BLE001
2848
- 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)
2849
2888
  try:
2850
- await client.log_activity(f"Task {tid}: {ttype} handling errored — {str(exc)[:200]}")
2851
- # Backlog only Epics for retry; a Story is left To Do (never
2852
- # marked In Progress/Done/Backlog) so developers keep it.
2853
- if ttype == "epic":
2854
- 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)
2855
2891
  except Exception: # noqa: BLE001
2856
2892
  pass
2857
2893
  await send(Envelope(
2858
2894
  type=Envelope.TYPE_ERROR, thread_id=thread_id,
2859
- data={"code": code, "task_id": tid, "message": str(exc)[:200]},
2895
+ data={"code": "EPIC_DECOMPOSE_FAILED", "task_id": tid, "message": str(exc)[:200]},
2860
2896
  ).model_dump())
2861
2897
  processed += 1
2862
2898
  continue
2863
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
+
2864
2932
  task_payload = {
2865
2933
  "thread_id": thread_id,
2866
2934
  "jira_task_id": tid,