forgexa-cli 1.82.0__tar.gz → 1.82.2__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.
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/PKG-INFO +1 -1
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/agent_core.py +20 -2
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/daemon.py +30 -13
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/pyproject.toml +1 -1
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/README.md +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/aws_evidence.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/aws_logs_evidence.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/observability_contract.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/observability_evidence.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/runtime_evidence.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli/zcode_protocol.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/setup.cfg +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.82.0 → forgexa_cli-1.82.2}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.82.
|
|
2
|
+
__version__ = "1.82.2"
|
|
@@ -239,6 +239,7 @@ def _assess_implementation_contributions(contract: dict, report: object) -> dict
|
|
|
239
239
|
task_types = {task["id"]: task.get("type") for task in contract.get("tasks") or []}
|
|
240
240
|
verification_ids = [task_id for task_id, task_type in task_types.items() if task_type in ("testing", "verify")]
|
|
241
241
|
verification_statuses = {row["task_id"]: row["status"] for row in report["task_checks"]}
|
|
242
|
+
pending_verification_ids = [task_id for task_id in verification_ids if verification_statuses.get(task_id) != "covered"]
|
|
242
243
|
checks = []
|
|
243
244
|
issues = []
|
|
244
245
|
pending = []
|
|
@@ -267,8 +268,25 @@ def _assess_implementation_contributions(contract: dict, report: object) -> dict
|
|
|
267
268
|
"task_id": task_id, "status": row.get("status"),
|
|
268
269
|
"description": row.get("description") or "Runtime acceptance remains pending",
|
|
269
270
|
"files": [], "evidence": row.get("evidence") or [],
|
|
270
|
-
} for task_id in
|
|
271
|
-
|
|
271
|
+
} for task_id in pending_verification_ids)
|
|
272
|
+
implementation_statuses = {row["task_id"]: row["status"] for row in checks}
|
|
273
|
+
acceptance_checks = []
|
|
274
|
+
for row in report["acceptance_checks"]:
|
|
275
|
+
if (
|
|
276
|
+
pending_verification_ids and row["status"] in ("blocked", "unverified")
|
|
277
|
+
and row.get("plan_revision") is None
|
|
278
|
+
and all(task_types.get(task_id) in ("coding", "fix") and implementation_statuses.get(task_id) == "covered"
|
|
279
|
+
for task_id in row["task_ids"])
|
|
280
|
+
):
|
|
281
|
+
row = {**row, "task_ids": [*row["task_ids"], *pending_verification_ids]}
|
|
282
|
+
pending.extend({
|
|
283
|
+
"task_id": task_id, "status": row["status"], "description": row["description"],
|
|
284
|
+
"files": row.get("files") or [], "evidence": row["evidence"],
|
|
285
|
+
} for task_id in pending_verification_ids)
|
|
286
|
+
acceptance_checks.append(row)
|
|
287
|
+
result = assess_implementation_audit({**contract, "version": 1}, {
|
|
288
|
+
**report, "task_checks": checks, "acceptance_checks": acceptance_checks,
|
|
289
|
+
})
|
|
272
290
|
result["report"] = report
|
|
273
291
|
result["issues"].extend(issues)
|
|
274
292
|
result["report_valid"] = not result["issues"]
|
|
@@ -1234,7 +1234,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
1234
1234
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
1235
1235
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
1236
1236
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
1237
|
-
DAEMON_VERSION = "1.82.
|
|
1237
|
+
DAEMON_VERSION = "1.82.2"
|
|
1238
1238
|
|
|
1239
1239
|
|
|
1240
1240
|
def _detect_client_type() -> str:
|
|
@@ -2212,6 +2212,7 @@ def _normalize_ai_job_output(
|
|
|
2212
2212
|
"qa_scenario_generate_prompt",
|
|
2213
2213
|
"qa_scenario_preview",
|
|
2214
2214
|
"qa_script_preview",
|
|
2215
|
+
"qa_recording_suggest",
|
|
2215
2216
|
"test_script_generate",
|
|
2216
2217
|
}:
|
|
2217
2218
|
return raw_output, None
|
|
@@ -9951,6 +9952,7 @@ class ServerConnection:
|
|
|
9951
9952
|
},
|
|
9952
9953
|
"implementation_audit": {"protocol_version": 3},
|
|
9953
9954
|
"task_delivery": {"protocol_version": 1},
|
|
9955
|
+
"recording_suggestions": {"protocol_version": 1, "isolated_workspace": True},
|
|
9954
9956
|
"task_attachment_staging": {"protocol_version": 1, "runtime_proxy": True, "image_agents": ["claude"]},
|
|
9955
9957
|
},
|
|
9956
9958
|
"server_url": self.server_url,
|
|
@@ -10376,6 +10378,7 @@ class ServerConnection:
|
|
|
10376
10378
|
},
|
|
10377
10379
|
"implementation_audit": {"protocol_version": 3},
|
|
10378
10380
|
"task_delivery": {"protocol_version": 1},
|
|
10381
|
+
"recording_suggestions": {"protocol_version": 1, "isolated_workspace": True},
|
|
10379
10382
|
"local_evidence": {"protocol_version": 1, "structured_summary": True},
|
|
10380
10383
|
"task_attachment_staging": {"protocol_version": 1, "runtime_proxy": True, "image_agents": ["claude"]},
|
|
10381
10384
|
},
|
|
@@ -13471,6 +13474,7 @@ class RuntimeDaemon:
|
|
|
13471
13474
|
result.status == "success"
|
|
13472
13475
|
and task.node_type in ("review", "coding", "testing")
|
|
13473
13476
|
and not bool((task.input_data or {}).get("verification_work_item"))
|
|
13477
|
+
and not isinstance((task.input_data or {}).get("approved_plan_baseline"), dict)
|
|
13474
13478
|
):
|
|
13475
13479
|
analysis_dir = (
|
|
13476
13480
|
(task.input_data or {}).get("analysis_output_dir", "")
|
|
@@ -15462,8 +15466,9 @@ class RuntimeDaemon:
|
|
|
15462
15466
|
if task_type == "deployment_merge":
|
|
15463
15467
|
await self._execute_deployment_merge_job(aj, conn)
|
|
15464
15468
|
return
|
|
15465
|
-
project_info =
|
|
15466
|
-
|
|
15469
|
+
project_info = ({"id": job_id, "project_key": f"recording-{job_id}", "name": "Recording suggestions", "repo_url": ""}
|
|
15470
|
+
if task_type == "qa_recording_suggest" else aj.get("project", {}))
|
|
15471
|
+
requirement_key = None if task_type == "qa_recording_suggest" else aj.get("requirement_key")
|
|
15467
15472
|
agent_override = aj.get("agent_override")
|
|
15468
15473
|
system_prompt = aj.get("system_prompt", "")
|
|
15469
15474
|
user_prompt = aj.get("user_prompt", "")
|
|
@@ -15491,6 +15496,7 @@ class RuntimeDaemon:
|
|
|
15491
15496
|
)
|
|
15492
15497
|
await project_lock.acquire()
|
|
15493
15498
|
workspace_execution_lock: _WorkspaceFileLock | None = None
|
|
15499
|
+
recording_workspace = None
|
|
15494
15500
|
|
|
15495
15501
|
try:
|
|
15496
15502
|
# Report progress: starting
|
|
@@ -15553,7 +15559,7 @@ class RuntimeDaemon:
|
|
|
15553
15559
|
# 2. Prepare workspace (using project info + requirement branch)
|
|
15554
15560
|
full_prompt = f"{system_prompt}\n\n{user_prompt}" if system_prompt else user_prompt
|
|
15555
15561
|
task_timeout = settings.AGENT_TIMEOUT
|
|
15556
|
-
if task_type in {"qa_scenario_preview", "qa_script_preview"}:
|
|
15562
|
+
if task_type in {"qa_scenario_preview", "qa_script_preview", "qa_recording_suggest"}:
|
|
15557
15563
|
try:
|
|
15558
15564
|
task_timeout = min(
|
|
15559
15565
|
max(int(input_ctx.get("timeout_seconds", settings.AGENT_TIMEOUT)), 30),
|
|
@@ -15583,13 +15589,19 @@ class RuntimeDaemon:
|
|
|
15583
15589
|
workspace_source_mode=qa_ws_source.get("mode"),
|
|
15584
15590
|
workspace_branch=qa_ws_source.get("branch"),
|
|
15585
15591
|
)
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15592
|
+
if task_type == "qa_recording_suggest":
|
|
15593
|
+
import tempfile as _recording_tempfile
|
|
15594
|
+
|
|
15595
|
+
recording_workspace = _recording_tempfile.TemporaryDirectory(prefix="forgexa-recording-suggest-")
|
|
15596
|
+
workspace_path = Path(recording_workspace.name)
|
|
15597
|
+
else:
|
|
15598
|
+
workspace_execution_lock = await self.workspace_manager.acquire_workspace_execution_lock(
|
|
15599
|
+
project_info,
|
|
15600
|
+
fake_task,
|
|
15601
|
+
)
|
|
15602
|
+
workspace_path = await self.workspace_manager.prepare_workspace(
|
|
15603
|
+
project_info, fake_task,
|
|
15604
|
+
)
|
|
15593
15605
|
|
|
15594
15606
|
if task_type == "qa_scenario_generate_prompt" and not input_ctx.get("project_context"):
|
|
15595
15607
|
local_project_context = _summarize_workspace_source(workspace_path)
|
|
@@ -15829,7 +15841,7 @@ class RuntimeDaemon:
|
|
|
15829
15841
|
# Pushing here is dangerous because it can propagate stale local branch
|
|
15830
15842
|
# state even when the AIJob produced no repo changes at all.
|
|
15831
15843
|
git_info = {}
|
|
15832
|
-
if result.status == "success":
|
|
15844
|
+
if result.status == "success" and task_type != "qa_recording_suggest":
|
|
15833
15845
|
git_info = await self._collect_workspace_git_info(workspace_path)
|
|
15834
15846
|
|
|
15835
15847
|
# 5. Report completion
|
|
@@ -15850,7 +15862,7 @@ class RuntimeDaemon:
|
|
|
15850
15862
|
scenario_ids=input_ctx.get("scenario_ids", []),
|
|
15851
15863
|
workspace_path=workspace_path,
|
|
15852
15864
|
)
|
|
15853
|
-
if task_type in {"qa_scenario_generate_prompt", "qa_scenario_preview", "qa_script_preview"}:
|
|
15865
|
+
if task_type in {"qa_scenario_generate_prompt", "qa_scenario_preview", "qa_script_preview", "qa_recording_suggest"}:
|
|
15854
15866
|
output_content, scenario_items = _prepare_ai_job_output(
|
|
15855
15867
|
task_type=task_type,
|
|
15856
15868
|
agent_id=agent.agent_id,
|
|
@@ -15913,6 +15925,11 @@ class RuntimeDaemon:
|
|
|
15913
15925
|
except Exception:
|
|
15914
15926
|
pass
|
|
15915
15927
|
finally:
|
|
15928
|
+
if recording_workspace is not None:
|
|
15929
|
+
try:
|
|
15930
|
+
recording_workspace.cleanup()
|
|
15931
|
+
except OSError:
|
|
15932
|
+
logger.warning("Recording suggestion temporary workspace cleanup failed")
|
|
15916
15933
|
if workspace_execution_lock is not None:
|
|
15917
15934
|
workspace_execution_lock.release()
|
|
15918
15935
|
project_lock.release()
|
|
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
|