forgexa-cli 1.37.3__tar.gz → 1.37.5__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.37.3 → forgexa_cli-1.37.5}/PKG-INFO +1 -1
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/daemon.py +36 -3
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/pyproject.toml +1 -1
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/README.md +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/agent_core.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/setup.cfg +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.37.3 → forgexa_cli-1.37.5}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.37.
|
|
2
|
+
__version__ = "1.37.5"
|
|
@@ -910,7 +910,7 @@ except (ImportError, ModuleNotFoundError):
|
|
|
910
910
|
# DAEMON_VERSION is the protocol/logic version of the daemon code.
|
|
911
911
|
# Kept in sync with pyproject.toml version via bump-version.sh.
|
|
912
912
|
# CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
|
|
913
|
-
DAEMON_VERSION = "1.37.
|
|
913
|
+
DAEMON_VERSION = "1.37.5"
|
|
914
914
|
|
|
915
915
|
|
|
916
916
|
def _detect_client_type() -> str:
|
|
@@ -1245,6 +1245,8 @@ class TaskResult:
|
|
|
1245
1245
|
observations: list[dict] = field(default_factory=list)
|
|
1246
1246
|
metrics: dict = field(default_factory=dict)
|
|
1247
1247
|
git: dict = field(default_factory=dict)
|
|
1248
|
+
# Set from the complete agent stream before stdout is retained as a bounded tail.
|
|
1249
|
+
has_verified_completion: bool = False
|
|
1248
1250
|
|
|
1249
1251
|
|
|
1250
1252
|
def _filter_local_bind_task_result(result: "TaskResult") -> None:
|
|
@@ -4742,6 +4744,21 @@ class ProcessManager:
|
|
|
4742
4744
|
or signals["has_meaningful_content"]
|
|
4743
4745
|
)
|
|
4744
4746
|
|
|
4747
|
+
@staticmethod
|
|
4748
|
+
def has_verified_agent_completion(result: "TaskResult") -> bool:
|
|
4749
|
+
"""Return True when structured output proves the agent completed a real turn."""
|
|
4750
|
+
if result.has_verified_completion:
|
|
4751
|
+
return True
|
|
4752
|
+
combined = "\n".join(part for part in (result.stdout, result.stderr) if part)
|
|
4753
|
+
signals = ProcessManager._extract_output_signals(combined)
|
|
4754
|
+
return bool(
|
|
4755
|
+
signals["has_assistant_events"]
|
|
4756
|
+
and signals["has_meaningful_content"]
|
|
4757
|
+
and (signals["has_result"] or signals["has_turn_completed"])
|
|
4758
|
+
and not signals["has_turn_failed"]
|
|
4759
|
+
and not signals["error_messages"]
|
|
4760
|
+
)
|
|
4761
|
+
|
|
4745
4762
|
@staticmethod
|
|
4746
4763
|
def _copilot_completed_without_result(stdout: str) -> bool:
|
|
4747
4764
|
"""Implementation lives in agent_core._copilot_completed_without_result."""
|
|
@@ -5914,6 +5931,13 @@ class ProcessManager:
|
|
|
5914
5931
|
and not signals["has_turn_failed"]
|
|
5915
5932
|
and not signals["error_messages"]
|
|
5916
5933
|
)
|
|
5934
|
+
has_verified_completion = bool(
|
|
5935
|
+
signals["has_assistant_events"]
|
|
5936
|
+
and signals["has_meaningful_content"]
|
|
5937
|
+
and (signals["has_result"] or signals["has_turn_completed"])
|
|
5938
|
+
and not signals["has_turn_failed"]
|
|
5939
|
+
and not signals["error_messages"]
|
|
5940
|
+
)
|
|
5917
5941
|
structured_errors = [
|
|
5918
5942
|
str(msg).strip()
|
|
5919
5943
|
for msg in (signals.get("error_messages") or [])
|
|
@@ -5935,6 +5959,7 @@ class ProcessManager:
|
|
|
5935
5959
|
stdout=stdout[-settings.AGENT_MAX_OUTPUT_SIZE:],
|
|
5936
5960
|
stderr=stderr[-10000:],
|
|
5937
5961
|
metrics=metrics,
|
|
5962
|
+
has_verified_completion=has_verified_completion,
|
|
5938
5963
|
)
|
|
5939
5964
|
elif not copilot_specific_error and copilot_exit is None and completed_without_result:
|
|
5940
5965
|
if returncode != 0:
|
|
@@ -5951,6 +5976,7 @@ class ProcessManager:
|
|
|
5951
5976
|
stdout=stdout[-settings.AGENT_MAX_OUTPUT_SIZE:],
|
|
5952
5977
|
stderr=stderr[-10000:],
|
|
5953
5978
|
metrics=metrics,
|
|
5979
|
+
has_verified_completion=has_verified_completion,
|
|
5954
5980
|
)
|
|
5955
5981
|
else:
|
|
5956
5982
|
# --- Immediate-failure retry ladder ---
|
|
@@ -6473,6 +6499,7 @@ class ProgressReporter:
|
|
|
6473
6499
|
"exit_code": result.exit_code,
|
|
6474
6500
|
"stdout_tail": result.stdout[-20000:] if result.stdout else "",
|
|
6475
6501
|
"stderr_tail": result.stderr[-5000:] if result.stderr else "",
|
|
6502
|
+
"verified_completion": result.has_verified_completion,
|
|
6476
6503
|
"error": result.error,
|
|
6477
6504
|
"failure_code": result.failure_code,
|
|
6478
6505
|
"preflight": result.preflight,
|
|
@@ -9077,10 +9104,16 @@ class RuntimeDaemon:
|
|
|
9077
9104
|
int(result.metrics.get("token_input", 0) or 0)
|
|
9078
9105
|
+ int(result.metrics.get("token_output", 0) or 0)
|
|
9079
9106
|
) > 0
|
|
9080
|
-
|
|
9107
|
+
has_verified_completion = self.process_manager.has_verified_agent_completion(result)
|
|
9108
|
+
if (
|
|
9109
|
+
not has_uncommitted
|
|
9110
|
+
and not has_committed
|
|
9111
|
+
and not has_tokens
|
|
9112
|
+
and not has_verified_completion
|
|
9113
|
+
):
|
|
9081
9114
|
logger.warning(
|
|
9082
9115
|
"Task %s (%s) agent reported success but produced no file changes "
|
|
9083
|
-
"
|
|
9116
|
+
"or verified completion evidence — marking as failed",
|
|
9084
9117
|
task.task_id, task.node_type,
|
|
9085
9118
|
)
|
|
9086
9119
|
result.status = "failed"
|
|
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
|