forgexa-cli 1.27.3__tar.gz → 1.27.4__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 (27) hide show
  1. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/PKG-INFO +1 -1
  2. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/__init__.py +1 -1
  3. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/agent_core.py +6 -0
  4. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/daemon.py +62 -10
  5. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli.egg-info/PKG-INFO +1 -1
  6. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/pyproject.toml +1 -1
  7. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/README.md +0 -0
  8. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/_build_config.py +0 -0
  9. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/_local_bind.py +0 -0
  10. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/autoupgrade.py +0 -0
  11. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/main.py +0 -0
  12. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli/py.typed +0 -0
  13. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli.egg-info/SOURCES.txt +0 -0
  14. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli.egg-info/dependency_links.txt +0 -0
  15. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli.egg-info/entry_points.txt +0 -0
  16. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli.egg-info/requires.txt +0 -0
  17. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/forgexa_cli.egg-info/top_level.txt +0 -0
  18. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/setup.cfg +0 -0
  19. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_auth_and_runtime_commands.py +0 -0
  20. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_autoupgrade.py +0 -0
  21. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_check_command.py +0 -0
  22. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_expiry_warnings_and_revoke.py +0 -0
  23. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_local_bind_commands.py +0 -0
  24. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_runtime_credentials.py +0 -0
  25. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_session_credentials.py +0 -0
  26. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/tests/test_silent_install.py +0 -0
  27. {forgexa_cli-1.27.3 → forgexa_cli-1.27.4}/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.27.3
3
+ Version: 1.27.4
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.27.3"
2
+ __version__ = "1.27.4"
@@ -802,6 +802,12 @@ def _extract_output_signals(text: str) -> dict[str, Any]:
802
802
  error_messages.append(err)
803
803
  elif isinstance(data.get("message"), str) and data.get("message", "").strip():
804
804
  error_messages.append(data["message"])
805
+ elif ev_type == "session.mcp_server_status_changed":
806
+ status_data = data.get("data") or {}
807
+ if isinstance(status_data, dict) and status_data.get("status") == "failed":
808
+ message = status_data.get("error") or status_data.get("message") or ""
809
+ if message:
810
+ error_messages.append(str(message))
805
811
  elif ev_type == "error":
806
812
  msg = data.get("message", "")
807
813
  if msg:
@@ -909,7 +909,7 @@ except (ImportError, ModuleNotFoundError):
909
909
  # DAEMON_VERSION is the protocol/logic version of the daemon code.
910
910
  # Kept in sync with pyproject.toml version via bump-version.sh.
911
911
  # CLIENT_TYPE identifies which packaging/distribution this daemon runs in.
912
- DAEMON_VERSION = "1.27.3"
912
+ DAEMON_VERSION = "1.27.4"
913
913
 
914
914
 
915
915
  def _detect_client_type() -> str:
@@ -2268,6 +2268,21 @@ class WorkspaceManager:
2268
2268
  )
2269
2269
  return any(marker in err for marker in retry_markers)
2270
2270
 
2271
+ @staticmethod
2272
+ def _is_unreadable_git_object_error(exc: Exception | str) -> bool:
2273
+ detail = str(exc).lower()
2274
+ return any(marker in detail for marker in (
2275
+ "unable to read tree",
2276
+ "could not read tree",
2277
+ "cannot read tree",
2278
+ "bad tree object",
2279
+ "missing tree object",
2280
+ "bad object",
2281
+ "unknown object",
2282
+ "not a valid object",
2283
+ "无法读取树",
2284
+ ))
2285
+
2271
2286
  @staticmethod
2272
2287
  def _safe_rmtree(path: Path) -> None:
