agent_os_kernel 3.1.0__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 (337) hide show
  1. agent_control_plane/__init__.py +662 -0
  2. agent_control_plane/a2a_adapter.py +543 -0
  3. agent_control_plane/adapter.py +417 -0
  4. agent_control_plane/agent_hibernation.py +394 -0
  5. agent_control_plane/agent_kernel.py +470 -0
  6. agent_control_plane/compliance.py +720 -0
  7. agent_control_plane/constraint_graphs.py +478 -0
  8. agent_control_plane/control_plane.py +854 -0
  9. agent_control_plane/example_executors.py +195 -0
  10. agent_control_plane/execution_engine.py +231 -0
  11. agent_control_plane/flight_recorder.py +846 -0
  12. agent_control_plane/governance_layer.py +435 -0
  13. agent_control_plane/hf_utils.py +563 -0
  14. agent_control_plane/interfaces/__init__.py +55 -0
  15. agent_control_plane/interfaces/kernel_interface.py +361 -0
  16. agent_control_plane/interfaces/plugin_interface.py +497 -0
  17. agent_control_plane/interfaces/protocol_interfaces.py +387 -0
  18. agent_control_plane/kernel_space.py +1009 -0
  19. agent_control_plane/langchain_adapter.py +424 -0
  20. agent_control_plane/lifecycle.py +3113 -0
  21. agent_control_plane/mcp_adapter.py +653 -0
  22. agent_control_plane/ml_safety.py +563 -0
  23. agent_control_plane/multimodal.py +727 -0
  24. agent_control_plane/mute_agent.py +422 -0
  25. agent_control_plane/observability.py +787 -0
  26. agent_control_plane/orchestrator.py +482 -0
  27. agent_control_plane/plugin_registry.py +750 -0
  28. agent_control_plane/policy_engine.py +954 -0
  29. agent_control_plane/process_isolation.py +777 -0
  30. agent_control_plane/shadow_mode.py +310 -0
  31. agent_control_plane/signals.py +493 -0
  32. agent_control_plane/supervisor_agents.py +430 -0
  33. agent_control_plane/time_travel_debugger.py +557 -0
  34. agent_control_plane/tool_registry.py +452 -0
  35. agent_control_plane/vfs.py +697 -0
  36. agent_kernel/__init__.py +69 -0
  37. agent_kernel/analyzer.py +435 -0
  38. agent_kernel/auditor.py +36 -0
  39. agent_kernel/completeness_auditor.py +237 -0
  40. agent_kernel/detector.py +203 -0
  41. agent_kernel/kernel.py +744 -0
  42. agent_kernel/memory_manager.py +85 -0
  43. agent_kernel/models.py +374 -0
  44. agent_kernel/nudge_mechanism.py +263 -0
  45. agent_kernel/outcome_analyzer.py +338 -0
  46. agent_kernel/patcher.py +582 -0
  47. agent_kernel/semantic_analyzer.py +316 -0
  48. agent_kernel/semantic_purge.py +349 -0
  49. agent_kernel/simulator.py +449 -0
  50. agent_kernel/teacher.py +85 -0
  51. agent_kernel/triage.py +152 -0
  52. agent_os/__init__.py +409 -0
  53. agent_os/_adversarial_impl.py +200 -0
  54. agent_os/_circuit_breaker_impl.py +232 -0
  55. agent_os/_mcp_metrics.py +193 -0
  56. agent_os/adversarial.py +20 -0
  57. agent_os/agents_compat.py +490 -0
  58. agent_os/audit_logger.py +135 -0
  59. agent_os/base_agent.py +651 -0
  60. agent_os/circuit_breaker.py +34 -0
  61. agent_os/cli/__init__.py +659 -0
  62. agent_os/cli/cmd_audit.py +128 -0
  63. agent_os/cli/cmd_init.py +152 -0
  64. agent_os/cli/cmd_policy.py +41 -0
  65. agent_os/cli/cmd_policy_gen.py +180 -0
  66. agent_os/cli/cmd_validate.py +258 -0
  67. agent_os/cli/mcp_scan.py +265 -0
  68. agent_os/cli/output.py +192 -0
  69. agent_os/cli/policy_checker.py +330 -0
  70. agent_os/compat.py +74 -0
  71. agent_os/constraint_graph.py +234 -0
  72. agent_os/content_governance.py +140 -0
  73. agent_os/context_budget.py +305 -0
  74. agent_os/credential_redactor.py +224 -0
  75. agent_os/diff_policy.py +89 -0
  76. agent_os/egress_policy.py +159 -0
  77. agent_os/escalation.py +276 -0
  78. agent_os/event_bus.py +124 -0
  79. agent_os/exceptions.py +180 -0
  80. agent_os/execution_context_policy.py +141 -0
  81. agent_os/github_enterprise.py +96 -0
  82. agent_os/health.py +20 -0
  83. agent_os/integrations/__init__.py +279 -0
  84. agent_os/integrations/a2a_adapter.py +279 -0
  85. agent_os/integrations/agent_lightning/__init__.py +30 -0
  86. agent_os/integrations/anthropic_adapter.py +420 -0
  87. agent_os/integrations/autogen_adapter.py +620 -0
  88. agent_os/integrations/base.py +1137 -0
  89. agent_os/integrations/compat.py +229 -0
  90. agent_os/integrations/config.py +98 -0
  91. agent_os/integrations/conversation_guardian.py +957 -0
  92. agent_os/integrations/crewai_adapter.py +467 -0
  93. agent_os/integrations/drift_detector.py +425 -0
  94. agent_os/integrations/dry_run.py +124 -0
  95. agent_os/integrations/escalation.py +582 -0
  96. agent_os/integrations/gemini_adapter.py +364 -0
  97. agent_os/integrations/google_adk_adapter.py +633 -0
  98. agent_os/integrations/guardrails_adapter.py +394 -0
  99. agent_os/integrations/health.py +197 -0
  100. agent_os/integrations/langchain_adapter.py +654 -0
  101. agent_os/integrations/llamafirewall.py +343 -0
  102. agent_os/integrations/llamaindex_adapter.py +188 -0
  103. agent_os/integrations/logging.py +191 -0
  104. agent_os/integrations/maf_adapter.py +631 -0
  105. agent_os/integrations/mistral_adapter.py +365 -0
  106. agent_os/integrations/openai_adapter.py +816 -0
  107. agent_os/integrations/openai_agents_sdk.py +406 -0
  108. agent_os/integrations/policy_compose.py +171 -0
  109. agent_os/integrations/profiling.py +144 -0
  110. agent_os/integrations/pydantic_ai_adapter.py +420 -0
  111. agent_os/integrations/rate_limiter.py +130 -0
  112. agent_os/integrations/rbac.py +143 -0
  113. agent_os/integrations/registry.py +113 -0
  114. agent_os/integrations/scope_guard.py +303 -0
  115. agent_os/integrations/semantic_kernel_adapter.py +769 -0
  116. agent_os/integrations/smolagents_adapter.py +629 -0
  117. agent_os/integrations/templates.py +178 -0
  118. agent_os/integrations/token_budget.py +134 -0
  119. agent_os/integrations/tool_aliases.py +190 -0
  120. agent_os/integrations/webhooks.py +177 -0
  121. agent_os/lite.py +208 -0
  122. agent_os/mcp_gateway.py +385 -0
  123. agent_os/mcp_message_signer.py +273 -0
  124. agent_os/mcp_protocols.py +161 -0
  125. agent_os/mcp_response_scanner.py +232 -0
  126. agent_os/mcp_security.py +924 -0
  127. agent_os/mcp_session_auth.py +231 -0
  128. agent_os/mcp_sliding_rate_limiter.py +184 -0
  129. agent_os/memory_guard.py +409 -0
  130. agent_os/metrics.py +134 -0
  131. agent_os/mute.py +428 -0
  132. agent_os/mute_agent.py +209 -0
  133. agent_os/policies/__init__.py +77 -0
  134. agent_os/policies/async_evaluator.py +275 -0
  135. agent_os/policies/backends.py +670 -0
  136. agent_os/policies/bridge.py +169 -0
  137. agent_os/policies/budget.py +85 -0
  138. agent_os/policies/cli.py +294 -0
  139. agent_os/policies/conflict_resolution.py +270 -0
  140. agent_os/policies/data_classification.py +252 -0
  141. agent_os/policies/evaluator.py +239 -0
  142. agent_os/policies/policy_schema.json +228 -0
  143. agent_os/policies/rate_limiting.py +145 -0
  144. agent_os/policies/schema.py +115 -0
  145. agent_os/policies/shared.py +331 -0
  146. agent_os/prompt_injection.py +694 -0
  147. agent_os/providers.py +182 -0
  148. agent_os/py.typed +0 -0
  149. agent_os/retry.py +81 -0
  150. agent_os/reversibility.py +251 -0
  151. agent_os/sandbox.py +432 -0
  152. agent_os/sandbox_provider.py +140 -0
  153. agent_os/secure_codegen.py +525 -0
  154. agent_os/security_skills.py +538 -0
  155. agent_os/semantic_policy.py +422 -0
  156. agent_os/server/__init__.py +15 -0
  157. agent_os/server/__main__.py +25 -0
  158. agent_os/server/app.py +277 -0
  159. agent_os/server/models.py +104 -0
  160. agent_os/shift_left_metrics.py +130 -0
  161. agent_os/stateless.py +742 -0
  162. agent_os/supervisor.py +148 -0
  163. agent_os/task_outcome.py +148 -0
  164. agent_os/transparency.py +181 -0
  165. agent_os/trust_root.py +128 -0
  166. agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
  167. agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
  168. agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
  169. agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
  170. agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
  171. agent_os_observability/__init__.py +27 -0
  172. agent_os_observability/dashboards.py +898 -0
  173. agent_os_observability/metrics.py +398 -0
  174. agent_os_observability/server.py +223 -0
  175. agent_os_observability/tracer.py +232 -0
  176. agent_primitives/__init__.py +24 -0
  177. agent_primitives/failures.py +84 -0
  178. agent_primitives/py.typed +0 -0
  179. amb_core/__init__.py +177 -0
  180. amb_core/adapters/__init__.py +57 -0
  181. amb_core/adapters/aws_sqs_broker.py +376 -0
  182. amb_core/adapters/azure_servicebus_broker.py +340 -0
  183. amb_core/adapters/kafka_broker.py +260 -0
  184. amb_core/adapters/nats_broker.py +285 -0
  185. amb_core/adapters/rabbitmq_broker.py +235 -0
  186. amb_core/adapters/redis_broker.py +262 -0
  187. amb_core/broker.py +145 -0
  188. amb_core/bus.py +481 -0
  189. amb_core/cloudevents.py +509 -0
  190. amb_core/dlq.py +345 -0
  191. amb_core/hf_utils.py +536 -0
  192. amb_core/memory_broker.py +410 -0
  193. amb_core/models.py +141 -0
  194. amb_core/persistence.py +529 -0
  195. amb_core/schema.py +294 -0
  196. amb_core/tracing.py +358 -0
  197. atr/__init__.py +640 -0
  198. atr/access.py +348 -0
  199. atr/composition.py +645 -0
  200. atr/decorator.py +357 -0
  201. atr/executor.py +384 -0
  202. atr/health.py +557 -0
  203. atr/hf_utils.py +449 -0
  204. atr/injection.py +422 -0
  205. atr/metrics.py +440 -0
  206. atr/policies.py +403 -0
  207. atr/py.typed +2 -0
  208. atr/registry.py +452 -0
  209. atr/schema.py +480 -0
  210. atr/tools/safe/__init__.py +75 -0
  211. atr/tools/safe/calculator.py +467 -0
  212. atr/tools/safe/datetime_tool.py +443 -0
  213. atr/tools/safe/file_reader.py +402 -0
  214. atr/tools/safe/http_client.py +316 -0
  215. atr/tools/safe/json_parser.py +374 -0
  216. atr/tools/safe/text_tool.py +537 -0
  217. atr/tools/safe/toolkit.py +175 -0
  218. caas/__init__.py +162 -0
  219. caas/api/__init__.py +7 -0
  220. caas/api/server.py +1328 -0
  221. caas/caching.py +834 -0
  222. caas/cli.py +210 -0
  223. caas/conversation.py +223 -0
  224. caas/decay.py +72 -0
  225. caas/detection/__init__.py +9 -0
  226. caas/detection/detector.py +238 -0
  227. caas/enrichment.py +130 -0
  228. caas/gateway/__init__.py +27 -0
  229. caas/gateway/trust_gateway.py +474 -0
  230. caas/hf_utils.py +479 -0
  231. caas/ingestion/__init__.py +23 -0
  232. caas/ingestion/processors.py +253 -0
  233. caas/ingestion/structure_parser.py +188 -0
  234. caas/models.py +356 -0
  235. caas/pragmatic_truth.py +444 -0
  236. caas/routing/__init__.py +10 -0
  237. caas/routing/heuristic_router.py +58 -0
  238. caas/storage/__init__.py +9 -0
  239. caas/storage/store.py +389 -0
  240. caas/triad.py +213 -0
  241. caas/tuning/__init__.py +9 -0
  242. caas/tuning/tuner.py +329 -0
  243. caas/vfs/__init__.py +14 -0
  244. caas/vfs/filesystem.py +452 -0
  245. cmvk/__init__.py +218 -0
  246. cmvk/audit.py +402 -0
  247. cmvk/benchmarks.py +478 -0
  248. cmvk/constitutional.py +904 -0
  249. cmvk/hf_utils.py +301 -0
  250. cmvk/metrics.py +473 -0
  251. cmvk/profiles.py +300 -0
  252. cmvk/py.typed +0 -0
  253. cmvk/types.py +12 -0
  254. cmvk/verification.py +956 -0
  255. emk/__init__.py +89 -0
  256. emk/causal.py +352 -0
  257. emk/hf_utils.py +421 -0
  258. emk/indexer.py +83 -0
  259. emk/py.typed +0 -0
  260. emk/schema.py +204 -0
  261. emk/sleep_cycle.py +347 -0
  262. emk/store.py +281 -0
  263. iatp/__init__.py +166 -0
  264. iatp/attestation.py +461 -0
  265. iatp/cli.py +317 -0
  266. iatp/hf_utils.py +472 -0
  267. iatp/ipc_pipes.py +580 -0
  268. iatp/main.py +412 -0
  269. iatp/models/__init__.py +447 -0
  270. iatp/policy_engine.py +337 -0
  271. iatp/py.typed +2 -0
  272. iatp/recovery.py +321 -0
  273. iatp/security/__init__.py +270 -0
  274. iatp/sidecar/__init__.py +519 -0
  275. iatp/telemetry/__init__.py +164 -0
  276. iatp/tests/__init__.py +1 -0
  277. iatp/tests/test_attestation.py +370 -0
  278. iatp/tests/test_cli.py +131 -0
  279. iatp/tests/test_ed25519_attestation.py +211 -0
  280. iatp/tests/test_models.py +130 -0
  281. iatp/tests/test_policy_engine.py +347 -0
  282. iatp/tests/test_recovery.py +281 -0
  283. iatp/tests/test_security.py +222 -0
  284. iatp/tests/test_sidecar.py +167 -0
  285. iatp/tests/test_telemetry.py +175 -0
  286. mcp_kernel_server/__init__.py +28 -0
  287. mcp_kernel_server/cli.py +274 -0
  288. mcp_kernel_server/resources.py +217 -0
  289. mcp_kernel_server/server.py +564 -0
  290. mcp_kernel_server/tools.py +1174 -0
  291. mute_agent/__init__.py +68 -0
  292. mute_agent/core/__init__.py +1 -0
  293. mute_agent/core/execution_agent.py +166 -0
  294. mute_agent/core/handshake_protocol.py +201 -0
  295. mute_agent/core/reasoning_agent.py +238 -0
  296. mute_agent/knowledge_graph/__init__.py +1 -0
  297. mute_agent/knowledge_graph/graph_elements.py +65 -0
  298. mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
  299. mute_agent/knowledge_graph/subgraph.py +224 -0
  300. mute_agent/listener/__init__.py +43 -0
  301. mute_agent/listener/adapters/__init__.py +31 -0
  302. mute_agent/listener/adapters/base_adapter.py +189 -0
  303. mute_agent/listener/adapters/caas_adapter.py +344 -0
  304. mute_agent/listener/adapters/control_plane_adapter.py +436 -0
  305. mute_agent/listener/adapters/iatp_adapter.py +332 -0
  306. mute_agent/listener/adapters/scak_adapter.py +251 -0
  307. mute_agent/listener/listener.py +610 -0
  308. mute_agent/listener/state_observer.py +436 -0
  309. mute_agent/listener/threshold_config.py +313 -0
  310. mute_agent/super_system/__init__.py +1 -0
  311. mute_agent/super_system/router.py +204 -0
  312. mute_agent/visualization/__init__.py +10 -0
  313. mute_agent/visualization/graph_debugger.py +502 -0
  314. nexus/README.md +60 -0
  315. nexus/__init__.py +51 -0
  316. nexus/arbiter.py +359 -0
  317. nexus/client.py +466 -0
  318. nexus/dmz.py +444 -0
  319. nexus/escrow.py +430 -0
  320. nexus/exceptions.py +286 -0
  321. nexus/pyproject.toml +36 -0
  322. nexus/registry.py +393 -0
  323. nexus/reputation.py +425 -0
  324. nexus/schemas/__init__.py +51 -0
  325. nexus/schemas/compliance.py +276 -0
  326. nexus/schemas/escrow.py +251 -0
  327. nexus/schemas/manifest.py +225 -0
  328. nexus/schemas/receipt.py +208 -0
  329. nexus/tests/__init__.py +0 -0
  330. nexus/tests/conftest.py +146 -0
  331. nexus/tests/test_arbiter.py +192 -0
  332. nexus/tests/test_dmz.py +194 -0
  333. nexus/tests/test_escrow.py +276 -0
  334. nexus/tests/test_exceptions.py +225 -0
  335. nexus/tests/test_registry.py +232 -0
  336. nexus/tests/test_reputation.py +328 -0
  337. nexus/tests/test_schemas.py +295 -0
