devops-bot-sdk 1.4.71__tar.gz → 1.4.74__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.71 → devops_bot_sdk-1.4.74}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/ipc/handlers.py +65 -13
  7. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/local_exec.py +6 -4
  8. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/README.md +0 -0
  9. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  10. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  11. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  12. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/devops_bot_sdk.egg-info/requires.txt +0 -0
  13. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  14. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/collectors/__init__.py +0 -0
  15. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/collectors/files.py +0 -0
  16. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/collectors/process.py +0 -0
  17. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/collectors/screenshot.py +0 -0
  18. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/config.py +0 -0
  19. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/crucial.py +0 -0
  20. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/exceptions.py +0 -0
  21. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/git_ops.py +0 -0
  22. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/graphify.py +0 -0
  23. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/hooks/__init__.py +0 -0
  24. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/hooks/crucial_guard.py +0 -0
  25. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/ipc/__init__.py +0 -0
  26. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/ipc/electron_bridge.py +0 -0
  27. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/models/__init__.py +0 -0
  28. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/models/envelope.py +0 -0
  29. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/models/requests.py +0 -0
  30. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/models/responses.py +0 -0
  31. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/models/snapshots.py +0 -0
  32. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/py.typed +0 -0
  33. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/run_auto.py +0 -0
  34. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.71 → devops_bot_sdk-1.4.74}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.71
