forgexa-cli 1.86.3__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.
Files changed (34) hide show
  1. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/PKG-INFO +1 -1
  2. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/__init__.py +1 -1
  3. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/agent_core.py +42 -9
  4. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/daemon.py +11 -5
  5. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/PKG-INFO +1 -1
  6. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/pyproject.toml +1 -1
  7. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/README.md +0 -0
  8. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/_build_config.py +0 -0
  9. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/_local_bind.py +0 -0
  10. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/autoupgrade.py +0 -0
  11. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/aws_evidence.py +0 -0
  12. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/aws_logs_evidence.py +0 -0
  13. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/main.py +0 -0
  14. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/observability_contract.py +0 -0
  15. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/observability_evidence.py +0 -0
  16. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/py.typed +0 -0
  17. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/runtime_evidence.py +0 -0
  18. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli/zcode_protocol.py +0 -0
  19. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/SOURCES.txt +0 -0
  20. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/dependency_links.txt +0 -0
  21. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/entry_points.txt +0 -0
  22. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/requires.txt +0 -0
  23. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/forgexa_cli.egg-info/top_level.txt +0 -0
  24. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/setup.cfg +0 -0
  25. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_auth_and_runtime_commands.py +0 -0
  26. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_autoupgrade.py +0 -0
  27. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_check_command.py +0 -0
  28. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_expiry_warnings_and_revoke.py +0 -0
  29. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_local_bind_commands.py +0 -0
  30. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_publish_script.py +0 -0
  31. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_runtime_credentials.py +0 -0
  32. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_session_credentials.py +0 -0
  33. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_silent_install.py +0 -0
  34. {forgexa_cli-1.86.3 → forgexa_cli-1.87.0}/tests/test_upgrade_observability.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.86.3
3
+ Version: 1.87.0
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,2 +1,2 @@
1
1
  """forgexa-cli — Forgexa command-line client."""
2
- __version__ = "1.86.3"
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
- def implementation_delivery_contract(node_type: str, task_input: dict, *, supported: bool, graph_type: str = "execution") -> dict | None:
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": 1, "mode": "execution_and_artifacts", "node_type": node_type, "graph_type": graph_type,
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, graph_type=contract.get("graph_type"))
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"] != 1:
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 set(report) != {"version", "contract_fingerprint", "status"} or report.get("contract_fingerprint") != contract.get("fingerprint") or not isinstance(report.get("status"), str) or report["status"] not in {"complete", "partial", "blocked"}:
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
- return {"version": report.get("version") if type(report.get("version")) is int else 0,
826
- "contract_fingerprint": report.get("contract_fingerprint") if isinstance(report.get("contract_fingerprint"), str) and re.fullmatch(r"[0-9a-f]{64}", report["contract_fingerprint"]) else "",
827
- "status": report.get("status") if report.get("status") in ("complete", "partial", "blocked") else "unknown"}
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.86.3"
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": 1},
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": 1},
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 delivery["status"] != "complete":
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 (result.metrics.get("execution_delivery") or {}).get("status") in {"partial", "blocked"}
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"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.86.3
3
+ Version: 1.87.0
4
4
  Summary: Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform
5
5
  Author-email: Jason Sun <dev.winds@gmail.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "forgexa-cli"
3
- version = "1.86.3"
3
+ version = "1.87.0"
4
4
  description = "Forgexa CLI — command-line client and AI agent runtime for the Forgexa platform"
5
5
  requires-python = ">=3.9"
6
6
  license = "MIT"
File without changes
File without changes