devops-bot-sdk 1.4.67__tar.gz → 1.4.71__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.67 → devops_bot_sdk-1.4.71}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/pyproject.toml +1 -1
  4. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/client.py +1 -1
  6. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/ipc/handlers.py +32 -14
  7. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/run_auto.py +50 -1
  8. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/README.md +0 -0
  9. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  10. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  11. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  12. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/devops_bot_sdk.egg-info/requires.txt +0 -0
  13. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  14. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/collectors/__init__.py +0 -0
  15. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/collectors/files.py +0 -0
  16. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/collectors/process.py +0 -0
  17. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/collectors/screenshot.py +0 -0
  18. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/config.py +0 -0
  19. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/crucial.py +0 -0
  20. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/exceptions.py +0 -0
  21. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/git_ops.py +0 -0
  22. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/graphify.py +0 -0
  23. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/hooks/__init__.py +0 -0
  24. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/hooks/crucial_guard.py +0 -0
  25. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/ipc/__init__.py +0 -0
  26. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/ipc/electron_bridge.py +0 -0
  27. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/local_exec.py +0 -0
  28. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/models/__init__.py +0 -0
  29. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/models/envelope.py +0 -0
  30. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/models/requests.py +0 -0
  31. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/models/responses.py +0 -0
  32. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/models/snapshots.py +0 -0
  33. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/py.typed +0 -0
  34. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/sse.py +0 -0
  35. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/test.py +0 -0
  36. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/test_pipeline.py +0 -0
  37. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/sdk/updater.py +0 -0
  38. {devops_bot_sdk-1.4.67 → devops_bot_sdk-1.4.71}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.67
