conduct-cli 0.5.7__tar.gz → 0.5.8__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 (28) hide show
  1. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/PKG-INFO +1 -1
  2. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/pyproject.toml +1 -1
  3. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/guard.py +6 -1
  4. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_template.py +36 -1
  5. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/PKG-INFO +1 -1
  6. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/README.md +0 -0
  7. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/setup.cfg +0 -0
  8. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/setup.py +0 -0
  9. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/__init__.py +0 -0
  10. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/api.py +0 -0
  11. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/guardmcp.py +0 -0
  12. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_precompact_template.py +0 -0
  13. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_session_start_template.py +0 -0
  14. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/hook_stop_template.py +0 -0
  15. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/main.py +0 -0
  16. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/mcp_server.py +0 -0
  17. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/memory.py +0 -0
  18. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli/paxel.py +0 -0
  19. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
  20. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  21. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  22. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/requires.txt +0 -0
  23. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/src/conduct_cli.egg-info/top_level.txt +0 -0
  24. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_bash_operator_signature.py +0 -0
  25. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_guard_policy.py +0 -0
  26. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_guard_savings.py +0 -0
  27. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_hook_syntax.py +0 -0
  28. {conduct_cli-0.5.7 → conduct_cli-0.5.8}/tests/test_switch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.5.7
3
+ Version: 0.5.8
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "conduct-cli"
7
- version = "0.5.7"
7
+ version = "0.5.8"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -572,8 +572,13 @@ def cmd_guard_install(args):
572
572
  api_key=api_key,
573
573
  )
574
574
  _save_policy(policy)
575
+ # Mirror fail_mode into guard config so the hook can enforce it
576
+ # even when POLICY_PATH is missing or unreadable.
577
+ cfg = _load_guard_config()
578
+ cfg["fail_mode"] = policy.get("fail_mode", "fail_open")
579
+ _save_guard_config(cfg)
575
580
  rule_count = len(policy.get("rules", []))
576
- print(f" {GREEN}Guard policies:{RESET} {rule_count} rule(s) active")
581
+ print(f" {GREEN}Guard policies:{RESET} {rule_count} rule(s) active · fail mode: {cfg['fail_mode']}")
577
582
  except SystemExit:
578
583
  rule_count = 0
579
584
 
@@ -86,7 +86,17 @@ def _maybe_sync_policy():
86
86
  POLICY_PATH.write_text(json.dumps(remote, indent=2))
87
87
  VERSION_CACHE_PATH.write_text(json.dumps({"ts": time.time(), "version": remote_version}))
88
88
  except Exception:
89
- pass # Never block a tool call due to sync failure
89
+ pass # sync failure does not directly block; policy file may still be present
90
+
91
+
92
+ def _get_fail_mode():
93
+ """Read fail_mode from ~/.conduct/config.json. Default: fail_open."""
94
+ try:
95
+ cfg = json.loads(CONFIG_PATH.read_text()) if CONFIG_PATH.exists() else {}
96
+ except Exception:
97
+ return "fail_open"
98
+ mode = cfg.get("fail_mode", "fail_open")
99
+ return mode if mode in ("fail_open", "fail_closed") else "fail_open"
90
100
 
91
101
 
92
102
  def _load_budget_cache():
@@ -685,10 +695,35 @@ def main():
685
695
  # Policy version check (cached 60s) — auto-syncs if server version differs
686
696
  _maybe_sync_policy()
687
697
 
698
+ # Fail-closed gate: if the local policy file is missing and the workspace
699
+ # is configured for fail_closed, block every tool call with a clear
700
+ # message. fail_open (default) silently allows.
701
+ if _get_fail_mode() == "fail_closed" and not POLICY_PATH.exists():
702
+ tool_name = (data.get("tool_name") or "").lower()
703
+ tool_input = data.get("tool_input") or {}
704
+ session_id = data.get("session_id")
705
+ msg = "[ConductGuard] Policy cache unavailable — fail-closed mode is on. Run `conduct guard sync` or contact your admin."
706
+ print(msg)
707
+ print(msg, file=sys.stderr)
708
+ _post_event(tool_name, tool_input, "blocked", "guard-unavailable", msg, session_id=session_id)
709
+ sys.exit(2)
710
+
688
711
  # Hard budget cap (cached 5 min)
689
712
  hard_blocked, reason = _load_budget_cache()
690
713
  if hard_blocked is None:
691
714
  hard_blocked, reason = _fetch_budget_status()
715
+ # Fail-closed: if the budget check returned default (None reason) AND
716
+ # we just hit the API for a fresh value, the API was unreachable.
717
+ # Treat as unavailable and block.
718
+ if not hard_blocked and reason is None and _get_fail_mode() == "fail_closed":
719
+ tool_name = (data.get("tool_name") or "").lower()
720
+ tool_input = data.get("tool_input") or {}
721
+ session_id = data.get("session_id")
722
+ msg = "[ConductGuard] Spend budget check unavailable — fail-closed mode is on."
723
+ print(msg)
724
+ print(msg, file=sys.stderr)
725
+ _post_event(tool_name, tool_input, "blocked", "guard-unavailable", msg, session_id=session_id)
726
+ sys.exit(2)
692
727
  if hard_blocked:
693
728
  msg = f"[ConductGuard] {reason or 'Budget hard cap reached. Contact your manager.'}"
694
729
  print(msg)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.5.7
3
+ Version: 0.5.8
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
File without changes
File without changes
File without changes