1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 domain::{
7 CodeImpactPathGroups, CodeImpactRequest, CodeIndexCheckpoint, CodeIndexSummary,
8 CodeIndexTaskRecord, CodeRepositoryRegistration, CodeRepositoryReport,
9 CodeRepositoryScopePreview, CodeRepositorySelector, CodeRepositoryStatus,
10 CodeRepositoryTotals, CodeRetrievalHit, CodeRetrievalRequest, CodeScopeRetentionSummary,
11 CommitReceipt, ConfidenceScore, EvidenceExtractionMetadata, EvidenceModality, EvidenceSpan,
12 ExtractionDiagnostic, FactStatus, FreshnessPolicy, FusionDiagnostics, GraphVersionRange,
13 IndexKind, IndexStatus, LayoutRegion, ProposalConflictRecord, ProposalRecord,
14 ProposalState, RerankDiagnostics, RetrievalBackendStatus, RetrievalBudgetUsed,
15 RetrievalHit, RetrievalMode, RetrievedContextPack, ServiceDefinitionPlan,
16 ServiceManagerAction, ServiceOperatorStatus, WorkerKind, WorkerStatus, WorkerTaskRecord,
17 },
18 storage::{GraphInspection, IndexCursor, IndexRefreshDiagnostics},
19};
20
21use super::{AgentProtocolStatus, ApiMetadata, RuntimeStatus};
22
23pub const GRAPH_CANVAS_DEFAULT_LIMIT: usize = 250;
24pub const GRAPH_CANVAS_MAX_LIMIT: usize = 1000;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "snake_case")]
29pub enum GraphCanvasKind {
30 Knowledge,
31 Code,
32 Mixed,
33}
34
35impl GraphCanvasKind {
36 pub fn parse(value: &str) -> Result<Self, String> {
38 match value {
39 "knowledge" => Ok(Self::Knowledge),
40 "code" => Ok(Self::Code),
41 "mixed" => Ok(Self::Mixed),
42 _ => Err(format!("unsupported graph canvas kind '{value}'")),
43 }
44 }
45
46 pub const fn as_str(self) -> &'static str {
48 match self {
49 Self::Knowledge => "knowledge",
50 Self::Code => "code",
51 Self::Mixed => "mixed",
52 }
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58pub struct GraphCanvasRequest {
59 pub kind: GraphCanvasKind,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub source_scope: Option<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub query: Option<String>,
64 pub limit: usize,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct GraphCanvasNode {
70 pub id: String,
71 pub kind: String,
72 pub label: String,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub subtitle: Option<String>,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub source_scope: Option<String>,
77 pub graph_version: u64,
78 pub weight: u32,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub status: Option<String>,
81 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
82 pub details: BTreeMap<String, String>,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87pub struct GraphCanvasEdge {
88 pub id: String,
89 pub kind: String,
90 pub source: String,
91 pub target: String,
92 pub label: String,
93 pub graph_version: u64,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub confidence_basis_points: Option<u16>,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub evidence_count: Option<usize>,
98 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
99 pub details: BTreeMap<String, String>,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct GraphCanvasSummary {
105 pub kind: GraphCanvasKind,
106 pub node_count: usize,
107 pub edge_count: usize,
108 pub truncated: bool,
109 pub available_kinds: Vec<String>,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114pub struct GraphCanvasResponse {
115 pub metadata: ApiMetadata,
116 pub nodes: Vec<GraphCanvasNode>,
117 pub edges: Vec<GraphCanvasEdge>,
118 pub summary: GraphCanvasSummary,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct IngestEvidence {
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub id: Option<String>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub source_path: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub span: Option<EvidenceSpan>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub confidence: Option<ConfidenceScore>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub status: Option<FactStatus>,
134 pub content: String,
135 #[serde(default)]
136 pub entity_labels: Vec<String>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub extraction: Option<IngestEvidenceExtraction>,
139}
140
141#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143pub struct IngestEvidenceExtraction {
144 pub modality: EvidenceModality,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub source_uri: Option<String>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub source_hash: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub media_hash: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub extractor: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub extractor_version: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub observed_at: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub parent_evidence_id: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub layout_region: Option<LayoutRegion>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub embedding_model: Option<String>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub embedding_dimension: Option<u16>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub diagnostic: Option<ExtractionDiagnostic>,
167}
168
169impl IngestEvidenceExtraction {
170 pub fn into_domain_metadata(self) -> EvidenceExtractionMetadata {
172 EvidenceExtractionMetadata {
173 modality: self.modality,
174 source_uri: self.source_uri,
175 source_hash: self.source_hash,
176 media_hash: self.media_hash,
177 extractor: self.extractor,
178 extractor_version: self.extractor_version,
179 observed_at: self.observed_at,
180 parent_evidence_id: self.parent_evidence_id,
181 layout_region: self.layout_region,
182 embedding_model: self.embedding_model,
183 embedding_dimension: self.embedding_dimension,
184 diagnostic: self
185 .diagnostic
186 .unwrap_or_else(|| EvidenceExtractionMetadata::text_span().diagnostic),
187 }
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct IngestRelation {
194 pub id: String,
195 pub source_entity_label: String,
196 pub relation_type: String,
197 pub target_entity_label: String,
198 #[serde(default)]
199 pub evidence_ids: Vec<String>,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub confidence: Option<ConfidenceScore>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub status: Option<FactStatus>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub version_range: Option<GraphVersionRange>,
206}
207
208#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
210pub struct IngestClaim {
211 pub id: String,
212 pub subject_entity_label: String,
213 pub predicate: String,
214 pub object: String,
215 #[serde(default)]
216 pub evidence_ids: Vec<String>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 pub confidence: Option<ConfidenceScore>,
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub status: Option<FactStatus>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub version_range: Option<GraphVersionRange>,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227pub struct IngestEvent {
228 pub id: String,
229 pub event_type: String,
230 #[serde(default)]
231 pub entity_labels: Vec<String>,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 pub occurred_at: Option<String>,
234 #[serde(default)]
235 pub evidence_ids: Vec<String>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub confidence: Option<ConfidenceScore>,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub status: Option<FactStatus>,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub version_range: Option<GraphVersionRange>,
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
246pub struct IngestRequest {
247 pub source_scope: String,
248 #[serde(default)]
249 pub evidence: Vec<IngestEvidence>,
250 #[serde(default)]
251 pub relations: Vec<IngestRelation>,
252 #[serde(default)]
253 pub claims: Vec<IngestClaim>,
254 #[serde(default)]
255 pub events: Vec<IngestEvent>,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
260pub struct IngestResponse {
261 pub metadata: ApiMetadata,
262 pub receipt: CommitReceipt,
263 pub indexes: Vec<IndexStatus>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub index_refresh_error: Option<String>,
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270pub struct MultimodalExtractionRequest {
271 pub source_scope: String,
272 pub parent_evidence_id: String,
273 pub derived_evidence: Vec<IngestEvidence>,
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
278pub struct MultimodalExtractionResponse {
279 pub metadata: ApiMetadata,
280 pub parent_evidence_id: String,
281 pub derived_evidence_count: usize,
282 pub receipt: CommitReceipt,
283 pub indexes: Vec<IndexStatus>,
284 #[serde(skip_serializing_if = "Option::is_none")]
285 pub index_refresh_error: Option<String>,
286}
287
288#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
290pub struct HybridRetrievalRequest {
291 pub query: String,
292 #[serde(skip_serializing_if = "Option::is_none")]
293 pub source_scope: Option<String>,
294 pub limit: usize,
295 pub freshness: FreshnessPolicy,
296}
297
298impl HybridRetrievalRequest {
299 pub fn new(query: impl Into<String>) -> Self {
301 Self {
302 query: query.into(),
303 source_scope: None,
304 limit: 10,
305 freshness: FreshnessPolicy::default(),
306 }
307 }
308}
309
310#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
312pub struct HybridRetrievalResponse {
313 pub metadata: ApiMetadata,
314 pub context_pack: RetrievedContextPack,
315 pub retrieval_mode: RetrievalMode,
316 #[serde(skip_serializing_if = "Option::is_none")]
317 pub source_scope: Option<String>,
318 pub freshness: FreshnessPolicy,
319 pub results: Vec<RetrievalHit>,
320 pub fusion: FusionDiagnostics,
321 pub rerank: RerankDiagnostics,
322 #[serde(default, skip_serializing_if = "Vec::is_empty")]
323 pub backend_statuses: Vec<RetrievalBackendStatus>,
324 pub truncated: bool,
325 pub budget_used: RetrievalBudgetUsed,
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub degraded_reason: Option<String>,
328 pub indexes: Vec<IndexStatus>,
329}
330
331#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
333pub struct GraphInspectionRequest {
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub source_scope: Option<String>,
336}
337
338#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
340pub struct GraphInspectionResponse {
341 pub metadata: ApiMetadata,
342 pub graph: GraphInspection,
343 pub repository_code_totals: CodeRepositoryTotals,
344}
345
346#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
348pub struct IndexRefreshRequest {
349 #[serde(default)]
350 pub kinds: Vec<IndexKind>,
351}
352
353#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
355pub struct IndexRefreshResponse {
356 pub metadata: ApiMetadata,
357 pub indexes: Vec<IndexStatus>,
358 pub index_cursors: Vec<IndexCursor>,
359 pub diagnostics: IndexRefreshDiagnostics,
360}
361
362#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
364pub struct FileIndexRequest {
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub source_scope: Option<String>,
367 #[serde(default)]
368 pub roots: Vec<String>,
369}
370
371#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
373pub struct FileIndexResponse {
374 pub metadata: ApiMetadata,
375 pub summary: crate::storage::FileIndexScanSummary,
376}
377
378#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
380pub struct FileQueryRequest {
381 pub query: String,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub source_scope: Option<String>,
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub root_id: Option<String>,
386 pub limit: usize,
387}
388
389#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
391pub struct FileQueryResponse {
392 pub metadata: ApiMetadata,
393 pub query: String,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub source_scope: Option<String>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub root_id: Option<String>,
398 pub results: Vec<crate::storage::FileSearchHit>,
399 pub truncated: bool,
400 pub duration_ms: u64,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 pub degraded_reason: Option<String>,
403}
404
405#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
407pub struct ServiceStatusResponse {
408 pub metadata: ApiMetadata,
409 pub service_name: String,
410 pub mode: String,
411 pub background_enabled: bool,
412 pub silent_updates_enabled: bool,
413 pub service_definition_path: String,
414 pub index_refresh: IndexRefreshDiagnostics,
415 pub file_index: crate::storage::FileIndexDiagnostics,
416 pub agent_protocols: AgentProtocolStatus,
417 pub operator: ServiceOperatorStatus,
418 pub workers: Vec<WorkerStatus>,
419 pub proposal_backlog: usize,
420 pub audit_sink: AuditSinkStatus,
421}
422
423#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
425pub struct AuditSinkStatus {
426 pub durable: bool,
427 pub event_count: usize,
428 #[serde(skip_serializing_if = "Option::is_none")]
429 pub last_error: Option<String>,
430}
431
432#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
434pub struct WorkerStatusRequest {
435 #[serde(skip_serializing_if = "Option::is_none")]
436 pub kind: Option<WorkerKind>,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
441pub struct WorkerStatusResponse {
442 pub metadata: ApiMetadata,
443 pub workers: Vec<WorkerStatus>,
444}
445
446#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
448pub struct WorkerRunRequest {
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub kind: Option<WorkerKind>,
451}
452
453#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
455pub struct WorkerRunResponse {
456 pub metadata: ApiMetadata,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub task: Option<WorkerTaskRecord>,
459 #[serde(default)]
460 pub proposals: Vec<ProposalRecord>,
461 pub workers: Vec<WorkerStatus>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub degraded_reason: Option<String>,
464}
465
466#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
468pub struct ProposalListApiRequest {
469 #[serde(skip_serializing_if = "Option::is_none")]
470 pub state: Option<ProposalState>,
471 pub limit: usize,
472}
473
474#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
476pub struct ProposalListResponse {
477 pub metadata: ApiMetadata,
478 pub proposals: Vec<ProposalRecord>,
479}
480
481#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
483pub struct ProposalShowResponse {
484 pub metadata: ApiMetadata,
485 pub proposal: ProposalRecord,
486 pub conflicts: Vec<ProposalConflictRecord>,
487 pub payload: serde_json::Value,
488}
489
490#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
492pub struct ProposalDecisionApiRequest {
493 pub actor: String,
494 #[serde(skip_serializing_if = "Option::is_none")]
495 pub reason: Option<String>,
496}
497
498#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
500pub struct ProposalDecisionResponse {
501 pub metadata: ApiMetadata,
502 pub proposal: ProposalRecord,
503 #[serde(skip_serializing_if = "Option::is_none")]
504 pub receipt: Option<CommitReceipt>,
505 #[serde(skip_serializing_if = "Option::is_none")]
506 pub index_refresh_error: Option<String>,
507}
508
509#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
511pub struct AuditQueryApiRequest {
512 #[serde(skip_serializing_if = "Option::is_none")]
513 pub operation: Option<String>,
514 pub limit: usize,
515}
516
517#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
519pub struct AuditQueryResponse {
520 pub metadata: ApiMetadata,
521 pub events: Vec<crate::domain::AuditEventRecord>,
522}
523
524#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
526pub struct ServicePlanRequest {
527 pub action: ServiceManagerAction,
528}
529
530#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
532pub struct ServicePlanResponse {
533 pub metadata: ApiMetadata,
534 pub plan: ServiceDefinitionPlan,
535}
536
537#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
539pub struct ServiceDefinitionWriteResponse {
540 pub metadata: ApiMetadata,
541 pub plan: ServiceDefinitionPlan,
542 pub written: bool,
543}
544
545#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
547pub struct ServiceOperatorResponse {
548 pub metadata: ApiMetadata,
549 pub operator: ServiceOperatorStatus,
550}
551
552#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
554pub struct ServiceRecoveryReport {
555 pub metadata: ApiMetadata,
556 pub graph_version: u64,
557 pub stale_index_kinds: Vec<IndexKind>,
558 pub refreshed_index_kinds: Vec<IndexKind>,
559 pub index_lag_max: u64,
560 pub task_queue_depth: usize,
561 pub dead_letter_count: usize,
562 pub heartbeat_state: String,
563}
564
565#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
567pub struct HealthResponse {
568 pub metadata: ApiMetadata,
569 pub healthy: bool,
570 pub graph: GraphInspection,
571 pub repository_code_totals: CodeRepositoryTotals,
572 pub indexes: Vec<IndexStatus>,
573 pub index_cursors: Vec<IndexCursor>,
574 pub index_refresh: IndexRefreshDiagnostics,
575 pub file_index: crate::storage::FileIndexDiagnostics,
576 pub runtime: RuntimeStatus,
577}
578
579#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
581pub struct EmbeddingProviderProbeResponse {
582 pub metadata: ApiMetadata,
583 pub ok: bool,
584 pub provider: Option<String>,
585 pub model: String,
586 pub dimension: u32,
587 #[serde(skip_serializing_if = "Option::is_none")]
588 pub latency_ms: Option<u64>,
589 #[serde(skip_serializing_if = "Option::is_none")]
590 pub error_code: Option<String>,
591 #[serde(skip_serializing_if = "Option::is_none")]
592 pub error_message: Option<String>,
593 #[serde(skip_serializing_if = "Option::is_none")]
594 pub retryable: Option<bool>,
595}
596
597#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
599pub struct CodeRepositoryRegisterRequest {
600 pub root_path: String,
601 pub alias: String,
602 #[serde(default)]
603 pub path_filters: Vec<String>,
604 #[serde(default)]
605 pub language_filters: Vec<String>,
606}
607
608#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
610pub struct CodeRepositoryRegisterResponse {
611 pub metadata: ApiMetadata,
612 pub registration: CodeRepositoryRegistration,
613 pub status: CodeRepositoryStatus,
614}
615
616#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
618pub struct CodeRepositoryIndexResponse {
619 pub metadata: ApiMetadata,
620 pub scope: CodeRepositoryScopeMetadata,
621 pub summary: CodeIndexSummary,
622 pub status: CodeRepositoryStatus,
623}
624
625#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
627pub struct CodeRepositoryIndexStartResponse {
628 pub metadata: ApiMetadata,
629 pub scope: CodeRepositoryScopeMetadata,
630 #[serde(skip_serializing_if = "Option::is_none")]
631 pub summary: Option<CodeIndexSummary>,
632 pub status: CodeRepositoryStatus,
633 #[serde(skip_serializing_if = "Option::is_none")]
634 pub task: Option<CodeIndexTaskRecord>,
635 #[serde(skip_serializing_if = "Option::is_none")]
636 pub checkpoint: Option<CodeIndexCheckpoint>,
637}
638
639#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
641pub struct CodeRepositoryScopePreviewResponse {
642 pub metadata: ApiMetadata,
643 pub scope: CodeRepositoryScopeMetadata,
644 pub preview: CodeRepositoryScopePreview,
645}
646
647#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
649pub struct CodeRepositoryScopeMetadata {
650 pub scope_id: String,
651 pub repository_id: String,
652 pub alias: String,
653 pub requested_ref: String,
654 pub resolved_commit_sha: String,
655 pub tree_hash: String,
656 pub path_filters: Vec<String>,
657 pub language_filters: Vec<String>,
658 pub index_versions: Vec<String>,
659 pub stale: bool,
660}
661
662impl CodeRepositoryScopeMetadata {
663 pub fn from_status(
665 status: &CodeRepositoryStatus,
666 selector: &CodeRepositorySelector,
667 requested_ref: impl Into<String>,
668 ) -> Self {
669 Self {
670 scope_id: status.last_indexed_scope_id.clone().unwrap_or_default(),
671 repository_id: status.repository_id.clone(),
672 alias: status.alias.clone(),
673 requested_ref: requested_ref.into(),
674 resolved_commit_sha: status.last_indexed_commit.clone().unwrap_or_default(),
675 tree_hash: status.tree_hash.clone().unwrap_or_default(),
676 path_filters: merged_filters(&status.path_filters, &selector.path_filters),
677 language_filters: merged_filters(&status.language_filters, &selector.language_filters),
678 index_versions: vec![format!(
679 "code:{}:{}",
680 status
681 .last_indexed_scope_id
682 .as_deref()
683 .unwrap_or("unscoped"),
684 status.tree_hash.as_deref().unwrap_or("unindexed")
685 )],
686 stale: status.stale,
687 }
688 }
689
690 pub fn from_index_task(task: &CodeIndexTaskRecord, requested_ref: impl Into<String>) -> Self {
692 Self {
693 scope_id: task.source_scope.clone(),
694 repository_id: task.repository_id.clone(),
695 alias: task.alias.clone(),
696 requested_ref: requested_ref.into(),
697 resolved_commit_sha: task.resolved_commit_sha.clone(),
698 tree_hash: task.tree_hash.clone(),
699 path_filters: task.path_filters.clone(),
700 language_filters: task.language_filters.clone(),
701 index_versions: vec![format!("code:{}:{}", task.source_scope, task.tree_hash)],
702 stale: true,
703 }
704 }
705}
706
707#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
709pub struct CodeRepositoryQueryResponse {
710 pub metadata: ApiMetadata,
711 pub scope: CodeRepositoryScopeMetadata,
712 pub request: CodeRetrievalRequest,
713 pub results: Vec<CodeRetrievalHit>,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 pub degraded_reason: Option<String>,
716}
717
718#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
720pub struct CodeRepositoryImpactResponse {
721 pub metadata: ApiMetadata,
722 pub scope: CodeRepositoryScopeMetadata,
723 pub request: CodeImpactRequest,
724 pub path_groups: CodeImpactPathGroups,
725 pub results: Vec<CodeRetrievalHit>,
726}
727
728#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
730pub struct CodeRepositoryStatusResponse {
731 pub metadata: ApiMetadata,
732 pub status: CodeRepositoryStatus,
733 #[serde(skip_serializing_if = "Option::is_none")]
734 pub active_task: Option<CodeIndexTaskRecord>,
735 #[serde(skip_serializing_if = "Option::is_none")]
736 pub checkpoint: Option<CodeIndexCheckpoint>,
737 pub retention: CodeScopeRetentionSummary,
738}
739
740#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
742pub struct CodeRepositoryReportResponse {
743 pub metadata: ApiMetadata,
744 pub scope: CodeRepositoryScopeMetadata,
745 pub report: CodeRepositoryReport,
746}
747
748fn merged_filters(base: &[String], request: &[String]) -> Vec<String> {
749 let mut merged = Vec::new();
750 for value in base.iter().chain(request.iter()) {
751 if !merged.contains(value) {
752 merged.push(value.clone());
753 }
754 }
755
756 merged
757}