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