devtorch-core 3.0.1__py3-none-any.whl

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 (193) hide show
  1. devtorch_core/__init__.py +158 -0
  2. devtorch_core/aggphi_textual.py +275 -0
  3. devtorch_core/alerts/__init__.py +23 -0
  4. devtorch_core/alerts/base.py +46 -0
  5. devtorch_core/alerts/config.py +60 -0
  6. devtorch_core/alerts/dispatcher.py +110 -0
  7. devtorch_core/alerts/jira.py +96 -0
  8. devtorch_core/alerts/linear.py +72 -0
  9. devtorch_core/alerts/pagerduty.py +66 -0
  10. devtorch_core/alerts/slack.py +81 -0
  11. devtorch_core/alerts/teams.py +70 -0
  12. devtorch_core/audit/__init__.py +43 -0
  13. devtorch_core/audit/exporter.py +297 -0
  14. devtorch_core/audit/privacy.py +101 -0
  15. devtorch_core/audit/scrubber.py +149 -0
  16. devtorch_core/audit/service.py +67 -0
  17. devtorch_core/audit/signing.py +127 -0
  18. devtorch_core/broadcast/__init__.py +4 -0
  19. devtorch_core/broadcast/broadcaster.py +100 -0
  20. devtorch_core/broadcast/watcher.py +71 -0
  21. devtorch_core/capability.py +639 -0
  22. devtorch_core/cloud/__init__.py +1 -0
  23. devtorch_core/cloud/client_config.py +472 -0
  24. devtorch_core/cloud/client_configs/.claude-opencode-fallback.json +8 -0
  25. devtorch_core/cloud/client_configs/.claude-stdio.json +13 -0
  26. devtorch_core/cloud/client_configs/.cursor-mcp.json +13 -0
  27. devtorch_core/cloud/client_configs/.opencode-bridge.json +13 -0
  28. devtorch_core/cloud/client_configs/.opencode.json +15 -0
  29. devtorch_core/cloud/client_configs/.vscode-mcp.json +13 -0
  30. devtorch_core/cloud/devtorch-mcp-bridge.js +357 -0
  31. devtorch_core/cloud/mcp_client.py +229 -0
  32. devtorch_core/cloud/setup.py +144 -0
  33. devtorch_core/cloud/sync.py +143 -0
  34. devtorch_core/cloud/sync_bundle.py +603 -0
  35. devtorch_core/cloud/sync_conflicts.py +159 -0
  36. devtorch_core/cloud/sync_state.py +159 -0
  37. devtorch_core/cloud/team_sync.py +283 -0
  38. devtorch_core/codex/__init__.py +9 -0
  39. devtorch_core/codex/__main__.py +97 -0
  40. devtorch_core/codex/capture.py +208 -0
  41. devtorch_core/codex/proxy.py +412 -0
  42. devtorch_core/concept_catalog.py +209 -0
  43. devtorch_core/consolidation/__init__.py +3 -0
  44. devtorch_core/consolidation/synthesizer.py +87 -0
  45. devtorch_core/consolidation/workflow.py +175 -0
  46. devtorch_core/daemon/__init__.py +27 -0
  47. devtorch_core/daemon/supervisor.py +293 -0
  48. devtorch_core/daemon/watcher.py +244 -0
  49. devtorch_core/dashboard_api.py +2012 -0
  50. devtorch_core/deltaf.py +97 -0
  51. devtorch_core/disclosure.py +50 -0
  52. devtorch_core/divergence/__init__.py +3 -0
  53. devtorch_core/divergence/detector.py +166 -0
  54. devtorch_core/gateway/__init__.py +32 -0
  55. devtorch_core/gateway/key_manager.py +124 -0
  56. devtorch_core/gateway/metrics_webhook.py +252 -0
  57. devtorch_core/gateway/policy.py +262 -0
  58. devtorch_core/gateway/server.py +727 -0
  59. devtorch_core/gateway/sso.py +233 -0
  60. devtorch_core/gcc.py +1246 -0
  61. devtorch_core/github/__init__.py +35 -0
  62. devtorch_core/github/app.py +240 -0
  63. devtorch_core/github/comment_builder.py +113 -0
  64. devtorch_core/github/pat.py +76 -0
  65. devtorch_core/github/pr_parser.py +82 -0
  66. devtorch_core/github/pr_reporter.py +555 -0
  67. devtorch_core/gitlab/__init__.py +177 -0
  68. devtorch_core/hitl/__init__.py +4 -0
  69. devtorch_core/hitl/channels.py +129 -0
  70. devtorch_core/hitl/orchestrator.py +95 -0
  71. devtorch_core/hooks/__init__.py +17 -0
  72. devtorch_core/hooks/claude_code.py +228 -0
  73. devtorch_core/hooks/git_capture.py +341 -0
  74. devtorch_core/hooks/git_commit.py +182 -0
  75. devtorch_core/hooks/installer.py +733 -0
  76. devtorch_core/hooks/pre_commit.py +157 -0
  77. devtorch_core/hooks/runner.py +344 -0
  78. devtorch_core/identity/__init__.py +4 -0
  79. devtorch_core/identity/agent.py +86 -0
  80. devtorch_core/identity/providers.py +85 -0
  81. devtorch_core/invariants.py +182 -0
  82. devtorch_core/mcp/__init__.py +10 -0
  83. devtorch_core/mcp/auth.py +177 -0
  84. devtorch_core/mcp/server.py +1049 -0
  85. devtorch_core/metrics/__init__.py +35 -0
  86. devtorch_core/metrics/aggregate.py +215 -0
  87. devtorch_core/metrics/calibrate.py +198 -0
  88. devtorch_core/metrics/calibration.py +125 -0
  89. devtorch_core/metrics/credibility.py +288 -0
  90. devtorch_core/metrics/delivery_time.py +70 -0
  91. devtorch_core/metrics/dhs.py +126 -0
  92. devtorch_core/metrics/mcs.py +96 -0
  93. devtorch_core/metrics/roi.py +88 -0
  94. devtorch_core/metrics/session_writer.py +81 -0
  95. devtorch_core/metrics/shadow_ai.py +117 -0
  96. devtorch_core/metrics/sprint_writer.py +243 -0
  97. devtorch_core/observability/__init__.py +78 -0
  98. devtorch_core/observability/datadog.py +157 -0
  99. devtorch_core/observability/formatter.py +119 -0
  100. devtorch_core/observability/report.py +264 -0
  101. devtorch_core/observability/servicenow.py +147 -0
  102. devtorch_core/observability/splunk.py +218 -0
  103. devtorch_core/observability/webhook.py +227 -0
  104. devtorch_core/parser/__init__.py +30 -0
  105. devtorch_core/parser/blocks.py +216 -0
  106. devtorch_core/parser/inference.py +159 -0
  107. devtorch_core/parser/thinking.py +112 -0
  108. devtorch_core/projects.py +169 -0
  109. devtorch_core/prompt_artifact.py +76 -0
  110. devtorch_core/proxy/__init__.py +9 -0
  111. devtorch_core/proxy/routes/__init__.py +1 -0
  112. devtorch_core/proxy/routes/anthropic.py +264 -0
  113. devtorch_core/proxy/routes/azure_openai.py +336 -0
  114. devtorch_core/proxy/routes/gemini.py +331 -0
  115. devtorch_core/proxy/routes/groq.py +284 -0
  116. devtorch_core/proxy/routes/ollama.py +279 -0
  117. devtorch_core/proxy/routes/openai.py +287 -0
  118. devtorch_core/proxy/server.py +356 -0
  119. devtorch_core/query/__init__.py +15 -0
  120. devtorch_core/query/grep.py +181 -0
  121. devtorch_core/query/hybrid.py +86 -0
  122. devtorch_core/query/semantic.py +157 -0
  123. devtorch_core/rdp.py +105 -0
  124. devtorch_core/reasoning/__init__.py +4 -0
  125. devtorch_core/reasoning/entry.py +31 -0
  126. devtorch_core/reasoning/store.py +122 -0
  127. devtorch_core/reasoning_plus/__init__.py +70 -0
  128. devtorch_core/reasoning_plus/augmenter.py +326 -0
  129. devtorch_core/reasoning_plus/capture.py +51 -0
  130. devtorch_core/reasoning_plus/config.py +256 -0
  131. devtorch_core/reasoning_plus/context.py +262 -0
  132. devtorch_core/reasoning_plus/learning/__init__.py +72 -0
  133. devtorch_core/reasoning_plus/learning/analytics.py +141 -0
  134. devtorch_core/reasoning_plus/learning/api.py +313 -0
  135. devtorch_core/reasoning_plus/learning/chain.py +285 -0
  136. devtorch_core/reasoning_plus/learning/composer.py +74 -0
  137. devtorch_core/reasoning_plus/learning/cross_project.py +234 -0
  138. devtorch_core/reasoning_plus/learning/embeddings.py +209 -0
  139. devtorch_core/reasoning_plus/learning/extractor.py +207 -0
  140. devtorch_core/reasoning_plus/learning/models.py +116 -0
  141. devtorch_core/reasoning_plus/learning/provenance.py +126 -0
  142. devtorch_core/reasoning_plus/learning/recorder.py +81 -0
  143. devtorch_core/reasoning_plus/learning/relevance.py +122 -0
  144. devtorch_core/reasoning_plus/learning/state.py +86 -0
  145. devtorch_core/reasoning_plus/learning/store.py +160 -0
  146. devtorch_core/reasoning_plus/learning/theta_learning_bridge.py +94 -0
  147. devtorch_core/reasoning_plus/prompt.py +90 -0
  148. devtorch_core/rep.py +134 -0
  149. devtorch_core/rep_network/__init__.py +25 -0
  150. devtorch_core/rep_network/merge.py +70 -0
  151. devtorch_core/rep_network/node.py +137 -0
  152. devtorch_core/rep_network/server.py +140 -0
  153. devtorch_core/rep_network/sync.py +207 -0
  154. devtorch_core/sensitivity.py +182 -0
  155. devtorch_core/serve.py +258 -0
  156. devtorch_core/session/__init__.py +39 -0
  157. devtorch_core/session/disagreement.py +188 -0
  158. devtorch_core/session/models.py +114 -0
  159. devtorch_core/session/orchestrator.py +182 -0
  160. devtorch_core/session/planner.py +169 -0
  161. devtorch_core/session/simulator.py +132 -0
  162. devtorch_core/signing.py +290 -0
  163. devtorch_core/sis.py +197 -0
  164. devtorch_core/storage.py +308 -0
  165. devtorch_core/templates/__init__.py +6 -0
  166. devtorch_core/templates/engine.py +122 -0
  167. devtorch_core/templates/go.py +18 -0
  168. devtorch_core/templates/infra.py +19 -0
  169. devtorch_core/templates/library/__init__.py +18 -0
  170. devtorch_core/templates/library/api_design.md +27 -0
  171. devtorch_core/templates/library/bug_fix.md +27 -0
  172. devtorch_core/templates/library/decision_record.md +27 -0
  173. devtorch_core/templates/library/engine.py +228 -0
  174. devtorch_core/templates/library/security_review.md +30 -0
  175. devtorch_core/templates/python.py +19 -0
  176. devtorch_core/templates/react.py +18 -0
  177. devtorch_core/templates/typescript.py +18 -0
  178. devtorch_core/theta.py +221 -0
  179. devtorch_core/theta_synthesis.py +268 -0
  180. devtorch_core/topics.py +320 -0
  181. devtorch_core/variance.py +219 -0
  182. devtorch_core/wrapper/__init__.py +52 -0
  183. devtorch_core/wrapper/anthropic.py +487 -0
  184. devtorch_core/wrapper/base.py +562 -0
  185. devtorch_core/wrapper/bedrock.py +342 -0
  186. devtorch_core/wrapper/gemini.py +422 -0
  187. devtorch_core/wrapper/ollama.py +527 -0
  188. devtorch_core/wrapper/openai.py +461 -0
  189. devtorch_core-3.0.1.dist-info/METADATA +867 -0
  190. devtorch_core-3.0.1.dist-info/RECORD +193 -0
  191. devtorch_core-3.0.1.dist-info/WHEEL +5 -0
  192. devtorch_core-3.0.1.dist-info/entry_points.txt +2 -0
  193. devtorch_core-3.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,96 @@
