@oswaldzsh/devhive 0.1.0

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 (46) hide show
  1. package/README.md +91 -0
  2. package/__init__.py +0 -0
  3. package/agents/__init__.py +0 -0
  4. package/agents/base.py +118 -0
  5. package/agents/execute.py +150 -0
  6. package/agents/verifier_dynamic.py +164 -0
  7. package/agents/verifier_semantic.py +84 -0
  8. package/agents/verifier_static.py +153 -0
  9. package/bin/dh +77 -0
  10. package/config.yaml +71 -0
  11. package/control_plane/__init__.py +0 -0
  12. package/control_plane/cli.py +596 -0
  13. package/control_plane/dashboard.py +57 -0
  14. package/control_plane/notifications.py +54 -0
  15. package/control_plane/tui.py +352 -0
  16. package/install.sh +67 -0
  17. package/orchestrator/__init__.py +0 -0
  18. package/orchestrator/agent_pool.py +107 -0
  19. package/orchestrator/convergence_gate.py +133 -0
  20. package/orchestrator/engine.py +353 -0
  21. package/orchestrator/event_bus.py +58 -0
  22. package/orchestrator/task_queue.py +59 -0
  23. package/package.json +50 -0
  24. package/protocol/__init__.py +0 -0
  25. package/protocol/schemas.py +222 -0
  26. package/setup.py +44 -0
  27. package/signature/__init__.py +0 -0
  28. package/signature/engine.py +211 -0
  29. package/signature/extractor.py +156 -0
  30. package/signature/learner.py +75 -0
  31. package/signature/src/matcher.c +263 -0
  32. package/signature/src/matcher.h +135 -0
  33. package/signatures/seed_signatures.json +174 -0
  34. package/storage/__init__.py +0 -0
  35. package/storage/checkpoint.py +153 -0
  36. package/storage/signature_db.py +62 -0
  37. package/tools/__init__.py +0 -0
  38. package/tools/api_client.py +101 -0
  39. package/tools/git.py +75 -0
  40. package/tools/sandbox.py +79 -0
  41. package/verification/__init__.py +0 -0
  42. package/verification/diagnostic.py +124 -0
  43. package/verification/patterns/api_breaking.yaml +25 -0
  44. package/verification/patterns/code_quality.yaml +41 -0
  45. package/verification/patterns/security.yaml +41 -0
  46. package/verification/pipeline.py +61 -0