3
+ Version: 1.4.74
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.71
3
+ Version: 1.4.74
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.71"
7
+ version = "1.4.74"
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.71
3
+ Version: 1.4.74
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.71"
33
+ __version__ = "1.4.74"
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.71"
61
+ SDK_VERSION = "1.4.74"
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
@@ -415,6 +415,56 @@ async def _run_with_heartbeat(coro, *, send: Callable, thread_id: str, label: st
415
415
  pass
416
416
 
417
417
 
418
+ async def _run_generation(
419
+ prompt: str, project_path: str, *, send: Callable, thread_id: str, label: str,
420
+ github_token: str | None = None, resume_session_id: str | None = None,
421
+ name: str | None = None,
422
+ ):
423
+ """Run a read-only generation (epic decomposition / PRD) with a heartbeat AND
424
+ output-token-cap chunking: if a single response is cut off at the per-response
425
+ output cap, resume the session and continue — concatenating the pieces — so a
426
+ large result (a big JSON / PRD) completes across chunks instead of truncating.
427
+ """
428
+ from sdk import local_exec
429
+
430
+ cap = local_exec._MAX_OUTPUT_TOKENS
431
+ res = await _run_with_heartbeat(
432
+ local_exec.run_claude_local(
433
+ prompt, project_path, allowed_tools=_RECON_ALLOWED_TOOLS,
434
+ github_token=github_token, resume_session_id=resume_session_id, max_output_tokens=cap,
435
+ ),
436
+ send=send, thread_id=thread_id, label=label, name=name,
437
+ )
438
+ combined = res.get("result") or ""
439
+ sid = res.get("session_id")
440
+ chunks = 0
441
+ while res.get("output_cap_exceeded") and sid and chunks < _MAX_OUTPUT_CAP_RESUMES:
442
+ chunks += 1
443
+ cap = min(cap * 2, 128000)
444
+ await send(Envelope(
445
+ type="step_update", thread_id=thread_id,
446
+ data={"status": "running",
447
+ "current_step": (f"[{name}] " if name else "") + f"{label}: response hit the "
448
+ f"output-token cap — continuing in chunks (part {chunks + 1})"},
449
+ ).model_dump())
450
+ res = await _run_with_heartbeat(
451
+ local_exec.run_claude_local(
452
+ "Your previous response was cut off at the output-token limit. Continue "
453
+ "EXACTLY where you left off — output ONLY the remaining content, with no "
454
+ "repetition and no preamble, so the two pieces concatenate seamlessly.",
455
+ project_path, allowed_tools=_RECON_ALLOWED_TOOLS,
456
+ github_token=github_token, resume_session_id=sid, max_output_tokens=cap,
457
+ ),
458
+ send=send, thread_id=thread_id, label=f"{label} (cont.)", name=name,
459
+ )
460
+ combined += res.get("result") or ""
461
+ sid = res.get("session_id") or sid
462
+ out = dict(res)
463
+ out["result"] = combined # the full, reassembled output
464
+ out["session_id"] = sid
465
+ return out
466
+
467
+
418
468
  # ── Epic decomposition ────────────────────────────────────────────────────
419
469
  # An EPIC is not coded directly. It is analysed (grounded in the codebase when a
420
470
  # project path exists) into a detailed user-story description + an implementation
@@ -801,9 +851,10 @@ async def _handle_epic(
801
851
  await graphify.prepare(project_path)
802
852
  except Exception: # noqa: BLE001
803
853
  pass
804
- gen = await local_exec.run_claude_local(
854
+ gen = await _run_generation(
805
855
  _build_epic_prompt(jira_key, summary, description, human),
806
- project_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only analysis
856
+ project_path, send=send, thread_id=thread_id,
857
+ label=f"Epic {jira_key}: generating user stories",
807
858
  github_token=await client.github_token(),
808
859
  )
809
860
  # A usage-limit hit must stop and retry later. For ANY other outcome, still try
@@ -1052,12 +1103,15 @@ async def _handle_story(
1052
1103
  await graphify.prepare(project_path)
1053
1104
  except Exception: # noqa: BLE001
1054
1105
  pass
1055
- gen = await local_exec.run_claude_local(
1106
+ gen = await _run_generation(
1056
1107
  _build_prd_prompt(jira_key, summary, user_story, human),
1057
- project_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only analysis
1108
+ project_path, send=send, thread_id=thread_id,
1109
+ label=f"Story {jira_key}: generating PRD",
1058
1110
  github_token=await client.github_token(),
1059
1111
  )
1060
- prd = (gen.get("result") or "").strip() if gen.get("ok") else ""
1112
+ # Use the output regardless of an is_error flag (a stray read-only tool denial
1113
+ # shouldn't discard a PRD the model actually produced); only a usage limit is fatal.
1114
+ prd = "" if gen.get("limit_exceeded") else (gen.get("result") or "").strip()
1061
1115
  if prd:
1062
1116
  # PRD lives alongside the user story in the (now Task) description.
1063
1117
  new_description = f"{user_story}\n\n---\n\n{_PRD_MARKER}\n\n{prd}"[:20000]
@@ -1488,16 +1542,14 @@ async def _code_one_repo(
1488
1542
  "code you found. Output ONLY the PRD as Markdown, following this format "
1489
1543
  "exactly:\n\n" + _PRD_FORMAT
1490
1544
  )
1491
- prd_res = await _run_with_heartbeat(
1492
- local_exec.run_claude_local(
1493
- instruction, repo_path, on_event=_on_event, # stream tool activity
1494
- allowed_tools=_RECON_ALLOWED_TOOLS, # read-only
1495
- github_token=gh_token, resume_session_id=recon_session,
1496
- ),
1497
- send=send, thread_id=thread_id, label="Working on the PRD", name=name,
1545
+ prd_res = await _run_generation(
1546
+ instruction, repo_path, send=send, thread_id=thread_id,
1547
+ label="Working on the PRD", name=name,
1548
+ github_token=gh_token, resume_session_id=recon_session, # continue the recon session
1498
1549
  )
1499
1550
  recon_session = prd_res.get("session_id") or recon_session
1500
- prd = (prd_res.get("result") or "").strip() if prd_res.get("ok") else ""
1551
+ # Use the PRD regardless of an is_error flag; only skip on a real usage limit.
1552
+ prd = "" if prd_res.get("limit_exceeded") else (prd_res.get("result") or "").strip()
1501
1553
  if prd:
1502
1554
  enriched = f"{before}{_PRD_MARKER}\n\n{prd}"[:20000]
1503
1555
  pending["user_input"] = enriched # shared across repos → reused
@@ -509,10 +509,12 @@ async def run_claude_local(
509
509
  result_text = str(result_event.get("result", ""))
510
510
  subtype = str(result_event.get("subtype", ""))
511
511
 
512
- # Claude USAGE/RATE limit: detected from the result/subtype even when the CLI
513
- # didn't flag is_error so a limit-truncated run is NOT treated as success
514
- # (which would wrongly mark the ticket Done).
515
- limit_hit = _is_limit(result_text) or "limit" in subtype.lower()
512
+ # Claude USAGE/RATE limit. A real limit ABORTS the run (is_error) with a limit
513
+ # message, or the CLI flags it in the subtype. Do NOT infer a limit from a
514
+ # SUCCESSFUL run's result text a task whose OUTPUT legitimately mentions
515
+ # "usage limit" / "rate limit" (e.g. building an observability/quota feature)
516
+ # would otherwise false-positive as a rate-limit hit.
517
+ limit_hit = "limit" in subtype.lower() or (is_error and _is_limit(result_text))
516
518
  reset_epoch = _parse_reset_epoch(result_text)
517
519
 
518
520
  err_text = None