1#[allow(unused_imports)]
4use super::{CommandRequest, WorkspaceError, WorkspaceVersionConflict};
5use super::{
6 LocalWorkspaceBackend, ManifestWorkspaceBackend, VirtualPathResolver, WorkspaceCapabilities,
7 WorkspaceChunkCatalog, WorkspaceCommandRunner, WorkspaceFileSystem, WorkspaceFileSystemExt,
8 WorkspaceGit, WorkspaceGitStashProvider, WorkspaceGitWorktreeProvider, WorkspacePath,
9 WorkspacePathResolver, WorkspaceRef, WorkspaceResult, WorkspaceRetrievalRuntime,
10 WorkspaceSearch, WorkspaceTextReader, WorkspaceWriteOutcome,
11};
12use crate::code_intelligence::{LocalCodeIntelligence, WorkspaceCodeIntelligence};
13use anyhow::{anyhow, Result};
14use std::path::{Path, PathBuf};
15use std::sync::Arc;
16
17pub struct WorkspaceServices {
19 workspace_ref: WorkspaceRef,
20 capabilities: WorkspaceCapabilities,
21 path_resolver: Arc<dyn WorkspacePathResolver>,
22 file_system: Arc<dyn WorkspaceFileSystem>,
23 file_system_ext: Option<Arc<dyn WorkspaceFileSystemExt>>,
24 text_reader: Option<Arc<dyn WorkspaceTextReader>>,
25 command_runner: Option<Arc<dyn WorkspaceCommandRunner>>,
26 search: Option<Arc<dyn WorkspaceSearch>>,
27 code_intelligence: Option<Arc<dyn WorkspaceCodeIntelligence>>,
28 chunk_catalog: Option<Arc<WorkspaceChunkCatalog>>,
29 workspace_retrieval: Option<Arc<WorkspaceRetrievalRuntime>>,
30 git: Option<Arc<dyn WorkspaceGit>>,
31 git_stash: Option<Arc<dyn WorkspaceGitStashProvider>>,
32 git_worktree: Option<Arc<dyn WorkspaceGitWorktreeProvider>>,
33 operation_timeout: Option<std::time::Duration>,
37 local_root: Option<PathBuf>,
38}
39
40impl std::fmt::Debug for WorkspaceServices {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.debug_struct("WorkspaceServices")
43 .field("workspace_ref", &self.workspace_ref)
44 .field("capabilities", &self.capabilities)
45 .field("file_system_ext", &self.file_system_ext.is_some())
46 .field("text_reader", &self.text_reader.is_some())
47 .field("command_runner", &self.command_runner.is_some())
48 .field("search", &self.search.is_some())
49 .field("code_intelligence", &self.code_intelligence.is_some())
50 .field("chunk_catalog", &self.chunk_catalog.is_some())
51 .field("workspace_retrieval", &self.workspace_retrieval.is_some())
52 .field("git", &self.git.is_some())
53 .field("git_stash", &self.git_stash.is_some())
54 .field("git_worktree", &self.git_worktree.is_some())
55 .field("local_root", &self.local_root)
56 .finish()
57 }
58}
59
60impl WorkspaceServices {
61 pub(crate) fn new_with_git(
62 workspace_ref: WorkspaceRef,
63 mut capabilities: WorkspaceCapabilities,
64 path_resolver: Arc<dyn WorkspacePathResolver>,
65 file_system: Arc<dyn WorkspaceFileSystem>,
66 command_runner: Option<Arc<dyn WorkspaceCommandRunner>>,
67 search: Option<Arc<dyn WorkspaceSearch>>,
68 git: Option<Arc<dyn WorkspaceGit>>,
69 ) -> Self {
70 if command_runner.is_none() {
71 capabilities.exec = false;
72 }
73 if search.is_none() {
74 capabilities.search = false;
75 }
76 if git.is_none() {
77 capabilities.git = false;
78 }
79 capabilities.code_intelligence = false;
80 Self {
81 workspace_ref,
82 capabilities,
83 path_resolver,
84 file_system,
85 file_system_ext: None,
86 text_reader: None,
87 command_runner,
88 search,
89 code_intelligence: None,
90 chunk_catalog: None,
91 workspace_retrieval: None,
92 git,
93 git_stash: None,
94 git_worktree: None,
95 operation_timeout: None,
96 local_root: None,
97 }
98 }
99
100 pub fn builder(
101 workspace_ref: WorkspaceRef,
102 file_system: Arc<dyn WorkspaceFileSystem>,
103 ) -> WorkspaceServicesBuilder {
104 WorkspaceServicesBuilder::new(workspace_ref, file_system)
105 }
106
107 pub fn local(root: impl Into<PathBuf>) -> Arc<Self> {
108 let backend = Arc::new(LocalWorkspaceBackend::new(root.into()));
109 let workspace_ref = WorkspaceRef::new(
110 backend.root.display().to_string(),
111 backend.root.display().to_string(),
112 );
113 let path_resolver: Arc<dyn WorkspacePathResolver> = backend.clone();
114 let file_system: Arc<dyn WorkspaceFileSystem> = backend.clone();
115 let text_reader: Arc<dyn WorkspaceTextReader> = backend.clone();
116 let command_runner: Arc<dyn WorkspaceCommandRunner> = backend.clone();
117 let search: Arc<dyn WorkspaceSearch> = backend.clone();
118 let git: Arc<dyn WorkspaceGit> = backend.clone();
119 let git_stash: Arc<dyn WorkspaceGitStashProvider> = backend.clone();
120 let git_worktree: Arc<dyn WorkspaceGitWorktreeProvider> = backend.clone();
121 Arc::new(Self {
122 workspace_ref,
123 capabilities: WorkspaceCapabilities::local_default(),
124 path_resolver,
125 file_system,
126 file_system_ext: None,
127 text_reader: Some(text_reader),
128 command_runner: Some(command_runner),
129 search: Some(search),
130 code_intelligence: None,
131 chunk_catalog: None,
132 workspace_retrieval: None,
133 git: Some(git),
134 git_stash: Some(git_stash),
135 git_worktree: Some(git_worktree),
136 operation_timeout: None,
137 local_root: Some(backend.root.clone()),
138 })
139 }
140
141 pub fn local_with_manifest(root: impl Into<PathBuf>) -> Arc<Self> {
146 let backend = ManifestWorkspaceBackend::new(root);
147 Self::local_with_manifest_backend(backend)
148 }
149
150 pub fn local_with_retrieval(root: impl Into<PathBuf>) -> Arc<Self> {
152 let backend = ManifestWorkspaceBackend::new(root);
153 Self::local_with_retrieval_backend(backend)
154 }
155
156 pub async fn local_with_code_intelligence(
161 root: impl Into<PathBuf>,
162 isolation_scope: impl Into<String>,
163 ) -> Result<Arc<Self>> {
164 let backend = ManifestWorkspaceBackend::new(root);
165 Self::local_with_code_intelligence_backend(backend, isolation_scope).await
166 }
167
168 pub async fn local_with_retrieval_and_code_intelligence(
171 root: impl Into<PathBuf>,
172 isolation_scope: impl Into<String>,
173 ) -> Result<Arc<Self>> {
174 let backend = ManifestWorkspaceBackend::new(root);
175 Self::local_with_retrieval_and_code_intelligence_backend(backend, isolation_scope).await
176 }
177
178 pub async fn local_with_code_intelligence_backend(
180 backend: Arc<ManifestWorkspaceBackend>,
181 isolation_scope: impl Into<String>,
182 ) -> Result<Arc<Self>> {
183 let manifest = backend.manifest();
184 let file_system: Arc<dyn WorkspaceFileSystem> = backend.clone();
185 let services = Self::local_with_manifest_backend(backend);
186 let provider = LocalCodeIntelligence::start(isolation_scope, manifest, file_system)
187 .await
188 .map_err(|error| anyhow!("failed to start Code Intelligence: {error}"))?;
189 Ok(services.with_code_intelligence(provider))
190 }
191
192 pub async fn local_with_retrieval_and_code_intelligence_backend(
194 backend: Arc<ManifestWorkspaceBackend>,
195 isolation_scope: impl Into<String>,
196 ) -> Result<Arc<Self>> {
197 let manifest = backend.manifest();
198 let file_system: Arc<dyn WorkspaceFileSystem> = backend.clone();
199 let services = Self::local_with_retrieval_backend(backend);
200 let provider = LocalCodeIntelligence::start(isolation_scope, manifest, file_system)
201 .await
202 .map_err(|error| anyhow!("failed to start Code Intelligence: {error}"))?;
203 Ok(services.with_code_intelligence(provider))
204 }
205
206 pub fn local_with_manifest_backend(backend: Arc<ManifestWorkspaceBackend>) -> Arc<Self> {
209 Self::local_with_manifest_backend_and_catalog(backend, None)
210 }
211
212 pub fn local_with_retrieval_backend(backend: Arc<ManifestWorkspaceBackend>) -> Arc<Self> {
214 let catalog = backend.chunk_catalog();
215 Self::local_with_manifest_backend_and_catalog(backend, Some(catalog))
216 }
217
218 fn local_with_manifest_backend_and_catalog(
219 backend: Arc<ManifestWorkspaceBackend>,
220 chunk_catalog: Option<Arc<WorkspaceChunkCatalog>>,
221 ) -> Arc<Self> {
222 let workspace_ref = WorkspaceRef::new(
223 backend.local_root().display().to_string(),
224 backend.local_root().display().to_string(),
225 );
226 let path_resolver: Arc<dyn WorkspacePathResolver> = backend.clone();
227 let file_system: Arc<dyn WorkspaceFileSystem> = backend.clone();
228 let text_reader: Arc<dyn WorkspaceTextReader> = backend.clone();
229 let command_runner: Arc<dyn WorkspaceCommandRunner> = backend.clone();
230 let search: Arc<dyn WorkspaceSearch> = backend.clone();
231 let git: Arc<dyn WorkspaceGit> = backend.clone();
232 let git_stash: Arc<dyn WorkspaceGitStashProvider> = backend.clone();
233 let git_worktree: Arc<dyn WorkspaceGitWorktreeProvider> = backend.clone();
234 Arc::new(Self {
235 workspace_ref,
236 capabilities: WorkspaceCapabilities::local_default(),
237 path_resolver,
238 file_system,
239 file_system_ext: None,
240 text_reader: Some(text_reader),
241 command_runner: Some(command_runner),
242 search: Some(search),
243 code_intelligence: None,
244 chunk_catalog,
245 workspace_retrieval: None,
246 git: Some(git),
247 git_stash: Some(git_stash),
248 git_worktree: Some(git_worktree),
249 operation_timeout: None,
250 local_root: Some(backend.local_root().to_path_buf()),
251 })
252 }
253
254 pub fn workspace_ref(&self) -> &WorkspaceRef {
255 &self.workspace_ref
256 }
257
258 pub fn capabilities(&self) -> WorkspaceCapabilities {
259 self.capabilities
260 }
261
262 pub fn normalize_path(&self, input: &str) -> Result<WorkspacePath> {
263 self.path_resolver.normalize(input)
264 }
265
266 pub fn fs(&self) -> Arc<dyn WorkspaceFileSystem> {
267 Arc::clone(&self.file_system)
268 }
269
270 pub fn fs_ext(&self) -> Option<Arc<dyn WorkspaceFileSystemExt>> {
277 self.file_system_ext.clone()
278 }
279
280 pub fn text_reader(&self) -> Option<Arc<dyn WorkspaceTextReader>> {
281 self.text_reader.clone()
282 }
283
284 pub fn command_runner(&self) -> Option<Arc<dyn WorkspaceCommandRunner>> {
285 self.command_runner.clone()
286 }
287
288 pub fn search(&self) -> Option<Arc<dyn WorkspaceSearch>> {
289 self.search.clone()
290 }
291
292 pub fn code_intelligence(&self) -> Option<Arc<dyn WorkspaceCodeIntelligence>> {
294 self.code_intelligence.clone()
295 }
296
297 pub fn chunk_catalog(&self) -> Option<Arc<WorkspaceChunkCatalog>> {
299 self.chunk_catalog.clone()
300 }
301
302 pub fn workspace_retrieval(&self) -> Option<Arc<WorkspaceRetrievalRuntime>> {
304 self.workspace_retrieval.clone()
305 }
306
307 pub async fn semantic_search(
310 &self,
311 mut request: super::WorkspaceSemanticSearchRequest,
312 cancellation: tokio_util::sync::CancellationToken,
313 ) -> super::WorkspaceRetrievalResult<super::WorkspaceSemanticSearchResult> {
314 if !self.capabilities.read {
315 return Err(super::WorkspaceRetrievalError::Unavailable);
316 }
317 if let Some(path) = request.path.take() {
318 request.path = Some(
319 self.path_resolver
320 .normalize(&path)
321 .map_err(|_| {
322 super::WorkspaceRetrievalError::InvalidQuery(
323 "path was rejected by the workspace resolver".to_owned(),
324 )
325 })?
326 .as_str()
327 .to_owned(),
328 );
329 }
330 let runtime = self
331 .workspace_retrieval
332 .as_ref()
333 .ok_or(super::WorkspaceRetrievalError::Unavailable)?;
334 runtime
335 .search(
336 request,
337 Arc::clone(&self.file_system),
338 self.operation_timeout,
339 cancellation,
340 )
341 .await
342 }
343
344 pub async fn hybrid_search(
347 &self,
348 mut request: super::WorkspaceHybridSearchRequest,
349 cancellation: tokio_util::sync::CancellationToken,
350 ) -> super::WorkspaceRetrievalResult<super::WorkspaceHybridSearchResult> {
351 if !self.capabilities.read {
352 return Err(super::WorkspaceRetrievalError::Unavailable);
353 }
354 if let Some(path) = request.path.take() {
355 request.path = Some(
356 self.path_resolver
357 .normalize(&path)
358 .map_err(|_| {
359 super::WorkspaceRetrievalError::InvalidQuery(
360 "path was rejected by the workspace resolver".to_owned(),
361 )
362 })?
363 .as_str()
364 .to_owned(),
365 );
366 }
367 let runtime = self
368 .workspace_retrieval
369 .as_ref()
370 .ok_or(super::WorkspaceRetrievalError::Unavailable)?;
371 runtime
372 .hybrid_search(
373 request,
374 Arc::clone(&self.file_system),
375 self.code_intelligence.clone(),
376 self.operation_timeout,
377 cancellation,
378 )
379 .await
380 }
381
382 pub(crate) fn with_workspace_retrieval(
383 &self,
384 runtime: Arc<WorkspaceRetrievalRuntime>,
385 ) -> Option<Arc<Self>> {
386 if self.workspace_retrieval.is_some() {
387 return None;
388 }
389 Some(Arc::new(Self {
390 workspace_ref: self.workspace_ref.clone(),
391 capabilities: self.capabilities,
392 path_resolver: Arc::clone(&self.path_resolver),
393 file_system: Arc::clone(&self.file_system),
394 file_system_ext: self.file_system_ext.clone(),
395 text_reader: self.text_reader.clone(),
396 command_runner: self.command_runner.clone(),
397 search: self.search.clone(),
398 code_intelligence: self.code_intelligence.clone(),
399 chunk_catalog: self.chunk_catalog.clone(),
400 workspace_retrieval: Some(runtime),
401 git: self.git.clone(),
402 git_stash: self.git_stash.clone(),
403 git_worktree: self.git_worktree.clone(),
404 operation_timeout: self.operation_timeout,
405 local_root: self.local_root.clone(),
406 }))
407 }
408
409 pub fn with_code_intelligence(
412 &self,
413 provider: Arc<dyn WorkspaceCodeIntelligence>,
414 ) -> Arc<Self> {
415 let mut capabilities = self.capabilities;
416 capabilities.code_intelligence = true;
417 Arc::new(Self {
418 workspace_ref: self.workspace_ref.clone(),
419 capabilities,
420 path_resolver: Arc::clone(&self.path_resolver),
421 file_system: Arc::clone(&self.file_system),
422 file_system_ext: self.file_system_ext.clone(),
423 text_reader: self.text_reader.clone(),
424 command_runner: self.command_runner.clone(),
425 search: self.search.clone(),
426 code_intelligence: Some(provider),
427 chunk_catalog: self.chunk_catalog.clone(),
428 workspace_retrieval: self.workspace_retrieval.clone(),
429 git: self.git.clone(),
430 git_stash: self.git_stash.clone(),
431 git_worktree: self.git_worktree.clone(),
432 operation_timeout: self.operation_timeout,
433 local_root: self.local_root.clone(),
434 })
435 }
436
437 pub fn git(&self) -> Option<Arc<dyn WorkspaceGit>> {
438 self.git.clone()
439 }
440
441 pub fn git_stash(&self) -> Option<Arc<dyn WorkspaceGitStashProvider>> {
442 self.git_stash.clone()
443 }
444
445 pub fn git_worktree(&self) -> Option<Arc<dyn WorkspaceGitWorktreeProvider>> {
446 self.git_worktree.clone()
447 }
448
449 pub(crate) fn with_git_provider(
466 &self,
467 git: Arc<dyn WorkspaceGit>,
468 git_stash: Option<Arc<dyn WorkspaceGitStashProvider>>,
469 ) -> Arc<Self> {
470 let mut capabilities = self.capabilities;
471 capabilities.git = true;
472 Arc::new(Self {
473 workspace_ref: self.workspace_ref.clone(),
474 capabilities,
475 path_resolver: Arc::clone(&self.path_resolver),
476 file_system: Arc::clone(&self.file_system),
477 file_system_ext: self.file_system_ext.clone(),
478 text_reader: self.text_reader.clone(),
479 command_runner: self.command_runner.clone(),
480 search: self.search.clone(),
481 code_intelligence: self.code_intelligence.clone(),
482 chunk_catalog: self.chunk_catalog.clone(),
483 workspace_retrieval: self.workspace_retrieval.clone(),
484 git: Some(git),
485 git_stash,
486 git_worktree: None,
487 operation_timeout: self.operation_timeout,
488 local_root: self.local_root.clone(),
489 })
490 }
491
492 pub fn operation_timeout(&self) -> Option<std::time::Duration> {
498 self.operation_timeout
499 }
500
501 pub async fn run_with_timeout<F, T, E>(
515 &self,
516 op: &'static str,
517 fut: F,
518 ) -> std::result::Result<T, E>
519 where
520 F: std::future::Future<Output = std::result::Result<T, E>>,
521 E: From<anyhow::Error>,
522 {
523 match self.operation_timeout {
524 Some(d) => tokio::time::timeout(d, fut).await.map_err(|_| {
525 E::from(anyhow!(
526 "workspace operation '{}' timed out after {:?}",
527 op,
528 d
529 ))
530 })?,
531 None => fut.await,
532 }
533 }
534
535 pub async fn read_for_edit(
542 &self,
543 path: &WorkspacePath,
544 ) -> WorkspaceResult<(String, Option<String>)> {
545 if let Some(ext) = self.fs_ext() {
546 let path = path.clone();
547 return self
548 .run_with_timeout("read_text_with_version", async move {
549 let (content, version) = ext.read_text_with_version(&path).await?;
550 Ok((content, Some(version)))
551 })
552 .await;
553 }
554 let fs = self.fs();
555 let path_owned = path.clone();
556 let content = self
557 .run_with_timeout("read_text", async move { fs.read_text(&path_owned).await })
558 .await?;
559 Ok((content, None))
560 }
561
562 pub async fn write_for_edit(
570 &self,
571 path: &WorkspacePath,
572 content: &str,
573 expected_version: Option<&str>,
574 ) -> WorkspaceResult<WorkspaceWriteOutcome> {
575 if let (Some(ext), Some(version)) = (self.fs_ext(), expected_version) {
576 let path = path.clone();
577 let content = content.to_string();
578 let expected = version.to_string();
579 return self
580 .run_with_timeout("write_text_if_version", async move {
581 ext.write_text_if_version(&path, &content, &expected).await
582 })
583 .await;
584 }
585 let fs = self.fs();
586 let path = path.clone();
587 let content = content.to_string();
588 self.run_with_timeout(
589 "write_text",
590 async move { fs.write_text(&path, &content).await },
591 )
592 .await
593 }
594
595 pub fn local_root(&self) -> Option<&Path> {
596 self.local_root.as_deref()
597 }
598
599 pub fn display_path(&self, path: &WorkspacePath) -> String {
600 if path.is_root() {
601 return self.workspace_ref.display_root.clone();
602 }
603
604 let root = self.workspace_ref.display_root.trim_end_matches('/');
605 if root.is_empty() {
606 path.as_str().to_string()
607 } else {
608 format!("{root}/{}", path.as_str())
609 }
610 }
611}
612
613pub struct WorkspaceServicesBuilder {
615 workspace_ref: WorkspaceRef,
616 capabilities: WorkspaceCapabilities,
617 path_resolver: Arc<dyn WorkspacePathResolver>,
618 file_system: Arc<dyn WorkspaceFileSystem>,
619 file_system_ext: Option<Arc<dyn WorkspaceFileSystemExt>>,
620 text_reader: Option<Arc<dyn WorkspaceTextReader>>,
621 command_runner: Option<Arc<dyn WorkspaceCommandRunner>>,
622 search: Option<Arc<dyn WorkspaceSearch>>,
623 code_intelligence: Option<Arc<dyn WorkspaceCodeIntelligence>>,
624 chunk_catalog: Option<Arc<WorkspaceChunkCatalog>>,
625 git: Option<Arc<dyn WorkspaceGit>>,
626 git_stash: Option<Arc<dyn WorkspaceGitStashProvider>>,
627 git_worktree: Option<Arc<dyn WorkspaceGitWorktreeProvider>>,
628 operation_timeout: Option<std::time::Duration>,
629}
630
631impl WorkspaceServicesBuilder {
632 pub fn new(workspace_ref: WorkspaceRef, file_system: Arc<dyn WorkspaceFileSystem>) -> Self {
633 Self {
634 workspace_ref,
635 capabilities: WorkspaceCapabilities::read_write(),
636 path_resolver: Arc::new(VirtualPathResolver),
637 file_system,
638 file_system_ext: None,
639 text_reader: None,
640 command_runner: None,
641 search: None,
642 code_intelligence: None,
643 chunk_catalog: None,
644 git: None,
645 git_stash: None,
646 git_worktree: None,
647 operation_timeout: None,
648 }
649 }
650
651 pub fn capabilities(mut self, capabilities: WorkspaceCapabilities) -> Self {
652 self.capabilities = capabilities;
653 self
654 }
655
656 pub fn command_runner(mut self, command_runner: Arc<dyn WorkspaceCommandRunner>) -> Self {
657 self.capabilities.exec = true;
658 self.command_runner = Some(command_runner);
659 self
660 }
661
662 pub fn search(mut self, search: Arc<dyn WorkspaceSearch>) -> Self {
663 self.capabilities.search = true;
664 self.search = Some(search);
665 self
666 }
667
668 pub fn code_intelligence(mut self, provider: Arc<dyn WorkspaceCodeIntelligence>) -> Self {
669 self.capabilities.code_intelligence = true;
670 self.code_intelligence = Some(provider);
671 self
672 }
673
674 pub fn chunk_catalog(mut self, catalog: Arc<WorkspaceChunkCatalog>) -> Self {
676 self.chunk_catalog = Some(catalog);
677 self
678 }
679
680 pub fn git(mut self, git: Arc<dyn WorkspaceGit>) -> Self {
681 self.capabilities.git = true;
682 self.git = Some(git);
683 self
684 }
685
686 pub fn git_stash(mut self, git_stash: Arc<dyn WorkspaceGitStashProvider>) -> Self {
687 self.git_stash = Some(git_stash);
688 self
689 }
690
691 pub fn git_worktree(mut self, git_worktree: Arc<dyn WorkspaceGitWorktreeProvider>) -> Self {
692 self.git_worktree = Some(git_worktree);
693 self
694 }
695
696 pub fn file_system_ext(mut self, ext: Arc<dyn WorkspaceFileSystemExt>) -> Self {
701 self.file_system_ext = Some(ext);
702 self
703 }
704
705 pub fn text_reader(mut self, reader: Arc<dyn WorkspaceTextReader>) -> Self {
706 self.text_reader = Some(reader);
707 self
708 }
709
710 pub fn operation_timeout(mut self, timeout: std::time::Duration) -> Self {
714 self.operation_timeout = Some(timeout);
715 self
716 }
717
718 pub fn build(self) -> Arc<WorkspaceServices> {
719 let mut services = WorkspaceServices::new_with_git(
720 self.workspace_ref,
721 self.capabilities,
722 self.path_resolver,
723 self.file_system,
724 self.command_runner,
725 self.search,
726 self.git,
727 );
728 services.file_system_ext = self.file_system_ext;
729 services.text_reader = self.text_reader;
730 services.capabilities.code_intelligence = self.code_intelligence.is_some();
731 services.code_intelligence = self.code_intelligence;
732 services.chunk_catalog = self.chunk_catalog;
733 services.workspace_retrieval = None;
734 services.git_stash = self.git_stash;
735 services.git_worktree = self.git_worktree;
736 services.operation_timeout = self.operation_timeout;
737 Arc::new(services)
738 }
739}