@@ -0,0 +1,153 @@
1
+ """Static Verifier — analyzes code diff for suspicious patterns."""
2
+
3
+ import json
4
+ import os
5
+ import yaml
6
+ from typing import Optional
7
+
8
+ from agents.base import AgentProcess
9
+ from protocol.schemas import (
10
+ Task, ExecutionHandoff, Verdict, Finding, FindingEvidence,
11
+ Severity, SuggestedAction, VerdictOverall, VerifierType,
12
+ )
13
+
14
+
15
+ STATIC_SYSTEM_PROMPT = """You are a Static Verifier Agent. Your job is to:
16
+
17
+ 1. Read the code diff and Execution Handoff
18
+ 2. Identify suspicious patterns in the code itself (not test results)
19
+ 3. Output a structured Verdict in JSON format
20
+
21
+ You focus on CODE PATTERNS, not runtime behavior:
22
+ - Public API breaks (changed signatures, unchanged callers)
23
+ - Unsafe operations (no timeout on network calls, missing error handling)
24
+ - Dependency changes (new packages, version bumps)
25
+ - Security issues (injection risks, exposed secrets, missing auth checks)
26
+ - Code quality regressions (swallowed exceptions, overly complex logic)
27
+
28
+ OUTPUT FORMAT:
29
+ ```json
30
+ {
31
+ "overall": "PASS|WARN|FAIL",
32
+ "findings": [
33
+ {
34
+ "severity": "HIGH|MEDIUM|LOW",
35
+ "category": "api_break|security|dependency|code_quality|...",
36
+ "title": "short description",
37
+ "detail": "detailed explanation",
38
+ "suggested_action": "ROLLBACK|FIX|ESCALATE|IGNORE"
39
+ }
40
+ ]
41
+ }
42
+ ```
43
+ """
44
+
45
+
46
+ class StaticVerifier(AgentProcess):
47
+ """Checks code structure for suspicious patterns."""
48
+
49
+ def __init__(self, agent_id: str, task_queue, result_queue, config: dict = None):
50
+ super().__init__(agent_id, "static_verifier", task_queue, result_queue, config)
51
+ self.rules: list[dict] = []
52
+ self.rules_dir = ""
53
+
54
+ def _setup(self):
55
+ self.rules_dir = self.config.get("rules_dir", "verification/patterns/")
56
+ self._load_rules()
57
+
58
+ def _load_rules(self):
59
+ """Load YAML rule definitions."""
60
+ if not os.path.isdir(self.rules_dir):
61
+ return
62
+ for fname in sorted(os.listdir(self.rules_dir)):
63
+ if fname.endswith((".yaml", ".yml")):
64
+ with open(os.path.join(self.rules_dir, fname)) as f:
65
+ data = yaml.safe_load(f)
66
+ if data and "patterns" in data:
67
+ self.rules.extend(data["patterns"])
68
+
69
+ def _execute(self, task: Task) -> Verdict:
70
+ handoff = task # In practice the task payload contains the Handoff
71
+
72
+ # 1. Run deterministic rule checks
73
+ rule_findings = self._check_rules(task)
74
+
75
+ # 2. Ask LLM for pattern-based analysis
76
+ llm_findings = self._llm_analyze(task)
77
+
78
+ all_findings = rule_findings + llm_findings
79
+
80
+ # Determine overall verdict
81
+ has_high = any(f.severity == Severity.HIGH for f in all_findings)
82
+ has_medium = any(f.severity == Severity.MEDIUM for f in all_findings)
83
+ if has_high:
84
+ overall = VerdictOverall.FAIL
85
+ elif has_medium:
86
+ overall = VerdictOverall.WARN
87
+ else:
88
+ overall = VerdictOverall.PASS
89
+
90
+ return Verdict(
91
+ verifier_type=VerifierType.STATIC,
92
+ task_id=task.id,
93
+ overall=overall,
94
+ findings=all_findings,
95
+ )
96
+
97
+ def _check_rules(self, task: Task) -> list[Finding]:
98
+ """Run deterministic YAML rules against the diff."""
99
+ findings = []
100
+ for rule in self.rules:
101
+ # Apply each rule's detector logic
102
+ detector = rule.get("detector", {})
103
+ if detector.get("type") == "file_diff":
104
+ findings.append(Finding(
105
+ severity=Severity(rule.get("severity", "MEDIUM")),
106
+ category=rule.get("name", "rule_match"),
107
+ title=rule.get("desc", ""),
108
+ detail=f"Rule {rule['id']} matched: {rule.get('desc', '')}",
109
+ evidence=FindingEvidence(type="rule", data=json.dumps(rule)),
110
+ suggested_action=SuggestedAction.ESCALATE,
111
+ ))
112
+ return findings
113
+
114
+ def _llm_analyze(self, task: Task) -> list[Finding]:
115
+ """Use LLM for pattern-based code analysis."""
116
+ import asyncio
117
+
118
+ task_json = json.dumps(task.model_dump(), indent=2, default=str)
119
+
120
+ loop = asyncio.new_event_loop()
121
+ try:
122
+ response = loop.run_until_complete(
123
+ self._call_model(STATIC_SYSTEM_PROMPT, task_json, max_tokens=4096)
124
+ )
125
+ finally:
126
+ loop.close()
127
+
128
+ return self._parse_findings(response)
129
+
130
+ def _parse_findings(self, response: dict) -> list[Finding]:
131
+ """Extract findings from LLM response."""
132
+ import re
133
+ text = ""
134
+ for block in response.get("content", []):
135
+ if block.get("type") == "text":
136
+ text += block["text"]
137
+
138
+ match = re.search(r'```json\s*\n(.*?)\n```', text, re.DOTALL)
139
+ if not match:
140
+ return []
141
+
142
+ data = json.loads(match.group(1))
143
+ return [
144
+ Finding(
145
+ severity=Severity(f.get("severity", "MEDIUM")),
146
+ category=f.get("category", "unknown"),
147
+ title=f.get("title", ""),
148
+ detail=f.get("detail", ""),
149
+ evidence=FindingEvidence(type="llm_analysis", data=json.dumps(f)),
150
+ suggested_action=SuggestedAction(f.get("suggested_action", "ESCALATE")),
151
+ )
152
+ for f in data.get("findings", [])
153
+ ]
package/bin/dh ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # dh — DevHive CLI entry point
3
+ # Resolves the Python module path and executes it.
4
+ #
5
+ # Resolution order:
6
+ # 1. $DEVHIVE_HOME (explicit override)
7
+ # 2. NPM global install: $(npm root -g)/devhive
8
+ # 3. ~/.devhive (postinstall copy)
9
+ # 4. ../ relative to this script (dev mode)
10
+ # 5. /usr/local/lib/devhive
11
+
12
+ set -e
13
+
14
+ # ── Resolve module directory ────────────────────────────────
15
+
16
+ find_module_dir() {
17
+ # 1. Explicit override
18
+ if [ -n "$DEVHIVE_HOME" ] && [ -d "$DEVHIVE_HOME/control_plane" ]; then
19
+ echo "$DEVHIVE_HOME"
20
+ return
21
+ fi
22
+
23
+ # 2. NPM global install (same dir as this script is in node_modules/devhive/bin)
24
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
25
+ NPM_MODULE="$(cd "$SCRIPT_DIR/.." 2>/dev/null && pwd)"
26
+ if [ -d "$NPM_MODULE/control_plane" ]; then
27
+ echo "$NPM_MODULE"
28
+ return
29
+ fi
30
+
31
+ # 3. Home dir copy
32
+ if [ -d "$HOME/.devhive/control_plane" ]; then
33
+ echo "$HOME/.devhive"
34
+ return
35
+ fi
36
+
37
+ # 4. System-wide
38
+ if [ -d "/usr/local/lib/devhive/control_plane" ]; then
39
+ echo "/usr/local/lib/devhive"
40
+ return
41
+ fi
42
+
43
+ echo ""
44
+ }
45
+
46
+ MODULE_DIR=$(find_module_dir)
47
+
48
+ if [ -z "$MODULE_DIR" ]; then
49
+ echo "DevHive: module not found." >&2
50
+ echo " Set DEVHIVE_HOME to the module path, or reinstall:" >&2
51
+ echo " npm install -g devhive" >&2
52
+ echo " pip3 install devhive" >&2
53
+ exit 1
54
+ fi
55
+
56
+ # ── Resolve Python ──────────────────────────────────────────
57
+
58
+ PYTHON="${DEVHIVE_PYTHON:-}"
59
+ if [ -z "$PYTHON" ]; then
60
+ for cmd in python3 python; do
61
+ if command -v "$cmd" &>/dev/null; then
62
+ PYTHON="$cmd"
63
+ break
64
+ fi
65
+ done
66
+ fi
67
+
68
+ if [ -z "$PYTHON" ]; then
69
+ echo "DevHive: Python 3.12+ required but not found." >&2
70
+ exit 1
71
+ fi
72
+
73
+ # ── Run ─────────────────────────────────────────────────────
74
+
75
+ export PYTHONPATH="$MODULE_DIR:$PYTHONPATH"
76
+ cd "$MODULE_DIR"
77
+ exec "$PYTHON" -m control_plane.cli "$@"
package/config.yaml ADDED
@@ -0,0 +1,71 @@
1
+ # DevHive Configuration
2
+ api:
3
+ base_url: "https://aiapi.lejurobot.com"
4
+ auth_token: "${LEJU_TOKEN}"
5
+ default_model: "deepseek/deepseek-v4-pro"
6
+
7
+ agents:
8
+ execute:
9
+ count: 2
10
+ max_context: 180000
11
+ timeout_seconds: 600
12
+ tools:
13
+ - read_file
14
+ - write_file
15
+ - edit_file
16
+ - bash
17
+ - git
18
+ sandbox:
19
+ file_whitelist: [".py", ".ts", ".js", ".rs", ".go", ".yaml", ".json", ".md", ".toml", ".cfg"]
20
+ forbidden_commands: ["rm -rf", "git push --force", "sudo", "chmod 777"]
21
+
22
+ static_verifier:
23
+ count: 1
24
+ max_context: 80000
25
+ timeout_seconds: 120
26
+ rules_dir: "verification/patterns/"
27
+
28
+ dynamic_verifier:
29
+ count: 1
30
+ max_context: 80000
31
+ timeout_seconds: 300
32
+ call_graph_db: "storage/call_graph.sqlite"
33
+ drift_thresholds:
34
+ duration_change_pct: 20
35
+ memory_change_pct: 50
36
+ log_volume_change_multiplier: 3.0
37
+
38
+ semantic_verifier:
39
+ count: 1
40
+ max_context: 120000
41
+ timeout_seconds: 180
42
+ trigger: "on_conflict_or_l2"
43
+
44
+ convergence:
45
+ l1_max_retries: 3
46
+ l2_max_retries: 2
47
+ loop_detection_window: 5
48
+ loop_similarity_threshold: 0.8
49
+
50
+ signature_db:
51
+ path: "signatures/signatures.db"
52
+ seed_file: "signatures/seed_signatures.json"
53
+ min_confidence: 0.65
54
+ top_k: 3
55
+ auto_learn: true
56
+
57
+ escalation:
58
+ notify:
59
+ - type: "desktop_notification"
60
+ - type: "terminal_bell"
61
+ require_approval_for:
62
+ - auth
63
+ - payment
64
+ - data_deletion
65
+ - permission_change
66
+
67
+ sandbox:
68
+ docker_image: "devhive-sandbox:latest"
69
+ memory_limit: "2g"
70
+ cpu_limit: 2
71
+ timeout_default: 600
File without changes