3
+ Version: 1.4.71
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.67
3
+ Version: 1.4.71
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.67"
7
+ version = "1.4.71"
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.67
3
+ Version: 1.4.71
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.67"
33
+ __version__ = "1.4.71"
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.67"
61
+ SDK_VERSION = "1.4.71"
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
@@ -521,14 +521,13 @@ def _build_epic_prompt(jira_key: str, summary: str, description: str | None,
521
521
  ' {\n'
522
522
  ' "summary": "Short imperative story title (a JIRA task summary)",\n'
523
523
  ' "description": "The story in the USER STORY FORMAT below (Markdown).",\n'
524
- ' "humanInstructions": "Operator instructions to implement THIS story. MUST '
525
- "repeat the same project location/path and constraints from the epic so the "
526
- 'coding run works in the right place.",\n'
527
524
  ' "assigneeEmail": "dev@company.com",\n'
528
525
  ' "points": 5\n'
529
526
  " }\n"
530
527
  " ]\n"
531
528
  "}\n\n"
529
+ "Do NOT produce humanInstructions for the stories — created stories are clean "
530
+ "user-story artifacts; implementation instructions are added later.\n"
532
531
  "Create as many stories as the epic genuinely needs (do not pad or truncate "
533
532
  "artificially); each story must be independently implementable.\n"
534
533
  "assigneeEmail is OPTIONAL per story: set it ONLY when the epic explicitly "
@@ -548,10 +547,11 @@ def _extract_json_block(text: str | None) -> dict | None:
548
547
  return None
549
548
  import json as _json
550
549
  candidates = re.findall(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.S | re.I)
551
- if not candidates:
552
- start, end = text.find("{"), text.rfind("}")
553
- if start != -1 and end > start:
554
- candidates = [text[start:end + 1]]
550
+ # ALWAYS also consider the widest brace span — covers a missing/late closing
551
+ # fence, extra prose around the block, or a fenced capture that fails to parse.
552
+ start, end = text.find("{"), text.rfind("}")
553
+ if start != -1 and end > start:
554
+ candidates.append(text[start:end + 1])
555
555
  for c in reversed(candidates):
556
556
  try:
557
557
  obj = _json.loads(c)
@@ -806,10 +806,26 @@ async def _handle_epic(
806
806
  project_path, allowed_tools=_RECON_ALLOWED_TOOLS, # read-only analysis
807
807
  github_token=await client.github_token(),
808
808
  )
809
- doc = _extract_json_block(gen.get("result")) if gen.get("ok") else None
809
+ # A usage-limit hit must stop and retry later. For ANY other outcome, still try
810
+ # to parse the JSON — the model often produces a valid decomposition even when the
811
+ # run is flagged is_error (e.g. a stray read-only tool denial during exploration),
812
+ # and the result then lands in `error`. Don't discard that work over an is_error flag.
813
+ if gen.get("limit_exceeded"):
814
+ await client.log_activity(f"Epic {jira_key}: usage limit during decomposition — will retry after reset")
815
+ await client.move_to_backlog(tid)
816
+ await send(Envelope(
817
+ type=Envelope.TYPE_ERROR, thread_id=thread_id,
818
+ data={"code": "EPIC_DECOMPOSE_LIMIT", "task_id": tid,
819
+ "message": "Claude usage limit reached during epic decomposition"},
820
+ ).model_dump())
821
+ return
822
+ doc = _extract_json_block(gen.get("result") or gen.get("error") or "")
810
823
  stories = (doc or {}).get("stories") or []
811
824
  if not doc or not stories:
812
- reason = (gen.get("error") or "could not generate a valid decomposition")[:200]
825
+ had_output = bool(gen.get("result") or gen.get("error"))
826
+ reason = ("model output did not contain a parseable decomposition JSON"
827
+ if had_output else
828
+ (gen.get("error") or "no output from the decomposition run"))[:300]
813
829
  await client.log_activity(f"Epic {jira_key}: decomposition failed — {reason}")
814
830
  await client.move_to_backlog(tid)
815
831
  await send(Envelope(
@@ -818,12 +834,12 @@ async def _handle_epic(
818
834
  ).model_dump())
819
835
  return
820
836
 
821
- # 2. Mark the Epic Done with the detailed description + plan.
822
- epic_desc = (doc.get("epic_description") or description or summary or "")[:20000]
837
+ # 2. Mark the Epic Done. Do NOT touch the Epic's description the parent Epic's
838
+ # description stays exactly as the human wrote it; only the status and the AI
839
+ # plan are recorded.
823
840
  plan = (doc.get("plan") or "")[:8000]
824
841
  await client.update_task_ml(
825
- tid, {"status": "Done", "description": epic_desc, "initialAiPlan": plan},
826
- model_used=_EPIC_MODELS,
842
+ tid, {"status": "Done", "initialAiPlan": plan}, model_used=_EPIC_MODELS,
827
843
  )
828
844
  await send(Envelope(
829
845
  type="step_update", thread_id=thread_id,
@@ -852,7 +868,9 @@ async def _handle_epic(
852
868
  "status": "To Do",
853
869
  "summary": str(st.get("summary", "")).strip()[:250],
854
870
  "description": str(st.get("description", "") or "")[:20000],
855
- "humanInstructions": str(st.get("humanInstructions") or human or "")[:20000] or None,
871
+ # NO humanInstructions on AI-created stories they are clean user-story
872
+ # artifacts; implementation instructions (repos/branch/etc.) are added
873
+ # later at the story/task stage, not baked in by the Epic.
856
874
  "parentTaskId": tid,
857
875
  }
858
876
  if child_email:
@@ -15,6 +15,7 @@ Commands:
15
15
  coding; no keys = all To Do Stories (alias: stories)
16
16
  agentos-auto stop ask the running loop to pause after the current task
17
17
  agentos-auto status show whether the background loop is running / stop pending
18
+ agentos-auto logs [-f] print the loop log (~/.agentos/auto.log); -f to follow live
18
19
  agentos-auto update upgrade the SDK to the latest version
19
20
 
20
21
  Options:
@@ -261,6 +262,50 @@ def _parse_ticket_keys(argv: list[str]) -> list[str]:
261
262
  return out
262
263
 
263
264
 
265
+ def _show_logs(argv: list[str]) -> None:
266
+ """`agentos-auto logs [-f] [--lines N]` — print the loop's log (~/.agentos/auto.log).
267
+
268
+ Prints the last N lines (default 200); with -f/--follow, then streams new lines
269
+ live (Ctrl-C to stop). Pure-Python tail — works on any platform, no `tail` needed.
270
+ """
271
+ import time
272
+
273
+ follow = "-f" in argv or "--follow" in argv
274
+ n = 200
275
+ if "--lines" in argv:
276
+ try:
277
+ n = max(1, int(argv[argv.index("--lines") + 1]))
278
+ except (IndexError, ValueError):
279
+ pass
280
+
281
+ if not _LOG_FILE.exists():
282
+ print(f"no log yet at {_LOG_FILE} — start a run with `agentos-auto`.")
283
+ print(f"per-run claude transcripts: {_AGENTOS_DIR / 'logs'}")
284
+ return
285
+
286
+ try:
287
+ tail = _LOG_FILE.read_text(encoding="utf-8", errors="replace").splitlines()[-n:]
288
+ except Exception as exc: # noqa: BLE001
289
+ print(f"could not read {_LOG_FILE}: {exc}")
290
+ return
291
+ for line in tail:
292
+ print(line)
293
+
294
+ if follow:
295
+ print(f"\n— following {_LOG_FILE} (Ctrl-C to stop) —", flush=True)
296
+ try:
297
+ with open(_LOG_FILE, "r", encoding="utf-8", errors="replace") as f:
298
+ f.seek(0, 2) # jump to end; only stream NEW lines
299
+ while True:
300
+ line = f.readline()
301
+ if line:
302
+ print(line, end="", flush=True)
303
+ else:
304
+ time.sleep(0.5)
305
+ except KeyboardInterrupt:
306
+ pass
307
+
308
+
264
309
  _current_task: str | None = None
265
310
 
266
311
 
@@ -464,6 +509,10 @@ def main() -> None:
464
509
  print("stop pending: yes" if _stop_requested() else "stop pending: no")
465
510
  return
466
511
 
512
+ if cmd == "logs":
513
+ _show_logs(argv)
514
+ return
515
+
467
516
  if cmd in ("run", "resume", "ticket", "tickets", "story", "stories"):
468
517
  # Run modes:
469
518
  # run/resume → the whole To Do queue
@@ -528,7 +577,7 @@ def main() -> None:
528
577
  _run_loop(workspace_root, only_keys, stories_only)
529
578
  return
530
579
 
531
- print(f"unknown command '{cmd}'. use: run | resume | ticket | story | stop | status | update")
580
+ print(f"unknown command '{cmd}'. use: run | resume | ticket | story | stop | status | logs | update")
532
581
  sys.exit(1)
533
582
 
534
583