1
+ import base64
2
+ import json
3
+ import urllib.request
4
+ import urllib.error
5
+
6
+ from .base import AlertChannel, AlertEnvelope, AlertSeverity, DeliveryResult
7
+
8
+
9
+ class JiraChannel(AlertChannel):
10
+ channel_name = "jira"
11
+
12
+ def __init__(self, url: str, project_key: str, api_token: str, email: str) -> None:
13
+ self.url = url.rstrip("/")
14
+ self.project_key = project_key
15
+ self.api_token = api_token
16
+ self.email = email
17
+
18
+ def _build_issue_body(self, envelope: AlertEnvelope) -> dict:
19
+ """Build Jira issue creation request body."""
20
+ issue_type = "Bug" if envelope.severity == AlertSeverity.CRITICAL else "Task"
21
+ summary = f"[DevTorch] {envelope.title}"
22
+
23
+ # ADF (Atlassian Document Format) — plain text wrapped in doc node
24
+ description_adf = {
25
+ "version": 1,
26
+ "type": "doc",
27
+ "content": [
28
+ {
29
+ "type": "paragraph",
30
+ "content": [
31
+ {
32
+ "type": "text",
33
+ "text": envelope.body,
34
+ }
35
+ ],
36
+ },
37
+ {
38
+ "type": "paragraph",
39
+ "content": [
40
+ {
41
+ "type": "text",
42
+ "text": f"Branch: {envelope.branch} | Commit: {envelope.commit_id} | Event: {envelope.event_type}",
43
+ }
44
+ ],
45
+ },
46
+ ],
47
+ }
48
+
49
+ return {
50
+ "fields": {
51
+ "project": {"key": self.project_key},
52
+ "summary": summary,
53
+ "description": description_adf,
54
+ "issuetype": {"name": issue_type},
55
+ }
56
+ }
57
+
58
+ def deliver(self, envelope: AlertEnvelope) -> DeliveryResult:
59
+ """POST to Jira REST API v3. Basic auth with email:token."""
60
+ try:
61
+ body = self._build_issue_body(envelope)
62
+ data = json.dumps(body).encode("utf-8")
63
+
64
+ credentials = f"{self.email}:{self.api_token}"
65
+ encoded = base64.b64encode(credentials.encode("utf-8")).decode("utf-8")
66
+
67
+ api_url = f"{self.url}/rest/api/3/issue"
68
+ req = urllib.request.Request(
69
+ api_url,
70
+ data=data,
71
+ headers={
72
+ "Content-Type": "application/json",
73
+ "Authorization": f"Basic {encoded}",
74
+ "Accept": "application/json",
75
+ },
76
+ method="POST",
77
+ )
78
+ with urllib.request.urlopen(req, timeout=10) as resp:
79
+ return DeliveryResult(
80
+ channel=self.channel_name,
81
+ success=True,
82
+ response_code=resp.status,
83
+ )
84
+ except urllib.error.HTTPError as exc:
85
+ return DeliveryResult(
86
+ channel=self.channel_name,
87
+ success=False,
88
+ error=f"HTTP {exc.code}: {exc.reason}",
89
+ response_code=exc.code,
90
+ )
91
+ except Exception as exc:
92
+ return DeliveryResult(
93
+ channel=self.channel_name,
94
+ success=False,
95
+ error=str(exc),
96
+ )
@@ -0,0 +1,72 @@
1
+ import json
2
+ import urllib.request
3
+ import urllib.error
4
+
5
+ from .base import AlertChannel, AlertEnvelope, AlertSeverity, DeliveryResult
6
+
7
+
8
+ class LinearChannel(AlertChannel):
9
+ channel_name = "linear"
10
+
11
+ def __init__(self, api_token: str, team_id: str) -> None:
12
+ self.api_token = api_token
13
+ self.team_id = team_id
14
+
15
+ def _build_issue_input(self, envelope: AlertEnvelope) -> dict:
16
+ """Build Linear GraphQL issue create input."""
17
+ priority_map = {
18
+ AlertSeverity.CRITICAL: 1,
19
+ AlertSeverity.WARNING: 2,
20
+ AlertSeverity.INFO: 3,
21
+ }
22
+ title = f"[DevTorch] {envelope.title}"
23
+ description = (
24
+ f"{envelope.body}\n\n"
25
+ f"Event: {envelope.event_type}\n"
26
+ f"Branch: {envelope.branch}\n"
27
+ f"Commit: {envelope.commit_id}"
28
+ )
29
+ return {
30
+ "title": title,
31
+ "description": description,
32
+ "teamId": self.team_id,
33
+ "priority": priority_map.get(envelope.severity, 3),
34
+ }
35
+
36
+ def deliver(self, envelope: AlertEnvelope) -> DeliveryResult:
37
+ """Create a Linear issue via GraphQL API."""
38
+ try:
39
+ issue = self._build_issue_input(envelope)
40
+ query = {
41
+ "query": "mutation IssueCreate($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier } } }",
42
+ "variables": {"input": issue},
43
+ }
44
+ data = json.dumps(query).encode("utf-8")
45
+ req = urllib.request.Request(
46
+ "https://api.linear.app/graphql",
47
+ data=data,
48
+ headers={
49
+ "Content-Type": "application/json",
50
+ "Authorization": self.api_token,
51
+ },
52
+ method="POST",
53
+ )
54
+ with urllib.request.urlopen(req, timeout=10) as resp:
55
+ return DeliveryResult(
56
+ channel=self.channel_name,
57
+ success=True,
58
+ response_code=resp.status,
59
+ )
60
+ except urllib.error.HTTPError as exc:
61
+ return DeliveryResult(
62
+ channel=self.channel_name,
63
+ success=False,
64
+ error=f"HTTP {exc.code}: {exc.reason}",
65
+ response_code=exc.code,
66
+ )
67
+ except Exception as exc:
68
+ return DeliveryResult(
69
+ channel=self.channel_name,
70
+ success=False,
71
+ error=str(exc),
72
+ )
@@ -0,0 +1,66 @@
1
+ import json
2
+ import urllib.request
3
+ import urllib.error
4
+
5
+ from .base import AlertChannel, AlertEnvelope, AlertSeverity, DeliveryResult
6
+
7
+ _PD_SEVERITY_MAP = {
8
+ AlertSeverity.CRITICAL: "critical",
9
+ AlertSeverity.WARNING: "warning",
10
+ AlertSeverity.INFO: "info",
11
+ }
12
+
13
+
14
+ class PagerDutyChannel(AlertChannel):
15
+ channel_name = "pagerduty"
16
+ EVENTS_API_URL = "https://events.pagerduty.com/v2/enqueue"
17
+
18
+ def __init__(self, routing_key: str) -> None:
19
+ self.routing_key = routing_key
20
+
21
+ def _build_payload(self, envelope: AlertEnvelope) -> dict:
22
+ """Build PagerDuty Events API v2 payload."""
23
+ event_action = "trigger" if envelope.severity == AlertSeverity.CRITICAL else "acknowledge"
24
+ pd_severity = _PD_SEVERITY_MAP.get(envelope.severity, "info")
25
+
26
+ return {
27
+ "routing_key": self.routing_key,
28
+ "event_action": event_action,
29
+ "payload": {
30
+ "summary": envelope.title,
31
+ "severity": pd_severity,
32
+ "source": "devtorch",
33
+ "custom_details": envelope.metadata,
34
+ },
35
+ }
36
+
37
+ def deliver(self, envelope: AlertEnvelope) -> DeliveryResult:
38
+ """POST to PagerDuty Events API. Never raises."""
39
+ try:
40
+ payload = self._build_payload(envelope)
41
+ data = json.dumps(payload).encode("utf-8")
42
+ req = urllib.request.Request(
43
+ self.EVENTS_API_URL,
44
+ data=data,
45
+ headers={"Content-Type": "application/json"},
46
+ method="POST",
47
+ )
48
+ with urllib.request.urlopen(req, timeout=10) as resp:
49
+ return DeliveryResult(
50
+ channel=self.channel_name,
51
+ success=True,
52
+ response_code=resp.status,
53
+ )
54
+ except urllib.error.HTTPError as exc:
55
+ return DeliveryResult(
56
+ channel=self.channel_name,
57
+ success=False,
58
+ error=f"HTTP {exc.code}: {exc.reason}",
59
+ response_code=exc.code,
60
+ )
61
+ except Exception as exc:
62
+ return DeliveryResult(
63
+ channel=self.channel_name,
64
+ success=False,
65
+ error=str(exc),
66
+ )
@@ -0,0 +1,81 @@
1
+ import json
2
+ import urllib.request
3
+ import urllib.error
4
+ from typing import Any
5
+
6
+ from .base import AlertChannel, AlertEnvelope, AlertSeverity, DeliveryResult
7
+
8
+ _SEVERITY_EMOJI = {
9
+ AlertSeverity.CRITICAL: "🔴",
10
+ AlertSeverity.WARNING: "🟡",
11
+ AlertSeverity.INFO: "🔵",
12
+ }
13
+
14
+
15
+ class SlackChannel(AlertChannel):
16
+ channel_name = "slack"
17
+
18
+ def __init__(self, webhook_url: str) -> None:
19
+ self.webhook_url = webhook_url
20
+
21
+ def _build_payload(self, envelope: AlertEnvelope) -> dict:
22
+ """Build Slack Block Kit payload."""
23
+ emoji = _SEVERITY_EMOJI.get(envelope.severity, "🔵")
24
+ blocks: list[dict[str, Any]] = [
25
+ {
26
+ "type": "header",
27
+ "text": {
28
+ "type": "plain_text",
29
+ "text": f"{emoji} {envelope.severity.value.upper()}: {envelope.event_type}",
30
+ "emoji": True,
31
+ },
32
+ },
33
+ {
34
+ "type": "section",
35
+ "text": {
36
+ "type": "mrkdwn",
37
+ "text": f"*{envelope.title}*\n{envelope.body}",
38
+ },
39
+ },
40
+ {
41
+ "type": "context",
42
+ "elements": [
43
+ {
44
+ "type": "mrkdwn",
45
+ "text": f"Branch: `{envelope.branch}` | Commit: `{envelope.commit_id}`",
46
+ }
47
+ ],
48
+ },
49
+ ]
50
+ return {"blocks": blocks}
51
+
52
+ def deliver(self, envelope: AlertEnvelope) -> DeliveryResult:
53
+ """POST to Slack webhook. Uses urllib (no requests dependency)."""
54
+ try:
55
+ payload = self._build_payload(envelope)
56
+ data = json.dumps(payload).encode("utf-8")
57
+ req = urllib.request.Request(
58
+ self.webhook_url,
59
+ data=data,
60
+ headers={"Content-Type": "application/json"},
61
+ method="POST",
62
+ )
63
+ with urllib.request.urlopen(req, timeout=10) as resp:
64
+ return DeliveryResult(
65
+ channel=self.channel_name,
66
+ success=True,
67
+ response_code=resp.status,
68
+ )
69
+ except urllib.error.HTTPError as exc:
70
+ return DeliveryResult(
71
+ channel=self.channel_name,
72
+ success=False,
73
+ error=f"HTTP {exc.code}: {exc.reason}",
74
+ response_code=exc.code,
75
+ )
76
+ except Exception as exc:
77
+ return DeliveryResult(
78
+ channel=self.channel_name,
79
+ success=False,
80
+ error=str(exc),
81
+ )
@@ -0,0 +1,70 @@
1
+ import json
2
+ import urllib.request
3
+ import urllib.error
4
+
5
+ from .base import AlertChannel, AlertEnvelope, AlertSeverity, DeliveryResult
6
+
7
+
8
+ class MicrosoftTeamsChannel(AlertChannel):
9
+ channel_name = "teams"
10
+
11
+ def __init__(self, webhook_url: str) -> None:
12
+ self.webhook_url = webhook_url
13
+
14
+ def _build_payload(self, envelope: AlertEnvelope) -> dict:
15
+ """Build a simple MessageCard payload for Microsoft Teams connectors."""
16
+ color = {
17
+ AlertSeverity.CRITICAL: "ff0000",
18
+ AlertSeverity.WARNING: "ffff00",
19
+ AlertSeverity.INFO: "0000ff",
20
+ }.get(envelope.severity, "0000ff")
21
+
22
+ return {
23
+ "@type": "MessageCard",
24
+ "@context": "https://schema.org/extensions",
25
+ "themeColor": color,
26
+ "summary": f"{envelope.severity.value.upper()}: {envelope.title}",
27
+ "sections": [
28
+ {
29
+ "activityTitle": f"DevTorch {envelope.severity.value.upper()} alert",
30
+ "activitySubtitle": envelope.event_type,
31
+ "facts": [
32
+ {"name": "Title", "value": envelope.title},
33
+ {"name": "Branch", "value": envelope.branch},
34
+ {"name": "Commit", "value": envelope.commit_id},
35
+ ],
36
+ "text": envelope.body,
37
+ }
38
+ ],
39
+ }
40
+
41
+ def deliver(self, envelope: AlertEnvelope) -> DeliveryResult:
42
+ """POST to Microsoft Teams webhook."""
43
+ try:
44
+ payload = self._build_payload(envelope)
45
+ data = json.dumps(payload).encode("utf-8")
46
+ req = urllib.request.Request(
47
+ self.webhook_url,
48
+ data=data,
49
+ headers={"Content-Type": "application/json"},
50
+ method="POST",
51
+ )
52
+ with urllib.request.urlopen(req, timeout=10) as resp:
53
+ return DeliveryResult(
54
+ channel=self.channel_name,
55
+ success=True,
56
+ response_code=resp.status,
57
+ )
58
+ except urllib.error.HTTPError as exc:
59
+ return DeliveryResult(
60
+ channel=self.channel_name,
61
+ success=False,
62
+ error=f"HTTP {exc.code}: {exc.reason}",
63
+ response_code=exc.code,
64
+ )
65
+ except Exception as exc:
66
+ return DeliveryResult(
67
+ channel=self.channel_name,
68
+ success=False,
69
+ error=str(exc),
70
+ )
@@ -0,0 +1,43 @@
1
+ """Sprint 30 — Trust, Compliance & Security audit primitives.
2
+
3
+ Provides:
4
+
5
+ * PII/secrets scrubbing (regex-based default, optional presidio).
6
+ * Privacy policy enforcement stored in `.GCC/privacy.json`.
7
+ * Exportable, filterable audit reports from `.GCC/events.log.jsonl`.
8
+ * Optional Ed25519 tamper-evident signing of exported JSON.
9
+
10
+ Design decisions
11
+ ----------------
12
+ * Regex-based redaction is the default so the OSS core stays lightweight and
13
+ does not pull heavy ML models (spacy/presidio) unless explicitly requested.
14
+ * Privacy configuration is a simple JSON file under `.GCC/` so it is versioned
15
+ with the repository and discoverable by the CLI and MCP server.
16
+ * Export signing uses asymmetric Ed25519 (via optional ``cryptography``) so a
17
+ report can be verified by anyone holding the public key, without access to the
18
+ private signing key.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from .scrubber import Scrubber, RegexScrubber, make_scrubber
24
+ from .privacy import PrivacyConfig, load_privacy_config, save_privacy_config, apply_privacy_policy
25
+ from .exporter import AuditFilters, AuditExporter, export_audit
26
+ from .signing import ExportSigner, AuditSignatureError
27
+ from .service import AuditService
28
+
29
+ __all__ = [
30
+ "Scrubber",
31
+ "RegexScrubber",
32
+ "make_scrubber",
33
+ "PrivacyConfig",
34
+ "load_privacy_config",
35
+ "save_privacy_config",
36
+ "apply_privacy_policy",
37
+ "AuditFilters",
38
+ "AuditExporter",
39
+ "export_audit",
40
+ "ExportSigner",
41
+ "AuditSignatureError",
42
+ "AuditService",
43
+ ]