devops-bot-sdk 1.4.86__tar.gz → 1.4.88__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.86 → devops_bot_sdk-1.4.88}/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/pyproject.toml +1 -1
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/client.py +1 -1
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/ipc/handlers.py +119 -9
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/README.md +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/config.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/test.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/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.88"
|
|
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.88
|
|
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.88"
|
|
34
34
|
__author__ = "AgentOS"
|
|
35
35
|
|
|
36
36
|
from sdk.client import BackendClient
|
|
@@ -648,12 +648,18 @@ def _extract_email(text: str | None) -> str | None:
|
|
|
648
648
|
return m.group(0).strip().lower() if m else None
|
|
649
649
|
|
|
650
650
|
|
|
651
|
+
# Time/SLA fields inherited from the parent Epic onto each generated child.
|
|
652
|
+
# Passed through VERBATIM (exactly as the backend returned them on GET) so the
|
|
653
|
+
# POST never carries a reformatted-and-wrong date; absent/empty ones are dropped.
|
|
654
|
+
_EPIC_TIME_FIELDS = ("startDate", "endDate", "dueDate", "slaTarget", "slaTargets")
|
|
655
|
+
|
|
656
|
+
|
|
651
657
|
def _epic_inherited_fields(task: dict) -> dict:
|
|
652
658
|
"""Parent-Epic fields a child Task should inherit (drops empties).
|
|
653
659
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
660
|
+
Includes the Epic's time/SLA targets (startDate, endDate, dueDate,
|
|
661
|
+
slaTarget[s]) when present, so generated stories/tasks carry the same
|
|
662
|
+
deadlines and SL targets instead of being created without any.
|
|
657
663
|
"""
|
|
658
664
|
fields = {
|
|
659
665
|
"projectKey": task.get("projectKey"),
|
|
@@ -661,6 +667,11 @@ def _epic_inherited_fields(task: dict) -> dict:
|
|
|
661
667
|
"assigneeEmail": (task.get("assignee") or {}).get("email"),
|
|
662
668
|
"teamId": task.get("teamId"),
|
|
663
669
|
}
|
|
670
|
+
for k in _EPIC_TIME_FIELDS:
|
|
671
|
+
v = task.get(k)
|
|
672
|
+
# Scalars only: a nested object would be mangled by the multipart str().
|
|
673
|
+
if v and isinstance(v, (str, int, float)):
|
|
674
|
+
fields[k] = v
|
|
664
675
|
sprint = task.get("sprint") or {}
|
|
665
676
|
if sprint.get("jiraSprintId"):
|
|
666
677
|
fields["jiraSprintId"] = sprint["jiraSprintId"]
|
|
@@ -1158,6 +1169,28 @@ def _wants_prd(text: str | None) -> bool:
|
|
|
1158
1169
|
return bool(text and _PRD_RE.search(text))
|
|
1159
1170
|
|
|
1160
1171
|
|
|
1172
|
+
# An explicit directive to UPDATE the user story ITSELF ("update my user story
|
|
1173
|
+
# based on these parameters", "revise the story", "the user story should be
|
|
1174
|
+
# updated"). Here the story text IS the deliverable: its description is rewritten
|
|
1175
|
+
# per the given parameters and the ticket is marked Done — no Task conversion, no
|
|
1176
|
+
# PRD, no coding. The verb must sit DIRECTLY on the story object ("update the
|
|
1177
|
+
# user story"), so dev instructions that merely reference the story ("modify the
|
|
1178
|
+
# login form as per the user story") don't false-positive.
|
|
1179
|
+
_STORY_UPDATE_RE = re.compile(
|
|
1180
|
+
r"\b(?:update|revise|rewrite|refine|rework|amend|modify|edit)\s+"
|
|
1181
|
+
r"(?:(?:my|the|this|that|our|its)\s+)?(?:user[\s-]+)?stor(?:y|ies)\b"
|
|
1182
|
+
r"|\b(?:user[\s-]+)?stor(?:y|ies)\s+"
|
|
1183
|
+
r"(?:should\s+be|to\s+be|needs?(?:\s+to\s+be)?|must\s+be)\s+"
|
|
1184
|
+
r"(?:updated|revised|rewritten|refined|reworked|amended|modified)\b",
|
|
1185
|
+
re.I,
|
|
1186
|
+
)
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
def _wants_story_update(text: str | None) -> bool:
|
|
1190
|
+
"""True when the humanInstructions explicitly ask to update the user story itself."""
|
|
1191
|
+
return bool(text and _STORY_UPDATE_RE.search(text))
|
|
1192
|
+
|
|
1193
|
+
|
|
1161
1194
|
# Signals that a ticket is DEVELOPMENT work (→ always gets a PRD). Deliberately
|
|
1162
1195
|
# code-specific so Finance/HR/ops tickets don't false-positive (they get a PRD
|
|
1163
1196
|
# only when explicitly requested).
|
|
@@ -1208,11 +1241,33 @@ def _build_prd_prompt(jira_key: str, summary: str, user_story: str | None,
|
|
|
1208
1241
|
return "\n\n".join(parts)
|
|
1209
1242
|
|
|
1210
1243
|
|
|
1244
|
+
def _build_story_update_prompt(jira_key: str, summary: str, user_story: str | None,
|
|
1245
|
+
human_instructions: str) -> str:
|
|
1246
|
+
"""Prompt that REWRITES a user story's description per the operator's parameters."""
|
|
1247
|
+
parts: list[str] = [
|
|
1248
|
+
"UPDATE the user story below by applying the operator's parameters/changes. "
|
|
1249
|
+
"This is a TEXT revision ONLY — do NOT create, edit, or delete any file. Keep "
|
|
1250
|
+
"every part of the story that still applies, change exactly what the parameters "
|
|
1251
|
+
"require, and keep it business-focused (no code, no technical design).",
|
|
1252
|
+
f"=== USER STORY {jira_key} ===\nSummary: {summary}",
|
|
1253
|
+
"Current description:\n"
|
|
1254
|
+
+ (_clip_text(user_story.strip(), _GEN_MAX_CHARS)
|
|
1255
|
+
if user_story and user_story.strip() else "(none provided)"),
|
|
1256
|
+
"Operator parameters / instructions:\n" + human_instructions.strip(),
|
|
1257
|
+
"Output ONLY the full UPDATED user story as Markdown — no preamble, no "
|
|
1258
|
+
"commentary, no diff — following this format exactly:\n\n" + _USER_STORY_FORMAT,
|
|
1259
|
+
]
|
|
1260
|
+
return "\n\n".join(parts)
|
|
1261
|
+
|
|
1262
|
+
|
|
1211
1263
|
async def _handle_story(
|
|
1212
1264
|
client: BackendClient, task: dict, payload: dict, send: Callable, thread_id: str,
|
|
1213
1265
|
) -> str | None:
|
|
1214
|
-
"""Run a Story:
|
|
1215
|
-
|
|
1266
|
+
"""Run a Story: when the humanInstructions explicitly ask to UPDATE the user
|
|
1267
|
+
story itself, rewrite its description per those parameters and mark it Done
|
|
1268
|
+
(no conversion, no coding). Otherwise: optionally generate a PRD (when
|
|
1269
|
+
instructed) into the description, then convert the ticket to a Task (To Do)
|
|
1270
|
+
for the coding run.
|
|
1216
1271
|
|
|
1217
1272
|
Returns the effective (PRD-enriched) description on success, or None on failure.
|
|
1218
1273
|
The caller uses the returned description to code the now-Task in the SAME run
|
|
@@ -1229,6 +1284,56 @@ async def _handle_story(
|
|
|
1229
1284
|
summary = task.get("summary", "") or ""
|
|
1230
1285
|
user_story = task.get("description") or ""
|
|
1231
1286
|
human = task.get("humanInstructions")
|
|
1287
|
+
|
|
1288
|
+
# STEP 0 — explicit UPDATE directive ("update my user story based on these
|
|
1289
|
+
# parameters"): the story text is the deliverable. Rewrite the description per
|
|
1290
|
+
# the parameters and mark the Story Done — no Task conversion, no PRD, no
|
|
1291
|
+
# coding. This outranks the codebase heuristics below: the parameters may
|
|
1292
|
+
# mention dev terms (endpoints, repos) without asking for any code.
|
|
1293
|
+
if _wants_story_update(human):
|
|
1294
|
+
await send(Envelope(
|
|
1295
|
+
type="step_update", thread_id=thread_id,
|
|
1296
|
+
data={"jira_task_id": tid, "task_id": tid,
|
|
1297
|
+
"current_step": f"Story {jira_key}: updating the user story per the instructions"},
|
|
1298
|
+
).model_dump())
|
|
1299
|
+
gen = await _run_generation(
|
|
1300
|
+
_build_story_update_prompt(jira_key, summary, user_story, human),
|
|
1301
|
+
_resolve_local_project_path(task, payload),
|
|
1302
|
+
send=send, thread_id=thread_id,
|
|
1303
|
+
label=f"Story {jira_key}: updating user story",
|
|
1304
|
+
github_token=await client.github_token(),
|
|
1305
|
+
)
|
|
1306
|
+
updated = "" if gen.get("limit_exceeded") else (gen.get("result") or "").strip()
|
|
1307
|
+
if not updated:
|
|
1308
|
+
# Leave the Story To Do so a later run retries; never mark Done on failure.
|
|
1309
|
+
reason = (gen.get("error") or "update generation produced no content")[:200]
|
|
1310
|
+
await client.log_activity(
|
|
1311
|
+
f"Story {jira_key}: user-story update failed ({reason}) — left To Do for retry"
|
|
1312
|
+
)
|
|
1313
|
+
await send(Envelope(
|
|
1314
|
+
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
1315
|
+
data={"code": "STORY_UPDATE_FAILED", "task_id": tid, "message": reason},
|
|
1316
|
+
).model_dump())
|
|
1317
|
+
return None
|
|
1318
|
+
if not await client.update_task_ml(
|
|
1319
|
+
tid, {"description": updated[:20000], "status": "Done"}, model_used=_EPIC_MODELS,
|
|
1320
|
+
):
|
|
1321
|
+
await client.log_activity(f"Story {jira_key}: user-story update could not be saved")
|
|
1322
|
+
await send(Envelope(
|
|
1323
|
+
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
1324
|
+
data={"code": "STORY_UPDATE_FAILED", "task_id": tid,
|
|
1325
|
+
"message": "could not save the updated user story"},
|
|
1326
|
+
).model_dump())
|
|
1327
|
+
return None
|
|
1328
|
+
await client.log_activity(f"Story {jira_key}: user story updated per instructions — Done")
|
|
1329
|
+
await send(Envelope(
|
|
1330
|
+
type="step_update", thread_id=thread_id,
|
|
1331
|
+
data={"jira_task_id": tid,
|
|
1332
|
+
"current_step": f"Story {jira_key}: description updated → Done"},
|
|
1333
|
+
).model_dump())
|
|
1334
|
+
# The Story is complete — nothing to convert or code in this run.
|
|
1335
|
+
return None
|
|
1336
|
+
|
|
1232
1337
|
# STEP 1 — validate whether this is a CODEBASE (development) task: repo links /
|
|
1233
1338
|
# file paths in the instructions, or dev signals (PRs, branches, endpoints,
|
|
1234
1339
|
# deploy, …). A codebase task ALWAYS gets a technical PRD built from the current
|
|
@@ -3374,11 +3479,16 @@ async def handle_orchestrate_auto_local_run(
|
|
|
3374
3479
|
):
|
|
3375
3480
|
processed += 1
|
|
3376
3481
|
continue
|
|
3377
|
-
# agentMode gate: wait for human approval before
|
|
3482
|
+
# agentMode gate: wait for human approval before actioning the
|
|
3483
|
+
# Story (update-in-place when instructed, else convert to Task).
|
|
3378
3484
|
if agent_mode:
|
|
3485
|
+
action = (
|
|
3486
|
+
"update this Story's description per the instructions and mark it Done"
|
|
3487
|
+
if _wants_story_update(task.get("humanInstructions"))
|
|
3488
|
+
else "convert this Story into a Task"
|
|
3489
|
+
)
|
|
3379
3490
|
dec = await _gate_transform_approval(
|
|
3380
|
-
client, tid, jira_key, task.get("summary"),
|
|
3381
|
-
"convert this Story into a Task",
|
|
3491
|
+
client, tid, jira_key, task.get("summary"), action,
|
|
3382
3492
|
send, thread_id, should_stop,
|
|
3383
3493
|
)
|
|
3384
3494
|
if dec == "stopped":
|
|
@@ -3391,7 +3501,7 @@ async def handle_orchestrate_auto_local_run(
|
|
|
3391
3501
|
if dec != "approved":
|
|
3392
3502
|
await send(Envelope(
|
|
3393
3503
|
type="step_update", thread_id=thread_id,
|
|
3394
|
-
data={"current_step": f"{jira_key}: {dec} — Story not
|
|
3504
|
+
data={"current_step": f"{jira_key}: {dec} — Story not actioned"},
|
|
3395
3505
|
).model_dump())
|
|
3396
3506
|
processed += 1
|
|
3397
3507
|
continue
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.4.86 → devops_bot_sdk-1.4.88}/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
|