forgexa-cli 1.86.4__tar.gz → 1.87.0__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.86.4 → forgexa_cli-1.87.0}/PKG-INFO +1 -1
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/__init__.py +1 -1
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/agent_core.py +42 -9
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/daemon.py +11 -5
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/PKG-INFO +1 -1
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/pyproject.toml +1 -1
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/README.md +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/_build_config.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/_local_bind.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/autoupgrade.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/aws_evidence.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/aws_logs_evidence.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/main.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/observability_contract.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/observability_evidence.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/py.typed +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/runtime_evidence.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli/zcode_protocol.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/SOURCES.txt +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/dependency_links.txt +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/entry_points.txt +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/requires.txt +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/top_level.txt +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/setup.cfg +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_auth_and_runtime_commands.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_autoupgrade.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_check_command.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_expiry_warnings_and_revoke.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_local_bind_commands.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_publish_script.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_runtime_credentials.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_session_credentials.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_silent_install.py +0 -0
- {forgexa_cli-1.86.4 → forgexa_cli-1.87.0}/tests/test_upgrade_observability.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""forgexa-cli — Forgexa command-line client."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.87.0"
|
|
@@ -782,7 +782,15 @@ def lock_provenance_projection(path: str, source: str) -> str | None:
|
|
|
782
782
|
}, ensure_ascii=True, sort_keys=True, separators=(",", ":"))
|
|
783
783
|
|
|
784
784
|
|
|
785
|
-
|
|
785
|
+
EXECUTION_DELIVERY_WARNING_CODES = frozenset({
|
|
786
|
+
"partial_delivery", "dependency_authorization", "dependency_network", "dependency_unavailable",
|
|
787
|
+
"environment_unavailable", "verification_pending", "source_evidence_limited",
|
|
788
|
+
})
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def implementation_delivery_contract(node_type: str, task_input: dict, *, supported: bool, graph_type: str = "execution", protocol_version: int = 1) -> dict | None:
|
|
792
|
+
if type(protocol_version) is not int or protocol_version not in (1, 2):
|
|
793
|
+
return None
|
|
786
794
|
if supported is not True or not isinstance(node_type, str) or not isinstance(graph_type, str) or node_type not in {"design", "coding", "testing", "review", "fix"} or graph_type not in {"execution", "implementation", "fix"}:
|
|
787
795
|
return None
|
|
788
796
|
if task_input.get("verification_work_item") or task_input.get("execution_actor") == "human":
|
|
@@ -796,7 +804,7 @@ def implementation_delivery_contract(node_type: str, task_input: dict, *, suppor
|
|
|
796
804
|
task_id = (baseline.get("task") or {}).get("id")
|
|
797
805
|
if not isinstance(task_id, str) or not task_id:
|
|
798
806
|
return None
|
|
799
|
-
contract = {"version":
|
|
807
|
+
contract = {"version": protocol_version, "mode": "execution_and_artifacts", "node_type": node_type, "graph_type": graph_type,
|
|
800
808
|
"task_id": task_id, "baseline_fingerprint": baseline.get("fingerprint"),
|
|
801
809
|
"qa_policy": task_input.get("qa_test_level_policy_snapshot"), "change_scope": task_input.get("change_scope")}
|
|
802
810
|
contract["fingerprint"] = hashlib.sha256(json.dumps(contract, sort_keys=True, ensure_ascii=True, separators=(",", ":")).encode()).hexdigest()
|
|
@@ -807,24 +815,49 @@ def uses_controlled_delivery(task_input: dict) -> bool:
|
|
|
807
815
|
contract = task_input.get("implementation_delivery")
|
|
808
816
|
if not isinstance(contract, dict):
|
|
809
817
|
return False
|
|
810
|
-
expected = implementation_delivery_contract(contract.get("node_type"), task_input, supported=True,
|
|
818
|
+
expected = implementation_delivery_contract(contract.get("node_type"), task_input, supported=True,
|
|
819
|
+
graph_type=contract.get("graph_type"), protocol_version=contract.get("version"))
|
|
811
820
|
return expected is not None and contract == expected
|
|
812
821
|
|
|
813
822
|
|
|
814
823
|
def assess_execution_delivery(contract: dict, report: object) -> dict:
|
|
815
|
-
if not isinstance(report, dict) or type(report.get("version")) is not int or report["version"]
|
|
824
|
+
if not isinstance(report, dict) or type(report.get("version")) is not int or report["version"] not in (1, 2):
|
|
816
825
|
return {"report_valid": False, "status": "unknown", "issues": ["Execution delivery declaration is missing or invalid"]}
|
|
817
|
-
if
|
|
826
|
+
if report["version"] != contract.get("version") or report.get("contract_fingerprint") != contract.get("fingerprint") or redact_execution_delivery(report) is None:
|
|
818
827
|
return {"report_valid": False, "status": "unknown", "issues": ["Execution delivery declaration does not match the current contract"]}
|
|
819
828
|
return {"report_valid": True, "status": report["status"], "issues": []}
|
|
820
829
|
|
|
821
830
|
|
|
831
|
+
def execution_delivery_allows_progress(contract: dict, assessment: dict) -> bool:
|
|
832
|
+
return assessment.get("report_valid") is True and (
|
|
833
|
+
assessment.get("status") == "complete" or contract.get("version") == 2 and assessment.get("status") == "partial"
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def execution_delivery_warnings(contract: dict, report: object) -> list[str]:
|
|
838
|
+
if not isinstance(report, dict) or contract.get("version") != 2 or not assess_execution_delivery(contract, report)["report_valid"]:
|
|
839
|
+
return []
|
|
840
|
+
return list(report.get("warnings") or (["partial_delivery"] if report["status"] == "partial" else []))
|
|
841
|
+
|
|
842
|
+
|
|
822
843
|
def redact_execution_delivery(report: object) -> dict | None:
|
|
823
|
-
if not isinstance(report, dict):
|
|
844
|
+
if not isinstance(report, dict) or type(report.get("version")) is not int or report["version"] not in (1, 2):
|
|
845
|
+
return None
|
|
846
|
+
required = {"version", "contract_fingerprint", "status"}
|
|
847
|
+
allowed = required | ({"warnings"} if report["version"] == 2 else set())
|
|
848
|
+
if not required <= set(report) <= allowed or not isinstance(report.get("contract_fingerprint"), str) or not re.fullmatch(r"[0-9a-f]{64}", report["contract_fingerprint"]):
|
|
849
|
+
return None
|
|
850
|
+
if not isinstance(report.get("status"), str) or report["status"] not in {"complete", "partial", "blocked"}:
|
|
851
|
+
return None
|
|
852
|
+
warnings = report.get("warnings", [])
|
|
853
|
+
if not isinstance(warnings, list) or len(warnings) > len(EXECUTION_DELIVERY_WARNING_CODES) or any(
|
|
854
|
+
not isinstance(code, str) or code not in EXECUTION_DELIVERY_WARNING_CODES for code in warnings
|
|
855
|
+
) or len(set(warnings)) != len(warnings):
|
|
824
856
|
return None
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
857
|
+
redacted = {key: report[key] for key in ("version", "contract_fingerprint", "status")}
|
|
858
|
+
if "warnings" in report:
|
|
859
|
+
redacted["warnings"] = list(warnings)
|
|
860
|
+
return redacted
|
|
828
861
|
|
|
829
862
|
|
|
830
863
|
def task_review_policy(node_type: str, task_input: dict, *, changed_files: list[str] | None = None, policy_supported: bool = True) -> dict:
|
|
@@ -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.
|
|
1237
|
+
DAEMON_VERSION = "1.87.0"
|
|
1238
1238
|
|
|
1239
1239
|
|
|
1240
1240
|
def _detect_client_type() -> str:
|
|
@@ -10223,7 +10223,7 @@ class ServerConnection:
|
|
|
10223
10223
|
"task_delivery": {"protocol_version": 1},
|
|
10224
10224
|
"verification_recovery": {"protocol_version": 1},
|
|
10225
10225
|
"task_review_policy": {"protocol_version": 1},
|
|
10226
|
-
"implementation_delivery": {"protocol_version":
|
|
10226
|
+
"implementation_delivery": {"protocol_version": 2},
|
|
10227
10227
|
"recording_suggestions": {"protocol_version": 1, "isolated_workspace": True},
|
|
10228
10228
|
"task_attachment_staging": {"protocol_version": 1, "runtime_proxy": True, "image_agents": ["claude"]},
|
|
10229
10229
|
},
|
|
@@ -10652,7 +10652,7 @@ class ServerConnection:
|
|
|
10652
10652
|
"task_delivery": {"protocol_version": 1},
|
|
10653
10653
|
"verification_recovery": {"protocol_version": 1},
|
|
10654
10654
|
"task_review_policy": {"protocol_version": 1},
|
|
10655
|
-
"implementation_delivery": {"protocol_version":
|
|
10655
|
+
"implementation_delivery": {"protocol_version": 2},
|
|
10656
10656
|
"recording_suggestions": {"protocol_version": 1, "isolated_workspace": True},
|
|
10657
10657
|
"local_evidence": {"protocol_version": 1, "structured_summary": True},
|
|
10658
10658
|
"task_attachment_staging": {"protocol_version": 1, "runtime_proxy": True, "image_agents": ["claude"]},
|
|
@@ -14643,10 +14643,15 @@ class RuntimeDaemon:
|
|
|
14643
14643
|
delivery = agent_core.assess_execution_delivery(input_data["implementation_delivery"], report)
|
|
14644
14644
|
result.metrics["execution_delivery"] = agent_core.redact_execution_delivery(report)
|
|
14645
14645
|
result.metrics["execution_delivery_valid"] = delivery["report_valid"]
|
|
14646
|
+
result.metrics.pop("completion_warnings", None)
|
|
14646
14647
|
if not delivery["report_valid"]:
|
|
14647
14648
|
issues.extend(_OutputValidationIssue("validation_unavailable", message) for message in delivery["issues"])
|
|
14648
|
-
elif
|
|
14649
|
+
elif not agent_core.execution_delivery_allows_progress(input_data["implementation_delivery"], delivery):
|
|
14649
14650
|
issues.append(_OutputValidationIssue("planned_task_incomplete", "Agent explicitly reported incomplete execution delivery"))
|
|
14651
|
+
else:
|
|
14652
|
+
warnings = agent_core.execution_delivery_warnings(input_data["implementation_delivery"], report)
|
|
14653
|
+
if warnings:
|
|
14654
|
+
result.metrics["completion_warnings"] = warnings
|
|
14650
14655
|
fix_context = input_data.get("fix_context")
|
|
14651
14656
|
if node_type == "testing" or (
|
|
14652
14657
|
node_type == "fix" and input_data.get("task") == "phase_remediation_fix"
|
|
@@ -16719,7 +16724,8 @@ class RuntimeDaemon:
|
|
|
16719
16724
|
if (
|
|
16720
16725
|
agent_core.uses_controlled_delivery(task.input_data or {})
|
|
16721
16726
|
and result.metrics.get("execution_delivery_valid") is True
|
|
16722
|
-
and
|
|
16727
|
+
and not agent_core.execution_delivery_allows_progress(task.input_data["implementation_delivery"],
|
|
16728
|
+
agent_core.assess_execution_delivery(task.input_data["implementation_delivery"], result.metrics.get("execution_delivery")))
|
|
16723
16729
|
):
|
|
16724
16730
|
result.status = "failed"
|
|
16725
16731
|
result.failure_code = "planned_task_incomplete"
|
|
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
|