2273
2288
  """Remove a directory tree, handling two distinct Windows failure modes.
@@ -3434,6 +3449,7 @@ class WorkspaceManager:
3434
3449
  workspace_key: str, branch_name: str, *, fresh_start: bool = False,
3435
3450
  project_key: str = "default", expect_branch: bool = False,
3436
3451
  detach_head: bool = False,
3452
+ _recovered_unreadable_object: bool = False,
3437
3453
  ) -> Path:
3438
3454
  main_repo = project_dir / "_main"
3439
3455
  ws_path = project_dir / workspace_key
@@ -4212,6 +4228,36 @@ class WorkspaceManager:
4212
4228
  return ws_path
4213
4229
  except Exception as recovery_exc:
4214
4230
  exc = recovery_exc
4231
+ if (
4232
+ not _recovered_unreadable_object
4233
+ and self._is_unreadable_git_object_error(exc)
4234
+ ):
4235
+ logger.warning(
4236
+ "Fresh start could not read a Git object from %s; rebuilding _main cache",
4237
+ main_repo,
4238
+ )
4239
+ self._safe_rmtree(ws_path)
4240
+ await self._safe_rmtree_main(main_repo)
4241
+ if main_repo.exists():
4242
+ raise RuntimeError(
4243
+ f"Cannot rebuild local Git cache at {main_repo}; "
4244
+ "please stop the daemon and remove it manually."
4245
+ ) from exc
4246
+ try:
4247
+ return await self._create_worktree_impl(
4248
+ project_dir, repo_url, default_branch,
4249
+ workspace_key, branch_name,
4250
+ fresh_start=fresh_start,
4251
+ project_key=project_key,
4252
+ expect_branch=expect_branch,
4253
+ detach_head=detach_head,
4254
+ _recovered_unreadable_object=True,
4255
+ )
4256
+ except Exception as recovery_exc:
4257
+ raise RuntimeError(
4258
+ "Failed to create fresh worktree after rebuilding a local Git cache "
4259
+ "with unreadable objects."
4260
+ ) from recovery_exc
4215
4261
  self._safe_rmtree(ws_path)
4216
4262
  raise RuntimeError(
4217
4263
  f"Failed to create clean fresh worktree for branch '{branch_name}'. "
@@ -5853,7 +5899,7 @@ class ProcessManager:
5853
5899
  break
5854
5900
  effective_rc = copilot_exit if copilot_exit is not None else returncode
5855
5901
 
5856
- if effective_rc == 0 and returncode == 0:
5902
+ if copilot_exit == 0 and returncode == 0:
5857
5903
  return TaskResult(
5858
5904
  status="success",
5859
5905
  exit_code=0,
@@ -5861,14 +5907,15 @@ class ProcessManager:
5861
5907
  stderr=stderr[-10000:],
5862
5908
  metrics=metrics,
5863
5909
  )
5864
- elif copilot_exit is None and returncode != 0 and completed_without_result:
5865
- logger.warning(
5866
- "Copilot exited with return code %s for task %s despite a completed assistant turn and no result event; recovering to validation path",
5867
- returncode,
5868
- task_id,
5869
- )
5870
- metrics["recovered_missing_result_event"] = True
5871
- metrics["recovered_process_returncode"] = returncode
5910
+ elif not copilot_specific_error and copilot_exit is None and completed_without_result:
5911
+ if returncode != 0:
5912
+ logger.warning(
5913
+ "Copilot exited with return code %s for task %s despite a completed assistant turn and no result event; recovering to validation path",
5914
+ returncode,
5915
+ task_id,
5916
+ )
5917
+ metrics["recovered_missing_result_event"] = True
5918
+ metrics["recovered_process_returncode"] = returncode
5872
5919
  return TaskResult(
5873
5920
  status="success",
5874
5921
  exit_code=0,
@@ -5933,6 +5980,11 @@ class ProcessManager:
5933
5980
  )
5934
5981
  if copilot_specific_error:
5935
5982
  failure_error = copilot_specific_error[:1500]
5983
+ elif copilot_exit is None and returncode == 0:
5984
+ stderr_detail = stderr[-500:].strip()
5985
+ failure_error = "Copilot exited without a successful result event"
5986
+ if stderr_detail:
5987
+ failure_error = f"{failure_error}: {stderr_detail}"
5936
5988
  elif stderr.strip():
5937
5989
  failure_error = f"Copilot exited with code {effective_rc}: {stderr[-500:].strip()}"
5938
5990
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgexa-cli
3
- Version: 1.27.3
3
+ Version: 1.27.4
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.27.3"
3
+ version = "1.27.4"
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