forgexa-cli 1.18.10__tar.gz → 1.18.12__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.18.10 → forgexa_cli-1.18.12}/PKG-INFO +1 -1
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli/daemon.py +83 -8
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/pyproject.toml +1 -1
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/README.md +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/setup.cfg +0 -0
- {forgexa_cli-1.18.10 → forgexa_cli-1.18.12}/tests/test_auth_and_runtime_commands.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.18.
|
|
2
|
+
__version__ = "1.18.12"
|
|
@@ -555,7 +555,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
555
555
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
556
556
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
557
557
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
558
|
-
DAEMON_VERSION = "1.18.
|
|
558
|
+
DAEMON_VERSION = "1.18.12"
|
|
559
559
|
|
|
560
560
|
|
|
561
561
|
def _detect_client_type() -> str:
|
|
@@ -7306,6 +7306,43 @@ class RuntimeDaemon:
|
|
|
7306
7306
|
result.metrics["recovered_from_timeout_uncommitted_changes"] = True
|
|
7307
7307
|
return True
|
|
7308
7308
|
|
|
7309
|
+
async def _discard_verification_scope_changes(
|
|
7310
|
+
self,
|
|
7311
|
+
workspace_path: Path,
|
|
7312
|
+
task: TaskInfo,
|
|
7313
|
+
result: TaskResult,
|
|
7314
|
+
before_sha: str,
|
|
7315
|
+
) -> None:
|
|
7316
|
+
"""Discard protected analysis changes from a failed verification task."""
|
|
7317
|
+
if not bool((task.input_data or {}).get("verification_work_item")):
|
|
7318
|
+
return
|
|
7319
|
+
|
|
7320
|
+
forbidden_paths = sorted(
|
|
7321
|
+
path
|
|
7322
|
+
for path in ProcessManager._normalize_repo_paths(result.files_changed)
|
|
7323
|
+
if _is_requirement_analysis_path(path)
|
|
7324
|
+
)
|
|
7325
|
+
if not forbidden_paths:
|
|
7326
|
+
return
|
|
7327
|
+
|
|
7328
|
+
try:
|
|
7329
|
+
await self.workspace_manager._git(
|
|
7330
|
+
"reset", "--hard", before_sha or "HEAD", cwd=workspace_path,
|
|
7331
|
+
)
|
|
7332
|
+
await self.workspace_manager._git(
|
|
7333
|
+
"clean", "-f", "--", *forbidden_paths, cwd=workspace_path,
|
|
7334
|
+
)
|
|
7335
|
+
logger.warning(
|
|
7336
|
+
"Discarded verification scope-violation changes for task %s: %s",
|
|
7337
|
+
task.task_id,
|
|
7338
|
+
forbidden_paths,
|
|
7339
|
+
)
|
|
7340
|
+
except Exception:
|
|
7341
|
+
logger.exception(
|
|
7342
|
+
"Failed to discard verification scope-violation changes for task %s",
|
|
7343
|
+
task.task_id,
|
|
7344
|
+
)
|
|
7345
|
+
|
|
7309
7346
|
async def _execute_task(self, task: TaskInfo, conn: ServerConnection):
|
|
7310
7347
|
"""Execute a single task, reporting to the originating server connection."""
|
|
7311
7348
|
reporter = conn.reporter
|
|
@@ -7783,6 +7820,7 @@ class RuntimeDaemon:
|
|
|
7783
7820
|
result = await self._validate_and_retry(
|
|
7784
7821
|
agent, task, workspace_path, result,
|
|
7785
7822
|
reporter, on_output_chunk, max_retries=2,
|
|
7823
|
+
default_branch=default_branch, before_sha=node_before_sha,
|
|
7786
7824
|
)
|
|
7787
7825
|
# Re-collect git info if validation triggered retries
|
|
7788
7826
|
if result.metrics.get("validation_issues") is not None or True:
|
|
@@ -7790,6 +7828,11 @@ class RuntimeDaemon:
|
|
|
7790
7828
|
except Exception:
|
|
7791
7829
|
logger.exception("Validation gate error for task %s (proceeding anyway)", task.task_id)
|
|
7792
7830
|
|
|
7831
|
+
if result.failure_code == "verification_work_item_scope_violation":
|
|
7832
|
+
await self._discard_verification_scope_changes(
|
|
7833
|
+
workspace_path, task, result, node_before_sha,
|
|
7834
|
+
)
|
|
7835
|
+
|
|
7793
7836
|
# 4.6 Post-validation rate-limit fallback.
|
|
7794
7837
|
# _validate_and_retry returns early (preserving the rate-limit error)
|
|
7795
7838
|
# when the agent hits a quota wall mid-retry. The initial-run fallback
|
|
@@ -7831,6 +7874,7 @@ class RuntimeDaemon:
|
|
|
7831
7874
|
result = await self._validate_and_retry(
|
|
7832
7875
|
agent, task, workspace_path, result,
|
|
7833
7876
|
reporter, on_output_chunk, max_retries=2,
|
|
7877
|
+
default_branch=default_branch, before_sha=node_before_sha,
|
|
7834
7878
|
)
|
|
7835
7879
|
pre_commit_git = await self.process_manager._collect_git_info(workspace_path)
|
|
7836
7880
|
except Exception:
|
|
@@ -8793,6 +8837,8 @@ class RuntimeDaemon:
|
|
|
8793
8837
|
reporter: "ProgressReporter",
|
|
8794
8838
|
on_chunk: Any,
|
|
8795
8839
|
max_retries: int = 2,
|
|
8840
|
+
default_branch: str = "",
|
|
8841
|
+
before_sha: str = "",
|
|
8796
8842
|
) -> TaskResult:
|
|
8797
8843
|
"""Validate agent output and retry with fix prompt if issues found.
|
|
8798
8844
|
|
|
@@ -8804,6 +8850,16 @@ class RuntimeDaemon:
|
|
|
8804
8850
|
Returns the (possibly updated) TaskResult.
|
|
8805
8851
|
"""
|
|
8806
8852
|
for attempt in range(max_retries):
|
|
8853
|
+
if before_sha and bool((task.input_data or {}).get("verification_work_item")):
|
|
8854
|
+
committed_git = await self.process_manager._collect_git_info_vs_parent(
|
|
8855
|
+
workspace_path,
|
|
8856
|
+
default_branch=default_branch,
|
|
8857
|
+
before_sha=before_sha,
|
|
8858
|
+
)
|
|
8859
|
+
result.files_changed = sorted(
|
|
8860
|
+
set(result.files_changed)
|
|
8861
|
+
| set(committed_git.get("files_changed") or [])
|
|
8862
|
+
)
|
|
8807
8863
|
issues = self._validate_outputs(workspace_path, task, result)
|
|
8808
8864
|
if not issues:
|
|
8809
8865
|
return result # All validations passed
|
|
@@ -8892,27 +8948,46 @@ class RuntimeDaemon:
|
|
|
8892
8948
|
return result
|
|
8893
8949
|
|
|
8894
8950
|
# Final check after all retries
|
|
8951
|
+
if before_sha and bool((task.input_data or {}).get("verification_work_item")):
|
|
8952
|
+
committed_git = await self.process_manager._collect_git_info_vs_parent(
|
|
8953
|
+
workspace_path,
|
|
8954
|
+
default_branch=default_branch,
|
|
8955
|
+
before_sha=before_sha,
|
|
8956
|
+
)
|
|
8957
|
+
result.files_changed = sorted(
|
|
8958
|
+
set(result.files_changed)
|
|
8959
|
+
| set(committed_git.get("files_changed") or [])
|
|
8960
|
+
)
|
|
8895
8961
|
remaining = self._validate_outputs(workspace_path, task, result)
|
|
8896
8962
|
if remaining:
|
|
8897
8963
|
# Distinguish critical issues (no output produced) from minor ones (syntax)
|
|
8964
|
+
scope_violations = [
|
|
8965
|
+
issue
|
|
8966
|
+
for issue in remaining
|
|
8967
|
+
if issue.startswith("Verification work item modified requirement analysis assets:")
|
|
8968
|
+
]
|
|
8898
8969
|
critical_patterns = ("missing", "not found", "is empty")
|
|
8899
8970
|
critical_issues = [
|
|
8900
8971
|
iss for iss in remaining
|
|
8901
8972
|
if any(p in iss.lower() for p in critical_patterns)
|
|
8902
8973
|
]
|
|
8903
|
-
if critical_issues:
|
|
8974
|
+
if critical_issues or scope_violations:
|
|
8904
8975
|
# Agent didn't produce required output — mark as failed
|
|
8905
8976
|
logger.warning(
|
|
8906
8977
|
"Validation gate: %d critical issues remain after %d retries for task %s — "
|
|
8907
8978
|
"marking as failed:\n%s",
|
|
8908
|
-
len(critical_issues), max_retries, task.task_id,
|
|
8909
|
-
"\n".join(f" - {iss}" for iss in critical_issues),
|
|
8979
|
+
len(critical_issues) + len(scope_violations), max_retries, task.task_id,
|
|
8980
|
+
"\n".join(f" - {iss}" for iss in critical_issues + scope_violations),
|
|
8910
8981
|
)
|
|
8911
8982
|
result.status = "failed"
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8983
|
+
if scope_violations:
|
|
8984
|
+
result.failure_code = "verification_work_item_scope_violation"
|
|
8985
|
+
result.error = "; ".join(scope_violations[:3])
|
|
8986
|
+
else:
|
|
8987
|
+
result.error = (
|
|
8988
|
+
f"Agent failed to produce required output after {max_retries} retries: "
|
|
8989
|
+
+ "; ".join(critical_issues[:3])
|
|
8990
|
+
)
|
|
8916
8991
|
else:
|
|
8917
8992
|
# Non-critical issues (syntax warnings) — proceed with warning
|
|
8918
8993
|
logger.warning(
|
|
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
|