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.
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/ipc/handlers.py +124 -56
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/README.md +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/setup.cfg +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devops-bot-sdk"
|
|
7
|
-
version = "1.4.
|
|
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.
|
|
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.
|
|
33
|
+
__version__ = "1.4.62"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -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
|
-
"-
|
|
579
|
-
"
|
|
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
|
|
586
|
-
"credentials, external accounts, secrets, manual
|
|
587
|
-
"MUST be named
|
|
588
|
-
"
|
|
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
|
|
594
|
-
"
|
|
595
|
-
"
|
|
596
|
-
'
|
|
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
|
|
1285
|
-
#
|
|
1286
|
-
#
|
|
1287
|
-
#
|
|
1288
|
-
# (
|
|
1289
|
-
#
|
|
1290
|
-
#
|
|
1291
|
-
if
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1311
|
-
|
|
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
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
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
|
|
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
|
|
2885
|
+
await _handle_epic(client, task, payload, send, thread_id)
|
|
2847
2886
|
except Exception as exc: # noqa: BLE001
|
|
2848
|
-
logger.warning("auto_local
|
|
2887
|
+
logger.warning("auto_local.epic_failed task=%s err=%s", tid, exc)
|
|
2849
2888
|
try:
|
|
2850
|
-
await client.log_activity(f"Task {tid}:
|
|
2851
|
-
|
|
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":
|
|
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,
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.58 → devops_bot_sdk-1.4.62}/devops_bot_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|