1use std::collections::HashMap;
6
7use chrono::{DateTime, Utc};
8pub use everruns_core::driver_registry::{
9 OPENROUTER_HTTP_REFERER_METADATA_KEY, OPENROUTER_X_TITLE_METADATA_KEY,
10};
11use everruns_core::network_access::NetworkAccessList;
12use everruns_core::{
13 Agent, AgentCapabilityConfig, AgentId, AgentStatus, DEFAULT_ORG_PUBLIC_ID, Harness, HarnessId,
14 HarnessStatus, ModelId, PrincipalId, ScopedMcpServers, Session, SessionId, SessionStatus,
15 ToolDefinition, plugin_capability_id,
16};
17use uuid::Uuid;
18
19#[derive(Debug, Clone)]
25pub struct HarnessBuilder {
26 id: HarnessId,
27 name: String,
28 display_name: Option<String>,
29 description: Option<String>,
30 system_prompt: String,
31 parent_harness_id: Option<HarnessId>,
32 default_model_id: Option<ModelId>,
33 tags: Vec<String>,
34 capabilities: Vec<AgentCapabilityConfig>,
35 initial_files: Vec<everruns_core::InitialFile>,
36 network_access: Option<NetworkAccessList>,
37 mcp_servers: ScopedMcpServers,
38 embedder_metadata: HashMap<String, String>,
39 is_built_in: bool,
40 status: HarnessStatus,
41 created_at: Option<DateTime<Utc>>,
42 updated_at: Option<DateTime<Utc>>,
43}
44
45impl HarnessBuilder {
46 pub fn new(name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
48 Self {
49 id: HarnessId::new(),
50 name: name.into(),
51 display_name: None,
52 description: None,
53 system_prompt: system_prompt.into(),
54 parent_harness_id: None,
55 default_model_id: None,
56 tags: Vec::new(),
57 capabilities: Vec::new(),
58 initial_files: Vec::new(),
59 network_access: None,
60 mcp_servers: ScopedMcpServers::default(),
61 embedder_metadata: HashMap::new(),
62 is_built_in: false,
63 status: HarnessStatus::Active,
64 created_at: None,
65 updated_at: None,
66 }
67 }
68
69 pub fn id(mut self, id: HarnessId) -> Self {
71 self.id = id;
72 self
73 }
74
75 pub fn harness_id(&self) -> HarnessId {
77 self.id
78 }
79
80 pub fn name(mut self, name: impl Into<String>) -> Self {
81 self.name = name.into();
82 self
83 }
84
85 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
86 self.display_name = Some(display_name.into());
87 self
88 }
89
90 pub fn description(mut self, description: impl Into<String>) -> Self {
91 self.description = Some(description.into());
92 self
93 }
94
95 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
96 self.system_prompt = system_prompt.into();
97 self
98 }
99
100 pub fn parent_harness_id(mut self, parent_harness_id: HarnessId) -> Self {
101 self.parent_harness_id = Some(parent_harness_id);
102 self
103 }
104
105 pub fn default_model_id(mut self, default_model_id: ModelId) -> Self {
106 self.default_model_id = Some(default_model_id);
107 self
108 }
109
110 pub fn tag(mut self, tag: impl Into<String>) -> Self {
111 self.tags.push(tag.into());
112 self
113 }
114
115 pub fn tags<I, S>(mut self, tags: I) -> Self
116 where
117 I: IntoIterator<Item = S>,
118 S: Into<String>,
119 {
120 self.tags.extend(tags.into_iter().map(Into::into));
121 self
122 }
123
124 pub fn capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
125 self.capabilities.push(capability.into());
126 self
127 }
128
129 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
130 self.capability(capability)
131 }
132
133 pub fn capabilities<I, C>(mut self, capabilities: I) -> Self
134 where
135 I: IntoIterator<Item = C>,
136 C: Into<AgentCapabilityConfig>,
137 {
138 self.capabilities
139 .extend(capabilities.into_iter().map(Into::into));
140 self
141 }
142
143 pub fn initial_file(mut self, file: everruns_core::InitialFile) -> Self {
144 self.initial_files.push(file);
145 self
146 }
147
148 pub fn network_access(mut self, network_access: NetworkAccessList) -> Self {
149 self.network_access = Some(network_access);
150 self
151 }
152
153 pub fn mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
154 self.mcp_servers = mcp_servers;
155 self
156 }
157
158 pub fn metadata_entry(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
159 self.embedder_metadata.insert(key.into(), value.into());
160 self
161 }
162
163 pub fn metadata_entries<I, K, V>(mut self, entries: I) -> Self
164 where
165 I: IntoIterator<Item = (K, V)>,
166 K: Into<String>,
167 V: Into<String>,
168 {
169 self.embedder_metadata
170 .extend(entries.into_iter().map(|(k, v)| (k.into(), v.into())));
171 self
172 }
173
174 pub fn openrouter_attribution(
179 mut self,
180 http_referer: impl Into<String>,
181 title: impl Into<String>,
182 ) -> Self {
183 self.embedder_metadata.insert(
184 OPENROUTER_HTTP_REFERER_METADATA_KEY.to_string(),
185 http_referer.into(),
186 );
187 self.embedder_metadata
188 .insert(OPENROUTER_X_TITLE_METADATA_KEY.to_string(), title.into());
189 self
190 }
191
192 pub fn is_built_in(mut self, is_built_in: bool) -> Self {
193 self.is_built_in = is_built_in;
194 self
195 }
196
197 pub fn status(mut self, status: HarnessStatus) -> Self {
198 self.status = status;
199 self
200 }
201
202 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
203 self.created_at = Some(created_at);
204 self
205 }
206
207 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
208 self.updated_at = Some(updated_at);
209 self
210 }
211
212 pub fn build(self) -> Harness {
214 let created_at = self.created_at.unwrap_or_else(Utc::now);
215 let updated_at = self.updated_at.unwrap_or(created_at);
216
217 Harness {
218 id: self.id,
219 name: self.name,
220 display_name: self.display_name,
221 description: self.description,
222 system_prompt: self.system_prompt,
223 parent_harness_id: self.parent_harness_id,
224 default_model_id: self.default_model_id,
225 tags: self.tags,
226 capabilities: self.capabilities,
227 initial_files: self.initial_files,
228 network_access: self.network_access,
229 mcp_servers: self.mcp_servers,
230 embedder_metadata: self.embedder_metadata,
231 is_built_in: self.is_built_in,
232 status: self.status,
233 created_at,
234 updated_at,
235 archived_at: None,
236 deleted_at: None,
237 }
238 }
239}
240
241#[derive(Debug, Clone)]
243pub struct AgentBuilder {
244 id: AgentId,
245 name: String,
246 display_name: Option<String>,
247 description: Option<String>,
248 system_prompt: String,
249 default_model_id: Option<ModelId>,
250 tags: Vec<String>,
251 capabilities: Vec<AgentCapabilityConfig>,
252 initial_files: Vec<everruns_core::InitialFile>,
253 network_access: Option<NetworkAccessList>,
254 max_iterations: Option<usize>,
255 tools: Vec<ToolDefinition>,
256 mcp_servers: ScopedMcpServers,
257 status: AgentStatus,
258 created_at: Option<DateTime<Utc>>,
259 updated_at: Option<DateTime<Utc>>,
260}
261
262impl AgentBuilder {
263 pub fn new(name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
265 Self {
266 id: AgentId::new(),
267 name: name.into(),
268 display_name: None,
269 description: None,
270 system_prompt: system_prompt.into(),
271 default_model_id: None,
272 tags: Vec::new(),
273 capabilities: Vec::new(),
274 initial_files: Vec::new(),
275 network_access: None,
276 max_iterations: None,
277 tools: Vec::new(),
278 mcp_servers: ScopedMcpServers::default(),
279 status: AgentStatus::Active,
280 created_at: None,
281 updated_at: None,
282 }
283 }
284
285 pub fn id(mut self, id: AgentId) -> Self {
287 self.id = id;
288 self
289 }
290
291 pub fn agent_id(&self) -> AgentId {
293 self.id
294 }
295
296 pub fn name(mut self, name: impl Into<String>) -> Self {
297 self.name = name.into();
298 self
299 }
300
301 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
302 self.display_name = Some(display_name.into());
303 self
304 }
305
306 pub fn description(mut self, description: impl Into<String>) -> Self {
307 self.description = Some(description.into());
308 self
309 }
310
311 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
312 self.system_prompt = system_prompt.into();
313 self
314 }
315
316 pub fn default_model_id(mut self, default_model_id: ModelId) -> Self {
317 self.default_model_id = Some(default_model_id);
318 self
319 }
320
321 pub fn tag(mut self, tag: impl Into<String>) -> Self {
322 self.tags.push(tag.into());
323 self
324 }
325
326 pub fn tags<I, S>(mut self, tags: I) -> Self
327 where
328 I: IntoIterator<Item = S>,
329 S: Into<String>,
330 {
331 self.tags.extend(tags.into_iter().map(Into::into));
332 self
333 }
334
335 pub fn capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
336 self.capabilities.push(capability.into());
337 self
338 }
339
340 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
341 self.capability(capability)
342 }
343
344 pub fn capabilities<I, C>(mut self, capabilities: I) -> Self
345 where
346 I: IntoIterator<Item = C>,
347 C: Into<AgentCapabilityConfig>,
348 {
349 self.capabilities
350 .extend(capabilities.into_iter().map(Into::into));
351 self
352 }
353
354 pub fn initial_file(mut self, file: everruns_core::InitialFile) -> Self {
355 self.initial_files.push(file);
356 self
357 }
358
359 pub fn network_access(mut self, network_access: NetworkAccessList) -> Self {
360 self.network_access = Some(network_access);
361 self
362 }
363
364 pub fn max_iterations(mut self, max_iterations: usize) -> Self {
365 self.max_iterations = Some(max_iterations);
366 self
367 }
368
369 pub fn tool(mut self, tool: ToolDefinition) -> Self {
370 self.tools.push(tool);
371 self
372 }
373
374 pub fn tools<I>(mut self, tools: I) -> Self
375 where
376 I: IntoIterator<Item = ToolDefinition>,
377 {
378 self.tools.extend(tools);
379 self
380 }
381
382 pub fn mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
383 self.mcp_servers = mcp_servers;
384 self
385 }
386
387 pub fn status(mut self, status: AgentStatus) -> Self {
388 self.status = status;
389 self
390 }
391
392 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
393 self.created_at = Some(created_at);
394 self
395 }
396
397 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
398 self.updated_at = Some(updated_at);
399 self
400 }
401
402 pub fn build(self) -> Agent {
404 let created_at = self.created_at.unwrap_or_else(Utc::now);
405 let updated_at = self.updated_at.unwrap_or(created_at);
406
407 Agent {
408 public_id: self.id,
409 internal_id: Uuid::nil(),
410 name: self.name,
411 display_name: self.display_name,
412 description: self.description,
413 system_prompt: self.system_prompt,
414 default_model_id: self.default_model_id,
415 default_version_id: None,
416 forked_from_agent_id: None,
417 forked_from_version_id: None,
418 root_agent_id: None,
419 tags: self.tags,
420 capabilities: self.capabilities,
421 initial_files: self.initial_files,
422 network_access: self.network_access,
423 max_iterations: self.max_iterations,
424 tools: self.tools,
425 mcp_servers: self.mcp_servers,
426 status: self.status,
427 created_at,
428 updated_at,
429 archived_at: None,
430 deleted_at: None,
431 usage: None,
432 }
433 }
434}
435
436#[derive(Debug, Clone)]
438pub struct SessionBuilder {
439 id: SessionId,
440 organization_id: String,
441 harness_id: HarnessId,
442 agent_id: Option<AgentId>,
443 owner_principal_id: PrincipalId,
444 title: Option<String>,
445 locale: Option<String>,
446 tags: Vec<String>,
447 model_id: Option<ModelId>,
448 capabilities: Vec<AgentCapabilityConfig>,
449 tools: Vec<ToolDefinition>,
450 mcp_servers: ScopedMcpServers,
451 system_prompt: Option<String>,
452 initial_files: Vec<everruns_core::InitialFile>,
453 network_access: Option<NetworkAccessList>,
454 max_iterations: Option<usize>,
455 status: SessionStatus,
456 created_at: Option<DateTime<Utc>>,
457 updated_at: Option<DateTime<Utc>>,
458}
459
460impl SessionBuilder {
461 pub fn new(harness_id: HarnessId) -> Self {
463 Self {
464 id: SessionId::new(),
465 organization_id: DEFAULT_ORG_PUBLIC_ID.to_string(),
466 harness_id,
467 agent_id: None,
468 owner_principal_id: PrincipalId::from_seed(1),
469 title: None,
470 locale: None,
471 tags: Vec::new(),
472 model_id: None,
473 capabilities: Vec::new(),
474 tools: Vec::new(),
475 mcp_servers: ScopedMcpServers::default(),
476 system_prompt: None,
477 initial_files: Vec::new(),
478 network_access: None,
479 max_iterations: None,
480 status: SessionStatus::Started,
481 created_at: None,
482 updated_at: None,
483 }
484 }
485
486 pub fn id(mut self, id: SessionId) -> Self {
488 self.id = id;
489 self
490 }
491
492 pub fn session_id(&self) -> SessionId {
494 self.id
495 }
496
497 pub fn organization_id(mut self, organization_id: impl Into<String>) -> Self {
498 self.organization_id = organization_id.into();
499 self
500 }
501
502 pub fn harness(mut self, harness_id: HarnessId) -> Self {
503 self.harness_id = harness_id;
504 self
505 }
506
507 pub fn agent(mut self, agent_id: AgentId) -> Self {
508 self.agent_id = Some(agent_id);
509 self
510 }
511
512 pub fn owner_principal_id(mut self, owner_principal_id: PrincipalId) -> Self {
513 self.owner_principal_id = owner_principal_id;
514 self
515 }
516
517 pub fn title(mut self, title: impl Into<String>) -> Self {
518 self.title = Some(title.into());
519 self
520 }
521
522 pub fn locale(mut self, locale: impl Into<String>) -> Self {
523 self.locale = Some(locale.into());
524 self
525 }
526
527 pub fn tag(mut self, tag: impl Into<String>) -> Self {
528 self.tags.push(tag.into());
529 self
530 }
531
532 pub fn tags<I, S>(mut self, tags: I) -> Self
533 where
534 I: IntoIterator<Item = S>,
535 S: Into<String>,
536 {
537 self.tags.extend(tags.into_iter().map(Into::into));
538 self
539 }
540
541 pub fn model_id(mut self, model_id: ModelId) -> Self {
542 self.model_id = Some(model_id);
543 self
544 }
545
546 pub fn capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
547 self.capabilities.push(capability.into());
548 self
549 }
550
551 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
552 self.capability(capability)
553 }
554
555 pub fn capabilities<I, C>(mut self, capabilities: I) -> Self
556 where
557 I: IntoIterator<Item = C>,
558 C: Into<AgentCapabilityConfig>,
559 {
560 self.capabilities
561 .extend(capabilities.into_iter().map(Into::into));
562 self
563 }
564
565 pub fn tool(mut self, tool: ToolDefinition) -> Self {
566 self.tools.push(tool);
567 self
568 }
569
570 pub fn tools<I>(mut self, tools: I) -> Self
571 where
572 I: IntoIterator<Item = ToolDefinition>,
573 {
574 self.tools.extend(tools);
575 self
576 }
577
578 pub fn mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
579 self.mcp_servers = mcp_servers;
580 self
581 }
582
583 pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
584 self.system_prompt = Some(system_prompt.into());
585 self
586 }
587
588 pub fn initial_file(mut self, file: everruns_core::InitialFile) -> Self {
589 self.initial_files.push(file);
590 self
591 }
592
593 pub fn network_access(mut self, network_access: NetworkAccessList) -> Self {
594 self.network_access = Some(network_access);
595 self
596 }
597
598 pub fn max_iterations(mut self, max_iterations: usize) -> Self {
599 self.max_iterations = Some(max_iterations);
600 self
601 }
602
603 pub fn status(mut self, status: SessionStatus) -> Self {
604 self.status = status;
605 self
606 }
607
608 pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
609 self.created_at = Some(created_at);
610 self
611 }
612
613 pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
614 self.updated_at = Some(updated_at);
615 self
616 }
617
618 pub fn build(self) -> Session {
620 let created_at = self.created_at.unwrap_or_else(Utc::now);
621 let updated_at = self.updated_at.unwrap_or(created_at);
622
623 Session {
624 id: self.id,
625 workspace_id: everruns_core::WorkspaceId::from_uuid((self.id).uuid()),
626 organization_id: self.organization_id,
627 harness_id: self.harness_id,
628 agent_id: self.agent_id,
629 agent_version_id: None,
630 agent_identity_id: None,
631 owner_principal_id: self.owner_principal_id,
632 resolved_owner_user_id: None,
633 owner: None,
634 effective_owner: None,
635 title: self.title,
636 locale: self.locale,
637 preview: None,
638 output_preview: None,
639 tags: self.tags,
640 model_id: self.model_id,
641 capabilities: self.capabilities,
642 tools: self.tools,
643 mcp_servers: self.mcp_servers,
644 system_prompt: self.system_prompt,
645 initial_files: self.initial_files,
646 hints: None,
647 network_access: self.network_access,
648 max_iterations: self.max_iterations,
649 status: self.status,
650 created_at,
651 updated_at,
652 started_at: None,
653 finished_at: None,
654 usage: None,
655 is_pinned: None,
656 active_schedule_count: None,
657 features: Vec::new(),
658 parent_session_id: None,
659 blueprint_id: None,
660 blueprint_config: None,
661 }
662 }
663}
664
665#[derive(Debug, Clone)]
670pub struct SingleSessionBuilder {
671 harness: HarnessBuilder,
672 agent: AgentBuilder,
673 session: SessionBuilder,
674}
675
676impl Default for SingleSessionBuilder {
677 fn default() -> Self {
678 let harness = HarnessBuilder::new("embedded-harness", "");
679 let agent = AgentBuilder::new("embedded-agent", "");
680 let session = SessionBuilder::new(harness.harness_id()).agent(agent.agent_id());
681 Self {
682 harness,
683 agent,
684 session,
685 }
686 }
687}
688
689impl SingleSessionBuilder {
690 pub fn harness(mut self, name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
694 let harness_id = self.harness.harness_id();
695 self.harness = self.harness.name(name).system_prompt(system_prompt);
696 self.session = self.session.harness(harness_id);
697 self
698 }
699
700 pub fn agent(mut self, name: impl Into<String>, system_prompt: impl Into<String>) -> Self {
704 let agent_id = self.agent.agent_id();
705 self.agent = self.agent.name(name).system_prompt(system_prompt);
706 self.session = self.session.agent(agent_id);
707 self
708 }
709
710 pub fn with_capability(self, capability: impl Into<AgentCapabilityConfig>) -> Self {
712 self.harness_capability(capability)
713 }
714
715 pub fn harness_capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
717 self.harness = self.harness.capability(capability);
718 self
719 }
720
721 pub fn agent_capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
723 self.agent = self.agent.capability(capability);
724 self
725 }
726
727 pub fn agent_plugin(mut self, name: &str) -> Self {
740 self.agent = self.agent.capability(plugin_capability_id(name));
741 self
742 }
743
744 pub fn session_capability(mut self, capability: impl Into<AgentCapabilityConfig>) -> Self {
746 self.session = self.session.capability(capability);
747 self
748 }
749
750 pub fn session_mcp_servers(mut self, mcp_servers: ScopedMcpServers) -> Self {
753 self.session = self.session.mcp_servers(mcp_servers);
754 self
755 }
756
757 pub fn harness_display_name(mut self, display_name: impl Into<String>) -> Self {
758 self.harness = self.harness.display_name(display_name);
759 self
760 }
761
762 pub fn agent_display_name(mut self, display_name: impl Into<String>) -> Self {
763 self.agent = self.agent.display_name(display_name);
764 self
765 }
766
767 pub fn harness_description(mut self, description: impl Into<String>) -> Self {
768 self.harness = self.harness.description(description);
769 self
770 }
771
772 pub fn openrouter_attribution(
773 mut self,
774 http_referer: impl Into<String>,
775 title: impl Into<String>,
776 ) -> Self {
777 self.harness = self.harness.openrouter_attribution(http_referer, title);
778 self
779 }
780
781 pub fn agent_description(mut self, description: impl Into<String>) -> Self {
782 self.agent = self.agent.description(description);
783 self
784 }
785
786 pub fn session_title(mut self, title: impl Into<String>) -> Self {
787 self.session = self.session.title(title);
788 self
789 }
790
791 pub fn locale(mut self, locale: impl Into<String>) -> Self {
792 self.session = self.session.locale(locale);
793 self
794 }
795
796 pub fn tag(mut self, tag: impl Into<String>) -> Self {
797 let tag = tag.into();
798 self.harness = self.harness.tag(tag.clone());
799 self.agent = self.agent.tag(tag.clone());
800 self.session = self.session.tag(tag);
801 self
802 }
803
804 pub fn session_model_id(mut self, model_id: ModelId) -> Self {
805 self.session = self.session.model_id(model_id);
806 self
807 }
808
809 pub fn harness_default_model_id(mut self, model_id: ModelId) -> Self {
810 self.harness = self.harness.default_model_id(model_id);
811 self
812 }
813
814 pub fn agent_default_model_id(mut self, model_id: ModelId) -> Self {
815 self.agent = self.agent.default_model_id(model_id);
816 self
817 }
818
819 pub fn agent_max_iterations(mut self, max_iterations: usize) -> Self {
820 self.agent = self.agent.max_iterations(max_iterations);
821 self
822 }
823
824 pub fn session_max_iterations(mut self, max_iterations: usize) -> Self {
825 self.session = self.session.max_iterations(max_iterations);
826 self
827 }
828
829 pub fn agent_tool(mut self, tool: ToolDefinition) -> Self {
830 self.agent = self.agent.tool(tool);
831 self
832 }
833
834 pub fn session_tool(mut self, tool: ToolDefinition) -> Self {
835 self.session = self.session.tool(tool);
836 self
837 }
838
839 pub fn harness_initial_file(mut self, file: everruns_core::InitialFile) -> Self {
840 self.harness = self.harness.initial_file(file);
841 self
842 }
843
844 pub fn agent_initial_file(mut self, file: everruns_core::InitialFile) -> Self {
845 self.agent = self.agent.initial_file(file);
846 self
847 }
848
849 pub fn session_initial_file(mut self, file: everruns_core::InitialFile) -> Self {
850 self.session = self.session.initial_file(file);
851 self
852 }
853
854 pub fn harness_network_access(mut self, network_access: NetworkAccessList) -> Self {
855 self.harness = self.harness.network_access(network_access);
856 self
857 }
858
859 pub fn agent_network_access(mut self, network_access: NetworkAccessList) -> Self {
860 self.agent = self.agent.network_access(network_access);
861 self
862 }
863
864 pub fn session_network_access(mut self, network_access: NetworkAccessList) -> Self {
865 self.session = self.session.network_access(network_access);
866 self
867 }
868
869 pub fn harness_id(&self) -> HarnessId {
870 self.harness.harness_id()
871 }
872
873 pub fn agent_id(&self) -> AgentId {
874 self.agent.agent_id()
875 }
876
877 pub fn session_id(mut self, id: SessionId) -> Self {
884 self.session = self.session.id(id);
885 self
886 }
887
888 pub(crate) fn build(self) -> (Harness, Agent, Session, SessionId) {
889 let session_id = self.session.session_id();
890 (
891 self.harness.build(),
892 self.agent.build(),
893 self.session.build(),
894 session_id,
895 )
896 }
897}
898
899#[cfg(test)]
900mod tests {
901 use super::*;
902
903 #[test]
904 fn harness_builder_openrouter_attribution_adds_metadata_keys() {
905 let harness = HarnessBuilder::new("app", "prompt")
906 .openrouter_attribution("https://app.example", "Example App")
907 .build();
908
909 assert_eq!(
910 harness
911 .embedder_metadata
912 .get(OPENROUTER_HTTP_REFERER_METADATA_KEY)
913 .map(String::as_str),
914 Some("https://app.example")
915 );
916 assert_eq!(
917 harness
918 .embedder_metadata
919 .get(OPENROUTER_X_TITLE_METADATA_KEY)
920 .map(String::as_str),
921 Some("Example App")
922 );
923 }
924
925 #[test]
926 fn single_session_builder_openrouter_attribution_configures_harness() {
927 let (harness, _agent, _session, _session_id) = SingleSessionBuilder::default()
928 .openrouter_attribution("https://single.example", "Single App")
929 .build();
930
931 assert_eq!(
932 harness
933 .embedder_metadata
934 .get(OPENROUTER_HTTP_REFERER_METADATA_KEY)
935 .map(String::as_str),
936 Some("https://single.example")
937 );
938 assert_eq!(
939 harness
940 .embedder_metadata
941 .get(OPENROUTER_X_TITLE_METADATA_KEY)
942 .map(String::as_str),
943 Some("Single App")
944 );
945 }
946}