caas/api/server.py ADDED
@@ -0,0 +1,1328 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ REST API for Context-as-a-Service.
5
+ """
6
+
7
+ import uuid
8
+ from datetime import datetime, timezone
9
+ from typing import List, Optional
10
+
11
+ from fastapi import FastAPI, HTTPException, UploadFile, File, Form
12
+ from fastapi.responses import JSONResponse
13
+
14
+ from caas.models import (
15
+ Document,
16
+ DocumentType,
17
+ ContentFormat,
18
+ ContextRequest,
19
+ ContextResponse,
20
+ ContextLayer,
21
+ ContextTriadRequest,
22
+ ContextTriadResponse,
23
+ AddContextRequest,
24
+ RouteRequest,
25
+ RoutingDecision,
26
+ ModelTier,
27
+ AddTurnRequest,
28
+ UpdateTurnRequest,
29
+ ConversationHistoryResponse,
30
+ CreateFileRequest,
31
+ UpdateFileRequest,
32
+ ReadFileRequest,
33
+ DeleteFileRequest,
34
+ ListFilesRequest,
35
+ FileResponse,
36
+ FileListResponse,
37
+ )
38
+ from caas.ingestion import ProcessorFactory
39
+ from caas.detection import DocumentTypeDetector, StructureAnalyzer
40
+ from caas.tuning import WeightTuner, CorpusAnalyzer
41
+ from caas.storage import DocumentStore, ContextExtractor
42
+ from caas.triad import ContextTriadManager
43
+ from caas.routing import HeuristicRouter
44
+ from caas.conversation import ConversationManager
45
+ from caas.gateway import TrustGateway, SecurityPolicy, DeploymentMode
46
+ from caas.vfs import VirtualFileSystem
47
+
48
+
49
+ # Initialize FastAPI app
50
+ app = FastAPI(
51
+ title="Context-as-a-Service",
52
+ description="Intelligent context extraction and serving",
53
+ version="0.1.0"
54
+ )
55
+
56
+ # Initialize components
57
+ document_store = DocumentStore()
58
+ detector = DocumentTypeDetector()
59
+ structure_analyzer = StructureAnalyzer()
60
+ weight_tuner = WeightTuner()
61
+ corpus_analyzer = CorpusAnalyzer()
62
+ triad_manager = ContextTriadManager()
63
+ heuristic_router = HeuristicRouter()
64
+ conversation_manager = ConversationManager(max_turns=10) # Sliding window with 10 turns
65
+ # Virtual File System for SDLC agents
66
+ vfs = VirtualFileSystem()
67
+ # Trust Gateway with enterprise-grade security
68
+ trust_gateway = TrustGateway(
69
+ security_policy=SecurityPolicy(
70
+ deployment_mode=DeploymentMode.ON_PREM,
71
+ security_level="high"
72
+ ),
73
+ audit_enabled=True
74
+ )
75
+ # Note: context_extractor is created per-request with user-specified decay settings
76
+
77
+
78
+ @app.get("/")
79
+ async def root():
80
+ """Root endpoint."""
81
+ return {
82
+ "service": "Context-as-a-Service",
83
+ "version": "0.1.0",
84
+ "status": "operational",
85
+ "endpoints": {
86
+ "ingest": "/ingest",
87
+ "documents": "/documents",
88
+ "context": "/context/{document_id}",
89
+ "analyze": "/analyze/{document_id}",
90
+ "corpus": "/corpus/analyze",
91
+ "route": "/route",
92
+ "triad": "/triad",
93
+ "triad_hot": "/triad/hot",
94
+ "triad_warm": "/triad/warm",
95
+ "triad_cold": "/triad/cold",
96
+ "conversation": "/conversation",
97
+ "conversation_add": "/conversation/turn",
98
+ "conversation_stats": "/conversation/stats",
99
+ "gateway": "/gateway",
100
+ "gateway_route": "/gateway/route",
101
+ "gateway_info": "/gateway/info",
102
+ "gateway_audit": "/gateway/audit",
103
+ "vfs_create": "/vfs/files (POST)",
104
+ "vfs_read": "/vfs/files (GET)",
105
+ "vfs_update": "/vfs/files (PUT)",
106
+ "vfs_delete": "/vfs/files (DELETE)",
107
+ "vfs_list": "/vfs/list",
108
+ "vfs_history": "/vfs/history",
109
+ "vfs_state": "/vfs/state",
110
+ }
111
+ }
112
+
113
+
114
+
115
+ @app.get("/health")
116
+ async def health_check():
117
+ """Health check endpoint."""
118
+ return {"status": "healthy", "timestamp": datetime.now(timezone.utc).isoformat()}
119
+
120
+
121
+ @app.post("/ingest")
122
+ async def ingest_document(
123
+ file: UploadFile = File(...),
124
+ format: ContentFormat = Form(...),
125
+ title: Optional[str] = Form(None),
126
+ source_type: Optional[str] = Form(None),
127
+ source_url: Optional[str] = Form(None)
128
+ ):
129
+ """
130
+ Ingest a document for processing.
131
+
132
+ The service will:
133
+ 1. Process the raw content
134
+ 2. Auto-detect the document type and structure
135
+ 3. Auto-tune weights for sections
136
+ 4. Detect or use provided source type for source attribution tracking
137
+ 5. Store the processed document
138
+
139
+ Source Attribution Support:
140
+ - source_type: Explicitly specify source type (official_docs, team_chat, practical_logs, etc.)
141
+ - source_url: Optional URL to the original source
142
+
143
+ Args:
144
+ file: The file to ingest
145
+ format: The file format (pdf, html, code)
146
+ title: Optional title for the document
147
+ source_type: Optional source type for citation tracking
148
+ source_url: Optional URL to the original source
149
+
150
+ Returns:
151
+ Processed document information
152
+ """
153
+ try:
154
+ # Read file content
155
+ content = await file.read()
156
+
157
+ # Generate document ID
158
+ doc_id = str(uuid.uuid4())
159
+
160
+ # Process the document
161
+ processor = ProcessorFactory.get_processor(format)
162
+ metadata = {
163
+ "id": doc_id,
164
+ "title": title or file.filename,
165
+ "filename": file.filename,
166
+ }
167
+
168
+ # Add source metadata if provided
169
+ if source_type:
170
+ metadata['source_type'] = source_type
171
+ if source_url:
172
+ metadata['source_url'] = source_url
173
+
174
+ document = processor.process(content, metadata)
175
+
176
+ # Auto-detect document type
177
+ detected_type = detector.detect(document)
178
+ document.detected_type = detected_type
179
+
180
+ # Auto-tune weights
181
+ document = weight_tuner.tune(document)
182
+
183
+ # Add timestamp
184
+ document.ingestion_timestamp = datetime.now(timezone.utc).isoformat()
185
+
186
+ # Store document
187
+ document_store.add(document)
188
+
189
+ # Add to corpus analyzer
190
+ corpus_analyzer.add_document(document)
191
+
192
+ return {
193
+ "document_id": document.id,
194
+ "title": document.title,
195
+ "detected_type": document.detected_type,
196
+ "format": document.format,
197
+ "sections_found": len(document.sections),
198
+ "weights": document.weights,
199
+ "source_type": source_type or "auto-detected",
200
+ "status": "ingested"
201
+ }
202
+
203
+ except Exception as e:
204
+ raise HTTPException(status_code=500, detail=f"Ingestion failed: {str(e)}")
205
+
206
+
207
+ @app.get("/documents")
208
+ async def list_documents(doc_type: Optional[DocumentType] = None):
209
+ """
210
+ List all documents or filter by type.
211
+
212
+ Args:
213
+ doc_type: Optional document type filter
214
+
215
+ Returns:
216
+ List of documents
217
+ """
218
+ if doc_type:
219
+ documents = document_store.list_by_type(doc_type)
220
+ else:
221
+ documents = document_store.list_all()
222
+
223
+ return {
224
+ "total": len(documents),
225
+ "documents": [
226
+ {
227
+ "id": doc.id,
228
+ "title": doc.title,
229
+ "type": doc.detected_type,
230
+ "format": doc.format,
231
+ "sections": len(doc.sections),
232
+ "timestamp": doc.ingestion_timestamp,
233
+ }
234
+ for doc in documents
235
+ ]
236
+ }
237
+
238
+
239
+ @app.get("/documents/{document_id}")
240
+ async def get_document(document_id: str):
241
+ """
242
+ Get detailed information about a specific document.
243
+
244
+ Args:
245
+ document_id: The document ID
246
+
247
+ Returns:
248
+ Document details
249
+ """
250
+ document = document_store.get(document_id)
251
+ if not document:
252
+ raise HTTPException(status_code=404, detail="Document not found")
253
+
254
+ return {
255
+ "id": document.id,
256
+ "title": document.title,
257
+ "type": document.detected_type,
258
+ "format": document.format,
259
+ "sections": [
260
+ {
261
+ "title": s.title,
262
+ "weight": s.weight,
263
+ "importance": s.importance_score,
264
+ "length": len(s.content),
265
+ }
266
+ for s in document.sections
267
+ ],
268
+ "metadata": document.metadata,
269
+ "weights": document.weights,
270
+ "timestamp": document.ingestion_timestamp,
271
+ }
272
+
273
+
274
+ @app.post("/context/{document_id}")
275
+ async def get_context(document_id: str, request: ContextRequest):
276
+ """
277
+ Get optimized context from a document.
278
+
279
+ This endpoint returns the most relevant context based on:
280
+ - Auto-tuned section weights
281
+ - Time-based decay (prioritizes recent content)
282
+ - Optional query for focused extraction
283
+ - Token limits
284
+ - Source citations for transparency (source attribution)
285
+ - Conflict detection between official and practical sources
286
+
287
+ Source Attribution Philosophy:
288
+ - Provides REAL answers, not just OFFICIAL ones
289
+ - When official docs conflict with practical experience, shows both
290
+ - Includes transparent citations (e.g., "from Slack conversation")
291
+ - Example: "Officially, limit is 100. However, team reports crashes after 50."
292
+
293
+ Time Decay Formula: Score = Base_Weight * (1 / (1 + days_elapsed * decay_rate))
294
+ Result: Recent documents rank higher than old documents, even with lower similarity.
295
+
296
+ Args:
297
+ document_id: The document ID
298
+ request: Context request parameters (includes enable_time_decay, decay_rate, enable_citations, detect_conflicts)
299
+
300
+ Returns:
301
+ Optimized context with time-weighted relevance, citations, and conflict detection
302
+ """
303
+ document = document_store.get(document_id)
304
+ if not document:
305
+ raise HTTPException(status_code=404, detail="Document not found")
306
+
307
+ # Create context extractor with requested settings
308
+ extractor = ContextExtractor(
309
+ document_store,
310
+ enrich_metadata=request.include_metadata,
311
+ enable_time_decay=request.enable_time_decay,
312
+ decay_rate=request.decay_rate,
313
+ enable_citations=request.enable_citations,
314
+ detect_conflicts=request.detect_conflicts
315
+ )
316
+
317
+ # Extract context
318
+ context, metadata = extractor.extract_context(
319
+ document_id,
320
+ request.query,
321
+ request.max_tokens
322
+ )
323
+
324
+ # Estimate tokens (rough approximation)
325
+ estimated_tokens = len(context) // 4
326
+
327
+ response = ContextResponse(
328
+ document_id=document_id,
329
+ document_type=document.detected_type,
330
+ context=context,
331
+ sections_used=metadata.get("sections_used", []),
332
+ total_tokens=estimated_tokens,
333
+ weights_applied=metadata.get("weights_applied", {}),
334
+ metadata=metadata if request.include_metadata else {},
335
+ source_citations=metadata.get("citations", []),
336
+ source_conflicts=metadata.get("conflicts", [])
337
+ )
338
+
339
+ return response
340
+
341
+
342
+ @app.get("/analyze/{document_id}")
343
+ async def analyze_document(document_id: str):
344
+ """
345
+ Analyze a document's structure and content.
346
+
347
+ Args:
348
+ document_id: The document ID
349
+
350
+ Returns:
351
+ Analysis results
352
+ """
353
+ document = document_store.get(document_id)
354
+ if not document:
355
+ raise HTTPException(status_code=404, detail="Document not found")
356
+
357
+ # Perform structure analysis
358
+ structure = detector.detect_structure(document)
359
+ analysis = structure_analyzer.analyze(document)
360
+
361
+ return {
362
+ "document_id": document_id,
363
+ "structure": structure,
364
+ "analysis": analysis,
365
+ }
366
+
367
+
368
+ @app.get("/corpus/analyze")
369
+ async def analyze_corpus():
370
+ """
371
+ Analyze the entire corpus of documents.
372
+
373
+ Returns insights about:
374
+ - Document type distribution
375
+ - Common section patterns
376
+ - Average weights
377
+ - Optimization suggestions
378
+
379
+ Returns:
380
+ Corpus analysis results
381
+ """
382
+ analysis = corpus_analyzer.analyze_corpus()
383
+ return analysis
384
+
385
+
386
+ @app.delete("/documents/{document_id}")
387
+ async def delete_document(document_id: str):
388
+ """
389
+ Delete a document.
390
+
391
+ Args:
392
+ document_id: The document ID
393
+
394
+ Returns:
395
+ Deletion status
396
+ """
397
+ success = document_store.delete(document_id)
398
+ if not success:
399
+ raise HTTPException(status_code=404, detail="Document not found")
400
+
401
+ return {"status": "deleted", "document_id": document_id}
402
+
403
+
404
+ @app.get("/search")
405
+ async def search_documents(
406
+ q: str,
407
+ enable_time_decay: bool = True,
408
+ decay_rate: float = 1.0
409
+ ):
410
+ """
411
+ Search documents by content or metadata with time-based decay ranking.
412
+
413
+ When time decay is enabled (default):
414
+ - Recent documents are ranked higher than old documents
415
+ - Formula: relevance_score = match_score * decay_factor
416
+ - Example: Yesterday's 80% match beats Last Year's 95% match
417
+
418
+ Args:
419
+ q: The search query
420
+ enable_time_decay: Apply time-based decay to ranking (default: True)
421
+ decay_rate: Rate of decay, higher = faster decay (default: 1.0)
422
+
423
+ Returns:
424
+ Matching documents sorted by time-weighted relevance
425
+ """
426
+ results = document_store.search(
427
+ q,
428
+ enable_time_decay=enable_time_decay,
429
+ decay_rate=decay_rate
430
+ )
431
+
432
+ return {
433
+ "query": q,
434
+ "enable_time_decay": enable_time_decay,
435
+ "decay_rate": decay_rate,
436
+ "total_results": len(results),
437
+ "documents": [
438
+ {
439
+ "id": doc.id,
440
+ "title": doc.title,
441
+ "type": doc.detected_type,
442
+ "format": doc.format,
443
+ "search_score": doc.metadata.get('_search_score', 0),
444
+ "decay_factor": doc.metadata.get('_decay_factor', 1.0),
445
+ "ingestion_timestamp": doc.ingestion_timestamp,
446
+ }
447
+ for doc in results
448
+ ]
449
+ }
450
+
451
+
452
+ @app.post("/route")
453
+ async def route_query(request: RouteRequest):
454
+ """
455
+ Route a query to the appropriate model tier using deterministic heuristics.
456
+
457
+ The Heuristic Router Philosophy:
458
+ Use Deterministic Heuristics, not AI Classifiers. We can solve 80% of routing
459
+ with simple logic that takes 0ms. The goal isn't 100% routing accuracy.
460
+ The goal is instant response time for the trivial stuff, preserving the
461
+ "Big Brain" budget for the hard stuff.
462
+
463
+ Routing Rules (in priority order):
464
+ 1. Greetings ("Hi", "Thanks") → CANNED response (zero cost, instant)
465
+ 2. Smart keywords ("Summarize", "Analyze", "Compare") → SMART model (GPT-4o)
466
+ 3. Short queries (< 50 chars) → FAST model (GPT-4o-mini)
467
+ 4. Long queries → SMART model (better safe than sorry)
468
+
469
+ Model Tiers:
470
+ - CANNED: Pre-defined responses for greetings (zero cost, 0ms latency)
471
+ - FAST: Fast model like GPT-4o-mini (low cost, ~200ms latency)
472
+ - SMART: Smart model like GPT-4o (high cost, ~500ms+ latency)
473
+
474
+ Args:
475
+ request: RouteRequest with the query to route
476
+
477
+ Returns:
478
+ RoutingDecision with tier, reason, confidence, and suggested model
479
+ """
480
+ try:
481
+ decision = heuristic_router.route(request.query)
482
+
483
+ # If it's a canned response, include the actual response
484
+ response_data = decision.model_dump()
485
+ if decision.model_tier == ModelTier.CANNED:
486
+ canned_response = heuristic_router.get_canned_response(request.query)
487
+ if canned_response:
488
+ response_data["canned_response"] = canned_response
489
+
490
+ return response_data
491
+
492
+ except Exception as e:
493
+ raise HTTPException(status_code=500, detail=f"Routing failed: {str(e)}")
494
+
495
+
496
+
497
+ # ===========================
498
+ # Tiered Context Endpoints
499
+ # ===========================
500
+
501
+ @app.post("/triad/hot")
502
+ async def add_hot_context(request: AddContextRequest):
503
+ """
504
+ Add hot context - the current situation.
505
+
506
+ Hot context represents what is happening RIGHT NOW:
507
+ - Current conversation messages
508
+ - Open VS Code tabs
509
+ - Error logs streaming in real-time
510
+ - Active debugging session
511
+
512
+ Policy: "Attention Head" - Hot context overrides everything.
513
+
514
+ Args:
515
+ request: AddContextRequest with content, metadata, and priority
516
+
517
+ Returns:
518
+ Created item ID
519
+ """
520
+ try:
521
+ item_id = triad_manager.add_hot_context(
522
+ request.content,
523
+ request.metadata,
524
+ request.priority
525
+ )
526
+ return {
527
+ "status": "success",
528
+ "layer": "hot",
529
+ "item_id": item_id,
530
+ "message": "Hot context added successfully"
531
+ }
532
+ except Exception as e:
533
+ raise HTTPException(status_code=500, detail=f"Failed to add hot context: {str(e)}")
534
+
535
+
536
+ @app.post("/triad/warm")
537
+ async def add_warm_context(request: AddContextRequest):
538
+ """
539
+ Add warm context - the user persona.
540
+
541
+ Warm context represents WHO THE USER IS:
542
+ - LinkedIn profile
543
+ - Medium articles
544
+ - GitHub bio
545
+ - Coding style preferences
546
+ - Favorite libraries
547
+ - Communication style
548
+
549
+ Policy: "Always On Filter" - Warm context is persistent and colors
550
+ how the AI speaks to you.
551
+
552
+ Args:
553
+ request: AddContextRequest with content, metadata, and priority
554
+
555
+ Returns:
556
+ Created item ID
557
+ """
558
+ try:
559
+ item_id = triad_manager.add_warm_context(
560
+ request.content,
561
+ request.metadata,
562
+ request.priority
563
+ )
564
+ return {
565
+ "status": "success",
566
+ "layer": "warm",
567
+ "item_id": item_id,
568
+ "message": "Warm context added successfully"
569
+ }
570
+ except Exception as e:
571
+ raise HTTPException(status_code=500, detail=f"Failed to add warm context: {str(e)}")
572
+
573
+
574
+ @app.post("/triad/cold")
575
+ async def add_cold_context(request: AddContextRequest):
576
+ """
577
+ Add cold context - the historical archive.
578
+
579
+ Cold context represents WHAT HAPPENED IN THE PAST:
580
+ - Old tickets from last year
581
+ - Closed PRs
582
+ - Historical design docs
583
+ - Legacy system documentation
584
+ - Archived meeting notes
585
+
586
+ Policy: "On Demand Only" - Cold context is NEVER automatically included.
587
+ It's only fetched when the user explicitly asks for history.
588
+
589
+ Args:
590
+ request: AddContextRequest with content, metadata, and priority
591
+
592
+ Returns:
593
+ Created item ID
594
+ """
595
+ try:
596
+ item_id = triad_manager.add_cold_context(
597
+ request.content,
598
+ request.metadata,
599
+ request.priority
600
+ )
601
+ return {
602
+ "status": "success",
603
+ "layer": "cold",
604
+ "item_id": item_id,
605
+ "message": "Cold context added successfully"
606
+ }
607
+ except Exception as e:
608
+ raise HTTPException(status_code=500, detail=f"Failed to add cold context: {str(e)}")
609
+
610
+
611
+ @app.post("/triad")
612
+ async def get_context_triad(request: ContextTriadRequest):
613
+ """
614
+ Get the complete tiered context.
615
+
616
+ The tiered context follows these policies:
617
+ 1. Hot Context: ALWAYS included (unless explicitly disabled)
618
+ - The Situation: what's happening right now
619
+ - Policy: "Attention Head" - overrides everything
620
+
621
+ 2. Warm Context: ALWAYS ON (unless explicitly disabled)
622
+ - The Persona: who you are
623
+ - Policy: "Filter" - colors how AI speaks to you
624
+
625
+ 3. Cold Context: ON DEMAND ONLY (requires explicit query)
626
+ - The Archive: what happened last year
627
+ - Policy: Never let cold data pollute the hot window
628
+
629
+ Args:
630
+ request: Tiered context request with layer flags and query
631
+
632
+ Returns:
633
+ Context from requested layers
634
+ """
635
+ try:
636
+ result = triad_manager.get_full_context(
637
+ include_hot=request.include_hot,
638
+ include_warm=request.include_warm,
639
+ include_cold=request.include_cold,
640
+ cold_query=request.query,
641
+ max_tokens_per_layer=request.max_tokens_per_layer,
642
+ include_metadata=True
643
+ )
644
+
645
+ response = ContextTriadResponse(
646
+ hot_context=result["hot_context"],
647
+ warm_context=result["warm_context"],
648
+ cold_context=result["cold_context"],
649
+ total_tokens=result["total_tokens"],
650
+ layers_included=result["layers_included"],
651
+ metadata=result["metadata"]
652
+ )
653
+
654
+ return response
655
+ except Exception as e:
656
+ raise HTTPException(status_code=500, detail=f"Failed to get tiered context: {str(e)}")
657
+
658
+
659
+ @app.get("/triad/state")
660
+ async def get_triad_state():
661
+ """
662
+ Get the current state of the tiered context.
663
+
664
+ Returns:
665
+ Current tiered context state with item counts
666
+ """
667
+ state = triad_manager.get_state()
668
+ return {
669
+ "hot_context_items": len(state.hot_context),
670
+ "warm_context_items": len(state.warm_context),
671
+ "cold_context_items": len(state.cold_context),
672
+ "total_items": len(state.hot_context) + len(state.warm_context) + len(state.cold_context)
673
+ }
674
+
675
+
676
+ @app.delete("/triad/hot")
677
+ async def clear_hot_context():
678
+ """Clear all hot context items."""
679
+ triad_manager.clear_hot_context()
680
+ return {"status": "success", "message": "Hot context cleared"}
681
+
682
+
683
+ @app.delete("/triad/warm")
684
+ async def clear_warm_context():
685
+ """Clear all warm context items."""
686
+ triad_manager.clear_warm_context()
687
+ return {"status": "success", "message": "Warm context cleared"}
688
+
689
+
690
+ @app.delete("/triad/cold")
691
+ async def clear_cold_context():
692
+ """Clear all cold context items."""
693
+ triad_manager.clear_cold_context()
694
+ return {"status": "success", "message": "Cold context cleared"}
695
+
696
+
697
+ @app.delete("/triad")
698
+ async def clear_all_context():
699
+ """Clear all context layers."""
700
+ triad_manager.clear_all()
701
+ return {"status": "success", "message": "All context cleared"}
702
+
703
+
704
+ # ===========================
705
+ # Conversation Manager Endpoints (Sliding Window / FIFO)
706
+ # ===========================
707
+
708
+ @app.post("/conversation/turn")
709
+ async def add_conversation_turn(request: AddTurnRequest):
710
+ """
711
+ Add a conversation turn to the history using Sliding Window (FIFO).
712
+
713
+ The Brutal Squeeze Philosophy:
714
+ Instead of asking an AI to summarize conversation history (which costs money
715
+ and loses nuance), we use a brutal "Sliding Window" approach:
716
+ - Keep the last 10 turns perfectly intact
717
+ - Delete turn 11 (FIFO - First In First Out)
718
+ - No summarization = No lossy compression
719
+
720
+ Why this works:
721
+ - Users rarely refer back to what they said 20 minutes ago
722
+ - They constantly refer to the exact code snippet they pasted 30 seconds ago
723
+ - Summary = Lossy Compression (loses specific error codes, exact wording)
724
+ - Chopping = Lossless Compression (of the recent past)
725
+
726
+ Example:
727
+ Turn 1: "I tried X and it failed with error code 500"
728
+ With Summarization: "User attempted troubleshooting" (ERROR CODE LOST!)
729
+ With Chopping: After 10 new turns, this is deleted entirely
730
+ But turns 2-11 are perfectly intact with all details
731
+
732
+ Args:
733
+ request: AddTurnRequest with user_message, ai_response, and metadata
734
+
735
+ Returns:
736
+ Created turn ID and current conversation statistics
737
+ """
738
+ try:
739
+ turn_id = conversation_manager.add_turn(
740
+ user_message=request.user_message,
741
+ ai_response=request.ai_response,
742
+ metadata=request.metadata
743
+ )
744
+
745
+ stats = conversation_manager.get_statistics()
746
+
747
+ return {
748
+ "status": "success",
749
+ "turn_id": turn_id,
750
+ "message": "Conversation turn added successfully",
751
+ "statistics": stats
752
+ }
753
+ except Exception as e:
754
+ raise HTTPException(status_code=500, detail=f"Failed to add turn: {str(e)}")
755
+
756
+
757
+ @app.get("/conversation")
758
+ async def get_conversation_history(
759
+ format_text: bool = True,
760
+ include_metadata: bool = False
761
+ ):
762
+ """
763
+ Get the conversation history (last N turns).
764
+
765
+ Returns the history in FIFO order (oldest to newest).
766
+ All turns are perfectly intact - no summarization, no loss.
767
+
768
+ The Sliding Window ensures:
769
+ 1. Recent precision: Last N turns are perfectly intact
770
+ 2. Zero summarization cost: No AI calls needed
771
+ 3. No information loss: What's kept is lossless
772
+ 4. Predictable behavior: Always know what's in context
773
+
774
+ Philosophy: In a frugal architecture, we value Recent Precision over Vague History.
775
+
776
+ Args:
777
+ format_text: If True, return formatted text; if False, return structured data
778
+ include_metadata: Whether to include metadata in text format
779
+
780
+ Returns:
781
+ Conversation history (formatted or structured)
782
+ """
783
+ try:
784
+ if format_text:
785
+ history_text = conversation_manager.get_conversation_history(
786
+ include_metadata=include_metadata,
787
+ format_as_text=True
788
+ )
789
+ return {"history": history_text}
790
+ else:
791
+ turns = conversation_manager.get_conversation_history(format_as_text=False)
792
+ stats = conversation_manager.get_statistics()
793
+
794
+ return ConversationHistoryResponse(
795
+ turns=turns,
796
+ total_turns=len(turns),
797
+ max_turns=conversation_manager.state.max_turns,
798
+ total_turns_ever=conversation_manager.state.total_turns_ever,
799
+ oldest_turn_timestamp=turns[0].timestamp if turns else None,
800
+ newest_turn_timestamp=turns[-1].timestamp if turns else None
801
+ )
802
+ except Exception as e:
803
+ raise HTTPException(status_code=500, detail=f"Failed to get history: {str(e)}")
804
+
805
+
806
+ @app.get("/conversation/stats")
807
+ async def get_conversation_statistics():
808
+ """
809
+ Get statistics about the conversation history.
810
+
811
+ Returns:
812
+ Statistics including current turns, deleted turns, and timestamps
813
+ """
814
+ try:
815
+ stats = conversation_manager.get_statistics()
816
+ return {
817
+ "status": "success",
818
+ "statistics": stats,
819
+ "sliding_window_info": {
820
+ "max_turns": conversation_manager.state.max_turns,
821
+ "policy": "FIFO (First In First Out)",
822
+ "philosophy": "Chopping > Summarizing",
823
+ "benefits": [
824
+ "Recent precision: Last N turns perfectly intact",
825
+ "Zero AI cost: No summarization needed",
826
+ "No information loss: Lossless compression of recent past",
827
+ "Predictable: Always know what's in context"
828
+ ]
829
+ }
830
+ }
831
+ except Exception as e:
832
+ raise HTTPException(status_code=500, detail=f"Failed to get stats: {str(e)}")
833
+
834
+
835
+ @app.get("/conversation/recent")
836
+ async def get_recent_turns(n: int = 5):
837
+ """
838
+ Get the N most recent conversation turns.
839
+
840
+ Args:
841
+ n: Number of recent turns to retrieve (default: 5)
842
+
843
+ Returns:
844
+ Recent conversation turns
845
+ """
846
+ try:
847
+ turns = conversation_manager.get_recent_turns(n=n)
848
+ return {
849
+ "status": "success",
850
+ "recent_turns": turns,
851
+ "count": len(turns),
852
+ "requested": n
853
+ }
854
+ except Exception as e:
855
+ raise HTTPException(status_code=500, detail=f"Failed to get recent turns: {str(e)}")
856
+
857
+
858
+ @app.patch("/conversation/turn/{turn_id}")
859
+ async def update_turn_response(turn_id: str, request: UpdateTurnRequest):
860
+ """
861
+ Update the AI response for a specific turn.
862
+
863
+ Useful when you add a turn with just the user message
864
+ and want to update it with the AI response later.
865
+
866
+ Args:
867
+ turn_id: The ID of the turn to update
868
+ request: UpdateTurnRequest with the AI response
869
+
870
+ Returns:
871
+ Update status
872
+ """
873
+ try:
874
+ success = conversation_manager.update_turn_response(turn_id, request.ai_response)
875
+ if success:
876
+ return {
877
+ "status": "success",
878
+ "turn_id": turn_id,
879
+ "message": "AI response updated successfully"
880
+ }
881
+ else:
882
+ raise HTTPException(status_code=404, detail="Turn not found")
883
+ except HTTPException:
884
+ raise
885
+ except Exception as e:
886
+ raise HTTPException(status_code=500, detail=f"Failed to update turn: {str(e)}")
887
+
888
+
889
+ @app.delete("/conversation")
890
+ async def clear_conversation():
891
+ """
892
+ Clear all conversation history.
893
+
894
+ Note: The total_turns_ever counter is preserved to track
895
+ how many turns have been processed across the lifetime.
896
+
897
+ Returns:
898
+ Deletion status
899
+ """
900
+ try:
901
+ conversation_manager.clear_conversation()
902
+ return {
903
+ "status": "success",
904
+ "message": "Conversation history cleared",
905
+ "total_turns_ever": conversation_manager.state.total_turns_ever
906
+ }
907
+ except Exception as e:
908
+ raise HTTPException(status_code=500, detail=f"Failed to clear conversation: {str(e)}")
909
+
910
+
911
+ # ===========================
912
+ # Trust Gateway Endpoints
913
+ # ===========================
914
+
915
+ @app.get("/gateway")
916
+ async def gateway_status():
917
+ """
918
+ Get Trust Gateway status and deployment information.
919
+
920
+ The Trust Gateway addresses the "Middleware Gap" by providing an
921
+ enterprise-grade, on-premises / private cloud router that CISOs can trust.
922
+
923
+ Philosophy:
924
+ No Enterprise CISO will send proprietary data to a random middleware startup.
925
+ The Trust Gateway can be deployed within your own infrastructure, ensuring:
926
+ - Data never leaves your environment
927
+ - Full audit trail for compliance
928
+ - Zero third-party data sharing
929
+ - Enterprise-grade security controls
930
+
931
+ Returns:
932
+ Trust Gateway deployment information and security status
933
+ """
934
+ try:
935
+ info = trust_gateway.get_deployment_info()
936
+ return {
937
+ "status": "operational",
938
+ "gateway_type": "Trust Gateway (Enterprise Private Cloud Router)",
939
+ "philosophy": "The winner won't be the one with the smartest routing; "
940
+ "it will be the one the Enterprise trusts with the keys to the kingdom.",
941
+ **info
942
+ }
943
+ except Exception as e:
944
+ raise HTTPException(status_code=500, detail=f"Failed to get gateway status: {str(e)}")
945
+
946
+
947
+ @app.post("/gateway/route")
948
+ async def gateway_route_request(
949
+ request: RouteRequest,
950
+ user_id: Optional[str] = None,
951
+ data_classification: Optional[str] = None
952
+ ):
953
+ """
954
+ Route a request through the Trust Gateway with enterprise security controls.
955
+
956
+ The Trust Gateway provides:
957
+ 1. On-Premises / Private Cloud deployment
958
+ 2. Zero data leakage (data never leaves your infrastructure)
959
+ 3. Full audit trail for compliance
960
+ 4. Configurable security policies
961
+ 5. Authentication and authorization
962
+ 6. Data classification and encryption
963
+
964
+ This endpoint validates the request against security policies, performs
965
+ heuristic routing, and logs all activity for compliance.
966
+
967
+ Example Use Case:
968
+ Enterprise CISO requirement: "We cannot send our proprietary financial data
969
+ to an external middleware service." Solution: Deploy Trust Gateway in your
970
+ own infrastructure. All routing decisions happen locally with zero external calls.
971
+
972
+ Args:
973
+ request: RouteRequest with the query to route
974
+ user_id: User ID making the request (for authentication)
975
+ data_classification: Classification level (public, internal, confidential, secret)
976
+
977
+ Returns:
978
+ Routing decision with security context and audit trail
979
+ """
980
+ try:
981
+ result = trust_gateway.route_request(
982
+ query=request.query,
983
+ user_id=user_id,
984
+ data_classification=data_classification
985
+ )
986
+ return result
987
+ except Exception as e:
988
+ raise HTTPException(status_code=500, detail=f"Gateway routing failed: {str(e)}")
989
+
990
+
991
+ @app.get("/gateway/info")
992
+ async def gateway_deployment_info():
993
+ """
994
+ Get detailed Trust Gateway deployment and security information.
995
+
996
+ Returns comprehensive information about:
997
+ - Deployment mode (on-prem, private cloud, hybrid, air-gapped)
998
+ - Security level and policies
999
+ - Data retention settings
1000
+ - Encryption status
1001
+ - Compliance mode
1002
+ - Trust guarantees
1003
+
1004
+ Returns:
1005
+ Detailed deployment and security configuration
1006
+ """
1007
+ try:
1008
+ info = trust_gateway.get_deployment_info()
1009
+ return {
1010
+ "gateway_info": info,
1011
+ "deployment_modes": {
1012
+ "on_prem": "Deployed on customer's own servers (maximum control)",
1013
+ "private_cloud": "Deployed in customer's private cloud (AWS VPC, Azure VNet, GCP VPC)",
1014
+ "hybrid": "Hybrid deployment with local processing and cloud backup",
1015
+ "air_gapped": "Completely isolated from internet (maximum security)"
1016
+ },
1017
+ "security_levels": {
1018
+ "standard": "Basic security controls",
1019
+ "high": "Enhanced security (encryption at rest and in transit)",
1020
+ "maximum": "Maximum security (air-gapped, zero data retention)"
1021
+ }
1022
+ }
1023
+ except Exception as e:
1024
+ raise HTTPException(status_code=500, detail=f"Failed to get deployment info: {str(e)}")
1025
+
1026
+
1027
+ @app.get("/gateway/audit")
1028
+ async def gateway_audit_logs(
1029
+ event_type: Optional[str] = None,
1030
+ user_id: Optional[str] = None,
1031
+ start_time: Optional[str] = None,
1032
+ end_time: Optional[str] = None
1033
+ ):
1034
+ """
1035
+ Retrieve Trust Gateway audit logs for compliance and security monitoring.
1036
+
1037
+ Audit logs include:
1038
+ - All routing decisions
1039
+ - Request validation events
1040
+ - Security policy changes
1041
+ - Data access events
1042
+ - User authentication attempts
1043
+
1044
+ This endpoint supports filtering by:
1045
+ - Event type (e.g., "request_routed", "policy_changed")
1046
+ - User ID
1047
+ - Time range (ISO format timestamps)
1048
+
1049
+ Example Use Cases:
1050
+ - Compliance audits (GDPR, HIPAA, SOC2)
1051
+ - Security incident investigation
1052
+ - User activity monitoring
1053
+ - Policy change tracking
1054
+
1055
+ Args:
1056
+ event_type: Filter by event type
1057
+ user_id: Filter by user ID
1058
+ start_time: Start of time range (ISO format)
1059
+ end_time: End of time range (ISO format)
1060
+
1061
+ Returns:
1062
+ Filtered audit log entries
1063
+ """
1064
+ try:
1065
+ logs = trust_gateway.get_audit_logs(
1066
+ event_type=event_type,
1067
+ user_id=user_id,
1068
+ start_time=start_time,
1069
+ end_time=end_time
1070
+ )
1071
+
1072
+ return {
1073
+ "status": "success",
1074
+ "total_logs": len(logs),
1075
+ "filters_applied": {
1076
+ "event_type": event_type,
1077
+ "user_id": user_id,
1078
+ "start_time": start_time,
1079
+ "end_time": end_time
1080
+ },
1081
+ "audit_logs": logs
1082
+ }
1083
+ except Exception as e:
1084
+ raise HTTPException(status_code=500, detail=f"Failed to retrieve audit logs: {str(e)}")
1085
+
1086
+
1087
+ @app.post("/gateway/validate")
1088
+ async def gateway_validate_request(
1089
+ query: str,
1090
+ user_id: Optional[str] = None,
1091
+ ip_address: Optional[str] = None,
1092
+ data_classification: Optional[str] = None
1093
+ ):
1094
+ """
1095
+ Validate a request against Trust Gateway security policies.
1096
+
1097
+ This endpoint checks:
1098
+ - Authentication requirements
1099
+ - User authorization (allowed users list)
1100
+ - IP address restrictions
1101
+ - Data classification requirements
1102
+ - Encryption requirements
1103
+
1104
+ Useful for pre-flight validation before sending actual requests.
1105
+
1106
+ Args:
1107
+ query: The query to validate
1108
+ user_id: User ID making the request
1109
+ ip_address: IP address of the requester
1110
+ data_classification: Data classification level
1111
+
1112
+ Returns:
1113
+ Validation result with status, warnings, and violations
1114
+ """
1115
+ try:
1116
+ validation = trust_gateway.validate_request(
1117
+ request_data={"query": query},
1118
+ user_id=user_id,
1119
+ ip_address=ip_address,
1120
+ data_classification=data_classification
1121
+ )
1122
+
1123
+ return {
1124
+ "status": "success" if validation["valid"] else "failed",
1125
+ "valid": validation["valid"],
1126
+ "warnings": validation["warnings"],
1127
+ "violations": validation["violations"],
1128
+ "timestamp": datetime.now(timezone.utc).isoformat()
1129
+ }
1130
+ except Exception as e:
1131
+ raise HTTPException(status_code=500, detail=f"Validation failed: {str(e)}")
1132
+
1133
+
1134
+ @app.delete("/gateway/audit")
1135
+ async def gateway_clear_audit_logs(user_id: Optional[str] = None):
1136
+ """
1137
+ Clear Trust Gateway audit logs.
1138
+
1139
+ Note: This operation itself is logged before clearing.
1140
+ Requires proper authorization in production environments.
1141
+
1142
+ Args:
1143
+ user_id: User ID requesting the clear operation
1144
+
1145
+ Returns:
1146
+ Clear operation status
1147
+ """
1148
+ try:
1149
+ result = trust_gateway.clear_audit_logs(user_id=user_id)
1150
+ return result
1151
+ except Exception as e:
1152
+ raise HTTPException(status_code=500, detail=f"Failed to clear audit logs: {str(e)}")
1153
+
1154
+
1155
+ # ============================================================================
1156
+ # Virtual File System Endpoints
1157
+ # ============================================================================
1158
+
1159
+ @app.post("/vfs/files", response_model=FileResponse, tags=["vfs"])
1160
+ async def create_vfs_file(request: CreateFileRequest):
1161
+ """
1162
+ Create a new file in the Virtual File System.
1163
+
1164
+ Allows SDLC agents to create files in shared project state.
1165
+ All agents can see files created by other agents.
1166
+ """
1167
+ try:
1168
+ file_node = vfs.create_file(
1169
+ path=request.path,
1170
+ content=request.content,
1171
+ agent_id=request.agent_id,
1172
+ metadata=request.metadata,
1173
+ )
1174
+
1175
+ return FileResponse(
1176
+ path=file_node.path,
1177
+ file_type=file_node.file_type,
1178
+ content=file_node.content,
1179
+ metadata=file_node.metadata,
1180
+ created_by=file_node.created_by,
1181
+ created_at=file_node.created_at,
1182
+ modified_by=file_node.modified_by,
1183
+ modified_at=file_node.modified_at,
1184
+ edit_count=len(file_node.edit_history),
1185
+ )
1186
+ except ValueError as e:
1187
+ raise HTTPException(status_code=400, detail=str(e))
1188
+
1189
+
1190
+ @app.get("/vfs/files", response_model=FileResponse, tags=["vfs"])
1191
+ async def read_vfs_file(path: str):
1192
+ """
1193
+ Read a file from the Virtual File System.
1194
+
1195
+ Agents can read files created or modified by other agents,
1196
+ ensuring shared visibility of project state.
1197
+ """
1198
+ try:
1199
+ content = vfs.read_file(path)
1200
+ info = vfs.get_file_info(path)
1201
+ return info
1202
+ except FileNotFoundError as e:
1203
+ raise HTTPException(status_code=404, detail=str(e))
1204
+ except ValueError as e:
1205
+ raise HTTPException(status_code=400, detail=str(e))
1206
+
1207
+
1208
+ @app.put("/vfs/files", response_model=FileResponse, tags=["vfs"])
1209
+ async def update_vfs_file(request: UpdateFileRequest):
1210
+ """
1211
+ Update an existing file in the Virtual File System.
1212
+
1213
+ Agents can update files and other agents will immediately see
1214
+ the changes. Edit history is maintained for auditability.
1215
+ """
1216
+ try:
1217
+ file_node = vfs.update_file(
1218
+ path=request.path,
1219
+ content=request.content,
1220
+ agent_id=request.agent_id,
1221
+ message=request.message,
1222
+ )
1223
+
1224
+ return FileResponse(
1225
+ path=file_node.path,
1226
+ file_type=file_node.file_type,
1227
+ content=file_node.content,
1228
+ metadata=file_node.metadata,
1229
+ created_by=file_node.created_by,
1230
+ created_at=file_node.created_at,
1231
+ modified_by=file_node.modified_by,
1232
+ modified_at=file_node.modified_at,
1233
+ edit_count=len(file_node.edit_history),
1234
+ )
1235
+ except FileNotFoundError as e:
1236
+ raise HTTPException(status_code=404, detail=str(e))
1237
+ except ValueError as e:
1238
+ raise HTTPException(status_code=400, detail=str(e))
1239
+
1240
+
1241
+ @app.delete("/vfs/files", tags=["vfs"])
1242
+ async def delete_vfs_file(path: str, agent_id: str):
1243
+ """
1244
+ Delete a file from the Virtual File System.
1245
+
1246
+ Removes a file from the shared project state.
1247
+ """
1248
+ try:
1249
+ vfs.delete_file(path, agent_id)
1250
+ return {"status": "deleted", "path": path}
1251
+ except FileNotFoundError as e:
1252
+ raise HTTPException(status_code=404, detail=str(e))
1253
+ except ValueError as e:
1254
+ raise HTTPException(status_code=400, detail=str(e))
1255
+
1256
+
1257
+ @app.get("/vfs/list", response_model=FileListResponse, tags=["vfs"])
1258
+ async def list_vfs_files(path: str = "/", recursive: bool = False):
1259
+ """
1260
+ List files in a directory within the Virtual File System.
1261
+
1262
+ Agents can browse the project structure to understand
1263
+ what files exist and have been created by other agents.
1264
+ """
1265
+ try:
1266
+ return vfs.list_files(path, recursive)
1267
+ except FileNotFoundError as e:
1268
+ raise HTTPException(status_code=404, detail=str(e))
1269
+ except ValueError as e:
1270
+ raise HTTPException(status_code=400, detail=str(e))
1271
+
1272
+
1273
+ @app.get("/vfs/history", tags=["vfs"])
1274
+ async def get_vfs_file_history(path: str):
1275
+ """
1276
+ Get the edit history of a file.
1277
+
1278
+ Shows all edits made to a file, including which agents
1279
+ made changes and when. Useful for understanding how a
1280
+ file evolved through multi-agent collaboration.
1281
+ """
1282
+ try:
1283
+ history = vfs.get_file_history(path)
1284
+ return {
1285
+ "path": path,
1286
+ "edit_count": len(history),
1287
+ "history": [
1288
+ {
1289
+ "agent_id": edit.agent_id,
1290
+ "timestamp": edit.timestamp,
1291
+ "message": edit.message,
1292
+ "content_preview": edit.content[:100] + "..." if len(edit.content) > 100 else edit.content,
1293
+ }
1294
+ for edit in history
1295
+ ]
1296
+ }
1297
+ except FileNotFoundError as e:
1298
+ raise HTTPException(status_code=404, detail=str(e))
1299
+
1300
+
1301
+ @app.get("/vfs/state", tags=["vfs"])
1302
+ async def get_vfs_state():
1303
+ """
1304
+ Get the complete Virtual File System state.
1305
+
1306
+ Returns the entire file system state, useful for debugging
1307
+ or snapshotting the current project state.
1308
+ """
1309
+ state = vfs.get_state()
1310
+ return {
1311
+ "root_path": state.root_path,
1312
+ "file_count": len(state.files),
1313
+ "files": [
1314
+ {
1315
+ "path": node.path,
1316
+ "type": node.file_type,
1317
+ "created_by": node.created_by,
1318
+ "modified_by": node.modified_by,
1319
+ "edit_count": len(node.edit_history),
1320
+ }
1321
+ for node in state.files.values()
1322
+ ]
1323
+ }
1324
+
1325
+
1326
+ if __name__ == "__main__":
1327
+ import uvicorn
1328
+ uvicorn.run(app, host="0.0.0.0", port=8000)